diff --git a/.env b/.env index 62cbae1b..2ec2386b 100644 --- a/.env +++ b/.env @@ -1,4 +1,4 @@ -VERSION="2.8.7" -MAJOR=2 -MINOR=8 -PATCH=7 \ No newline at end of file +VERSION="3.0.4" +MAJOR=3 +MINOR=0 +PATCH=4 diff --git a/.github/workflows/build-bebopc.yml b/.github/workflows/build-bebopc.yml index f1566e7d..2aea8fce 100644 --- a/.github/workflows/build-bebopc.yml +++ b/.github/workflows/build-bebopc.yml @@ -7,12 +7,14 @@ on: - "Core/**" branches: - master + - vnext pull_request: paths: - "Compiler/**" - "Core/**" branches: - master + - vnext jobs: build-compiler: runs-on: ${{ matrix.os }} @@ -31,20 +33,22 @@ jobs: - os: ubuntu-22.04 IDENTIFIER: linux ARTIFACT: bebopc + BUILD_WASI: true env: CONFIGURATION: Release BUILD_ARTIFACT_X86_64: ./bin/compiler/Release/publish/${{matrix.IDENTIFIER}}-x64/${{matrix.ARTIFACT}} BUILD_ARTIFACT_ARM64: ./bin/compiler/Release/publish/${{matrix.IDENTIFIER}}-arm64/${{matrix.ARTIFACT}} + BUILD_ARTIFACT_WASI: ./bin/compiler/Release/artifacts/wasi-wasm/AppBundle/${{matrix.ARTIFACT}}.wasm BUILD_ZIP_ARTIFACT_X86_64: ./bin/compiler/Release/publish/${{matrix.ARTIFACT}}-${{matrix.IDENTIFIER}}-x64.zip BUILD_ZIP_ARTIFACT_ARM64: ./bin/compiler/Release/publish/${{matrix.ARTIFACT}}-${{matrix.IDENTIFIER}}-arm64.zip steps: - - uses: actions/checkout@v1 + - uses: actions/checkout@v4 - name: Get Enviorment Variables id: dotenv - uses: falti/dotenv-action@v0.2.5 + uses: falti/dotenv-action@v1.0.4 - if: matrix.os == 'ubuntu-22.04' name: Install Dependencies @@ -52,11 +56,16 @@ jobs: sudo apt-get update sudo apt-get install clang zlib1g-dev libkrb5-dev libtinfo5 + - if: matrix.os == 'ubuntu-22.04' && matrix.BUILD_WASI + name: Install WASI Dependencies + run: ./install-wasi.sh + working-directory: ./scripts/ + - name: Setup .NET - uses: actions/setup-dotnet@v1 + uses: actions/setup-dotnet@v3 with: - dotnet-version: "7.0.x" # SDK Version to use; x will use the latest version of the 7.0 channel - include-prerelease: true + dotnet-version: "8.0.x" + dotnet-quality: "preview" - name: Restore Solution run: dotnet restore @@ -66,6 +75,11 @@ jobs: dotnet publish -c ${{env.CONFIGURATION}} -r ${{matrix.IDENTIFIER}}-arm64 -p:ReleaseVersion=${{ steps.dotenv.outputs.version }} -p:PublishTrimmed=false -p:PublishSingleFile=true --self-contained working-directory: ./Compiler/ + - if: matrix.os == 'ubuntu-22.04' && matrix.BUILD_WASI + name: Build bebopc for WASI + run: ./build-wasi.sh + working-directory: ./scripts/ + - if: matrix.os == 'macos-latest' name: Zip macOS Binary run: | @@ -86,13 +100,20 @@ jobs: Compress-Archive -Path ${{env.BUILD_ARTIFACT_ARM64}} -DestinationPath ${{env.BUILD_ZIP_ARTIFACT_ARM64}} - name: Upload X86_64 Build - uses: actions/upload-artifact@v2 + uses: actions/upload-artifact@v4 with: name: ${{matrix.IDENTIFIER}}-x64 path: ${{env.BUILD_ZIP_ARTIFACT_X86_64}} - name: Upload ARM64 Build - uses: actions/upload-artifact@v2 + uses: actions/upload-artifact@v4 with: name: ${{matrix.IDENTIFIER}}-arm64 path: ${{env.BUILD_ZIP_ARTIFACT_ARM64}} + + - if: matrix.os == 'ubuntu-22.04' && matrix.BUILD_WASI + name: Upload WASI_WASM Build + uses: actions/upload-artifact@v4 + with: + name: wasi-wasm + path: ${{env.BUILD_ARTIFACT_WASI}} diff --git a/.github/workflows/build-chordc.yml b/.github/workflows/build-chordc.yml new file mode 100644 index 00000000..12d988ab --- /dev/null +++ b/.github/workflows/build-chordc.yml @@ -0,0 +1,108 @@ +name: build-chordc +on: + workflow_dispatch: + push: + paths: + - "extensions/**" + branches: + - master + - vnext + pull_request: + paths: + - "extensions/**" + branches: + - master + - vnext +jobs: + build-chordc: + runs-on: ${{ matrix.os }} + strategy: + matrix: + os: [windows-latest, macos-latest, ubuntu-22.04] + include: + - os: macos-latest + IDENTIFIER: osx + ARTIFACT: chordc + + - os: windows-latest + IDENTIFIER: win + ARTIFACT: chordc.exe + + - os: ubuntu-22.04 + IDENTIFIER: linux + ARTIFACT: chordc + BUILD_WASI: true + + env: + CONFIGURATION: Release + CHORD_ARTIFACT_X86_64: ./extensions/chordc/bin/Release/net8.0/${{matrix.IDENTIFIER}}-x64/publish/${{matrix.ARTIFACT}} + CHORD_ARTIFACT_ARM64: ./extensions/chordc/bin/Release/net8.0/${{matrix.IDENTIFIER}}-arm64/publish/${{matrix.ARTIFACT}} + CHORD_ZIP_ARTIFACT_X86_64: ./extensions/chordc/bin/Release/net8.0/${{matrix.IDENTIFIER}}-x64/publish/${{matrix.ARTIFACT}}-${{matrix.IDENTIFIER}}-x64.zip + CHORD_ZIP_ARTIFACT_ARM64: ./extensions/chordc/bin/Release/net8.0/${{matrix.IDENTIFIER}}-arm64/publish/${{matrix.ARTIFACT}}-${{matrix.IDENTIFIER}}-arm64.zip + + steps: + - uses: actions/checkout@v4 + + - name: Get Enviorment Variables + id: dotenv + uses: falti/dotenv-action@v1.0.4 + + - if: matrix.os == 'ubuntu-22.04' + name: Install Dependencies + run: | + sudo dpkg --add-architecture arm64 + sudo bash -c 'cat > /etc/apt/sources.list.d/arm64.list < /etc/apt/sources.list.d/arm64.list < tmp && mv tmp package.json + VERSION=$(echo ${{ steps.dotenv.outputs.version }} | sed 's/-.*//') + jq '.version = "'"$VERSION"'"' package.json > tmp && mv tmp package.json npm install --global @vscode/vsce yarn install vsce package + if [[ $GITHUB_REF == refs/heads/vnext ]] || [[ $GITHUB_REF == refs/tags/*rc* ]] || [[ $GITHUB_REF == refs/tags/*alpha* ]] || [[ $GITHUB_REF == refs/tags/*beta* ]]; then + echo "Building pre-release package" + vsce package --pre-release + else + echo "Building standard package" + vsce package + fi working-directory: ./vscode-bebop - name: Upload VSCode artifact @@ -341,7 +411,7 @@ jobs: runs-on: ubuntu-22.04 needs: [build-compiler, build-runtimes, build-tools] steps: - - uses: actions/checkout@v1 + - uses: actions/checkout@v4 - uses: actions/setup-node@v1 with: node-version: "18.16.0" # LTS @@ -351,10 +421,19 @@ jobs: id: dotenv uses: falti/dotenv-action@v0.2.5 + - id: release-info + name: Set Release Info + run: | + if [[ $GITHUB_REF == refs/heads/vnext ]] || [[ $GITHUB_REF == refs/tags/*rc* ]] || [[ $GITHUB_REF == refs/tags/*alpha* ]] || [[ $GITHUB_REF == refs/tags/*beta* ]]; then + echo "PRERELEASE=true" >> "$GITHUB_OUTPUT" + else + echo "PRERELEASE=false" >> "$GITHUB_OUTPUT" + fi + - name: Setup .NET uses: actions/setup-dotnet@v1 with: - dotnet-version: "7.0.x" # SDK Version to use; x will use the latest version of the 7.0 channel + dotnet-version: "8.0.x" # SDK Version to use; x will use the latest version of the 7.0 channel include-prerelease: true - name: Setup Rust @@ -389,24 +468,40 @@ jobs: working-directory: ${{env.RUST_RUNTIME_ROOT}} - name: Publish NodeJS Tools - run: npm publish "./bebop-tools-npm-${{ steps.dotenv.outputs.version }}/bebop-tools-v${{ steps.dotenv.outputs.version }}.tgz" --access public + run: npm publish "./bebop-tools-npm-${{ steps.dotenv.outputs.version }}/bebop-tools-v${{ steps.dotenv.outputs.version }}.tgz" --access public ${{ steps.release-info.outputs.PRERELEASE == 'true' && '--tag next' || '' }} env: NODE_AUTH_TOKEN: ${{ secrets.NPM_ACCESS_TOKEN }} - name: Publish TypeScript Runtime - run: npm publish "./bebop-runtime-ts-${{ steps.dotenv.outputs.version }}/bebop-v${{ steps.dotenv.outputs.version }}.tgz" --access public + run: npm publish "./bebop-runtime-ts-${{ steps.dotenv.outputs.version }}/bebop-v${{ steps.dotenv.outputs.version }}.tgz" --access public ${{ steps.release-info.outputs.PRERELEASE == 'true' && '--tag next' || '' }} env: NODE_AUTH_TOKEN: ${{ secrets.NPM_ACCESS_TOKEN }} - name: Publish VSCode Extension + continue-on-error: true run: | npm install --global @vscode/vsce - vsce publish --packagePath ./vscode-bebop/bebop-lang-${{ steps.dotenv.outputs.version }}.vsix -p ${{ secrets.VSCE_TOKEN }} - + VERSION=$(echo ${{ steps.dotenv.outputs.version }} | sed 's/-.*//') + if [[ $GITHUB_REF == refs/heads/vnext ]] || [[ $GITHUB_REF == refs/tags/*rc* ]] || [[ $GITHUB_REF == refs/tags/*alpha* ]] || [[ $GITHUB_REF == refs/tags/*beta* ]]; then + echo "Publishing vscode pre-release package" + vsce publish --pre-release --packagePath ./vscode-bebop/bebop-lang-$VERSION.vsix -p ${{ secrets.VSCE_TOKEN }} + else + echo "Publishing vscode standard package" + vsce publish --packagePath ./vscode-bebop/bebop-lang-$VERSION.vsix -p ${{ secrets.VSCE_TOKEN }} + fi + - name: Publish Open VSX Extension + continue-on-error: true run: | npm i -g ovsx - npx ovsx publish ./vscode-bebop/bebop-lang-${{ steps.dotenv.outputs.version }}.vsix -p ${{ secrets.OPENVSX }} + VERSION=$(echo ${{ steps.dotenv.outputs.version }} | sed 's/-.*//') + if [[ $GITHUB_REF == refs/heads/vnext ]] || [[ $GITHUB_REF == refs/tags/*rc* ]] || [[ $GITHUB_REF == refs/tags/*alpha* ]] || [[ $GITHUB_REF == refs/tags/*beta* ]]; then + echo "Publishing ovsx pre-release package" + npx ovsx publish --pre-release ./vscode-bebop/bebop-lang-$VERSION.vsix -p ${{ secrets.OPENVSX }} + else + echo "Publishing ovsx standard package" + npx ovsx publish ./vscode-bebop/bebop-lang-$VERSION.vsix -p ${{ secrets.OPENVSX }} + fi - name: Create Release id: create_release @@ -417,7 +512,7 @@ jobs: tag_name: ${{ github.ref }} release_name: Bebop ${{ github.ref }} draft: false - prerelease: false + prerelease: ${{ steps.release-info.outputs.PRERELEASE == 'true' }} - name: Upload Compiler for Windows x64 uses: actions/upload-release-asset@v1 @@ -478,3 +573,74 @@ jobs: asset_path: ./linux-arm64/bebopc-linux-arm64.zip asset_name: bebopc-linux-arm64.zip asset_content_type: application/zip + + - name: Upload Compiler for WASM + uses: actions/upload-release-asset@v1 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + with: + upload_url: ${{ steps.create_release.outputs.upload_url }} + asset_path: ./wasi-wasm/bebopc.wasm + asset_name: bebopc.wasm + asset_content_type: application/wasm + + + - name: Upload chordc for Windows x64 + uses: actions/upload-release-asset@v1 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + with: + upload_url: ${{ steps.create_release.outputs.upload_url }} + asset_path: ./chordc-win-x64/chordc.exe-win-x64.zip + asset_name: chordc-windows-x64.zip + asset_content_type: application/zip + + - name: Upload chordc for Windows ARM64 + uses: actions/upload-release-asset@v1 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + with: + upload_url: ${{ steps.create_release.outputs.upload_url }} + asset_path: ./chordc-win-arm64/chordc.exe-win-arm64.zip + asset_name: chordc-windows-arm64.zip + asset_content_type: application/zip + + - name: Upload chordc for Mac x64 + uses: actions/upload-release-asset@v1 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + with: + upload_url: ${{ steps.create_release.outputs.upload_url }} + asset_path: ./chordc-osx-x64/chordc-osx-x64.zip + asset_name: chordc-macos-x64.zip + asset_content_type: application/zip + + - name: Upload chordc for Mac ARM64 + uses: actions/upload-release-asset@v1 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + with: + upload_url: ${{ steps.create_release.outputs.upload_url }} + asset_path: ./chordc-osx-arm64/chordc-osx-arm64.zip + asset_name: chordc-macos-arm64.zip + asset_content_type: application/zip + + - name: Upload chordc for Linux x64 + uses: actions/upload-release-asset@v1 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + with: + upload_url: ${{ steps.create_release.outputs.upload_url }} + asset_path: ./chordc-linux-x64/chordc-linux-x64.zip + asset_name: chordc-linux-x64.zip + asset_content_type: application/zip + + - name: Upload chordc for Linux ARM64 + uses: actions/upload-release-asset@v1 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + with: + upload_url: ${{ steps.create_release.outputs.upload_url }} + asset_path: ./chordc-linux-arm64/chordc-linux-arm64.zip + asset_name: chordc-linux-arm64.zip + asset_content_type: application/zip diff --git a/.github/workflows/static.yml b/.github/workflows/static.yml index 92b8780d..7cc8bc67 100644 --- a/.github/workflows/static.yml +++ b/.github/workflows/static.yml @@ -1,5 +1,5 @@ # Simple workflow for deploying static content to GitHub Pages -name: Deploy Website +name: Deploy Websites on: # Runs on pushes targeting the default branch @@ -35,26 +35,53 @@ jobs: - name: Checkout uses: actions/checkout@v3 - - uses: actions/checkout@v1 - name: Get Environment Variables id: dotenv uses: falti/dotenv-action@v0.2.5 - name: Setup .NET - uses: actions/setup-dotnet@v1 + uses: actions/setup-dotnet@v3 with: - dotnet-version: "7.0.x" # SDK Version to use; x will use the latest version of the 7.0 channel - include-prerelease: true + dotnet-version: "8.0.x" # SDK Version to use; x will use the latest version of the 7.0 channel + dotnet-quality: 'preview' - - name: Build REPL + - name: Build Playground run: | - dotnet restore - dotnet publish -c Release -p:ReleaseVersion=${{ steps.dotenv.outputs.version }} - working-directory: ./Repl/ + ../scripts/install-wasi.sh + yarn install + yarn build:site + working-directory: ./playground/ - - name: Stage Repl + - name: Deploy Playground + uses: cloudflare/pages-action@v1 + with: + apiToken: ${{ secrets.CLOUDFLARE_API_TOKEN }} + accountId: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }} + projectName: bebopc-playground + directory: ${{github.workspace}}/playground/dist + # Optional: Enable this if you want to have GitHub Deployments triggered + gitHubToken: ${{ secrets.GITHUB_TOKEN }} + + + - name: Build Docs + run: | + ../scripts/install-wasi.sh + yarn install + yarn build + working-directory: ./docs/ + + - name: Deploy Docs + uses: cloudflare/pages-action@v1 + with: + apiToken: ${{ secrets.CLOUDFLARE_API_TOKEN }} + accountId: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }} + projectName: bebop-docs + directory: ${{github.workspace}}/docs/dist + # Optional: Enable this if you want to have GitHub Deployments triggered + gitHubToken: ${{ secrets.GITHUB_TOKEN }} + + - name: Stage Homepage run: | - mkdir -p homepage/repl && cp -a ./bin/repl/Release/publish/wwwroot/. homepage/repl sed 's/0.0.0/${{ steps.dotenv.outputs.version }}/' ${{env.TOOLS_ROOT}}/bash/install.sh > homepage/install.sh sed 's/0.0.0/${{ steps.dotenv.outputs.version }}/' ${{env.TOOLS_ROOT}}/ps/install.ps1 > homepage/install.ps1 diff --git a/.github/workflows/test-csharp.yml b/.github/workflows/test-csharp.yml index 2648c3b1..7c77c685 100644 --- a/.github/workflows/test-csharp.yml +++ b/.github/workflows/test-csharp.yml @@ -8,17 +8,18 @@ jobs: test-csharp: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v1 - - uses: actions/setup-dotnet@v1 + - uses: actions/checkout@v4 + - uses: actions/setup-dotnet@v3 with: dotnet-version: | 5.0.x 6.0.x 7.0.x - include-prerelease: true + 8.0.x + dotnet-quality: 'preview' - name: Build and run tests shell: bash run: | - dotnet run --project ../../Compiler/ --cs "./GeneratedTestCode/Output.g.cs" --namespace Bebop.Codegen --files $(ls -p ../Schemas/Valid/*.bop | tr '\n' ' ') + dotnet run --project ../../Compiler/ --trace -i $(ls -p ../Schemas/Valid/*.bop | tr '\n' ' ') build -g "cs:./GeneratedTestCode/Output.g.cs,namespace=Bebop.Codegen" dotnet test -nowarn:CS0618 working-directory: "./Laboratory/C#" diff --git a/.github/workflows/test-dart.yml b/.github/workflows/test-dart.yml index eed8979c..1a08aa23 100644 --- a/.github/workflows/test-dart.yml +++ b/.github/workflows/test-dart.yml @@ -9,12 +9,12 @@ jobs: test-dart: runs-on: ubuntu-22.04 steps: - - uses: actions/checkout@v1 + - uses: actions/checkout@v3 - uses: dart-lang/setup-dart@v1 - - uses: actions/setup-dotnet@v1 + - uses: actions/setup-dotnet@v3 with: - dotnet-version: "7.0.x" # SDK Version to use; x will use the latest version of the 7.0 channel - include-prerelease: true + dotnet-version: "8.0.x" # SDK Version to use; x will use the latest version of the 7.0 channel + dotnet-quality: 'preview' - name: Build and run tests run: | mkdir gen diff --git a/.github/workflows/test-rust.yml b/.github/workflows/test-rust.yml index 2a3ef726..d6e264fa 100644 --- a/.github/workflows/test-rust.yml +++ b/.github/workflows/test-rust.yml @@ -19,10 +19,10 @@ jobs: run: cargo test working-directory: ./Runtime/Rust - name: Setup .NET - uses: actions/setup-dotnet@v1 + uses: actions/setup-dotnet@v3 with: - dotnet-version: "7.0.x" # SDK Version to use; x will use the latest version of the 7.0 channel - include-prerelease: true + dotnet-version: "8.0.x" + dotnet-quality: 'preview' - name: Build Compiler run: | dotnet build Compiler diff --git a/.github/workflows/test-typescript.yml b/.github/workflows/test-typescript.yml index 21e8d333..d1a403f3 100644 --- a/.github/workflows/test-typescript.yml +++ b/.github/workflows/test-typescript.yml @@ -9,14 +9,14 @@ jobs: test-typescript: runs-on: ubuntu-22.04 steps: - - uses: actions/checkout@v1 + - uses: actions/checkout@v3 - uses: actions/setup-node@v1 with: node-version: "18.16.0" # LTS - - uses: actions/setup-dotnet@v1 + - uses: actions/setup-dotnet@v3 with: - dotnet-version: "7.0.x" # SDK Version to use; x will use the latest version of the 7.0 channel - include-prerelease: true + dotnet-version: "8.0.x" + dotnet-quality: 'preview' - name: Build Typescript runtime run: | yarn install diff --git a/.gitignore b/.gitignore index 8d75866b..5a1a2d39 100644 --- a/.gitignore +++ b/.gitignore @@ -274,3 +274,23 @@ Core/Meta/VersionInfo.cs .DS_Store .vscode +*.wasm +*.wasm.map +*.so +*.dylib + +# Test binary, built with `go test -c` +*.test + +# Output of the go coverage tool, specifically when used with LiteIDE +*.out + +# Dependency directories (remove the comment below to include it) +# vendor/ + +# Go workspace file +go.work +*.wat +.chord +dist +*.chord diff --git a/Bebop.sln b/Bebop.sln index 04743885..81fa4955 100644 --- a/Bebop.sln +++ b/Bebop.sln @@ -7,8 +7,6 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Core", "Core\Core.csproj", EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Compiler", "Compiler\Compiler.csproj", "{A509F5AB-5702-49EC-9B7F-09E8F24FC542}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Repl", "Repl\Repl.csproj", "{55D1BCCA-9580-4360-8691-959860F1DDDF}" -EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|AnyCPU = Debug|AnyCPU @@ -23,10 +21,6 @@ Global {A509F5AB-5702-49EC-9B7F-09E8F24FC542}.Debug|AnyCPU.Build.0 = Debug|AnyCPU {A509F5AB-5702-49EC-9B7F-09E8F24FC542}.Release|AnyCPU.ActiveCfg = Release|AnyCPU {A509F5AB-5702-49EC-9B7F-09E8F24FC542}.Release|AnyCPU.Build.0 = Release|AnyCPU - {55D1BCCA-9580-4360-8691-959860F1DDDF}.Debug|AnyCPU.ActiveCfg = Debug|Any CPU - {55D1BCCA-9580-4360-8691-959860F1DDDF}.Debug|AnyCPU.Build.0 = Debug|Any CPU - {55D1BCCA-9580-4360-8691-959860F1DDDF}.Release|AnyCPU.ActiveCfg = Release|Any CPU - {55D1BCCA-9580-4360-8691-959860F1DDDF}.Release|AnyCPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/Compiler/BebopCompiler.cs b/Compiler/BebopCompiler.cs index e57db4ae..2af66b0a 100644 --- a/Compiler/BebopCompiler.cs +++ b/Compiler/BebopCompiler.cs @@ -1,79 +1,76 @@ using System; using System.Collections.Generic; -using System.IO; using System.Linq; using System.Threading.Tasks; -using Core.Generators; -using Core.Logging; +using System.Threading; using Core.Meta; +using Core.Exceptions; using Core.Parser; +using Core.Generators; +using System.IO; +using Core; namespace Compiler; -public class BebopCompiler +public class BebopCompiler(CompilerHost Host) { public const int Ok = 0; public const int Err = 1; - public CommandLineFlags Flags { get; } - - public BebopCompiler(CommandLineFlags flags) - { - Flags = flags; - } - private async Task ParseAndValidateSchema(List schemaPaths, string nameSpace) + public BebopSchema ParseSchema(IEnumerable schemaPaths) { - var parser = new SchemaParser(schemaPaths, nameSpace); - var schema = await parser.Parse(); + var parser = new SchemaParser(schemaPaths, Host); + var schema = parser.Parse(); schema.Validate(); return schema; } - public async Task CompileSchema(Func makeGenerator, - List schemaPaths, - FileInfo outputFile, - string nameSpace, TempoServices services, Version? langVersion) + public static void EmitGeneratedFiles(List generatedFiles, BebopConfig config) { - if (outputFile.Directory is not null && !outputFile.Directory.Exists) + foreach (var generatedFile in generatedFiles) { - outputFile.Directory.Create(); - } - if (outputFile.Exists) - { - File.Delete(outputFile.FullName); - } + var outFile = generatedFile.Name; - var schema = await ParseAndValidateSchema(schemaPaths, nameSpace); - var result = await ReportSchemaDiagnostics(schema); - if (result == Err) return Err; - var generator = makeGenerator(schema); - generator.WriteAuxiliaryFiles(outputFile.DirectoryName ?? string.Empty); - var compiled = generator.Compile(langVersion, services: services, writeGeneratedNotice: Flags?.SkipGeneratedNotice ?? false, emitBinarySchema: Flags?.EmitBinarySchema ?? false); - await File.WriteAllTextAsync(outputFile.FullName, compiled); - return Ok; - } + // Normalize the path + if (!Path.IsPathRooted(outFile)) + { + outFile = Path.GetFullPath(Path.Combine(config.WorkingDirectory, outFile)); + } - private async Task ReportSchemaDiagnostics(BebopSchema schema) - { - var noWarn = Flags?.NoWarn ?? new List(); - var loudWarnings = schema.Warnings.Where(x => !noWarn.Contains(x.ErrorCode.ToString())); - var errors = loudWarnings.Concat(schema.Errors).ToList(); - DiagnosticLogger.Instance.WriteSpanDiagonstics(errors); - return schema.Errors.Count > 0 ? Err : Ok; + var outDirectory = Path.GetDirectoryName(outFile) ?? throw new CompilerException("Could not determine output directory."); + if (!Directory.Exists(outDirectory)) + { + Directory.CreateDirectory(outDirectory); + } + + File.WriteAllText(outFile, generatedFile.Content); + + if (generatedFile.AuxiliaryFile is not null) + { + var auxiliaryOutFile = Path.GetFullPath(Path.Combine(outDirectory, generatedFile.AuxiliaryFile.Name)); + File.WriteAllBytes(auxiliaryOutFile, generatedFile.AuxiliaryFile.Content); + } + } } - public async Task CheckSchema(string textualSchema) + + public async ValueTask BuildAsync(GeneratorConfig generatorConfig, BebopSchema schema, BebopConfig config, CancellationToken cancellationToken) { - var parser = new SchemaParser(textualSchema, "CheckNameSpace"); - var schema = await parser.Parse(); - schema.Validate(); - return await ReportSchemaDiagnostics(schema); + if (!Host.TryGetGenerator(generatorConfig.Alias, out var generator)) + { + throw new CompilerException($"Could not find generator with alias '{generatorConfig.Alias}'."); + } + + var compiled = await generator.Compile(schema, generatorConfig, cancellationToken); + var auxiliary = generator.GetAuxiliaryFile(); + return new GeneratedFile(generatorConfig.OutFile, compiled, generator.Alias, auxiliary); } - public async Task CheckSchemas(List schemaPaths) + public static (List Warnings, List Errors) GetSchemaDiagnostics(BebopSchema schema, int[] supressWarningCodes) { - var schema = await ParseAndValidateSchema(schemaPaths, "CheckNameSpace"); - return await ReportSchemaDiagnostics(schema); + var noWarn = supressWarningCodes; + var loudWarnings = schema.Warnings.Where(x => !noWarn.Contains(x.ErrorCode)).ToList(); + return (loudWarnings, schema.Errors); } } \ No newline at end of file diff --git a/Compiler/BebopConfig.cs b/Compiler/BebopConfig.cs deleted file mode 100644 index 4e9d0b79..00000000 --- a/Compiler/BebopConfig.cs +++ /dev/null @@ -1,201 +0,0 @@ -using System; -using System.Text.Json; -using System.Text.Json.Serialization; -using Core.Generators; - -namespace Compiler -{ - /// - /// A strongly typed representation of the bebop.json file. - /// - public class BebopConfig - { - /// - /// Specifies a list of code generators to target during compilation. - /// - [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] - [JsonPropertyName("generators")] - public GeneratorConfig[]? Generators { get; set; } - - /// - /// Specifies a namespace that generated code will use. - /// - [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] - [JsonPropertyName("namespace")] - [JsonConverter(typeof(MinMaxLengthCheckConverter))] - public string? Namespace { get; set; } - - /// - /// Specifies an array of filenames or patterns to compile. These filenames are resolved - /// relative to the directory containing the bebop.json file. If no 'include' property is - /// present in a bebop.json, the compiler defaults to including all files in the containing - /// directory and subdirectories except those specified by 'exclude'. - /// - [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] - [JsonPropertyName("include")] - public string[]? Include { get; set; } - - /// - /// Specifies an array of filenames or patterns that should be skipped when resolving - /// include. The 'exclude' property only affects the files included via the 'include' - /// property. - /// - [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] - [JsonPropertyName("exclude")] - public string[]? Exclude { get; set; } - - /// - /// Settings for the watch mode in bebopc. - /// - [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] - [JsonPropertyName("watchOptions")] - public WatchOptions? WatchOptions { get; set; } - - public static BebopConfig? FromJson(string json) => JsonSerializer.Deserialize(json, Settings); - - private static readonly JsonSerializerOptions Settings = new(JsonSerializerDefaults.General) - { - Converters = - { - ServicesConverter.Singleton - }, - }; - } - - public partial class GeneratorConfig - { - /// - /// Specify the code generator schemas will be compiled to. - /// - [JsonPropertyName("alias")] - public string? Alias { get; set; } - - /// - /// Specify the version of the language the code generator should target. - /// - [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] - [JsonPropertyName("langVersion")] - public string? LangVersion { get; set; } - - /// - /// Specify if the code generator should produces a notice at the start of the output file - /// stating code was auto-generated. - /// - [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] - [JsonPropertyName("noGenerationNotice")] - public bool? NoGenerationNotice { get; set; } - - - /// - /// Specify if the code generator should emit a binary schema within the output file. - /// - [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] - [JsonPropertyName("emitBinarySchema")] - public bool? EmitBinarySchema { get; set; } - - /// - /// Specify a file that bundles all generated code into one file. - /// - [JsonPropertyName("outFile")] - public string? OutFile { get; set; } - - /// - /// By default, bebopc generates a concrete client and a service base class. This property - /// can be used to limit bebopc asset generation. - /// - [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] - [JsonPropertyName("services")] - public TempoServices? Services { get; set; } - } - - /// - /// Settings for the watch mode in bebopc. - /// - public partial class WatchOptions - { - /// - /// Remove a list of directories from the watch process. - /// - [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] - [JsonPropertyName("excludeDirectories")] - public string[]? ExcludeDirectories { get; set; } - - /// - /// Remove a list of files from the watch mode's processing. - /// - [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] - [JsonPropertyName("excludeFiles")] - public string[]? ExcludeFiles { get; set; } - } - - internal class MinMaxLengthCheckConverter : JsonConverter - { - public override bool CanConvert(Type t) => t == typeof(string); - - public override string Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) - { - var value = reader.GetString(); - if (value?.Length >= 1) - { - return value; - } - throw new Exception("Cannot unmarshal type string"); - } - - public override void Write(Utf8JsonWriter writer, string value, JsonSerializerOptions options) - { - if (value.Length >= 1) - { - JsonSerializer.Serialize(writer, value, options); - return; - } - throw new Exception("Cannot marshal type string"); - } - - public static readonly MinMaxLengthCheckConverter Singleton = new MinMaxLengthCheckConverter(); - } - - internal class ServicesConverter : JsonConverter - { - public override bool CanConvert(Type t) => t == typeof(TempoServices); - - public override TempoServices Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) - { - var value = reader.GetString(); - switch (value) - { - case "both": - return TempoServices.Both; - case "client": - return TempoServices.Client; - case "none": - return TempoServices.None; - case "server": - return TempoServices.Server; - } - throw new Exception("Cannot unmarshal type Services"); - } - - public override void Write(Utf8JsonWriter writer, TempoServices value, JsonSerializerOptions options) - { - switch (value) - { - case TempoServices.Both: - JsonSerializer.Serialize(writer, "both", options); - return; - case TempoServices.Client: - JsonSerializer.Serialize(writer, "client", options); - return; - case TempoServices.None: - JsonSerializer.Serialize(writer, "none", options); - return; - case TempoServices.Server: - JsonSerializer.Serialize(writer, "server", options); - return; - } - throw new Exception("Cannot marshal type Services"); - } - - public static readonly ServicesConverter Singleton = new ServicesConverter(); - } -} \ No newline at end of file diff --git a/Compiler/CliStrings.cs b/Compiler/CliStrings.cs new file mode 100644 index 00000000..b2ce0a49 --- /dev/null +++ b/Compiler/CliStrings.cs @@ -0,0 +1,34 @@ +namespace Compiler; + +public static class CliStrings +{ + + public const string ConfigFlag = "--config"; + public const string NoEmitFlag = "--no-emit"; + public const string NoWarnFlag = "--no-warn"; + public const string GeneratorFlag = "--generator"; + public const string IncludeFlag = "--include"; + public const string ExcludeFlag = "--exclude"; + public const string DiagnosticFormatFlag = "--diagnostic-format"; + public const string ExcludeDirectoriesFlag = "--exclude-directories"; + public const string ExcludeFilesFlag = "--exclude-files"; + public const string PreserveWatchOutputFlag = "--preserve-watch-output"; + public const string InitFlag = "--init"; + public const string ListSchemasFlag = "--list-schemas-only"; + public const string LocaleFlag = "--locale"; + public const string ShowConfigFlag = "--show-config"; + public const string TraceFlag = "--trace"; + public const string StandardInputFlag = "--stdin"; + public const string StandardOutputFlag = "--stdout"; + + public const string DryRunFlag = "--dry-run"; + + public const string FromFlag = "--from"; + public const string ToFlag = "--to"; + public const string WatchCommand = "watch"; + public const string BuildCommand = "build"; + + public const string ConvertCommand = "convert"; + public const string LangServerCommand = "langserver"; + +} \ No newline at end of file diff --git a/Compiler/CommandLineFlags.cs b/Compiler/CommandLineFlags.cs deleted file mode 100644 index 6d055416..00000000 --- a/Compiler/CommandLineFlags.cs +++ /dev/null @@ -1,714 +0,0 @@ -using System; -using System.Collections; -using System.Collections.Generic; -using System.IO; -using System.Linq; -using System.Reflection; -using Core.Generators; -using Core.Logging; -using Core.Meta; -using Microsoft.Extensions.FileSystemGlobbing; - -namespace Compiler -{ - #region Records - /// - /// Represents a code generator passed to bebopc. - /// - /// The alias of the code generator. - /// The quailified file that the generator will produce. - /// The control for which service compontents will be generated. - /// If set this value defines the version of the language generated code will use. - public record CodeGenerator(string Alias, string OutputFile, TempoServices Services, Version? LangVersion); - #endregion - - #region FlagAttribute - - /// - /// Models an application command-line flag. - /// - [AttributeUsage(AttributeTargets.Property)] - public class CommandLineFlagAttribute : Attribute - { - /// - /// Creates a new command-line flag attribute - /// - /// The name of the command-line flag. - /// A detailed description of flag. - /// An example of how to use the attributed flag. - /// Indicates if a flag is used to generate code. - public CommandLineFlagAttribute(string name, - string helpText, - string usageExample = "", - bool isGeneratorFlag = false, - bool valuesRequired = true, - bool hideFromHelp = false) - { - if (string.IsNullOrWhiteSpace(name)) - { - throw new ArgumentNullException(nameof(name)); - } - if (string.IsNullOrWhiteSpace(helpText)) - { - throw new ArgumentNullException(nameof(helpText)); - } - Name = name; - HelpText = helpText; - UsageExample = usageExample; - IsGeneratorFlag = isGeneratorFlag; - ValuesRequired = valuesRequired; - HideFromHelp = hideFromHelp; - } - - - /// - /// The name command-line flag. This name is usually a single english word. - /// - /// - /// For compound words you should use a hyphen separator rather than camel casing. - /// - public string Name { get; } - - /// - /// A detailed description of the command-line flag. - /// - public string HelpText { get; } - - /// - /// If any an example of the parameter that is used in conjunction with the flag - /// - public string UsageExample { get; } - - /// - /// If this property is set to true the attributed command-line flag is used to instantiate a code generator. - /// - public bool IsGeneratorFlag { get; } - - /// - /// If this property is true, the flag requires values (arguments) to be set. - /// - public bool ValuesRequired { get; } - - /// - /// If this property is true, the command-line flag will be hidden from help output. - /// - public bool HideFromHelp { get; } - } - - #endregion - /// - /// Static extension methods for the command-line flag types. - /// - public static class CommandLineExtensions - { - /// - /// Determines if the provided contains the specified - /// - /// The collection of flags to check. - /// The name of the flag to look for. - /// true if the flag is present, otherwise false. - public static bool HasFlag(this List flags, string flagName) - { - return flags.Any(f => f.Name.Equals(flagName, StringComparison.OrdinalIgnoreCase)); - } - - /// - /// Finds the flag associated with the specified - /// - /// The collection of flags to check. - /// The name of the flag to look for. - /// An instance of the desired flag. - public static CommandLineFlag GetFlag(this List flags, string flagName) - { - return flags.Find(f => f.Name.Equals(flagName, StringComparison.OrdinalIgnoreCase))!; - } - - } - /// - /// Represents a parsed command-line flag and its values. - /// - public class CommandLineFlag - { - /// - /// Creates a new command-line flag - /// - /// The name of the flag - /// The values (if any) corresponding to the flag. - public CommandLineFlag(string name, string[] values) - { - Name = name; - Values = values; - } - /// - /// Indicates if the current flag has any values assigned to it. - /// - /// true if the flag has values, otherwise false. - public bool HasValues() => Values is { Length: > 0 }; - - /// - /// Returns the first value associated with the current flag. - /// - /// - public string? GetValue() - { - return Values.FirstOrDefault()?.Trim(); - } - - /// - /// A collection of all values associated with the current flag. - /// - public string[] Values { get; init; } - /// - /// The name of the current flag. - /// - public string Name { get; init; } - } - - /// - /// A class for constructing and parsing all available commands. - /// - public class CommandLineFlags - { - /// - /// The name of the config file used by bebopc. - /// - private const string ConfigFileName = "bebop.json"; - - [CommandLineFlag("config", "Initializes the compiler from the specified configuration file.", - "--config bebop.json")] - public string? ConfigFile { get; private set; } - - [CommandLineFlag("cpp", "Generate C++ source code to the specified file", - "--cpp ./my/output/HelloWorld.hpp", true)] - public string? CPlusPlusOutput { get; private set; } - - [CommandLineFlag("cs", "Generate C# source code to the specified file", "--cs ./my/output/HelloWorld.cs", - true)] - public string? CSharpOutput { get; private set; } - - [CommandLineFlag("dart", "Generate Dart source code to the specified file", - "--dart ./my/output/HelloWorld.dart", true)] - public string? DartOutput { get; private set; } - - [CommandLineFlag("rust", "Generate Rust source code to the specified file", "--rust ./my/output/HelloWorld.rs", - true)] - public string? RustOutput { get; private set; } - - [CommandLineFlag("ts", "Generate TypeScript source code to the specified file", - "--ts ./my/output/HelloWorld.ts", true)] - public string? TypeScriptOutput { get; private set; } - - [CommandLineFlag("py", "Generate Python source code to the specified file", - "--py ./my/output/HelloWorld.py", true)] - public string? PythonOutput { get; private set; } - - [CommandLineFlag("namespace", "When this option is specified generated code will use namespaces", - "--cs --namespace [package]")] - public string? Namespace { get; private set; } - - [CommandLineFlag("skip-generated-notice", "Flag to disable generating the file header announcing the file was autogenerated by bebop.", "--rust --skip-generation-notice", valuesRequired: false)] - public bool SkipGeneratedNotice { get; private set; } - - [CommandLineFlag("emit-binary-schema", "Flag to enable experimental binary schemas in generated code.", "--ts --emit-binary-schema", valuesRequired: false)] - public bool EmitBinarySchema { get; private set; } - - [CommandLineFlag("dir", "Parse and generate code from a directory of schemas", "--ts --dir [input dir]")] - public string? SchemaDirectory { get; private set; } - - [CommandLineFlag("files", "Parse and generate code from a list of schemas", "--files [file1] [file2] ...")] - public List? SchemaFiles { get; private set; } - - [CommandLineFlag("check", "Checks that the provided schema files are valid, or entire project defined by bebop.json if no files provided", "--check [file.bop] [file2.bop] ...", false, false)] - public List? CheckSchemaFiles { get; private set; } - - [CommandLineFlag("check-schema", "Reads a schema from stdin and validates it.", "--check-schema < [schema text]")] - public string? CheckSchemaFile { get; private set; } - - /// - /// When set to true the process will output the product version and exit with a zero return code. - /// - [CommandLineFlag("version", "Show version info and exit.", "--version")] - public bool Version { get; private set; } - - /// - /// When set to true the process will output the and exit with a zero return code. - /// - [CommandLineFlag("help", "Show this text and exit.", "--help")] - public bool Help { get; private set; } - - [CommandLineFlag("langserv", "Starts the language server", "--langserv", hideFromHelp: true)] - public bool LanguageServer { get; private set; } - - [CommandLineFlag("debug", "Waits for a debugger to attach", "--debug", hideFromHelp: true)] - public bool Debug { get; private set; } - - [CommandLineFlag("watch", "Watches schemas for changes and regenerates code", "--watch")] - public bool Watch { get; private set; } - - [CommandLineFlag("watch-excluded-directories", "Directories which willbe excluded from the watch process. Supports globs.", "--watch-excluded-directories [directory1] [directory2] ...")] - public List? WatchExcludeDirectories { get; private set; } - - [CommandLineFlag("watch-excluded-files", "Files which willbe excluded from the watch process. Supports globs.", "--watch-excluded-files [file1] [file2] ...")] - public List? WatchExcludeFiles { get; private set; } - - [CommandLineFlag("preserve-watch-output", "Whether to keep outdated console output in watch mode instead of clearing the screen every time a change happened.", "--watch --preserve-watch-output")] - public bool PreserveWatchOutput { get; private set; } - - /// - /// Controls how loggers format data. - /// - [CommandLineFlag("log-format", "Defines the formatter that will be used with logging.", - "--log-format (structured|msbuild|json)")] - public LogFormatter LogFormatter { get; private set; } - - /// - /// An optional flag to set the version of a language a code generator will use. - /// - [CommandLineFlag("cs-version", "Defines the C# language version the C# generator will target.", - "--cs ./my/output/HelloWorld.cs --cs-version (9.0|8.0)")] - public Version? CSharpVersion { get; private set; } - - [CommandLineFlag("no-warn", "Disable a list of warning codes", "--no-warn 200 201 202")] - public List? NoWarn { get; private set; } - - public string HelpText { get; } - - public string WorkingDirectory { get; private set; } - - private readonly Dictionary ServiceConfigs = new Dictionary() - { - ["cpp"] = TempoServices.Both, - ["cs"] = TempoServices.Both, - ["dart"] = TempoServices.Both, - ["rust"] = TempoServices.Both, - ["ts"] = TempoServices.Both, - ["py"] = TempoServices.Both - }; - - /// - /// Finds a language version flag set for a generator. - /// - private Version? GetGeneratorVersion(CommandLineFlagAttribute attribute) - { - foreach (var flag in GetFlagAttributes()) - { - if ($"{attribute.Name}-version".Equals(flag.Attribute.Name, StringComparison.OrdinalIgnoreCase) && flag.Property.GetValue(this) is Version value) - { - return value; - } - } - return null; - } - - /// - /// Returns the alias and output file of all command-line specified code generators. - /// - public IEnumerable GetParsedGenerators() - { - foreach (var flag in GetFlagAttributes()) - { - if (flag.Attribute.IsGeneratorFlag && flag.Property.GetValue(this) is string value) - { - yield return new CodeGenerator(flag.Attribute.Name, value, ServiceConfigs[flag.Attribute.Name], GetGeneratorVersion(flag.Attribute)); - } - } - } - - #region Static - - /// - /// Walks all properties in and maps them to their assigned - /// - /// - /// - private static List<(PropertyInfo Property, CommandLineFlagAttribute Attribute)> GetFlagAttributes() - { - return (from p in typeof(CommandLineFlags).GetProperties() - let attr = p.GetCustomAttributes(typeof(CommandLineFlagAttribute), true) - where attr.Length == 1 - select (p, attr.First() as CommandLineFlagAttribute)) - .Select(t => ((PropertyInfo, CommandLineFlagAttribute))t) - .ToList(); - } - - /// - /// Hide the constructor to prevent direct initializationW - /// - private CommandLineFlags(string helpText) - { - HelpText = helpText; - } - - /// - /// Searches recursively upward to locate the config file belonging to . - /// - /// The fully qualified path to the config file, or null if not found. - public static string? FindBebopConfig() - { - var workingDirectory = Directory.GetCurrentDirectory(); - var configFile = Directory.GetFiles(workingDirectory, ConfigFileName).FirstOrDefault(); - while (string.IsNullOrWhiteSpace(configFile)) - { - if (Directory.GetParent(workingDirectory) is not { Exists: true } parent) - { - break; - } - workingDirectory = parent.FullName; - if (parent.GetFiles(ConfigFileName)?.FirstOrDefault() is { Exists: true } file) - { - configFile = file.FullName; - } - } - return configFile; - } - - #endregion - - #region Parsing - - /// - /// Parses an array of command-line flags into dictionary. - /// - /// The flags to be parsed. - /// A dictionary containing all parsed flags and their value if any. - private static List GetFlags(string[] args) - { - var flags = new List(); - foreach (var token in args) - { - if (token.StartsWith("--")) - { - var key = new string(token.SkipWhile(c => c == '-').ToArray()).ToLowerInvariant(); - var value = args.SkipWhile(i => i != $"--{key}").Skip(1).TakeWhile(i => !i.StartsWith("--")).ToArray(); - - flags.Add(new CommandLineFlag(key, value)); - } - } - return flags; - } - - /// - /// Attempts to find the flag and parse its value. - /// - /// The command-line arguments to sort through - /// - /// If the flag was present an had a valid value, that enum member will be returned. - /// Otherwise the default formatter is used. - /// - public static LogFormatter FindLogFormatter(string[] args) - { - var flags = GetFlags(args); - foreach (var flag in flags) - { - if (flag.Name.Equals("log-format", StringComparison.OrdinalIgnoreCase) && - Enum.TryParse(flag.GetValue(), true, out var parsedEnum)) - { - return parsedEnum; - } - } - return LogFormatter.Enhanced; - } - - - /// - /// Attempts to parse command-line flags into a instance - /// - /// the array of arguments to parse - /// An instance which contains all parsed flags and their values - /// A human-friendly message describing why parsing failed. - /// - /// If the provided - /// - /// were parsed this method returns true. - /// - public static bool TryParse(string[] args, out CommandLineFlags flagStore, out string errorMessage) - { - errorMessage = string.Empty; - var props = GetFlagAttributes(); - - var stringBuilder = new IndentedStringBuilder(); - - stringBuilder.AppendLine("Usage:"); - stringBuilder.Indent(4); - foreach (var prop in props.Where(prop => !string.IsNullOrWhiteSpace(prop.Attribute.UsageExample))) - { - stringBuilder.AppendLine($"{ReservedWords.CompilerName} {prop.Attribute.UsageExample}"); - } - stringBuilder.Dedent(4); - - stringBuilder.AppendLine(string.Empty); - stringBuilder.AppendLine(string.Empty); - stringBuilder.AppendLine("Options:"); - stringBuilder.Indent(4); - foreach (var prop in props.Where(p => !p.Attribute.HideFromHelp)) - { - stringBuilder.AppendLine($"--{prop.Attribute.Name} {prop.Attribute.HelpText}"); - } - - flagStore = new CommandLineFlags(stringBuilder.ToString()); - - var parsedFlags = GetFlags(args); - - // prevent the user from passing both --langserv and --config - // and also for the compiler trying to find a config file when running as a language server - if (parsedFlags.HasFlag("langserv")) - { - flagStore.LanguageServer = true; - return true; - } - - - string? configPath; - if (parsedFlags.HasFlag("config")) - { - configPath = parsedFlags.GetFlag("config").GetValue(); - if (string.IsNullOrWhiteSpace(configPath)) - { - errorMessage = $"'--config' must be followed by the explicit path to a bebop.json config"; - return false; - } - } - else - { - configPath = FindBebopConfig(); - } - - var rootDirectory = !string.IsNullOrWhiteSpace(configPath) ? Path.GetDirectoryName(configPath) : Directory.GetCurrentDirectory(); - if (string.IsNullOrWhiteSpace(rootDirectory)) - { - errorMessage = "Failed to determine the working directory."; - return false; - } - - flagStore.WorkingDirectory = rootDirectory; - - - var parsedConfig = false; - // always parse the config, we'll override any values with command-line flags - if (!string.IsNullOrWhiteSpace(configPath)) - { - if (!new FileInfo(configPath).Exists) - { - errorMessage = $"Bebop configuration file not found at '{configPath}'"; - return false; - } - parsedConfig = TryParseConfig(flagStore, configPath); - if (!parsedConfig) - { - errorMessage = $"Failed to parse Bebop configuration file at '{configPath}'"; - return false; - } - } - - // if we didn't parse a 'bebop.json' and no flags were passed, we can't continue. - if (!parsedConfig && parsedFlags.Count == 0) - { - errorMessage = "No command-line flags found."; - return false; - } - - if (parsedFlags.HasFlag("help")) - { - flagStore.Help = true; - return true; - } - - if (parsedFlags.HasFlag("version")) - { - flagStore.Version = true; - return true; - } - - - var validFlagNames = props.Select(p => p.Attribute.Name).ToHashSet(); - if (parsedFlags.Find(x => !validFlagNames.Contains(x.Name)) is CommandLineFlag unrecognizedFlag) - { - errorMessage = $"Unrecognized flag: --{unrecognizedFlag.Name}"; - return false; - } - - // parse all present command-line flags - // any flag on the command-line that was also present in bebop.json will be overwritten. - foreach (var flag in props) - { - if (!parsedFlags.HasFlag(flag.Attribute.Name)) - { - continue; - } - - var parsedFlag = parsedFlags.GetFlag(flag.Attribute.Name); - var propertyType = flag.Property.PropertyType; - if (flag.Attribute.Name.Equals("check-schema")) - { - using var reader = new StreamReader(Console.OpenStandardInput()); - flagStore.CheckSchemaFile = reader.ReadToEnd(); - continue; - } - if (propertyType == typeof(bool)) - { - flag.Property.SetValue(flagStore, true); - continue; - } - if (flag.Attribute.ValuesRequired && !parsedFlag.HasValues()) - { - errorMessage = $"command-line flag '{flag.Attribute.Name}' was not assigned any values."; - return false; - } - if (propertyType.IsGenericType && propertyType.GetGenericTypeDefinition() == typeof(List<>)) - { - Type itemType = propertyType.GetGenericArguments()[0]; - if (!(Activator.CreateInstance(typeof(List<>).MakeGenericType(itemType)) is IList genericList)) - { - errorMessage = $"Failed to activate '{flag.Property.Name}'."; - return false; - } - - if (flag.Attribute.Name.Equals("watch-excluded-directories") || flag.Attribute.Name.Equals("watch-excluded-files")) - { - var excluded = FindFiles(rootDirectory, parsedFlag.Values, Array.Empty()); - flag.Property.SetValue(flagStore, excluded, null); - } - else - { - // file paths wrapped in quotes may contain spaces. - foreach (var item in parsedFlag.Values) - { - if (string.IsNullOrWhiteSpace(item)) - { - continue; - } - // remove double quotes from the string so file paths can be parsed properly. - genericList.Add(Convert.ChangeType(item.Trim(), itemType)); - } - flag.Property.SetValue(flagStore, genericList, null); - } - } - else if (propertyType.IsEnum) - { - if (!Enum.TryParse(propertyType, parsedFlag.GetValue(), true, out var parsedEnum)) - { - errorMessage = $"Failed to parse '{parsedFlag.GetValue()}' into a member of '{propertyType}'."; - return false; - } - flag.Property.SetValue(flagStore, parsedEnum, null); - } - else if (propertyType == typeof(Version)) - { - if (System.Version.TryParse(parsedFlag.GetValue(), out var version) && version is Version) - { - flag.Property.SetValue(flagStore, version, null); - } - } - else - { - flag.Property.SetValue(flagStore, Convert.ChangeType(parsedFlag.GetValue(), flag.Property.PropertyType), - null); - } - } - errorMessage = string.Empty; - return true; - } - - - private static List FindFiles(string rootDirectory, string[] includes, string[] excludes) - { - var matcher = new Matcher(); - matcher.AddIncludePatterns(includes); - matcher.AddExcludePatterns(excludes); - IEnumerable matchingFiles = matcher.GetResultsInFullPath(rootDirectory); - return matchingFiles.ToList(); - } - - /// - /// Parses the bebop config file and assigns entries to their corresponding command-line flag. - /// - /// A instance. - /// The fully qualified path to the bebop config file, or null to trigger searching. - /// true if the config could be parsed without error, otherwise false. - private static bool TryParseConfig(CommandLineFlags flagStore, string? configPath) - { - if (string.IsNullOrWhiteSpace(configPath)) - { - return false; - } - var configFile = new FileInfo(configPath); - if (!configFile.Exists) - { - return false; - } - var configDirectory = configFile.DirectoryName; - if (string.IsNullOrWhiteSpace(configDirectory)) - { - return false; - } - - var bebopConfig = BebopConfig.FromJson(File.ReadAllText(configPath)); - if (bebopConfig is null) - { - return false; - } - - const string defaultIncludeGlob = "**/*.bop"; - var includeGlob = bebopConfig.Include ?? new string[] { defaultIncludeGlob }; - var excludeGlob = bebopConfig.Exclude ?? Array.Empty(); - flagStore.SchemaFiles = FindFiles(configDirectory, includeGlob, excludeGlob); - flagStore.Namespace = bebopConfig.Namespace; - if (bebopConfig.Generators is null) - { - return false; - } - foreach (var generator in bebopConfig.Generators) - { - if (generator.Alias is null || generator.OutFile is null) - { - return false; - } - foreach (var flagAttribute in GetFlagAttributes() - .Where(flagAttribute => flagAttribute.Attribute.IsGeneratorFlag && - flagAttribute.Attribute.Name.Equals(generator.Alias))) - { - flagAttribute.Property.SetValue(flagStore, Path.GetFullPath(Path.Combine(configDirectory, generator.OutFile))); - if (generator.Services.HasValue) - { - flagStore.ServiceConfigs[generator.Alias] = generator.Services.Value; - } - else - { - flagStore.ServiceConfigs[generator.Alias] = TempoServices.Both; - } - if (generator.LangVersion is not null && System.Version.TryParse(generator.LangVersion, out var version)) - { - foreach (var flag in GetFlagAttributes()) - { - if ($"{flagAttribute.Attribute.Name}-version".Equals(flag.Attribute.Name, StringComparison.OrdinalIgnoreCase)) - { - flag.Property.SetValue(flagStore, version, null); - } - } - } - if (generator.NoGenerationNotice is not null && generator.NoGenerationNotice.Value is true) { - flagStore.SkipGeneratedNotice = true; - } - if (generator.EmitBinarySchema is not null && generator.EmitBinarySchema is true) { - flagStore.EmitBinarySchema = true; - } - } - } - - if (bebopConfig.WatchOptions is not null) - { - if (bebopConfig.WatchOptions.ExcludeDirectories is not null) - { - flagStore.WatchExcludeDirectories = bebopConfig.WatchOptions.ExcludeDirectories.ToList(); - } - if (bebopConfig.WatchOptions.ExcludeFiles is not null) - { - flagStore.WatchExcludeFiles = bebopConfig.WatchOptions.ExcludeFiles.ToList(); - } - } - return true; - } - } - - #endregion -} \ No newline at end of file diff --git a/Compiler/Commands/BuildCommand.cs b/Compiler/Commands/BuildCommand.cs new file mode 100644 index 00000000..0e728c08 --- /dev/null +++ b/Compiler/Commands/BuildCommand.cs @@ -0,0 +1,114 @@ +using System; +using System.Collections.Generic; +using System.CommandLine; +using System.IO; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using Core; +using Core.Exceptions; +using Core.Logging; +using Core.Meta; + +namespace Compiler.Commands; + +public class BuildCommand : CliCommand +{ + public BuildCommand() : base(CliStrings.BuildCommand, "Build schemas into one or more target languages.") + { + SetAction(HandleCommandAsync); + } + + private async Task HandleCommandAsync(ParseResult result, CancellationToken cancellationToken) + { + var config = result.GetValue(CliStrings.ConfigFlag)!; + + config.Validate(); + + using var host = CompilerHost.CompilerHostBuilder.Create(config.WorkingDirectory) + .WithDefaults() +#if !WASI_WASM_BUILD + .WithExtensions(config.Extensions) +#endif + .Build(); + + var compiler = new BebopCompiler(host); + Helpers.WriteHostInfo(host); + BebopSchema schema = default; + string? tempFilePath = null; + try + { + // if input is redirected, read from stdin, ignore includes. + // this is a temporary flag as it appears there is a bug in wasi builds + // that always returns true for Console.IsInputRedirected + if (result.GetValue(CliStrings.StandardInputFlag)) + { + // we should write the textual stream to a temp file and then parse it. + tempFilePath = Helpers.GetTempFileName(); + using var fs = File.Open(tempFilePath, FileMode.OpenOrCreate, FileAccess.ReadWrite, FileShare.ReadWrite); + using var standardInput = Console.OpenStandardInput(); + // dont use async as wasi currently has threading issues +#if !WASI_WASM_BUILD + await standardInput.CopyToAsync(fs, cancellationToken); +#else + standardInput.CopyTo(fs); +#endif + + fs.Seek(0, SeekOrigin.Begin); + schema = compiler.ParseSchema([tempFilePath]); + } + else + { + var resolvedSchemas = config.ResolveIncludes(); + if (!resolvedSchemas.Any()) + { + return DiagnosticLogger.Instance.WriteDiagonstic(new CompilerException("No input files specified.")); + } + schema = compiler.ParseSchema(resolvedSchemas); + } + + if (config is { Generators.Length: <= 0 } && !config.NoEmit) + { + return DiagnosticLogger.Instance.WriteDiagonstic(new CompilerException("No code generators specified.")); + } + var isStandardOut = result.GetValue(CliStrings.StandardOutputFlag); + var (Warnings, Errors) = BebopCompiler.GetSchemaDiagnostics(schema, config.SupressedWarningCodes); + if (config.NoEmit || Errors.Count != 0) + { + DiagnosticLogger.Instance.WriteSpanDiagonstics([.. Warnings, .. Errors]); + return Errors.Count != 0 ? BebopCompiler.Err : BebopCompiler.Ok; + } + // we only want to write the warnings if we are not writing results to stdout + // as we concat the outputs of all generators into one stream + if (!isStandardOut && Warnings.Count != 0) + { + DiagnosticLogger.Instance.WriteSpanDiagonstics(Warnings); + } + var generatedFiles = new List(); + foreach (var generatorConfig in config.Generators) + { + generatedFiles.Add(await compiler.BuildAsync(generatorConfig, schema, config, cancellationToken)); + } + if (isStandardOut) + { + DiagnosticLogger.Instance.PrintCompilerOutput(new CompilerOutput(Warnings, Errors, [.. generatedFiles])); + } + else + { + BebopCompiler.EmitGeneratedFiles(generatedFiles, config); + } + return BebopCompiler.Ok; + } + catch (Exception ex) + { + return DiagnosticLogger.Instance.WriteDiagonstic(ex); + } + finally + { + if (tempFilePath is not null) + { + File.Delete(tempFilePath); + } + } + } +} \ No newline at end of file diff --git a/Compiler/Commands/ConvertCommand.cs b/Compiler/Commands/ConvertCommand.cs new file mode 100644 index 00000000..e2bdf643 --- /dev/null +++ b/Compiler/Commands/ConvertCommand.cs @@ -0,0 +1,132 @@ +using System; +using System.Collections.Generic; +using System.CommandLine; +using System.IO; +using System.Text.RegularExpressions; +using Core.Logging; +using Core.Meta; +using Spectre.Console; + +namespace Compiler.Commands; + +public partial class ConvertCommand : CliCommand +{ + + public ConvertCommand() : base(CliStrings.ConvertCommand, "Convert schema files from one format to another.") + { + SetAction(HandleCommand); + } + + private int HandleCommand(ParseResult result) + { + var config = result.GetValue(CliStrings.ConfigFlag)!; + config.Validate(); + var schemas = config.ResolveIncludes(); + if (result.GetValue(CliStrings.FromFlag) is "v2" && result.GetValue(CliStrings.ToFlag) is "v3") + { + var isDryRun = result.GetValue(CliStrings.DryRunFlag); + if (isDryRun) + { + DiagnosticLogger.Instance.Out.MarkupLine("[yellow]Dry run mode enabled. No files will be written.[/]"); + } + foreach (var (schemaPath, originalSchema, transformedSchema) in TwoToThreeConverter.Convert(schemas)) + { + ShowDiff(schemaPath, originalSchema, transformedSchema); + if (!isDryRun) + { + File.WriteAllText(schemaPath, transformedSchema); + } + } + return 0; + } + DiagnosticLogger.Instance.Error.MarkupLine("[red]Only conversion from v2 to v3 is currently supported.[/]"); + return 1; + } + + private static readonly string[] separators = ["\r\n", "\r", "\n"]; + + internal static void ShowDiff(string schemaPath, string originalContent, string transformedSchema) + { + string[] originalLines = originalContent.Split(separators, StringSplitOptions.None); + string[] transformedLines = transformedSchema.Split(separators, StringSplitOptions.None); + + var panelContent = new List(); + int maxLines = Math.Max(originalLines.Length, transformedLines.Length); + + for (int i = 0; i < maxLines; i++) + { + string originalLine = i < originalLines.Length ? originalLines[i] : string.Empty; + string modifiedLine = i < transformedLines.Length ? transformedLines[i] : string.Empty; + + if (originalLine != modifiedLine) + { + if (!string.IsNullOrEmpty(originalLine)) + { + panelContent.Add($"[yellow]- {originalLine.EscapeMarkup()}[/]"); + } + if (!string.IsNullOrEmpty(modifiedLine)) + { + panelContent.Add($"[blue]+ {modifiedLine.EscapeMarkup()}[/]"); + } + } + else + { + panelContent.Add($"{originalLine.EscapeMarkup()}"); + } + } + + + + + // Render in a panel + var panel = new Panel(string.Join(Environment.NewLine, panelContent)) + .Header($"[underline white][bold]{schemaPath}[/][/]") + .Expand() + + .Border(BoxBorder.Heavy); + + + + DiagnosticLogger.Instance.Out.Write(panel); + } + + + internal static partial class TwoToThreeConverter + { + public static IEnumerable<(string SchemaPath, string OriginalSchema, string TransformedSchema)> Convert(IEnumerable schemas) + { + + foreach (var schemaPath in schemas) + { + + var schema = File.ReadAllText(schemaPath); + var wasModified = false; + var transformedContent = AttributePattern().Replace(schema, match => + { + wasModified = true; + string identifier = match.Groups[1].Value; + string? value = match.Groups[2].Success ? match.Groups[2].Value : null; + // Handling for cases with and without value + return value != null ? $"@{identifier}({value})" : $"@{identifier}"; + }); + transformedContent = ReadOnlyPattern().Replace(transformedContent, match => + { + wasModified = true; + // Remove 'readonly' if present, otherwise append 'mut' + return match.Groups[1].Success ? $"struct {match.Groups[2].Value}" : $"mut struct {match.Groups[2].Value}"; + }); + if (wasModified) + { + yield return (schemaPath, schema, transformedContent); + } + } + } + + [GeneratedRegex(@"\[([\w]+)(?:\(([^,\]]*?)\))?\](?![\],])")] + private static partial Regex AttributePattern(); + [GeneratedRegex(@"(? HandleCommandAsync(ParseResult result, CancellationToken token) + { +#if !WASI_WASM_BUILD + Dictionary extensions = []; + var config = result.GetValue(CliStrings.ConfigFlag)!; + extensions = config.Extensions; + using var host = CompilerHost.CompilerHostBuilder.Create(config.WorkingDirectory).WithDefaultDecorators().WithExtensions(extensions).Build(); + await BebopLangServer.RunAsync(host, token); +#endif + return BebopCompiler.Ok; + } +} \ No newline at end of file diff --git a/Compiler/Commands/RootCommand.cs b/Compiler/Commands/RootCommand.cs new file mode 100644 index 00000000..1eede3a6 --- /dev/null +++ b/Compiler/Commands/RootCommand.cs @@ -0,0 +1,85 @@ +using System; +using System.CommandLine; +using System.IO; +using Core.Logging; +using Core.Meta; +using Spectre.Console; +using Spectre.Console.Json; + +namespace Compiler.Commands; + +public class RootCommand +{ + + public static int HandleCommand(ParseResult result) + { + var config = result.GetValue(CliStrings.ConfigFlag)!; + if (result.GetValue(CliStrings.InitFlag) is true) + { + return InitProject(); + } + if (result.GetValue(CliStrings.ListSchemasFlag) is true) + { + return ListSchemas(config); + } + if (result.GetValue(CliStrings.ShowConfigFlag) is true) + { + return ShowConfig(config); + } + return 0; + } + + /// + /// Shows the current configuration and stops processing. + /// + /// The bebop configuration object. + /// An integer representing the status of the operation. + private static int ShowConfig(BebopConfig config) + { + var json = new JsonText(config.ToJson()); + DiagnosticLogger.Instance.Out.Write(json); + DiagnosticLogger.Instance.Out.WriteLine(); + return 0; + } + + /// + /// Lists all schemas defined in the configuration. + /// + /// The bebop configuration object. + /// An integer representing the status of the operation. + private static int ListSchemas(BebopConfig config) + { + foreach (var schema in config.ResolveIncludes()) + { + DiagnosticLogger.Instance.WriteLine(schema); + } + return 0; + } + + /// + /// Initializes a new project with a default configuration. + /// + /// The bebop configuration object to initialize the project with. + /// An integer representing the status of the operation. + private static int InitProject() + { + try + { + var workingDirectory = Directory.GetCurrentDirectory(); + var configPath = Path.GetFullPath(Path.Combine(workingDirectory, BebopConfig.ConfigFileName)); + if (File.Exists(configPath)) + { + DiagnosticLogger.Instance.Error.MarkupLine($"[maroon]{BebopConfig.ConfigFileName} already exists in the current directory.[/]"); + return 1; + } + File.WriteAllText(configPath, BebopConfig.Default.ToJson()); + } + catch (Exception ex) + { + DiagnosticLogger.Instance.Error.WriteException(ex); + return 1; + } + + return 0; + } +} \ No newline at end of file diff --git a/Compiler/Commands/WatchCommand.cs b/Compiler/Commands/WatchCommand.cs new file mode 100644 index 00000000..220264ca --- /dev/null +++ b/Compiler/Commands/WatchCommand.cs @@ -0,0 +1,32 @@ +using System.CommandLine; +using System.Threading; +using System.Threading.Tasks; +using Core; +using Core.Meta; + +namespace Compiler.Commands; + +public class WatchCommand : CliCommand +{ + public WatchCommand() : base(CliStrings.WatchCommand, "Watch input files.") + { + SetAction(HandleCommandAsync); + } + + private async Task HandleCommandAsync(ParseResult result, CancellationToken token) + { + var config = result.GetValue(CliStrings.ConfigFlag)!; + config.Validate(); + using var host = CompilerHost.CompilerHostBuilder.Create(config.WorkingDirectory) + .WithDefaults() +#if !WASI_WASM_BUILD + .WithExtensions(config.Extensions) +#endif + .Build(); + + Helpers.WriteHostInfo(host); + var compiler = new BebopCompiler(host); + var watcher = new SchemaWatcher(config.WorkingDirectory, config, compiler); + return await watcher.StartAsync(token); + } +} \ No newline at end of file diff --git a/Compiler/Compiler.csproj b/Compiler/Compiler.csproj index 4e1364ee..8a359e3b 100644 --- a/Compiler/Compiler.csproj +++ b/Compiler/Compiler.csproj @@ -3,9 +3,9 @@ true Exe - net7.0 + net8.0 enable - 11.0 + preview bebopc $(ReleaseVersion) 0.0.1 @@ -18,14 +18,21 @@ ../bin/compiler/$(Configuration)/publish/$(RuntimeIdentifier) AnyCPU Debug;Release + false false - true + true + + + + - + - + + + @@ -41,14 +48,28 @@ false true true + false + false + false + false + false + false + true + false + true + true + true - - - + + + + $(DefineConstants);WASI_WASM_BUILD + + \ No newline at end of file diff --git a/Compiler/Compiler.sln b/Compiler/Compiler.sln new file mode 100644 index 00000000..40f2bea4 --- /dev/null +++ b/Compiler/Compiler.sln @@ -0,0 +1,25 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 17 +VisualStudioVersion = 17.5.002.0 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Compiler", "Compiler.csproj", "{6ACD7520-B667-4633-95DE-3CB2C252005A}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {6ACD7520-B667-4633-95DE-3CB2C252005A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {6ACD7520-B667-4633-95DE-3CB2C252005A}.Debug|Any CPU.Build.0 = Debug|Any CPU + {6ACD7520-B667-4633-95DE-3CB2C252005A}.Release|Any CPU.ActiveCfg = Release|Any CPU + {6ACD7520-B667-4633-95DE-3CB2C252005A}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {938C961D-5B5E-4954-AE3D-6C9AD2769225} + EndGlobalSection +EndGlobal diff --git a/Compiler/Helpers.cs b/Compiler/Helpers.cs index 760e44b6..df9ca5db 100644 --- a/Compiler/Helpers.cs +++ b/Compiler/Helpers.cs @@ -1,7 +1,116 @@ +using System; +using System.CommandLine; +using System.IO; +using System.Linq; +using Core; + +#if WASI_WASM_BUILD +using Core.Exceptions; +#endif +using Core.Generators; +using Core.Logging; +using Core.Meta; +using Spectre.Console; + namespace Compiler; public static class Helpers { + + /// + /// Creates a uniquely named, zero-byte temporary file on disk and returns the full path of that file. + /// + /// + public static string GetTempFileName() + { +#if WASI_WASM_BUILD + const string tempDirectory = "/tmp"; + try + { + if (!Directory.Exists(tempDirectory)) + { + Directory.CreateDirectory(tempDirectory); + } + var tempFileName = Path.Combine(tempDirectory, Path.GetRandomFileName()); + File.Create(tempFileName).Dispose(); + return tempFileName; + } + catch (Exception ex) + { + throw new CompilerException($"Could not create temporary file in {tempDirectory}.", ex); + } +#else + return Path.GetTempFileName(); +#endif + } + + /// + /// Merges the results of a bebopc command line parse into the bebop.json config instance. + /// + /// + /// When options are supplied on the command line, the corresponding bebop.json fields will be ignored. + /// + /// The parsed commandline. + public static void MergeConfig(ParseResult parseResults, BebopConfig config) + { + if (parseResults.GetValue(CliStrings.IncludeFlag) is { Length: > 0 } includes) + { + config.Includes = includes; + } + if (parseResults.GetValue(CliStrings.ExcludeFlag) is { Length: > 0 } excludes) + { + config.Excludes = excludes; + } +#if !WASI_WASM_BUILD + if (parseResults.GetValue(CliStrings.ExcludeDirectoriesFlag) is { Length: > 0 } watchExcludeDirectories) + { + config.WatchOptions.ExcludeDirectories = watchExcludeDirectories; + } + if (parseResults.GetValue(CliStrings.ExcludeFilesFlag) is { Length: > 0 } watchExcludeFiles) + { + config.WatchOptions.ExcludeFiles = watchExcludeFiles; + } + if (parseResults.GetValue(CliStrings.PreserveWatchOutputFlag) is true) + { + config.WatchOptions.PreserveWatchOutput = true; + } +#endif + if (parseResults.GetValue(CliStrings.NoWarnFlag) is { Length: > 0 } noWarn) + { + config.SupressedWarningCodes = noWarn; + } + if (parseResults.GetValue(CliStrings.NoEmitFlag) is true) + { + config.NoEmit = true; + } + if (parseResults.GetValue(CliStrings.GeneratorFlag) is { Length: > 0 } generators) + { + config.Generators = generators; + } + } + + public static void WriteHostInfo(CompilerHost host) + { + if (host.Extensions.Any()) + { + DiagnosticLogger.Instance.Out.MarkupLine("[yellow]Using extensions defined in bebop.json[/]"); + DiagnosticLogger.Instance.Out.MarkupLine("[blue]- Extensions:[/]"); + foreach (var extension in host.Extensions) + { + DiagnosticLogger.Instance.Out.MarkupLine($" - [white]{extension.Name}[/]: [green]{extension.Version}[/]"); + } + } + if (host.EnvironmentVariableStore.DevVarsCount > 0) + { + DiagnosticLogger.Instance.Out.MarkupLine("[yellow]Using vars defined in .dev.vars[/]"); + DiagnosticLogger.Instance.Out.MarkupLine("[blue]- Vars:[/]"); + foreach (var name in host.EnvironmentVariableStore.DevVarNames) + { + DiagnosticLogger.Instance.Out.MarkupLine($" - [white]{name}[/]: [green](hidden)[/]"); + } + } + } + public static int ProcessId { get @@ -15,4 +124,4 @@ public static int ProcessId } } private static int? _processId; -} \ No newline at end of file +} diff --git a/Compiler/LangServer/BebopLangServer.cs b/Compiler/LangServer/BebopLangServer.cs index 363d42e9..c0c72ff0 100644 --- a/Compiler/LangServer/BebopLangServer.cs +++ b/Compiler/LangServer/BebopLangServer.cs @@ -1,5 +1,7 @@ using System; +using System.Threading; using System.Threading.Tasks; +using Core; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; using OmniSharp.Extensions.LanguageServer.Server; @@ -9,8 +11,9 @@ namespace Compiler.LangServer { internal sealed class BebopLangServer { - public static async Task RunAsync() + public static async Task RunAsync(CompilerHost host, CancellationToken cancellationToken) { + var server = await OmnisharpLanguageServer.From(options => options .WithInput(Console.OpenStandardInput()) @@ -22,12 +25,16 @@ public static async Task RunAsync() services.AddSingleton(); services.AddSingleton(); services.AddSingleton(); + services.AddSingleton(host); }) .WithHandler() .WithHandler() - .WithHandler()); + .WithHandler() + .WithHandler(), cancellationToken); + await server.WaitForExit; + } } } diff --git a/Compiler/LangServer/Handlers/CompletionHandler.cs b/Compiler/LangServer/Handlers/CompletionHandler.cs index 7a3b3ada..b11368e0 100644 --- a/Compiler/LangServer/Handlers/CompletionHandler.cs +++ b/Compiler/LangServer/Handlers/CompletionHandler.cs @@ -2,6 +2,7 @@ using System.Linq; using System.Threading; using System.Threading.Tasks; +using Core; using Core.Parser.Extensions; using OmniSharp.Extensions.LanguageServer.Protocol.Client.Capabilities; using OmniSharp.Extensions.LanguageServer.Protocol.Document; @@ -14,10 +15,10 @@ public sealed class CompletionHandler : ICompletionHandler private readonly BufferManager _bufferManager; private readonly DocumentSelector _documentSelector; private readonly HashSet _keywords; - private readonly HashSet _attributes; + private readonly HashSet _decorators; private readonly HashSet _constants; - public CompletionHandler(BufferManager bufferManager) + public CompletionHandler(BufferManager bufferManager, CompilerHost compilerHost) { _bufferManager = bufferManager; _documentSelector = new DocumentSelector( @@ -27,22 +28,19 @@ public CompletionHandler(BufferManager bufferManager) } ); - _keywords = new HashSet() - { + _keywords = + [ "enum", "struct", "message", - "readonly", "map", "array", - "union", "service", - }; + "mut", "map", "array", + "union", "service", "stream", + ]; - _constants = new HashSet() - { + _constants = + [ "true", "false", "inf", "nan", - }; + ]; - _attributes = new HashSet() - { - "opcode", "deprecated", "flags", - }; + _decorators = new HashSet(compilerHost.Decorators.Select(decorator => decorator.Identifier)); } public CompletionRegistrationOptions GetRegistrationOptions(CompletionCapability capability, ClientCapabilities clientCapabilities) @@ -61,11 +59,11 @@ public Task Handle(CompletionParams request, CancellationToken c var buffer = _bufferManager.GetBuffer(request.TextDocument.Uri); if (buffer?.Schema != null) { - items.Add(new CompletionItem + /*items.Add(new CompletionItem { Label = buffer.Schema.Value.Namespace, Kind = CompletionItemKind.Reference, - }); + });*/ // TODO: Only top level definitions here? foreach (var definition in buffer.Schema.Value.Definitions) @@ -79,12 +77,12 @@ public Task Handle(CompletionParams request, CancellationToken c } // Add attributes - foreach (var item in _attributes) + foreach (var item in _decorators) { items.Add(new CompletionItem { Label = item, - Kind = CompletionItemKind.EnumMember, + Kind = CompletionItemKind.Function, }); } diff --git a/Compiler/LangServer/Handlers/HoverHandler.cs b/Compiler/LangServer/Handlers/HoverHandler.cs new file mode 100644 index 00000000..3e926027 --- /dev/null +++ b/Compiler/LangServer/Handlers/HoverHandler.cs @@ -0,0 +1,144 @@ +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using Core; +using Core.IO; +using Core.Lexer.Tokenization; +using Core.Meta; +using Core.Parser.Extensions; +using OmniSharp.Extensions.LanguageServer.Protocol.Client.Capabilities; +using OmniSharp.Extensions.LanguageServer.Protocol.Document; +using OmniSharp.Extensions.LanguageServer.Protocol.Models; + +namespace Compiler.LangServer +{ + public sealed class HoverHandler : IHoverHandler + { + private BufferManager _bufferManager; + private BebopLangServerLogger _logger; + private CompilerHost _compilerHost; + + private static readonly Hover _emptyHoverResult = new Hover() { Contents = new MarkedStringsOrMarkupContent(new MarkupContent()) }; + public HoverHandler(BufferManager bufferManager, + BebopLangServerLogger logger, + CompilerHost compilerHost) + { + _bufferManager = bufferManager; + _logger = logger; + _compilerHost = compilerHost; + } + + + public HoverRegistrationOptions GetRegistrationOptions(HoverCapability capability, ClientCapabilities clientCapabilities) + { + return new HoverRegistrationOptions() { DocumentSelector = DocumentSelector.ForLanguage("bebop") }; + } + + public Task Handle(HoverParams request, CancellationToken cancellationToken) + { + + var buffer = _bufferManager.GetBuffer(request.TextDocument.Uri); + if (buffer?.Schema != null) + { + var position = request.Position; + var schema = buffer.Schema.Value; + var tokenizer = new Tokenizer(SchemaReader.FromTextualSchema(buffer.Text)); + var tokens = tokenizer.Tokens.ToArray(); + + bool isDefinition([NotNullWhen(true)] out Core.Meta.Definition? definition) + { + var token = tokens.FirstOrDefault(token => token.Span.StartLine == position.Line && token.Span.StartColumn <= position.Character && token.Span.EndColumn >= position.Character); + if (string.IsNullOrWhiteSpace(token.Lexeme)) + { + definition = null; + return false; + } + if (schema.Definitions.TryGetValue(token.Lexeme, out definition)) + { + return true; + } + return false; + } + bool isDecorator([NotNullWhen(true)] out Core.Meta.Decorators.DecoratorDefinition? decorator) + { + var token = tokens.FirstOrDefault(token => token.Span.StartLine == position.Line && token.Span.StartColumn <= position.Character && token.Span.EndColumn >= position.Character); + if (string.IsNullOrWhiteSpace(token.Lexeme)) + { + decorator = null; + return false; + } + foreach (var d in _compilerHost.Decorators) + { + if (d.Identifier == token.Lexeme) + { + decorator = d; + return true; + } + } + decorator = null; + return false; + } + if (isDefinition(out var definition)) + { + var stringBuilder = new System.Text.StringBuilder(); + stringBuilder.AppendLine($"**{definition.Name}**"); + stringBuilder.AppendLine(); + if (string.IsNullOrWhiteSpace(definition.Documentation)) + { + stringBuilder.AppendLine("No documentation available."); + } + else + { + stringBuilder.AppendLine(definition.Documentation); + } + + return Task.FromResult(new Hover() + { + Contents = new MarkedStringsOrMarkupContent(new MarkupContent() + { + Kind = MarkupKind.Markdown, + Value = stringBuilder.ToString() + }) + }); + } + else if (isDecorator(out var decorator)) + { + var stringBuilder = new System.Text.StringBuilder(); + stringBuilder.AppendLine($"**{decorator.Identifier}**"); + stringBuilder.AppendLine(); + stringBuilder.AppendLine(decorator.Description); + // create a markdown table showing the parameters + + if (decorator.Parameters is not null) + { + stringBuilder.AppendLine(); + stringBuilder.AppendLine($"**Parameters**"); + stringBuilder.AppendLine(); + stringBuilder.AppendLine($"| Parameter | Type | Required | Default | Description |"); + stringBuilder.AppendLine($"| --- | --- | --- | --- | --- |"); + foreach (var parameter in decorator.Parameters) + { + stringBuilder.AppendLine($"| {parameter.Identifier} | {parameter.Type.ToTokenString()} | {parameter.IsRequired} | {parameter.DefaultValue ?? "N/A"} | {parameter.Description} |"); + } + } + return Task.FromResult(new Hover() + { + Contents = new MarkedStringsOrMarkupContent(new MarkupContent() + { + Kind = MarkupKind.Markdown, + Value = stringBuilder.ToString() + }) + }); + } + return Task.FromResult(_emptyHoverResult); + } + else + { + _logger.LogInfo($"Hover request at {request.TextDocument.Uri}"); + } + return Task.FromResult(_emptyHoverResult); + } + } +} diff --git a/Compiler/LangServer/Handlers/SemanticTokenHandler.cs b/Compiler/LangServer/Handlers/SemanticTokenHandler.cs index eb1ec3e5..ceb4e24a 100644 --- a/Compiler/LangServer/Handlers/SemanticTokenHandler.cs +++ b/Compiler/LangServer/Handlers/SemanticTokenHandler.cs @@ -2,8 +2,10 @@ using System.Collections.Generic; using System.Threading; using System.Threading.Tasks; +using Core; using Core.IO; using Core.Lexer.Tokenization; +using Core.Meta; using OmniSharp.Extensions.LanguageServer.Protocol.Client.Capabilities; using OmniSharp.Extensions.LanguageServer.Protocol.Document; using OmniSharp.Extensions.LanguageServer.Protocol.Models; @@ -21,7 +23,8 @@ public sealed class SemanticTokenHandler : SemanticTokensHandlerBase public SemanticTokenHandler( BufferManager bufferManager, - BebopLangServerLogger logger) + BebopLangServerLogger logger, + CompilerHost compilerHost) { _bufferManager = bufferManager ?? throw new ArgumentNullException(nameof(bufferManager)); _logger = logger ?? throw new ArgumentNullException(nameof(logger)); @@ -33,7 +36,7 @@ public SemanticTokenHandler( _tokenTypes = new Dictionary { - { TokenKind.ReadOnly, SemanticTokenType.Keyword }, + { TokenKind.Mut, SemanticTokenType.Keyword }, { TokenKind.Array, SemanticTokenType.Keyword }, { TokenKind.Enum, SemanticTokenType.Keyword }, { TokenKind.Map, SemanticTokenType.Keyword }, @@ -43,6 +46,7 @@ public SemanticTokenHandler( { TokenKind.Service, SemanticTokenType.Keyword }, { TokenKind.Stream, SemanticTokenType.Keyword }, { TokenKind.Number, SemanticTokenType.Number }, + { TokenKind.Decorator, SemanticTokenType.Method } }; _identifiers = new Dictionary @@ -64,6 +68,11 @@ public SemanticTokenHandler( { "guid", SemanticTokenType.Type }, { "date", SemanticTokenType.Type }, }; + + foreach (var decorator in compilerHost.Decorators) + { + _identifiers[decorator.Identifier] = SemanticTokenType.Method; + } } protected override SemanticTokensRegistrationOptions CreateRegistrationOptions(SemanticTokensCapability capability, ClientCapabilities clientCapabilities) @@ -106,16 +115,18 @@ protected override Task Tokenize(SemanticTokensBuilder builder, ITextDocumentIde var isDefinition = false; if (buffer?.Schema != null) { - if (buffer.Schema?.Definitions?.ContainsKey(token.Lexeme) ?? false) + if (buffer.Schema?.Definitions?.TryGetValue(token.Lexeme, out var definition) ?? false) { isDefinition = true; + + _logger.LogInfo($"Found definition {definition.Name} at {token.Span.StartLine}:{token.Span.StartColumn}"); builder.Push( - new OmniSharp.Extensions.LanguageServer.Protocol.Models.Range - { - Start = new Position(token.Span.StartLine, token.Span.StartColumn), - End = new Position(token.Span.EndLine, token.Span.EndColumn) - }, - SemanticTokenType.Type as SemanticTokenType?); + new OmniSharp.Extensions.LanguageServer.Protocol.Models.Range + { + Start = new Position(token.Span.StartLine, token.Span.StartColumn), + End = new Position(token.Span.EndLine, token.Span.EndColumn) + }, + SemanticTokenType.Type as SemanticTokenType?); } } diff --git a/Compiler/LangServer/Handlers/TextDocumentSyncHandler.cs b/Compiler/LangServer/Handlers/TextDocumentSyncHandler.cs index 9c3d7a30..604d848f 100644 --- a/Compiler/LangServer/Handlers/TextDocumentSyncHandler.cs +++ b/Compiler/LangServer/Handlers/TextDocumentSyncHandler.cs @@ -3,6 +3,7 @@ using System.Linq; using System.Threading; using System.Threading.Tasks; +using Core; using Core.Exceptions; using Core.Meta; using Core.Parser; @@ -19,6 +20,7 @@ namespace Compiler.LangServer { public sealed class TextDocumentSyncHandler : TextDocumentSyncHandlerBase { + private readonly CompilerHost _compilerHost; private readonly ILanguageServerFacade _router; private readonly BufferManager _bufferManager; private readonly BebopDiagnosticPublisher _publisher; @@ -31,8 +33,10 @@ public TextDocumentSyncHandler( ILanguageServerFacade router, BufferManager bufferManager, BebopDiagnosticPublisher publisher, - BebopLangServerLogger logger) + BebopLangServerLogger logger, + CompilerHost compilerHost) { + _compilerHost = compilerHost ?? throw new ArgumentNullException(nameof(compilerHost)); _router = router ?? throw new ArgumentNullException(nameof(router)); _bufferManager = bufferManager ?? throw new ArgumentNullException(nameof(bufferManager)); _publisher = publisher ?? throw new ArgumentNullException(nameof(publisher)); @@ -57,20 +61,19 @@ protected override TextDocumentSyncRegistrationOptions CreateRegistrationOptions }; } - public override async Task Handle(DidOpenTextDocumentParams request, CancellationToken cancellationToken) + public override Task Handle(DidOpenTextDocumentParams request, CancellationToken cancellationToken) { _logger.LogInfo($"Opening document: {request.TextDocument.Uri}"); - await UpdateBufferAsync(request.TextDocument.Uri, request.TextDocument.Text, request.TextDocument.Version); - return Unit.Value; + UpdateBuffer(request.TextDocument.Uri, request.TextDocument.Text, request.TextDocument.Version); + return Task.FromResult(Unit.Value); } - public override async Task Handle(DidChangeTextDocumentParams request, CancellationToken cancellationToken) + public override Task Handle(DidChangeTextDocumentParams request, CancellationToken cancellationToken) { var text = request.ContentChanges.FirstOrDefault()?.Text ?? string.Empty; - await UpdateBufferAsync(request.TextDocument.Uri, text, request.TextDocument.Version); - - return Unit.Value; + UpdateBuffer(request.TextDocument.Uri, text, request.TextDocument.Version); + return Task.FromResult(Unit.Value); } public override Task Handle(DidSaveTextDocumentParams request, CancellationToken cancellationToken) @@ -110,11 +113,11 @@ public override Task Handle(DidCloseTextDocumentParams request, Cancellati return Unit.Task; } - private async Task UpdateBufferAsync(DocumentUri uri, string text, int? version) + private void UpdateBuffer(DocumentUri uri, string text, int? version) { try { - var schema = await ParseSchemaAsync(uri, text, version); + var schema = ParseSchema(uri, text, version); _bufferManager.UpdateBuffer(uri, new Buffer(schema, text, version)); } catch (Exception ex) @@ -127,9 +130,9 @@ private async Task UpdateBufferAsync(DocumentUri uri, string text, int? version) } } - private async Task ParseSchemaAsync(DocumentUri uri, string text, int? version) + private BebopSchema ParseSchema(DocumentUri uri, string text, int? version) { - var (schema, errors) = await ParseSchemaAsync(uri, text); + var (schema, errors) = ParseSchema(uri, text); // TODO: Don't count indirect errors here. // If there only is indirect errors (from an import), @@ -148,18 +151,18 @@ private async Task ParseSchemaAsync(DocumentUri uri, string text, i return schema; } - private async Task<(BebopSchema, List)> ParseSchemaAsync(DocumentUri uri, string text) + private (BebopSchema, List) ParseSchema(DocumentUri uri, string text) { var diagnostics = new List(); try { - var parser = new SchemaParser(text, DocumentUri.GetFileSystemPath(uri) ?? string.Empty) + var parser = new SchemaParser(text, _compilerHost) { ImportResolver = new BebopLangServerImportResolver(uri, _logger) }; - var schema = await parser.Parse(); + var schema = parser.Parse(); // Perform validation PerformValidation(ref schema); diff --git a/Compiler/Options/BebopConfigOption.cs b/Compiler/Options/BebopConfigOption.cs new file mode 100644 index 00000000..53432e4a --- /dev/null +++ b/Compiler/Options/BebopConfigOption.cs @@ -0,0 +1,49 @@ +using System; +using System.CommandLine; +using System.CommandLine.Parsing; +using System.IO; +using System.Linq; +using Core.Logging; +using Core.Meta; +using Errata; + +namespace Compiler.Options +{ + /// + /// Represents a command line option for specifying Bebop configuration. + /// + public class BebopConfigOption : CliOption + { + /// + /// Initializes a new instance of the class. + /// + public BebopConfigOption() : base(name: CliStrings.ConfigFlag, aliases: ["-c"]) + { + Description = "Compile the project given the path to its configuration file."; + AllowMultipleArgumentsPerToken = false; + AcceptLegalFilePathsOnly(); + + Validators.Add(static result => + { + var token = result.Tokens.SingleOrDefault()?.Value?.Trim(); + if (string.IsNullOrWhiteSpace(token)) return; + if (new FileInfo(token).Exists) return; + result.AddError($"Specified config '{token}' does not exist."); + }); + + DefaultValueFactory = (_) => + { + var path = BebopConfig.Locate(); + if (string.IsNullOrEmpty(path)) return BebopConfig.Default; + return BebopConfig.FromFile(path); + }; + + CustomParser = new((ArgumentResult result) => + { + var configPath = result.Tokens.SingleOrDefault()?.Value; + var config = new FileInfo(configPath!); + return BebopConfig.FromFile(config.FullName); + }); + } + } +} \ No newline at end of file diff --git a/Compiler/Options/DiagnosticFormatOption.cs b/Compiler/Options/DiagnosticFormatOption.cs new file mode 100644 index 00000000..70546d85 --- /dev/null +++ b/Compiler/Options/DiagnosticFormatOption.cs @@ -0,0 +1,69 @@ +using System; +using System.CommandLine; +using System.CommandLine.Parsing; +using System.Linq; +using Core.Logging; + +namespace Compiler.Options +{ + /// + /// Represents a command line option for specifying the format of diagnostic messages. + /// + public class DiagnosticFormatOption : CliOption + { + /// + /// Initializes a new instance of the class. + /// + public DiagnosticFormatOption() : base(name: CliStrings.DiagnosticFormatFlag, aliases: ["-df"]) + { + Description = "Specifies the format which diagnostic messages are printed in."; + AllowMultipleArgumentsPerToken = false; + Validators.Add(result => + { + var token = result.Tokens.SingleOrDefault()?.Value?.Trim(); + if (string.IsNullOrWhiteSpace(token)) return; + if (!IsLogFormatter(token)) + { + result.AddError($"Invalid diagnostic format '{token}'."); + return; + } + }); + + CustomParser = new((ArgumentResult result) => + { + var token = result.Tokens.SingleOrDefault()?.Value?.Trim(); + return Parse(token); + }); + + + DefaultValueFactory = (a) => + { + return LogFormatter.Enhanced; + }; + } + + public static bool IsLogFormatter(string? token) + { + if (string.IsNullOrWhiteSpace(token)) return false; + return token.ToLowerInvariant() switch + { + "enhanced" => true, + "json" => true, + "msbuild" => true, + _ => false, + }; + } + + public static LogFormatter Parse(string? token) + { + if (string.IsNullOrWhiteSpace(token)) return LogFormatter.Enhanced; + return token.ToLowerInvariant() switch + { + "enhanced" => LogFormatter.Enhanced, + "json" => LogFormatter.JSON, + "msbuild" => LogFormatter.MSBuild, + _ => throw new ArgumentException($"Invalid diagnostic format '{token}'."), + }; + } + } +} \ No newline at end of file diff --git a/Compiler/Options/GeneratorOption.cs b/Compiler/Options/GeneratorOption.cs new file mode 100644 index 00000000..1a924b32 --- /dev/null +++ b/Compiler/Options/GeneratorOption.cs @@ -0,0 +1,111 @@ +using System; +using System.CommandLine; +using System.CommandLine.Parsing; +using System.IO; +using System.Linq; +using Core.Generators; +using Core.Meta.Extensions; +namespace Compiler.Options +{ + /// + /// Represents a command line option for specifying generator configurations. + /// + public class GeneratorOption : CliOption + { + /// + /// Initializes a new instance of the class. + /// + public GeneratorOption() : base(name: CliStrings.GeneratorFlag, ["-g"]) + { + Description = "Specifies code generators to use for compilation."; + AllowMultipleArgumentsPerToken = false; + CustomParser = new((ArgumentResult result) => + { + if (result.Tokens.Count == 0) + { + return []; + } + return result.Tokens.Select(t => ParseGeneratorToken(t.Value, result)).Where(c => c is not null).Select(c => c!).ToArray(); + }); + } + + /// + /// Parses a generator token into a object. + /// + /// The generator token to parse. + /// The argument result to which errors can be added. + /// A object if the token could be parsed; otherwise, null. + private static GeneratorConfig? ParseGeneratorToken(string token, ArgumentResult result) + { + var parts = token.Split(':'); + if (parts.Length != 2) + { + result.AddError($"Incomplete generator token specified '{token}'."); + return null; + } + + var generatorAlias = parts[0].Trim().ToLower(); + var remaining = parts[1].Split(','); + var outputPath = remaining[0] ?? string.Empty; + + + // If the output path is 'stdout', no need to validate it as a file path + if (!string.Equals("stdout", outputPath, StringComparison.OrdinalIgnoreCase) && !outputPath.IsLegalFilePath(out var invalidCharacterIndex)) + { + if (invalidCharacterIndex >= 0) + { + result.AddError($"Invalid character '{outputPath[invalidCharacterIndex]}' in output path of generator '{generatorAlias}': '{outputPath}'."); + return null; + } + } + + var additionalOptions = remaining.Skip(1).Select(s => s.Split('=')).Where(p => p.Length == 2).ToDictionary(p => p[0].Trim(), p => p[1].Trim()); + + // Initialize default values + var services = TempoServices.Both; + var emitNotice = true; + var emitBinarySchema = true; + string @namespace = string.Empty; + + foreach (var option in additionalOptions) + { + switch (option.Key.ToLower()) + { + case "services": + if (!Enum.TryParse(option.Value, true, out var parsedServices)) + { + result.AddError($"Invalid value '{option.Value}' for option 'services'."); + break; + } + services = parsedServices; + break; + case "emitnotice": + if (!bool.TryParse(option.Value, out var parsedEmitNotice)) + { + result.AddError($"Invalid value '{option.Value}' for option 'emitNotice'."); + break; + } + emitNotice = parsedEmitNotice; + break; + case "emitbinaryschema": + if (!bool.TryParse(option.Value, out var parsedEmitBinarySchema)) + { + result.AddError($"Invalid value '{option.Value}' for option 'emitBinarySchema'."); + break; + } + emitBinarySchema = parsedEmitBinarySchema; + break; + case "namespace": + if (string.IsNullOrWhiteSpace(option.Value)) + { + result.AddError($"Invalid value '{option.Value}' for option 'namespace'."); + break; + } + @namespace = option.Value; + break; + } + } + return new GeneratorConfig(generatorAlias, outputPath, services, emitNotice, @namespace, emitBinarySchema, additionalOptions); + } + } +} \ No newline at end of file diff --git a/Compiler/Options/SimpleOptions.cs b/Compiler/Options/SimpleOptions.cs new file mode 100644 index 00000000..d80cdd23 --- /dev/null +++ b/Compiler/Options/SimpleOptions.cs @@ -0,0 +1,194 @@ +using System.CommandLine; + +namespace Compiler.Options +{ + /// + /// Represents a command line option for including certain schemas. + /// + public class IncludeOption : CliOption + { + public IncludeOption() : base( + name: CliStrings.IncludeFlag, + aliases: ["-i"]) + { + Description = "Specifies an array of filenames or patterns to include in the compiler. These filenames are resolved relative to the directory containing the bebop.json file."; + AllowMultipleArgumentsPerToken = true; + } + } + + /// + /// Represents a command line option for excluding certain schemas. + /// + public class ExcludeOption : CliOption + { + public ExcludeOption() : base( + name: CliStrings.ExcludeFlag, + aliases: ["-e"]) + { + Description = "Specifies an array of filenames or patterns that should be skipped when resolving include."; + AllowMultipleArgumentsPerToken = true; + } + } + + /// + /// Represents a command line option for excluding certain directories from being watched. + /// + public class WatchExcludeDirectoriesOption : CliOption + { + public WatchExcludeDirectoriesOption() : base( + name: CliStrings.ExcludeDirectoriesFlag) + { + Description = "Remove a list of directories from the watch process."; + AllowMultipleArgumentsPerToken = true; + } + } + + /// + /// Represents a command line option for excluding certain files from being watched. + /// + public class WatchExcludeFilesOption : CliOption + { + public WatchExcludeFilesOption() : base( + name: CliStrings.ExcludeFilesFlag) + { + Description = "Remove a list of files from the watch mode's processing."; + AllowMultipleArgumentsPerToken = true; + } + } + + public class PreserveWatchOutputOption : CliOption + { + public PreserveWatchOutputOption() : base( + name: CliStrings.PreserveWatchOutputFlag) + { + Description = "Disable wiping the console in watch mode."; + AllowMultipleArgumentsPerToken = false; + } + } + + /// + /// Represents a command line option for disabling the emission of files. + /// + public class NoEmitOption : CliOption + { + public NoEmitOption() : base( + name: CliStrings.NoEmitFlag) + { + Description = "Disable emitting files from a compilation."; + AllowMultipleArgumentsPerToken = false; + } + } + + /// + /// Represents a command line option for suppressing specific warnings. + /// + public class NoWarnOption : CliOption + { + public NoWarnOption() : base( + name: CliStrings.NoWarnFlag) + { + Description = "Suppresses the specified warning codes from reports during compilation."; + AllowMultipleArgumentsPerToken = true; + } + } + + public class InitOption : CliOption + { + public InitOption() : base( + name: CliStrings.InitFlag) + { + Description = "Initializes a Bebop project and creates a bebop.json file."; + AllowMultipleArgumentsPerToken = false; + } + } + + public class ListSchemaOption : CliOption + { + public ListSchemaOption() : base( + name: CliStrings.ListSchemasFlag) + { + Description = "Print names of schemas that are part of the compilation and then stop processing."; + AllowMultipleArgumentsPerToken = false; + } + } + + public class ShowConfigOption : CliOption + { + public ShowConfigOption() : base( + name: CliStrings.ShowConfigFlag) + { + Description = "Print the final configuration instead of building."; + AllowMultipleArgumentsPerToken = false; + } + } + + public class LocaleOption : CliOption + { + public LocaleOption() : base( + name: CliStrings.LocaleFlag) + { + Description = "Set the language of the messaging from bebopc. This does not affect emit."; + AllowMultipleArgumentsPerToken = false; + } + } + + public class TraceOption : CliOption + { + public TraceOption() : base( + name: CliStrings.TraceFlag) + { + Description = "Enable tracing of the compiler."; + AllowMultipleArgumentsPerToken = false; + } + } + + public class StandardInputOption : CliOption + { + public StandardInputOption() : base( + name: CliStrings.StandardInputFlag) + { + Description = "Read a schema from standard input."; + AllowMultipleArgumentsPerToken = false; + } + } + + public class StandardOutputOption : CliOption + { + public StandardOutputOption() : base( + name: CliStrings.StandardOutputFlag) + { + Description = "Write the results of compilation to standard output."; + AllowMultipleArgumentsPerToken = false; + } + } + + public class FromOption : CliOption + { + public FromOption() : base( + name: CliStrings.FromFlag) + { + Description = "The format of the input schema."; + AllowMultipleArgumentsPerToken = false; + } + } + + public class ToOption : CliOption + { + public ToOption() : base( + name: CliStrings.ToFlag) + { + Description = "The format of the output schema."; + AllowMultipleArgumentsPerToken = false; + } + } + + public class DryRunOption : CliOption + { + public DryRunOption() : base( + name: CliStrings.DryRunFlag) + { + Description = "Do not write any files to disk."; + AllowMultipleArgumentsPerToken = false; + } + } +} \ No newline at end of file diff --git a/Compiler/Program.cs b/Compiler/Program.cs index 67dc8d57..817e0aed 100644 --- a/Compiler/Program.cs +++ b/Compiler/Program.cs @@ -1,186 +1,108 @@ using System; -using System.Collections; -using System.Collections.Generic; -using System.Diagnostics; -using System.IO; +using System.CommandLine; +using System.Runtime.InteropServices; using System.Linq; -using System.Threading; -using System.Threading.Tasks; -using Compiler.LangServer; -using Core.Exceptions; -using Core.Generators; +using Compiler.Commands; +using Compiler.Options; using Core.Logging; using Core.Meta; +using System.Text.Json; +using Core.Exceptions; +using Compiler; -namespace Compiler +if (RuntimeInformation.OSArchitecture is Architecture.Wasm) +{ + Environment.SetEnvironmentVariable("DOTNET_SYSTEM_GLOBALIZATION_INVARIANT", "1"); +} +Console.OutputEncoding = System.Text.Encoding.UTF8; +var forcedFormatter = Environment.GetEnvironmentVariable("BEBOPC_LOG_FORMAT"); +if (DiagnosticFormatOption.IsLogFormatter(forcedFormatter) is true) { - internal class Program - { - private static CommandLineFlags? _flags; - private static void WriteHelpText() - { - if (_flags is not null) - { - DiagnosticLogger.Instance.WriteLine(string.Empty); - DiagnosticLogger.Instance.WriteLine(_flags.HelpText); - } - } + DiagnosticLogger.Initialize(DiagnosticFormatOption.Parse(forcedFormatter)); +} +else +{ + DiagnosticLogger.Initialize(LogFormatter.Enhanced); +} - private static async Task Main() +try +{ + var rootCommand = new CliRootCommand("The Bebop schema language compiler") + { + new DiagnosticFormatOption(), + new BebopConfigOption(), + new IncludeOption(), + new ExcludeOption(), + new InitOption(), + new ListSchemaOption(), + new ShowConfigOption(), + new LocaleOption(), + new TraceOption(), + #if !WASI_WASM_BUILD + new LanguageServerCommand(), + #endif + new BuildCommand() { - System.Console.OutputEncoding = System.Text.Encoding.UTF8; - var formatter = CommandLineFlags.FindLogFormatter(Environment.GetCommandLineArgs()); - DiagnosticLogger.Initialize(CommandLineFlags.FindLogFormatter(Environment.GetCommandLineArgs())); - try - { - if (!CommandLineFlags.TryParse(Environment.GetCommandLineArgs(), out _flags, out var message)) - { - DiagnosticLogger.Instance.WriteDiagonstic(new CompilerException(message)); - return BebopCompiler.Err; - } - - if (_flags.Debug) - { - - DiagnosticLogger.Instance.WriteLine($"Waiting for debugger to attach (PID={Helpers.ProcessId})..."); - // Wait 5 minutes for a debugger to attach - var timeoutToken = new CancellationTokenSource(TimeSpan.FromMinutes(5)).Token; - while (!Debugger.IsAttached) - { - await Task.Delay(100, timeoutToken); - } - Debugger.Break(); - } - - if (_flags.Version) - { - DiagnosticLogger.Instance.WriteLine($"{ReservedWords.CompilerName} {DotEnv.Generated.Environment.Version}"); - return BebopCompiler.Ok; - } - - if (_flags.Help) - { - WriteHelpText(); - return BebopCompiler.Ok; - } - - if (_flags.LanguageServer) - { - await BebopLangServer.RunAsync(); - return BebopCompiler.Ok; - } - - var compiler = new BebopCompiler(_flags); - - - if (_flags.CheckSchemaFile is not null) - { - if (string.IsNullOrWhiteSpace(_flags.CheckSchemaFile)) - { - DiagnosticLogger.Instance.WriteDiagonstic(new CompilerException("No textual schema was read from standard input.")); - return BebopCompiler.Err; - } - return await compiler.CheckSchema(_flags.CheckSchemaFile); - } - - List? paths = null; - - if (_flags.SchemaDirectory is not null) - { - paths = new DirectoryInfo(_flags.SchemaDirectory!) - .GetFiles($"*.{ReservedWords.SchemaExt}", SearchOption.AllDirectories) - .Select(f => f.FullName) - .ToList(); - } - else if (_flags.SchemaFiles is not null) - { - paths = _flags.SchemaFiles; - } - - if (_flags.CheckSchemaFiles is not null) - { - if (_flags.CheckSchemaFiles.Count > 0) - { - return await compiler.CheckSchemas(_flags.CheckSchemaFiles); - } - // Fall back to the paths defined by the config if none specified - else if (paths is not null && paths.Count > 0) - { - return await compiler.CheckSchemas(paths); - } - DiagnosticLogger.Instance.WriteDiagonstic(new CompilerException("No schemas specified in check.")); - return BebopCompiler.Err; - } + new GeneratorOption(), + new NoEmitOption(), + new NoWarnOption(), + new StandardInputOption(), + new StandardOutputOption(), + }, + #if !WASI_WASM_BUILD + new WatchCommand() + { + new GeneratorOption(), + new WatchExcludeDirectoriesOption(), + new WatchExcludeFilesOption(), + new NoEmitOption(), + new NoWarnOption(), + new PreserveWatchOutputOption(), + }, + #endif + new ConvertCommand() + { + new FromOption(), + new ToOption(), + new DryRunOption(), + }, + }; - if (!_flags.GetParsedGenerators().Any()) - { - DiagnosticLogger.Instance.WriteDiagonstic(new CompilerException("No code generators were specified.")); - return BebopCompiler.Err; - } - if (_flags.SchemaDirectory is not null && _flags.SchemaFiles is not null) - { - DiagnosticLogger.Instance.WriteDiagonstic( - new CompilerException("Can't specify both an input directory and individual input files")); - return BebopCompiler.Err; - } + rootCommand.SetAction(RootCommand.HandleCommand); - if (_flags.Watch) - { + var results = rootCommand.Parse(args); - var watcher = new Watcher(_flags.WorkingDirectory, compiler, _flags.PreserveWatchOutput); - if (_flags.WatchExcludeDirectories is not null) - { - watcher.AddExcludeDirectories(_flags.WatchExcludeDirectories); - } - if (_flags.WatchExcludeFiles is not null) - { - watcher.AddExcludeFiles(_flags.WatchExcludeFiles); - } - return await watcher.StartAsync(); - } - if (paths is null) - { - DiagnosticLogger.Instance.WriteDiagonstic(new CompilerException("Specify one or more input files with --dir or --files.")); - return BebopCompiler.Err; - } - if (paths.Count == 0) - { - DiagnosticLogger.Instance.WriteDiagonstic(new CompilerException("No input files were found at the specified target location.")); - return BebopCompiler.Err; - } + results.Configuration.EnableDefaultExceptionHandler = false; + results.Configuration.ProcessTerminationTimeout = null; + if (results.GetValue(CliStrings.TraceFlag)) + { + DiagnosticLogger.Instance.EnableTrace(); + } - // Everything below this point requires paths - foreach (var parsedGenerator in _flags.GetParsedGenerators()) - { - if (!GeneratorUtils.ImplementedGenerators.ContainsKey(parsedGenerator.Alias)) - { - DiagnosticLogger.Instance.WriteDiagonstic(new CompilerException($"'{parsedGenerator.Alias}' is not a recognized code generator")); - return BebopCompiler.Err; - } - if (string.IsNullOrWhiteSpace(parsedGenerator.OutputFile)) - { - DiagnosticLogger.Instance.WriteDiagonstic(new CompilerException("No output file was specified.")); - return BebopCompiler.Err; - } - var result = await compiler.CompileSchema(GeneratorUtils.ImplementedGenerators[parsedGenerator.Alias], paths, new FileInfo(parsedGenerator.OutputFile), _flags.Namespace ?? string.Empty, parsedGenerator.Services, parsedGenerator.LangVersion); - if (result != BebopCompiler.Ok) - { - return result; - } - } - return BebopCompiler.Ok; + var formatter = results.GetValue(CliStrings.DiagnosticFormatFlag); + DiagnosticLogger.Instance.SetFormatter(formatter ?? LogFormatter.Enhanced); - } - catch (Exception e) - { - DiagnosticLogger.Instance.WriteDiagonstic(e); - return BebopCompiler.Err; - } + if (!results.Errors.Any()) + { + var parsedConfig = results.GetValue(CliStrings.ConfigFlag); + if (parsedConfig is not null) + { + Helpers.MergeConfig(results, parsedConfig); } - } + return await results.InvokeAsync(); } +catch (Exception e) +{ + switch (e) + { + case JsonException: + return DiagnosticLogger.Instance.WriteDiagonstic(new CompilerException("error during JSON marshaling", e)); + default: + return DiagnosticLogger.Instance.WriteDiagonstic(e); + } +} \ No newline at end of file diff --git a/Compiler/Properties/AssemblyInfo.cs b/Compiler/Properties/AssemblyInfo.cs new file mode 100644 index 00000000..1e407edc --- /dev/null +++ b/Compiler/Properties/AssemblyInfo.cs @@ -0,0 +1,4 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +[assembly:System.Runtime.Versioning.SupportedOSPlatform("wasi")] diff --git a/Compiler/TrimmerRoots.xml b/Compiler/TrimmerRoots.xml deleted file mode 100644 index fc391f0a..00000000 --- a/Compiler/TrimmerRoots.xml +++ /dev/null @@ -1,5 +0,0 @@ - - - - - \ No newline at end of file diff --git a/Compiler/Watcher.cs b/Compiler/Watcher.cs index 27a51def..028e3371 100644 --- a/Compiler/Watcher.cs +++ b/Compiler/Watcher.cs @@ -5,21 +5,23 @@ using System.Threading; using System.Threading.Tasks; using Compiler; -using Core.Generators; using Core.Logging; +using Core.Meta; using Microsoft.Extensions.FileSystemGlobbing; using Spectre.Console; -public class Watcher +public class SchemaWatcher { private readonly string _watchDirectory; private readonly FileSystemWatcher _filewatcher; private List _excludeDirectories = new List(); private List _excludeFiles = new List(); private List _trackedFiles = new List(); + private readonly BebopConfig _config; private readonly BebopCompiler _compiler; private readonly bool _preserveWatchOutput; - private readonly TaskCompletionSource _tcs; + private TaskCompletionSource? _tcs; + private CancellationToken _cancellationToken; private readonly SemaphoreSlim _compileSemaphore = new SemaphoreSlim(1, 1); private readonly TimeSpan _recompileTimeout = TimeSpan.FromSeconds(2); @@ -29,12 +31,12 @@ public class Watcher /// ///The directory to watch. ///The list of files to track. - public Watcher(string watchDirectory, BebopCompiler compiler, bool preserveWatchOutput) + public SchemaWatcher(string watchDirectory, BebopConfig config, BebopCompiler compiler) { _compiler = compiler; - _preserveWatchOutput = preserveWatchOutput; - _tcs = new TaskCompletionSource(); - _trackedFiles = compiler.Flags.SchemaFiles!; + _config = config; + _preserveWatchOutput = config.WatchOptions.PreserveWatchOutput; + _trackedFiles = config.ResolveIncludes().ToList(); _watchDirectory = watchDirectory; _filewatcher = new FileSystemWatcher(); _filewatcher.Path = _watchDirectory; @@ -54,8 +56,12 @@ public Watcher(string watchDirectory, BebopCompiler compiler, bool preserveWatch /// Starts the watcher. /// ///Returns a Task representing the watcher operation. - public async Task StartAsync() + public async Task StartAsync(CancellationToken cancellationToken = default) { + if (_tcs is not null) throw new InvalidOperationException("Watcher is already running."); + _cancellationToken = cancellationToken; + _tcs = new TaskCompletionSource(_cancellationToken); + var table = new Table().HeavyBorder().BorderColor(Color.Grey).Title("Watching").RoundedBorder(); table.AddColumns("[white]Status[/]", "[white]Path[/]"); table.AddRow(new Text("Watching", new Style(Color.Green)), new TextPath(_watchDirectory)); @@ -73,7 +79,14 @@ public async Task StartAsync() table.AddRow(new Text(""), new TextPath(excludeFile)); } DiagnosticLogger.Instance.WriteTable(table); - return await _tcs.Task; + try { return await _tcs.Task; } + catch (OperationCanceledException) + { + // Handle the cancellation + _filewatcher.EnableRaisingEvents = false; + _filewatcher.Dispose(); + return 0; + } } /// @@ -81,8 +94,7 @@ public async Task StartAsync() /// private void OnError(object sender, ErrorEventArgs e) { - DiagnosticLogger.Instance.WriteDiagonstic(e.GetException()); - _tcs.TrySetResult(1); + _tcs?.TrySetResult(DiagnosticLogger.Instance.WriteDiagonstic(e.GetException())); } /// @@ -122,7 +134,9 @@ public void AddExcludeFiles(List excludeFiles) /// /// Handles the Renamed event of the FileSystemWatcher. /// +#pragma warning disable VSTHRD100 // Avoid async void methods private async void FileRenamed(object sender, RenamedEventArgs e) +#pragma warning restore VSTHRD100 // Avoid async void methods { string oldFullPath = Path.GetFullPath(e.OldFullPath); string newFullPath = Path.GetFullPath(e.FullPath); @@ -135,8 +149,7 @@ private async void FileRenamed(object sender, RenamedEventArgs e) catch (Exception ex) { // The file or directory may have been deleted or is inaccessible - DiagnosticLogger.Instance.WriteDiagonstic(ex); - _tcs.SetResult(1); + _tcs?.SetResult(DiagnosticLogger.Instance.WriteDiagonstic(ex)); return; } @@ -199,7 +212,7 @@ private async void FileRenamed(object sender, RenamedEventArgs e) } } LogEvent("[orangered1]Schema renamed. Start recompile[/]", e.OldFullPath, e.FullPath); - await CompileSchemas(); + await CompileSchemasAsync(_cancellationToken); } @@ -207,7 +220,9 @@ private async void FileRenamed(object sender, RenamedEventArgs e) /// /// Handles the Deleted event of the FileSystemWatcher. /// +#pragma warning disable VSTHRD100 // Avoid async void methods private async void FileDeleted(object sender, FileSystemEventArgs e) +#pragma warning restore VSTHRD100 // Avoid async void methods { if (!Path.GetExtension(e.FullPath).Equals(".bop", StringComparison.InvariantCultureIgnoreCase) || IsPathExcluded(e.FullPath)) { @@ -216,13 +231,13 @@ private async void FileDeleted(object sender, FileSystemEventArgs e) _trackedFiles.Remove(e.FullPath); LogEvent("[indianred1]Schema deleted. Starting recompile[/]", e.FullPath); - await CompileSchemas(); + await CompileSchemasAsync(_cancellationToken); } /// /// Handles the Created event of the FileSystemWatcher. /// - private async void FileCreated(object sender, FileSystemEventArgs e) + private void FileCreated(object sender, FileSystemEventArgs e) { if (!Path.GetExtension(e.FullPath).Equals(".bop", StringComparison.InvariantCultureIgnoreCase) || IsPathExcluded(e.FullPath)) { @@ -240,7 +255,9 @@ private async void FileCreated(object sender, FileSystemEventArgs e) /// /// Handles the Changed event of the FileSystemWatcher. /// +#pragma warning disable VSTHRD100 // Avoid async void methods private async void FileChanged(object sender, FileSystemEventArgs e) +#pragma warning restore VSTHRD100 // Avoid async void methods { if (!Path.GetExtension(e.FullPath).Equals(".bop", StringComparison.InvariantCultureIgnoreCase) || IsPathExcluded(e.FullPath)) { @@ -250,12 +267,12 @@ private async void FileChanged(object sender, FileSystemEventArgs e) // Handle file changes if (_trackedFiles.Contains(e.FullPath)) { - if (await _compileSemaphore.WaitAsync(_recompileTimeout)) + if (_compileSemaphore.Wait(_recompileTimeout, _cancellationToken)) { try { LogEvent("[blue]Schema changed. Starting recompile[/]", e.FullPath); - var result = await CompileSchemas(); + var result = await CompileSchemasAsync(_cancellationToken); if (result is BebopCompiler.Ok) { LogEvent("[green]Schema recompilation succeeded. Resuming watch.[/]"); @@ -341,7 +358,7 @@ private void LogEvent(string message, string? filePath = null, string? newFilePa /// An integer representing the compilation result. Returns BebopCompiler.Ok if the compilation is successful, /// otherwise, returns BebopCompiler.Err. /// - public async Task CompileSchemas() + public async ValueTask CompileSchemasAsync(CancellationToken cancellationToken = default) { try { @@ -349,30 +366,31 @@ public async Task CompileSchemas() { DiagnosticLogger.Instance.Error.Clear(); } - foreach (var parsedGenerator in _compiler.Flags.GetParsedGenerators()) + if (_trackedFiles.Count == 0) { - if (!GeneratorUtils.ImplementedGenerators.ContainsKey(parsedGenerator.Alias)) - { - LogEvent($"[red]The alias '{parsedGenerator.Alias}' is not a recognized code generator[/]", isError: true); - return BebopCompiler.Err; - } - if (string.IsNullOrWhiteSpace(parsedGenerator.OutputFile)) - { - LogEvent($"[red]No out file was specified for generator '{parsedGenerator.Alias}'[/]", isError: true); - return BebopCompiler.Err; - } - var result = await _compiler.CompileSchema(GeneratorUtils.ImplementedGenerators[parsedGenerator.Alias], _trackedFiles, new FileInfo(parsedGenerator.OutputFile), _compiler.Flags.Namespace ?? string.Empty, parsedGenerator.Services, parsedGenerator.LangVersion); - if (result != BebopCompiler.Ok) - { - return result; - } + LogEvent("[yellow]Recompile skipped. No schemas being tracked.[/]"); + return BebopCompiler.Ok; + } + var schema = _compiler.ParseSchema(_trackedFiles); + var (warnings, errors) = BebopCompiler.GetSchemaDiagnostics(schema, _config.SupressedWarningCodes); + if (_config.NoEmit || errors.Count != 0) + { + DiagnosticLogger.Instance.WriteSpanDiagonstics([.. warnings, .. errors]); + return errors.Count != 0 ? BebopCompiler.Err : BebopCompiler.Ok; } + DiagnosticLogger.Instance.WriteSpanDiagonstics(warnings); + var generatedFiles = new List(); + foreach (var generatorConfig in _config.Generators) + { + generatedFiles.Add(await _compiler.BuildAsync(generatorConfig, schema, _config, cancellationToken)); + } + BebopCompiler.EmitGeneratedFiles(generatedFiles, _config); return BebopCompiler.Ok; } catch (Exception ex) { - DiagnosticLogger.Instance.WriteDiagonstic(ex); - return BebopCompiler.Err; + return DiagnosticLogger.Instance.WriteDiagonstic(ex); } + } } \ No newline at end of file diff --git a/Compiler/nuget.config b/Compiler/nuget.config new file mode 100644 index 00000000..46175792 --- /dev/null +++ b/Compiler/nuget.config @@ -0,0 +1,10 @@ + + + + + + + + + + \ No newline at end of file diff --git a/Compiler/runtimeconfig.template.json b/Compiler/runtimeconfig.template.json new file mode 100644 index 00000000..77b79428 --- /dev/null +++ b/Compiler/runtimeconfig.template.json @@ -0,0 +1,13 @@ +{ + "wasmHostProperties": { + "perHostConfig": [ + { + "name": "wasmtime", + "Host": "wasmtime" + } + ] + }, + "configProperties": { + "System.Globalization.Invariant": true + } +} diff --git a/Core/CompilerHost.cs b/Core/CompilerHost.cs new file mode 100644 index 00000000..e6bd7240 --- /dev/null +++ b/Core/CompilerHost.cs @@ -0,0 +1,237 @@ +using System; +using System.Collections.Frozen; +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; +using Chord.Common; +using Chord.Runtime; +using Core.Exceptions; +using Core.Generators; +using Core.Generators.CPlusPlus; +using Core.Generators.CSharp; +using Core.Generators.Dart; +using Core.Generators.Python; +using Core.Generators.Rust; +using Core.Generators.TypeScript; +using Core.Logging; +using Core.Meta; +using Core.Meta.Decorators; +using Spectre.Console; + +namespace Core; + +public class CompilerHost : IDisposable +{ + private readonly FrozenDictionary _decorators; + private readonly FrozenDictionary _generators; + private readonly ExtensionRuntime? _extensionRuntime; + private readonly EnvironmentVariableStore _environmentVariableStore; + private CompilerHost(FrozenDictionary decorators, FrozenDictionary generators, EnvironmentVariableStore environmentVariableStore, ExtensionRuntime? extensionRuntime) + { + _decorators = decorators; + _generators = generators; + _extensionRuntime = extensionRuntime; + _environmentVariableStore = environmentVariableStore; + } + + public bool TryGetDecorator(string identifier, [NotNullWhen(true)] out DecoratorDefinition? decorator) + { + return _decorators.TryGetValue(identifier, out decorator); + } + + public bool TryGetGenerator(string alias, [NotNullWhen(true)] out BaseGenerator? generator) + { + return _generators.TryGetValue(alias, out generator); + } + + public EnvironmentVariableStore EnvironmentVariableStore => _environmentVariableStore; + + public IEnumerable<(string Alias, string Name)> Generators + { + get + { + foreach (var generator in _generators) + { + yield return (generator.Key, generator.Value.Name); + } + } + } + + public IEnumerable Decorators + { + get + { + foreach (var decorator in _decorators) + { + yield return decorator.Value; + } + } + } + + public IEnumerable<(string Name, string Version)> Extensions + { + get + { + if (_extensionRuntime is not null) + { + foreach (var extension in _extensionRuntime.Extensions) + { + yield return (extension.Name, extension.Version); + } + } + } + } + + public void Dispose() + { + _extensionRuntime?.Dispose(); + } + + public class CompilerHostBuilder + { + private static bool _isBuilt = false; + private ExtensionRuntime? _extensionRuntime; + private readonly Dictionary _decorators; + private readonly Dictionary _generators; + private readonly EnvironmentVariableStore _environmentVariableStore; + + private CompilerHostBuilder(string workingDirectory) + { + + _decorators = []; + _generators = []; + _environmentVariableStore = new EnvironmentVariableStore(workingDirectory); + } + + public static CompilerHostBuilder Create(string workingDirectory) + { + return new CompilerHostBuilder(workingDirectory); + } + + public CompilerHost Build() + { + if (_isBuilt) + { + throw new CompilerException("A compiler host has already been built."); + } + _isBuilt = true; + return new CompilerHost(_decorators.ToFrozenDictionary(), _generators.ToFrozenDictionary(), _environmentVariableStore, _extensionRuntime); + } + + public CompilerHostBuilder WithDefaults() + { + return WithDefaultDecorators() + .WithDefaultGenerators(); + } + + public CompilerHostBuilder WithExtensions(Dictionary extensions) + { + _extensionRuntime = new ExtensionRuntime(DotEnv.Generated.Environment.Version, DiagnosticLogger.Instance.Out, DiagnosticLogger.Instance.Error); + foreach (var kv in extensions) + { + Extension? extension; + try + { + extension = _extensionRuntime.LoadExtension(kv.Key, kv.Value); + } + catch (Exception e) when (e is ExtensionRuntimeException) + { + throw new CompilerException("Extension runtime encountered a fatal exception", e); + } + catch (Exception e) when (e is ExtensionException) + { + DiagnosticLogger.Instance.Error.MarkupLineInterpolated($"[maroon]Skipping extension: {e.Message}[/]"); + continue; + } + if (extension.Decorators is { Count: > 0 }) + { + foreach (var decorator in extension.Decorators) + { + WithDecorator(() => + { + var parameters = new List(); + if (decorator.Parameters is not null) + { + foreach (var parameter in decorator.Parameters) + { + + string? defaultValue = parameter.DefaultValue is null ? null : parameter.DefaultValue.ToString(); + DecoratorParameterValueValidator? validator = null; + if (parameter.Validator is not null) + { + if (string.IsNullOrWhiteSpace(parameter.ValidationErrorReason)) + { + throw new InvalidOperationException($"Decorator parameter {parameter.Identifier} has a validator but no validation error reason."); + } + validator = new DecoratorParameterValueValidator(parameter.Validator, parameter.ValidationErrorReason); + } + parameters.Add(new Meta.Decorators.DecoratorParameter(parameter.Identifier, parameter.Description, BaseTypeHelpers.FromTokenString(parameter.Type), parameter.Required, defaultValue, validator)); + } + } + return new ContributedDecorator(decorator.Identifier, decorator.Description, (DecoratorTargets)decorator.Targets, decorator.AllowMultiple, parameters.ToArray()); + }); + } + } + + if (extension.Contributions is ChordGenerator chordGenerator) + { + WithGenerator(chordGenerator.Alias, new ContributedGenerator(extension)); + } + } + return this; + } + + public CompilerHostBuilder WithDefaultGenerators() + { + return WithGenerator("cs", new CSharpGenerator()) + .WithGenerator("dart", new DartGenerator()) + .WithGenerator("rust", new RustGenerator()) + .WithGenerator("py", new PythonGenerator()) + .WithGenerator("ts", new TypeScriptGenerator()) + .WithGenerator("cpp", new CPlusPlusGenerator()); + } + + public CompilerHostBuilder WithGenerator(string alias, BaseGenerator generator) + { + if (_generators.ContainsKey(alias)) + { + throw new CompilerException($"unable to register '{alias}': already registered."); + } + _generators.Add(alias, generator); + return this; + } + + + public CompilerHostBuilder WithDefaultDecorators() + { + return WithDecorator(new FlagsDecorator()) + .WithDecorator(new OpcodeDecorator()) + .WithDecorator(new DeprecatedDecorator()) + .WithDecorator(new DebugDecorator()); + } + + public CompilerHostBuilder WithDecorator(DecoratorDefinition decorator) + { + if (_decorators.ContainsKey(decorator.Identifier)) + { + throw new CompilerException($"unable to register '{decorator.Identifier}': already registered."); + } + _decorators.Add(decorator.Identifier, decorator); + return this; + } + + public CompilerHostBuilder WithDecorator(Func mapper) + { + var instance = mapper(); + if (instance is null) + { + throw new CompilerException("mapper returned null"); + } + if (_decorators.ContainsKey(instance.Identifier)) + { + throw new CompilerException($"unable to register '{instance.Identifier}': already registered."); + } + _decorators.Add(instance.Identifier, instance); + return this; + } + } +} \ No newline at end of file diff --git a/Core/Core.csproj b/Core/Core.csproj index cbe832fe..bc88bc3e 100644 --- a/Core/Core.csproj +++ b/Core/Core.csproj @@ -2,9 +2,9 @@ true - net7.0 + net8.0 enable - 11.0 + preview BebopCore Core AnyCPU @@ -13,7 +13,8 @@ 0.0.1 $([System.DateTime]::UtcNow.ToString(`yyyyMMdd-HHmm`)) Debug;Release - true + true + true @@ -35,6 +36,11 @@ all + + + + + diff --git a/Core/Core.sln b/Core/Core.sln new file mode 100644 index 00000000..6f7bd085 --- /dev/null +++ b/Core/Core.sln @@ -0,0 +1,25 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 17 +VisualStudioVersion = 17.5.002.0 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Core", "Core.csproj", "{F6301C83-5306-47BE-909F-17D1F64FC809}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {F6301C83-5306-47BE-909F-17D1F64FC809}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {F6301C83-5306-47BE-909F-17D1F64FC809}.Debug|Any CPU.Build.0 = Debug|Any CPU + {F6301C83-5306-47BE-909F-17D1F64FC809}.Release|Any CPU.ActiveCfg = Release|Any CPU + {F6301C83-5306-47BE-909F-17D1F64FC809}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {15C51B00-5D46-41A5-B49C-8E0B0A3B13F5} + EndGlobalSection +EndGlobal diff --git a/Core/EnvironmentVariableStore.cs b/Core/EnvironmentVariableStore.cs new file mode 100644 index 00000000..fef9ef6d --- /dev/null +++ b/Core/EnvironmentVariableStore.cs @@ -0,0 +1,102 @@ +using System; +using System.Collections.Frozen; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Security; +using System.Text.RegularExpressions; +using Core.Exceptions; +using Core.Lexer.Tokenization.Models; +namespace Core; +public sealed partial class EnvironmentVariableStore +{ + + private readonly FrozenDictionary _devVariables; + + public EnvironmentVariableStore(string workingDirectory) + { + var devEnvFilePath = Path.Combine(workingDirectory, ".dev.vars"); + if (File.Exists(devEnvFilePath)) + { + _devVariables = ParseDevVars(File.ReadAllText(devEnvFilePath)); + } + else + { + _devVariables = FrozenDictionary.Empty; + } + } + private static FrozenDictionary ParseDevVars(string fileContent) + { + fileContent = fileContent.Replace("\r\n", "\n"); + var result = new Dictionary(); + + foreach (Match match in LineRegex().Matches(fileContent)) + { + if (match.Success) + { + var key = match.Groups[1].Value; + var value = match.Groups[2].Value.Trim(); + + if (value.StartsWith("'") || value.StartsWith("\"") || value.StartsWith("`")) + { + value = UnquoteRegex().Replace(value, "$2"); + } + + if (value.StartsWith("\"")) + { + value = value.Replace("\\n", "\n").Replace("\\r", "\r"); + } + + if (!string.IsNullOrEmpty(key) && !result.ContainsKey(key)) + { + result[key] = value; + } + } + } + return result.ToFrozenDictionary(); + } + + + public int DevVarsCount => _devVariables.Count; + public IEnumerable DevVarNames => _devVariables.Keys; + + public string Replace(string input, List errors, Span span) + { + return TemplateRegex().Replace(input, match => + { + string varName = match.Groups[1].Value; + string? envVar = null; + try + { + envVar = Get(varName); + if (string.IsNullOrEmpty(envVar)) + { + errors.Add(new EnvironmentVariableNotFoundException(span, $"String substitution failed: environment variable '{varName}' was not found.")); + return string.Empty; + } + return envVar.Trim(); + } + catch (SecurityException) + { + errors.Add(new EnvironmentVariableNotFoundException(span, $"String substitution failed: environment variable '{varName}' was not found.")); + return string.Empty; + } + }); + } + + public string? Get(string variableName) + { + if (_devVariables.TryGetValue(variableName, out var value)) + { + return value.Trim(); + } + return Environment.GetEnvironmentVariable(variableName)?.Trim(); + } + + [GeneratedRegex(@"\$\{([^\}]+)\}")] + private static partial Regex TemplateRegex(); + [GeneratedRegex(@"\s*([\w.-]+)(?:\s*=\s*?|:\s+?)(\s*'(?:\\'|[^'])*'|\s*""(?:\\""|[^""])*""|\s*`(?:\\`|[^`])*`|[^#\r\n]+)?\s*(?:#.*)?", RegexOptions.Multiline | RegexOptions.Compiled)] + private static partial Regex LineRegex(); + [GeneratedRegex(@"^(['""`])([\s\S]*)\1$")] + private static partial Regex UnquoteRegex(); +} \ No newline at end of file diff --git a/Core/Exceptions/Exceptions.cs b/Core/Exceptions/Exceptions.cs index 53bee033..82ba6a23 100644 --- a/Core/Exceptions/Exceptions.cs +++ b/Core/Exceptions/Exceptions.cs @@ -23,23 +23,41 @@ public class CompilerException : Exception public CompilerException(string message) : base(message) { } + public CompilerException(string message, Exception innerException) : base(message, innerException) + { + } } + + [Serializable] + public class MalformedBebopConfigException : Exception + { + /// + /// A unique error code identifying the type of exception + /// + public int ErrorCode => 601; + public MalformedBebopConfigException(string message) : base($"Error parsing bebop.json: {message}") + { + } + } + [Serializable] public class SpanException : Exception { - + public Span Span { get; } /// /// A unique error code identifying the type of exception /// public int ErrorCode { get; } public Severity Severity { get; } + public string? Hint { get; } - public SpanException(string message, Span span, int errorCode, Severity severity = Severity.Error) : base(message) + public SpanException(string message, Span span, int errorCode, string? hint = null, Severity severity = Severity.Error) : base(message) { Span = span; ErrorCode = errorCode; Severity = severity; + Hint = hint; } } [Serializable] @@ -48,7 +66,7 @@ class UnrecognizedTokenException : SpanException public UnrecognizedTokenException(char tokenStart, Span span) : base($"Unrecognized token start '{tokenStart}'", span, 100) { } } - + [Serializable] class MultipleDefinitionsException : SpanException { @@ -71,7 +89,7 @@ public InvalidFieldException(Field field, string reason) class UnexpectedTokenException : SpanException { public UnexpectedTokenException(TokenKind expectedKind, Token token, string? hint = null) - : base($"Expected {expectedKind}, but found '{token.Lexeme}' of kind {token.Kind}." + : base($"Expected type {expectedKind}, but found '{token.Lexeme}' of kind {token.Kind}." + (string.IsNullOrWhiteSpace(hint) ? "" : $" (Hint: {hint})"), token.Span, 104) { } public UnexpectedTokenException(Token token, string? hint = null) @@ -96,32 +114,32 @@ public InvalidReadOnlyException(Definition definition) : base($"The 'readonly' modifer cannot be applied to '{definition.Name}' as it is not a struct", definition.Span, 106) { } } [Serializable] - class InvalidDeprecatedAttributeUsageException : SpanException + class InvalidDeprecatedDecoratorUsageException : SpanException { - public InvalidDeprecatedAttributeUsageException(Field field) + public InvalidDeprecatedDecoratorUsageException(Field field) : base($"The field '{field.Name}' cannot be marked as 'deprecated' as it is not a member of a message or enum", field.Span, 107) { } } [Serializable] - class InvalidOpcodeAttributeUsageException : SpanException + class InvalidOpcodeDecoratorUsageException : SpanException { - public InvalidOpcodeAttributeUsageException(Definition definition) - : base($"The definition '{definition.Name}' cannot be marked with an opcode attribute as it is not a message or struct", definition.Span, 108) + public InvalidOpcodeDecoratorUsageException(Definition definition) + : base($"The definition '{definition.Name}' cannot be marked with an opcode identifier as it is not a message or struct", definition.Span, 108) { } } [Serializable] - class InvalidOpcodeAttributeValueException : SpanException + class InvalidOpcodeDecoratorValueException : SpanException { - public InvalidOpcodeAttributeValueException(Definition definition, string reason) + public InvalidOpcodeDecoratorValueException(Definition definition, string reason) : base($"The definition '{definition.Name}' was marked with an" + $" opcode " + - $"attribute containing an invalid value: {reason}", definition.Span, 109) + $"identifier containing an invalid value: {reason}", definition.Span, 109) { } } [Serializable] class DuplicateOpcodeException : SpanException { public DuplicateOpcodeException(RecordDefinition definition) - : base($"Multiple definitions for opcode '{definition.OpcodeAttribute?.Value}'", definition.Span, 110) { } + : base($"Multiple definitions for opcode '{definition.OpcodeDecorator?.Arguments["fourcc"]}'", definition.Span, 110) { } } [Serializable] @@ -223,22 +241,22 @@ public class ReferenceScopeException : SpanException { public ReferenceScopeException(Definition definition, Definition reference, string scopeLabel) : base($"Cannot reference {reference.Name} within {scopeLabel} from {definition.Name}.", definition.Span, 123) - {} + { } } [Serializable] public class UnknownIdentifierException : SpanException { public UnknownIdentifierException(Token identifier) - : base($"The identifier '{identifier.Lexeme}' is not defined at this point. (Try reordering your enum branches.)", identifier.Span, 124) + : base($"The decorator '{identifier.Lexeme}' is not defined at this point. (Try reordering your enum branches.)", identifier.Span, 124) { } } [Serializable] - public class UnknownAttributeException : SpanException + public class UnknownDecoratorException : SpanException { - public UnknownAttributeException(Token attribute) - : base($"The attribute '{attribute.Lexeme}' is recognized.", attribute.Span, 125) + public UnknownDecoratorException(Token identifier) + : base($"The decorator '{identifier.Lexeme}' is not defined.", identifier.Span, 125) { } } @@ -265,7 +283,7 @@ public InvalidEnumTypeException(TypeBase t) : base($"Enums must have an integer underlying type, not {t}.", t.Span, 128) { } } - + [Serializable] class DuplicateServiceMethodNameException : SpanException { @@ -313,13 +331,97 @@ public UnexpectedEndOfFile(Span span) { } } + [Serializable] + class MissingArgumentException : SpanException + { + public MissingArgumentException(string identifier, string paramName, int expectedArgCount, int foundArgCount, Span span, string? hint = null) + : base($"Expected {expectedArgCount} arguments for '{identifier}', found {foundArgCount}. Missing '{paramName}'." + (string.IsNullOrWhiteSpace(hint) ? "" : $" (Hint: {hint})"), span, 136) + { } + } + class InvalidArgumentTypeException : SpanException + { + public InvalidArgumentTypeException(string identifier, string paramName, BaseType expectedType, TokenKind foundKind, Span span, string? hint = null) + : base($"Expected argument for '{paramName}' of '{identifier}' to be of type '{expectedType}', found '{foundKind}'." + (string.IsNullOrWhiteSpace(hint) ? "" : $" (Hint: {hint})"), span, 137) + { } + } + + class InvalidArgumentValueException : SpanException + { + public InvalidArgumentValueException(string identifier, string paramName, string reason, Span span, string? hint = null) + : base($"Invalid value for argument '{paramName}' of '{identifier}': {reason}." + (string.IsNullOrWhiteSpace(hint) ? "" : $" (Hint: {hint})"), span, 138) + { } + } + + class InvalidNamedArgumentException : SpanException + { + public InvalidNamedArgumentException(string identifier, string paramName, Span span, string? hint = null) + : base($"Decorator '{identifier}' does not have a parameter named '{paramName}' " + (string.IsNullOrWhiteSpace(hint) ? "" : $" (Hint: {hint})"), span, 139) + { } + } + + class MultipleDecoratorsException : SpanException + { + public MultipleDecoratorsException(string identifier, Span span, string? hint = null) + : base($"The decorator '{identifier}' cannot be defined multiple times on a single target." + (string.IsNullOrWhiteSpace(hint) ? "" : $" (Hint: {hint})"), span, 140) + { } + } + + + class InvalidDecoratorUsageException : SpanException + { + public InvalidDecoratorUsageException(string identifier, string reason, Span span, string? hint = null) + : base($"Decorator '{identifier}' cannot be applied to this target: {reason}." + (string.IsNullOrWhiteSpace(hint) ? "" : $" (Hint: {hint})"), span, 141) + { } + } + class DecoratorValidationException : SpanException + { + public DecoratorValidationException(string identifier, string reason, Span span, string? hint = null) + : base($"Decorator '{identifier}' is invalid: {reason}." + (string.IsNullOrWhiteSpace(hint) ? "" : $" (Hint: {hint})"), span, 142) + { } + } + + class StackSizeExceededException : SpanException + { + public StackSizeExceededException(string reason, Span span, string? hint = null) + : base($"Stack size exceeded: {reason}", span, 143) + { } + } + + class UnknownParameterException : SpanException + { + public UnknownParameterException(string identifier, string paramName, Span span, string? hint = null) + : base($"Decorator '{identifier}' does not have a parameter named '{paramName}'.", span, 144, hint) + { } + } + + class TooManyArgumentsException : SpanException + { + public TooManyArgumentsException(string identifier, int expectedArgCount, int foundArgCount, Span span, string? hint = null) + : base($"Expected {expectedArgCount} arguments for '{identifier}', found {foundArgCount}.", span, 145, hint) + { } + } + + class MissingValueForArgumentException : SpanException + { + public MissingValueForArgumentException(string identifier, string paramName, Span span, string? hint = null) + : base($"Decorator '{identifier}' is missing a value for parameter '{paramName}'.", span, 146, hint) + { } + } + [Serializable] + public class EnvironmentVariableNotFoundException : SpanException + { + public EnvironmentVariableNotFoundException(Span span, string reason) + : base(reason, span, 147, severity: Severity.Error) + { } + } + [Serializable] public class EnumZeroWarning : SpanException { public EnumZeroWarning(Field field) - : base($"Bebop recommends that 0 in an enum be reserved for a value named 'Unknown', 'Default', or similar. See https://github.com/betwixt-labs/bebop/wiki/Why-should-0-be-a-%22boring%22-value-in-an-enum%3F for more info.", field.Span, 200, Severity.Warning) + : base($"Bebop recommends that 0 in an enum be reserved for a value named 'Unknown', 'Default', or similar.", field.Span, 200, " See https://github.com/betwixt-labs/bebop/wiki/Why-should-0-be-a-%22boring%22-value-in-an-enum%3F for more info.", Severity.Warning) { } } @@ -327,7 +429,8 @@ public EnumZeroWarning(Field field) public class DeprecatedFeatureWarning : SpanException { public DeprecatedFeatureWarning(Span span, string reason) - : base(reason, span, 201, Severity.Warning) + : base(reason, span, 201, severity: Severity.Warning) { } } + } diff --git a/Core/Generators/BaseGenerator.cs b/Core/Generators/BaseGenerator.cs index 2a76b7b3..62483c44 100644 --- a/Core/Generators/BaseGenerator.cs +++ b/Core/Generators/BaseGenerator.cs @@ -1,34 +1,60 @@ using System; +using System.Text.Json; +using System.Threading; +using System.Threading.Tasks; using Core.Meta; namespace Core.Generators { + /// + /// Represents an abstract base class for generating code from Bebop schemas. + /// This class encapsulates the common functionalities needed for various code generators. + /// public abstract class BaseGenerator { /// - /// The schema to generate code from. + /// The Bebop schema from which the code is generated. /// - protected BebopSchema Schema; + protected BebopSchema Schema = default!; - protected BaseGenerator(BebopSchema schema) - { - Schema = schema; - } + /// + /// Configuration settings specific to the generator. + /// + protected GeneratorConfig Config = default!; + + /// + /// Initializes a new instance of the class with a given schema and configuration. + /// + /// The Bebop schema used for code generation. + /// The generator-specific configuration settings. + protected BaseGenerator() { } + + /// + /// Generates code based on the provided Bebop schema. + /// + /// A string containing the generated code. + public abstract ValueTask Compile(BebopSchema schema, GeneratorConfig config, CancellationToken cancellationToken = default); + + /// + /// Writes auxiliary files, if any, associated with the generated code to the specified output directory. + /// + /// The directory path where auxiliary files should be written. + public abstract void WriteAuxiliaryFile(string outputPath); + + /// + /// Retrieves information about any auxiliary files associated with the generated code. + /// + /// An representing the contents and metadata of the auxiliary file, or null if there are no auxiliary files. + public abstract AuxiliaryFile? GetAuxiliaryFile(); /// - /// Generate code for a Bebop schema. + /// Gets the alias of the code generator, which uniquely identifies it among other generators. /// - /// Determines a default language version the generated code will target. - /// Determines which components of a service will be generated. default to both client and server. - /// Whether a generation notice should be written at the top of files. This is true by default. - /// Whether a binary schema should be emitted. This is false by default. - /// The generated code. - public abstract string Compile(Version? languageVersion, TempoServices services = TempoServices.Both, bool writeGeneratedNotice = true, bool emitBinarySchema = false); + public abstract string Alias { get; set; } /// - /// Write auxiliary files to an output directory path. + /// Gets the friendly name of the code generator, which is used for display purposes. /// - /// The output directory path. - public abstract void WriteAuxiliaryFiles(string outputPath); + public abstract string Name { get; set; } } -} \ No newline at end of file +} diff --git a/Core/Generators/CPlusPlus/CPlusPlusGenerator.cs b/Core/Generators/CPlusPlus/CPlusPlusGenerator.cs index 6ba74359..c2f382a6 100644 --- a/Core/Generators/CPlusPlus/CPlusPlusGenerator.cs +++ b/Core/Generators/CPlusPlus/CPlusPlusGenerator.cs @@ -6,6 +6,8 @@ using System.Text.Encodings.Web; using System.Text.Json; using System.Text.RegularExpressions; +using System.Threading; +using System.Threading.Tasks; using Core.Meta; using Core.Meta.Extensions; @@ -15,7 +17,7 @@ public class CPlusPlusGenerator : BaseGenerator { const int indentStep = 2; - public CPlusPlusGenerator(BebopSchema schema) : base(schema) { } + public CPlusPlusGenerator() : base() { } private string FormatDocumentation(string documentation, int spaces) { @@ -51,7 +53,7 @@ private string CompileEncodeMessage(MessageDefinition definition) builder.AppendLine($"const auto start = writer.length();"); foreach (var field in definition.Fields) { - if (field.DeprecatedAttribute != null) + if (field.DeprecatedDecorator != null) { continue; } @@ -330,12 +332,11 @@ private string TypeName(in TypeBase type) private static string EscapeStringLiteral(string value) { - // C++ accepts \u0000 style escape sequences, so we can escape the string JSON-style. - var options = new JsonSerializerOptions { Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping }; - return JsonSerializer.Serialize(value, options); + return $@"""{value.EscapeString()}"""; } - private string EmitLiteral(Literal literal) { + private string EmitLiteral(Literal literal) + { return literal switch { BoolLiteral bl => bl.Value ? "true" : "false", @@ -354,10 +355,12 @@ private string EmitLiteral(Literal literal) { /// Generate code for a Bebop schema. /// /// The generated code. - public override string Compile(Version? languageVersion, TempoServices services = TempoServices.Both, bool writeGeneratedNotice = true, bool emitBinarySchema = false) + public override ValueTask Compile(BebopSchema schema, GeneratorConfig config, CancellationToken cancellationToken = default) { + Schema = schema; + Config = config; var builder = new StringBuilder(); - if (writeGeneratedNotice) + if (Config.EmitNotice) { builder.AppendLine(GeneratorUtils.GetXmlAutoGeneratedNotice()); } @@ -373,9 +376,9 @@ public override string Compile(Version? languageVersion, TempoServices services builder.AppendLine("#include \"bebop.hpp\""); builder.AppendLine(""); - if (!string.IsNullOrWhiteSpace(Schema.Namespace)) + if (!string.IsNullOrWhiteSpace(Config.Namespace)) { - builder.AppendLine($"namespace {Schema.Namespace} {{"); + builder.AppendLine($"namespace {Config.Namespace} {{"); builder.AppendLine(""); } @@ -396,9 +399,9 @@ public override string Compile(Version? languageVersion, TempoServices services { builder.Append(FormatDocumentation(field.Documentation, 2)); } - if (field.DeprecatedAttribute != null) + if (field.DeprecatedDecorator is not null && field.DeprecatedDecorator.TryGetValue("reason", out var reason)) { - builder.AppendLine($" /// @deprecated {field.DeprecatedAttribute.Value}"); + builder.AppendLine($" /// @deprecated {reason}"); } builder.AppendLine($" {field.Name} = {field.ConstantValue},"); } @@ -408,9 +411,9 @@ public override string Compile(Version? languageVersion, TempoServices services case RecordDefinition td: builder.AppendLine($"struct {td.Name} {{"); builder.AppendLine($" static const size_t minimalEncodedSize = {td.MinimalEncodedSize(Schema)};"); - if (td.OpcodeAttribute != null) + if (td.OpcodeDecorator is not null && td.OpcodeDecorator.TryGetValue("fourcc", out var fourcc)) { - builder.AppendLine($" static const uint32_t opcode = {td.OpcodeAttribute.Value};"); + builder.AppendLine($" static const uint32_t opcode = {fourcc};"); builder.AppendLine(""); } @@ -425,9 +428,9 @@ public override string Compile(Version? languageVersion, TempoServices services { builder.Append(FormatDocumentation(field.Documentation, 2)); } - if (field.DeprecatedAttribute != null) + if (field.DeprecatedDecorator is not null && field.DeprecatedDecorator.TryGetValue("reason", out var reason)) { - builder.AppendLine($" /// @deprecated {field.DeprecatedAttribute.Value}"); + builder.AppendLine($" /// @deprecated {reason}"); } builder.AppendLine($" {(isMessage ? Optional(type) : type)} {field.Name};"); } @@ -507,13 +510,13 @@ public override string Compile(Version? languageVersion, TempoServices services } } - if (!string.IsNullOrWhiteSpace(Schema.Namespace)) + if (!string.IsNullOrWhiteSpace(Config.Namespace)) { - builder.AppendLine($"}} // namespace {Schema.Namespace}"); + builder.AppendLine($"}} // namespace {Config.Namespace}"); builder.AppendLine(""); } - return builder.ToString(); + return ValueTask.FromResult(builder.ToString()); } private const string _bytePattern = @"(?:"")?(\b(1?[0-9]{1,2}|2[0-4][0-9]|25[0-5])\b)(?:"")?"; @@ -522,7 +525,8 @@ public override string Compile(Version? languageVersion, TempoServices services private static readonly Regex _patchRegex = new Regex($@"(?<=BEBOPC_VER_PATCH\s){_bytePattern}", RegexOptions.Compiled | RegexOptions.Singleline); private static readonly Regex _informationalRegex = new Regex($@"(?<=BEBOPC_VER_INFO\s){_bytePattern}", RegexOptions.Compiled | RegexOptions.Singleline); - public override void WriteAuxiliaryFiles(string outputPath) + + public override AuxiliaryFile? GetAuxiliaryFile() { var assembly = Assembly.GetEntryAssembly()!; var runtime = assembly.GetManifestResourceNames()!.FirstOrDefault(n => n.Contains("bebop.hpp"))!; @@ -542,7 +546,20 @@ public override void WriteAuxiliaryFiles(string outputPath) } } - File.WriteAllText(Path.Join(outputPath, "bebop.hpp"), builder.ToString()); + var encoding = new UTF8Encoding(false); + return new AuxiliaryFile("bebop.hpp", encoding.GetBytes(builder.ToString())); } + + public override void WriteAuxiliaryFile(string outputPath) + { + var auxiliary = GetAuxiliaryFile(); + if (auxiliary is not null) + { + File.WriteAllBytes(Path.Join(outputPath, auxiliary.Name), auxiliary.Content); + } + } + + public override string Alias { get => "cpp"; set => throw new NotImplementedException(); } + public override string Name { get => "C++"; set => throw new NotImplementedException(); } } } diff --git a/Core/Generators/CSharp/CSharpGenerator.cs b/Core/Generators/CSharp/CSharpGenerator.cs index a5ceea34..0bd84929 100644 --- a/Core/Generators/CSharp/CSharpGenerator.cs +++ b/Core/Generators/CSharp/CSharpGenerator.cs @@ -3,6 +3,8 @@ using System.Text; using System.Text.Encodings.Web; using System.Text.Json; +using System.Threading; +using System.Threading.Tasks; using Core.Meta; using Core.Meta.Extensions; @@ -13,19 +15,22 @@ public class CSharpGenerator : BaseGenerator private Version LanguageVersion = CSharpNine; - public CSharpGenerator(BebopSchema schema) : base(schema) + public CSharpGenerator() : base() { + } - public override string Compile(Version? languageVersion, TempoServices services = TempoServices.Both, bool writeGeneratedNotice = true, bool emitBinarySchema = false) + public override ValueTask Compile(BebopSchema schema, GeneratorConfig config, CancellationToken cancellationToken = default) { - if (languageVersion is not null) + Config = config; + Schema = schema; + if (Version.TryParse(config.GetOptionRawValue("langVersion"), out var langVersion)) { - LanguageVersion = languageVersion; + LanguageVersion = langVersion; } var builder = new IndentedStringBuilder(); - if (writeGeneratedNotice) + if (Config.EmitNotice) { builder .AppendLine(GeneratorUtils.GetXmlAutoGeneratedNotice()) @@ -34,9 +39,9 @@ public override string Compile(Version? languageVersion, TempoServices services .AppendLine("//"); } - if (!string.IsNullOrWhiteSpace(Schema.Namespace)) + if (!string.IsNullOrWhiteSpace(Config.Namespace)) { - builder.AppendLine($"namespace {Schema.Namespace.ToPascalCase()} {{"); + builder.AppendLine($"namespace {Config.Namespace.ToPascalCase()} {{"); builder.Indent(indentStep).AppendLine(); } @@ -94,10 +99,9 @@ public override string Compile(Version? languageVersion, TempoServices services { builder.AppendLine(FormatDocumentation(field.Documentation, 6)); } - if (field.DeprecatedAttribute is not null && - !string.IsNullOrWhiteSpace(field.DeprecatedAttribute.Value)) + if (field.DeprecatedDecorator is not null && field.DeprecatedDecorator.Arguments.TryGetValue("reason", out var reason)) { - builder.AppendLine($"[System.Obsolete(\"{field.DeprecatedAttribute.Value}\")]"); + builder.AppendLine($"[System.Obsolete(\"{reason}\")]"); } builder.AppendLine($"{field.Name.ToPascalCase()} = {field.ConstantValue}{(i + 1 < ed.Members.Count ? "," : "")}"); } @@ -112,7 +116,7 @@ public override string Compile(Version? languageVersion, TempoServices services var recordAttribute = fd switch { MessageDefinition => "[global::Bebop.Attributes.BebopRecord(global::Bebop.Runtime.BebopKind.Message)]", - StructDefinition { IsReadOnly: true } => "[global::Bebop.Attributes.BebopRecord(global::Bebop.Runtime.BebopKind.Struct, true)]", + StructDefinition { IsMutable: true } => "[global::Bebop.Attributes.BebopRecord(global::Bebop.Runtime.BebopKind.Struct, true)]", StructDefinition => "[global::Bebop.Attributes.BebopRecord(global::Bebop.Runtime.BebopKind.Struct)]", _ => string.Empty }; @@ -124,10 +128,9 @@ public override string Compile(Version? languageVersion, TempoServices services { builder.AppendLine("#nullable enable"); } - - if (fd.OpcodeAttribute is { Value: not null }) + if (fd.OpcodeDecorator is not null && fd.OpcodeDecorator.Arguments.TryGetValue("fourcc", out var fourcc)) { - builder.AppendLine($"public const uint OpCode = {fd.OpcodeAttribute.Value};"); + builder.AppendLine($"public const uint OpCode = {fourcc};"); } builder.AppendLine("/// "); @@ -144,10 +147,9 @@ public override string Compile(Version? languageVersion, TempoServices services { builder.AppendLine(FormatDocumentation(field.Documentation, 0)); } - if (field.DeprecatedAttribute is not null && - !string.IsNullOrWhiteSpace(field.DeprecatedAttribute.Value)) + if (field.DeprecatedDecorator is not null && field.DeprecatedDecorator.Arguments.TryGetValue("reason", out var reason)) { - builder.AppendLine($"[System.Obsolete(\"{field.DeprecatedAttribute.Value}\")]"); + builder.AppendLine($"[System.Obsolete(\"{reason}\")]"); } if (fd is StructDefinition) { @@ -159,7 +161,7 @@ public override string Compile(Version? languageVersion, TempoServices services } var type = TypeName(field.Type, string.Empty); var opt = fd is MessageDefinition && IsNullableType(field.Type) ? "?" : ""; - var setOrInit = fd is StructDefinition { IsReadOnly: true } ? LanguageVersion == CSharpNine ? "init" : "private set" : "set"; + var setOrInit = fd is StructDefinition { IsMutable: false } ? LanguageVersion == CSharpNine ? "init" : "private set" : "set"; builder.AppendLine($"public {type}{opt} {field.Name.ToPascalCase()} {{ get; {setOrInit}; }}"); } @@ -265,20 +267,18 @@ public override string Compile(Version? languageVersion, TempoServices services } - if (!string.IsNullOrWhiteSpace(Schema.Namespace)) + if (!string.IsNullOrWhiteSpace(Config.Namespace)) { builder.Dedent(indentStep); builder.AppendLine("}"); } - return builder.ToString(); + return ValueTask.FromResult(builder.ToString()); } #region Const private static string EscapeStringLiteral(string value) { - // C# accepts \u0000 style escape sequences, so we can escape the string JSON-style. - var options = new JsonSerializerOptions { Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping }; - return JsonSerializer.Serialize(value, options); + return $@"""{value.EscapeString()}"""; } private string EmitLiteral(Literal literal) @@ -314,7 +314,7 @@ private string CompileEncodeMessage(MessageDefinition definition) builder.AppendLine("var start = writer.Length;"); foreach (var field in definition.Fields) { - if (field.DeprecatedAttribute is not null) + if (field.DeprecatedDecorator is not null) { continue; } @@ -647,9 +647,9 @@ void CompileUnionConcreteClass() builder.AppendLine(GeneratedAttribute); builder.AppendLine(recordAttribute); builder.AppendLine($"public partial class {ud.ClassName()} : {PrefixNamespace(ud.BaseClassName())}<{genericTypeArguments}> {{").Indent(indentStep).AppendLine(); - if (ud.OpcodeAttribute is { Value: not null }) + if (ud.OpcodeDecorator is not null && ud.OpcodeDecorator.Arguments.TryGetValue("fourcc", out var fourcc)) { - builder.AppendLine($"public const uint OpCode = {ud.OpcodeAttribute.Value};"); + builder.AppendLine($"public const uint OpCode = {fourcc};"); } builder.AppendLine("/// "); @@ -1233,7 +1233,7 @@ DefinedType dt when dt.IsEnum(Schema) => true, private string PrefixNamespace(string definitionName) { - var nameSpace = string.IsNullOrWhiteSpace(Schema.Namespace) ? string.Empty : $"global::{Schema.Namespace.ToPascalCase()}."; + var nameSpace = string.IsNullOrWhiteSpace(Config.Namespace) ? string.Empty : $"global::{Config.Namespace.ToPascalCase()}."; return $"{nameSpace}{definitionName}"; } @@ -1314,10 +1314,15 @@ private static string As(string from, string to, string value) } - public override void WriteAuxiliaryFiles(string outputPath) + public override AuxiliaryFile? GetAuxiliaryFile() => null; + + public override void WriteAuxiliaryFile(string outputPath) { } + public override string Alias { get => "cs"; set => throw new NotImplementedException(); } + public override string Name { get => "C#"; set => throw new NotImplementedException(); } + #endregion #region Consts diff --git a/Core/Generators/ContributedGenerator.cs b/Core/Generators/ContributedGenerator.cs new file mode 100644 index 00000000..2fa4f89c --- /dev/null +++ b/Core/Generators/ContributedGenerator.cs @@ -0,0 +1,69 @@ +using System; +using System.IO; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using Chord.Common; +using Chord.Runtime; +using Core.Exceptions; +using Core.Meta; +using Core.Meta.Extensions; + +namespace Core.Generators; + +public class ContributedGenerator : BaseGenerator +{ + private readonly Extension _extension; + + public ContributedGenerator(Extension extension) + { + _extension = extension; + if (extension.Contributions is not ChordGenerator chordGenerator) + { + throw new InvalidOperationException($"Extension {extension.Name} does not contribute a ChordGenerator."); + } + Alias = chordGenerator.Alias; + Name = chordGenerator.Name; + } + + public override async ValueTask Compile(BebopSchema schema, GeneratorConfig config, CancellationToken cancellationToken) + { + var context = new GeneratorContext(schema, config); + return await _extension.ChordCompileAsync(context.ToString(), cancellationToken); + } + + public override AuxiliaryFile? GetAuxiliaryFile() + { + if (_extension.PackedFiles is not { Count: > 0 }) + { + return null; + } + var packedFile = _extension.PackedFiles.Where(f => f.Alias == Alias).FirstOrDefault(); + if (packedFile is null) + { + return null; + } + return new AuxiliaryFile(packedFile.Name, packedFile.Data); + } + + public override void WriteAuxiliaryFile(string outputPath) + { + var auxiliary = GetAuxiliaryFile(); + if (auxiliary is not null) + { + if (outputPath.IsPathAttemptingTraversal()) + { + throw new CompilerException($"Output path {outputPath} is attempting to traverse the directory structure."); + } + if (auxiliary.Name.IsPathAttemptingTraversal()) + { + throw new CompilerException($"Auxiliary file name {auxiliary.Name} is attempting to traverse the directory structure."); + } + var filePath = Path.GetFullPath(Path.Join(outputPath, auxiliary.Name)); + File.WriteAllBytes(filePath, auxiliary.Content); + } + } + + public override string Alias { get; set; } + public override string Name { get; set; } +} \ No newline at end of file diff --git a/Core/Generators/Dart/DartGenerator.cs b/Core/Generators/Dart/DartGenerator.cs index 6141d84e..2e57f051 100644 --- a/Core/Generators/Dart/DartGenerator.cs +++ b/Core/Generators/Dart/DartGenerator.cs @@ -5,6 +5,8 @@ using System.Text; using System.Text.Encodings.Web; using System.Text.Json; +using System.Threading; +using System.Threading.Tasks; using Core.Meta; using Core.Meta.Extensions; @@ -14,7 +16,7 @@ public class DartGenerator : BaseGenerator { const int indentStep = 2; - public DartGenerator(BebopSchema schema) : base(schema) { } + public DartGenerator() : base() { } private string FormatDocumentation(string documentation, int spaces) { @@ -49,7 +51,7 @@ private string CompileEncodeMessage(MessageDefinition definition) builder.AppendLine($"final start = view.length;"); foreach (var field in definition.Fields) { - if (field.DeprecatedAttribute != null) + if (field.DeprecatedDecorator != null) { continue; } @@ -277,12 +279,11 @@ private string TypeName(in TypeBase type) private static string EscapeStringLiteral(string value) { - // Dart accepts \u0000 style escape sequences, so we can escape the string JSON-style. - var options = new JsonSerializerOptions { Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping }; - return JsonSerializer.Serialize(value, options); + return $@"""{value.EscapeString()}"""; } - private string EmitLiteral(Literal literal) { + private string EmitLiteral(Literal literal) + { return literal switch { BoolLiteral bl => bl.Value ? "true" : "false", @@ -301,8 +302,10 @@ private string EmitLiteral(Literal literal) { /// Generate code for a Bebop schema. /// /// The generated code. - public override string Compile(Version? languageVersion, TempoServices services = TempoServices.Both, bool writeGeneratedNotice = true, bool emitBinarySchema = false) + public override ValueTask Compile(BebopSchema schema, GeneratorConfig config, CancellationToken cancellationToken = default) { + Schema = schema; + Config = config; var builder = new StringBuilder(); builder.AppendLine("import 'dart:typed_data';"); builder.AppendLine("import 'package:meta/meta.dart';"); @@ -329,9 +332,9 @@ public override string Compile(Version? languageVersion, TempoServices services { builder.Append(FormatDocumentation(field.Documentation, 2)); } - if (field.DeprecatedAttribute != null) + if (field.DeprecatedDecorator is not null && field.DeprecatedDecorator.TryGetValue("reason", out var reason)) { - builder.AppendLine($" /// @deprecated {field.DeprecatedAttribute.Value}"); + builder.AppendLine($" /// @deprecated {reason}"); } builder.AppendLine($" static const {field.Name} = {ed.Name}.fromRawValue({field.ConstantValue});"); } @@ -347,11 +350,11 @@ public override string Compile(Version? languageVersion, TempoServices services { builder.Append(FormatDocumentation(field.Documentation, 2)); } - if (field.DeprecatedAttribute != null) + if (field.DeprecatedDecorator is not null && field.DeprecatedDecorator.TryGetValue("reason", out var reason)) { - builder.AppendLine($" /// @deprecated {field.DeprecatedAttribute.Value}"); + builder.AppendLine($" /// @deprecated {reason}"); } - var final = fd is StructDefinition { IsReadOnly: true } ? "final " : ""; + var final = fd is StructDefinition { IsMutable: false } ? "final " : ""; var optional = fd is MessageDefinition ? "?" : ""; builder.AppendLine($" {final}{type}{optional} {field.Name};"); } @@ -361,7 +364,7 @@ public override string Compile(Version? languageVersion, TempoServices services } else { - builder.AppendLine($" {(fd is StructDefinition { IsReadOnly: true } ? "const " : "")}{fd.Name}({{"); + builder.AppendLine($" {(fd is StructDefinition { IsMutable: false } ? "const " : "")}{fd.Name}({{"); foreach (var field in fd.Fields) { builder.AppendLine($" required this.{field.Name},"); @@ -369,9 +372,9 @@ public override string Compile(Version? languageVersion, TempoServices services builder.AppendLine(" });"); } builder.AppendLine(""); - if (fd.OpcodeAttribute != null) + if (fd.OpcodeDecorator is not null && fd.OpcodeDecorator.TryGetValue("fourcc", out var fourcc)) { - builder.AppendLine($" static const int opcode = {fd.OpcodeAttribute.Value};"); + builder.AppendLine($" static const int opcode = {fourcc};"); builder.AppendLine(""); } builder.AppendLine($" static Uint8List encode({fd.Name} message) {{"); @@ -406,12 +409,16 @@ public override string Compile(Version? languageVersion, TempoServices services } } - return builder.ToString(); + return ValueTask.FromResult(builder.ToString()); } - public override void WriteAuxiliaryFiles(string outputPath) + public override AuxiliaryFile? GetAuxiliaryFile() => null; + public override void WriteAuxiliaryFile(string outputPath) { // There is nothing to do here. } + + public override string Alias { get => "dart"; set => throw new NotImplementedException(); } + public override string Name { get => "Dart"; set => throw new NotImplementedException(); } } } diff --git a/Core/Generators/GeneratorConfig.cs b/Core/Generators/GeneratorConfig.cs new file mode 100644 index 00000000..729ecc15 --- /dev/null +++ b/Core/Generators/GeneratorConfig.cs @@ -0,0 +1,127 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text.Json.Serialization; + +namespace Core.Generators +{ + /// + /// Represents a configuration for a generator. + /// + public sealed record GeneratorConfig + { + public GeneratorConfig(string alias, string outFile) : this(alias, outFile, TempoServices.Both, true, string.Empty, true, null) + { + + } + + //// + /// Initializes a new instance of the class with all parameters. + /// + public GeneratorConfig(string alias, + string outFile, + TempoServices services, + bool emitNotice, + string @namespace, + bool emitBinarySchema, + Dictionary? options) + { + ArgumentNullException.ThrowIfNullOrWhiteSpace(alias, nameof(alias)); + ArgumentNullException.ThrowIfNullOrWhiteSpace(outFile, nameof(outFile)); + Alias = alias; + OutFile = outFile; + Services = services; + EmitNotice = emitNotice; + EmitBinarySchema = emitBinarySchema; + Namespace = string.IsNullOrWhiteSpace(@namespace) ? string.Empty : @namespace; + Options = options ?? new Dictionary(StringComparer.OrdinalIgnoreCase); + } + + [JsonIgnore] + public string Alias { get; init; } + public string OutFile { get; init; } + public TempoServices Services { get; init; } + public bool EmitNotice { get; init; } + public bool EmitBinarySchema { get; init; } + public string Namespace { get; init; } + private Dictionary Options { get; init; } + + public int OptionCount => Options.Count; + + /// + /// Gets a boolean option value. + /// + public bool GetOptionBoolValue(string key, bool defaultValue = false) + { + return Options.TryGetValue(key, out var value) + ? bool.TryParse(value, out var boolValue) + ? boolValue + : defaultValue + : defaultValue; + } + + /// + /// Gets an enum option value. + /// + public T GetOptionEnumValue(string key, T? defaultValue = null) where T : struct, Enum + { + return Options.TryGetValue(key, out var value) + ? Enum.TryParse(value, out var enumValue) + ? enumValue + : defaultValue ?? default + : defaultValue ?? default; + } + + /// + /// Gets a raw option value. + /// + public string? GetOptionRawValue(string key) + { + return Options.TryGetValue(key, out var value) ? value : null; + } + + /// + /// Gets an integer option value. + /// + public int? GetOptionIntValue(string key) + { + return Options.TryGetValue(key, out var value) + ? int.TryParse(value, out var intValue) + ? intValue + : null + : null; + } + + /// + /// Adds a new option to the configuration. + /// + /// The key of the option. + /// The value of the option. + public void AddOption(string key, string value) + { + Options.Add(key, value); + } + + /// + /// Adds multiple options to the configuration. + /// + /// The options to add, represented as a dictionary. + public void AddOptions(Dictionary options) + { + foreach (var (key, value) in options) + { + Options.Add(key, value); + } + } + + public KeyValuePair[] GetOptions() + { + var options = new KeyValuePair[Options.Count]; + for (var i = 0; i < options.Length; i++) + { + options[i] = Options.ElementAt(i); + } + return options; + } + } +} \ No newline at end of file diff --git a/Core/Generators/GeneratorContext.cs b/Core/Generators/GeneratorContext.cs new file mode 100644 index 00000000..51d45d4b --- /dev/null +++ b/Core/Generators/GeneratorContext.cs @@ -0,0 +1,466 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Numerics; +using System.Text.Json; +using System.Text.Json.Serialization; +using Core.Exceptions; +using Core.Meta; +using Core.Meta.Decorators; +using Core.Parser; +namespace Core.Generators; +public record GeneratorContext(BebopSchema Schema, GeneratorConfig Config) +{ + public override string ToString() => JsonSerializer.Serialize(this, JsonContext.Default.GeneratorContext); +} + +internal class GeneratorContextConverter : JsonConverter +{ + public override GeneratorContext Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + { + throw new NotImplementedException(); + } + + public override void Write(Utf8JsonWriter writer, GeneratorContext value, JsonSerializerOptions options) + { + writer.WriteStartObject(); + + writer.WriteStartObject("definitions"); + var aggregates = value.Schema.Definitions.Where(x => x.Value is StructDefinition or MessageDefinition or EnumDefinition); + foreach (var (key, def) in aggregates) + { + writer.WriteStartObject(key); + WriteAggregateDefinition(writer, def, value.Schema, options); + writer.WriteEndObject(); + } + var unions = value.Schema.Definitions.Where(x => x.Value is UnionDefinition); + foreach (var (key, def) in unions) + { + writer.WriteStartObject(key); + WriteUnionDefinition(writer, def, value.Schema, options); + writer.WriteEndObject(); + } + writer.WriteEndObject(); + + writer.WriteStartObject("services"); + var services = value.Schema.Definitions.Values.OfType(); + foreach (var service in services) + { + writer.WriteStartObject(service.Name); + WriteServiceDefinition(writer, service, value.Schema, options); + writer.WriteEndObject(); + } + writer.WriteEndObject(); + + var constants = value.Schema.Definitions.Values.OfType(); + writer.WriteStartObject("constants"); + foreach (var constant in constants) + { + writer.WriteStartObject(constant.Name); + WriteConstantDefinition(writer, constant, value.Schema, options); + writer.WriteEndObject(); + } + writer.WriteEndObject(); + + writer.WriteStartObject("config"); + WriteGeneratorConfig(value.Config, writer); + writer.WriteEndObject(); + + writer.WriteEndObject(); + } + + private static void WriteGeneratorConfig(GeneratorConfig config, Utf8JsonWriter writer) + { + writer.WriteString("alias", config.Alias); + writer.WriteString("outFile", config.OutFile); + writer.WriteString("namespace", config.Namespace); + writer.WriteBoolean("emitNotice", config.EmitNotice); + writer.WriteBoolean("emitBinarySchema", config.EmitBinarySchema); + writer.WriteString("services", config.Services switch + { + TempoServices.None => "none", + TempoServices.Both => "both", + TempoServices.Client => "client", + TempoServices.Server => "server", + _ => throw new CompilerException("Unknown service type") + }); + writer.WriteStartObject("options"); + foreach (var (key, val) in config.GetOptions()) + { + writer.WriteString(key, val); + } + writer.WriteEndObject(); + } + + private static void WriteConstantDefinition(Utf8JsonWriter writer, ConstDefinition constant, BebopSchema schema, JsonSerializerOptions options) + { + writer.WriteString("kind", GetDefinitionKind(constant)); + if (!string.IsNullOrWhiteSpace(constant.Documentation)) + { + writer.WriteString("documentation", constant.Documentation); + } + writer.WriteString("type", constant.Value.Type.ToTokenString()); + var literal = constant.Value; + switch (literal) + { + case BoolLiteral bl: + writer.WriteBoolean("value", bl.Value); + break; + case IntegerLiteral il: + writer.WriteString("value", il.Value); + break; + case FloatLiteral fl: + writer.WriteString("value", fl.Value); + break; + case StringLiteral sl: + writer.WriteString("value", sl.Value); + break; + case GuidLiteral gl: + writer.WriteString("value", gl.Value.ToString("N")); + break; + default: + throw new CompilerException($"Unknown literal type {literal.GetType()}"); + } + } + + private static void WriteServiceDefinition(Utf8JsonWriter writer, ServiceDefinition def, BebopSchema schema, JsonSerializerOptions options) + { + + writer.WriteString("kind", GetDefinitionKind(def)); + if (!string.IsNullOrWhiteSpace(def.Documentation)) + { + writer.WriteString("documentation", def.Documentation); + } + WriteDecorators(writer, def.Decorators); + writer.WriteStartObject("methods"); + foreach (var method in def.Methods) + { + var methodDefinition = method.Definition; + writer.WriteStartObject(methodDefinition.Name); + WriteDecorators(writer, method.Decorators); + if (!string.IsNullOrWhiteSpace(methodDefinition.Documentation)) + { + writer.WriteString("documentation", methodDefinition.Documentation); + } + writer.WriteString("type", RpcSchema.GetMethodTypeName(methodDefinition.Type)); + writer.WriteString("requestType", methodDefinition.RequestDefinition.ToString()); + writer.WriteString("responseType", methodDefinition.ResponseDefintion.ToString()); + writer.WriteNumber("id", method.Id); + writer.WriteEndObject(); + } + writer.WriteEndObject(); + } + + private static void WriteUnionDefinition(Utf8JsonWriter writer, Definition def, BebopSchema schema, JsonSerializerOptions options) + { + if (def is not UnionDefinition ud) + { + throw new CompilerException("Expected UnionDefinition"); + } + writer.WriteString("kind", GetDefinitionKind(def)); + writer.WriteNumber("minimalEncodedSize", ud.MinimalEncodedSize(schema)); + if (!string.IsNullOrWhiteSpace(def.Documentation)) + { + writer.WriteString("documentation", def.Documentation); + } + var branches = def.Dependencies(); + var branchCount = branches.Count(); + if (branchCount < 0 || branchCount > byte.MaxValue) + { + throw new CompilerException($"{def.Name} exceeds maximum branches: has {branchCount} branches"); + } + WriteDecorators(writer, ud.Decorators); + writer.WriteStartObject("branches"); + for (var i = 0; i < branches.Count(); i++) + { + var branch = branches.ElementAt(i); + // discriminator + writer.WriteNumber(branch, i + 1); + } + writer.WriteEndObject(); + } + + private static void WriteAggregateDefinition(Utf8JsonWriter writer, Definition def, BebopSchema schema, JsonSerializerOptions options) + { + writer.WriteString("kind", GetDefinitionKind(def)); + if (!string.IsNullOrWhiteSpace(def.Documentation)) + { + writer.WriteString("documentation", def.Documentation); + } + WriteDecorators(writer, def switch + { + StructDefinition sd => sd.Decorators, + MessageDefinition md => md.Decorators, + EnumDefinition ed => ed.Decorators, + _ => null, + }); + switch (def) + { + case StructDefinition sd: + WriteStruct(sd, schema, writer); + break; + case MessageDefinition md: + WriteMessage(md, schema, writer); + break; + case EnumDefinition ed: + WriteEnum(ed, schema, writer); + break; + } + if (def.Parent is not null) + { + writer.WriteString("parent", def.Parent.Name); + } + } + + private static void WriteEnum(EnumDefinition ed, BebopSchema schema, Utf8JsonWriter writer) + { + writer.WriteBoolean("isBitFlags", ed.IsBitFlags); + writer.WriteNumber("minimalEncodedSize", ed.ScalarType.MinimalEncodedSize(schema)); + writer.WriteString("baseType", ed.ScalarType.ToTokenString()); + var memberCount = ed.Members.Count; + if (memberCount > byte.MaxValue) + { + throw new CompilerException($"{ed.Name} exceeds maximum members: has {memberCount} members"); + } + writer.WriteStartObject("members"); + foreach (var member in ed.Members) + { + writer.WriteStartObject(member.Name); + if (!string.IsNullOrWhiteSpace(member.Documentation)) + { + writer.WriteString("documentation", member.Documentation); + } + WriteDecorators(writer, member.Decorators); + WriteConstant(ed.ScalarType.BaseType, member.ConstantValue, writer); + writer.WriteEndObject(); + } + + writer.WriteEndObject(); + } + + private static void WriteMessage(MessageDefinition md, BebopSchema schema, Utf8JsonWriter writer) + { + writer.WriteNumber("minimalEncodedSize", md.MinimalEncodedSize(schema)); + if (md.DiscriminatorInParent is not null) + { + writer.WriteNumber("discriminatorInParent", md.DiscriminatorInParent.Value); + } + var fieldCount = md.Fields.Count; + if (fieldCount > byte.MaxValue) + { + throw new CompilerException($"{md.Name} exceeds maximum fields: has {fieldCount} fields"); + } + WriteFields(md, md.Fields, writer); + } + + private static void WriteStruct(StructDefinition sd, BebopSchema schema, Utf8JsonWriter writer) + { + writer.WriteNumber("minimalEncodedSize", sd.MinimalEncodedSize(schema)); + if (sd.DiscriminatorInParent is not null) + { + writer.WriteNumber("discriminatorInParent", sd.DiscriminatorInParent.Value); + } + writer.WriteBoolean("mutable", sd.IsMutable); + writer.WriteBoolean("isFixedSize", sd.IsFixedSize(schema)); + var fieldCount = sd.Fields.Count; + if (fieldCount < 0 || fieldCount > byte.MaxValue) + { + throw new CompilerException($"{sd.Name} exceeds maximum fields: has {fieldCount} fields"); + } + WriteFields(sd, sd.Fields, writer); + } + + private static void WriteFields(Definition parent, IEnumerable fields, Utf8JsonWriter writer) + { + writer.WriteStartObject("fields"); + foreach (var field in fields) + { + WriteField(parent, field, writer); + } + writer.WriteEndObject(); + } + + private static void WriteField(Definition parent, Field field, Utf8JsonWriter writer) + { + writer.WriteStartObject(field.Name); + if (!string.IsNullOrWhiteSpace(field.Documentation)) + { + writer.WriteString("documentation", field.Documentation); + } + WriteDecorators(writer, field.Decorators); + writer.WriteString("type", field.Type.ToTokenString()); + if (field.Type is ArrayType at) + { + WriteArray(at, writer); + } + else if (field.Type is MapType mt) + { + WriteMap(mt, writer); + } + if (parent is MessageDefinition) + { + writer.WriteNumber("index", (byte)field.ConstantValue); + } + writer.WriteEndObject(); + } + + private static void WriteArray(ArrayType arrayType, Utf8JsonWriter writer) + { + byte depth = 0; + var memberType = arrayType.MemberType; + while (memberType is ArrayType at) + { + depth++; + if (depth > byte.MaxValue) + { + throw new CompilerException("Array depth is too large"); + } + memberType = at.MemberType; + } + writer.WriteStartObject("array"); + writer.WriteNumber("depth", depth); + writer.WriteString("memberType", memberType.ToTokenString()); + if (memberType is MapType mt) + { + WriteMap(mt, writer); + } + writer.WriteEndObject(); + } + + private static void WriteMap(MapType mapType, Utf8JsonWriter writer) + { + writer.WriteStartObject("map"); + writer.WriteString("keyType", mapType.KeyType.ToTokenString()); + writer.WriteString("valueType", mapType.ValueType.ToTokenString()); + if (mapType.ValueType is ArrayType at) + { + WriteArray(at, writer); + } + else if (mapType.ValueType is MapType mt) + { + WriteMap(mt, writer); + } + writer.WriteEndObject(); + } + + private static void WriteDecorators(Utf8JsonWriter writer, List? decorators) + { + if (decorators is null || decorators.Count == 0) + { + return; + } + writer.WriteStartObject("decorators"); + foreach (var decorator in decorators) + { + writer.WriteStartObject(decorator.Identifier); + if (decorator.Arguments.Count > 0) + { + writer.WriteStartObject("arguments"); + foreach (var (key, value) in decorator.Arguments) + { + writer.WriteStartObject(key); + var parameter = decorator.Definition.Parameters!.Single(x => x.Identifier == key); + writer.WriteString("type", parameter.Type.ToTokenString()); + WriteConstant(parameter.Type, value, writer); + writer.WriteEndObject(); + } + writer.WriteEndObject(); + } + writer.WriteEndObject(); + } + writer.WriteEndObject(); + } + + private static string GetDefinitionKind(Definition def) + { + return def switch + { + EnumDefinition => "enum", + StructDefinition => "struct", + MessageDefinition => "message", + ServiceDefinition => "service", + UnionDefinition => "union", + MethodDefinition => "method", + ConstDefinition => "const", + _ => throw new Exception("Unknown definition type") + }; + } + + + private static void WriteConstant(BaseType baseType, object value, Utf8JsonWriter writer) + { + if (value is BigInteger bigInt) + { + switch (baseType) + { + case BaseType.Byte: + writer.WriteString("value", ((byte)bigInt).ToString()); + return; + case BaseType.UInt16: + writer.WriteString("value", ((ushort)bigInt).ToString()); + return; + case BaseType.Int16: + writer.WriteString("value", ((short)bigInt).ToString()); + return; + case BaseType.UInt32: + writer.WriteString("value", ((uint)bigInt).ToString()); + return; + case BaseType.Int32: + writer.WriteString("value", ((int)bigInt).ToString()); + return; + case BaseType.UInt64: + writer.WriteString("value", ((ulong)bigInt).ToString()); + return; + } + } + else if (value is string v && baseType.IsNumber()) + { + switch (baseType) + { + case BaseType.Byte: + writer.WriteString("value", byte.Parse(v).ToString()); + return; + case BaseType.UInt16: + writer.WriteString("value", ushort.Parse(v).ToString()); + return; + case BaseType.Int16: + writer.WriteString("value", short.Parse(v).ToString()); + return; + case BaseType.UInt32: + writer.WriteString("value", uint.Parse(v).ToString()); + return; + case BaseType.Int32: + writer.WriteString("value", int.Parse(v).ToString()); + return; + case BaseType.UInt64: + writer.WriteString("value", ulong.Parse(v).ToString()); + return; + case BaseType.Int64: + writer.WriteString("value", long.Parse(v).ToString()); + return; + case BaseType.Float32: + writer.WriteString("value", float.Parse(v).ToString()); + return; + case BaseType.Float64: + writer.WriteString("value", double.Parse(v).ToString()); + return; + } + } + else if (value is string s && baseType == BaseType.Guid) + { + writer.WriteString("value", Guid.Parse(s).ToString("N")); + return; + } + else if (value is string b && baseType == BaseType.Bool) + { + writer.WriteString("value", bool.Parse(b).ToString().ToLowerInvariant()); + return; + } + else if (value is string ss && baseType == BaseType.String) + { + writer.WriteString("value", ss); + return; + } + throw new CompilerException($"Unknown base type {baseType}"); + } +} \ No newline at end of file diff --git a/Core/Generators/GeneratorUtils.cs b/Core/Generators/GeneratorUtils.cs index c7a74e3a..e4e6878e 100644 --- a/Core/Generators/GeneratorUtils.cs +++ b/Core/Generators/GeneratorUtils.cs @@ -1,13 +1,5 @@ -using System.Runtime.Serialization; -using System; -using System.Collections.Generic; -using System.Text; -using Core.Generators.CPlusPlus; +using System.Text; using Core.Generators.CSharp; -using Core.Generators.Dart; -using Core.Generators.Rust; -using Core.Generators.TypeScript; -using Core.Generators.Python; using Core.Meta; using Core.Meta.Extensions; @@ -20,7 +12,7 @@ public static string GetXmlAutoGeneratedNotice() var builder = new IndentedStringBuilder(); builder.AppendLine("//------------------------------------------------------------------------------"); builder.AppendLine("// "); - foreach(var line in GetAutoGeneratedNotice().GetLines()) + foreach (var line in GetAutoGeneratedNotice().GetLines()) { builder.Append("//").Indent(5).Append(line).Dedent(5).AppendLine(); if (line.EndsWith("regenerated.")) @@ -35,7 +27,7 @@ public static string GetMarkdownAutoGeneratedNotice() { var builder = new StringBuilder(); builder.AppendLine("// "); - foreach(var line in GetAutoGeneratedNotice().GetLines()) + foreach (var line in GetAutoGeneratedNotice().GetLines()) { builder.Append("// ").AppendLine(line); } @@ -43,7 +35,7 @@ public static string GetMarkdownAutoGeneratedNotice() } private static string GetAutoGeneratedNotice() { - const string repo = "https://github.com/RainwayApp/bebop"; + const string repo = "https://github.com/betwixt-labs/bebop"; var builder = new IndentedStringBuilder(); builder.AppendLine("This code was generated by a tool.").Indent(2); builder.AppendLine().AppendLine(); @@ -80,7 +72,8 @@ private static string GetAutoGeneratedNotice() /// public static string ClassName(this Definition definition) { - if (definition is ServiceDefinition) { + if (definition is ServiceDefinition) + { return $"{definition.Name.ToPascalCase()}Service"; } return definition.Name.ToPascalCase(); @@ -95,7 +88,8 @@ public static string ClassName(this Definition definition) /// public static string BaseClassName(this Definition definition) { - if (definition is ServiceDefinition) { + if (definition is ServiceDefinition) + { return $"Base{definition.Name.ToPascalCase()}Service"; } return $"Base{definition.Name.ToPascalCase()}"; @@ -110,32 +104,6 @@ public static string BaseClassName(this Definition definition) /// public static int GenericIndex(this UnionBranch branch) => branch.Discriminator - 1; - /// - /// A dictionary that contains generators. - /// - /// - /// Generators are keyed via their commandline alias. - /// - public static Dictionary> ImplementedGenerators = new() - { - { "cpp", s => new CPlusPlusGenerator(s) }, - { "cs", s => new CSharpGenerator(s) }, - { "dart", s => new DartGenerator(s) }, - { "rust", s => new RustGenerator(s) }, - { "ts", s => new TypeScriptGenerator(s) }, - { "py", s => new PythonGenerator(s) } - }; - - public static Dictionary ImplementedGeneratorNames = new() - { - { "cpp", "C++" }, - { "cs", "C#" }, - { "dart", "Dart" }, - { "rust", "Rust" }, - { "ts", "TypeScript" }, - { "py", "Python" } - }; - /// /// Returns a loop variable name based on the provided loop /// diff --git a/Core/Generators/Python/PythonGenerator.cs b/Core/Generators/Python/PythonGenerator.cs index 7d476ffa..2038c2b4 100644 --- a/Core/Generators/Python/PythonGenerator.cs +++ b/Core/Generators/Python/PythonGenerator.cs @@ -6,7 +6,9 @@ using System.Text.Json; using Core.Meta; using Core.Meta.Extensions; -using Core.Meta.Attributes; +using Core.Meta.Decorators; +using System.Threading.Tasks; +using System.Threading; namespace Core.Generators.Python { @@ -14,17 +16,19 @@ public class PythonGenerator : BaseGenerator { const int indentStep = 4; - public PythonGenerator(BebopSchema schema) : base(schema) { } + public PythonGenerator() : base() { } - private string FormatDocumentation(string documentation, BaseAttribute? deprecated){ + private string FormatDocumentation(string documentation, string? deprecated) + { var builder = new StringBuilder(); builder.AppendLine("\"\"\""); foreach (var line in documentation.GetLines()) { builder.AppendLine(line); } - if (deprecated != null) { - builder.AppendLine($"@deprecated {deprecated.Value}"); + if (deprecated is not null) + { + builder.AppendLine($"@deprecated {deprecated}"); } builder.AppendLine("\"\"\""); return builder.ToString(); @@ -53,7 +57,7 @@ private string CompileEncodeMessage(MessageDefinition definition) builder.AppendLine($"start = writer.length"); foreach (var field in definition.Fields) { - if (field.DeprecatedAttribute != null) + if (field.DeprecatedDecorator != null) { continue; } @@ -75,13 +79,15 @@ private string CompileEncodeStruct(StructDefinition definition) builder.AppendLine(CompileEncodeField(field.Type, $"message.{field.Name}")); builder.AppendLine(""); } - if (definition.Fields.Count == 0) { + if (definition.Fields.Count == 0) + { builder.AppendLine("pass"); } return builder.ToString(); } - private string CompileEncodeUnion(UnionDefinition definition) { + private string CompileEncodeUnion(UnionDefinition definition) + { var builder = new IndentedStringBuilder(4); builder.AppendLine($"pos = writer.reserve_message_length()"); builder.AppendLine($"start = writer.length + 1"); @@ -153,7 +159,7 @@ public string CompileDecode(RecordDefinition definition) { MessageDefinition d => CompileDecodeMessage(d), StructDefinition d => CompileDecodeStruct(d), - UnionDefinition d => CompileDecodeUnion(d), + UnionDefinition d => CompileDecodeUnion(d), _ => throw new InvalidOperationException($"invalid CompileDecode value: {definition}"), }; } @@ -201,7 +207,8 @@ private string CompileDecodeStruct(StructDefinition definition) return builder.ToString(); } - private string CompileDecodeUnion(UnionDefinition definition) { + private string CompileDecodeUnion(UnionDefinition definition) + { var builder = new IndentedStringBuilder(4); builder.AppendLine("length = reader.read_message_length()"); builder.AppendLine("end = reader.index + 1 + length"); @@ -304,12 +311,11 @@ private string TypeName(in TypeBase type) private static string EscapeStringLiteral(string value) { - // Dart accepts \u0000 style escape sequences, so we can escape the string JSON-style. - var options = new JsonSerializerOptions { Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping }; - return JsonSerializer.Serialize(value, options); + return $@"""{value.EscapeString()}"""; } - private string EmitLiteral(Literal literal) { + private string EmitLiteral(Literal literal) + { return literal switch { BoolLiteral bl => bl.Value ? "True" : "False", @@ -328,8 +334,10 @@ private string EmitLiteral(Literal literal) { /// Generate code for a Bebop schema. /// /// The generated code. - public override string Compile(Version? languageVersion, TempoServices services = TempoServices.Both, bool writeGeneratedNotice = true, bool emitBinarySchema = false) + public override ValueTask Compile(BebopSchema schema, GeneratorConfig config, CancellationToken cancellationToken = default) { + Schema = schema; + Config = config; var builder = new IndentedStringBuilder(); builder.AppendLine("from enum import Enum"); builder.AppendLine("from python_bebop import BebopWriter, BebopReader, UnionType, UnionDefinition"); @@ -357,7 +365,8 @@ public override string Compile(Version? languageVersion, TempoServices services builder.AppendLine($"{field.Name.ToUpper()} = {field.ConstantValue}"); if (!string.IsNullOrWhiteSpace(field.Documentation)) { - builder.Append(FormatDocumentation(field.Documentation, field.DeprecatedAttribute)); + var deprecatedReason = field.DeprecatedDecorator?.TryGetValue("reason", out var reason) ?? false ? reason : null; + builder.Append(FormatDocumentation(field.Documentation, deprecatedReason)); builder.AppendLine(""); } } @@ -365,7 +374,8 @@ public override string Compile(Version? languageVersion, TempoServices services builder.Dedent(indentStep); break; case RecordDefinition rd: - if (rd is FieldsDefinition fd) { + if (rd is FieldsDefinition fd) + { builder.AppendLine($"class {fd.Name}:"); builder.Indent(indentStep); if (!string.IsNullOrWhiteSpace(definition.Documentation)) @@ -373,101 +383,118 @@ public override string Compile(Version? languageVersion, TempoServices services builder.Append(FormatDocumentation(definition.Documentation, null)); builder.AppendLine(); } - var isReadonlyStruct = rd is StructDefinition sd ? sd.IsReadOnly : false; - var fieldPrepend = isReadonlyStruct ? "_" : ""; - for (var i = 0; i < fd.Fields.Count; i++) { + var isImmutableStruct = rd is StructDefinition sd && !sd.IsMutable; + var fieldPrepend = isImmutableStruct ? "_" : ""; + for (var i = 0; i < fd.Fields.Count; i++) + { var field = fd.Fields.ElementAt(i); var type = TypeName(field.Type); builder.AppendLine($"{fieldPrepend}{field.Name}: {type}"); if (!string.IsNullOrWhiteSpace(field.Documentation)) { - builder.Append(FormatDocumentation(field.Documentation, field.DeprecatedAttribute)); + builder.Append(FormatDocumentation(field.Documentation, field.DeprecatedDecorator?.Arguments?["reason"])); } builder.AppendLine(); } - if (rd.OpcodeAttribute != null) { - builder.AppendLine($"opcode = {rd.OpcodeAttribute.Value}"); + if (rd.OpcodeDecorator != null) + { + builder.AppendLine($"opcode = {rd.OpcodeDecorator.Arguments["fourcc"]}"); builder.AppendLine(""); } builder.AppendLine(""); - if (!(fd is MessageDefinition)) { + if (!(fd is MessageDefinition)) + { List fields = new List(); foreach (var field in fd.Fields) { fields.Add($" {field.Name}: {TypeName(field.Type)}"); } - if (fields.Count != 0) { + if (fields.Count != 0) + { builder.Append("def __init__(self, "); builder.Append(string.Join(",", fields)); builder.AppendLine("):"); builder.Indent(indentStep); builder.AppendLine("self.encode = self._encode"); - foreach (var field in fd.Fields) { + foreach (var field in fd.Fields) + { builder.AppendLine($"self.{fieldPrepend}{field.Name} = {field.Name}"); } builder.Dedent(indentStep); - } else { + } + else + { builder.AppendLine("def __init__(self):"); builder.AppendLine(" self.encode = self._encode"); } builder.AppendLine(); - } else { - builder.CodeBlock("def __init__(self):", indentStep, () => { + } + else + { + builder.CodeBlock("def __init__(self):", indentStep, () => + { builder.AppendLine("self.encode = self._encode"); }, open: string.Empty, close: string.Empty); } - if (isReadonlyStruct) { - for (var i = 0; i < fd.Fields.Count; i++) { + if (isImmutableStruct) + { + for (var i = 0; i < fd.Fields.Count; i++) + { var field = fd.Fields.ElementAt(i); builder.AppendLine("@property"); - builder.CodeBlock($"def {field.Name}(self):", indentStep, () => { + builder.CodeBlock($"def {field.Name}(self):", indentStep, () => + { builder.AppendLine($"return self._{field.Name}"); }, close: string.Empty, open: string.Empty); } } - } else if (rd is UnionDefinition ud) { + } + else if (rd is UnionDefinition ud) + { builder.CodeBlock($"class {ud.ClassName()}:", indentStep, () => - { - builder.AppendLine(); - if (!string.IsNullOrWhiteSpace(definition.Documentation)) { - builder.Append(FormatDocumentation(definition.Documentation, null)); - } - if (rd.OpcodeAttribute != null) { - builder.AppendLine($"opcode = {rd.OpcodeAttribute.Value}"); - builder.AppendLine(""); - } - builder.AppendLine($"data: UnionType"); - builder.AppendLine(); - builder.CodeBlock($"def __init__(self, data: UnionType):", indentStep, () => - { - builder.AppendLine("self.encode = self._encode"); - builder.AppendLine($"self.data = data"); - }, open: string.Empty, close: string.Empty); - builder.AppendLine("@property"); - builder.CodeBlock($"def discriminator(self):", indentStep, () => - { - builder.AppendLine($"return self.data.discriminator"); - }, open: string.Empty, close: string.Empty); - builder.AppendLine("@property"); - builder.CodeBlock($"def value(self):", indentStep, () => - { - builder.AppendLine($"return self.data.value"); - }, open: string.Empty, close: string.Empty); - foreach (var b in ud.Branches) - { - builder.AppendLine("@staticmethod"); - builder.CodeBlock($"def from{b.ClassName()}(value: {b.ClassName()}):", indentStep, () => + builder.AppendLine(); + if (!string.IsNullOrWhiteSpace(definition.Documentation)) { - builder.AppendLine($"return {definition.ClassName()}(UnionDefinition({b.Discriminator}, value))" ); - }, open: string.Empty, close: string.Empty); - builder.CodeBlock($"def is{b.ClassName()}(self):", indentStep, () => + builder.Append(FormatDocumentation(definition.Documentation, null)); + } + if (rd.OpcodeDecorator is not null && rd.OpcodeDecorator.TryGetValue("fourcc", out var fourcc)) { - builder.AppendLine($"return isinstance(self.value, {b.ClassName()})"); - }, open: string.Empty, close: string.Empty); - } - }, close: string.Empty, open: string.Empty); + builder.AppendLine($"opcode = {fourcc}"); + builder.AppendLine(""); + } + + builder.AppendLine($"data: UnionType"); + builder.AppendLine(); + builder.CodeBlock($"def __init__(self, data: UnionType):", indentStep, () => + { + builder.AppendLine("self.encode = self._encode"); + builder.AppendLine($"self.data = data"); + }, open: string.Empty, close: string.Empty); + builder.AppendLine("@property"); + builder.CodeBlock($"def discriminator(self):", indentStep, () => + { + builder.AppendLine($"return self.data.discriminator"); + }, open: string.Empty, close: string.Empty); + builder.AppendLine("@property"); + builder.CodeBlock($"def value(self):", indentStep, () => + { + builder.AppendLine($"return self.data.value"); + }, open: string.Empty, close: string.Empty); + foreach (var b in ud.Branches) + { + builder.AppendLine("@staticmethod"); + builder.CodeBlock($"def from{b.ClassName()}(value: {b.ClassName()}):", indentStep, () => + { + builder.AppendLine($"return {definition.ClassName()}(UnionDefinition({b.Discriminator}, value))"); + }, open: string.Empty, close: string.Empty); + builder.CodeBlock($"def is{b.ClassName()}(self):", indentStep, () => + { + builder.AppendLine($"return isinstance(self.value, {b.ClassName()})"); + }, open: string.Empty, close: string.Empty); + } + }, close: string.Empty, open: string.Empty); builder.Indent(indentStep); } builder.AppendLine($"def _encode(self):"); @@ -529,12 +556,17 @@ public override string Compile(Version? languageVersion, TempoServices services } } - return builder.ToString(); + return ValueTask.FromResult(builder.ToString()); } - public override void WriteAuxiliaryFiles(string outputPath) + public override void WriteAuxiliaryFile(string outputPath) { // There is nothing to do here. } + + public override AuxiliaryFile? GetAuxiliaryFile() => null; + + public override string Alias { get => "py"; set => throw new NotImplementedException(); } + public override string Name { get => "Python"; set => throw new NotImplementedException(); } } } diff --git a/Core/Generators/Rust/RustGenerator.cs b/Core/Generators/Rust/RustGenerator.cs index b72d25d0..a10f9f11 100644 --- a/Core/Generators/Rust/RustGenerator.cs +++ b/Core/Generators/Rust/RustGenerator.cs @@ -4,8 +4,10 @@ using System.Collections.Generic; using System.Linq; using System.Text; +using System.Threading; +using System.Threading.Tasks; using Core.Meta; -using Core.Meta.Attributes; +using Core.Meta.Decorators; using Core.Meta.Extensions; namespace Core.Generators.Rust @@ -52,10 +54,12 @@ public class RustGenerator : BaseGenerator #region entrypoints - public RustGenerator(BebopSchema schema) : base(schema) { } + public RustGenerator() : base() { } - public override string Compile(Version? languageVersion, TempoServices services = TempoServices.Both, bool writeGeneratedNotice = true, bool emitBinarySchema = false) + public override ValueTask Compile(BebopSchema schema, GeneratorConfig config, CancellationToken cancellationToken = default) { + Schema = schema; + Config = config; // the main scope which is where we write the const definitions and the borrowed types (as these are the // primary way to use bebop in Rust) var mainBuilder = new IndentedStringBuilder(); @@ -63,16 +67,16 @@ public override string Compile(Version? languageVersion, TempoServices services // access the main scope. var ownedBuilder = new IndentedStringBuilder(); - if (writeGeneratedNotice) + if (Config.EmitNotice) { mainBuilder .AppendLine(GeneratorUtils.GetMarkdownAutoGeneratedNotice()) .AppendLine(); } - if (!string.IsNullOrWhiteSpace(Schema.Namespace)) + if (!string.IsNullOrWhiteSpace(Config.Namespace)) { - mainBuilder.AppendLine($"#![cfg(feature = \"{Schema.Namespace.ToKebabCase()}\")]"); + mainBuilder.AppendLine($"#![cfg(feature = \"{Config.Namespace.ToKebabCase()}\")]"); } WriteStandardImportsForModule(mainBuilder); @@ -118,22 +122,27 @@ public override string Compile(Version? languageVersion, TempoServices services } mainBuilder - .AppendLine(string.IsNullOrWhiteSpace(Schema.Namespace) + .AppendLine(string.IsNullOrWhiteSpace(Config.Namespace) ? "#[cfg(feature = \"bebop-owned-all\")]" - : $"#[cfg(any(feature = \"bebop-owned-all\", feature = \"{Schema.Namespace.ToKebabCase()}-owned\"))]") + : $"#[cfg(any(feature = \"bebop-owned-all\", feature = \"{Config.Namespace.ToKebabCase()}-owned\"))]") .CodeBlock("pub mod owned", _tab, () => { mainBuilder.Append(ownedBuilder.ToString()); }); - return mainBuilder.ToString(); + return ValueTask.FromResult(mainBuilder.ToString()); } - public override void WriteAuxiliaryFiles(string outputPath) + public override AuxiliaryFile? GetAuxiliaryFile() => null; + + public override void WriteAuxiliaryFile(string outputPath) { // Nothing to do because the runtime is a cargo package. } + public override string Alias { get => "rust"; set => throw new NotImplementedException(); } + public override string Name { get => "Rust"; set => throw new NotImplementedException(); } + #endregion #region definition_writers @@ -188,7 +197,7 @@ private void WriteEnumDefinition(IndentedStringBuilder builder, EnumDefinition d foreach (var m in d.Members) { WriteDocumentation(builder, m.Documentation); - WriteDeprecation(builder, m.DeprecatedAttribute); + WriteDeprecation(builder, m.DeprecatedDecorator); builder.AppendLine($"const {MakeConstIdent(m.Name)} = {m.ConstantValue};"); } }); @@ -204,7 +213,7 @@ private void WriteEnumDefinition(IndentedStringBuilder builder, EnumDefinition d foreach (var m in d.Members) { WriteDocumentation(builder, m.Documentation); - WriteDeprecation(builder, m.DeprecatedAttribute); + WriteDeprecation(builder, m.DeprecatedDecorator); builder.AppendLine($"{MakeEnumVariantIdent(m.Name)} = {m.ConstantValue},"); } }).AppendLine(); @@ -408,7 +417,7 @@ private void WriteStructDefinition(IndentedStringBuilder builder, StructDefiniti } /// - /// Write the part within the `pub struct` definition. This will just write the attributes. + /// Write the part within the `pub struct` definition. This will just write the decorator. /// private void WriteStructDefinitionAttrs(IndentedStringBuilder builder, StructDefinition d, OwnershipType ot, bool makePub = true) @@ -416,7 +425,7 @@ private void WriteStructDefinitionAttrs(IndentedStringBuilder builder, StructDef foreach (var f in d.Fields) { WriteDocumentation(builder, f.Documentation); - WriteDeprecation(builder, f.DeprecatedAttribute); + WriteDeprecation(builder, f.DeprecatedDecorator); var pub = makePub ? "pub " : ""; builder.AppendLine($"{pub}{MakeAttrIdent(f.Name)}: {TypeName(f.Type, ot)},"); } @@ -530,7 +539,7 @@ private void WriteMessageDefinition(IndentedStringBuilder builder, MessageDefini } /// - /// Write the part within the `pub struct` definition. This will just write the attributes. + /// Write the part within the `pub struct` definition. This will just write the decorator. /// private void WriteMessageDefinitionAttrs(IndentedStringBuilder builder, MessageDefinition d, OwnershipType ot, bool makePub = true) @@ -539,7 +548,7 @@ private void WriteMessageDefinitionAttrs(IndentedStringBuilder builder, MessageD { WriteDocumentation(builder, f.Documentation); WriteDocumentation(builder, $"Field {f.ConstantValue}"); - WriteDeprecation(builder, f.DeprecatedAttribute); + WriteDeprecation(builder, f.DeprecatedDecorator); var pub = makePub ? "pub " : ""; builder.AppendLine($"{pub}{MakeAttrIdent(f.Name)}: ::core::option::Option<{TypeName(f.Type, ot)}>,"); } @@ -977,14 +986,14 @@ private static void WriteDocumentation(IndentedStringBuilder builder, string doc } } - private static void WriteDeprecation(IndentedStringBuilder builder, BaseAttribute? attr) + private static void WriteDeprecation(IndentedStringBuilder builder, SchemaDecorator? attr) { if (attr is null) { return; } builder.Append("#[deprecated"); - if (!string.IsNullOrEmpty(attr.Value)) + if (attr is not null && attr.TryGetValue("reason", out var value) && !string.IsNullOrEmpty(value)) { - builder.AppendMid($"(note = \"{attr.Value}\")"); + builder.Append($"(note = \"{value}\")"); } builder.AppendEnd("]"); @@ -992,12 +1001,14 @@ private static void WriteDeprecation(IndentedStringBuilder builder, BaseAttribut private static void WriteRecordImpl(IndentedStringBuilder builder, string name, RecordDefinition d) { - if (d.OpcodeAttribute is { Value: not (null or "") }) + + if (d.OpcodeDecorator is not null && d.OpcodeDecorator.TryGetValue("fourcc", out var fourcc) && + !string.IsNullOrEmpty(fourcc)) { builder.CodeBlock($"impl<'raw> ::bebop::Record<'raw> for {name}", _tab, () => { builder.AppendLine( - $"const OPCODE: ::core::option::Option = Some({d.OpcodeAttribute.Value});"); + $"const OPCODE: ::core::option::Option = Some({fourcc});"); }); } else diff --git a/Core/Generators/ServiceGeneratorFlags.cs b/Core/Generators/ServiceGeneratorFlags.cs index b2a4a1c3..de6aa082 100644 --- a/Core/Generators/ServiceGeneratorFlags.cs +++ b/Core/Generators/ServiceGeneratorFlags.cs @@ -1,6 +1,8 @@ using System; +using System.Text.Json.Serialization; namespace Core.Generators { + [JsonConverter(typeof(JsonStringEnumConverter))] /// /// An enum that defines which parts of a service are generated /// diff --git a/Core/Generators/TypeScript/TypeScriptGenerator.cs b/Core/Generators/TypeScript/TypeScriptGenerator.cs index 5c8ba3a7..eff38f49 100644 --- a/Core/Generators/TypeScript/TypeScriptGenerator.cs +++ b/Core/Generators/TypeScript/TypeScriptGenerator.cs @@ -7,7 +7,10 @@ using System.Text; using System.Text.Encodings.Web; using System.Text.Json; +using System.Threading; +using System.Threading.Tasks; using Core.Meta; +using Core.Meta.Decorators; using Core.Meta.Extensions; using Core.Parser; @@ -17,19 +20,26 @@ public class TypeScriptGenerator : BaseGenerator { const int indentStep = 2; - public TypeScriptGenerator(BebopSchema schema) : base(schema) { } + public TypeScriptGenerator() : base() { } - private static string FormatDocumentation(string documentation, string deprecationReason, int spaces) + private static string FormatDocumentation(string documentation, SchemaDecorator? deprecatedDecorator, int spaces) { + if (string.IsNullOrWhiteSpace(documentation) && deprecatedDecorator is null) + { + return string.Empty; + } var builder = new IndentedStringBuilder(); builder.Indent(spaces); builder.AppendLine("/**"); builder.Indent(1); - foreach (var line in documentation.GetLines()) + if (!string.IsNullOrWhiteSpace(documentation)) { - builder.AppendLine($"* {line}"); + foreach (var line in documentation.GetLines()) + { + builder.AppendLine($"* {line}"); + } } - if (!string.IsNullOrWhiteSpace(deprecationReason)) + if (deprecatedDecorator?.TryGetValue("reason", out var deprecationReason) == true) { builder.AppendLine($"* @deprecated {deprecationReason}"); } @@ -71,7 +81,7 @@ private string CompileEncodeMessage(MessageDefinition definition) builder.AppendLine($"const start = view.length;"); foreach (var field in definition.Fields) { - if (field.DeprecatedAttribute != null) + if (field.DeprecatedDecorator != null) { continue; } @@ -314,14 +324,14 @@ private string CompileDecodeField(TypeBase type, string target, int depth = 0) public string CompileJsonMethods(Definition definition) { var builder = new IndentedStringBuilder(0); - builder.AppendLine(FormatDocumentation("Serializes the current instance into a JSON-Over-Bebop string", string.Empty, 0)); - builder.CodeBlock($"public toJSON(): string", indentStep, () => + builder.AppendLine(FormatDocumentation("Serializes the current instance into a JSON-Over-Bebop string", null, 0)); + builder.CodeBlock($"public stringify(): string", indentStep, () => { builder.AppendLine($"return {definition.ClassName()}.encodeToJSON(this);"); }); builder.AppendLine(); - builder.AppendLine(FormatDocumentation("Serializes the specified object into a JSON-Over-Bebop string", string.Empty, 0)); + builder.AppendLine(FormatDocumentation("Serializes the specified object into a JSON-Over-Bebop string", null, 0)); builder.CodeBlock($"public static encodeToJSON(record: I{definition.ClassName()}): string", indentStep, () => { if (definition is UnionDefinition) @@ -333,13 +343,13 @@ public string CompileJsonMethods(Definition definition) }); builder.AppendLine(); - builder.AppendLine(FormatDocumentation("Validates that the runtime types of members in the current instance are correct.", string.Empty, 0)); + builder.AppendLine(FormatDocumentation("Validates that the runtime types of members in the current instance are correct.", null, 0)); builder.CodeBlock($"public validateTypes(): void", indentStep, () => { builder.AppendLine($"{definition.ClassName()}.validateCompatibility(this);"); }); builder.AppendLine(); - builder.AppendLine(FormatDocumentation($"Validates that the specified dynamic object can become an instance of {{@link {definition.ClassName()}}}.", string.Empty, 0)); + builder.AppendLine(FormatDocumentation($"Validates that the specified dynamic object can become an instance of {{@link {definition.ClassName()}}}.", null, 0)); builder.CodeBlock($"public static validateCompatibility(record: I{definition.ClassName()}): void", indentStep, () => { builder.AppendLine(definition switch @@ -352,7 +362,7 @@ public string CompileJsonMethods(Definition definition) }); builder.AppendLine(); var returnType = definition is UnionDefinition ? definition.ClassName() : $"I{definition.ClassName()}"; - builder.AppendLine(FormatDocumentation($"Unsafely creates an instance of {{@link {definition.ClassName()}}} from the specified dynamic object. No type checking is performed.", string.Empty, 0)); + builder.AppendLine(FormatDocumentation($"Unsafely creates an instance of {{@link {definition.ClassName()}}} from the specified dynamic object. No type checking is performed.", null, 0)); builder.CodeBlock($"public static unsafeCast(record: any): {returnType}", indentStep, () => { builder.AppendLine(definition switch @@ -364,7 +374,7 @@ public string CompileJsonMethods(Definition definition) }); }); builder.AppendLine(); - builder.AppendLine(FormatDocumentation($"Creates a new {{@link {definition.ClassName()}}} instance from a JSON-Over-Bebop string. Type checking is performed.", string.Empty, 0)); + builder.AppendLine(FormatDocumentation($"Creates a new {{@link {definition.ClassName()}}} instance from a JSON-Over-Bebop string. Type checking is performed.", null, 0)); builder.CodeBlock($"public static fromJSON(json: string): {returnType}", indentStep, () => { builder.CodeBlock("if (typeof json !== 'string' || json.trim().length === 0)", indentStep, () => @@ -612,9 +622,7 @@ BaseType.Byte or BaseType.UInt16 or BaseType.Int16 or BaseType.UInt32 or BaseTyp private static string EscapeStringLiteral(string value) { - // TypeScript accepts \u0000 style escape sequences, so we can escape the string JSON-style. - var options = new JsonSerializerOptions { Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping }; - return JsonSerializer.Serialize(value, options); + return $@"""{value.EscapeString()}"""; } private string EmitLiteral(Literal literal) @@ -638,10 +646,12 @@ private string EmitLiteral(Literal literal) /// Generate code for a Bebop schema. /// /// The generated code. - public override string Compile(Version? languageVersion, TempoServices services = TempoServices.Both, bool writeGeneratedNotice = true, bool emitBinarySchema = false) + public override ValueTask Compile(BebopSchema schema, GeneratorConfig config, CancellationToken cancellationToken = default) { + Schema = schema; + Config = config; var builder = new IndentedStringBuilder(); - if (writeGeneratedNotice) + if (Config.EmitNotice) { builder.AppendLine(GeneratorUtils.GetXmlAutoGeneratedNotice()); } @@ -649,24 +659,24 @@ public override string Compile(Version? languageVersion, TempoServices services if (Schema.Definitions.Values.OfType().Any()) { builder.AppendLine("import { Metadata, MethodType } from \"@tempojs/common\";"); - if (services is TempoServices.Client or TempoServices.Both) + if (Config.Services is TempoServices.Client or TempoServices.Both) { builder.AppendLine("import { BaseClient, MethodInfo, CallOptions } from \"@tempojs/client\";"); } - if (services is TempoServices.Server or TempoServices.Both) + if (Config.Services is TempoServices.Server or TempoServices.Both) { builder.AppendLine("import { ServiceRegistry, BaseService, ServerContext, BebopMethodAny, BebopMethod } from \"@tempojs/server\";"); } } builder.AppendLine(""); - if (!string.IsNullOrWhiteSpace(Schema.Namespace)) + if (!string.IsNullOrWhiteSpace(Config.Namespace)) { - builder.AppendLine($"export namespace {Schema.Namespace} {{"); + builder.AppendLine($"export namespace {Config.Namespace} {{"); builder.Indent(2); } - if (emitBinarySchema) + if (Config.EmitBinarySchema) { builder.AppendLine($"export {Schema.ToBinary().ConvertToTypeScriptUInt8ArrayInitializer("BEBOP_SCHEMA")}"); @@ -675,16 +685,12 @@ public override string Compile(Version? languageVersion, TempoServices services foreach (var definition in Schema.Definitions.Values) { - if (!string.IsNullOrWhiteSpace(definition.Documentation) || (definition is EnumDefinition { DeprecatedAttribute: not null }) || (definition is RecordDefinition { DeprecatedAttribute: not null })) + builder.AppendLine(FormatDocumentation(definition.Documentation, definition switch { - var deprecationReason = definition switch - { - EnumDefinition e => e?.DeprecatedAttribute?.Value, - RecordDefinition r => r?.DeprecatedAttribute?.Value, - _ => string.Empty - } ?? string.Empty; - builder.AppendLine(FormatDocumentation(definition.Documentation, deprecationReason, 0)); - } + EnumDefinition e => e?.DeprecatedDecorator, + RecordDefinition r => r?.DeprecatedDecorator, + _ => null + }, 0)); if (definition is EnumDefinition ed) { var is64Bit = ed.ScalarType.Is64Bit; @@ -700,15 +706,7 @@ public override string Compile(Version? languageVersion, TempoServices services for (var i = 0; i < ed.Members.Count; i++) { var field = ed.Members.ElementAt(i); - var deprecationReason = field.DeprecatedAttribute?.Value ?? string.Empty; - if (!string.IsNullOrWhiteSpace(field.Documentation)) - { - builder.AppendLine(FormatDocumentation(field.Documentation, deprecationReason, 2)); - } - else if (string.IsNullOrWhiteSpace(field.Documentation) && !string.IsNullOrWhiteSpace(deprecationReason)) - { - builder.AppendLine(FormatDeprecationDoc(deprecationReason, 2)); - } + builder.AppendLine(FormatDocumentation(field.Documentation, field.DeprecatedDecorator, 2)); if (is64Bit) { builder.AppendLine($" {field.Name.ToPascalCase()}: {field.ConstantValue}n,"); @@ -731,24 +729,17 @@ public override string Compile(Version? languageVersion, TempoServices services { var field = fd.Fields.ElementAt(i); var type = TypeName(field.Type); - var deprecationReason = field.DeprecatedAttribute?.Value ?? string.Empty; - if (!string.IsNullOrWhiteSpace(field.Documentation)) - { - builder.AppendLine(FormatDocumentation(field.Documentation, deprecationReason, 2)); - } - else if (string.IsNullOrWhiteSpace(field.Documentation) && !string.IsNullOrWhiteSpace(deprecationReason)) - { - builder.AppendLine(FormatDeprecationDoc(deprecationReason, 2)); - } - builder.AppendLine($" {(fd is StructDefinition { IsReadOnly: true } ? "readonly " : "")}{field.NameCamelCase}{(fd is MessageDefinition ? "?" : "")}: {type};"); + builder.AppendLine(FormatDocumentation(field.Documentation, field.DeprecatedDecorator, 2)); + + builder.AppendLine($" {(fd is StructDefinition { IsMutable: false } ? "readonly " : string.Empty)}{field.NameCamelCase}{(fd is MessageDefinition ? "?" : "")}: {type};"); } builder.AppendLine("}"); builder.AppendLine(); builder.CodeBlock($"export class {td.ClassName()} implements I{td.ClassName()}", indentStep, () => { - if (td.OpcodeAttribute is not null) + if (td.OpcodeDecorator is not null && td.OpcodeDecorator.TryGetValue("fourcc", out var fourcc)) { - builder.AppendLine($"public static readonly opcode: number = {td.OpcodeAttribute.Value} as {td.OpcodeAttribute.Value};"); + builder.AppendLine($"public static readonly opcode: number = {fourcc} as {fourcc};"); } if (td.DiscriminatorInParent != null) { @@ -759,7 +750,7 @@ public override string Compile(Version? languageVersion, TempoServices services { var field = fd.Fields.ElementAt(i); var type = TypeName(field.Type); - builder.AppendLine($"public {(fd is StructDefinition { IsReadOnly: true } ? "readonly " : "")}{field.NameCamelCase}{(fd is MessageDefinition ? "?" : "")}: {type};"); + builder.AppendLine($"public {(fd is StructDefinition { IsMutable: false } ? "readonly " : string.Empty)}{field.NameCamelCase}{(fd is MessageDefinition ? "?" : string.Empty)}: {type};"); } builder.AppendLine(); @@ -812,7 +803,7 @@ public override string Compile(Version? languageVersion, TempoServices services { builder.CodeBlock($"public static from{b.ClassName()}(value: I{b.ClassName()})", indentStep, () => { - builder.AppendLine($"return new {definition.ClassName()}({{ discriminator: {b.Discriminator}, value: new {b.ClassName()}(value)}});" ); + builder.AppendLine($"return new {definition.ClassName()}({{ discriminator: {b.Discriminator}, value: new {b.ClassName()}(value)}});"); }); builder.AppendLine(); builder.CodeBlock($"public is{b.ClassName()}(): this is {{ value: {b.ClassName()} }} & {{ data: Extract }}", indentStep, () => @@ -886,15 +877,16 @@ public override string Compile(Version? languageVersion, TempoServices services } } var serviceDefinitions = Schema.Definitions.Values.OfType(); - if (serviceDefinitions is not null && serviceDefinitions.Any() && services is not TempoServices.None) + if (serviceDefinitions is not null && serviceDefinitions.Any() && Config.Services is not TempoServices.None) { - if (services is TempoServices.Server or TempoServices.Both) + if (Config.Services is TempoServices.Server or TempoServices.Both) { foreach (var service in serviceDefinitions) { if (!string.IsNullOrWhiteSpace(service.Documentation)) { - builder.AppendLine(FormatDocumentation(service.Documentation, service.DeprecatedAttribute?.Value ?? string.Empty, 0)); + + builder.AppendLine(FormatDocumentation(service.Documentation, service.DeprecatedDecorator, 0)); } builder.CodeBlock($"export abstract class {service.BaseClassName()} extends BaseService", indentStep, () => { @@ -904,23 +896,24 @@ public override string Compile(Version? languageVersion, TempoServices services var methodType = method.Definition.Type; if (!string.IsNullOrWhiteSpace(method.Documentation)) { - builder.AppendLine(FormatDocumentation(method.Documentation, method.DeprecatedAttribute?.Value ?? string.Empty, 0)); + + builder.AppendLine(FormatDocumentation(method.Documentation, method.DeprecatedDecorator, 0)); } if (methodType is MethodType.Unary) { - builder.AppendLine($"public abstract {method.Definition.Name.ToCamelCase()}(record: I{method.Definition.RequestDefinition}, context: ServerContext): Promise;"); + builder.AppendLine($"public abstract {method.Definition.Name.ToCamelCase()}(record: I{method.Definition.RequestDefinition}, context: ServerContext): Promise;"); } else if (methodType is MethodType.ClientStream) { - builder.AppendLine($"public abstract {method.Definition.Name.ToCamelCase()}(records: () => AsyncGenerator, context: ServerContext): Promise;"); + builder.AppendLine($"public abstract {method.Definition.Name.ToCamelCase()}(records: () => AsyncGenerator, context: ServerContext): Promise;"); } else if (methodType is MethodType.ServerStream) { - builder.AppendLine($"public abstract {method.Definition.Name.ToCamelCase()}(record: I{method.Definition.RequestDefinition}, context: ServerContext): AsyncGenerator;"); + builder.AppendLine($"public abstract {method.Definition.Name.ToCamelCase()}(record: I{method.Definition.RequestDefinition}, context: ServerContext): AsyncGenerator;"); } else if (methodType is MethodType.DuplexStream) { - builder.AppendLine($"public abstract {method.Definition.Name.ToCamelCase()}(records: () => AsyncGenerator, context: ServerContext): AsyncGenerator;"); + builder.AppendLine($"public abstract {method.Definition.Name.ToCamelCase()}(records: () => AsyncGenerator, context: ServerContext): AsyncGenerator;"); } else { @@ -991,12 +984,12 @@ public override string Compile(Version? languageVersion, TempoServices services builder.AppendLine($"name: '{methodName}',"); builder.AppendLine($"service: serviceName,"); builder.AppendLine($"invoke: service.{methodName},"); - builder.AppendLine($"serialize: {method.Definition.ReturnDefintion}.encode,"); + builder.AppendLine($"serialize: {method.Definition.ResponseDefintion}.encode,"); builder.AppendLine($"deserialize: {method.Definition.RequestDefinition}.decode,"); - builder.AppendLine($"toJSON: {method.Definition.ReturnDefintion}.encodeToJSON,"); + builder.AppendLine($"stringify: {method.Definition.ResponseDefintion}.encodeToJSON,"); builder.AppendLine($"fromJSON: {method.Definition.RequestDefinition}.fromJSON,"); builder.AppendLine($"type: MethodType.{RpcSchema.GetMethodTypeName(methodType)},"); - }, close: $"}} as BebopMethod);"); + }, close: $"}} as BebopMethod);"); } } @@ -1012,16 +1005,16 @@ public override string Compile(Version? languageVersion, TempoServices services } - if (services is TempoServices.Client or TempoServices.Both) + if (Config.Services is TempoServices.Client or TempoServices.Both) { static (string RequestType, string ResponseType) GetFunctionTypes(MethodDefinition definition) { return definition.Type switch { - MethodType.Unary => ($"I{definition.RequestDefinition}", $"Promise"), - MethodType.ServerStream => ($"I{definition.RequestDefinition}", $"Promise>"), - MethodType.ClientStream => ($"() => AsyncGenerator", $"Promise"), - MethodType.DuplexStream => ($"() => AsyncGenerator", $"Promise>"), + MethodType.Unary => ($"I{definition.RequestDefinition}", $"Promise"), + MethodType.ServerStream => ($"I{definition.RequestDefinition}", $"Promise>"), + MethodType.ClientStream => ($"() => AsyncGenerator", $"Promise"), + MethodType.DuplexStream => ($"() => AsyncGenerator", $"Promise>"), _ => throw new InvalidOperationException($"Unsupported function type {definition.Type}") }; } @@ -1029,28 +1022,19 @@ public override string Compile(Version? languageVersion, TempoServices services foreach (var service in serviceDefinitions) { var clientName = service.ClassName().ReplaceLastOccurrence("Service", "Client"); - if (!string.IsNullOrWhiteSpace(service.Documentation)) - { - builder.AppendLine(FormatDocumentation(service.Documentation, service.DeprecatedAttribute?.Value ?? string.Empty, 0)); - } + builder.AppendLine(FormatDocumentation(service.Documentation, service.DeprecatedDecorator, 0)); builder.CodeBlock($"export interface I{clientName}", indentStep, () => { foreach (var method in service.Methods) { var (requestType, responseType) = GetFunctionTypes(method.Definition); - if (!string.IsNullOrWhiteSpace(method.Documentation)) - { - builder.AppendLine(FormatDocumentation(method.Documentation, method.DeprecatedAttribute?.Value ?? string.Empty, 0)); - } + builder.AppendLine(FormatDocumentation(method.Documentation, method.DeprecatedDecorator, 0)); builder.AppendLine($"{method.Definition.Name.ToCamelCase()}(request: {requestType}): {responseType};"); builder.AppendLine($"{method.Definition.Name.ToCamelCase()}(request: {requestType}, metadata: Metadata): {responseType};"); } }); builder.AppendLine(); - if (!string.IsNullOrWhiteSpace(service.Documentation)) - { - builder.AppendLine(FormatDocumentation(service.Documentation, service.DeprecatedAttribute?.Value ?? string.Empty, 0)); - } + builder.AppendLine(FormatDocumentation(service.Documentation, service.DeprecatedDecorator, 0)); builder.CodeBlock($"export class {clientName} extends BaseClient implements I{clientName}", indentStep, () => { foreach (var method in service.Methods) @@ -1059,22 +1043,19 @@ public override string Compile(Version? languageVersion, TempoServices services var methodName = method.Definition.Name.ToCamelCase(); var (requestType, responseType) = GetFunctionTypes(method.Definition); var methodType = method.Definition.Type; - builder.CodeBlock($"private static readonly {methodInfoName}: MethodInfo =", indentStep, () => + builder.CodeBlock($"private static readonly {methodInfoName}: MethodInfo =", indentStep, () => { builder.AppendLine($"name: '{methodName}',"); builder.AppendLine($"service: '{service.ClassName()}',"); builder.AppendLine($"id: {method.Id},"); builder.AppendLine($"serialize: {method.Definition.RequestDefinition}.encode,"); - builder.AppendLine($"deserialize: {method.Definition.ReturnDefintion}.decode,"); - builder.AppendLine($"toJSON: {method.Definition.RequestDefinition}.encodeToJSON,"); - builder.AppendLine($"fromJSON: {method.Definition.ReturnDefintion}.fromJSON,"); + builder.AppendLine($"deserialize: {method.Definition.ResponseDefintion}.decode,"); + builder.AppendLine($"stringify: {method.Definition.RequestDefinition}.encodeToJSON,"); + builder.AppendLine($"fromJSON: {method.Definition.ResponseDefintion}.fromJSON,"); builder.AppendLine($"type: MethodType.{RpcSchema.GetMethodTypeName(methodType)},"); }); - if (!string.IsNullOrWhiteSpace(method.Documentation)) - { - builder.AppendLine(FormatDocumentation(method.Documentation, method.DeprecatedAttribute?.Value ?? string.Empty, 0)); - } + builder.AppendLine(FormatDocumentation(method.Documentation, method.DeprecatedDecorator, 0)); builder.AppendLine($"async {methodName}(request: {requestType}): {responseType};"); builder.AppendLine($"async {methodName}(request: {requestType}, options: CallOptions): {responseType};"); @@ -1106,17 +1087,21 @@ public override string Compile(Version? languageVersion, TempoServices services } - if (!string.IsNullOrWhiteSpace(Schema.Namespace)) + if (!string.IsNullOrWhiteSpace(Config.Namespace)) { builder.Dedent(2); builder.AppendLine("}"); } - return builder.ToString(); + return ValueTask.FromResult(builder.ToString()); } - public override void WriteAuxiliaryFiles(string outputPath) + public override AuxiliaryFile? GetAuxiliaryFile() => null; + public override void WriteAuxiliaryFile(string outputPath) { // There is nothing to do here now that BebopView.ts is an npm package. } + + public override string Alias { get => "ts"; set => throw new NotImplementedException(); } + public override string Name { get => "TypeScript"; set => throw new NotImplementedException(); } } } diff --git a/Core/IO/BinarySchemaWriter.cs b/Core/IO/BinarySchemaWriter.cs index d77b2910..9bacc660 100644 --- a/Core/IO/BinarySchemaWriter.cs +++ b/Core/IO/BinarySchemaWriter.cs @@ -6,7 +6,7 @@ using System.Text; using Core.Exceptions; using Core.Meta; -using Core.Meta.Attributes; +using Core.Meta.Decorators; namespace Core.IO { @@ -50,7 +50,7 @@ private void WriteField(Definition parent, Field field) WriteMap(mt); } - WriteAttributes(field.Attributes); + WriteDecorators(field.Decorators); // Write the constant value for message fields if (parent is MessageDefinition) { @@ -71,10 +71,12 @@ private void WriteArray(ArrayType arrayType) } memberType = at.MemberType; } - Console.WriteLine($"Array depth: {depth}"); - Console.WriteLine($"Array member type: {memberType}"); _writer.Write(depth); _writer.Write(TypeToId(memberType)); + if (memberType is MapType mt) + { + WriteMap(mt); + } } private void WriteMap(MapType mapType) @@ -98,69 +100,71 @@ private void WriteConstant(BaseType baseType, object value) switch (baseType) { case BaseType.Byte: - value = (byte)bigInt; - break; + _writer.Write((byte)bigInt); + return; case BaseType.UInt16: - value = (ushort)bigInt; - break; + _writer.Write((ushort)bigInt); + return; case BaseType.Int16: - value = (short)bigInt; - break; - case BaseType.Int32: - value = (int)bigInt; - break; + _writer.Write((short)bigInt); + return; case BaseType.UInt32: - value = (uint)bigInt; - break; + _writer.Write((uint)bigInt); + return; + case BaseType.Int32: + _writer.Write((int)bigInt); + return; + case BaseType.UInt64: + _writer.Write((ulong)bigInt); + return; case BaseType.Int64: - value = (long)bigInt; - break; + _writer.Write((long)bigInt); + return; + } + } + else if (baseType.IsNumber() && value is string str) + { + switch (baseType) + { + case BaseType.Byte: + _writer.Write(byte.Parse(str)); + return; + case BaseType.UInt16: + _writer.Write(ushort.Parse(str)); + return; + case BaseType.Int16: + _writer.Write(short.Parse(str)); + return; + case BaseType.UInt32: + _writer.Write(uint.Parse(str)); + return; + case BaseType.Int32: + _writer.Write(int.Parse(str)); + return; case BaseType.UInt64: - value = (ulong)bigInt; - break; + _writer.Write(ulong.Parse(str)); + return; + case BaseType.Int64: + _writer.Write(long.Parse(str)); + return; } } - switch (baseType) - { - case BaseType.Bool: - _writer.Write((bool)value); - break; - case BaseType.Byte: - _writer.Write((byte)value); - break; - case BaseType.UInt16: - _writer.Write((ushort)value); - break; - case BaseType.Int16: - _writer.Write((short)value); - break; - case BaseType.UInt32: - _writer.Write((uint)value); - break; - case BaseType.Int32: - _writer.Write((int)value); - break; - case BaseType.UInt64: - _writer.Write((ulong)value); - break; - case BaseType.Int64: - _writer.Write((long)value); - break; - case BaseType.Float32: - _writer.Write((float)value); - break; - case BaseType.Float64: - _writer.Write((double)value); - break; - case BaseType.Guid: - _writer.Write(((Guid)value).ToByteArray()); - break; - case BaseType.String: - WriteString((string)value); - break; - default: - throw new CompilerException($"Unknown base type {baseType}"); + else if (baseType is BaseType.Guid && value is string guidStr) + { + _writer.Write(Guid.Parse(guidStr).ToByteArray()); + return; + } + else if (baseType is BaseType.Bool && value is string boolString) + { + _writer.Write(bool.Parse(boolString)); + return; } + else if (baseType is BaseType.String && value is string ss) + { + WriteString(ss); + return; + } + throw new CompilerException($"Unsupported base type {baseType}"); } private void WriteString(string value) @@ -171,58 +175,65 @@ private void WriteString(string value) #endregion - #region Attributes - private void WriteAttribute(BaseAttribute attribute) + #region Decorators + private void WriteDecorator(SchemaDecorator decorator) { - // attribute name - WriteString(attribute.Name); - // has value - bool hasValue = !string.IsNullOrWhiteSpace(attribute.Value); - _writer.Write(hasValue); - if (hasValue) - { - WriteString(attribute.Value); + // decorator name + WriteString(decorator.Identifier); + var arguments = decorator.Arguments; + // argument count + var count = (byte)arguments.Count; + if (count == 0) + { + _writer.Write((byte)0); + return; } - if (attribute is OpcodeAttribute oa) + if (count > byte.MaxValue) { - // eventually this should be a type id instead of a bool - // since they would both take up the same amount of space - // it will be forward compatible - _writer.Write(oa.IsNumber); + throw new CompilerException($"Decorator {decorator.Identifier} has too many arguments: {count}"); } - else + _writer.Write(count); + var definition = decorator.Definition; + foreach (var argument in arguments) { - _writer.Write(false); + var parameter = definition?.Parameters?.Where((p) => p.Identifier == argument.Key).FirstOrDefault(); + if (parameter is null) + { + throw new CompilerException($"Unknown parameter {argument.Key} for decorator {decorator.Identifier}"); + } + WriteString(argument.Key); + _writer.Write(TypeToId(new ScalarType(parameter.Type))); + WriteConstant(parameter.Type, argument.Value); } } - private void WriteAttributes(List? attributes) + private void WriteDecorators(List? decorators) { - if (attributes is null) + if (decorators is null) { _writer.Write((byte)0); return; } - _writer.Write((byte)attributes.Count); - foreach (var attribute in attributes) + _writer.Write((byte)decorators.Count); + foreach (var decorator in decorators) { - WriteAttribute(attribute); + WriteDecorator(decorator); } } - private void WriteAttributes(Definition definition) + private void WriteDecorators(Definition definition) { - List? attributes = definition switch + List? decorators = definition switch { - StructDefinition sd => sd.Attributes, - MessageDefinition md => md.Attributes, - UnionDefinition ud => ud.Attributes, - EnumDefinition ed => ed.Attributes, - ServiceDefinition sd => sd.Attributes, + StructDefinition sd => sd.Decorators, + MessageDefinition md => md.Decorators, + UnionDefinition ud => ud.Decorators, + EnumDefinition ed => ed.Decorators, + ServiceDefinition sd => sd.Decorators, _ => null, }; - WriteAttributes(attributes); + WriteDecorators(decorators); } #endregion @@ -247,7 +258,7 @@ private void WriteDefinitions() var definition = _definedTypes.ElementAt(i); WriteString(definition.Name); _writer.Write(DefinitionToKind(definition)); - WriteAttributes(definition); + WriteDecorators(definition); switch (definition) { case StructDefinition sd: @@ -274,17 +285,17 @@ private void WriteServices() foreach (var service in _services) { WriteString(service.Name); - WriteAttributes(service); + WriteDecorators(service); var methodCount = service.Methods.Count; _writer.Write((uint)methodCount); foreach (var method in service.Methods) { var methodDefinition = method.Definition; WriteString(methodDefinition.Name); - WriteAttributes(method.Attributes); + WriteDecorators(method.Decorators); _writer.Write((byte)methodDefinition.Type); _writer.Write(DefinitionToId(methodDefinition.RequestDefinition.AsString)); - _writer.Write(DefinitionToId(methodDefinition.ReturnDefintion.AsString)); + _writer.Write(DefinitionToId(methodDefinition.ResponseDefintion.AsString)); _writer.Write(method.Id); } } @@ -293,6 +304,7 @@ private void WriteServices() private void WriteMessage(MessageDefinition definition) { + _writer.Write(definition.MinimalEncodedSize(_schema)); var fieldCount = definition.Fields.Count; if (fieldCount < 0 || fieldCount > 255) { @@ -308,6 +320,8 @@ private void WriteMessage(MessageDefinition definition) private void WriteEnum(EnumDefinition definition) { _writer.Write(TypeToId(definition.ScalarType)); + _writer.Write(definition.IsBitFlags); + _writer.Write(definition.ScalarType.MinimalEncodedSize(_schema)); // TODO formalize 255 as the max number of fields var memberCount = definition.Members.Count; if (memberCount < 0 || memberCount > 255) @@ -318,22 +332,25 @@ private void WriteEnum(EnumDefinition definition) foreach (var member in definition.Members) { WriteString(member.Name); - WriteAttributes(member.Attributes); + WriteDecorators(member.Decorators); WriteConstant(definition.ScalarType.BaseType, member.ConstantValue); } + } private void WriteStruct(StructDefinition definition) { // modifier(s) - _writer.Write(definition.IsReadOnly); + _writer.Write(definition.IsMutable); + _writer.Write(definition.MinimalEncodedSize(_schema)); + _writer.Write(definition.IsFixedSize(_schema)); + var fieldCount = definition.Fields.Count; if (fieldCount < 0 || fieldCount > 255) { throw new CompilerException($"{definition.Name} exceeds maximum fields: has {fieldCount} fields"); } _writer.Write((byte)fieldCount); - Console.WriteLine($"Writing {fieldCount} fields for {definition.Name}"); foreach (var field in definition.Fields) { WriteField(definition, field); @@ -348,6 +365,7 @@ private void WriteUnion(UnionDefinition definition) { throw new CompilerException($"{definition.Name} exceeds maximum branches: has {branchCount} branches"); } + _writer.Write(definition.MinimalEncodedSize(_schema)); _writer.Write((byte)branchCount); for (var i = 0; i < branches.Count(); i++) { diff --git a/Core/IO/SchemaReader.cs b/Core/IO/SchemaReader.cs index 6444f7c2..bc4de91f 100644 --- a/Core/IO/SchemaReader.cs +++ b/Core/IO/SchemaReader.cs @@ -2,6 +2,7 @@ using System.Collections.Generic; using System.IO; using System.Linq; +using System.Threading; using System.Threading.Tasks; using Core.Lexer.Extensions; using Core.Lexer.Tokenization.Models; @@ -36,7 +37,12 @@ public static SchemaReader FromTextualSchema(string textualSchema) public static SchemaReader FromSchemaPaths(IEnumerable schemaPaths) { - return new SchemaReader(schemaPaths.Select(path => (path, File.ReadAllText(path))).ToList()); + return new SchemaReader(schemaPaths.Select(path => + { + if (!File.Exists(path)) + throw new FileNotFoundException($"Schema file not found: {path}"); + return (path, File.ReadAllText(path)); + }).ToList()); } private string CurrentFile => _schemas[_schemaIndex].Item2; @@ -108,12 +114,12 @@ public char GetChar() /// Append a file path to be read. /// /// True if a new file was actually added and must now be tokenized; false if this path was a duplicate. - public async Task AddFile(string absolutePath) + public bool AddFile(string absolutePath) { var fullPath = Path.GetFullPath(absolutePath); if (!_schemas.Any(t => Path.GetFullPath(t.Item1) == fullPath)) { - var text = await File.ReadAllTextAsync(fullPath); + var text = File.ReadAllText(fullPath); _schemas.Add((fullPath, text)); return true; } diff --git a/Core/Internal/SafeDirectoryInfoWrapper.cs b/Core/Internal/SafeDirectoryInfoWrapper.cs new file mode 100644 index 00000000..c8bbcb49 --- /dev/null +++ b/Core/Internal/SafeDirectoryInfoWrapper.cs @@ -0,0 +1,131 @@ + +using System; +using System.Collections.Generic; +using System.IO; +using Microsoft.Extensions.FileSystemGlobbing.Abstractions; + +namespace Core.Internal; + +/// +/// A wrapper for that doesn't throw for inaccessible directories or files. +/// +/// +/// This is a workaround for WASI where some weird behavior is observed when preopening directories. +/// +internal class SafeDirectoryInfoWrapper : DirectoryInfoBase +{ + private readonly DirectoryInfo _directoryInfo; + private readonly bool _isParentPath; + + private static readonly EnumerationOptions _enumerationOptions = new() + { + RecurseSubdirectories = false, + ReturnSpecialDirectories = false, + IgnoreInaccessible = true + }; + + /// + /// Initializes an instance of . + /// + /// The . + public SafeDirectoryInfoWrapper(DirectoryInfo directoryInfo) + : this(directoryInfo, isParentPath: false) + { } + + private SafeDirectoryInfoWrapper(DirectoryInfo directoryInfo, bool isParentPath) + { + _directoryInfo = directoryInfo; + _isParentPath = isParentPath; + } + + /// + public override IEnumerable EnumerateFileSystemInfos() + { + if (_directoryInfo.Exists) + { + IEnumerable fileSystemInfos; + try + { + fileSystemInfos = _directoryInfo.EnumerateFileSystemInfos("*", _enumerationOptions); + } + catch (DirectoryNotFoundException) + { + yield break; + } + foreach (FileSystemInfo fileSystemInfo in fileSystemInfos) + { + if (fileSystemInfo is DirectoryInfo directoryInfo) + { + yield return new SafeDirectoryInfoWrapper(directoryInfo); + } + else + { + yield return new FileInfoWrapper((FileInfo)fileSystemInfo); + } + } + } + } + + /// + /// Returns an instance of that represents a subdirectory. + /// + /// + /// If equals '..', this returns the parent directory. + /// + /// The directory name + /// The directory + public override DirectoryInfoBase? GetDirectory(string name) + { + bool isParentPath = string.Equals(name, "..", StringComparison.Ordinal); + + if (isParentPath) + { + return new SafeDirectoryInfoWrapper( + new DirectoryInfo(Path.Combine(_directoryInfo.FullName, name)), + isParentPath); + } + else + { + DirectoryInfo[] dirs = _directoryInfo.GetDirectories(name); + + if (dirs.Length == 1) + { + return new SafeDirectoryInfoWrapper(dirs[0], isParentPath); + } + else if (dirs.Length == 0) + { + return null; + } + else + { + // This shouldn't happen. The parameter name isn't supposed to contain wild card. + throw new InvalidOperationException( + $"More than one sub directories are found under {_directoryInfo.FullName} with name {name}."); + } + } + } + + /// + public override FileInfoBase GetFile(string name) + => new FileInfoWrapper(new FileInfo(Path.Combine(_directoryInfo.FullName, name))); + + /// + public override string Name => _isParentPath ? ".." : _directoryInfo.Name; + + /// + /// Returns the full path to the directory. + /// + /// + /// Equals the value of . + /// + public override string FullName => _directoryInfo.FullName; + + /// + /// Returns the parent directory. + /// + /// + /// Equals the value of . + /// + public override DirectoryInfoBase? ParentDirectory + => new DirectoryInfoWrapper(_directoryInfo.Parent!); +} \ No newline at end of file diff --git a/Core/Lexer/Tokenization/TokenKind.cs b/Core/Lexer/Tokenization/TokenKind.cs index 9a8825f2..a5639b56 100644 --- a/Core/Lexer/Tokenization/TokenKind.cs +++ b/Core/Lexer/Tokenization/TokenKind.cs @@ -7,7 +7,7 @@ namespace Core.Lexer.Tokenization /// public enum TokenKind : ushort { - #region Keywords + #region Keywords /// /// The 'enum' keyword which is used by the enum @@ -30,8 +30,8 @@ public enum TokenKind : ushort /// /// The 'readonly' keyword which is reserved by the compiler /// - [Keyword("readonly")] - ReadOnly, + [Keyword("mut")] + Mut, /// /// The 'map' keyword which is reserved by the compiler @@ -50,7 +50,7 @@ public enum TokenKind : ushort /// [Keyword("union")] Union, - + /// /// The 'service' keyword which is reserved by the compiler /// @@ -62,7 +62,18 @@ public enum TokenKind : ushort [Keyword("stream")] Stream, - #endregion + /// + /// The 'false' keyword which is reserved by the compiler + /// + [Keyword("false")] + False, + + /// + /// The 'trie' keyword which is reserved by the compiler + /// + [Keyword("true")] + True, + #endregion #region Literals @@ -72,7 +83,6 @@ public enum TokenKind : ushort /// A string literal. /// String, - /// /// Any numerical literal token. /// @@ -83,10 +93,10 @@ public enum TokenKind : ushort BlockComment, EndOfFile, - #endregion + #endregion - #region Symbols + #region Symbols /// /// @@ -200,7 +210,28 @@ public enum TokenKind : ushort /// /// [Symbol('-')] - Hyphen + Hyphen, + + /// + /// + /// + [Symbol('@')] + Decorator, + /// + /// + /// + [Symbol('$')] + Template, + /// + /// + /// + [Symbol('\\')] + BackSlash, + /// + /// + /// + [Symbol('`')] + BackTick, #endregion } } \ No newline at end of file diff --git a/Core/Lexer/Tokenization/Tokenizer.cs b/Core/Lexer/Tokenization/Tokenizer.cs index 7052be4a..73c225cf 100644 --- a/Core/Lexer/Tokenization/Tokenizer.cs +++ b/Core/Lexer/Tokenization/Tokenizer.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.Text; +using System.Threading; using System.Threading.Tasks; using Core.Exceptions; using Core.IO; @@ -41,14 +42,14 @@ public List Tokens } } - public async Task AddFile(string absolutePath) + public void AddFile(string absolutePath) { - if (await _reader.AddFile(absolutePath)) + if (_reader.AddFile(absolutePath)) { _newFilesToTokenize = true; } } - + /// /// Add an arbitrary bebop string to the token stream. /// @@ -158,9 +159,6 @@ _ when IsLiteral(surrogate, out var l) => l, _ => null }; - - - /// /// Determines if a surrogate leads into a block comment. /// diff --git a/Core/Logging/DiagnosticLogger.Enhanced.cs b/Core/Logging/DiagnosticLogger.Enhanced.cs index 82c4834e..34523150 100644 --- a/Core/Logging/DiagnosticLogger.Enhanced.cs +++ b/Core/Logging/DiagnosticLogger.Enhanced.cs @@ -34,11 +34,11 @@ private void RenderEnhancedSpanErrors(List exs) foreach (var ex in group) { var diagnostic = new Diagnostic(ex.Severity, ex.Message, ex.ErrorCode, ex.Span); - if (diagnostic.Severity == Severity.Warning) + if (diagnostic is { Severity: Severity.Warning, Span: not null }) { errataDiagnostic.WithLabel(new Label(fileName, GetRangeFromSpan(schemaSource, diagnostic.Span.Value), diagnostic.Message).WithColor(Color.Yellow)); } - else if (diagnostic.Severity == Severity.Error) + else if (diagnostic is { Severity: Severity.Error, Span: not null }) { errataDiagnostic.WithLabel(new Label(fileName, GetRangeFromSpan(schemaSource, diagnostic.Span.Value), diagnostic.Message).WithColor(Color.Red)); } @@ -55,29 +55,55 @@ public void WriteTable(Table table) private void RenderEnhancedException(Exception ex, int errorCode) { - string code = Markup.Escape($"[BOP{errorCode}]"); + try + { + string code = Markup.Escape($"[BOP{errorCode}]"); - // Write error code and exception name - _err.Markup($"[red bold]Error {code}:[/] "); - _err.MarkupLine($"[white]{ex.Message}[/]"); + // Write error code and exception name + _err.MarkupLine($"[maroon]Error {code}:[/] "); + _err.MarkupLineInterpolated($"[white]{ex.Message}[/]"); - // Write file path if FileNotFoundException - if (ex is FileNotFoundException fileNotFoundException) - { - var filePath = new TextPath(fileNotFoundException?.FileName ?? "[unknown]") + // Write file path if FileNotFoundException + if (ex is FileNotFoundException fileNotFoundException) { - StemStyle = Style.Parse("white"), - LeafStyle = Style.Parse("white") - }; - _err.WriteLine(); - _err.Write("File: "); - _err.Write(filePath); - _err.WriteLine(); + var filePath = new TextPath(fileNotFoundException?.FileName ?? "[unknown]") + { + StemStyle = Style.Parse("white"), + LeafStyle = Style.Parse("white") + }; + _err.WriteLine(); + _err.Write("File: "); + _err.Write(filePath); + _err.WriteLine(); + } + if (ex is { StackTrace: null } and { InnerException: not null }) + { + _err.WriteLine(); + _err.MarkupLine("[maroon]Inner Exception:[/]"); + if (_traceEnabled) + { + _err.WriteException(ex.InnerException); + } + else + { + _err.MarkupLineInterpolated($"[white]{ex.InnerException.Message}[/]"); + + } + } + if (_traceEnabled && !string.IsNullOrWhiteSpace(ex.StackTrace)) + { + // Write exception message + _err.WriteException(ex); + } } - if (!string.IsNullOrWhiteSpace(ex.StackTrace)) + catch (Exception e) { - // Write exception message + _err.WriteLine($"{errorCode}:"); _err.WriteException(ex); + _err.WriteLine(); + _err.WriteLine("An error occurred while rendering the exception:"); + _err.WriteException(e); + return; } } diff --git a/Core/Logging/DiagnosticLogger.Format.cs b/Core/Logging/DiagnosticLogger.Format.cs index 195b24f7..1664ef65 100644 --- a/Core/Logging/DiagnosticLogger.Format.cs +++ b/Core/Logging/DiagnosticLogger.Format.cs @@ -1,6 +1,5 @@ using System; using System.Text.Json; -using System.Text.Json.Serialization; using Core.Exceptions; using Core.Meta; @@ -21,15 +20,16 @@ private string FormatDiagnostic(Diagnostic diagnostic) case LogFormatter.MSBuild: var where = span == null ? ReservedWords.CompilerName : $"{span?.FileName}({span?.StartColonString(',')})"; return $"{where} : {diagnostic.Severity.ToString().ToLowerInvariant()} BOP{diagnostic.ErrorCode}: {message}"; - case LogFormatter.Structured: - where = span == null ? "" : $"Issue located in '{span?.FileName}' at {span?.StartColonString()}: "; - return $"[{DateTime.Now}][Compiler][{diagnostic.Severity}] {where}{message}"; case LogFormatter.JSON: - var options = new JsonSerializerOptions { Converters = { new JsonStringEnumConverter(JsonNamingPolicy.CamelCase) } }; - return JsonSerializer.Serialize(diagnostic, options); + return JsonSerializer.Serialize(diagnostic, JsonContext.Default.Diagnostic); case LogFormatter.Enhanced: default: throw new ArgumentOutOfRangeException(); } } + + private string FormatCompilerOutput(CompilerOutput output) + { + return JsonSerializer.Serialize(output, JsonContext.Default.CompilerOutput); + } } \ No newline at end of file diff --git a/Core/Logging/DiagnosticLogger.cs b/Core/Logging/DiagnosticLogger.cs index f986aceb..3eb99871 100644 --- a/Core/Logging/DiagnosticLogger.cs +++ b/Core/Logging/DiagnosticLogger.cs @@ -2,25 +2,30 @@ using System.Collections.Generic; using System.IO; using System.Linq; +using System.Runtime.InteropServices; using Core.Exceptions; using Spectre.Console; +using Core.Meta; namespace Core.Logging; public partial class DiagnosticLogger { private static DiagnosticLogger? _instance; - private readonly LogFormatter _formatter; + private LogFormatter _formatter; + private bool _traceEnabled; private readonly IAnsiConsole _out; private readonly IAnsiConsole _err; public IAnsiConsole Out => _out; public IAnsiConsole Error => _err; + public bool TraceEnabled => _traceEnabled; + #region Static Methods private DiagnosticLogger(LogFormatter formatter) { _formatter = formatter; - var isWasm = Environment.GetEnvironmentVariable("WASM") is not null; + var isWasm = RuntimeInformation.OSArchitecture is Architecture.Wasm; if (isWasm) { _out = VirtualTerminal.Create(Console.Out); @@ -43,6 +48,16 @@ private DiagnosticLogger(LogFormatter formatter) } } + public void SetFormatter(LogFormatter formatter) + { + _formatter = formatter; + } + + public void EnableTrace() + { + _traceEnabled = true; + } + public static void Initialize(LogFormatter formatter) { if (!Enum.IsDefined(typeof(LogFormatter), formatter)) @@ -63,7 +78,7 @@ public static DiagnosticLogger Instance { if (_instance is null) { - throw new Exception("The diagonstic logger has not been initialized."); + throw new CompilerException("The diagonstic logger has not been initialized."); } return _instance; } @@ -72,22 +87,22 @@ public static DiagnosticLogger Instance #endregion - public void WriteDiagonstic(Exception exception) + public int WriteDiagonstic(Exception exception) { switch (exception) { case SpanException span: WriteSpanDiagonstics(new List() { span }); - break; + return 1; case FileNotFoundException file: WriteFileNotFoundDiagonstic(file); - break; + return 1; case CompilerException compiler: WriteCompilerDiagonstic(compiler); - break; + return 1; default: WriteBaseDiagonstic(exception); - break; + return 1; } } @@ -96,6 +111,14 @@ public void WriteSpanDiagonstics(List exceptions) if (_formatter is LogFormatter.Enhanced) { RenderEnhancedSpanErrors(exceptions); + WriteErrorLine(string.Empty); + return; + } + if (_formatter is LogFormatter.JSON) + { + var warnings = exceptions.Where(e => e.Severity == Severity.Warning).ToList(); + var errors = exceptions.Where(e => e.Severity == Severity.Error).ToList(); + ErrorCompilerOutput(new CompilerOutput(warnings, errors, null)); return; } var messages = exceptions.Select(FormatSpanError); @@ -146,6 +169,25 @@ private void WriteBaseDiagonstic(Exception ex) _err.WriteLine(FormatDiagnostic(new(Severity.Error, ex.Message, Unknown, null))); } + + public void ErrorCompilerOutput(CompilerOutput output) + { + if (output.Errors.Count > 0 || output.Warnings.Count > 0) + { + _err.WriteLine(FormatCompilerOutput(output)); + _err.WriteLine(string.Empty); + } + } + + public void PrintCompilerOutput(CompilerOutput output) + { + if (output.Results is { Length: > 0 }) + { + _out.WriteLine(FormatCompilerOutput(output)); + _out.WriteLine(string.Empty); + } + } + public void WriteLine(string message) { _out.WriteLine(message); diff --git a/Core/Logging/DiagonsticLogger.Misc.cs b/Core/Logging/DiagonsticLogger.Misc.cs index 2c21a81d..e3ec6406 100644 --- a/Core/Logging/DiagonsticLogger.Misc.cs +++ b/Core/Logging/DiagonsticLogger.Misc.cs @@ -5,7 +5,8 @@ namespace Core.Logging; public partial class DiagnosticLogger { + internal const int FileNotFound = 404; internal const int Unknown = 1000; - internal record Diagnostic(Severity Severity, string Message, int ErrorCode, Span? Span) { } + public record Diagnostic(Severity Severity, string Message, int ErrorCode, Span? Span) { } } \ No newline at end of file diff --git a/Core/Logging/LogFormatter.cs b/Core/Logging/LogFormatter.cs index 89e9d5e4..adbbe8f1 100644 --- a/Core/Logging/LogFormatter.cs +++ b/Core/Logging/LogFormatter.cs @@ -1,18 +1,14 @@ using System; +using System.Text.Json.Serialization; namespace Core.Logging { + [JsonConverter(typeof(JsonStringEnumConverter))] /// /// Formatters that control the way writes data. /// public enum LogFormatter : uint { - /// - /// Data is formatted using structured logging. This is the default formatter. - /// - [Obsolete("Use Enhanced instead of Structure")] - Structured, - /// /// Data is formatted for MSBuild comparability. View the /// Enhanced } -} \ No newline at end of file +} diff --git a/Core/Logging/VirtualTerminal.cs b/Core/Logging/VirtualTerminal.cs index c8a019d7..d3e5e03e 100644 --- a/Core/Logging/VirtualTerminal.cs +++ b/Core/Logging/VirtualTerminal.cs @@ -25,7 +25,7 @@ private VirtualTerminal(IAnsiConsole owner, TextWriter output) Output = output ?? throw new ArgumentNullException(nameof(output)); } - public static VirtualTerminal Create(TextWriter consoleWriter, int width = 80, int height = 24) + public static VirtualTerminal Create(TextWriter consoleWriter, int width = int.MaxValue, int height = int.MaxValue) { var consoleOutput = new ConsoleRedirectWriter(consoleWriter); return new VirtualTerminal(AnsiConsole.Create(new AnsiConsoleSettings @@ -33,7 +33,7 @@ public static VirtualTerminal Create(TextWriter consoleWriter, int width = 80, i Ansi = AnsiSupport.Yes, Out = new VirtualConsoleOutput(consoleOutput, width, height), Interactive = InteractionSupport.No, - }), consoleOutput); + }), consoleWriter); } public void Clear(bool home) diff --git a/Core/Meta/Attributes/BaseAttribute.cs b/Core/Meta/Attributes/BaseAttribute.cs deleted file mode 100644 index eac4c2d3..00000000 --- a/Core/Meta/Attributes/BaseAttribute.cs +++ /dev/null @@ -1,9 +0,0 @@ -namespace Core.Meta.Attributes -{ - public abstract class BaseAttribute - { - public string Name {get; set;} = null!; - public string Value { get; set; } = null!; - public abstract bool TryValidate(out string reason); - } -} diff --git a/Core/Meta/Attributes/DeprecatedAttribute.cs b/Core/Meta/Attributes/DeprecatedAttribute.cs deleted file mode 100644 index fee6b347..00000000 --- a/Core/Meta/Attributes/DeprecatedAttribute.cs +++ /dev/null @@ -1,32 +0,0 @@ -namespace Core.Meta.Attributes -{ - /// - /// An attribute that deprecates fields of messages so they are skipped over. - /// - public sealed class DeprecatedAttribute : BaseAttribute - { - - public DeprecatedAttribute(string value) - { - - Name = "deprecated"; - Value = value; - } - - /// - /// Validates whether a reason was provided in the deprecation attribute. - /// - /// - public override bool TryValidate(out string message) - { - if (string.IsNullOrWhiteSpace(Value)) - { - message = "deprecation attributes must contain a reason."; - return false; - } - message = string.Empty; - return true; - - } - } -} \ No newline at end of file diff --git a/Core/Meta/Attributes/FlagsAttribute.cs b/Core/Meta/Attributes/FlagsAttribute.cs deleted file mode 100644 index 115845c2..00000000 --- a/Core/Meta/Attributes/FlagsAttribute.cs +++ /dev/null @@ -1,19 +0,0 @@ -namespace Core.Meta.Attributes -{ - /// - /// An attribute that marks an enum as a set of bit flags. - /// - public sealed class FlagsAttribute : BaseAttribute - { - public FlagsAttribute() - { - Name = "flags"; - } - - public override bool TryValidate(out string message) - { - message = ""; - return true; - } - } -} \ No newline at end of file diff --git a/Core/Meta/Attributes/OpcodeAttribute.cs b/Core/Meta/Attributes/OpcodeAttribute.cs deleted file mode 100644 index 040040b1..00000000 --- a/Core/Meta/Attributes/OpcodeAttribute.cs +++ /dev/null @@ -1,64 +0,0 @@ -using System; -using System.Globalization; -using System.Linq; -using Core.Meta.Extensions; - -namespace Core.Meta.Attributes -{ - /// - /// An attribute that uniquely identifies struct and messages - /// - public sealed class OpcodeAttribute : BaseAttribute - { - public readonly bool IsNumber; - - public OpcodeAttribute(string value, bool isNumber) - { - Name = "opcode"; - IsNumber = isNumber; - Value = value; - } - - /// - /// Validates if the inner value of the Opcode attribute is either an unsigned integer or FourCC. - /// - /// A tuple indicating if validation passed and if not a message describing why. - public override bool TryValidate(out string message) - { - if (string.IsNullOrWhiteSpace(Value)) - { - message = "No opcode was provided."; - return false; - } - - if (IsNumber) - { - if (!Value.TryParseUInt(out var result)) - { - message = $"Could not parse integer value \"{Value}\" of opcode attribute."; - return false; - } - Value = $"0x{result:X}"; - message = string.Empty; - return true; - } - - switch (Value.Length) - { - case 4 when Value.Any(ch => ch > sbyte.MaxValue): - message = "FourCC opcodes may only be ASCII"; - return false; - case 4: - { - char[] c = Value.ToCharArray(); - Value = $"0x{(c[3] << 24) | (c[2] << 16) | (c[1] << 8) | c[0]:X}"; - message = string.Empty; - return true; - } - default: - message = $"\"{Value}\" is not a valid FourCC."; - return false; - } - } - } -} diff --git a/Core/Meta/BebopConfig.cs b/Core/Meta/BebopConfig.cs new file mode 100644 index 00000000..fc32d446 --- /dev/null +++ b/Core/Meta/BebopConfig.cs @@ -0,0 +1,837 @@ +using System.Text.Json; +using System.Text.Json.Serialization; +using Core.Generators; +using System; +using System.Collections.Generic; +using System.IO; +using Microsoft.Extensions.FileSystemGlobbing; +using System.Linq; +using Core.Exceptions; +using Core.Meta.Extensions; +using Core.Logging; +using Core.Internal; + +namespace Core.Meta; + +/// +/// Represents the configuration for Bebop compiler and runtime behavior. +/// +public partial class BebopConfig +{ + /// + /// The name of the config file used by bebopc. + /// + public const string ConfigFileName = "bebop.json"; + + internal const string DefaultIncludeGlob = "**/*.bop"; + + /// + /// Gets or sets the file inclusion patterns for the Bebop compiler. + /// + [JsonPropertyName("include")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + public string[] Includes { get; set; } = [DefaultIncludeGlob]; + + /// + /// Gets or sets the file exclusion patterns for the Bebop compiler. + /// + [JsonPropertyName("exclude")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + public string[] Excludes { get; set; } = []; + + /// + /// Gets or sets the configurations for each code generator. + /// + [JsonPropertyName("generators")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + public GeneratorConfig[] Generators { get; set; } = []; + + /// + /// Gets or sets the options for file system watching. + /// + [JsonPropertyName("watchOptions")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + public WatchOptions WatchOptions { get; set; } = new(); + + /// + /// Gets or sets the warning codes to be suppressed by the Bebop compiler. + /// + [JsonPropertyName("noWarn")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + public int[] SupressedWarningCodes { get; set; } = []; + /// + /// Gets or sets a value indicating whether the Bebop compiler should emit code. + /// + [JsonPropertyName("noEmit")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + public bool NoEmit { get; set; } = false; + + [JsonPropertyName("extensions")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + public Dictionary Extensions { get; set; } = []; + + [JsonIgnore] + public string WorkingDirectory { get; private set; } = null!; + + /// + /// Resolves the file paths included in the configuration using the specified glob patterns. + /// + /// An enumerable of full file paths that match the include patterns and do not match the exclude patterns. + public IEnumerable ResolveIncludes() + { + List files = []; + + var matcher = new Matcher(); + // Add relative paths + matcher.AddIncludePatterns(Includes.Where(p => !Path.IsPathRooted(p) || !p.Contains("**"))); + matcher.AddExcludePatterns(Excludes); + var matches = matcher.Execute(new SafeDirectoryInfoWrapper(new DirectoryInfo(WorkingDirectory))).Files; + files.AddRange(matches.Select(match => Path.GetFullPath(Path.Combine(WorkingDirectory, match.Path)))); + + foreach (var include in Includes) + { + // Handle non-glob absolute paths + if (Path.IsPathRooted(include) && !include.Contains('*')) + { + if (File.Exists(include) && !Excludes.Contains(include)) + { + files.Add(include); + } + } + // Handle absolute paths with glob patterns, including recursive patterns + else if (Path.IsPathRooted(include) && include.Contains('*')) + { + string? baseDirPath; + string? globPattern; + + if (include.Contains("**")) + { + var separatorIndex = include.IndexOf("**"); + baseDirPath = include[..separatorIndex]; + globPattern = include[separatorIndex..]; + } + else + { + baseDirPath = Path.GetDirectoryName(include); + if (baseDirPath is null) + { + // Handle error or invalid path + continue; // Skip this iteration + } + globPattern = include[baseDirPath.Length..].TrimStart(Path.DirectorySeparatorChar); + } + var baseDirInfo = new DirectoryInfo(baseDirPath); + if (baseDirInfo.Exists) + { + var dirMatcher = new Matcher(); + dirMatcher.AddInclude(globPattern); + var dirMatches = dirMatcher.Execute(new SafeDirectoryInfoWrapper(baseDirInfo)).Files; + files.AddRange(dirMatches.Select(match => Path.Combine(baseDirPath, match.Path))); + } + } + } + return files.Distinct(); // To avoid duplicates if any + } + + /// + /// Searches recursively upwards to locate the Bebop configuration file. + /// + /// The fully qualified path to the configuration file, or null if not found. + public static string? Locate() + { + try + { + var workingDirectory = Directory.GetCurrentDirectory(); + var configFile = Directory.GetFiles(workingDirectory, ConfigFileName).FirstOrDefault(); + while (string.IsNullOrWhiteSpace(configFile)) + { + if (Directory.GetParent(workingDirectory) is not { Exists: true } parent) + { + break; + } + workingDirectory = parent.FullName; + if (parent.GetFiles(ConfigFileName)?.FirstOrDefault() is { Exists: true } file) + { + configFile = file.FullName; + } + } + return configFile; + } + catch (Exception ex) + { + if (DiagnosticLogger.Instance is { TraceEnabled: true } logger) + { + logger.WriteDiagonstic(ex); + } + return null; + } + } + + /// + /// Loads a BebopConfig from a file. + /// + /// The path to the configuration file. + /// The deserialized BebopConfig object. + /// Thrown when the specified configuration file is not found. + public static BebopConfig FromFile(string? configPath) + { + ArgumentNullException.ThrowIfNullOrWhiteSpace(configPath, nameof(configPath)); + if (!File.Exists(configPath)) + throw new FileNotFoundException("Failed to find bebop.json", configPath); + + var json = File.ReadAllText(configPath); + var config = FromJson(json); + config.WorkingDirectory = Path.GetDirectoryName(configPath) ?? throw new DirectoryNotFoundException("Failed to find directory containing bebop.json"); + return config; + } + + /// + /// Deserializes a BebopConfig from a JSON string. + /// + /// The JSON string to deserialize. + /// The deserialized BebopConfig object. + /// Thrown when deserialization fails. + public static BebopConfig FromJson(string json) => JsonSerializer.Deserialize(json, JsonContext.Default.BebopConfig) ?? throw new JsonException("Failed to deserialize bebop.json"); + + /// + /// Serializes the BebopConfig to a JSON string. + /// + /// The current configuration as JSON. + public string ToJson() => JsonSerializer.Serialize(this, JsonContext.Default.BebopConfig); + + public static BebopConfig Default => new() + { + WorkingDirectory = Directory.GetCurrentDirectory(), + }; + + public void Validate() + { + if (string.IsNullOrWhiteSpace(WorkingDirectory)) + { + throw new CompilerException("working directory is not defined."); + } + if (Directory.Exists(WorkingDirectory) is false) + { + throw new CompilerException($"working directory '{WorkingDirectory}' does not exist."); + } + if (Includes is { Length: > 0 }) + { + for (var i = 0; i < Includes.Length; i++) + { + var include = Includes[i]; + if (string.IsNullOrWhiteSpace(include)) + { + throw new CompilerException($"include pattern at index {i} is null or whitespace"); + } + if (!include.IsLegalFilePathOrGlob(out var invalidCharIndex)) + { + throw new CompilerException($"include pattern at index {i} is not a valid path or glob pattern{(invalidCharIndex >= 0 ? $": invalid character '{include[invalidCharIndex]}' at index {invalidCharIndex}" : ".")}"); + } + } + } + if (Excludes is { Length: > 0 }) + { + for (var i = 0; i < Excludes.Length; i++) + { + var exclude = Excludes[i]; + if (string.IsNullOrWhiteSpace(exclude)) + { + throw new CompilerException($"exclude pattern at index {i} is null or whitespace"); + } + if (!exclude.IsLegalFilePathOrGlob(out var invalidCharIndex)) + { + throw new CompilerException($"exclude pattern at index {i} is not a valid path or glob pattern{(invalidCharIndex >= 0 ? $": invalid character '{exclude[invalidCharIndex]}' at index {invalidCharIndex}" : ".")}"); + } + } + } + foreach (var generator in Generators) + { + if (string.IsNullOrWhiteSpace(generator.Alias)) + { + throw new CompilerException("Generator alias is null or whitespace"); + } + if (!generator.OutFile.IsLegalFilePath(out var invalidCharIndex)) + { + throw new CompilerException($"Generator outFile '{generator.OutFile}' is not a valid file path{(invalidCharIndex >= 0 ? $": invalid character '{generator.OutFile[invalidCharIndex]}' at index {invalidCharIndex}" : ".")}"); + } + if (!string.IsNullOrEmpty(generator.Namespace) && !generator.Namespace.IsValidNamespace()) + { + throw new CompilerException($"Generator namespace '{generator.Namespace}' is not a valid namespace."); + } + + if (generator is { OptionCount: > 0 }) + { + foreach (var option in generator.GetOptions()) + { + if (string.IsNullOrWhiteSpace(option.Key)) + { + throw new CompilerException($"Generator option key is null or whitespace"); + } + if (string.IsNullOrWhiteSpace(option.Value)) + { + throw new CompilerException($"Generator option value is null or whitespace"); + } + } + } + } + + for (var i = 0; i < WatchOptions.ExcludeDirectories.Length; i++) + { + var excludeDirectory = WatchOptions.ExcludeDirectories[i]; + if (string.IsNullOrWhiteSpace(excludeDirectory)) + { + throw new CompilerException($"exclude directory at index {i} is null or whitespace"); + } + if (!excludeDirectory.IsLegalPathOrGlob(out var invalidCharIndex)) + { + throw new CompilerException($"exclude directory at index {i} is not a valid path or glob pattern{(invalidCharIndex >= 0 ? $": invalid character '{excludeDirectory[invalidCharIndex]}' at index {invalidCharIndex}" : ".")}"); + } + } + for (var i = 0; i < WatchOptions.ExcludeFiles.Length; i++) + { + var excludeFile = WatchOptions.ExcludeFiles[i]; + if (string.IsNullOrWhiteSpace(excludeFile)) + { + throw new CompilerException($"exclude file at index {i} is null or whitespace"); + } + if (!excludeFile.IsLegalFilePathOrGlob(out var invalidCharIndex)) + { + throw new CompilerException($"exclude file at index {i} is not a valid file path or glob pattern{(invalidCharIndex >= 0 ? $": invalid character '{excludeFile[invalidCharIndex]}' at index {invalidCharIndex}" : ".")}"); + } + } + } +} + +public class WatchOptions +{ + /// + /// Gets or sets a list of directories to be excluded from file system watching. + /// + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("excludeDirectories")] + public string[] ExcludeDirectories { get; set; } = []; + + /// + /// Gets or sets a list of files to be excluded from file system watching. + /// + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("excludeFiles")] + public string[] ExcludeFiles { get; set; } = []; + + /// + /// Gets or sets a value indicating whether the output of the watch process should be preserved. + /// + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("preserveWatchOutput")] + public bool PreserveWatchOutput { get; set; } = false; +} + +/// +/// Provides custom JSON serialization and deserialization for . +/// This converter handles the unique JSON structure of the Bebop configuration. +/// +public class BebopConfigConverter : JsonConverter +{ + /// + /// Reads and converts the JSON to an instance of . + /// + /// The to read from. + /// The type of object to convert to. + /// The serializer options to use. + /// The deserialized object from the JSON. + /// Thrown when the JSON structure does not meet the expected format. + public override BebopConfig Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + { + if (reader.TokenType is not JsonTokenType.StartObject) + { + throw new JsonException("expected StartObject token"); + } + var bebopConfig = new BebopConfig(); + while (reader.Read()) + { + if (reader.TokenType is JsonTokenType.EndObject) + { + return bebopConfig; + } + if (reader.TokenType is not JsonTokenType.PropertyName) + { + throw new JsonException("expected PropertyName token"); + } + var propertyName = reader.GetString(); + reader.Read(); + switch (propertyName) + { + case "include": + var includes = GetStringArray(ref reader); + if (includes is not { Length: > 0 }) + { + includes = [BebopConfig.DefaultIncludeGlob]; + } + bebopConfig.Includes = includes; + break; + case "exclude": + bebopConfig.Excludes = GetStringArray(ref reader); + break; + case "generators": + bebopConfig.Generators = ReadGenerators(ref reader, options); + break; + case "watchOptions": + bebopConfig.WatchOptions = ReadWatchOptions(ref reader, options); + break; + case "noWarn": + bebopConfig.SupressedWarningCodes = GetIntArray(ref reader); + break; + case "noEmit": + bebopConfig.NoEmit = reader.GetBoolean(); + break; + case "extensions": + bebopConfig.Extensions = ReadExtensions(ref reader); + break; + default: + throw new JsonException($"unexpected property {propertyName}"); + } + } + return bebopConfig; + } + + /// + /// Reads the generator configurations from the JSON reader and constructs an array of . + /// + /// The JSON reader to read from. + /// The JSON serializer options. + /// An array of objects. + /// Thrown when the JSON structure of the generators is not valid. + private static Dictionary ReadExtensions(ref Utf8JsonReader reader) + { + var extensions = new Dictionary(); + if (reader.TokenType != JsonTokenType.StartObject) + { + throw new JsonException("expected StartObject token for options"); + } + + while (reader.Read()) + { + if (reader.TokenType is JsonTokenType.EndObject) + { + break; + } + + if (reader.TokenType == JsonTokenType.PropertyName) + { + var extensionName = reader.GetString(); + if (string.IsNullOrWhiteSpace(extensionName)) + { + throw new JsonException("extension name is null or whitespace"); + } + reader.Read(); // Move to the value + if (reader.TokenType is not JsonTokenType.String) + { + throw new JsonException("expected String token for extension value"); + } + + var extensionValue = reader.GetString(); + if (string.IsNullOrWhiteSpace(extensionValue)) + { + throw new JsonException($"version for '{extensionName}' is null or whitespace"); + } + extensions[extensionName] = extensionValue; + } + } + return extensions; + } + + private static GeneratorConfig[] ReadGenerators(ref Utf8JsonReader reader, JsonSerializerOptions options) + { + var generators = new List(); + + if (reader.TokenType != JsonTokenType.StartObject) + { + throw new JsonException("expected StartObject token for generators"); + } + + while (reader.Read()) + { + if (reader.TokenType == JsonTokenType.EndObject) + { + break; + } + + if (reader.TokenType == JsonTokenType.PropertyName) + { + var alias = reader.GetString(); + if (string.IsNullOrWhiteSpace(alias)) + { + throw new JsonException("unable to read generator alias"); + } + reader.Read(); + + if (reader.TokenType != JsonTokenType.StartObject) + { + throw new JsonException("expected StartObject token for generator config"); + } + bool simpleConstructor = true; + string? outFile = null; + TempoServices? services = null; + bool? emitNotice = null; + bool? emitBinarySchema = null; + string? @namespace = null; + var generatorOptions = new Dictionary(); + + while (reader.Read() && reader.TokenType != JsonTokenType.EndObject) + { + if (reader.TokenType == JsonTokenType.PropertyName) + { + var propName = reader.GetString(); + reader.Read(); // Move to the value + + switch (propName) + { + case "outFile": + outFile = reader.GetString(); + break; + case "services": + services = JsonSerializer.Deserialize(ref reader, JsonContext.Default.TempoServices); + simpleConstructor = false; + break; + case "emitNotice": + emitNotice = reader.GetBoolean(); + simpleConstructor = false; + break; + case "emitBinarySchema": + emitBinarySchema = reader.GetBoolean(); + simpleConstructor = false; + break; + case "namespace": + @namespace = reader.GetString(); + simpleConstructor = false; + break; + case "options": + ReadGeneratorOptions(ref reader, generatorOptions); + simpleConstructor = false; + break; + } + } + } + if (string.IsNullOrWhiteSpace(outFile)) + { + throw new JsonException("'outFile' is null or whitespace"); + } + EnsureLegalFilePath(outFile); + + GeneratorConfig generatorConfig; + if (simpleConstructor) + { + // Use the minimal constructor if only required properties are present + generatorConfig = new GeneratorConfig(alias, outFile); + } + else + { + // Use the full constructor if any optional properties are present + generatorConfig = new GeneratorConfig(alias, + outFile, + services.GetValueOrDefault(TempoServices.Both), + emitNotice.GetValueOrDefault(true), + @namespace ?? string.Empty, + emitBinarySchema.GetValueOrDefault(true), + generatorOptions); + } + generators.Add(generatorConfig); + } + } + return generators.ToArray(); + } + + + /// + /// Reads the generator configurations from the JSON reader and constructs an array of . + /// + /// The JSON reader to read from. + /// The JSON serializer options. + /// An array of objects. + /// Thrown when the JSON structure of the generators is not valid. + private static void ReadGeneratorOptions(ref Utf8JsonReader reader, Dictionary options) + { + if (reader.TokenType != JsonTokenType.StartObject) + { + throw new JsonException("expected StartObject token for options"); + } + + while (reader.Read()) + { + if (reader.TokenType is JsonTokenType.EndObject) + { + break; + } + + if (reader.TokenType == JsonTokenType.PropertyName) + { + var optionKey = reader.GetString(); + if (string.IsNullOrWhiteSpace(optionKey)) + { + throw new JsonException("option key is null or whitespace"); + } + reader.Read(); // Move to the value + if (reader.TokenType is not JsonTokenType.String) + { + throw new JsonException("expected String token for option value"); + } + + var optionValue = reader.GetString(); + if (string.IsNullOrWhiteSpace(optionValue)) + { + throw new JsonException($"option value for '{optionKey}' is null or whitespace"); + } + options[optionKey] = optionValue; + } + } + } + + /// + /// Reads watch options from the JSON reader and returns an instance of . + /// + /// The JSON reader to read from. + /// The JSON serializer options. + /// An instance of . + /// Thrown when the JSON structure of the watch options is not valid. + private static WatchOptions ReadWatchOptions(ref Utf8JsonReader reader, JsonSerializerOptions options) + { + var watchOptions = new WatchOptions(); + if (reader.TokenType is not JsonTokenType.StartObject) + { + throw new JsonException("expected StartObject token for watchOptions"); + } + + while (reader.Read()) + { + if (reader.TokenType is JsonTokenType.EndObject) + { + break; + } + + if (reader.TokenType == JsonTokenType.PropertyName) + { + var propName = reader.GetString(); + reader.Read(); // Move to the value + + switch (propName) + { + case "excludeDirectories": + watchOptions.ExcludeDirectories = GetStringArray(ref reader); + break; + case "excludeFiles": + watchOptions.ExcludeFiles = GetStringArray(ref reader); + break; + case "preserveWatchOutput": + watchOptions.PreserveWatchOutput = reader.GetBoolean(); + break; + } + } + } + return watchOptions; + } + + + /// + /// Writes the object to JSON. + /// + /// The to write to. + /// The object to serialize. + /// The serializer options to use. + /// Thrown when the method is not implemented. + public override void Write(Utf8JsonWriter writer, BebopConfig value, JsonSerializerOptions options) + { + writer.WriteStartObject(); + if (value.Includes is { Length: > 0 }) + { + // Write "include" array + writer.WritePropertyName("include"); + writer.WriteStartArray(); + foreach (var include in value.Includes) + { + writer.WriteStringValue(include); + } + writer.WriteEndArray(); + } + + // Write "exclude" array + if (value.Excludes is { Length: > 0 }) + { + writer.WritePropertyName("exclude"); + writer.WriteStartArray(); + foreach (var exclude in value.Excludes) + { + writer.WriteStringValue(exclude); + } + writer.WriteEndArray(); + } + + if (value.Generators is { Length: > 0 }) + { + // Write "generators" object + WriteGenerators(writer, value.Generators, options); + } + + if (value.WatchOptions is { ExcludeDirectories.Length: > 0 } or { ExcludeFiles.Length: > 0 }) + { + // Write "watchOptions" object + WriteWatchOptions(writer, value.WatchOptions, options); + } + if (value.SupressedWarningCodes is { Length: > 0 }) + { + writer.WritePropertyName("noWarn"); + writer.WriteStartArray(); + foreach (var warningCode in value.SupressedWarningCodes) + { + writer.WriteNumberValue(warningCode); + } + writer.WriteEndArray(); + } + if (value.NoEmit) + { + writer.WriteBoolean("noEmit", value.NoEmit); + } + + writer.WriteEndObject(); + } + + private static void WriteGenerators(Utf8JsonWriter writer, GeneratorConfig[] generators, JsonSerializerOptions options) + { + writer.WriteStartObject("generators"); + + foreach (var generator in generators) + { + writer.WritePropertyName(generator.Alias); + + writer.WriteStartObject(); + writer.WriteString("outFile", generator.OutFile); + + if (generator.Services is not TempoServices.Both) + JsonSerializer.Serialize(writer, generator.Services, JsonContext.Default.TempoServices); + + if (generator.EmitNotice is false) + writer.WriteBoolean("emitNotice", generator.EmitNotice); + + if (generator.EmitBinarySchema is false) + writer.WriteBoolean("emitBinarySchema", generator.EmitBinarySchema); + + if (!string.IsNullOrWhiteSpace(generator.Namespace)) + writer.WriteString("namespace", generator.Namespace); + + if (generator is { OptionCount: > 0 }) + { + writer.WriteStartObject("options"); + foreach (var option in generator.GetOptions()) + { + writer.WritePropertyName(option.Key); + writer.WriteStringValue(option.Value); + } + writer.WriteEndObject(); + } + writer.WriteEndObject(); + } + writer.WriteEndObject(); + } + + private static void WriteWatchOptions(Utf8JsonWriter writer, WatchOptions watchOptions, JsonSerializerOptions options) + { + writer.WriteStartObject("watchOptions"); + if (watchOptions.ExcludeDirectories is { Length: > 0 }) + { + writer.WritePropertyName("excludeDirectories"); + writer.WriteStartArray(); + foreach (var excludeDirectory in watchOptions.ExcludeDirectories) + { + writer.WriteStringValue(excludeDirectory); + } + writer.WriteEndArray(); + } + + if (watchOptions.ExcludeFiles is { Length: > 0 }) + { + writer.WritePropertyName("excludeFiles"); + writer.WriteStartArray(); + foreach (var excludeFile in watchOptions.ExcludeFiles) + { + writer.WriteStringValue(excludeFile); + } + writer.WriteEndArray(); + } + + if (watchOptions.PreserveWatchOutput is true) + { + writer.WriteBoolean("preserveWatchOutput", watchOptions.PreserveWatchOutput); + } + + writer.WriteEndObject(); + } + + /// + /// Ensures that the given file path and file name do not contain any illegal characters. + /// + /// The file path to validate. + /// Thrown if the file path or file name contains illegal characters. + private static void EnsureLegalFilePath(string path) + { + // Check for invalid path characters + if (!path.IsLegalPath(out var invalidPathCharIndex)) + { + throw new JsonException($"The path '{path}' contains invalid characters: '{path[invalidPathCharIndex]}'"); + } + + // Extract the file name from the path and check for invalid file name characters + if (!path.IsLegalFilePath(out var invalidFileNameCharIndex)) + { + throw new JsonException($"The file name '{Path.GetFileName(path)}' contains invalid characters: '{path[invalidFileNameCharIndex]}'"); + } + } + + private static string[] GetStringArray(ref Utf8JsonReader reader) + { + if (reader.TokenType is not JsonTokenType.StartArray) + { + throw new JsonException("expected StartArray token"); + } + var list = new List(); + while (reader.Read()) + { + if (reader.TokenType is JsonTokenType.EndArray) + { + return [.. list]; + } + if (reader.TokenType is not JsonTokenType.String) + { + throw new JsonException("expected String token"); + } + var value = reader.GetString(); + if (string.IsNullOrEmpty(value)) + { + throw new JsonException("expected non-null string value"); + } + list.Add(value); + } + throw new JsonException("expected EndArray token"); + } + + private static int[] GetIntArray(ref Utf8JsonReader reader) + { + if (reader.TokenType is not JsonTokenType.StartArray) + { + throw new JsonException("expected StartArray token"); + } + var list = new List(); + while (reader.Read()) + { + if (reader.TokenType is JsonTokenType.EndArray) + { + return list.ToArray(); + } + if (reader.TokenType is not JsonTokenType.Number) + { + throw new JsonException("expected Number token"); + } + list.Add(reader.GetInt32()); + } + throw new JsonException("expected EndArray token"); + } + +} \ No newline at end of file diff --git a/Core/Meta/BebopSchema.cs b/Core/Meta/BebopSchema.cs index b38f850b..d6dd425b 100644 --- a/Core/Meta/BebopSchema.cs +++ b/Core/Meta/BebopSchema.cs @@ -5,6 +5,7 @@ using Core.Exceptions; using Core.IO; using Core.Lexer.Tokenization.Models; +using Core.Meta.Decorators; using Core.Meta.Extensions; using Core.Parser; using Core.Parser.Extensions; @@ -30,9 +31,8 @@ public struct BebopSchema public List Imports { get; } - public BebopSchema(string nameSpace, Dictionary definitions, HashSet<(Token, Token)> typeReferences, List? parsingErrors = null, List? parsingWarnings = null, List? imports = null) + public BebopSchema(Dictionary definitions, HashSet<(Token, Token)> typeReferences, List? parsingErrors = null, List? parsingWarnings = null, List? imports = null) { - Namespace = nameSpace; Definitions = definitions; Imports = imports ?? new List(); @@ -43,10 +43,7 @@ public BebopSchema(string nameSpace, Dictionary definitions, _parsingWarnings = parsingWarnings ?? new(); _typeReferences = typeReferences; } - /// - /// An optional namespace that is provided to the compiler. - /// - public string Namespace { get; } + /// /// All Bebop definitions in this schema, keyed by their name. /// @@ -122,6 +119,67 @@ public List SortedDefinitions() return _sortedDefinitions; } + private readonly List ValidateDefinitionDecorators(List decorators, Definition definition) + { + var validationErrors = new List(); + foreach (var decorator in decorators) + { + if (!decorator.IsUsableOn()) + { + validationErrors.Add(new InvalidDecoratorUsageException(decorator.Identifier, $"Decorator '{decorator.Identifier}' cannot be applied to {definition.Name.ToLowerInvariant()} '{definition.Name}'", decorator.Span)); + } + if (!decorator.Definition.TryValidate(out var reason, decorator)) + { + if (decorator.Identifier == "opcode") + { + validationErrors.Add(new InvalidOpcodeDecoratorValueException(definition, reason)); + } + else + { + validationErrors.Add(new DecoratorValidationException(decorator.Identifier, reason, decorator.Span)); + } + } + if (decorator.Identifier == "opcode") + { + if (definition is RecordDefinition td && td.OpcodeDecorator is not null) + { + if (Definitions.Values.Count(d => d is RecordDefinition td2 && td2.OpcodeDecorator is not null && td2.OpcodeDecorator.Arguments["fourcc"].Equals(td.OpcodeDecorator.Arguments["fourcc"])) > 1) + { + validationErrors.Add(new DuplicateOpcodeException(td)); + } + } + } + if (!decorator.Definition.AllowMultiple && decorators.Count(a => a.Identifier == decorator.Identifier) > 1) + { + validationErrors.Add(new MultipleDecoratorsException(decorator.Identifier, decorator.Span)); + } + } + return validationErrors; + } + + private static List ValidateFieldDecorators(List decorators, Field field, Definition parent) + { + var validationErrors = new List(); + foreach (var decorator in decorators) + { + + if (!decorator.IsUsableOn()) + { + var hint = parent is StructDefinition && decorator.Identifier == "deprecated" ? "deprecated decorator cannot be applied to struct fields" : ""; + validationErrors.Add(new InvalidDecoratorUsageException(decorator.Identifier, $"Decorator '{decorator.Identifier}' cannot be applied to '{parent.Name}.{field.Name}'", decorator.Span, hint)); + } + if (!decorator.Definition.TryValidate(out var reason, decorator)) + { + validationErrors.Add(new DecoratorValidationException(decorator.Identifier, reason, decorator.Span)); + } + if (!decorator.Definition.AllowMultiple && decorators.Count(a => a.Identifier == decorator.Identifier) > 1) + { + validationErrors.Add(new MultipleDecoratorsException(decorator.Identifier, decorator.Span)); + } + } + return validationErrors; + } + /// /// Validates that the schema is made up of well-formed values. /// @@ -140,8 +198,9 @@ public List Validate() // TODO figure out why this is happening // it is obvious that the ServiceDefinition isn't being added // but it remains to be seen why it is still being picked up by _typeReferences - if (!Definitions.ContainsKey(definitionToken.Lexeme)) { - // errors.Add(new UnrecognizedTypeException(definitionToken, typeToken.Lexeme)); + if (!Definitions.ContainsKey(definitionToken.Lexeme)) + { + // errors.Add(new UnrecognizedTypeException(definitionToken, typeToken.Lexeme)); continue; } var definition = Definitions[definitionToken.Lexeme]; @@ -168,28 +227,27 @@ public List Validate() { errors.Add(new ReservedIdentifierException(definition.Name, definition.Span)); } - if (definition is RecordDefinition td && td.OpcodeAttribute != null) + if (definition is FieldsDefinition fd) { - if (!td.OpcodeAttribute.TryValidate(out var opcodeReason)) + var fieldDefinitionDecorators = fd.Decorators; + if (fieldDefinitionDecorators.Count is > 0) { - errors.Add(new InvalidOpcodeAttributeValueException(td, opcodeReason)); + errors.AddRange(ValidateDefinitionDecorators(fieldDefinitionDecorators, fd)); } - if (Definitions.Values.Count(d => d is RecordDefinition td2 && td2.OpcodeAttribute != null && td2.OpcodeAttribute.Value.Equals(td.OpcodeAttribute.Value)) > 1) + if (fd.Fields.Count > byte.MaxValue) { - errors.Add(new DuplicateOpcodeException(td)); + errors.Add(new StackSizeExceededException("A definition cannot have more than 255 fields", fd.Span)); } - } - if (definition is FieldsDefinition fd) - { foreach (var field in fd.Fields) { - if (ReservedWords.Identifiers.Contains(field.Name)) + var fieldDecorators = field.Decorators; + if (fieldDecorators.Count is > 0) { - errors.Add(new ReservedIdentifierException(field.Name, field.Span)); + errors.AddRange(ValidateFieldDecorators(fieldDecorators, field, fd)); } - if (field.DeprecatedAttribute != null && fd is StructDefinition) + if (ReservedWords.Identifiers.Contains(field.Name)) { - errors.Add(new InvalidDeprecatedAttributeUsageException(field)); + errors.Add(new ReservedIdentifierException(field.Name, field.Span)); } if (fd.Fields.Count(f => f.Name.Equals(field.Name, StringComparison.OrdinalIgnoreCase)) > 1) { @@ -216,6 +274,11 @@ public List Validate() errors.Add(new InvalidFieldException(field, "Message member index must start at 1")); break; } + case MessageDefinition when field.ConstantValue > byte.MaxValue: + { + errors.Add(new InvalidFieldException(field, "Message index must be less than or equal to 255")); + break; + } case MessageDefinition when field.ConstantValue > fd.Fields.Count: { errors.Add(new InvalidFieldException(field, "Message index is greater than field count")); @@ -228,11 +291,26 @@ public List Validate() } if (definition is ServiceDefinition sd) { + var serviceDecorators = sd.Decorators; + if (serviceDecorators.Count is > 0) + { + errors.AddRange(ValidateDefinitionDecorators(serviceDecorators, sd)); + } var usedMethodNames = new HashSet(); var usedMethodIds = new HashSet(); + if (sd.Methods.Count > byte.MaxValue) + { + errors.Add(new StackSizeExceededException("A service cannot have more than 255 methods", sd.Span)); + } foreach (var b in sd.Methods) { + var fnd = b.Definition; + var methodDecorators = b.Decorators; + if (methodDecorators.Count is > 0) + { + errors.AddRange(ValidateDefinitionDecorators(methodDecorators, fnd)); + } if (!usedMethodNames.Add(fnd.Name.ToSnakeCase())) { errors.Add(new DuplicateServiceMethodNameException(sd.Name, fnd.Name, fnd.Span)); @@ -245,9 +323,9 @@ public List Validate() { errors.Add(new InvalidServiceRequestTypeException(sd.Name, fnd.Name, fnd.RequestDefinition, fnd.Span)); } - if (fnd.ReturnDefintion.IsDefined(this) && !fnd.ReturnDefintion.IsAggregate(this)) + if (fnd.ResponseDefintion.IsDefined(this) && !fnd.ResponseDefintion.IsAggregate(this)) { - errors.Add(new InvalidServiceReturnTypeException(sd.Name, fnd.Name, fnd.ReturnDefintion, fnd.Span)); + errors.Add(new InvalidServiceReturnTypeException(sd.Name, fnd.Name, fnd.ResponseDefintion, fnd.Span)); } if (fnd.Parent != sd) @@ -258,10 +336,25 @@ public List Validate() } if (definition is EnumDefinition ed) { + var enumDecorators = ed.Decorators; + if (enumDecorators.Count is > 0) + { + errors.AddRange(ValidateDefinitionDecorators(enumDecorators, ed)); + } var values = new HashSet(); var names = new HashSet(StringComparer.OrdinalIgnoreCase); + if (ed.Members.Count > byte.MaxValue) + { + errors.Add(new StackSizeExceededException("An enum cannot have more than 255 members", ed.Span)); + } foreach (var field in ed.Members) { + + var fieldDecorators = field.Decorators; + if (fieldDecorators.Count is > 0) + { + errors.AddRange(ValidateFieldDecorators(fieldDecorators, field, ed)); + } if (!ed.IsBitFlags && values.Contains(field.ConstantValue)) { errors.Add(new InvalidFieldException(field, "Enum value must be unique if the enum is not a [flags] enum")); @@ -287,6 +380,18 @@ public List Validate() names.Add(field.Name); } } + if (definition is UnionDefinition ud) + { + var unionDecorators = ud.Decorators; + if (unionDecorators.Count is > 0) + { + errors.AddRange(ValidateDefinitionDecorators(unionDecorators, ud)); + } + if (ud.Branches.Count > byte.MaxValue) + { + errors.Add(new StackSizeExceededException("A union cannot have more than 255 members", ud.Span)); + } + } } var methodIds = new Dictionary(); @@ -304,8 +409,9 @@ public List Validate() _validationErrors = errors; return errors; } - - public readonly byte[] ToBinary() { + + public readonly byte[] ToBinary() + { using var writer = new BinarySchemaWriter(this); return writer.Encode(); } diff --git a/Core/Meta/Decorators/ContributedDecorator.cs b/Core/Meta/Decorators/ContributedDecorator.cs new file mode 100644 index 00000000..c97d4b9e --- /dev/null +++ b/Core/Meta/Decorators/ContributedDecorator.cs @@ -0,0 +1,38 @@ +namespace Core.Meta.Decorators; + +public sealed record ContributedDecorator : DecoratorDefinition +{ + public ContributedDecorator(string Identifier, string Description, DecoratorTargets Targets, bool AllowMultiple, DecoratorParameter[]? Parameters = null) + : base(Identifier, Description, Targets, AllowMultiple, Parameters) + { + + } + + public override bool TryValidate(out string reason, SchemaDecorator? schemaDecorator = null) + { + if (schemaDecorator is null) + { + reason = "Schema decorator is null."; + return false; + } + if (Parameters is not null) + { + foreach (var parameter in Parameters) + { + if (schemaDecorator.Arguments.TryGetValue(parameter.Identifier, out var argumentValue)) + { + if (parameter.Validator is not null) + { + if (!parameter.Validator.Pattern.IsMatch(argumentValue)) + { + reason = parameter.Validator.Reason; + return false; + } + } + } + } + } + reason = string.Empty; + return true; + } +} \ No newline at end of file diff --git a/Core/Meta/Decorators/DebugDecorator.cs b/Core/Meta/Decorators/DebugDecorator.cs new file mode 100644 index 00000000..b231d1a3 --- /dev/null +++ b/Core/Meta/Decorators/DebugDecorator.cs @@ -0,0 +1,22 @@ +namespace Core.Meta.Decorators; + +public sealed record DebugDecorator() : +DecoratorDefinition("debug", "For debugging the parser", DecoratorTargets.All, false, new DecoratorParameter[] + { + new("astring", "The a string value", BaseType.String, true, null, null), + new("anumber", "The b uint32 value", BaseType.UInt32, true, null, null), + new("aboolean", "The c bool value", BaseType.Bool, false, "false", null), + new("anumber2", "The d uint32 value", BaseType.UInt32, false, "0", null), + }) +{ + public override bool TryValidate(out string reason, SchemaDecorator? schemaDecorator = null) + { + if (schemaDecorator is null) + { + reason = "Schema decorator is null."; + return false; + } + reason = string.Empty; + return true; + } +} \ No newline at end of file diff --git a/Core/Meta/Decorators/DecoratorDefinition.cs b/Core/Meta/Decorators/DecoratorDefinition.cs new file mode 100644 index 00000000..6fd21359 --- /dev/null +++ b/Core/Meta/Decorators/DecoratorDefinition.cs @@ -0,0 +1,178 @@ +using System; +using System.Text.RegularExpressions; + +namespace Core.Meta.Decorators; + + +public abstract record DecoratorDefinition(string Identifier, string Description, DecoratorTargets Targets, bool AllowMultiple, DecoratorParameter[]? Parameters = null) +{ + public abstract bool TryValidate(out string reason, SchemaDecorator? schemaDecorator = null); +} + +public sealed record DecoratorParameter(string Identifier, string Description, BaseType Type, bool IsRequired, string? DefaultValue = null, DecoratorParameterValueValidator? Validator = null) +{ + public bool IsValueAssignable(string value, out string reason) + { + switch (Type) + { + case BaseType.String: + if (string.IsNullOrEmpty(value)) + { + reason = "String values cannot be empty."; + return false; + } + reason = string.Empty; + return true; + case BaseType.Bool: + if (string.IsNullOrWhiteSpace(value)) + { + reason = "Bool values cannot be empty."; + return false; + } + if (bool.TryParse(value, out _)) + { + reason = string.Empty; + return true; + } + reason = $"\"{value}\" is not a valid bool."; + return false; + case BaseType.Byte: + if (string.IsNullOrWhiteSpace(value)) + { + reason = "Byte values cannot be empty."; + return false; + } + if (byte.TryParse(value, out _)) + { + reason = string.Empty; + return true; + } + reason = $"\"{value}\" is not a valid byte."; + return false; + case BaseType.UInt16: + if (string.IsNullOrWhiteSpace(value)) + { + reason = "UInt16 values cannot be empty."; + return false; + } + if (ushort.TryParse(value, out _)) + { + reason = string.Empty; + return true; + } + reason = $"\"{value}\" is not a valid UInt16."; + return false; + case BaseType.Int16: + if (string.IsNullOrWhiteSpace(value)) + { + reason = "Int16 values cannot be empty."; + return false; + } + if (short.TryParse(value, out _)) + { + reason = string.Empty; + return true; + } + reason = $"\"{value}\" is not a valid Int16."; + return false; + case BaseType.UInt32: + if (string.IsNullOrWhiteSpace(value)) + { + reason = "UInt32 values cannot be empty."; + return false; + } + if (uint.TryParse(value, out _)) + { + reason = string.Empty; + return true; + } + reason = $"\"{value}\" is not a valid UInt32."; + return false; + case BaseType.Int32: + if (string.IsNullOrWhiteSpace(value)) + { + reason = "Int32 values cannot be empty."; + return false; + } + if (int.TryParse(value, out _)) + { + reason = string.Empty; + return true; + } + reason = $"\"{value}\" is not a valid Int32."; + return false; + case BaseType.UInt64: + if (string.IsNullOrWhiteSpace(value)) + { + reason = "UInt64 values cannot be empty."; + return false; + } + if (ulong.TryParse(value, out _)) + { + reason = string.Empty; + return true; + } + reason = $"\"{value}\" is not a valid UInt64."; + return false; + case BaseType.Int64: + if (string.IsNullOrWhiteSpace(value)) + { + reason = "Int64 values cannot be empty."; + return false; + } + if (long.TryParse(value, out _)) + { + reason = string.Empty; + return true; + } + reason = $"\"{value}\" is not a valid Int64."; + return false; + case BaseType.Float32: + if (string.IsNullOrWhiteSpace(value)) + { + reason = "Float32 values cannot be empty."; + return false; + } + if (float.TryParse(value, out _)) + { + reason = string.Empty; + return true; + } + reason = $"\"{value}\" is not a valid Float32."; + return false; + case BaseType.Float64: + if (string.IsNullOrWhiteSpace(value)) + { + reason = "Float64 values cannot be empty."; + return false; + } + if (double.TryParse(value, out _)) + { + reason = string.Empty; + return true; + } + reason = $"\"{value}\" is not a valid Float64."; + return false; + case BaseType.Guid: + if (string.IsNullOrWhiteSpace(value)) + { + reason = "Guid values cannot be empty."; + return false; + } + if (Guid.TryParse(value, out _)) + { + reason = string.Empty; + return true; + } + reason = $"\"{value}\" is not a valid Guid."; + return false; + } + reason = $"\"{value}\" is not a valid {Type}."; + return false; + } +} + +public sealed record DecoratorParameterValueValidator(Regex Pattern, string Reason) +{ + +} \ No newline at end of file diff --git a/Core/Meta/Decorators/DecoratorTargets.cs b/Core/Meta/Decorators/DecoratorTargets.cs new file mode 100644 index 00000000..0e4aaf76 --- /dev/null +++ b/Core/Meta/Decorators/DecoratorTargets.cs @@ -0,0 +1,17 @@ +using System; + +namespace Core.Meta.Decorators; + +[System.Flags] +public enum DecoratorTargets +{ + None = 0, + Enum = 1 << 0, // 1 + Message = 1 << 1, // 2 + Struct = 1 << 2, // 4 + Union = 1 << 3, // 8 + Field = 1 << 4, // 16 + Service = 1 << 5, // 32 + Method = 1 << 6, // 64 + All = Enum | Message | Struct | Union | Field | Service | Method // Combines all flags +} \ No newline at end of file diff --git a/Core/Meta/Decorators/DeprecatedDecorator.cs b/Core/Meta/Decorators/DeprecatedDecorator.cs new file mode 100644 index 00000000..4f0e7965 --- /dev/null +++ b/Core/Meta/Decorators/DeprecatedDecorator.cs @@ -0,0 +1,36 @@ +using System.Linq; + +namespace Core.Meta.Decorators +{ + + public sealed record DeprecatedDecorator() : DecoratorDefinition("deprecated", "A decorator that marks types as deprecated. When applied to fields of messages they are skipped over.", + DecoratorTargets.All, + false, + new[] + { + new DecoratorParameter("reason", "The reason for the deprecation.", BaseType.String, true) + }) + { + public override bool TryValidate(out string reason, SchemaDecorator? schemaDecorator = null) + { + if (schemaDecorator is null) + { + reason = "Schema decorator is null."; + return false; + } + if (!schemaDecorator.Arguments.TryGetValue("reason", out var value)) + { + reason = "No value found for 'reason'."; + return false; + + } + if (string.IsNullOrWhiteSpace(value)) + { + reason = "The deprecated reason cannot be an empty strings."; + return false; + } + reason = string.Empty; + return true; + } + } +} \ No newline at end of file diff --git a/Core/Meta/Decorators/FlagsDecorator.cs b/Core/Meta/Decorators/FlagsDecorator.cs new file mode 100644 index 00000000..522bd531 --- /dev/null +++ b/Core/Meta/Decorators/FlagsDecorator.cs @@ -0,0 +1,17 @@ +namespace Core.Meta.Decorators +{ + + public sealed record FlagsDecorator() : DecoratorDefinition("flags", "A decorator that marks an enum as a set of bit flags.", DecoratorTargets.Enum, false) + { + public override bool TryValidate(out string reason, SchemaDecorator? schemaDecorator = null) + { + if (schemaDecorator is null) + { + reason = "Schema decorator is null."; + return false; + } + reason = string.Empty; + return true; + } + } +} \ No newline at end of file diff --git a/Core/Meta/Decorators/OpcodeDecorator.cs b/Core/Meta/Decorators/OpcodeDecorator.cs new file mode 100644 index 00000000..7d1e9e07 --- /dev/null +++ b/Core/Meta/Decorators/OpcodeDecorator.cs @@ -0,0 +1,57 @@ +using System; +using System.Globalization; +using System.Linq; +using Core.Meta.Extensions; + +namespace Core.Meta.Decorators +{ + + public sealed record OpcodeDecorator() : DecoratorDefinition("opcode", "A decorator that uniquely identifies a struct or messages.", + DecoratorTargets.Message | DecoratorTargets.Struct | DecoratorTargets.Union, false, + new DecoratorParameter[] + { + new("fourcc", "The value of the opcode.", BaseType.String, true, null, null), + }) + { + public override bool TryValidate(out string reason, SchemaDecorator? schemaDecorator = null) + { + if (schemaDecorator is null) + { + reason = "Schema decorator is null."; + return false; + } + if (!schemaDecorator.Arguments.TryGetValue("fourcc", out var fourcc)) + { + reason = "No value found for 'fourcc'."; + return false; + } + if (string.IsNullOrWhiteSpace(fourcc)) + { + reason = "The fourcc cannot be an empty string."; + return false; + } + if (fourcc.TryParseUInt(out var result)) + { + schemaDecorator.Arguments["fourcc"] = $"0x{result:X}"; + reason = string.Empty; + return true; + } + switch (fourcc.Length) + { + case 4 when fourcc.Any(ch => ch > sbyte.MaxValue): + reason = "FourCC opcodes may only be ASCII"; + return false; + case 4: + { + char[] c = fourcc.ToCharArray(); + schemaDecorator.Arguments["fourcc"] = $"0x{(c[3] << 24) | (c[2] << 16) | (c[1] << 8) | c[0]:X}"; + reason = string.Empty; + return true; + } + default: + reason = $"\"{fourcc}\" is not a valid FourCC."; + return false; + } + } + } +} diff --git a/Core/Meta/Decorators/SchemaDecorator.cs b/Core/Meta/Decorators/SchemaDecorator.cs new file mode 100644 index 00000000..0da838d6 --- /dev/null +++ b/Core/Meta/Decorators/SchemaDecorator.cs @@ -0,0 +1,35 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; +using System.Linq; +using System.Reflection.Metadata; +using Core.Lexer.Tokenization.Models; + +namespace Core.Meta.Decorators; + +public sealed record SchemaDecorator(string Identifier, DecoratorTargets Target, Span Span, Dictionary Arguments, DecoratorDefinition Definition) +{ + + public bool TryGetValue(string parameter, [NotNullWhen(true)] out string? value) + { + return Arguments.TryGetValue(parameter, out value); + } + + public bool IsUsableOn() + { + if (Definition.Targets is DecoratorTargets.All) + { + if (Target.HasFlag(DecoratorTargets.Struct | DecoratorTargets.Field) && Identifier.Equals("deprecated", StringComparison.OrdinalIgnoreCase)) + { + return false; + } + return true; + } + + if (Target == Definition.Targets) + { + return true; + } + return Definition.Targets.HasFlag(Target); + } +} \ No newline at end of file diff --git a/Core/Meta/Definition.cs b/Core/Meta/Definition.cs index 1308c4d0..0a308b62 100644 --- a/Core/Meta/Definition.cs +++ b/Core/Meta/Definition.cs @@ -3,7 +3,7 @@ using System.Linq; using System.Text; using Core.Lexer.Tokenization.Models; -using Core.Meta.Attributes; +using Core.Meta.Decorators; using Core.Parser; namespace Core.Meta @@ -71,17 +71,17 @@ public List Scope /// public abstract class RecordDefinition : Definition { - protected RecordDefinition(string name, Span span, string documentation, List? attributes, Definition? parent = null) : + protected RecordDefinition(string name, Span span, string documentation, List decorators, Definition? parent = null) : base(name, span, documentation, parent) { - Attributes = attributes; + Decorators = decorators; } - public BaseAttribute? OpcodeAttribute => Attributes?.FirstOrDefault((a) => a is OpcodeAttribute); + public SchemaDecorator? OpcodeDecorator => Decorators?.FirstOrDefault((a) => a.Identifier == "opcode"); - public BaseAttribute? DeprecatedAttribute => Attributes?.FirstOrDefault((a) => a is DeprecatedAttribute); + public SchemaDecorator? DeprecatedDecorator => Decorators?.FirstOrDefault((a) => a.Identifier == "deprecated"); - public List? Attributes { get; } + public List Decorators { get; } /// /// If this definition is part of a union branch, then this is its discriminator in the parent union. @@ -102,8 +102,8 @@ protected RecordDefinition(string name, Span span, string documentation, List public abstract class FieldsDefinition : RecordDefinition { - protected FieldsDefinition(string name, Span span, string documentation, List? attributes, ICollection fields, Definition? parent = null) : - base(name, span, documentation, attributes, parent) + protected FieldsDefinition(string name, Span span, string documentation, List decorators, ICollection fields, Definition? parent = null) : + base(name, span, documentation, decorators, parent) { Fields = fields; } @@ -121,16 +121,16 @@ public override IEnumerable Dependencies() => /// public class StructDefinition : FieldsDefinition { - public StructDefinition(string name, Span span, string documentation, List? attributes, ICollection fields, bool isReadOnly, Definition? parent = null) : - base(name, span, documentation, attributes, fields, parent) + public StructDefinition(string name, Span span, string documentation, List decorators, ICollection fields, bool isMutable, Definition? parent = null) : + base(name, span, documentation, decorators, fields, parent) { - IsReadOnly = isReadOnly; + IsMutable = isMutable; } /// /// Is this struct "read-only"? (This will mean something like: not generating setters in the codegen.) /// - public bool IsReadOnly { get; } + public bool IsMutable { get; } override public int MinimalEncodedSize(BebopSchema schema) { @@ -157,15 +157,6 @@ public bool IsFixedSize(Dictionary definitions) => Fields.Al _ => false }); public bool IsFixedSize(BebopSchema schema) => IsFixedSize(schema.Definitions); - - public byte[] EncodeSchema() - { - - - - - return Array.Empty(); - } } /// @@ -175,7 +166,7 @@ public byte[] EncodeSchema() /// public class MessageDefinition : FieldsDefinition { - public MessageDefinition(string name, Span span, string documentation, List? attributes, ICollection fields, Definition? parent = null) : base(name, span, documentation, attributes, fields, parent) + public MessageDefinition(string name, Span span, string documentation, List decorators, ICollection fields, Definition? parent = null) : base(name, span, documentation, decorators, fields, parent) { } @@ -196,14 +187,14 @@ public EnumDefinition( Span span, string documentation, ICollection members, - List? attributes, + List decorators, BaseType baseType, Definition? parent = null ) : base(name, span, documentation, parent) { Members = members; - Attributes = attributes; - IsBitFlags = attributes?.Any((a) => a is Attributes.FlagsAttribute) ?? false; + Decorators = decorators; + IsBitFlags = decorators?.Any((a) => a.Identifier == "flags") ?? false; BaseType = baseType; } @@ -217,12 +208,12 @@ public EnumDefinition( public ScalarType ScalarType => new ScalarType(BaseType); - public List? Attributes {get; } + public List Decorators { get; } - public BaseAttribute? DeprecatedAttribute => Attributes?.FirstOrDefault((a) => a is DeprecatedAttribute); + public SchemaDecorator? DeprecatedDecorator => Decorators?.FirstOrDefault((a) => a.Identifier == "deprecated"); } - public readonly struct UnionBranch + public class UnionBranch { public readonly byte Discriminator; public readonly RecordDefinition Definition; @@ -234,27 +225,27 @@ public UnionBranch(byte discriminator, RecordDefinition definition) } } - public readonly struct ServiceMethod + public class ServiceMethod { public readonly string Documentation; public readonly uint Id; public readonly MethodDefinition Definition; - public BaseAttribute? DeprecatedAttribute => Attributes?.FirstOrDefault((a) => a is DeprecatedAttribute); - public List? Attributes { get; } + public SchemaDecorator? DeprecatedDecorator => Decorators?.FirstOrDefault((a) => a.Identifier == "deprecated"); + public List Decorators { get; } - public ServiceMethod(uint id, MethodDefinition definition, string documentation, List? attributes) + public ServiceMethod(uint id, MethodDefinition definition, string documentation, List decorators) { Id = id; Definition = definition; Documentation = documentation; - Attributes = attributes; + Decorators = decorators; } } public class UnionDefinition : RecordDefinition { - public UnionDefinition(string name, Span span, string documentation, List? attributes, ICollection branches, Definition? parent = null) : base(name, span, documentation, attributes, parent) + public UnionDefinition(string name, Span span, string documentation, List decorators, ICollection branches, Definition? parent = null) : base(name, span, documentation, decorators, parent) { Branches = branches; } @@ -272,19 +263,19 @@ override public int MinimalEncodedSize(BebopSchema schema) public class ServiceDefinition : Definition { - - public ServiceDefinition(string name, Span span, string documentation, ICollection methods, List? attributes) : base(name, span, documentation) + + public ServiceDefinition(string name, Span span, string documentation, ICollection methods, List decorators) : base(name, span, documentation) { foreach (var m in methods) { m.Definition.Parent = this; } Methods = methods; - Attributes = attributes; + Decorators = decorators; } - public List? Attributes { get; } - public BaseAttribute? DeprecatedAttribute => Attributes?.FirstOrDefault((a) => a is DeprecatedAttribute); + public List Decorators { get; } + public SchemaDecorator? DeprecatedDecorator => Decorators?.FirstOrDefault((a) => a.Identifier == "deprecated"); public ICollection Methods { get; } public override IEnumerable Dependencies() => Methods.SelectMany(f => f.Definition.Dependencies()); @@ -296,7 +287,7 @@ public override string ToString() builder.AppendLine("Methods ->"); foreach (var method in Methods) { - builder.AppendLine($" {method.Definition.Name}({method.Definition.RequestDefinition}): {method.Definition.ReturnDefintion} ({method.Id})"); + builder.AppendLine($" {method.Definition.Name}({method.Definition.RequestDefinition}): {method.Definition.ResponseDefintion} ({method.Id})"); } return builder.ToString(); } @@ -307,20 +298,20 @@ public override string ToString() /// public class MethodDefinition : Definition { - public MethodDefinition(string name, Span span, string documentation, TypeBase requestDefinition, TypeBase returnDefintion, MethodType methodType, Definition? parent = null) + public MethodDefinition(string name, Span span, string documentation, TypeBase requestDefinition, TypeBase responseDefinition, MethodType methodType, Definition? parent = null) : base(name, span, documentation, parent) { RequestDefinition = requestDefinition; - ReturnDefintion = returnDefintion; + ResponseDefintion = responseDefinition; Type = methodType; } public TypeBase RequestDefinition { get; } - public TypeBase ReturnDefintion { get; } + public TypeBase ResponseDefintion { get; } public MethodType Type { get; } public override IEnumerable Dependencies() => - RequestDefinition.Dependencies().Concat(ReturnDefintion.Dependencies()); + RequestDefinition.Dependencies().Concat(ResponseDefintion.Dependencies()); } public class ConstDefinition : Definition diff --git a/Core/Meta/Extensions/StringExtensions.cs b/Core/Meta/Extensions/StringExtensions.cs index 304b65eb..0d0a8238 100644 --- a/Core/Meta/Extensions/StringExtensions.cs +++ b/Core/Meta/Extensions/StringExtensions.cs @@ -1,12 +1,14 @@ using System; +using System.IO; using System.Linq; using System.Runtime.CompilerServices; using System.Text; +using System.Text.RegularExpressions; using Core.Lexer.Extensions; namespace Core.Meta.Extensions { - public static class StringExtensions + public static partial class StringExtensions { private const char SnakeSeparator = '_'; private const char KebabSeparator = '-'; @@ -290,5 +292,199 @@ public static string ConvertToTypeScriptUInt8ArrayInitializer(this byte[] byteAr return builder.ToString(); } + + + public static bool IsLegalPath(this string path, out int index) + { + index = -1; + if (string.IsNullOrWhiteSpace(path)) + { + return false; + } + // Check for invalid path characters + var invalidPathChars = Path.GetInvalidPathChars(); + var invalidPathCharIndex = path.IndexOfAny(invalidPathChars); + if (invalidPathCharIndex >= 0) + { + index = invalidPathCharIndex; + return false; + } + return true; + } + + public static bool IsLegalFilePath(this string filePath, out int index) + { + index = -1; + if (string.IsNullOrWhiteSpace(filePath)) + { + return false; + } + + // Check for invalid path characters in the entire filePath + if (!IsLegalPath(filePath, out index)) + { + return false; + } + + // Extract the file name from the path and check for invalid file name characters + var fileName = Path.GetFileName(filePath); + var invalidFileNameChars = Path.GetInvalidFileNameChars(); + var invalidFileNameCharIndex = fileName.IndexOfAny(invalidFileNameChars); + if (invalidFileNameCharIndex >= 0) + { + // Adjust the index to be in the context of the full filePath, not just fileName + index = filePath.LastIndexOf(fileName) + invalidFileNameCharIndex; + return false; + } + return true; + } + + public static bool IsLegalPathGlob(this string pathOrGlob) + { + if (string.IsNullOrWhiteSpace(pathOrGlob)) + { + return false; + } + if (LegalPathGlobRegex().IsMatch(pathOrGlob)) + { + return true; + } + return true; + } + + public static bool IsLegalFileGlobal(this string fileGlob) + { + if (string.IsNullOrWhiteSpace(fileGlob)) + { + return false; + } + if (LegalFileGlobRegex().IsMatch(fileGlob)) + { + return true; + } + return true; + } + + public static bool IsValidNamespace(this string @namespace) + { + if (string.IsNullOrWhiteSpace(@namespace)) + { + return false; + } + return NamespaceRegex().IsMatch(@namespace); + } + + public static bool IsLegalPathOrGlob(this string pathOrGlob, out int invalidIndex) + { + invalidIndex = -1; + if (string.IsNullOrWhiteSpace(pathOrGlob)) + { + return false; + } + if (IsLegalPathGlob(pathOrGlob)) + { + return true; + } + if (IsLegalPath(pathOrGlob, out invalidIndex)) + { + return true; + } + return false; + } + public static bool IsLegalFilePathOrGlob(this string filePathOrGlob, out int invalidIndex) + { + invalidIndex = -1; + if (string.IsNullOrWhiteSpace(filePathOrGlob)) + { + return false; + } + if (IsLegalFileGlobal(filePathOrGlob)) + { + return true; + } + if (IsLegalFilePath(filePathOrGlob, out invalidIndex)) + { + return true; + } + return false; + } + + public static bool IsPathAttemptingTraversal(this string path) + { + // Split the path into segments + var segments = path.Split(new char[] { Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar }, StringSplitOptions.None); + + // Keep track of the depth + int depth = 0; + + foreach (var segment in segments) + { + if (segment == "..") + { + // Going up one directory + depth--; + + // If depth goes negative, it's attempting to go above the root + if (depth < 0) + { + return true; + } + } + else if (!string.IsNullOrEmpty(segment) && segment != ".") + { + // Normal directory, go one level deeper + depth++; + } + } + + return false; + } + + + /// + /// Escapes a string with \u0000 style escape sequences + /// + /// + /// + public static string EscapeString(this string? value) + { + ArgumentNullException.ThrowIfNull(value); + + StringBuilder escaped = new StringBuilder(); + foreach (char c in value) + { + switch (c) + { + case '\\': escaped.Append(@"\\"); break; + case '\"': escaped.Append("\\\""); break; + case '\b': escaped.Append("\\b"); break; + case '\f': escaped.Append("\\f"); break; + case '\n': escaped.Append("\\n"); break; + case '\r': escaped.Append("\\r"); break; + case '\t': escaped.Append("\\t"); break; + default: + if (c < 32 || c > 127) + { + escaped.AppendFormat("\\u{0:x4}", (int)c); + } + else + { + escaped.Append(c); + } + break; + } + } + return escaped.ToString(); + } + + + [GeneratedRegex(@"^(\*\*\/|.*\/)$")] + private static partial Regex LegalPathGlobRegex(); + + [GeneratedRegex(@"^.*[\*\?\[\]].*(\.[a-zA-Z0-9]+)?$")] + private static partial Regex LegalFileGlobRegex(); + + [GeneratedRegex(@"^[a-zA-Z]+(\.[a-zA-Z]+)*$")] + private static partial Regex NamespaceRegex(); } } diff --git a/Core/Meta/Field.cs b/Core/Meta/Field.cs index 3237599e..a2553911 100644 --- a/Core/Meta/Field.cs +++ b/Core/Meta/Field.cs @@ -2,7 +2,7 @@ using System.Numerics; using System.Linq; using Core.Lexer.Tokenization.Models; -using Core.Meta.Attributes; +using Core.Meta.Decorators; using Core.Meta.Extensions; namespace Core.Meta @@ -12,14 +12,14 @@ public readonly struct Field public Field(string name, in TypeBase type, in Span span, - in List? attributes, + in List decorators, in BigInteger constantValue, string documentation) { Name = name; Type = type; Span = span; - Attributes = attributes; - DeprecatedAttribute = attributes?.FirstOrDefault((a) => a is DeprecatedAttribute); + Decorators = decorators; + DeprecatedDecorator = decorators?.FirstOrDefault((a) => a.Identifier == "deprecated"); ConstantValue = constantValue; Documentation = documentation; } @@ -40,12 +40,12 @@ public Field(string name, /// public Span Span { get; } - public List? Attributes {get; } + public List Decorators { get; } /// /// Indicates if the member has been marked as no longer recommended for use. /// - public BaseAttribute? DeprecatedAttribute { get; } + public SchemaDecorator? DeprecatedDecorator { get; } /// /// For enums, this is a constant value. For messages, this is a field index. For structs, this is unused. diff --git a/Core/Meta/JsonContext.cs b/Core/Meta/JsonContext.cs new file mode 100644 index 00000000..95895b89 --- /dev/null +++ b/Core/Meta/JsonContext.cs @@ -0,0 +1,87 @@ + +using System; +using System.Text.Json; +using System.Text.Json.Serialization; +using System.Text.Json.Serialization.Metadata; +using Core.Exceptions; +using Core.Generators; +using Core.Lexer.Tokenization.Models; +using Core.Logging; +using Core.Meta.Extensions; + +namespace Core.Meta; +[JsonSourceGenerationOptions( + JsonSerializerDefaults.Web, + AllowTrailingCommas = true, + ReadCommentHandling = JsonCommentHandling.Skip, + DefaultBufferSize = 10, + WriteIndented = true, + PropertyNamingPolicy = JsonKnownNamingPolicy.CamelCase, + UseStringEnumConverter = true, + Converters = [typeof(SpanExceptionConverter), typeof(ExceptionConverter), typeof(BebopConfigConverter), typeof(GeneratorContextConverter)])] +[JsonSerializable(typeof(BebopConfig))] +[JsonSerializable(typeof(TempoServices))] +[JsonSerializable(typeof(Severity))] +[JsonSerializable(typeof(GeneratorConfig))] +[JsonSerializable(typeof(WatchOptions))] +[JsonSerializable(typeof(LogFormatter))] +[JsonSerializable(typeof(CompilerOutput))] +[JsonSerializable(typeof(GeneratedFile))] +[JsonSerializable(typeof(AuxiliaryFile))] +[JsonSerializable(typeof(SpanException))] +[JsonSerializable(typeof(Exception))] +[JsonSerializable(typeof(DiagnosticLogger.Diagnostic))] +[JsonSerializable(typeof(Span))] +[JsonSerializable(typeof(GeneratorContext))] +public partial class JsonContext : JsonSerializerContext +{ +} + +class SpanExceptionConverter : JsonConverter +{ + public override SpanException Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + { + throw new NotImplementedException(); + } + + public override void Write(Utf8JsonWriter writer, SpanException value, JsonSerializerOptions options) + { + writer.WriteStartObject(); + writer.WriteStartObject("span"); + writer.WriteString("fileName", value.Span.FileName); + writer.WriteNumber("startLine", value.Span.StartLine); + writer.WriteNumber("endLine", value.Span.EndLine); + writer.WriteNumber("startColumn", value.Span.StartColumn); + writer.WriteNumber("endColumn", value.Span.EndColumn); + writer.WriteNumber("lines", value.Span.Lines); + writer.WriteEndObject(); + writer.WriteNumber("errorCode", value.ErrorCode); + writer.WriteString("severity", value.Severity.ToString().ToCamelCase()); + writer.WriteString("message", value.Message); + writer.WriteEndObject(); + } +} + +class ExceptionConverter : JsonConverter +{ + public override Exception Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + { + throw new NotImplementedException(); + } + + public override void Write(Utf8JsonWriter writer, Exception value, JsonSerializerOptions options) + { + writer.WriteStartObject(); + if (value is CompilerException compilerException) + { + writer.WriteNumber("errorCode", compilerException.ErrorCode); + } + writer.WriteString("message", value.Message); + if (value.InnerException is not null) + { + writer.WritePropertyName("innerException"); + JsonSerializer.Serialize(writer, value.InnerException, options); + } + writer.WriteEndObject(); + } +} \ No newline at end of file diff --git a/Core/Meta/Misc.cs b/Core/Meta/Misc.cs new file mode 100644 index 00000000..a6cac050 --- /dev/null +++ b/Core/Meta/Misc.cs @@ -0,0 +1,10 @@ +namespace Core.Meta; +using Core.Logging; +using System.Collections.Generic; +using Core.Exceptions; +using System.Text.Json.Serialization; + + +public record AuxiliaryFile(string Name, byte[] Content) { } +public record GeneratedFile(string Name, string Content, string Generator, AuxiliaryFile? AuxiliaryFile) { } +public record CompilerOutput(List Warnings, List Errors, GeneratedFile[]? Results) { } \ No newline at end of file diff --git a/Core/Meta/Type.cs b/Core/Meta/Type.cs index 2168b5be..23bd67b6 100644 --- a/Core/Meta/Type.cs +++ b/Core/Meta/Type.cs @@ -169,7 +169,7 @@ public static int MinimalEncodedSize(this TypeBase type, BebopSchema schema) { ArrayType or MapType => 4, ScalarType st => st.BaseType.Size(), - DefinedType dt when schema.Definitions[dt.Name] is EnumDefinition => 4, + DefinedType dt when schema.Definitions[dt.Name] is EnumDefinition ed => ed.BaseType.Size(), DefinedType dt when schema.Definitions[dt.Name] is RecordDefinition td => td.MinimalEncodedSize(schema), _ => throw new ArgumentOutOfRangeException(type.ToString()) }; diff --git a/Core/Meta/WellKnownTypes.cs b/Core/Meta/WellKnownTypes.cs index 3c3d1e29..c0ebfc55 100644 --- a/Core/Meta/WellKnownTypes.cs +++ b/Core/Meta/WellKnownTypes.cs @@ -1,8 +1,11 @@ using System; using System.Collections.Generic; +using System.Globalization; using System.Numerics; using System.Reflection; +using Core.Exceptions; using Core.Lexer.Extensions; +using Core.Lexer.Tokenization; namespace Core.Meta { @@ -11,7 +14,7 @@ public static class ReservedWords public const string CompilerName = "bebopc"; public const string SchemaExt = "bop"; - public const byte SchemaLangVersion = 2; + public const byte SchemaLangVersion = 3; public static HashSet Identifiers = new() { @@ -165,6 +168,55 @@ public static BigInteger MaximumInteger(BaseType type) throw new ArgumentException("MaximumInteger: non-integer type"); } } + /// + /// Given the given integral BaseType, determine if a string value can be parsed into it. + /// + /// + public static bool IsAssignableFrom(this BaseType type, string value) + { + // Helper method to check if a value is a hexadecimal number and parse it + static bool TryParseHex(string s) where T : struct + { + if (s.StartsWith("0x", StringComparison.OrdinalIgnoreCase) || + s.StartsWith("&H", StringComparison.OrdinalIgnoreCase)) + { + s = s[2..]; + if (typeof(T) == typeof(byte)) + return byte.TryParse(s, NumberStyles.HexNumber, null, out _); + if (typeof(T) == typeof(ushort)) + return ushort.TryParse(s, NumberStyles.HexNumber, null, out _); + if (typeof(T) == typeof(short)) + return short.TryParse(s, NumberStyles.HexNumber, null, out _); + if (typeof(T) == typeof(uint)) + return uint.TryParse(s, NumberStyles.HexNumber, null, out _); + if (typeof(T) == typeof(int)) + return int.TryParse(s, NumberStyles.HexNumber, null, out _); + if (typeof(T) == typeof(ulong)) + return ulong.TryParse(s, NumberStyles.HexNumber, null, out _); + if (typeof(T) == typeof(long)) + return long.TryParse(s, NumberStyles.HexNumber, null, out _); + } + return false; + } + + return type switch + { + BaseType.Bool => value == "true" || value == "false", + BaseType.Byte => byte.TryParse(value, out _) || TryParseHex(value), + BaseType.UInt16 => ushort.TryParse(value, out _) || TryParseHex(value), + BaseType.Int16 => short.TryParse(value, out _) || TryParseHex(value), + BaseType.UInt32 => uint.TryParse(value, out _) || TryParseHex(value), + BaseType.Int32 => int.TryParse(value, out _) || TryParseHex(value), + BaseType.UInt64 => ulong.TryParse(value, out _) || TryParseHex(value), + BaseType.Int64 => long.TryParse(value, out _) || TryParseHex(value), + BaseType.Float32 => float.TryParse(value, out _), + BaseType.Float64 => double.TryParse(value, out _), + BaseType.String => true, + BaseType.Guid => Guid.TryParse(value, out _), + BaseType.Date => DateTime.TryParse(value, out _), + _ => throw new ArgumentOutOfRangeException(nameof(type), type, null) + }; + } /// /// Can the given integral BaseType represent the given integer value? @@ -176,6 +228,106 @@ public static bool CanRepresent(this BaseType type, BigInteger value) { return value >= MinimumInteger(type) && value <= MaximumInteger(type); } + + /// + /// Given the given integral BaseType, determine if it is a number. + /// + /// An integral BaseType. + /// Whether the BaseType is a number. + public static bool IsNumber(this BaseType type) + { + return type switch + { + BaseType.Byte or BaseType.UInt16 or BaseType.UInt32 or BaseType.UInt64 or BaseType.Int16 or BaseType.Int32 or BaseType.Int64 or BaseType.Float32 or BaseType.Float64 => true, + _ => false + }; + } + + public static bool IsBoolean(this TokenKind kind) + { + return kind == TokenKind.True || kind == TokenKind.False; + } + + public static TokenKind ToTokenKind(this BaseType type) + { + return type switch + { + BaseType.Bool => TokenKind.True, + BaseType.Byte or BaseType.UInt16 or BaseType.UInt32 or BaseType.UInt64 or BaseType.Int16 or BaseType.Int32 or BaseType.Int64 or BaseType.Float32 or BaseType.Float64 => TokenKind.Number, + BaseType.String => TokenKind.String, + BaseType.Guid => TokenKind.String, + _ => throw new ArgumentOutOfRangeException(nameof(type), type, null) + }; + } + public static string ToTokenString(this BaseType type) + { + return type switch + { + BaseType.Bool => "bool", + BaseType.Byte => "byte", + BaseType.UInt16 => "uint16", + BaseType.Int16 => "int16", + BaseType.UInt32 => "uint32", + BaseType.Int32 => "int32", + BaseType.UInt64 => "uint64", + BaseType.Int64 => "int64", + BaseType.Float32 => "float32", + BaseType.Float64 => "float64", + BaseType.String => "string", + BaseType.Guid => "guid", + BaseType.Date => "date", + _ => throw new ArgumentOutOfRangeException(nameof(type), type, null) + }; + } + + public static string ToTokenString(this TypeBase type) + { + return type switch + { + DefinedType dt => dt.Name, + ScalarType st => st.BaseType switch + { + BaseType.Bool => "bool", + BaseType.Byte => "byte", + BaseType.UInt16 => "uint16", + BaseType.Int16 => "int16", + BaseType.UInt32 => "uint32", + BaseType.Int32 => "int32", + BaseType.UInt64 => "uint64", + BaseType.Int64 => "int64", + BaseType.Float32 => "float32", + BaseType.Float64 => "float64", + BaseType.String => "string", + BaseType.Guid => "guid", + BaseType.Date => "date", + _ => throw new CompilerException($"Invalid scalar type {st.BaseType}") + }, + ArrayType => "array", + MapType => "map", + _ => throw new CompilerException($"Invalid type {type.GetType()}") + }; + } + + public static BaseType FromTokenString(string token) + { + return token switch + { + "bool" => BaseType.Bool, + "byte" => BaseType.Byte, + "uint16" => BaseType.UInt16, + "int16" => BaseType.Int16, + "uint32" => BaseType.UInt32, + "int32" => BaseType.Int32, + "uint64" => BaseType.UInt64, + "int64" => BaseType.Int64, + "float32" => BaseType.Float32, + "float64" => BaseType.Float64, + "string" => BaseType.String, + "guid" => BaseType.Guid, + "date" => BaseType.Date, + _ => throw new ArgumentOutOfRangeException(nameof(token), token, null) + }; + } } /// diff --git a/Core/Parser/SchemaParser.cs b/Core/Parser/SchemaParser.cs index f48c7078..07f10a97 100644 --- a/Core/Parser/SchemaParser.cs +++ b/Core/Parser/SchemaParser.cs @@ -1,26 +1,26 @@ using System; using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; using System.Globalization; using System.IO; using System.Linq; using System.Numerics; +using System.Security; using System.Text; using System.Text.RegularExpressions; -using System.Threading.Tasks; using Core.Exceptions; using Core.IO; using Core.Lexer.Extensions; using Core.Lexer.Tokenization; using Core.Lexer.Tokenization.Models; using Core.Meta; -using Core.Meta.Attributes; +using Core.Meta.Decorators; using Core.Meta.Extensions; using Core.Parser.Extensions; -using FlagsAttribute = Core.Meta.Attributes.FlagsAttribute; namespace Core.Parser { - public class SchemaParser + public partial class SchemaParser { /// /// Definitions which are allowed to appear at the top level of a bebop file. @@ -32,20 +32,16 @@ public class SchemaParser private readonly HashSet _universalFollowKinds = new() { TokenKind.Enum, TokenKind.Struct, TokenKind.Message, TokenKind.Union, TokenKind.Service, TokenKind.EndOfFile }; private readonly Stack> _scopes = new(); private readonly Tokenizer _tokenizer; + private readonly CompilerHost _compilerHost; private readonly Dictionary _definitions = new(); private readonly List _imports = new(); - /// - /// Whether the RPC boilerplate has already been generated. - /// - private bool _rpcBoilerplateGenerated = false; /// /// A set of references to named types found in message/struct definitions: /// the left token is the type name, and the right token is the definition it's used in (used to report a helpful error). /// private readonly HashSet<(Token, Token)> _typeReferences = new(); private int _index; - private readonly string _nameSpace; private readonly List _errors = new(); private readonly List _warnings = new(); private List _tokens => _tokenizer.Tokens; @@ -98,22 +94,20 @@ private void CancelScope() /// Creates a new schema parser instance from some schema files on disk. /// /// The Bebop schema files that will be parsed - /// - public SchemaParser(List schemaPaths, string nameSpace) + public SchemaParser(IEnumerable schemaPaths, CompilerHost compilerHost) { _tokenizer = new Tokenizer(SchemaReader.FromSchemaPaths(schemaPaths)); - _nameSpace = nameSpace; + _compilerHost = compilerHost; } /// /// Creates a new schema parser instance and loads the schema into memory. /// /// A string representation of a schema. - /// - public SchemaParser(string textualSchema, string nameSpace) + public SchemaParser(string textualSchema, CompilerHost compilerHost) { _tokenizer = new Tokenizer(SchemaReader.FromTextualSchema(textualSchema)); - _nameSpace = nameSpace; + _compilerHost = compilerHost; } /// @@ -273,7 +267,7 @@ private string ConsumeBlockComments() /// Parse the current input files into an object. /// /// - public async Task Parse() + public BebopSchema Parse() { _index = 0; _errors.Clear(); @@ -296,7 +290,7 @@ public async Task Parse() try { - await _tokenizer.AddFile(fullPath); + _tokenizer.AddFile(fullPath); // Add the resolved path to known imports _imports.Add(fullPath); @@ -325,7 +319,6 @@ public async Task Parse() } } return new BebopSchema( - nameSpace: _nameSpace, definitions: _definitions, typeReferences: _typeReferences, parsingErrors: _errors, @@ -358,28 +351,24 @@ public async Task Parse() return ParseConstDefinition(definitionDocumentation); } - List? attributes = null; + List decorators = []; try { - BaseAttribute? attribute; - while ((attribute = EatAttribute()) is not null) + SchemaDecorator? decorator; + // for now eat all decorators with a target of all + while ((decorator = EatDecorator(DecoratorTargets.All)) is not null) { - attributes ??= new List(); - attributes.Add(attribute); + decorators.Add(decorator); } } catch (SpanException e) { - // If there's a syntax error in the attribute, we'll be skipping ahead to the next top level definition anyway. + // If there's a syntax error in the decorator, we'll be skipping ahead to the next top level definition anyway. _errors.Add(e); } var readonlySpan = CurrentToken.Span; - var isReadOnly = Eat(TokenKind.ReadOnly); - if (isReadOnly) - { - _warnings.Add(new DeprecatedFeatureWarning(readonlySpan, "the 'readonly' modifier will be removed in the next major version of Bebop; structs will be immutable by default, and the 'mut' modifier will be added to make them mutable.")); - } + var isMutable = Eat(TokenKind.Mut); ExpectAndSkip(_topLevelDefinitionKinds, _universalFollowKinds, hint: "Expecting a top-level definition."); if (CurrentToken.Kind == TokenKind.EndOfFile) { @@ -388,23 +377,25 @@ public async Task Parse() } if (Eat(TokenKind.Service)) { - if (isReadOnly) + if (isMutable) { - throw new UnexpectedTokenException(TokenKind.Service, CurrentToken, "Did not expect service definition after readonly. (Services are not allowed to be readonly)."); + throw new UnexpectedTokenException(TokenKind.Service, CurrentToken, "Did not expect service definition after 'mut' modifier. (Services are not allowed to be mutable)."); } - if (attributes is not null && attributes.Any((a) => a is OpcodeAttribute)) + if (decorators.Any((a) => a.Identifier == "opcode")) { throw new UnexpectedTokenException(TokenKind.Service, CurrentToken, "Did not expect service definition after opcode. (Services are not allowed opcodes)."); } - if (attributes is not null && attributes.Any((a) => a is FlagsAttribute)) + if (decorators.Any((a) => a.Identifier == "flags")) { throw new UnexpectedTokenException(TokenKind.Service, CurrentToken, "Did not expect service definition after flags. (Services are not allowed flags)."); } - return ParseServiceDefinition(CurrentToken, definitionDocumentation, attributes); + decorators = decorators.Select((a) => a with { Target = DecoratorTargets.Service }).ToList(); + return ParseServiceDefinition(CurrentToken, definitionDocumentation, decorators); } if (Eat(TokenKind.Union)) { - return ParseUnionDefinition(CurrentToken, definitionDocumentation, attributes); + decorators = decorators.Select((a) => a with { Target = DecoratorTargets.Union }).ToList(); + return ParseUnionDefinition(CurrentToken, definitionDocumentation, decorators); } else { @@ -415,23 +406,31 @@ _ when Eat(TokenKind.Struct) => AggregateKind.Struct, _ when Eat(TokenKind.Message) => AggregateKind.Message, _ => throw new UnexpectedTokenException(TokenKind.Message, CurrentToken) }; + var decoratorTarget = kind switch + { + AggregateKind.Enum => DecoratorTargets.Enum, + AggregateKind.Struct => DecoratorTargets.Struct, + AggregateKind.Message => DecoratorTargets.Message, + _ => throw new InvalidOperationException("invalid kind when making definition") + }; + decorators = decorators.Select((a) => a with { Target = decoratorTarget }).ToList(); ExpectAndSkip(TokenKind.Identifier, _universalFollowKinds); if (CurrentToken.Kind != TokenKind.Identifier) { // Uh oh we skipped ahead due to a missing identifier, get outta there return null; } - return ParseNonUnionDefinition(CurrentToken, kind, isReadOnly, definitionDocumentation, attributes); + return ParseNonUnionDefinition(CurrentToken, kind, isMutable, definitionDocumentation, decorators); } } - private ConstDefinition ParseConstDefinition(string definitionDocumentation) + private ConstDefinition? ParseConstDefinition(string definitionDocumentation) { var definitionStart = CurrentToken.Span; TypeBase type; - string name = ""; - Literal? value = new IntegerLiteral(new ScalarType(BaseType.UInt32, new Span(), ""), new Span(), ""); + Literal? value; + string name; try { type = ParseType(CurrentToken); @@ -442,6 +441,7 @@ private ConstDefinition ParseConstDefinition(string definitionDocumentation) catch (SpanException e) { _errors.Add(e); + return null; } ExpectAndSkip(TokenKind.Semicolon, hint: "A constant definition must end in a semicolon: const uint32 pianoKeys = 88;"); Eat(TokenKind.Semicolon); @@ -458,9 +458,8 @@ private ConstDefinition ParseConstDefinition(string definitionDocumentation) return definition; } - private readonly Regex _reFloat = new(@"^(-?inf|nan|-?\d+(\.\d*)?(e-?\d+)?)$"); - private readonly Regex _reInteger = new(@"^-?(0[xX][0-9a-fA-F]+|\d+)$"); - private readonly Regex _reGuid = new(@"^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$"); + private readonly Regex _reFloat = FloatRegex(); + private readonly Regex _reInteger = IntegerRegex(); private (Span, string) ParseNumberLiteral() { @@ -473,6 +472,35 @@ private ConstDefinition ParseConstDefinition(string definitionDocumentation) return (span, hyphen + numberToken.Lexeme); } + private bool EatEnvironmentVariable([NotNullWhen(true)] out string? envValue) + { + var startSpan = CurrentToken.Span; + if (Eat(TokenKind.Template)) + { + Expect(TokenKind.OpenBrace); + + var envVar = ExpectLexeme(TokenKind.Identifier); + Expect(TokenKind.CloseBrace); + try + { + envValue = _compilerHost.EnvironmentVariableStore.Get(envVar); + if (string.IsNullOrEmpty(envValue)) + { + _errors.Add(new EnvironmentVariableNotFoundException(Span.Combine(startSpan, CurrentToken.Span), $"String substitution failed: environment variable '{envVar}' was not found.")); + envValue = string.Empty; + } + return true; + } + catch (SecurityException) + { + _errors.Add(new EnvironmentVariableNotFoundException(Span.Combine(startSpan, CurrentToken.Span), $"String substitution failed: insufficient permissions to access environment variable '{envVar}'.")); + envValue = string.Empty; + return true; + } + } + return (envValue = null) is not null; + } + private Literal ParseLiteral(TypeBase type) { var token = CurrentToken; @@ -492,82 +520,179 @@ private Literal ParseLiteral(TypeBase type) return new FloatLiteral(st, floatSpan, floatLexeme); case ScalarType st when st.IsInteger: var (intSpan, intLexeme) = ParseNumberLiteral(); - if (!_reInteger.IsMatch(intLexeme)) throw new InvalidLiteralException(token, st); + if (!st.BaseType.IsAssignableFrom(intLexeme)) throw new InvalidLiteralException(token, st); return new IntegerLiteral(st, intSpan, intLexeme); case ScalarType st when st.BaseType == BaseType.Bool: - Expect(TokenKind.Identifier); - return token.Lexeme switch - { - "true" => new BoolLiteral(st, token.Span, true), - "false" => new BoolLiteral(st, token.Span, false), - _ => throw new InvalidLiteralException(token, st), - }; + if (Eat(TokenKind.True)) return new BoolLiteral(st, token.Span, true); + if (Eat(TokenKind.False)) return new BoolLiteral(st, token.Span, false); + throw new InvalidLiteralException(token, st); case ScalarType st when st.BaseType == BaseType.String: + // env variable substitution + if (EatEnvironmentVariable(out var envValue)) + { + return new StringLiteral(st, token.Span, envValue); + } ExpectStringLiteral(); - return new StringLiteral(st, token.Span, token.Lexeme.Replace("\r\n", "\n")); + var str = token.Lexeme.Replace("\r\n", "\n"); + // the string has possible template variables + if (str.Contains('$')) + { + str = _compilerHost.EnvironmentVariableStore.Replace(str, _errors, token.Span); + } + return new StringLiteral(st, token.Span, str); case ScalarType st when st.BaseType == BaseType.Guid: ExpectStringLiteral(); - if (!_reGuid.IsMatch(token.Lexeme)) throw new InvalidLiteralException(token, st); - return new GuidLiteral(st, token.Span, Guid.Parse(token.Lexeme)); + if (!Guid.TryParse(token.Lexeme, out var guid)) throw new InvalidLiteralException(token, st); + return new GuidLiteral(st, token.Span, guid); default: throw new UnsupportedConstTypeException($"Constant definitions for type {type.AsString} are not supported.", type.Span); } } /// - /// Consumes all the tokens belonging to an attribute + /// Consumes all the tokens belonging to a decorator /// - /// An instance of the attribute. - private BaseAttribute? EatAttribute() + /// An instance of the decorator. + private SchemaDecorator? EatDecorator(DecoratorTargets decoratorTargets) { - if (Eat(TokenKind.OpenBracket)) + if (Eat(TokenKind.Decorator)) { + var startSpan = CurrentToken.Span; var kindToken = CurrentToken; var kind = kindToken.Lexeme; Expect(TokenKind.Identifier); - var value = string.Empty; - var isNumber = false; - if (Eat(TokenKind.OpenParenthesis)) + + if (!_compilerHost.TryGetDecorator(kind, out var decorator)) { - value = CurrentToken.Lexeme; - if (Eat(TokenKind.String) || (kind == "opcode" && Eat(TokenKind.Number))) + throw new UnknownDecoratorException(kindToken); + } + + var arguments = new Dictionary(); + var parameters = decorator.Parameters ?? Enumerable.Empty(); + var expectedArgCount = parameters.Count(p => p.IsRequired); + var providedArguments = new HashSet(); + + if (!parameters.Any()) + { + return new SchemaDecorator(kind, decoratorTargets, Span.Combine(startSpan, CurrentToken.Span), arguments, decorator); + } + + var hasOpenParenthesis = Eat(TokenKind.OpenParenthesis); + if (expectedArgCount > 0 && !hasOpenParenthesis) + { + throw new MissingArgumentException(kind, parameters.First(p => p.IsRequired).Identifier, expectedArgCount, 0, startSpan, "Expected '(' after decorator identifier."); + } + + while (CurrentToken.Kind != TokenKind.CloseParenthesis && CurrentToken.Kind != TokenKind.EndOfFile) + { + string paramName; + bool namedArgument = false; + + if (CurrentToken.Kind == TokenKind.Identifier) { - isNumber = PeekToken(_index - 1).Kind == TokenKind.Number; + paramName = CurrentToken.Lexeme; + Eat(TokenKind.Identifier); // Eat the identifier + Eat(TokenKind.Colon); // Eat the colon + namedArgument = true; + + if (CurrentToken.Kind is TokenKind.Comma or TokenKind.CloseParenthesis or TokenKind.EndOfFile) + { + throw new MissingValueForArgumentException(kind, paramName, startSpan); + } } else { - throw new UnexpectedTokenException(TokenKind.String, CurrentToken); + if (providedArguments.Count >= parameters.Count()) + { + throw new TooManyArgumentsException(kind, expectedArgCount, providedArguments.Count, startSpan); + } + paramName = parameters.ElementAt(providedArguments.Count).Identifier; + } + + var parameter = parameters.FirstOrDefault(p => p.Identifier == paramName); + if (parameter == null) + { + throw new UnknownParameterException(kind, paramName, startSpan); + } + + // Special case: opcode decorators + if (kind == "opcode" && (CurrentToken.Kind == TokenKind.String || CurrentToken.Kind == TokenKind.Number)) + { + // Special case handling for opcode decorators + goto isAssignable; + } + + if (CurrentToken.Kind != parameter.Type.ToTokenKind() || (parameter.Type == BaseType.Bool && !CurrentToken.Kind.IsBoolean())) + { + if (!parameter.IsRequired && !namedArgument) + { + // Skip optional parameter if it's not a named argument + continue; + } + throw new InvalidArgumentTypeException(kind, parameter.Identifier, parameter.Type, CurrentToken.Kind, startSpan); + } + +isAssignable: + if (!parameter.IsValueAssignable(CurrentToken.Lexeme, out var reason)) + { + throw new InvalidArgumentValueException(kind, parameter.Identifier, reason, startSpan); + } + + arguments[parameter.Identifier] = CurrentToken.Lexeme; + providedArguments.Add(parameter.Identifier); + Eat(CurrentToken.Kind); + + // Only expect a comma if it's not the last argument or if it's a named argument + if (CurrentToken.Kind is not TokenKind.CloseParenthesis && (namedArgument || providedArguments.Count < parameters.Count())) + { + Expect(TokenKind.Comma, "Expected ',' between arguments"); } + } + + // Add default values for missing optional parameters + foreach (var param in parameters.Where(p => !providedArguments.Contains(p.Identifier))) + { + if (!param.IsRequired) + { + arguments[param.Identifier] = param.DefaultValue ?? string.Empty; + } + } + + if (hasOpenParenthesis) + { Expect(TokenKind.CloseParenthesis); } - Expect(TokenKind.CloseBracket); - return kind switch + + if (providedArguments.Count < expectedArgCount) { - "deprecated" => new DeprecatedAttribute(value), - "opcode" => new OpcodeAttribute(value, isNumber), - "flags" => new FlagsAttribute(), - _ => throw new UnknownAttributeException(kindToken), - }; + var missingParam = parameters.First(p => p.IsRequired && !providedArguments.Contains(p.Identifier)); + throw new MissingArgumentException(kind, missingParam.Identifier, expectedArgCount, providedArguments.Count, startSpan); + } + + var endSpan = CurrentToken.Span; + var combinedSpan = Span.Combine(startSpan, endSpan); + return new SchemaDecorator(kind, decoratorTargets, combinedSpan, arguments, decorator); } + return null; } + + /// /// Parses a non-union data structure and adds it to the collection /// /// The token that names the type to define. /// The the type will represents. - /// + /// /// - /// - /// /// The parsed definition. private Definition? ParseNonUnionDefinition(Token definitionToken, AggregateKind kind, - bool isReadOnly, + bool isMutable, string definitionDocumentation, - List? definitionAttributes) + List definitionDecorators) { var fields = new List(); @@ -635,19 +760,25 @@ private Literal ParseLiteral(TypeBase type) { break; } - List? fieldAttributes = null; + List fieldDecorators = []; try { - BaseAttribute? attribute; - while ((attribute = EatAttribute()) is not null) + SchemaDecorator? decorator; + var target = kind switch + { + AggregateKind.Enum => DecoratorTargets.Enum | DecoratorTargets.Field, + AggregateKind.Struct => DecoratorTargets.Struct | DecoratorTargets.Field, + AggregateKind.Message => DecoratorTargets.Message | DecoratorTargets.Field, + _ => throw new InvalidOperationException("invalid kind when making definition") + }; + while ((decorator = EatDecorator(target)) is not null) { - fieldAttributes ??= new List(); - fieldAttributes.Add(attribute); + fieldDecorators.Add(decorator); } } catch (SpanException e) { - // If there's a syntax error in the attribute, we'll be skipping ahead to the next top level definition anyway. + // If there's a syntax error in the decorator, we'll be skipping ahead to the next top level definition anyway. _errors.Add(e); } @@ -717,7 +848,7 @@ private Literal ParseLiteral(TypeBase type) Expect(TokenKind.Semicolon, hint: CurrentToken.Kind == TokenKind.OpenBracket ? "Try 'Type[] foo' instead of 'Type foo[]'." : $"Elements in {aKindName} are delimited using semicolons."); - fields.Add(new Field(fieldName, type, fieldStart.Combine(fieldEnd), fieldAttributes, value, fieldDocumentation)); + fields.Add(new Field(fieldName, type, fieldStart.Combine(fieldEnd), fieldDecorators, value, fieldDocumentation)); definitionEnd = CurrentToken.Span; } catch (SpanException e) @@ -731,23 +862,23 @@ private Literal ParseLiteral(TypeBase type) var name = definitionToken.Lexeme; var definitionSpan = definitionToken.Span.Combine(definitionEnd); - + Definition definition = kind switch { - AggregateKind.Enum => new EnumDefinition(name, definitionSpan, definitionDocumentation, fields, definitionAttributes, enumBaseType), - AggregateKind.Struct => new StructDefinition(name, definitionSpan, definitionDocumentation, definitionAttributes, fields, isReadOnly), - AggregateKind.Message => new MessageDefinition(name, definitionSpan, definitionDocumentation, definitionAttributes, fields), + AggregateKind.Enum => new EnumDefinition(name, definitionSpan, definitionDocumentation, fields, definitionDecorators, enumBaseType), + AggregateKind.Struct => new StructDefinition(name, definitionSpan, definitionDocumentation, definitionDecorators, fields, isMutable), + AggregateKind.Message => new MessageDefinition(name, definitionSpan, definitionDocumentation, definitionDecorators, fields), _ => throw new InvalidOperationException("invalid kind when making definition"), }; - if (isReadOnly && definition is not StructDefinition) + if (isMutable && definition is not StructDefinition) { _errors.Add(new InvalidReadOnlyException(definition)); } - if (definitionAttributes is not null && definitionAttributes.Any((a) => a is OpcodeAttribute) && definition is not RecordDefinition) + if (definitionDecorators is not null && definitionDecorators.Any((a) => a.Identifier == "opcode") && definition is not RecordDefinition) { - _errors.Add(new InvalidOpcodeAttributeUsageException(definition)); + _errors.Add(new InvalidOpcodeDecoratorUsageException(definition)); } if (_definitions.ContainsKey(name)) @@ -769,7 +900,7 @@ private Literal ParseLiteral(TypeBase type) /// The documentation above the service definition. /// The parsed rpc service definition. private Definition? ParseServiceDefinition(Token definitionToken, - string definitionDocumentation, List? definitionAttributes) + string definitionDocumentation, List definitionDecorators) { ExpectAndSkip(TokenKind.Identifier, new(_universalFollowKinds.Append(TokenKind.OpenBrace)), "Did you forget to specify a name for this service?"); Eat(TokenKind.Identifier); @@ -817,16 +948,15 @@ private Literal ParseLiteral(TypeBase type) try { - List? methodAttributes = null; - BaseAttribute? attribute; - while ((attribute = EatAttribute()) is not null) + List methodDecorators = []; + SchemaDecorator? decorator; + while ((decorator = EatDecorator(DecoratorTargets.Method)) is not null) { - methodAttributes ??= new List(); - methodAttributes.Add(attribute); + methodDecorators.Add(decorator); } - if (methodAttributes is not null && methodAttributes.Any((a) => a is FlagsAttribute or OpcodeAttribute)) + if (methodDecorators.Any((a) => a.Identifier is "flags" or "opcode")) { - throw new UnexpectedTokenException(TokenKind.Identifier, CurrentToken, $"Service methods are not allowed to use the flags and opcode attribute."); + throw new UnexpectedTokenException(TokenKind.Identifier, CurrentToken, $"Service methods cannot be marked with the flags and opcode decorator."); } var methodStart = CurrentToken.Span; const string hint = "A method must be defined with name, request type, and return type, such as 'myMethod(MyRequest): MyResponse;'"; @@ -885,7 +1015,7 @@ private Literal ParseLiteral(TypeBase type) return null; } definitionEnd = CurrentToken.Span; - methods.Add(new(methodId, method, documentation, methodAttributes)); + methods.Add(new(methodId, method, documentation, methodDecorators)); } catch (SpanException e) { @@ -899,7 +1029,7 @@ private Literal ParseLiteral(TypeBase type) var definitionSpan = definitionToken.Span.Combine(definitionEnd); // make the service itself - var serviceDefinition = new ServiceDefinition(serviceName, definitionSpan, definitionDocumentation, methods, definitionAttributes); + var serviceDefinition = new ServiceDefinition(serviceName, definitionSpan, definitionDocumentation, methods, definitionDecorators); CloseScope(serviceDefinition); return serviceDefinition; } @@ -910,11 +1040,11 @@ private Literal ParseLiteral(TypeBase type) /// /// The token that names the union to define. /// The documentation above the union definition. - /// The opcode attribute above the union definition, if any. + /// The decorators of the parent /// The parsed union definition. private Definition? ParseUnionDefinition(Token definitionToken, string definitionDocumentation, - List? attributes) + List decorators) { ExpectAndSkip(TokenKind.Identifier, new(_universalFollowKinds.Append(TokenKind.OpenBrace)), hint: $"Did you forget to specify a name for this union?"); Eat(TokenKind.Identifier); @@ -1015,7 +1145,7 @@ private Literal ParseLiteral(TypeBase type) branches.Add(new UnionBranch((byte)discriminator, td)); } var definitionSpan = definitionToken.Span.Combine(definitionEnd); - var unionDefinition = new UnionDefinition(name, definitionSpan, definitionDocumentation, attributes, branches); + var unionDefinition = new UnionDefinition(name, definitionSpan, definitionDocumentation, decorators, branches); CloseScope(unionDefinition); if (unionDefinition.Branches.Count == 0) { @@ -1334,5 +1464,10 @@ private StringBuilder TypeSignature(StringBuilder builder, Definition type) return builder; } + + [GeneratedRegex(@"^-?(0[xX][0-9a-fA-F]+|\d+)$")] + private static partial Regex IntegerRegex(); + [GeneratedRegex(@"^(-?inf|nan|-?\d+(\.\d*)?(e-?\d+)?)$")] + private static partial Regex FloatRegex(); } } diff --git a/Laboratory/C++/run_test.sh b/Laboratory/C++/run_test.sh index ba517d1c..7d2f75b6 100755 --- a/Laboratory/C++/run_test.sh +++ b/Laboratory/C++/run_test.sh @@ -9,7 +9,7 @@ else # Linux or Mac bebopc="dotnet run --project ../../Compiler" fi -$bebopc --cpp "gen/$1.hpp" --files "../Schemas/Valid/$1.bop" +$bebopc --include "../Schemas/Valid/$1.bop" build --generator "cpp:gen/$1.hpp" >&2 echo "Timing C++ compiler:" time g++ -std=c++17 test/$1.cpp ./a.out diff --git a/Laboratory/Dart/test.sh b/Laboratory/Dart/test.sh index e70c0552..6e8597fe 100755 --- a/Laboratory/Dart/test.sh +++ b/Laboratory/Dart/test.sh @@ -1,4 +1,4 @@ #!/usr/bin/env bash mkdir -p gen -dotnet run --project ../../Compiler --files ../Schemas/Valid/{array_of_strings,const,jazz,request}.bop --dart gen/gen.dart +dotnet run --project ../../Compiler --trace -i ../Schemas/Valid/{array_of_strings,const,jazz,request}.bop build -g dart:gen/gen.dart pub run test diff --git a/Laboratory/Integration/IntegrationTesting.csproj b/Laboratory/Integration/IntegrationTesting.csproj index d627d8b3..2abea591 100644 --- a/Laboratory/Integration/IntegrationTesting.csproj +++ b/Laboratory/Integration/IntegrationTesting.csproj @@ -3,9 +3,10 @@ Exe - 10 - net6.0 + 12 + net8.0 false + $(NoWarn);CS9193 diff --git a/Laboratory/Integration/Python/src/lib.py b/Laboratory/Integration/Python/src/lib.py index e65f2dd5..3598e9e2 100644 --- a/Laboratory/Integration/Python/src/lib.py +++ b/Laboratory/Integration/Python/src/lib.py @@ -5,6 +5,7 @@ def make_lib(): giant_steps_song = Song() giant_steps_song.title = "Giant Steps" + giant_steps_song.title = "Giant Steps" giant_steps_song.year = 1959 giant_steps_song.performers = [ Musician(name="John Coltrane", plays=Instrument.PIANO, id=UUID("ff990458-a276-4b71-b2e3-57d49470b949")) diff --git a/Laboratory/Integration/run_test.js b/Laboratory/Integration/run_test.js index 106bb7ed..e618839d 100644 --- a/Laboratory/Integration/run_test.js +++ b/Laboratory/Integration/run_test.js @@ -4,13 +4,17 @@ const aout = process.platform === "win32" ? "a.exe" : "./a.out"; const py = process.platform === "win32" ? "py" : "python3"; shell.echo("Compiling schema..."); -if (shell.exec("dotnet run --project ../../Compiler --files schema.bop --cs schema.cs --ts schema.ts --cpp schema.hpp --rust Rust/src/schema.rs --py Python/src/schema.py").code !== 0) { +if ( + shell.exec( + "dotnet run --project ../../Compiler --include schema.bop build --generator 'cs:schema.cs' --generator 'ts:schema.ts' --generator 'cpp:schema.hpp' --generator 'rust:Rust/src/schema.rs' --generator 'py:Python/src/schema.py'" + ).code !== 0 +) { shell.echo("Error: bebopc failed"); shell.exit(1); } -shell.echo("Generating encodings from each language...") -shell.exec("dotnet run encode > cs.enc"); +shell.echo("Generating encodings from each language..."); +shell.exec("dotnet run encode 1> cs.enc"); shell.exec("npx ts-node encode.ts > ts.enc"); if (shell.exec(`${cxx} --std=c++17 encode.cpp`).code !== 0) { shell.echo(`Error: ${cxx} encode.cpp failed`); @@ -18,9 +22,10 @@ if (shell.exec(`${cxx} --std=c++17 encode.cpp`).code !== 0) { } shell.exec(`${aout} > cpp.enc`); shell.rm("-f", aout); -shell.exec("cargo run --manifest-path Rust/Cargo.toml --example encode > rs.enc"); -shell.exec(`${py} Python/src/encode.py`) - +shell.exec( + "cargo run --manifest-path Rust/Cargo.toml --example encode > rs.enc" +); +shell.exec(`${py} Python/src/encode.py`); // Files can have some variance and still be equivalent because of ordering in maps and C# dates. // Perform full matrix testing because it seems like a good idea @@ -33,11 +38,15 @@ if (shell.exec(`${cxx} --std=c++17 decode.cpp`).code !== 0) { // rust first because it has the most useful errors const languages = [ - {name: "Rust", cmd: "cargo run --manifest-path Rust/Cargo.toml -q --example decode --", file: "rs.enc"}, - {name: "C#", cmd: "dotnet run decode", file: "cs.enc"}, - {name: "TypeScript", cmd: "npx ts-node decode.ts", file: "ts.enc"}, - {name: "C++", cmd: aout, file: "cpp.enc"}, - {name: "Python", cmd: `${py} Python/src/decode.py`, file: "py.enc"}, + { + name: "Rust", + cmd: "cargo run --manifest-path Rust/Cargo.toml -q --example decode --", + file: "rs.enc", + }, + { name: "C#", cmd: "dotnet run decode", file: "cs.enc" }, + { name: "TypeScript", cmd: "npx ts-node decode.ts", file: "ts.enc" }, + { name: "C++", cmd: aout, file: "cpp.enc" }, + { name: "Python", cmd: `${py} Python/src/decode.py`, file: "py.enc" }, ]; var failed = false; @@ -45,7 +54,9 @@ for (const decode of languages) { for (const encode of languages) { shell.echo(`\x1b[33m${decode.cmd} ${encode.file}\x1b[0m`); if (shell.exec(`${decode.cmd} ${encode.file}`).code !== 0) { - shell.echo(`\x1b[31mError: ${decode.name} failed to decode ${encode.name}\x1b[0m`); + shell.echo( + `\x1b[31mError: ${decode.name} failed to decode ${encode.name}\x1b[0m` + ); failed = true; } } @@ -56,5 +67,5 @@ shell.rm("-f", aout); if (failed) { shell.exit(1); } else { - shell.echo("\x1b[32mAll tests passed.\x1b[0m") + shell.echo("\x1b[32mAll tests passed.\x1b[0m"); } diff --git a/Laboratory/Integration/schema.bop b/Laboratory/Integration/schema.bop index 7f73b3b2..f249f2e4 100644 --- a/Laboratory/Integration/schema.bop +++ b/Laboratory/Integration/schema.bop @@ -6,7 +6,7 @@ enum Instrument : uint16 { Piano = 80; } -readonly struct Musician { +struct Musician { string name; Instrument plays; guid id; @@ -19,7 +19,7 @@ message Song { } union Album { - 1 -> struct StudioAlbum { + 1 -> mut struct StudioAlbum { Song[] tracks; } 2 -> message LiveAlbum { @@ -29,6 +29,6 @@ union Album { } } -struct Library { +mut struct Library { map[string, Album] albums; } diff --git a/Laboratory/Rust/functionality-testing/schemas/fixedsize.bop b/Laboratory/Rust/functionality-testing/schemas/fixedsize.bop index 3b7b92ae..658e4e2a 100644 --- a/Laboratory/Rust/functionality-testing/schemas/fixedsize.bop +++ b/Laboratory/Rust/functionality-testing/schemas/fixedsize.bop @@ -1,9 +1,9 @@ -struct FixedSizeStruct { +mut struct FixedSizeStruct { byte a; int32 b; int64 c; } -struct StructWithArrayOfFixedSizeType { +mut struct StructWithArrayOfFixedSizeType { FixedSizeStruct[] ary; } diff --git a/Laboratory/Rust/functionality-testing/schemas/flags.bop b/Laboratory/Rust/functionality-testing/schemas/flags.bop index 260c1699..07b47a8b 100644 --- a/Laboratory/Rust/functionality-testing/schemas/flags.bop +++ b/Laboratory/Rust/functionality-testing/schemas/flags.bop @@ -1,4 +1,4 @@ -[flags] +@flags enum MyFlags { Red = 0x1; Green = 0x2; diff --git a/Laboratory/Rust/functionality-testing/schemas/jazz.bop b/Laboratory/Rust/functionality-testing/schemas/jazz.bop index 32a30782..11728381 100644 --- a/Laboratory/Rust/functionality-testing/schemas/jazz.bop +++ b/Laboratory/Rust/functionality-testing/schemas/jazz.bop @@ -8,7 +8,7 @@ enum Instrument { Piano = 5; } -struct Performer { +mut struct Performer { string name; Instrument plays; } @@ -20,7 +20,7 @@ message Song { } union Album { - 1 -> struct StudioAlbum { + 1 -> mut struct StudioAlbum { Song[] tracks; } 2 -> message LiveAlbum { @@ -30,6 +30,6 @@ union Album { } } -struct Library { +mut struct Library { map[string, Album] albums; } diff --git a/Laboratory/Schemas/ShouldFail/invalid_map_keys.bop b/Laboratory/Schemas/ShouldFail/invalid_map_keys.bop index b012d4c7..15c0ce9d 100644 --- a/Laboratory/Schemas/ShouldFail/invalid_map_keys.bop +++ b/Laboratory/Schemas/ShouldFail/invalid_map_keys.bop @@ -1,3 +1,3 @@ -struct WrongMap { +mut struct WrongMap { map[bool[], bool] m1; } diff --git a/Laboratory/Schemas/ShouldFail/invalid_syntax.bop b/Laboratory/Schemas/ShouldFail/invalid_syntax.bop index a8b68921..9d388001 100644 --- a/Laboratory/Schemas/ShouldFail/invalid_syntax.bop +++ b/Laboratory/Schemas/ShouldFail/invalid_syntax.bop @@ -1,3 +1,3 @@ -struct Nope } +mut struct Nope } int32 x; { diff --git a/Laboratory/Schemas/ShouldFail/invalid_union_reference.bop b/Laboratory/Schemas/ShouldFail/invalid_union_reference.bop index d42ecd6b..d8ff2a30 100644 --- a/Laboratory/Schemas/ShouldFail/invalid_union_reference.bop +++ b/Laboratory/Schemas/ShouldFail/invalid_union_reference.bop @@ -1,9 +1,9 @@ union Whattheheck { - 1 -> struct Sxxx { bool xxxx; } - 2 -> struct Syyy { bool yy; } + 1 -> mut struct Sxxx { bool xxxx; } + 2 -> mut struct Syyy { bool yy; } } -struct O { +mut struct O { Sxxx y; } diff --git a/Laboratory/Schemas/ShouldFail/multiple_errors.bop b/Laboratory/Schemas/ShouldFail/multiple_errors.bop index 3d1cd2ef..8c9638fc 100644 --- a/Laboratory/Schemas/ShouldFail/multiple_errors.bop +++ b/Laboratory/Schemas/ShouldFail/multiple_errors.bop @@ -1,6 +1,6 @@ union Whattheheck { - f - struct Sxxxx { bool xxxx; } - 2 --> struct Syyyx { bool yy; } + f - mut struct Sxxxx { bool xxxx; } + 2 --> mut struct Syyyx { bool yy; } } message BadFields { @@ -9,7 +9,7 @@ message BadFields { 3 -> wtf z; } -struct NastyStruct { +mut struct NastyStruct { 1 -> int24 x; } @@ -17,7 +17,7 @@ mesage Nah { } -struct Wrong { +mut struct Wrong { map[bool[], bool] m1; } @@ -34,20 +34,20 @@ union Z { /** * This branch is, too! */ - 2 -> struct B { bool c; } - 1 -> struct C { } + 2 -> mut struct B { bool c; } + 1 -> mut struct C { } /* You can't do this, nested unions are not allowed. 4 -> union W { - 1 -> struct D { string e; } - 2 -> struct X { bool z; } + 1 -> mut struct D { string e; } + 2 -> mut struct X { bool z; } } */ } union U { - 1 -> struct S { int32 x; } + 1 -> mut struct S { int32 x; } } -struct Ox { +mut struct Ox { S y; } diff --git a/Laboratory/Schemas/ShouldFail/nested_union.bop b/Laboratory/Schemas/ShouldFail/nested_union.bop index ae377960..3bcb7ed8 100644 --- a/Laboratory/Schemas/ShouldFail/nested_union.bop +++ b/Laboratory/Schemas/ShouldFail/nested_union.bop @@ -1,5 +1,5 @@ union A { 1 -> union B { - 1 -> struct Cx { int32 x; } + 1 -> mut struct Cx { int32 x; } } } diff --git a/Laboratory/Schemas/ShouldFail/reserved.bop b/Laboratory/Schemas/ShouldFail/reserved.bop index 2db823e2..f244e916 100644 --- a/Laboratory/Schemas/ShouldFail/reserved.bop +++ b/Laboratory/Schemas/ShouldFail/reserved.bop @@ -1,4 +1,4 @@ -struct BebopRecord { +mut struct BebopRecord { string prototype; string constructor; string __proto__; diff --git a/Laboratory/Schemas/Valid/album.bop b/Laboratory/Schemas/Valid/album.bop index 84e97551..78e01cdd 100644 --- a/Laboratory/Schemas/Valid/album.bop +++ b/Laboratory/Schemas/Valid/album.bop @@ -1,7 +1,7 @@ import "jazz.bop" union Album { - 1 -> struct StudioAlbum { + 1 -> mut struct StudioAlbum { Song[] tracks; } 2 -> message LiveAlbum { diff --git a/Laboratory/Schemas/Valid/array_of_strings.bop b/Laboratory/Schemas/Valid/array_of_strings.bop index 55431ac9..f4e53382 100644 --- a/Laboratory/Schemas/Valid/array_of_strings.bop +++ b/Laboratory/Schemas/Valid/array_of_strings.bop @@ -1,3 +1,3 @@ -struct ArrayOfStrings { +mut struct ArrayOfStrings { string[] strings; } diff --git a/Laboratory/Schemas/Valid/basic_arrays.bop b/Laboratory/Schemas/Valid/basic_arrays.bop index fb43c3d1..b75c4a7a 100644 --- a/Laboratory/Schemas/Valid/basic_arrays.bop +++ b/Laboratory/Schemas/Valid/basic_arrays.bop @@ -1,4 +1,4 @@ -struct BasicArrays { +mut struct BasicArrays { bool[] a_bool; byte[] a_byte; int16[] a_int16; @@ -13,4 +13,4 @@ struct BasicArrays { guid[] a_guid; } -struct TestInt32Array { int32[] a; } +mut struct TestInt32Array { int32[] a; } diff --git a/Laboratory/Schemas/Valid/basic_types.bop b/Laboratory/Schemas/Valid/basic_types.bop index b40a7855..7664cb2b 100644 --- a/Laboratory/Schemas/Valid/basic_types.bop +++ b/Laboratory/Schemas/Valid/basic_types.bop @@ -1,4 +1,4 @@ -struct BasicTypes { +mut struct BasicTypes { bool a_bool; byte a_byte; int16 a_int16; diff --git a/Laboratory/Schemas/Valid/bitflags.bop b/Laboratory/Schemas/Valid/bitflags.bop index 143397a3..3e8ac5f2 100644 --- a/Laboratory/Schemas/Valid/bitflags.bop +++ b/Laboratory/Schemas/Valid/bitflags.bop @@ -1,4 +1,4 @@ -[flags] +@flags enum TestFlags : int32 { None = 0; Read = 0x0001; diff --git a/Laboratory/Schemas/Valid/documentation.bop b/Laboratory/Schemas/Valid/documentation.bop index 752c558d..0c3b9704 100644 --- a/Laboratory/Schemas/Valid/documentation.bop +++ b/Laboratory/Schemas/Valid/documentation.bop @@ -1,10 +1,10 @@ enum DepE { - [deprecated("X in DepE")] + @deprecated("X in DepE") X = 1; } message DepM { - [deprecated("x in DepM")] + @deprecated("x in DepM") 1 -> int32 x; } @@ -13,11 +13,11 @@ enum DocE { /* Documented constant */ X = 1; - [deprecated("Y in DocE")] + @deprecated("Y in DocE") Y = 2; /* Deprecated, documented constant */ - [deprecated("Z in DocE")] + @deprecated("Z in DocE") Z = 3; } @@ -26,16 +26,16 @@ message DocM { /* Documented field */ 1 -> int32 x; - [deprecated("y in DocM")] + @deprecated("y in DocM") 2 -> int32 y; /* Deprecated, documented field */ - [deprecated("z in DocM")] + @deprecated("z in DocM") 3 -> int32 z; } /* Documented struct */ -struct DocS { +mut struct DocS { /* Documented field */ int32 x; } diff --git a/Laboratory/Schemas/Valid/enum_size.bop b/Laboratory/Schemas/Valid/enum_size.bop index 5082d536..a13264a0 100644 --- a/Laboratory/Schemas/Valid/enum_size.bop +++ b/Laboratory/Schemas/Valid/enum_size.bop @@ -9,7 +9,7 @@ enum HugeEnum : int64 { MaxInt = 0x7FFFFFFFFFFFFFFF; } -struct SmallAndHuge { +mut struct SmallAndHuge { SmallEnum small; HugeEnum huge; } diff --git a/Laboratory/Schemas/Valid/imports.bop b/Laboratory/Schemas/Valid/imports.bop index 499bad36..c7acee14 100644 --- a/Laboratory/Schemas/Valid/imports.bop +++ b/Laboratory/Schemas/Valid/imports.bop @@ -3,5 +3,5 @@ import "documentation.bop" import "./toposort.bop" import "../Valid/basic_arrays.bop" -struct Band { Musician[] members; } -struct Something { DocE imported; } +mut struct Band { Musician[] members; } +mut struct Something { DocE imported; } diff --git a/Laboratory/Schemas/Valid/jazz.bop b/Laboratory/Schemas/Valid/jazz.bop index cadda963..e6945275 100644 --- a/Laboratory/Schemas/Valid/jazz.bop +++ b/Laboratory/Schemas/Valid/jazz.bop @@ -5,8 +5,8 @@ enum Instrument { } /* test */ -[opcode("JAZZ")] -readonly struct Musician { +@opcode("JAZZ") +struct Musician { /* a name */ string name; /* an instrument */ @@ -19,6 +19,6 @@ message Song { 3 -> Musician[] performers; } -struct Library { +mut struct Library { map[guid, Song] songs; } diff --git a/Laboratory/Schemas/Valid/lab.bop b/Laboratory/Schemas/Valid/lab.bop index 03f0565a..39702541 100644 --- a/Laboratory/Schemas/Valid/lab.bop +++ b/Laboratory/Schemas/Valid/lab.bop @@ -1,12 +1,12 @@ -struct Int32s { int32[] a; } -struct Uint32s { uint32[] a; } -struct Float32s { float32[] a; } -struct Int64s { int64[] a; } -struct Uint64s { uint64[] a; } -struct Float64s { float64[] a; } +mut struct Int32s { int32[] a; } +mut struct Uint32s { uint32[] a; } +mut struct Float32s { float32[] a; } +mut struct Int64s { int64[] a; } +mut struct Uint64s { uint64[] a; } +mut struct Float64s { float64[] a; } enum VideoCodec { H264=0; H265=1; } -struct VideoData { float64 time; uint32 width; uint32 height; byte[] fragment; } +mut struct VideoData { float64 time; uint32 width; uint32 height; byte[] fragment; } message MediaMessage { 1 -> VideoCodec codec; 2 -> VideoData data; } // Should be able to decode a "SkipTestNewContainer" as a "SkipTestOldContainer". diff --git a/Laboratory/Schemas/Valid/map_types.bop b/Laboratory/Schemas/Valid/map_types.bop index 9ccf52b1..cd73cf5d 100644 --- a/Laboratory/Schemas/Valid/map_types.bop +++ b/Laboratory/Schemas/Valid/map_types.bop @@ -1,7 +1,7 @@ message M { 1 -> float32 a; 2 -> float64 b; } -readonly struct S { int32 x; int32 y; } +struct S { int32 x; int32 y; } -struct SomeMaps { +mut struct SomeMaps { map[bool, bool] m1; map[string, map[string, string]] m2; map[int32, map[bool, S][]][] m3; diff --git a/Laboratory/Schemas/Valid/msgpack_comparison.bop b/Laboratory/Schemas/Valid/msgpack_comparison.bop index 19b932dc..edfc7eac 100644 --- a/Laboratory/Schemas/Valid/msgpack_comparison.bop +++ b/Laboratory/Schemas/Valid/msgpack_comparison.bop @@ -2,7 +2,7 @@ // These field names are weird, because I wanted the // key names in JSON to be the same length while not coinciding with Bebop keywords. -struct MsgpackComparison { +mut struct MsgpackComparison { uint8 ant0; // "int0": 0, uint8 ant1; // "int1": 1, int16 ant1x; // "int1-": -1, diff --git a/Laboratory/Schemas/Valid/nested_message.bop b/Laboratory/Schemas/Valid/nested_message.bop index b67cbf15..3eea121b 100644 --- a/Laboratory/Schemas/Valid/nested_message.bop +++ b/Laboratory/Schemas/Valid/nested_message.bop @@ -1,12 +1,12 @@ message InnerM { 1 -> int32 x; } -struct InnerS { bool y; } +mut struct InnerS { bool y; } message OuterM { 1 -> InnerM innerM; 2 -> InnerS innerS; } -struct OuterS { +mut struct OuterS { InnerM innerM; InnerS innerS; } diff --git a/Laboratory/Schemas/Valid/request.bop b/Laboratory/Schemas/Valid/request.bop index 7ec337f6..c82f30cb 100644 --- a/Laboratory/Schemas/Valid/request.bop +++ b/Laboratory/Schemas/Valid/request.bop @@ -4,20 +4,20 @@ enum FurnitureFamily { Shoe = 2; } -readonly struct Furniture { +struct Furniture { string name; uint32 price; FurnitureFamily family; } -[opcode("IKEA")] +@opcode("IKEA") message RequestCatalog { 1 -> FurnitureFamily family; - [deprecated("Nobody react to what I'm about to say...")] + @deprecated("Nobody react to what I'm about to say...") 2 -> string secretTunnel; } -[opcode(0x31323334)] -readonly struct RequestResponse { +@opcode(0x31323334) +struct RequestResponse { Furniture[] availableFurniture; } diff --git a/Laboratory/Schemas/Valid/toposort.bop b/Laboratory/Schemas/Valid/toposort.bop index 167aa6f3..e670ac6c 100644 --- a/Laboratory/Schemas/Valid/toposort.bop +++ b/Laboratory/Schemas/Valid/toposort.bop @@ -1,10 +1,10 @@ -struct Toposort7 { Toposort6 x; } -struct Toposort2 { Toposort1 x; } -struct Toposort9 { Toposort8 x; } -struct Toposort0 { int32 x; } -struct Toposort5 { Toposort4 x; } -struct Toposort1 { Toposort0 x; } -struct Toposort6 { Toposort5 x; } -struct Toposort4 { Toposort3 x; } -struct Toposort3 { Toposort2 x; } -struct Toposort8 { Toposort7 x; } +mut struct Toposort7 { Toposort6 x; } +mut struct Toposort2 { Toposort1 x; } +mut struct Toposort9 { Toposort8 x; } +mut struct Toposort0 { int32 x; } +mut struct Toposort5 { Toposort4 x; } +mut struct Toposort1 { Toposort0 x; } +mut struct Toposort6 { Toposort5 x; } +mut struct Toposort4 { Toposort3 x; } +mut struct Toposort3 { Toposort2 x; } +mut struct Toposort8 { Toposort7 x; } diff --git a/Laboratory/Schemas/Valid/union.bop b/Laboratory/Schemas/Valid/union.bop index dd47fd55..d5eac239 100644 --- a/Laboratory/Schemas/Valid/union.bop +++ b/Laboratory/Schemas/Valid/union.bop @@ -3,26 +3,26 @@ message InnerM2 { 1 -> int32 x; } /** * This union is so documented! */ -[opcode("yeah")] +@opcode("yeah") union U { 1 -> message A { 1 -> uint32 b; } /** * This branch is, too! */ - 2 -> struct B { bool c; } - 3 -> struct C { } - 4 -> struct D { InnerM2 msg; } + 2 -> mut struct B { bool c; } + 3 -> mut struct C { } + 4 -> mut struct D { InnerM2 msg; } /* You can't do this, nested unions are not allowed. 5 -> union W { - 1 -> struct D { string e; } - 2 -> struct X { bool z; } + 1 -> mut struct D { string e; } + 2 -> mut struct X { bool z; } } */ } union WeirdOrder { - 2 -> struct TwoComesFirst { byte b; } - 4 -> struct ThreeIsSkipped {} - 1 -> struct OneComesLast {} + 2 -> mut struct TwoComesFirst { byte b; } + 4 -> mut struct ThreeIsSkipped {} + 1 -> mut struct OneComesLast {} } diff --git a/Laboratory/Schemas/Valid/union_perf_a.bop b/Laboratory/Schemas/Valid/union_perf_a.bop index 17a0428e..ab9f96b7 100644 --- a/Laboratory/Schemas/Valid/union_perf_a.bop +++ b/Laboratory/Schemas/Valid/union_perf_a.bop @@ -2,59 +2,59 @@ * Option A: put the whole thing in a union. */ union UnionPerfU { - 1 -> struct A1 { int32 i1; uint64 u; float64 f; string s; guid g; bool b; } - 2 -> struct A2 { int32 i2; uint64 u; float64 f; string s; guid g; bool b; } - 3 -> struct A3 { int32 i3; uint64 u; float64 f; string s; guid g; bool b; } - 4 -> struct A4 { int32 i4; uint64 u; float64 f; string s; guid g; bool b; } - 5 -> struct A5 { int32 i5; uint64 u; float64 f; string s; guid g; bool b; } - 6 -> struct A6 { int32 i6; uint64 u; float64 f; string s; guid g; bool b; } - 7 -> struct A7 { int32 i7; uint64 u; float64 f; string s; guid g; bool b; } - 8 -> struct A8 { int32 i8; uint64 u; float64 f; string s; guid g; bool b; } - 9 -> struct A9 { int32 i9; uint64 u; float64 f; string s; guid g; bool b; } - 10 -> struct A10 { int32 i10; uint64 u; float64 f; string s; guid g; bool b; } - 11 -> struct A11 { int32 i11; uint64 u; float64 f; string s; guid g; bool b; } - 12 -> struct A12 { int32 i12; uint64 u; float64 f; string s; guid g; bool b; } - 13 -> struct A13 { int32 i13; uint64 u; float64 f; string s; guid g; bool b; } - 14 -> struct A14 { int32 i14; uint64 u; float64 f; string s; guid g; bool b; } - 15 -> struct A15 { int32 i15; uint64 u; float64 f; string s; guid g; bool b; } - 16 -> struct A16 { int32 i16; uint64 u; float64 f; string s; guid g; bool b; } - 17 -> struct A17 { int32 i17; uint64 u; float64 f; string s; guid g; bool b; } - 18 -> struct A18 { int32 i18; uint64 u; float64 f; string s; guid g; bool b; } - 19 -> struct A19 { int32 i19; uint64 u; float64 f; string s; guid g; bool b; } - 20 -> struct A20 { int32 i20; uint64 u; float64 f; string s; guid g; bool b; } - 21 -> struct A21 { int32 i21; uint64 u; float64 f; string s; guid g; bool b; } - 22 -> struct A22 { int32 i22; uint64 u; float64 f; string s; guid g; bool b; } - 23 -> struct A23 { int32 i23; uint64 u; float64 f; string s; guid g; bool b; } - 24 -> struct A24 { int32 i24; uint64 u; float64 f; string s; guid g; bool b; } - 25 -> struct A25 { int32 i25; uint64 u; float64 f; string s; guid g; bool b; } - 26 -> struct A26 { int32 i26; uint64 u; float64 f; string s; guid g; bool b; } - 27 -> struct A27 { int32 i27; uint64 u; float64 f; string s; guid g; bool b; } - 28 -> struct A28 { int32 i28; uint64 u; float64 f; string s; guid g; bool b; } - 29 -> struct A29 { int32 i29; uint64 u; float64 f; string s; guid g; bool b; } - 30 -> struct A30 { int32 i30; uint64 u; float64 f; string s; guid g; bool b; } - 31 -> struct A31 { int32 i31; uint64 u; float64 f; string s; guid g; bool b; } - 32 -> struct A32 { int32 i32; uint64 u; float64 f; string s; guid g; bool b; } - 33 -> struct A33 { int32 i33; uint64 u; float64 f; string s; guid g; bool b; } - 34 -> struct A34 { int32 i34; uint64 u; float64 f; string s; guid g; bool b; } - 35 -> struct A35 { int32 i35; uint64 u; float64 f; string s; guid g; bool b; } - 36 -> struct A36 { int32 i36; uint64 u; float64 f; string s; guid g; bool b; } - 37 -> struct A37 { int32 i37; uint64 u; float64 f; string s; guid g; bool b; } - 38 -> struct A38 { int32 i38; uint64 u; float64 f; string s; guid g; bool b; } - 39 -> struct A39 { int32 i39; uint64 u; float64 f; string s; guid g; bool b; } - 40 -> struct A40 { int32 i40; uint64 u; float64 f; string s; guid g; bool b; } - 41 -> struct A41 { int32 i41; uint64 u; float64 f; string s; guid g; bool b; } - 42 -> struct A42 { int32 i42; uint64 u; float64 f; string s; guid g; bool b; } - 43 -> struct A43 { int32 i43; uint64 u; float64 f; string s; guid g; bool b; } - 44 -> struct A44 { int32 i44; uint64 u; float64 f; string s; guid g; bool b; } - 45 -> struct A45 { int32 i45; uint64 u; float64 f; string s; guid g; bool b; } - 46 -> struct A46 { int32 i46; uint64 u; float64 f; string s; guid g; bool b; } - 47 -> struct A47 { int32 i47; uint64 u; float64 f; string s; guid g; bool b; } - 48 -> struct A48 { int32 i48; uint64 u; float64 f; string s; guid g; bool b; } - 49 -> struct A49 { int32 i49; uint64 u; float64 f; string s; guid g; bool b; } - 50 -> struct A50 { int32 i50; uint64 u; float64 f; string s; guid g; bool b; } + 1 -> mut struct A1 { int32 i1; uint64 u; float64 f; string s; guid g; bool b; } + 2 -> mut struct A2 { int32 i2; uint64 u; float64 f; string s; guid g; bool b; } + 3 -> mut struct A3 { int32 i3; uint64 u; float64 f; string s; guid g; bool b; } + 4 -> mut struct A4 { int32 i4; uint64 u; float64 f; string s; guid g; bool b; } + 5 -> mut struct A5 { int32 i5; uint64 u; float64 f; string s; guid g; bool b; } + 6 -> mut struct A6 { int32 i6; uint64 u; float64 f; string s; guid g; bool b; } + 7 -> mut struct A7 { int32 i7; uint64 u; float64 f; string s; guid g; bool b; } + 8 -> mut struct A8 { int32 i8; uint64 u; float64 f; string s; guid g; bool b; } + 9 -> mut struct A9 { int32 i9; uint64 u; float64 f; string s; guid g; bool b; } + 10 -> mut struct A10 { int32 i10; uint64 u; float64 f; string s; guid g; bool b; } + 11 -> mut struct A11 { int32 i11; uint64 u; float64 f; string s; guid g; bool b; } + 12 -> mut struct A12 { int32 i12; uint64 u; float64 f; string s; guid g; bool b; } + 13 -> mut struct A13 { int32 i13; uint64 u; float64 f; string s; guid g; bool b; } + 14 -> mut struct A14 { int32 i14; uint64 u; float64 f; string s; guid g; bool b; } + 15 -> mut struct A15 { int32 i15; uint64 u; float64 f; string s; guid g; bool b; } + 16 -> mut struct A16 { int32 i16; uint64 u; float64 f; string s; guid g; bool b; } + 17 -> mut struct A17 { int32 i17; uint64 u; float64 f; string s; guid g; bool b; } + 18 -> mut struct A18 { int32 i18; uint64 u; float64 f; string s; guid g; bool b; } + 19 -> mut struct A19 { int32 i19; uint64 u; float64 f; string s; guid g; bool b; } + 20 -> mut struct A20 { int32 i20; uint64 u; float64 f; string s; guid g; bool b; } + 21 -> mut struct A21 { int32 i21; uint64 u; float64 f; string s; guid g; bool b; } + 22 -> mut struct A22 { int32 i22; uint64 u; float64 f; string s; guid g; bool b; } + 23 -> mut struct A23 { int32 i23; uint64 u; float64 f; string s; guid g; bool b; } + 24 -> mut struct A24 { int32 i24; uint64 u; float64 f; string s; guid g; bool b; } + 25 -> mut struct A25 { int32 i25; uint64 u; float64 f; string s; guid g; bool b; } + 26 -> mut struct A26 { int32 i26; uint64 u; float64 f; string s; guid g; bool b; } + 27 -> mut struct A27 { int32 i27; uint64 u; float64 f; string s; guid g; bool b; } + 28 -> mut struct A28 { int32 i28; uint64 u; float64 f; string s; guid g; bool b; } + 29 -> mut struct A29 { int32 i29; uint64 u; float64 f; string s; guid g; bool b; } + 30 -> mut struct A30 { int32 i30; uint64 u; float64 f; string s; guid g; bool b; } + 31 -> mut struct A31 { int32 i31; uint64 u; float64 f; string s; guid g; bool b; } + 32 -> mut struct A32 { int32 i32; uint64 u; float64 f; string s; guid g; bool b; } + 33 -> mut struct A33 { int32 i33; uint64 u; float64 f; string s; guid g; bool b; } + 34 -> mut struct A34 { int32 i34; uint64 u; float64 f; string s; guid g; bool b; } + 35 -> mut struct A35 { int32 i35; uint64 u; float64 f; string s; guid g; bool b; } + 36 -> mut struct A36 { int32 i36; uint64 u; float64 f; string s; guid g; bool b; } + 37 -> mut struct A37 { int32 i37; uint64 u; float64 f; string s; guid g; bool b; } + 38 -> mut struct A38 { int32 i38; uint64 u; float64 f; string s; guid g; bool b; } + 39 -> mut struct A39 { int32 i39; uint64 u; float64 f; string s; guid g; bool b; } + 40 -> mut struct A40 { int32 i40; uint64 u; float64 f; string s; guid g; bool b; } + 41 -> mut struct A41 { int32 i41; uint64 u; float64 f; string s; guid g; bool b; } + 42 -> mut struct A42 { int32 i42; uint64 u; float64 f; string s; guid g; bool b; } + 43 -> mut struct A43 { int32 i43; uint64 u; float64 f; string s; guid g; bool b; } + 44 -> mut struct A44 { int32 i44; uint64 u; float64 f; string s; guid g; bool b; } + 45 -> mut struct A45 { int32 i45; uint64 u; float64 f; string s; guid g; bool b; } + 46 -> mut struct A46 { int32 i46; uint64 u; float64 f; string s; guid g; bool b; } + 47 -> mut struct A47 { int32 i47; uint64 u; float64 f; string s; guid g; bool b; } + 48 -> mut struct A48 { int32 i48; uint64 u; float64 f; string s; guid g; bool b; } + 49 -> mut struct A49 { int32 i49; uint64 u; float64 f; string s; guid g; bool b; } + 50 -> mut struct A50 { int32 i50; uint64 u; float64 f; string s; guid g; bool b; } } -struct UnionPerfA { +mut struct UnionPerfA { uint32 containerOpcode; uint32 protocolVersion; UnionPerfU u; diff --git a/Laboratory/Schemas/Valid/union_perf_b.bop b/Laboratory/Schemas/Valid/union_perf_b.bop index edb14a7e..28a9573a 100644 --- a/Laboratory/Schemas/Valid/union_perf_b.bop +++ b/Laboratory/Schemas/Valid/union_perf_b.bop @@ -1,59 +1,59 @@ /** * Option B: an "encodedData" field, that "decode" is called a second time on. */ -struct UnionPerfB { +mut struct UnionPerfB { uint32 protocolVersion; uint32 incomingOpcode; byte[] encodedData; } -[opcode(1)] struct B1 { int32 i1; uint64 u; float64 f; string s; guid g; bool b; } -[opcode(2)] struct B2 { int32 i2; uint64 u; float64 f; string s; guid g; bool b; } -[opcode(3)] struct B3 { int32 i3; uint64 u; float64 f; string s; guid g; bool b; } -[opcode(4)] struct B4 { int32 i4; uint64 u; float64 f; string s; guid g; bool b; } -[opcode(5)] struct B5 { int32 i5; uint64 u; float64 f; string s; guid g; bool b; } -[opcode(6)] struct B6 { int32 i6; uint64 u; float64 f; string s; guid g; bool b; } -[opcode(7)] struct B7 { int32 i7; uint64 u; float64 f; string s; guid g; bool b; } -[opcode(8)] struct B8 { int32 i8; uint64 u; float64 f; string s; guid g; bool b; } -[opcode(9)] struct B9 { int32 i9; uint64 u; float64 f; string s; guid g; bool b; } -[opcode(10)] struct B10 { int32 i10; uint64 u; float64 f; string s; guid g; bool b; } -[opcode(11)] struct B11 { int32 i11; uint64 u; float64 f; string s; guid g; bool b; } -[opcode(12)] struct B12 { int32 i12; uint64 u; float64 f; string s; guid g; bool b; } -[opcode(13)] struct B13 { int32 i13; uint64 u; float64 f; string s; guid g; bool b; } -[opcode(14)] struct B14 { int32 i14; uint64 u; float64 f; string s; guid g; bool b; } -[opcode(15)] struct B15 { int32 i15; uint64 u; float64 f; string s; guid g; bool b; } -[opcode(16)] struct B16 { int32 i16; uint64 u; float64 f; string s; guid g; bool b; } -[opcode(17)] struct B17 { int32 i17; uint64 u; float64 f; string s; guid g; bool b; } -[opcode(18)] struct B18 { int32 i18; uint64 u; float64 f; string s; guid g; bool b; } -[opcode(19)] struct B19 { int32 i19; uint64 u; float64 f; string s; guid g; bool b; } -[opcode(20)] struct B20 { int32 i20; uint64 u; float64 f; string s; guid g; bool b; } -[opcode(21)] struct B21 { int32 i21; uint64 u; float64 f; string s; guid g; bool b; } -[opcode(22)] struct B22 { int32 i22; uint64 u; float64 f; string s; guid g; bool b; } -[opcode(23)] struct B23 { int32 i23; uint64 u; float64 f; string s; guid g; bool b; } -[opcode(24)] struct B24 { int32 i24; uint64 u; float64 f; string s; guid g; bool b; } -[opcode(25)] struct B25 { int32 i25; uint64 u; float64 f; string s; guid g; bool b; } -[opcode(26)] struct B26 { int32 i26; uint64 u; float64 f; string s; guid g; bool b; } -[opcode(27)] struct B27 { int32 i27; uint64 u; float64 f; string s; guid g; bool b; } -[opcode(28)] struct B28 { int32 i28; uint64 u; float64 f; string s; guid g; bool b; } -[opcode(29)] struct B29 { int32 i29; uint64 u; float64 f; string s; guid g; bool b; } -[opcode(30)] struct B30 { int32 i30; uint64 u; float64 f; string s; guid g; bool b; } -[opcode(31)] struct B31 { int32 i31; uint64 u; float64 f; string s; guid g; bool b; } -[opcode(32)] struct B32 { int32 i32; uint64 u; float64 f; string s; guid g; bool b; } -[opcode(33)] struct B33 { int32 i33; uint64 u; float64 f; string s; guid g; bool b; } -[opcode(34)] struct B34 { int32 i34; uint64 u; float64 f; string s; guid g; bool b; } -[opcode(35)] struct B35 { int32 i35; uint64 u; float64 f; string s; guid g; bool b; } -[opcode(36)] struct B36 { int32 i36; uint64 u; float64 f; string s; guid g; bool b; } -[opcode(37)] struct B37 { int32 i37; uint64 u; float64 f; string s; guid g; bool b; } -[opcode(38)] struct B38 { int32 i38; uint64 u; float64 f; string s; guid g; bool b; } -[opcode(39)] struct B39 { int32 i39; uint64 u; float64 f; string s; guid g; bool b; } -[opcode(40)] struct B40 { int32 i40; uint64 u; float64 f; string s; guid g; bool b; } -[opcode(41)] struct B41 { int32 i41; uint64 u; float64 f; string s; guid g; bool b; } -[opcode(42)] struct B42 { int32 i42; uint64 u; float64 f; string s; guid g; bool b; } -[opcode(43)] struct B43 { int32 i43; uint64 u; float64 f; string s; guid g; bool b; } -[opcode(44)] struct B44 { int32 i44; uint64 u; float64 f; string s; guid g; bool b; } -[opcode(45)] struct B45 { int32 i45; uint64 u; float64 f; string s; guid g; bool b; } -[opcode(46)] struct B46 { int32 i46; uint64 u; float64 f; string s; guid g; bool b; } -[opcode(47)] struct B47 { int32 i47; uint64 u; float64 f; string s; guid g; bool b; } -[opcode(48)] struct B48 { int32 i48; uint64 u; float64 f; string s; guid g; bool b; } -[opcode(49)] struct B49 { int32 i49; uint64 u; float64 f; string s; guid g; bool b; } -[opcode(50)] struct B50 { int32 i50; uint64 u; float64 f; string s; guid g; bool b; } +@opcode(1) mut struct B1 { int32 i1; uint64 u; float64 f; string s; guid g; bool b; } +@opcode(2) mut struct B2 { int32 i2; uint64 u; float64 f; string s; guid g; bool b; } +@opcode(3) mut struct B3 { int32 i3; uint64 u; float64 f; string s; guid g; bool b; } +@opcode(4) mut struct B4 { int32 i4; uint64 u; float64 f; string s; guid g; bool b; } +@opcode(5) mut struct B5 { int32 i5; uint64 u; float64 f; string s; guid g; bool b; } +@opcode(6) mut struct B6 { int32 i6; uint64 u; float64 f; string s; guid g; bool b; } +@opcode(7) mut struct B7 { int32 i7; uint64 u; float64 f; string s; guid g; bool b; } +@opcode(8) mut struct B8 { int32 i8; uint64 u; float64 f; string s; guid g; bool b; } +@opcode(9) mut struct B9 { int32 i9; uint64 u; float64 f; string s; guid g; bool b; } +@opcode(10) mut struct B10 { int32 i10; uint64 u; float64 f; string s; guid g; bool b; } +@opcode(11) mut struct B11 { int32 i11; uint64 u; float64 f; string s; guid g; bool b; } +@opcode(12) mut struct B12 { int32 i12; uint64 u; float64 f; string s; guid g; bool b; } +@opcode(13) mut struct B13 { int32 i13; uint64 u; float64 f; string s; guid g; bool b; } +@opcode(14) mut struct B14 { int32 i14; uint64 u; float64 f; string s; guid g; bool b; } +@opcode(15) mut struct B15 { int32 i15; uint64 u; float64 f; string s; guid g; bool b; } +@opcode(16) mut struct B16 { int32 i16; uint64 u; float64 f; string s; guid g; bool b; } +@opcode(17) mut struct B17 { int32 i17; uint64 u; float64 f; string s; guid g; bool b; } +@opcode(18) mut struct B18 { int32 i18; uint64 u; float64 f; string s; guid g; bool b; } +@opcode(19) mut struct B19 { int32 i19; uint64 u; float64 f; string s; guid g; bool b; } +@opcode(20) mut struct B20 { int32 i20; uint64 u; float64 f; string s; guid g; bool b; } +@opcode(21) mut struct B21 { int32 i21; uint64 u; float64 f; string s; guid g; bool b; } +@opcode(22) mut struct B22 { int32 i22; uint64 u; float64 f; string s; guid g; bool b; } +@opcode(23) mut struct B23 { int32 i23; uint64 u; float64 f; string s; guid g; bool b; } +@opcode(24) mut struct B24 { int32 i24; uint64 u; float64 f; string s; guid g; bool b; } +@opcode(25) mut struct B25 { int32 i25; uint64 u; float64 f; string s; guid g; bool b; } +@opcode(26) mut struct B26 { int32 i26; uint64 u; float64 f; string s; guid g; bool b; } +@opcode(27) mut struct B27 { int32 i27; uint64 u; float64 f; string s; guid g; bool b; } +@opcode(28) mut struct B28 { int32 i28; uint64 u; float64 f; string s; guid g; bool b; } +@opcode(29) mut struct B29 { int32 i29; uint64 u; float64 f; string s; guid g; bool b; } +@opcode(30) mut struct B30 { int32 i30; uint64 u; float64 f; string s; guid g; bool b; } +@opcode(31) mut struct B31 { int32 i31; uint64 u; float64 f; string s; guid g; bool b; } +@opcode(32) mut struct B32 { int32 i32; uint64 u; float64 f; string s; guid g; bool b; } +@opcode(33) mut struct B33 { int32 i33; uint64 u; float64 f; string s; guid g; bool b; } +@opcode(34) mut struct B34 { int32 i34; uint64 u; float64 f; string s; guid g; bool b; } +@opcode(35) mut struct B35 { int32 i35; uint64 u; float64 f; string s; guid g; bool b; } +@opcode(36) mut struct B36 { int32 i36; uint64 u; float64 f; string s; guid g; bool b; } +@opcode(37) mut struct B37 { int32 i37; uint64 u; float64 f; string s; guid g; bool b; } +@opcode(38) mut struct B38 { int32 i38; uint64 u; float64 f; string s; guid g; bool b; } +@opcode(39) mut struct B39 { int32 i39; uint64 u; float64 f; string s; guid g; bool b; } +@opcode(40) mut struct B40 { int32 i40; uint64 u; float64 f; string s; guid g; bool b; } +@opcode(41) mut struct B41 { int32 i41; uint64 u; float64 f; string s; guid g; bool b; } +@opcode(42) mut struct B42 { int32 i42; uint64 u; float64 f; string s; guid g; bool b; } +@opcode(43) mut struct B43 { int32 i43; uint64 u; float64 f; string s; guid g; bool b; } +@opcode(44) mut struct B44 { int32 i44; uint64 u; float64 f; string s; guid g; bool b; } +@opcode(45) mut struct B45 { int32 i45; uint64 u; float64 f; string s; guid g; bool b; } +@opcode(46) mut struct B46 { int32 i46; uint64 u; float64 f; string s; guid g; bool b; } +@opcode(47) mut struct B47 { int32 i47; uint64 u; float64 f; string s; guid g; bool b; } +@opcode(48) mut struct B48 { int32 i48; uint64 u; float64 f; string s; guid g; bool b; } +@opcode(49) mut struct B49 { int32 i49; uint64 u; float64 f; string s; guid g; bool b; } +@opcode(50) mut struct B50 { int32 i50; uint64 u; float64 f; string s; guid g; bool b; } diff --git a/Laboratory/Schemas/bebop.json b/Laboratory/Schemas/bebop.json index 8f14e25f..6b89eda2 100644 --- a/Laboratory/Schemas/bebop.json +++ b/Laboratory/Schemas/bebop.json @@ -1,18 +1,15 @@ { - "inputDirectory": "./Valid", - "exclude": ["./ShouldFail"], - "generators": [ - { - "alias": "cs", - "outFile": "csoutput/models.cs" - }, - { - "alias": "ts", - "outFile": "tsoutput/models.ts" - }, - { - "alias": "py", - "outFile": "pyoutput/models.py" - } - ] + "include": ["./Valid/*.bop"], + "exclude": ["./ShouldFail"], + "generators": { + "cs": { + "outFile": "csoutput/models.cs" + }, + "ts": { + "outFile": "tsoutput/models.ts" + }, + "py": { + "outFile": "pyoutput/models.py" + } } +} diff --git a/Laboratory/Schemas/enum.bop b/Laboratory/Schemas/enum.bop index 55f93f37..f54c8191 100644 --- a/Laboratory/Schemas/enum.bop +++ b/Laboratory/Schemas/enum.bop @@ -1,4 +1,4 @@ -/** Use a different name to prevent conflicts with jazz.bop */ +/** Use a different name to prevent conflicts with jazz.bop */ enum OtherInstrument { Sax = 0; Trumpet = 1; @@ -9,13 +9,13 @@ enum OtherInstrument { /** Doc comment on enum */ enum letters { /** Doc comment on deprecated attr */ - [deprecated("for fun")] + @deprecated("i'm counting that money") a = 0; b = 1; /** Doc comment on attribute */ c = 2; d = 3; - [deprecated] + @deprecated("for fun") f = 4; g = 5; h = 6; diff --git a/Laboratory/Schemas/message.bop b/Laboratory/Schemas/message.bop index a9fc6ffc..cb670f01 100644 --- a/Laboratory/Schemas/message.bop +++ b/Laboratory/Schemas/message.bop @@ -1,17 +1,17 @@ -message Msg { 1 -> float32 a; 2 -> float64 b; } +message Msg { 1 -> float32 a; 2 -> float64 b; } /** other docs */ message InnerM3 { 1 -> int32 x; } -struct InnerS3 { bool y; } +mut struct InnerS3 { bool y; } -[opcode(874)] +@opcode(874) message OuterM3 { - [deprecated("reasons")] + @deprecated("reasons") 1 -> InnerM3 innerM; /** some docs */ 2 -> InnerS3 innerS; } -struct OuterS3 { +mut struct OuterS3 { InnerM3 innerM; InnerS3 innerS; } diff --git a/Laboratory/Schemas/struct.bop b/Laboratory/Schemas/struct.bop index 74819084..a9e86caf 100644 --- a/Laboratory/Schemas/struct.bop +++ b/Laboratory/Schemas/struct.bop @@ -1,4 +1,4 @@ -struct BasicTypes2 { +mut struct BasicTypes2 { bool a_bool; byte a_byte; int16 a_int16; @@ -14,7 +14,7 @@ date a_date; } -struct BasicArrays2 { +mut struct BasicArrays2 { bool[] a_bool; byte[] a_byte; int16[] a_int16; @@ -29,33 +29,33 @@ struct BasicArrays2 { guid[] a_guid; } -readonly struct S2 { int32 x; int32 y; } +struct S2 { int32 x; int32 y; } -struct SomeMaps2 { +mut struct SomeMaps2 { map[bool, bool] m1; map[string, map[string, string]] m2; map[int32, map[bool, S2][]][] m3; array[map[string, array[float32]]] m4; } -struct TestInt32Array2 { int32[] a; } +mut struct TestInt32Array2 { int32[] a; } -struct ArrayOfStrings2 { +mut struct ArrayOfStrings2 { string[] strings; } -struct StructOfStructs { +mut struct StructOfStructs { BasicTypes2 basicTypes; BasicArrays2 basicArrays; } -struct EmptyStruct {} +mut struct EmptyStruct {} -[opcode("WRLD")] -struct OpcodeStruct { +@opcode("WRLD") +mut struct OpcodeStruct { int32 x; } -struct ShouldNotHaveLifetime { +mut struct ShouldNotHaveLifetime { OpcodeStruct[] v; } diff --git a/Laboratory/Schemas/union_perf_c.bop b/Laboratory/Schemas/union_perf_c.bop index c6cff960..0f83f32a 100644 --- a/Laboratory/Schemas/union_perf_c.bop +++ b/Laboratory/Schemas/union_perf_c.bop @@ -54,7 +54,7 @@ union UnionPerfV { 50 -> message C50 { 1 -> int32 i50; 2 -> uint64 u; 3 -> float64 f; 4 -> string s; 5 -> guid g; 6 -> bool b; } } -struct UnionPerfC { +mut struct UnionPerfC { uint32 containerOpcode; uint32 protocolVersion; UnionPerfV v; diff --git a/Laboratory/TypeScript/compile-schemas.ps1 b/Laboratory/TypeScript/compile-schemas.ps1 index 455e118c..a3458489 100644 --- a/Laboratory/TypeScript/compile-schemas.ps1 +++ b/Laboratory/TypeScript/compile-schemas.ps1 @@ -1 +1 @@ -dotnet run --project ..\..\Compiler --ts "test\generated\gen.ts" --files (gci ..\Schemas\Valid\*.bop) +dotnet run --project ..\..\Compiler --include (gci ..\Schemas\Valid\*.bop) --generator "ts:test\generated\gen.ts" diff --git a/Laboratory/TypeScript/compile-schemas.sh b/Laboratory/TypeScript/compile-schemas.sh index 0afb390c..d3011b55 100755 --- a/Laboratory/TypeScript/compile-schemas.sh +++ b/Laboratory/TypeScript/compile-schemas.sh @@ -1,4 +1,3 @@ #!/bin/bash -dotnet run --project ../../Compiler --ts "test/generated/gen.ts" --files ../Schemas/Valid/*.bop - +dotnet run --project ../../Compiler --trace --include ../Schemas/Valid/*.bop build --generator "ts:test/generated/gen.ts" \ No newline at end of file diff --git a/README.md b/README.md index e68a5738..dfb17998 100644 --- a/README.md +++ b/README.md @@ -18,55 +18,30 @@ Twitter -
-
-
- - REPL demo - -
-

- Modern code generation. No bloat. -

-
-

## Intro -Bebop enables schema-based, typesafe, binary serialization and code generation. It is designed to be a good fit for client–server or distributed web apps that require a faster, more concise, and more typesafe alternative to JSON or MessagePack, while avoiding the complexity of Protocol Buffers, FlatBuffers, and similar solutions. +Bebop is an insanely fast data interchange format. Think JSON, except binary. Or think Protocol Buffers, except faster with a better DevEx. In fact, in benchmarks, Bebop is 100 TIMES faster than Protocol Buffers and 1000 TIMES faster than JSON. + +![P12CpmA](https://user-images.githubusercontent.com/1297077/235745675-fc8a18e2-361f-4b7b-b9c9-47155e511b0a.png) -If you want to get familiar with the schema language and see what the generated code looks like, try out the [web REPL](https://bebop.sh/repl/). +Bebop is designed to be a modern, developer-friendly, and high-performance alternative to existing serialization formats. It's a great fit for any application that needs to serialize data, especially in performance-critical scenarios. + +If you want to get familiar with the schema language and see what the generated code looks like, try out the [playground](https://play.bebop.sh/). ### Features -- 🧙‍♂️  Supports [Typescript](https://github.com/betwixt-labs/bebop/wiki/Getting-Started-with-TypeScript), [C#](https://github.com/betwixt-labs/bebop/wiki/Getting-Started-with-.NET), [Rust](https://github.com/betwixt-labs/bebop/wiki/Getting-Started-with-Rust), C++, and more. +- 🧙‍♂️  Supports [Typescript](https://docs.bebop.sh/guide/getting-started-typescript/), [C#](https://docs.bebop.sh/guide/getting-started-csharp/), [Rust](https://docs.bebop.sh/guide/getting-started-rust/), C++, and more. - 🐎  Snappy DX - integrate `bebopc` into your project with ease. Language support available in [VSCode](https://marketplace.visualstudio.com/items?itemName=betwixt.bebop-lang). - 🍃  Light - Bebop has zero deps and a tiny runtime footprint. Generated code is tightly optimized. -- 🌗  RPC - build efficent APIs with [Tempo](https://github.com/betwixt-labs/tempo/). +- 🌗  RPC - build efficent APIs with [Tempo](https://docs.bebop.sh/tempo/). - ☁️  Runs everywhere - browsers, serverless platforms, and on bare metal. +- 📚  Extendable - write extensions for the compiler [in any language](https://docs.bebop.sh/chords/what-are-chords/). -**👉 For more information check out the [wiki](https://github.com/betwixt-labs/bebop/wiki). 👈** - - -Bebop is insanely fast! 🚀 - - - ![P12CpmA](https://user-images.githubusercontent.com/1297077/235745675-fc8a18e2-361f-4b7b-b9c9-47155e511b0a.png) - -## Releases - -To find the latest release of the `bebopc` and its corresponding runtimes, visit the [release page](https://github.com/betwixt-labs/bebop/releases). - -## Documentation - -Bebop is documented on [this repository's wiki](https://github.com/RainwayApp/bebop/wiki). Here are some quick links to get you started: +**👉 For more information check out the [docs](https://docs.bebop.sh). 👈** -- [Writing Bops: The Bebop Schema Language](https://github.com/RainwayApp/bebop/wiki/Writing-Bops:-The-Bebop-Schema-Language) -- [Getting Started with TypeScript](https://github.com/RainwayApp/bebop/wiki/Getting-Started-with-TypeScript) -- [Getting Started with Rust](https://github.com/RainwayApp/bebop/wiki/Getting-Started-with-Rust) -- [Getting Started with .NET](https://github.com/RainwayApp/bebop/wiki/Getting-Started-with-.NET) [_See You Space Cowboy_...](https://www.youtube.com/watch?v=u1UZHXB_r6g) diff --git a/Repl/App.razor b/Repl/App.razor deleted file mode 100644 index 1c360b71..00000000 --- a/Repl/App.razor +++ /dev/null @@ -1,10 +0,0 @@ - - - - - - -

Sorry, there's nothing at this address.

-
-
-
diff --git a/Repl/CompilerOutput.cs b/Repl/CompilerOutput.cs deleted file mode 100644 index 182bc59c..00000000 --- a/Repl/CompilerOutput.cs +++ /dev/null @@ -1,24 +0,0 @@ -using System.Collections.Generic; -using Core.Exceptions; - -namespace Repl -{ - /// - /// Holds results from the Bebop compiler - /// - /// - /// Blazor doesn't support returning ValueTuples to Javascript so here we are. - /// - public class CompilerOutput - { - /// - /// Indicates whether the compiler succeeded or not. - /// - public bool IsOk { get; set; } - /// - /// The returned value from the compiler. Either a stacktrace or generated code. - /// - public string Result { get; set; } - public List? Diagonstics { get; set; } - } -} diff --git a/Repl/Pages/Index.razor b/Repl/Pages/Index.razor deleted file mode 100644 index 8ed8e0cc..00000000 --- a/Repl/Pages/Index.razor +++ /dev/null @@ -1,250 +0,0 @@ -@page "/" -@using Core.Generators -@using Core.Meta -@using Core.Parser -@inject IJSRuntime JS - - -
-
-
- -
- -
- -
-
- -
- - - - @(Core.Meta.ReservedWords.CompilerName) @(DotEnv.Generated.Environment.Version) -
-
- - - -@code { - private StandaloneCodeEditor _schemaEditor = null!; - private StandaloneCodeEditor _previewEditor = null!; - string _selectedGenerator = "ts"; - string SelectedGenerator - { - get => _selectedGenerator; - set - { - if (_selectedGenerator != value) - { - _selectedGenerator = value; - ShowOutput(); - } - } - } - string _schema = ""; - - protected override async Task OnAfterRenderAsync(bool firstRender) - { - if (firstRender) - { - if (await JS.InvokeAsync("hasExampleParam")) - { - await JS.InvokeVoidAsync("setExampleSchema"); - _schema = GetExampleSchema(); - } - } - } - - private StandaloneEditorConstructionOptions SchemaConstructionOptions(StandaloneCodeEditor editor) -{ - return new StandaloneEditorConstructionOptions - { - AutomaticLayout = true, - Theme = "vs-dark", - Language = "bebop", - Value = "struct Point { int32 x; int32 y; }", - FontLigatures = true, - FormatOnType = true, - FontFamily = "Fira Code", - FontSize = 16, - Minimap = new EditorMinimapOptions() { - Enabled = false - } - }; -} - - private StandaloneEditorConstructionOptions PreviewConstructionOptions(StandaloneCodeEditor editor) -{ - return new StandaloneEditorConstructionOptions - { - AutomaticLayout = true, - Theme = "vs-dark", - Language = "text", - Value = "Generated code will appear here...", - FontLigatures = true, - FontFamily = "Fira Code", - FontSize = 15, - Minimap = new EditorMinimapOptions() { - Enabled = false - } - }; -} - -protected override async Task OnInitializedAsync() -{ - await JS.InvokeVoidAsync("registerBebop"); -} - -} - -@functions { - [JSInvokable] - public static string GetExampleSchema() - { - const string exampleSchema = @"enum Instrument { - Sax = 0; - Trumpet = 1; - Clarinet = 2; -} - -readonly struct Musician { - string name; - Instrument plays; -} - -message Song { - 1 -> string title; - 2 -> uint16 year; - 3 -> Musician[] performers; -} - -struct Library { - map[guid, Song] songs; -}"; - return exampleSchema; - } - - [JSInvokable] - public static string GetCompilerName() - { - return ReservedWords.CompilerName; - } - - [JSInvokable] - public static string GetCompilerVersion() - { - return DotEnv.Generated.Environment.Version; - } - - [JSInvokable] - public static async Task CompileSchema(string textualSchema, string generatorAlias) - { - textualSchema = textualSchema?.Trim() ?? string.Empty; - generatorAlias = generatorAlias?.Trim() ?? string.Empty; - if (string.IsNullOrWhiteSpace(textualSchema)) - { - return new CompilerOutput - { - IsOk = false, - Result = "No schema was provided" - }; - } - if (string.IsNullOrWhiteSpace(generatorAlias)) - { - return new CompilerOutput - { - IsOk = false, - Result = "No code generator was specified" - }; - } - if (!GeneratorUtils.ImplementedGenerators.ContainsKey(generatorAlias)) - { - return new CompilerOutput - { - IsOk = false, - Result = $"The specified generator '{generatorAlias}' is not valid." - }; - } - - try - { - var parser = new SchemaParser(textualSchema, string.Empty); - var schema = await parser.Parse(); - _ = schema.Validate(); - var diagonstics = schema.Errors.Concat(schema.Warnings).ToList(); - var hasErrorDiagonstics = diagonstics.Any((d) => d.Severity is Core.Exceptions.Severity.Error); - return new CompilerOutput - { - IsOk = !hasErrorDiagonstics, - Result = hasErrorDiagonstics ? string.Empty : GeneratorUtils.ImplementedGenerators[generatorAlias](schema).Compile(null, writeGeneratedNotice: false, emitBinarySchema: true), - Diagonstics = diagonstics - }; - } - catch (Exception e) - { - return new CompilerOutput - { - IsOk = false, - Result = e.ToString() - }; - } - } - - private async Task ShowOutput() - { - - var co = await CompileSchema(_schema, SelectedGenerator); - if (co.IsOk) { - var languageId = SelectedGenerator switch { - "ts" => "typescript", - "cs" => "csharp", - "go" => "go", - "java" => "java", - "cpp" => "cpp", - "rust" => "rust", - "dart" => "dart", - "py" => "python" - }; - var model = await _previewEditor.GetModel(); - await _previewEditor.UpdateOptions(new EditorUpdateOptions - { - ReadOnly = true - }); - await Global.SetModelLanguage(model,languageId); - await _previewEditor.SetValue(co.Result); - - } - await JS.InvokeVoidAsync("disableError", _previewEditor.Id); - if(co.Diagonstics is not null) { - var diagonstics = new List(); - foreach(var err in co.Diagonstics) { - var range = new BlazorMonaco.Range(err.Span.StartLine + 1, err.Span.StartColumn + 1, err.Span.EndLine + 1, err.Span.EndColumn + 1); - diagonstics.Add(new { - severity = err.Severity is Core.Exceptions.Severity.Error ? 8 : 4, - startLineNumber = range.StartLineNumber, - startColumn = range.StartColumn, - endLineNumber = range.EndLineNumber, - endColumn = range.EndColumn, - message = err.Message, - }); - } - await JS.InvokeVoidAsync("setMarkers", _schemaEditor.Id, diagonstics.ToArray()); - } else { - await JS.InvokeVoidAsync("clearMarkers", _schemaEditor.Id); - } - } - - [JSInvokable] - private async Task OnInput(KeyboardEvent e) - { - _schema = await _schemaEditor.GetValue(); - await ShowOutput(); - } - -} diff --git a/Repl/Program.cs b/Repl/Program.cs deleted file mode 100644 index 12e62fff..00000000 --- a/Repl/Program.cs +++ /dev/null @@ -1,16 +0,0 @@ -using System.Threading.Tasks; -using Microsoft.AspNetCore.Components.WebAssembly.Hosting; - - -namespace Repl -{ - public class Program - { - public static async Task Main(string[] args) - { - var builder = WebAssemblyHostBuilder.CreateDefault(args); - builder.RootComponents.Add("#app"); - await builder.Build().RunAsync(); - } - } -} \ No newline at end of file diff --git a/Repl/Properties/launchSettings.json b/Repl/Properties/launchSettings.json deleted file mode 100644 index 7a10d80c..00000000 --- a/Repl/Properties/launchSettings.json +++ /dev/null @@ -1,30 +0,0 @@ -{ - "iisSettings": { - "windowsAuthentication": false, - "anonymousAuthentication": true, - "iisExpress": { - "applicationUrl": "http://localhost:38341", - "sslPort": 44383 - } - }, - "profiles": { - "IIS Express": { - "commandName": "IISExpress", - "launchBrowser": true, - "inspectUri": "{wsProtocol}://{url.hostname}:{url.port}/_framework/debug/ws-proxy?browser={browserInspectUri}", - "environmentVariables": { - "ASPNETCORE_ENVIRONMENT": "Development" - } - }, - "Repl": { - "commandName": "Project", - "launchBrowser": true, - "inspectUri": "{wsProtocol}://{url.hostname}:{url.port}/_framework/debug/ws-proxy?browser={browserInspectUri}", - "applicationUrl": "https://localhost:5001;http://localhost:5000", - "environmentVariables": { - "ASPNETCORE_ENVIRONMENT": "Development" - }, - "dotnetRunMessages": "true" - } - } -} \ No newline at end of file diff --git a/Repl/Repl.csproj b/Repl/Repl.csproj deleted file mode 100644 index eec091d4..00000000 --- a/Repl/Repl.csproj +++ /dev/null @@ -1,35 +0,0 @@ - - - - net7.0 - 9.0 - AnyCPU - ../bin/repl/$(Configuration) - ../bin/repl/$(Configuration)/native/ - ../bin/repl/$(Configuration)/publish - false - - - - AnyCPU - false - - - - AnyCPU - true - true - - - - - - - - - - - - - - diff --git a/Repl/Repl.csproj.DotSettings b/Repl/Repl.csproj.DotSettings deleted file mode 100644 index 6162834d..00000000 --- a/Repl/Repl.csproj.DotSettings +++ /dev/null @@ -1,2 +0,0 @@ - - CSharp90 \ No newline at end of file diff --git a/Repl/Shared/MainLayout.razor b/Repl/Shared/MainLayout.razor deleted file mode 100644 index ad874c77..00000000 --- a/Repl/Shared/MainLayout.razor +++ /dev/null @@ -1,8 +0,0 @@ -@inherits LayoutComponentBase - -
- -
- @Body -
-
diff --git a/Repl/_Imports.razor b/Repl/_Imports.razor deleted file mode 100644 index abff2263..00000000 --- a/Repl/_Imports.razor +++ /dev/null @@ -1,12 +0,0 @@ -@using System.Net.Http -@using System.Net.Http.Json -@using Microsoft.AspNetCore.Components.Forms -@using Microsoft.AspNetCore.Components.Routing -@using Microsoft.AspNetCore.Components.Web -@using Microsoft.AspNetCore.Components.Web.Virtualization -@using Microsoft.AspNetCore.Components.WebAssembly.Http -@using Microsoft.JSInterop -@using Repl -@using Repl.Shared -@using BlazorMonaco -@using BlazorMonaco.Editor \ No newline at end of file diff --git a/Repl/wwwroot/css/app.css b/Repl/wwwroot/css/app.css deleted file mode 100644 index fffc61f8..00000000 --- a/Repl/wwwroot/css/app.css +++ /dev/null @@ -1,59 +0,0 @@ -* { - box-sizing: border-box; - } - - body { - font: 14px 'Fira Code', monospace; - letter-spacing: -0.75px; - margin: 0; - background: #2F2D83; - color: #F5F4FC; - } - - .container { - max-width: 1500px; - margin: 0 auto; - display: flex; - flex-direction: column; - height: 100vh; - position: relative; /* Add this line */ - } - - .side-by-side { - flex: 1; - display: flex; - } - - .editor-wrapper { - flex: 1; - margin: 4px; - overflow: auto; - position: relative; /* Add this line */ - } - - .toolbar { - position: absolute; /* Add this line */ - top: 4px; - right: calc(100% + 4px); /* Adjust the distance as needed */ - margin: 0; - text-align: right; - z-index: 1; - } - - .my-editor-class { - font-family: 'Fira Code', monospace !important; - letter-spacing: -0.75px; - font-size: 14px; - resize: none; - padding: 0.5em; - white-space: pre-wrap; - display: block; - background: #2d2d2d; - color: #cccccc; - width: 100%; - height: 100%; - overflow-y: scroll; - margin: 0; - border: 1px solid #00000040; - } - \ No newline at end of file diff --git a/Repl/wwwroot/favicon.ico b/Repl/wwwroot/favicon.ico deleted file mode 100644 index 63e859b4..00000000 Binary files a/Repl/wwwroot/favicon.ico and /dev/null differ diff --git a/Repl/wwwroot/index.html b/Repl/wwwroot/index.html deleted file mode 100644 index 16a585e9..00000000 --- a/Repl/wwwroot/index.html +++ /dev/null @@ -1,187 +0,0 @@ - - - - - - - Bebop REPL - - - - - - - -
Loading...
- - - - - - diff --git a/Runtime/C#/Attributes/BebopRecordAttribute.cs b/Runtime/C#/Attributes/BebopRecordAttribute.cs index 637a2ec8..b4952429 100644 --- a/Runtime/C#/Attributes/BebopRecordAttribute.cs +++ b/Runtime/C#/Attributes/BebopRecordAttribute.cs @@ -13,17 +13,17 @@ public class BebopRecordAttribute : Attribute /// Creates a new attribute. ///
/// The kind of the decorated type. - /// Whether or not the type record is read-only. - public BebopRecordAttribute(BebopKind kind, bool isReadOnly = false) + /// Whether or not the type record is mutable. + public BebopRecordAttribute(BebopKind kind, bool isMutable = false) { Kind = kind; - IsReadOnly = isReadOnly; + IsMutable = isMutable; } /// /// Indicates if the decorated type is read-only. /// - public bool IsReadOnly { get; set; } + public bool IsMutable { get; set; } /// /// Indicates the kind of the decorated type. diff --git a/Runtime/C#/Bebop.csproj b/Runtime/C#/Bebop.csproj index 8bf400c3..7ea9858f 100644 --- a/Runtime/C#/Bebop.csproj +++ b/Runtime/C#/Bebop.csproj @@ -1,7 +1,7 @@  - net472;netstandard2.0;netstandard2.1;netcoreapp3.1;net5.0;net6.0 + net472;netstandard2.0;netstandard2.1;netcoreapp3.1;net5.0;net6.0;net7.0;net8.0 enable 10.0 The .NET runtime for Bebop, a schema-based binary serialization format. @@ -79,10 +79,11 @@ - + TRACE;AGGRESSIVE_OPTIMIZE + true diff --git a/Runtime/TypeScript/binary.ts b/Runtime/TypeScript/binary.ts index fcf8aab4..96d6ea96 100644 --- a/Runtime/TypeScript/binary.ts +++ b/Runtime/TypeScript/binary.ts @@ -51,41 +51,47 @@ enum WireTypeKind { Enum, } -type Attributes = { [name: string]: Attribute }; -interface Attribute { - attributeName: string; - attributeValue: string | null; - isNumber: boolean; +type Decorators = { [identifier: string]: Decorator }; +interface Decorator { + arguments?: { [identifier: string]: DecoratorArgument }; +} + +interface DecoratorArgument { + typeId: number; + value: string | number | bigint | Guid | null; } interface EnumMember { name: string; - attributes: Attributes; - value: number | BigInt | null; + decorators: Decorators; + value: number | bigint | null; } interface Field { name: string; typeId: number; fieldProperties: FieldTypes; - attributes: Attributes; - constantValue?: number | BigInt | null; + decorators: Decorators; + constantValue?: number | null; } interface Definition { index: number; name: string; kind: WireTypeKind; - attributes: Attributes; + minimalEncodeSize: number; + decorators: Decorators; } interface Enum extends Definition { baseType: WireBaseType; + isBitFlags: boolean; members: { [name: string]: EnumMember }; } interface Struct extends Definition { - isReadOnly: boolean; + isMutable: boolean; + isFixedSize: boolean; fields: { [fieldName: string]: Field }; } @@ -105,22 +111,22 @@ interface Union extends Definition { interface Service { name: string; - attributes: Attributes; + decorators: Decorators; methods: { [methodName: string]: ServiceMethod }; } interface ServiceMethod { name: string; - attributes: Attributes; + decorators: Decorators; requestTypeId: number; responseTypeId: number; methodType: WireMethodType; id: number; } -interface ParsedSchema { +interface SchemaAst { bebopVersion: number; - definedTypes: { [typeName: string]: Definition }; + definitions: { [typeName: string]: Definition }; services?: { [serviceName: string]: Service }; } @@ -182,7 +188,7 @@ export class RecordReader { throw new BebopRuntimeError(`Missing field ${field.name}`); } }); - if (definition.isReadOnly) { + if (!definition.isMutable) { Object.freeze(record); } return record; @@ -798,7 +804,7 @@ export class RecordWriter { * `BinarySchema` represents a class that allows parsing of a Bebop schema in binary form. * * This class holds the DataView representation of the binary data, its parsing position, - * and contains methods to parse each specific type of Bebop schema structure. + * and contains methods to get each specific type of Bebop schema structure. */ export class BinarySchema { private readonly view: DataView; @@ -806,7 +812,7 @@ export class BinarySchema { private pos: number; private readonly ArrayType = -14; private readonly MapType = -15; - private parsedSchema?: ParsedSchema; + private parsedSchema?: SchemaAst; private indexToDefinition: { [index: number]: Definition } = {}; private nameToDefinition: { [name: string]: Definition } = {}; public reader: RecordReader; @@ -836,7 +842,10 @@ export class BinarySchema { return target[Number(prop)]; } // If prop is the name of a method of Uint8Array, return the function - if (typeof prop === 'string' && typeof (target as any)[prop] === 'function') { + if ( + typeof prop === "string" && + typeof (target as any)[prop] === "function" + ) { return (target as any)[prop].bind(target); } // Optionally, you can throw an error or return undefined for all other properties @@ -849,42 +858,46 @@ export class BinarySchema { } /** - * Parse the schema. + * Get the schema. * This method should only be called once per instance. */ - public parse(): void { + public get(): void { if (this.parsedSchema !== undefined) { return; } - const schemaVersion = this.parseUint8(); - const numDefinedTypes = this.parseUint32(); + const schemaVersion = this.getUint8(); + const numDefinedTypes = this.getUint32(); let definedTypes: { [typeName: string]: Definition } = {}; for (let i = 0; i < numDefinedTypes; i++) { - const def = this.parseDefinedType(i); + const def = this.getDefinedType(i); definedTypes[def.name] = def; this.indexToDefinition[i] = def; this.nameToDefinition[def.name] = def; } - const serviceCount = this.parseUint32(); + const serviceCount = this.getUint32(); let services: { [serviceName: string]: Service } = {}; for (let i = 0; i < serviceCount; i++) { - const service = this.parseServiceDefinition(); + const service = this.getServiceDefinition(); services[service.name] = service; } - this.parsedSchema = { bebopVersion: schemaVersion, definedTypes, services }; + this.parsedSchema = { + bebopVersion: schemaVersion, + definitions: definedTypes, + services, + }; Object.freeze(this.parsedSchema); } /** - * Returns the parsed schema. + * Returns the getd schema. */ - public get ast(): ParsedSchema { + public get ast(): Readonly { if (this.parsedSchema === undefined) { - this.parse(); + this.get(); } return this.parsedSchema!; } @@ -912,205 +925,207 @@ export class BinarySchema { return definition; } - private parseDefinedType(index: number): Definition { - const typeName = this.parseString(); - const typeKind = this.parseUint8() as WireTypeKind; - const typeAttributes = this.parseAttributes(); - - switch (typeKind) { + private getDefinedType(index: number): Definition { + const name = this.getString(); + const kind = this.getUint8() as WireTypeKind; + const decorators = this.getDecorators(); + switch (kind) { case WireTypeKind.Enum: - return this.parseEnumDefinition( - typeName, - typeKind, - typeAttributes, - index - ); + return this.getEnumDefinition(name, kind, decorators, index); case WireTypeKind.Union: - return this.parseUnionDefinition( - typeName, - typeKind, - typeAttributes, - index - ); + return this.getUnionDefinition(name, kind, decorators, index); case WireTypeKind.Struct: - return this.parseStructDefinition( - typeName, - typeKind, - typeAttributes, - index - ); + return this.getStructDefinition(name, kind, decorators, index); case WireTypeKind.Message: - return this.parseMessageDefinition( - typeName, - typeKind, - typeAttributes, - index - ); + return this.getMessageDefinition(name, kind, decorators, index); default: - throw new BebopRuntimeError(`Unknown type kind: ${typeKind}`); + throw new BebopRuntimeError(`Unknown type kind: ${kind}`); } } - private parseAttributes() { - const numAttributes = this.parseUint8(); - const attributes: { [name: string]: Attribute } = {}; - for (let i = 0; i < numAttributes; i++) { - const attribute = this.parseAttribute(); - attributes[attribute.attributeName] = attribute; + private getDecorators() { + const decoratorCount = this.getUint8(); + const decorators: Decorators = {}; + if (decoratorCount === 0) { + return decorators; } - return attributes; + for (let i = 0; i < decoratorCount; i++) { + const identifier = this.getString(); + decorators[identifier] = this.getDecorator(); + } + return decorators; } - private parseAttribute(): Attribute { - const attributeName = this.parseString(); - const hasValue = this.parseBool(); - const attributeValue = hasValue ? this.parseString() : null; - const isNumber = this.parseBool(); - return { attributeName, attributeValue, isNumber }; + private getDecorator(): Decorator { + const argCount = this.getUint8(); + const args: { [name: string]: DecoratorArgument } = {}; + for (let i = 0; i < argCount; i++) { + const identifier = this.getString(); + const typeId = this.getTypeId(); + const argumentValue = this.getConstantValue(typeId); + args[identifier] = { + typeId, + value: argumentValue, + }; + } + return { arguments: args }; } - private parseEnumDefinition( - typeName: string, - typeKind: WireTypeKind, - typeAttributes: Attributes, + private getEnumDefinition( + name: string, + kind: WireTypeKind, + decorators: Decorators, index: number ): Enum { - const baseType = this.parseTypeId(); - const memberCount = this.parseUint8(); + const baseType = this.getTypeId(); + const isBitFlags = this.getBool(); + const minimalEncodeSize = this.getInt32(); + const memberCount = this.getUint8(); const members: { [name: string]: EnumMember } = {}; for (let i = 0; i < memberCount; i++) { - const member = this.parseEnumMember(baseType); + const member = this.getEnumMember(baseType); members[member.name] = member; } return { index, - name: typeName, - kind: typeKind, - attributes: typeAttributes, + name: name, + isBitFlags, + kind: kind, + decorators: decorators, + minimalEncodeSize, baseType, members, }; } - private parseEnumMember(baseType: number): EnumMember { - const name = this.parseString(); - const attributes = this.parseAttributes(); - const value = this.parseConstantValue(baseType); - return { name, attributes, value }; + private getEnumMember(baseType: number): EnumMember { + const name = this.getString(); + const decorators = this.getDecorators(); + const value = this.getConstantValue(baseType) as number; + return { name, decorators, value }; } - private parseUnionDefinition( - typeName: string, - typeKind: WireTypeKind, - typeAttributes: { [name: string]: Attribute }, + private getUnionDefinition( + name: string, + kind: WireTypeKind, + decorators: { [name: string]: Decorator }, index: number ): Union { - const branchCount = this.parseUint8(); + const minimalEncodeSize = this.getInt32(); + const branchCount = this.getUint8(); const branches = new Array(branchCount) .fill(null) - .map(() => this.parseUnionBranch()); + .map(() => this.getUnionBranch()); return { index, - name: typeName, - kind: typeKind, - attributes: typeAttributes, + name: name, + kind: kind, + decorators: decorators, + minimalEncodeSize, branchCount, branches, }; } - private parseUnionBranch(): UnionBranch { - const discriminator = this.parseUint8(); - const typeId = this.parseTypeId(); + private getUnionBranch(): UnionBranch { + const discriminator = this.getUint8(); + const typeId = this.getTypeId(); return { discriminator, typeId }; } - private parseStructDefinition( - typeName: string, - typeKind: WireTypeKind, - typeAttributes: { [name: string]: Attribute }, + private getStructDefinition( + name: string, + kind: WireTypeKind, + decorators: { [name: string]: Decorator }, index: number ): Struct { - const isReadOnly = this.parseBool(); - const fields = this.parseFields(typeKind); + const isMutable = this.getBool(); + const minimalEncodeSize = this.getInt32(); + const isFixedSize = this.getBool(); + const fields = this.getFields(kind); return { index, - name: typeName, - kind: typeKind, - attributes: typeAttributes, - isReadOnly, + name: name, + kind: kind, + decorators: decorators, + isMutable, + minimalEncodeSize, + isFixedSize, fields, }; } - private parseMessageDefinition( - typeName: string, - typeKind: WireTypeKind, - typeAttributes: { [name: string]: Attribute }, + private getMessageDefinition( + name: string, + kind: WireTypeKind, + decorators: { [name: string]: Decorator }, index: number ): Message { - const fields = this.parseFields(typeKind); + const minimalEncodeSize = this.getInt32(); + const fields = this.getFields(kind); return { index, - name: typeName, - kind: typeKind, - attributes: typeAttributes, + minimalEncodeSize, + name: name, + kind: kind, + decorators: decorators, fields, }; } - private parseFields(parentKind: WireTypeKind): { [name: string]: Field } { - const numFields = this.parseUint8(); + private getFields(parentKind: WireTypeKind): { [name: string]: Field } { + const numFields = this.getUint8(); const fields: { [name: string]: Field } = {}; for (let i = 0; i < numFields; i++) { - const field = this.parseField(parentKind); + const field = this.getField(parentKind); fields[field.name] = field; } return fields; } - private parseField(parentKind: WireTypeKind): Field { - const fieldName = this.parseString(); - let fieldTypeId = this.parseTypeId(); + private getField(parentKind: WireTypeKind): Field { + const fieldName = this.getString(); + let fieldTypeId = this.getTypeId(); let fieldProperties: FieldTypes; if (fieldTypeId === this.ArrayType || fieldTypeId === this.MapType) { - fieldProperties = this.parseNestedType( + fieldProperties = this.getNestedType( fieldTypeId === this.ArrayType ? "array" : "map" ); } else { fieldProperties = { type: "scalar" }; } - const attributes = this.parseAttributes(); - const constantValue = + const decorators = this.getDecorators(); + const constantValue = ( parentKind === WireTypeKind.Message - ? this.parseConstantValue(WireBaseType.Byte) - : null; + ? this.getConstantValue(WireBaseType.Byte) + : null + ) as any; return { name: fieldName, typeId: fieldTypeId, fieldProperties, - attributes, + decorators: decorators, constantValue, }; } - private parseNestedType(parentType: string): FieldTypes { + private getNestedType(parentType: string): FieldTypes { if (parentType === "array") { - const depth = this.parseUint8(); - const memberTypeId = this.parseTypeId(); + const depth = this.getUint8(); + const memberTypeId = this.getTypeId(); return { type: parentType, memberTypeId: memberTypeId, depth }; } if (parentType === "map") { - const keyTypeId = this.parseTypeId(); - const valueTypeId = this.parseTypeId(); + const keyTypeId = this.getTypeId(); + const valueTypeId = this.getTypeId(); let nestedType: FieldTypes | undefined; if (valueTypeId === this.ArrayType || valueTypeId === this.MapType) { - nestedType = this.parseNestedType( + nestedType = this.getNestedType( valueTypeId === this.ArrayType ? "array" : "map" ); } @@ -1125,48 +1140,54 @@ export class BinarySchema { throw new BebopRuntimeError("Invalid initial type"); } - private parseConstantValue(typeId: number): number | BigInt | null { + private getConstantValue( + typeId: number + ): string | number | bigint | Guid | null { switch (typeId) { case WireBaseType.Bool: - return this.parseBool() ? 1 : 0; + return this.getBool() ? 1 : 0; case WireBaseType.Byte: - return this.parseUint8(); + return this.getUint8(); case WireBaseType.UInt16: - return this.parseUint16(); + return this.getUint16(); case WireBaseType.Int16: - return this.parseInt16(); + return this.getInt16(); case WireBaseType.UInt32: - return this.parseUint32(); + return this.getUint32(); case WireBaseType.Int32: - return this.parseInt32(); + return this.getInt32(); case WireBaseType.UInt64: - return BigInt(this.parseUint64()); + return BigInt(this.getUint64()) as bigint; case WireBaseType.Int64: - return BigInt(this.parseInt64()); + return BigInt(this.getInt64()); case WireBaseType.Float32: - return this.parseFloat32(); + return this.getFloat32(); case WireBaseType.Float64: - return this.parseFloat64(); + return this.getFloat64(); + case WireBaseType.String: + return this.getString(); + case WireBaseType.Guid: + return Guid.fromBytes(this.getGuid(), 0); default: throw new BebopRuntimeError(`Unsupported constant type ID: ${typeId}`); } } - private parseServiceDefinition(): Service { - let name = this.parseString(); - let attributes = this.parseAttributes(); + private getServiceDefinition(): Service { + let name = this.getString(); + let decorators = this.getDecorators(); let methods: { [name: string]: ServiceMethod } = {}; - let methodCount = this.parseUint32(); + let methodCount = this.getUint32(); for (let i = 0; i < methodCount; i++) { - let methodName = this.parseString(); - let methodAttributes = this.parseAttributes(); - let methodType = this.parseUint8() as WireMethodType; - let requestTypeId = this.parseTypeId(); - let responseTypeId = this.parseTypeId(); - let id = this.parseUint32(); + let methodName = this.getString(); + let methodDecorators = this.getDecorators(); + let methodType = this.getUint8() as WireMethodType; + let requestTypeId = this.getTypeId(); + let responseTypeId = this.getTypeId(); + let id = this.getUint32(); methods[methodName] = { name: methodName, - attributes: methodAttributes, + decorators: methodDecorators, methodType: methodType, requestTypeId: requestTypeId, responseTypeId: responseTypeId, @@ -1175,12 +1196,12 @@ export class BinarySchema { } return { name: name, - attributes: attributes, + decorators: decorators, methods: methods, }; } - private parseString(): string { + private getString(): string { const start = this.pos; while (this.pos < this.data.length && this.data[this.pos] !== 0) { this.pos++; @@ -1193,67 +1214,73 @@ export class BinarySchema { return decoder.decode(strBytes); } - private parseUint8() { + private getUint8() { let value = this.view.getUint8(this.pos); this.pos++; return value; } - private parseUint16() { + private getUint16() { let value = this.view.getUint16(this.pos, true); this.pos += 2; return value; } - private parseInt16() { + private getInt16() { let value = this.view.getInt16(this.pos, true); this.pos += 2; return value; } - private parseUint32() { + private getUint32() { let value = this.view.getUint32(this.pos, true); this.pos += 4; return value; } - private parseInt32() { + private getInt32() { let value = this.view.getInt32(this.pos, true); this.pos += 4; return value; } - private parseUint64() { + private getUint64() { let value = this.view.getBigUint64(this.pos, true); this.pos += 8; return Number(value); } - private parseInt64() { + private getInt64() { let value = this.view.getBigInt64(this.pos, true); this.pos += 8; return Number(value); } - private parseFloat32() { + private getFloat32() { let value = this.view.getFloat32(this.pos, true); this.pos += 4; return value; } - private parseFloat64() { + private getFloat64() { let value = this.view.getFloat64(this.pos, true); this.pos += 8; return value; } - private parseBool() { - return this.parseUint8() !== 0; + private getBool() { + return this.getUint8() !== 0; } - private parseTypeId() { + private getTypeId() { let typeId = this.view.getInt32(this.pos, true); this.pos += 4; return typeId; } + + private getGuid() { + let value = this.data.subarray(this.pos, this.pos + 16); + this.pos += 16; + return value; + } } diff --git a/Runtime/TypeScript/bs.test.ts b/Runtime/TypeScript/bs.test.ts index 6a6e20fb..04de61cc 100644 --- a/Runtime/TypeScript/bs.test.ts +++ b/Runtime/TypeScript/bs.test.ts @@ -1,70 +1,96 @@ -import { BebopRuntimeError, BinarySchema, Guid } from "./index"; +import { BebopRuntimeError, BinarySchema, Guid, BebopJson } from "./index"; import { describe, expect, it } from "vitest"; const schemaData = new Uint8Array([ - 2, 4, 0, 0, 0, 83, 116, 97, 116, 117, 115, 0, 4, 0, 254, 255, 255, 255, 2, 79, - 107, 97, 121, 0, 0, 1, 68, 111, 97, 98, 108, 101, 0, 0, 2, 80, 111, 105, 110, - 116, 0, 1, 0, 1, 6, 120, 0, 250, 255, 255, 255, 0, 121, 0, 250, 255, 255, 255, - 0, 110, 97, 109, 101, 0, 245, 255, 255, 255, 0, 115, 116, 97, 116, 117, 115, - 0, 0, 0, 0, 0, 0, 110, 117, 109, 98, 101, 114, 115, 0, 242, 255, 255, 255, 0, - 250, 255, 255, 255, 0, 97, 77, 97, 112, 0, 241, 255, 255, 255, 245, 255, 255, - 255, 245, 255, 255, 255, 0, 67, 111, 111, 114, 100, 105, 110, 97, 116, 101, 0, - 2, 0, 3, 112, 111, 105, 110, 116, 0, 1, 0, 0, 0, 0, 1, 105, 100, 0, 244, 255, - 255, 255, 0, 2, 110, 101, 115, 116, 101, 100, 0, 241, 255, 255, 255, 245, 255, - 255, 255, 241, 255, 255, 255, 245, 255, 255, 255, 242, 255, 255, 255, 1, 245, - 255, 255, 255, 0, 3, 80, 111, 119, 101, 114, 0, 3, 0, 2, 1, 1, 0, 0, 0, 2, 2, - 0, 0, 0, 0, 0, 0, 0, + 3, 5, 0, 0, 0, 82, 105, 103, 104, 116, 115, 0, 4, 1, 102, 108, 97, 103, 115, + 0, 0, 254, 255, 255, 255, 1, 1, 0, 0, 0, 4, 68, 101, 102, 97, 117, 108, 116, + 0, 0, 0, 85, 115, 101, 114, 0, 0, 1, 77, 111, 100, 0, 0, 2, 65, 100, 109, 105, + 110, 0, 0, 3, 65, 112, 105, 82, 101, 113, 117, 101, 115, 116, 0, 1, 0, 0, 12, + 0, 0, 0, 0, 2, 117, 114, 105, 0, 245, 255, 255, 255, 0, 116, 114, 97, 99, 101, + 0, 249, 255, 255, 255, 0, 68, 101, 102, 97, 117, 108, 116, 82, 101, 115, 112, + 111, 110, 115, 101, 0, 1, 0, 0, 5, 0, 0, 0, 0, 2, 114, 105, 103, 104, 116, + 115, 0, 0, 0, 0, 0, 0, 117, 115, 101, 114, 110, 97, 109, 101, 0, 245, 255, + 255, 255, 0, 69, 114, 114, 111, 114, 82, 101, 115, 112, 111, 110, 115, 101, 0, + 2, 0, 5, 0, 0, 0, 3, 114, 101, 97, 115, 111, 110, 0, 245, 255, 255, 255, 0, 1, + 99, 111, 100, 101, 0, 251, 255, 255, 255, 0, 2, 105, 100, 0, 244, 255, 255, + 255, 1, 100, 101, 112, 114, 101, 99, 97, 116, 101, 100, 0, 1, 114, 101, 97, + 115, 111, 110, 0, 245, 255, 255, 255, 106, 117, 115, 116, 32, 98, 101, 99, 97, + 117, 115, 101, 0, 3, 65, 112, 105, 82, 101, 115, 112, 111, 110, 115, 101, 0, + 3, 0, 10, 0, 0, 0, 2, 1, 2, 0, 0, 0, 2, 3, 0, 0, 0, 1, 0, 0, 0, 71, 114, 101, + 101, 116, 101, 114, 0, 0, 1, 0, 0, 0, 115, 97, 121, 72, 101, 108, 108, 111, 0, + 0, 0, 1, 0, 0, 0, 4, 0, 0, 0, 85, 246, 254, 77, ]); const recordData = new Uint8Array([ - 160, 0, 0, 0, 2, 156, 0, 0, 0, 1, 232, 3, 0, 0, 208, 7, 0, 0, 11, 0, 0, 0, 72, - 101, 108, 108, 111, 32, 87, 111, 114, 108, 100, 2, 2, 0, 0, 0, 0, 8, 0, 0, - 192, 33, 0, 0, 1, 0, 0, 0, 5, 0, 0, 0, 104, 101, 108, 108, 111, 5, 0, 0, 0, - 119, 111, 114, 108, 100, 2, 207, 129, 243, 159, 71, 35, 167, 74, 162, 56, 120, - 38, 2, 125, 63, 63, 3, 1, 0, 0, 0, 9, 0, 0, 0, 111, 117, 116, 101, 114, 75, - 101, 121, 49, 1, 0, 0, 0, 9, 0, 0, 0, 105, 110, 110, 101, 114, 75, 101, 121, - 49, 2, 0, 0, 0, 2, 0, 0, 0, 4, 0, 0, 0, 118, 97, 108, 49, 4, 0, 0, 0, 118, 97, - 108, 50, 2, 0, 0, 0, 4, 0, 0, 0, 118, 97, 108, 51, 4, 0, 0, 0, 118, 97, 108, - 52, 0, + 9, 0, 0, 0, 1, 3, 4, 0, 0, 0, 116, 101, 115, 116, +]); + +const recordData2 = new Uint8Array([ + 39, 0, 0, 0, 2, 35, 0, 0, 0, 1, 7, 0, 0, 0, 102, 97, 105, 108, 117, 114, 101, + 2, 244, 1, 0, 0, 3, 13, 118, 226, 151, 228, 248, 79, 67, 167, 238, 53, 223, + 133, 176, 67, 209, 0, ]); const schema = new BinarySchema(schemaData); describe("binary schema", () => { - it("can parse", () => { - schema.parse(); + it("can read", () => { + schema.get(); const ast = schema.ast; + console.log(JSON.stringify(ast, BebopJson.replacer, 2)); + expect(ast).toBeDefined(); - expect(ast.bebopVersion).toBe(2); - expect(ast.definedTypes).keys(["Coordinate", "Point", "Power", "Status"]); - expect(ast.definedTypes["Status"]).toBeDefined(); - expect(ast.definedTypes["Status"].kind).toBe(4); - expect(ast.definedTypes["Status"]["members"]).keys(["Okay", "Doable"]); + expect(ast.bebopVersion).toBe(3); + expect(ast.definitions).keys([ + "Rights", + "ApiRequest", + "ApiResponse", + "DefaultResponse", + "ErrorResponse", + ]); + expect(ast.definitions["ApiResponse"]).toBeDefined(); + expect(ast.definitions["ApiResponse"].kind).toBe(3); + expect(ast.definitions["Rights"]["members"]).keys([ + "Default", + "User", + "Mod", + "Admin", + ]); }); it("can read a record using the schema", () => { - const record = schema.reader.read("Power", recordData); + const record = schema.reader.read("ApiResponse", recordData); + expect(record).toBeDefined(); + expect(record["discriminator"]).toBe(1); + expect(record["value"]).keys(["rights", "username"]); + const value = record["value"] as Record; + expect(value["rights"]).toBe(3); + expect(value["username"]).toBe("test"); + }); + + it("can read a record with guid using the schema", () => { + const record = schema.reader.read("ApiResponse", recordData2); expect(record).toBeDefined(); expect(record["discriminator"]).toBe(2); - expect(record["value"]).keys(["id", "nested", "point"]); + expect(record["value"]).keys(["code", "reason", "id"]); const value = record["value"] as Record; - expect(value["id"]).toBeInstanceOf(Guid); - expect((value["id"] as Guid).toString()).toBe( - "9ff381cf-2347-4aa7-a238-7826027d3f3f" + expect(value["code"]).toBe(500); + expect(value["reason"]).toBe("failure"); + expect(value["id"]).toStrictEqual( + Guid.parseGuid("97e2760d-f8e4-434f-a7ee-35df85b043d1") ); }); it("can write a record using the schema", () => { - const record = schema.reader.read("Power", recordData); + const record = schema.reader.read("ApiResponse", recordData); expect(record).toBeDefined(); - const data = schema.writer.write("Power", record); + const data = schema.writer.write("ApiResponse", record); expect(data).toBeDefined(); expect(data).toEqual(recordData); - const read = schema.reader.read("Power", data); + const read = schema.reader.read("ApiResponse", data); expect(read).toEqual(record); }); - it("cannot modify schema data", () => { expect(() => { schema.raw[0] = 0; diff --git a/Tools/bash/install.sh b/Tools/bash/install.sh index 2f58dea7..556404d6 100755 --- a/Tools/bash/install.sh +++ b/Tools/bash/install.sh @@ -383,13 +383,11 @@ fi # check if bebopc is already installed if [[ -x "$(which bebopc)" ]]; then - bebopc_version_output="$(bebopc --version 2>/dev/null)" - readonly bebopc_version_output - readonly bebopc_name_and_version="${bebopc_version_output%% (*}" - readonly installed_bebopc_version="${bebopc_name_and_version##* }" + installed_bebopc_version="$(bebopc --version 2>/dev/null)" + readonly installed_bebopc_version # exit when the remote version of bebopc is the same as the currently installed version if [[ "${installed_bebopc_version}" == "${BEBOPC_VERSION}" ]]; then - point "${ROCKET_UTF8} ${tty_underline}${tty_white}$bebopc_name_and_version${tty_reset} is already installed and up-to-date" + point "${ROCKET_UTF8} ${tty_underline}${tty_white}bebopc $installed_bebopc_version${tty_reset} is already installed and up-to-date" exit 0 fi # abort when the remote version is less than the installed version. @@ -397,7 +395,7 @@ if [[ -x "$(which bebopc)" ]]; then if version_lt "$(major_minor "${BEBOPC_VERSION}")" "$(major_minor "${installed_bebopc_version}")"; then abort "$( cat <, destination: impl AsRef, con println!("cargo:rerun-if-changed={}", schema.to_str().unwrap()); let mut cmd = Command::new(compiler_path); - if config.skip_generated_notice { - cmd.arg("--skip-generated-notice"); - } let output = cmd - .arg("--files") + .arg("-i") .arg(schema) - .arg("--rust") - .arg(destination.to_str().unwrap()) + .arg("build") + .arg("--generator") + .arg(format!( + "rust:{},noEmitNotice={}", + destination.to_str().unwrap(), + config.skip_generated_notice + )) .output() .expect("Could not run bebopc"); diff --git a/Tools/cmake/Bebop.cmake b/Tools/cmake/Bebop.cmake index a6a0e911..3882c806 100644 --- a/Tools/cmake/Bebop.cmake +++ b/Tools/cmake/Bebop.cmake @@ -1,81 +1,85 @@ -set(BEBOP_RELEASES_URL https://github.com/RainwayApp/bebop/releases/download - CACHE STRING "Public location of Bebop binary releases" FORCE) +set(BEBOP_RELEASES_URL https://github.com/betwixt-labs/bebop/releases/download + CACHE STRING "Public location of Bebop binary releases" FORCE) -set(BEBOP_LANGUAGES cpp cs ts dart rust) +set(BEBOP_LANGUAGES cpp cs ts dart rust py) include(FetchContent) function(Bebop_Generate target_name) - set(_options) - set(_unaryargs VERSION LANGUAGE OUTPUT NAMESPACE) - set(_varargs BOPS) + set(_options) + set(_unaryargs VERSION LANGUAGE OUTPUT NAMESPACE OPTIONS) + set(_varargs BOPS) - cmake_parse_arguments(PARSE_ARGV 1 Bebop_Generate "${_options}" "${_unaryargs}" "${_varargs}") + cmake_parse_arguments(PARSE_ARGV 1 Bebop_Generate "${_options}" "${_unaryargs}" "${_varargs}") - if(NOT Bebop_Generate_BOPS) - message(SEND_ERROR "Error: Bebop_Generate was not given any BOPS as input") - endif() + if(NOT Bebop_Generate_BOPS) + message(SEND_ERROR "Error: Bebop_Generate was not given any BOPS as input") + endif() - if(NOT Bebop_Generate_VERSION) - message(SEND_ERROR "Error: Bebop_Generate must be pinned to a VERSION") - endif() - set(_bebopc_prefix "bebopc_${Bebop_Generate_VERSION}") + if(NOT Bebop_Generate_VERSION) + message(SEND_ERROR "Error: Bebop_Generate must be pinned to a VERSION") + endif() + set(_bebopc_prefix "bebopc_${Bebop_Generate_VERSION}") - if(NOT Bebop_Generate_LANGUAGE) - set(Bebop_Generate_LANGUAGE cpp) - endif() - string(TOLOWER "${Bebop_Generate_LANGUAGE}" Bebop_Generate_LANGUAGE) - list(FIND BEBOP_LANGUAGES ${Bebop_Generate_LANGUAGE} _i) - if(_i EQUAL -1) - message(SEND_ERROR "Error: Bebop_Generate was given an unknown LANGUAGE \"${Bebop_Generate_LANGUAGE}\"") - endif() + if(NOT Bebop_Generate_LANGUAGE) + set(Bebop_Generate_LANGUAGE cpp) + endif() + string(TOLOWER "${Bebop_Generate_LANGUAGE}" Bebop_Generate_LANGUAGE) + list(FIND BEBOP_LANGUAGES ${Bebop_Generate_LANGUAGE} _i) + if(_i EQUAL -1) + message(SEND_ERROR "Error: Bebop_Generate was given an unknown LANGUAGE \"${Bebop_Generate_LANGUAGE}\"") + endif() - if(NOT Bebop_Generate_OUTPUT) - message(SEND_ERROR "Error: Bebop_Generate not given an OUTPUT path") - endif() + if(NOT Bebop_Generate_OUTPUT) + message(SEND_ERROR "Error: Bebop_Generate not given an OUTPUT path") + endif() - if(Bebop_Generate_NAMESPACE) - set(_namespace_args --namespace "${Bebop_Generate_NAMESPACE}") - else() - set(_namespace_args) - endif() - - set(_bebopc_executable_name "bebopc") + set(_bebopc_executable_name "bebopc") - - string( TOLOWER "${CMAKE_HOST_SYSTEM_PROCESSOR}" _system_processor ) - - if (_system_processor STREQUAL "amd64") - set(_system_processor "x64") - endif() + string(TOLOWER "${CMAKE_HOST_SYSTEM_PROCESSOR}" _system_processor) + + if(_system_processor STREQUAL "amd64") + set(_system_processor "x64") + elseif(_system_processor STREQUAL "arm64") + set(_system_processor "arm64") + endif() - if(NOT ${_bebopc_prefix}_POPULATED) - if(CMAKE_HOST_WIN32) - string(APPEND _bebopc_executable_name ".exe") - set(_bebopc_zip "bebopc-windows-${_system_processor}.zip") - elseif(CMAKE_HOST_APPLE) - set(_bebopc_zip "bebopc-macos-${_system_processor}.zip") - else() - set(_bebopc_zip "bebopc-linux-${_system_processor}.zip") - endif() - - set(_bebopc_zip_url "${BEBOP_RELEASES_URL}/${Bebop_Generate_VERSION}/${_bebopc_zip}") + if(NOT ${_bebopc_prefix}_POPULATED) + if(CMAKE_HOST_WIN32) + string(APPEND _bebopc_executable_name ".exe") + set(_bebopc_zip "bebopc-windows-${_system_processor}.zip") + elseif(CMAKE_HOST_APPLE) + set(_bebopc_zip "bebopc-macos-${_system_processor}.zip") + else() + set(_bebopc_zip "bebopc-linux-${_system_processor}.zip") + endif() + + set(_bebopc_zip_url "${BEBOP_RELEASES_URL}/${Bebop_Generate_VERSION}/${_bebopc_zip}") + + FetchContent_Declare(${_bebopc_prefix} + URL "${_bebopc_zip_url}" + ) + FetchContent_Populate(${_bebopc_prefix}) + endif() + set(_bebopc "${${_bebopc_prefix}_SOURCE_DIR}/${_bebopc_executable_name}") + + set(_includeArgs --include) + foreach(_bop IN LISTS Bebop_Generate_BOPS) + list(APPEND _includeArgs ${_bop}) + endforeach() - FetchContent_Declare(${_bebopc_prefix} - URL "${_bebopc_zip_url}" - ) - FetchContent_Populate(${_bebopc_prefix}) - endif() - set(_bebopc "${${_bebopc_prefix}_SOURCE_DIR}/${_bebopc_executable_name}") + set(_generatorArgs "--generator" "${Bebop_Generate_LANGUAGE}:${Bebop_Generate_OUTPUT}") + if(Bebop_Generate_OPTIONS) + string(APPEND _generatorArgs ",${Bebop_Generate_OPTIONS}") + endif() - add_custom_command( - OUTPUT ${Bebop_Generate_OUTPUT} - COMMAND "${_bebopc}" - "--${Bebop_Generate_LANGUAGE}" "${Bebop_Generate_OUTPUT}" - ${_namespace_args} - --files ${Bebop_Generate_BOPS} - DEPENDS ${BEBOP_COMPILER} ${Bebop_Generate_BOPS} - ) + add_custom_command( + OUTPUT ${Bebop_Generate_OUTPUT} + COMMAND "${_bebopc}" + ${_includeArgs} build + ${_generatorArgs} + DEPENDS ${BEBOP_COMPILER} ${Bebop_Generate_BOPS} + ) - add_custom_target(${target_name} DEPENDS ${Bebop_Generate_OUTPUT}) -endfunction() \ No newline at end of file + add_custom_target(${target_name} DEPENDS ${Bebop_Generate_OUTPUT}) +endfunction() diff --git a/Tools/cmake/CMakeLists.txt b/Tools/cmake/CMakeLists.txt index ad26a2ac..96be25fc 100644 --- a/Tools/cmake/CMakeLists.txt +++ b/Tools/cmake/CMakeLists.txt @@ -12,7 +12,7 @@ file(GLOB bop_files CONFIGURE_DEPENDS ../../Laboratory/Schemas/Valid/*.bop) bebop_generate(generate_bop_hpp VERSION "v${BEBOPC_VERSION}" LANGUAGE cpp - NAMESPACE rw::bop OUTPUT ${bop_hpp} BOPS ${bop_files} + OPTIONS "namespace=rw::bop" ) diff --git a/Tools/node/bebopc.js b/Tools/node/bebopc.js deleted file mode 100644 index 7f3e095f..00000000 --- a/Tools/node/bebopc.js +++ /dev/null @@ -1,16 +0,0 @@ -#!/usr/bin/env node - -const child_process = require('child_process') -const { resolveBebopcPath } = require('./scripts/common') - -const args = process.argv.slice(2) - -const bebopc = resolveBebopcPath(); - -try { - child_process.execFileSync(bebopc, args, { stdio: "inherit" }) - process.exit(0) -} -catch (e) { - process.exit(e.status) -} diff --git a/Tools/node/lib/common.ts b/Tools/node/lib/common.ts new file mode 100644 index 00000000..3dda7704 --- /dev/null +++ b/Tools/node/lib/common.ts @@ -0,0 +1,210 @@ +import path from "path"; +import fs from "fs"; +import os from "os"; +import child_process from "child_process"; +import nodeBindings from "wasi-js/dist/bindings/node"; +import { WASIExitError, WASIKillError } from "wasi-js/dist/types"; +import WASI from "wasi-js"; + +const supportedCpuArchitectures = ["x64", "arm64"]; +const supportedPlatforms = ["win32", "darwin", "linux"]; + +const isWebContainer = (() => { + return true; + const isStackblitz = + process.env.SHELL === "/bin/jsh" && process.versions.webcontainer != null; + if (isStackblitz) { + return true; + } + // codesandbox + if (process.env.CSB?.includes("true")) { + return true; + } + return false; +})(); + +/** + * Ensures the current host OS is supported by the Bebop compiler + * @param {NodeJS.Architecture} arch the host arch + * @param {NodeJS.Platform} platform the host os + */ +function ensureHostSupport(arch: string, platform: string) { + if (isWebContainer) return; + if (!supportedCpuArchitectures.includes(arch)) + throw new Error(`Unsupported CPU arch: ${arch}`); + if (!supportedPlatforms.includes(platform)) + throw new Error(`Unsupported platform: ${platform}`); +} +/** + * Gets information about the current compiler host + * @returns + */ +function getHostInfo() { + const arch = process.arch; + const platform = process.platform; + if (isWebContainer) { + return { + arch: "wasm", + os: "wasi", + exeSuffix: ".wasm", + }; + } + ensureHostSupport(arch, platform); + const osName = (() => { + switch (platform) { + case "win32": + return "windows"; + case "darwin": + return "macos"; + case "linux": + return "linux"; + default: + throw new Error(`Unknown platform name: ${platform}`); + } + })(); + return { + arch: arch, + os: osName, + exeSuffix: osName === "windows" ? ".exe" : "", + }; +} +/** + * Gets the fully qualified and normalized path to correct bundled version of the Bebop compiler + */ +const resolveBebopcPath = () => { + const toolsDir = path.resolve(__dirname, "../tools"); + if (!fs.existsSync(toolsDir)) { + throw new Error(`The root 'tools' directory does not exist: ${toolsDir}`); + } + const info = getHostInfo(); + const executable = path.normalize( + `${path.resolve( + toolsDir, + `${info.os}/${info.arch}/bebopc${info.exeSuffix}` + )}` + ); + if (!fs.existsSync(executable)) { + throw new Error(`${executable} does not exist`); + } + return executable; +}; +/** + * Ensures that bebopc binary is executable + * @param {string} executable the path to the executable + */ +const setExecutableBit = (executable: string) => { + if (isWebContainer) return; + if (process.platform === "win32") { + child_process.execSync(`Unblock-File -Path "${executable}"`, { + stdio: "ignore", + shell: "powershell.exe", + }); + } else { + child_process.execSync(`chmod +x "${executable}"`, { stdio: "ignore" }); + } +}; + +const launchBebopc = async (args: string[]): Promise => { + if (!isWebContainer) { + const executable = resolveBebopcPath(); + const child = child_process.spawn(executable, args, { + stdio: "inherit", + }); + return await new Promise((resolve, reject) => { + child.on("exit", (code) => { + if (code === 0) { + resolve(code); + } else { + reject(new Error(`bebopc exited with code ${code}`)); + } + }); + }); + } else { + return await launchWasi(args); + } +}; + +const decoder = new TextDecoder(); + +const launchWasi = async (args: string[]): Promise => { + // add the executable name to the front + args.unshift("bebopc"); + const module = await WebAssembly.compile( + await new Promise((resolve, reject) => { + fs.readFile(resolveBebopcPath(), (err, data) => { + if (err) { + reject(err); + } else { + resolve(data); + } + }); + }) + ); + + let sab: Int32Array | undefined; + const workingDir = process.cwd(); + let standardOutput = ""; + let standardError = ""; + const wasi = new WASI({ + args, + env: { + RUST_BACKTRACE: "1", + }, + bindings: { + ...nodeBindings, + exit: (code: number | null) => { + throw new WASIExitError(code); + }, + kill: (signal: string) => { + throw new WASIKillError(signal); + }, + }, + preopens: { + // we're giving the wasi module access to the current working directory + "/": workingDir, + "/tmp": os.tmpdir(), + }, + + sendStdout: (data: Uint8Array): void => { + standardOutput += decoder.decode(data); + }, + sendStderr: (data: Uint8Array) => { + standardError += decoder.decode(data); + }, + sleep: (ms: number) => { + sab ??= new Int32Array(new SharedArrayBuffer(4)); + Atomics.wait(sab, 0, 0, Math.max(ms, 1)); + }, + }); + + let imports = wasi.getImports(module); + imports = { + wasi_snapshot_preview1: { + ...imports.wasi_snapshot_preview1, + sock_accept: () => -1, + }, + }; + + const instance = await WebAssembly.instantiate(module, imports); + let exitCode = 0; + try { + wasi.start(instance); + } catch (e) { + if (e instanceof WASIExitError) { + exitCode = e.code ?? 127; + } else { + throw e; + } + } + standardOutput = standardOutput.trim(); + if (standardOutput) { + console.log(standardOutput); + } + standardError = standardError.trim(); + if (standardError) { + console.error(standardError); + } + return exitCode; +}; + +export { resolveBebopcPath, setExecutableBit, launchBebopc }; diff --git a/Tools/node/lib/index.ts b/Tools/node/lib/index.ts index 763b90f6..597a71cf 100644 --- a/Tools/node/lib/index.ts +++ b/Tools/node/lib/index.ts @@ -1,148 +1,19 @@ -import child_process = require('child_process') -import {promisify} from 'util' -import path = require('path') -const exec = promisify(child_process.exec) - -// const bebopc = path.resolve(__dirname, "../bebopc.js") - - -/** Ensure that the bebopc executable can be executed. */ -export function ensurePermissions() { - const toolsDir = path.resolve(__dirname, '../tools') - if (process.platform === "darwin") { - const executable = path.resolve(toolsDir, 'macos/bebopc') - child_process.execSync(`chmod +x "${executable}"`, {stdio: 'ignore'}) - } - else if (process.platform === "linux") { - const executable = path.resolve(toolsDir, 'linux/bebopc') - child_process.execSync(`chmod +x "${executable}"`, {stdio: 'ignore'}) - } - else if (process.platform === "win32") { - const executable = path.resolve(toolsDir, 'windows/bebopc.exe') - child_process.execSync(`Unblock-File -Path "${executable}"`, {stdio: 'ignore', shell: "powershell.exe"}) - } -} - -interface BebopcCheckResponse { - Message: string - Severity: "warning" | "error" - Span?: { - FileName: string - StartLine: number - EndLine: number - StartColumn: number - EndColumn: number - } -} - -export type CheckResults = {error: true, issues: Issue[]} | {error: false} - -export interface Issue { - severity: string - startLine: number - startColumn: number - endLine: number - endColumn: number - description: string - fileName: string -} - -function parseBebopcCheckResponse(stderr: string): Issue[] { - const response = JSON.parse(stderr.trim().replace(/\\/g, "\\")) - const issues = Array.isArray(response) ? response as BebopcCheckResponse[] : [response as BebopcCheckResponse]; - return issues.map((issue) => { - const { Message, Severity, Span } = issue - return { - severity: Severity, - description: Message, - startLine: Span?.StartLine ?? 0, - endLine: Span?.EndLine ?? 0, - startColumn: Span?.StartColumn ?? 0, - endColumn: Span?.EndColumn ?? 0, - fileName: Span?.FileName ?? "" - } - }) -} - -function getBebopCompilerPath() { - const toolsDir = path.resolve(__dirname, '../tools') - if (process.platform === "win32") { - return path.resolve(toolsDir, "windows/bebopc.exe") - } - else if (process.platform === "darwin") { - return path.resolve(toolsDir, "macos/bebopc") - } - else if (process.platform === "linux") { - return path.resolve(toolsDir, "linux/bebopc") - } - throw new Error("Unsupported operating system.") -} - -const bebopc = getBebopCompilerPath() - -checkProject({config: ""}) - -/** Validate entire project, providing either a directory to search for bebop.json or a path to a config. */ -export async function checkProject(cfg: - {directory: string, config?: never} | - {directory?: never, config: string} -): Promise { - const processConfig: {cwd?: string} = {} - if (cfg.directory) { - processConfig.cwd = cfg.directory - } - let commandExtension = "" - if (cfg.config) { - commandExtension = `--config ${cfg.config}` - } - return new Promise((resolve, reject) => { - child_process.exec( - `${bebopc} --check --log-format JSON ${commandExtension}`, - processConfig, - (error, stdout, stderr) => { - if (stderr.trim().length > 0) { - resolve({ - error: true, - issues: parseBebopcCheckResponse(stderr) - }) - } - resolve({error: false}) - } - ) - }) -} - -/** Validate schema file passed by path. */ -export async function check(path: string): Promise { - return new Promise((resolve, reject) => { - child_process.exec(`${bebopc} --check ${path} --log-format JSON `, (error, stdout, stderr) => { - if (stderr.trim().length > 0) { - resolve({error: true, issues: parseBebopcCheckResponse(stderr)}) - } - else { - resolve({error: false}) - } - }) - }) -} - -/** Validate schema passed as string. */ -export async function checkSchema(contents: string): Promise<{error: boolean, issues?: Issue[]}> { - return new Promise((resolve, reject) => { - const compiler = child_process.spawn(bebopc, ['--log-format', 'JSON', '--check-schema']) - let stderr: string = "" - compiler.stderr.on('data', (data: string) =>{ - stderr += data - }) - compiler.on("close", (code: number) => { - if (stderr.trim().length > 0) { - resolve({error: true, issues: parseBebopcCheckResponse(stderr)}) - } - else { - resolve({error: false}) - } - }) - compiler.stdin.write(contents) - compiler.stdin.end() - }) -} +#!/usr/bin/env node +import { launchBebopc, resolveBebopcPath, setExecutableBit } from "./common"; + +const args = process.argv.slice(2); + +// use an IIFE to allow top-level await +(async () => { + if (args[0] === "install") { + setExecutableBit(resolveBebopcPath()); + process.exit(0); + } else { + try { + process.exit(await launchBebopc(args)); + } catch (e) { + console.error("Error running bebopc", e); + process.exit(1); + } + } +})(); diff --git a/Tools/node/package.json b/Tools/node/package.json index 8009ab15..66b6b0b4 100644 --- a/Tools/node/package.json +++ b/Tools/node/package.json @@ -14,18 +14,22 @@ "compiler" ], "scripts": { - "prepack": "tsc -p ./", - "prepare": "tsc -p ./", - "build": "tsc -p ./", - "install": "node ./scripts/install.js", - "watch": "tsc -watch -p ./" + "build": "esbuild lib/index.ts --bundle --platform=node --outdir=dist --external:*.node", + "watch": "esbuild lib/index.ts --bundle --platform=node --outdir=dist --external:*.node --watch", + "prepack": "yarn build", + "prepare": "yarn build", + "start": "node ./dist/index.js", + "install": "test -f ./dist/index.js && node ./dist/index.js install || echo 'index.js not found, skipping install script'" }, "bin": { - "bebopc": "./bebopc.js" + "bebopc": "dist/index.js" + }, + "dependencies": { + "wasi-js": "^1.7.3" }, - "dependencies": {}, "devDependencies": { "@types/node": "^20.3.1", + "esbuild": "^0.19.11", "ts-node": "^9.1.1", "typescript": "^5.1.3" } diff --git a/Tools/node/scripts/common.js b/Tools/node/scripts/common.js deleted file mode 100644 index 3d508db1..00000000 --- a/Tools/node/scripts/common.js +++ /dev/null @@ -1,67 +0,0 @@ -const path = require('path') -const fs = require('fs'); -const child_process = require('child_process') -const supportedCpuArchitectures = ['x64', 'arm64']; -const supportedPlatforms = ['win32', 'darwin', 'linux'] - - -/** - * Ensures the current host OS is supported by the Bebop compiler - * @param {NodeJS.Architecture} arch the host arch - * @param {NodeJS.Platform} platform the host os - */ -function ensureHostSupport(arch, platform) { - if (!supportedCpuArchitectures.includes(arch)) throw new Error(`Unsupported CPU arch: ${arch}`); - if (!supportedPlatforms.includes(platform)) throw new Error(`Unsupported platform: ${platform}`); -} -/** - * Gets information about the current compiler host - * @returns - */ -function getHostInfo() { - const arch = process.arch; - const platform = process.platform; - ensureHostSupport(arch, platform); - const osName = (() => { - switch (platform) { - case "win32": - return "windows"; - case "darwin": - return "macos"; - case "linux": - return "linux"; - default: - throw new Error(`Unknown platform name: ${platform}`); - } - })(); - return { arch: arch, os: osName, exeSuffix: osName === "windows" ? '.exe' : '' }; - -} -/** - * Gets the fully qualified and normalized path to correct bundled version of the Bebop compiler - */ -const resolveBebopcPath = () => { - const toolsDir = path.resolve(__dirname, '../tools') - if (!fs.existsSync(toolsDir)) { - throw new Error(`The root 'tools' directory does not exist: ${toolsDir}`); - } - const info = getHostInfo(); - const executable = path.normalize(`${path.resolve(toolsDir, `${info.os}/${info.arch}/bebopc${info.exeSuffix}`)}`); - if (!fs.existsSync(executable)) { - throw new Error(`${executable} does not exist`); - } - return executable; -} -/** - * Ensures that bebopc binary is executable - * @param {string} executable the path to the executable - */ -const setExecutableBit = (executable) => { - if (process.platform === "win32") { - child_process.execSync(`Unblock-File -Path "${executable}"`, { stdio: 'ignore', shell: "powershell.exe" }) - } else { - child_process.execSync(`chmod +x "${executable}"`, { stdio: 'ignore' }) - } -} - -module.exports = { resolveBebopcPath, setExecutableBit }; \ No newline at end of file diff --git a/Tools/node/scripts/install.js b/Tools/node/scripts/install.js deleted file mode 100644 index c876c5cc..00000000 --- a/Tools/node/scripts/install.js +++ /dev/null @@ -1,4 +0,0 @@ -const { resolveBebopcPath, setExecutableBit } = require("./common"); - -const bebopc = resolveBebopcPath(); -setExecutableBit(bebopc) \ No newline at end of file diff --git a/Tools/node/test/bebop.json b/Tools/node/test/bebop.json new file mode 100644 index 00000000..dac6fdba --- /dev/null +++ b/Tools/node/test/bebop.json @@ -0,0 +1,12 @@ +{ + "include": ["*.bop"], + "exclude": ["node_modules", "**/node_modules/*"], + "generators": { + "ts": { + "outFile": "test.ts" + }, + "cs": { + "outFile": "test.cs" + } + } +} diff --git a/Tools/node/test/check.ts b/Tools/node/test/check.ts deleted file mode 100644 index 03832d29..00000000 --- a/Tools/node/test/check.ts +++ /dev/null @@ -1,10 +0,0 @@ -import {check} from "../lib" -import path = require("path") - -// okay so it's not exactly a test yet, but I'm using this to see what the output looks like - -async function testCheck() { - console.log(await check(path.resolve(__dirname, "./invalid.bop"))) -} - -testCheck() diff --git a/Tools/node/test/checkSchema.ts b/Tools/node/test/checkSchema.ts deleted file mode 100644 index 5b70da4b..00000000 --- a/Tools/node/test/checkSchema.ts +++ /dev/null @@ -1,12 +0,0 @@ -import {checkSchema} from "../lib" -import path = require("path") -import fs = require("fs") - -// okay so it's not exactly a test yet, but I'm using this to see what the output looks like - -async function testCheck() { - const fileText = fs.readFileSync(path.resolve(__dirname, "./invalid.bop"), "utf8") - console.log(await checkSchema(fileText)) -} - -testCheck() diff --git a/Tools/node/test/invalid.bop b/Tools/node/test/invalid.bop deleted file mode 100644 index 3e7936bc..00000000 --- a/Tools/node/test/invalid.bop +++ /dev/null @@ -1,4 +0,0 @@ -message Test { - 1 => int32 x; - 2 -> int32 y; -} diff --git a/Tools/node/tsconfig.json b/Tools/node/tsconfig.json index ceab6536..0c815dfb 100644 --- a/Tools/node/tsconfig.json +++ b/Tools/node/tsconfig.json @@ -1,24 +1,21 @@ { - "compilerOptions": { - "module": "commonjs", - "target": "es6", - "outDir": "dist", - "lib": [ - "es6", - "dom" - ], - "declaration": true, - "sourceMap": true, - "rootDir": "lib", - "strict": true /* enable all strict type-checking options */ - /* Additional Checks */ - // "noImplicitReturns": true, /* Report error when not all code paths in function return a value. */ - // "noFallthroughCasesInSwitch": true, /* Report errors for fallthrough cases in switch statement. */ - // "noUnusedParameters": true, /* Report errors on unused parameters. */ - }, - "exclude": [ - "node_modules", - "dist", - "test" - ] + "compilerOptions": { + "module": "esnext", // Use ESNext as the module system + "target": "esnext", // Compile to latest ECMAScript version + "outDir": "dist", + "lib": [ + "esnext", // Include latest ECMAScript features + "dom" + ], + "declaration": true, + "sourceMap": true, + "rootDir": "lib", + "strict": true, // Enable all strict type-checking options + // "noImplicitReturns": true, // Uncomment for additional checks + // "noFallthroughCasesInSwitch": true, + // "noUnusedParameters": true, + "moduleResolution": "node", // Add moduleResolution for node-style module resolution + "esModuleInterop": true // Enables compatibility with default-imported modules + }, + "exclude": ["node_modules", "dist", "test"] } diff --git a/Tools/node/yarn.lock b/Tools/node/yarn.lock index 43e2b601..81b1bc78 100644 --- a/Tools/node/yarn.lock +++ b/Tools/node/yarn.lock @@ -2,52 +2,259 @@ # yarn lockfile v1 +"@cowasm/memfs@^3.5.1": + version "3.5.1" + resolved "https://registry.yarnpkg.com/@cowasm/memfs/-/memfs-3.5.1.tgz#8846077117ff300fe3edc41e8657184c01437c62" + integrity sha512-TSz00K+BdLxAYFQvwHmKgM/eAK6h9OYSQhPTcv/9XSyOF0Q90EtMkJvtwirwAXunfTsZw8R9YMu1UU213ooVKw== + dependencies: + fs-monkey "^1.0.3" + +"@esbuild/aix-ppc64@0.19.11": + version "0.19.11" + resolved "https://registry.yarnpkg.com/@esbuild/aix-ppc64/-/aix-ppc64-0.19.11.tgz#2acd20be6d4f0458bc8c784103495ff24f13b1d3" + integrity sha512-FnzU0LyE3ySQk7UntJO4+qIiQgI7KoODnZg5xzXIrFJlKd2P2gwHsHY4927xj9y5PJmJSzULiUCWmv7iWnNa7g== + +"@esbuild/android-arm64@0.19.11": + version "0.19.11" + resolved "https://registry.yarnpkg.com/@esbuild/android-arm64/-/android-arm64-0.19.11.tgz#b45d000017385c9051a4f03e17078abb935be220" + integrity sha512-aiu7K/5JnLj//KOnOfEZ0D90obUkRzDMyqd/wNAUQ34m4YUPVhRZpnqKV9uqDGxT7cToSDnIHsGooyIczu9T+Q== + +"@esbuild/android-arm@0.19.11": + version "0.19.11" + resolved "https://registry.yarnpkg.com/@esbuild/android-arm/-/android-arm-0.19.11.tgz#f46f55414e1c3614ac682b29977792131238164c" + integrity sha512-5OVapq0ClabvKvQ58Bws8+wkLCV+Rxg7tUVbo9xu034Nm536QTII4YzhaFriQ7rMrorfnFKUsArD2lqKbFY4vw== + +"@esbuild/android-x64@0.19.11": + version "0.19.11" + resolved "https://registry.yarnpkg.com/@esbuild/android-x64/-/android-x64-0.19.11.tgz#bfc01e91740b82011ef503c48f548950824922b2" + integrity sha512-eccxjlfGw43WYoY9QgB82SgGgDbibcqyDTlk3l3C0jOVHKxrjdc9CTwDUQd0vkvYg5um0OH+GpxYvp39r+IPOg== + +"@esbuild/darwin-arm64@0.19.11": + version "0.19.11" + resolved "https://registry.yarnpkg.com/@esbuild/darwin-arm64/-/darwin-arm64-0.19.11.tgz#533fb7f5a08c37121d82c66198263dcc1bed29bf" + integrity sha512-ETp87DRWuSt9KdDVkqSoKoLFHYTrkyz2+65fj9nfXsaV3bMhTCjtQfw3y+um88vGRKRiF7erPrh/ZuIdLUIVxQ== + +"@esbuild/darwin-x64@0.19.11": + version "0.19.11" + resolved "https://registry.yarnpkg.com/@esbuild/darwin-x64/-/darwin-x64-0.19.11.tgz#62f3819eff7e4ddc656b7c6815a31cf9a1e7d98e" + integrity sha512-fkFUiS6IUK9WYUO/+22omwetaSNl5/A8giXvQlcinLIjVkxwTLSktbF5f/kJMftM2MJp9+fXqZ5ezS7+SALp4g== + +"@esbuild/freebsd-arm64@0.19.11": + version "0.19.11" + resolved "https://registry.yarnpkg.com/@esbuild/freebsd-arm64/-/freebsd-arm64-0.19.11.tgz#d478b4195aa3ca44160272dab85ef8baf4175b4a" + integrity sha512-lhoSp5K6bxKRNdXUtHoNc5HhbXVCS8V0iZmDvyWvYq9S5WSfTIHU2UGjcGt7UeS6iEYp9eeymIl5mJBn0yiuxA== + +"@esbuild/freebsd-x64@0.19.11": + version "0.19.11" + resolved "https://registry.yarnpkg.com/@esbuild/freebsd-x64/-/freebsd-x64-0.19.11.tgz#7bdcc1917409178257ca6a1a27fe06e797ec18a2" + integrity sha512-JkUqn44AffGXitVI6/AbQdoYAq0TEullFdqcMY/PCUZ36xJ9ZJRtQabzMA+Vi7r78+25ZIBosLTOKnUXBSi1Kw== + +"@esbuild/linux-arm64@0.19.11": + version "0.19.11" + resolved "https://registry.yarnpkg.com/@esbuild/linux-arm64/-/linux-arm64-0.19.11.tgz#58ad4ff11685fcc735d7ff4ca759ab18fcfe4545" + integrity sha512-LneLg3ypEeveBSMuoa0kwMpCGmpu8XQUh+mL8XXwoYZ6Be2qBnVtcDI5azSvh7vioMDhoJFZzp9GWp9IWpYoUg== + +"@esbuild/linux-arm@0.19.11": + version "0.19.11" + resolved "https://registry.yarnpkg.com/@esbuild/linux-arm/-/linux-arm-0.19.11.tgz#ce82246d873b5534d34de1e5c1b33026f35e60e3" + integrity sha512-3CRkr9+vCV2XJbjwgzjPtO8T0SZUmRZla+UL1jw+XqHZPkPgZiyWvbDvl9rqAN8Zl7qJF0O/9ycMtjU67HN9/Q== + +"@esbuild/linux-ia32@0.19.11": + version "0.19.11" + resolved "https://registry.yarnpkg.com/@esbuild/linux-ia32/-/linux-ia32-0.19.11.tgz#cbae1f313209affc74b80f4390c4c35c6ab83fa4" + integrity sha512-caHy++CsD8Bgq2V5CodbJjFPEiDPq8JJmBdeyZ8GWVQMjRD0sU548nNdwPNvKjVpamYYVL40AORekgfIubwHoA== + +"@esbuild/linux-loong64@0.19.11": + version "0.19.11" + resolved "https://registry.yarnpkg.com/@esbuild/linux-loong64/-/linux-loong64-0.19.11.tgz#5f32aead1c3ec8f4cccdb7ed08b166224d4e9121" + integrity sha512-ppZSSLVpPrwHccvC6nQVZaSHlFsvCQyjnvirnVjbKSHuE5N24Yl8F3UwYUUR1UEPaFObGD2tSvVKbvR+uT1Nrg== + +"@esbuild/linux-mips64el@0.19.11": + version "0.19.11" + resolved "https://registry.yarnpkg.com/@esbuild/linux-mips64el/-/linux-mips64el-0.19.11.tgz#38eecf1cbb8c36a616261de858b3c10d03419af9" + integrity sha512-B5x9j0OgjG+v1dF2DkH34lr+7Gmv0kzX6/V0afF41FkPMMqaQ77pH7CrhWeR22aEeHKaeZVtZ6yFwlxOKPVFyg== + +"@esbuild/linux-ppc64@0.19.11": + version "0.19.11" + resolved "https://registry.yarnpkg.com/@esbuild/linux-ppc64/-/linux-ppc64-0.19.11.tgz#9c5725a94e6ec15b93195e5a6afb821628afd912" + integrity sha512-MHrZYLeCG8vXblMetWyttkdVRjQlQUb/oMgBNurVEnhj4YWOr4G5lmBfZjHYQHHN0g6yDmCAQRR8MUHldvvRDA== + +"@esbuild/linux-riscv64@0.19.11": + version "0.19.11" + resolved "https://registry.yarnpkg.com/@esbuild/linux-riscv64/-/linux-riscv64-0.19.11.tgz#2dc4486d474a2a62bbe5870522a9a600e2acb916" + integrity sha512-f3DY++t94uVg141dozDu4CCUkYW+09rWtaWfnb3bqe4w5NqmZd6nPVBm+qbz7WaHZCoqXqHz5p6CM6qv3qnSSQ== + +"@esbuild/linux-s390x@0.19.11": + version "0.19.11" + resolved "https://registry.yarnpkg.com/@esbuild/linux-s390x/-/linux-s390x-0.19.11.tgz#4ad8567df48f7dd4c71ec5b1753b6f37561a65a8" + integrity sha512-A5xdUoyWJHMMlcSMcPGVLzYzpcY8QP1RtYzX5/bS4dvjBGVxdhuiYyFwp7z74ocV7WDc0n1harxmpq2ePOjI0Q== + +"@esbuild/linux-x64@0.19.11": + version "0.19.11" + resolved "https://registry.yarnpkg.com/@esbuild/linux-x64/-/linux-x64-0.19.11.tgz#b7390c4d5184f203ebe7ddaedf073df82a658766" + integrity sha512-grbyMlVCvJSfxFQUndw5mCtWs5LO1gUlwP4CDi4iJBbVpZcqLVT29FxgGuBJGSzyOxotFG4LoO5X+M1350zmPA== + +"@esbuild/netbsd-x64@0.19.11": + version "0.19.11" + resolved "https://registry.yarnpkg.com/@esbuild/netbsd-x64/-/netbsd-x64-0.19.11.tgz#d633c09492a1721377f3bccedb2d821b911e813d" + integrity sha512-13jvrQZJc3P230OhU8xgwUnDeuC/9egsjTkXN49b3GcS5BKvJqZn86aGM8W9pd14Kd+u7HuFBMVtrNGhh6fHEQ== + +"@esbuild/openbsd-x64@0.19.11": + version "0.19.11" + resolved "https://registry.yarnpkg.com/@esbuild/openbsd-x64/-/openbsd-x64-0.19.11.tgz#17388c76e2f01125bf831a68c03a7ffccb65d1a2" + integrity sha512-ysyOGZuTp6SNKPE11INDUeFVVQFrhcNDVUgSQVDzqsqX38DjhPEPATpid04LCoUr2WXhQTEZ8ct/EgJCUDpyNw== + +"@esbuild/sunos-x64@0.19.11": + version "0.19.11" + resolved "https://registry.yarnpkg.com/@esbuild/sunos-x64/-/sunos-x64-0.19.11.tgz#e320636f00bb9f4fdf3a80e548cb743370d41767" + integrity sha512-Hf+Sad9nVwvtxy4DXCZQqLpgmRTQqyFyhT3bZ4F2XlJCjxGmRFF0Shwn9rzhOYRB61w9VMXUkxlBy56dk9JJiQ== + +"@esbuild/win32-arm64@0.19.11": + version "0.19.11" + resolved "https://registry.yarnpkg.com/@esbuild/win32-arm64/-/win32-arm64-0.19.11.tgz#c778b45a496e90b6fc373e2a2bb072f1441fe0ee" + integrity sha512-0P58Sbi0LctOMOQbpEOvOL44Ne0sqbS0XWHMvvrg6NE5jQ1xguCSSw9jQeUk2lfrXYsKDdOe6K+oZiwKPilYPQ== + +"@esbuild/win32-ia32@0.19.11": + version "0.19.11" + resolved "https://registry.yarnpkg.com/@esbuild/win32-ia32/-/win32-ia32-0.19.11.tgz#481a65fee2e5cce74ec44823e6b09ecedcc5194c" + integrity sha512-6YOrWS+sDJDmshdBIQU+Uoyh7pQKrdykdefC1avn76ss5c+RN6gut3LZA4E2cH5xUEp5/cA0+YxRaVtRAb0xBg== + +"@esbuild/win32-x64@0.19.11": + version "0.19.11" + resolved "https://registry.yarnpkg.com/@esbuild/win32-x64/-/win32-x64-0.19.11.tgz#a5d300008960bb39677c46bf16f53ec70d8dee04" + integrity sha512-vfkhltrjCAb603XaFhqhAF4LGDi2M4OrCRrFusyQ+iTLQ/o60QQXxc9cZC/FFpihBI9N1Grn6SMKVJ4KP7Fuiw== + "@types/node@^20.3.1": - version "20.3.1" - resolved "https://registry.yarnpkg.com/@types/node/-/node-20.3.1.tgz#e8a83f1aa8b649377bb1fb5d7bac5cb90e784dfe" - integrity sha512-EhcH/wvidPy1WeML3TtYFGR83UzjxeWRen9V402T8aUGYsCHOmfoisV3ZSg03gAFIbLq8TnWOJ0f4cALtnSEUg== + version "20.11.5" + resolved "https://registry.yarnpkg.com/@types/node/-/node-20.11.5.tgz#be10c622ca7fcaa3cf226cf80166abc31389d86e" + integrity sha512-g557vgQjUUfN76MZAN/dt1z3dzcUsimuysco0KeluHgrPdJXkP/XdAURgyO2W9fZWHRtRBiVKzKn8vyOAwlG+w== + dependencies: + undici-types "~5.26.4" + +"@wapython/unionfs@^4.5.7": + version "4.5.7" + resolved "https://registry.yarnpkg.com/@wapython/unionfs/-/unionfs-4.5.7.tgz#bdc35e34534709702cf125ac3e96d7a67a761af3" + integrity sha512-7809nAVelP9TqXCq5c1yCwymPreTXrK4n4q/zxOlQOIIz4PNmoQmIKiLnwkKk39GDnLmP1MvP9ZkBDyXCCmTfg== + dependencies: + fs-monkey "^1.0.0" arg@^4.1.0: version "4.1.3" - resolved "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz" + resolved "https://registry.yarnpkg.com/arg/-/arg-4.1.3.tgz#269fc7ad5b8e42cb63c896d5666017261c144089" integrity sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA== buffer-from@^1.0.0: - version "1.1.1" - resolved "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.1.tgz" - integrity sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A== + version "1.1.2" + resolved "https://registry.yarnpkg.com/buffer-from/-/buffer-from-1.1.2.tgz#2b146a6fd72e80b4f55d255f35ed59a3a9a41bd5" + integrity sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ== create-require@^1.1.0: version "1.1.1" - resolved "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz" + resolved "https://registry.yarnpkg.com/create-require/-/create-require-1.1.1.tgz#c1d7e8f1e5f6cfc9ff65f9cd352d37348756c333" integrity sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ== +debug@^4.3.4: + version "4.3.4" + resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.4.tgz#1319f6579357f2338d3337d2cdd4914bb5dcc865" + integrity sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ== + dependencies: + ms "2.1.2" + diff@^4.0.1: version "4.0.2" - resolved "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz" + resolved "https://registry.yarnpkg.com/diff/-/diff-4.0.2.tgz#60f3aecb89d5fae520c11aa19efc2bb982aade7d" integrity sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A== +esbuild@^0.19.11: + version "0.19.11" + resolved "https://registry.yarnpkg.com/esbuild/-/esbuild-0.19.11.tgz#4a02dca031e768b5556606e1b468fe72e3325d96" + integrity sha512-HJ96Hev2hX/6i5cDVwcqiJBBtuo9+FeIJOtZ9W1kA5M6AMJRHUZlpYZ1/SbEwtO0ioNAW8rUooVpC/WehY2SfA== + optionalDependencies: + "@esbuild/aix-ppc64" "0.19.11" + "@esbuild/android-arm" "0.19.11" + "@esbuild/android-arm64" "0.19.11" + "@esbuild/android-x64" "0.19.11" + "@esbuild/darwin-arm64" "0.19.11" + "@esbuild/darwin-x64" "0.19.11" + "@esbuild/freebsd-arm64" "0.19.11" + "@esbuild/freebsd-x64" "0.19.11" + "@esbuild/linux-arm" "0.19.11" + "@esbuild/linux-arm64" "0.19.11" + "@esbuild/linux-ia32" "0.19.11" + "@esbuild/linux-loong64" "0.19.11" + "@esbuild/linux-mips64el" "0.19.11" + "@esbuild/linux-ppc64" "0.19.11" + "@esbuild/linux-riscv64" "0.19.11" + "@esbuild/linux-s390x" "0.19.11" + "@esbuild/linux-x64" "0.19.11" + "@esbuild/netbsd-x64" "0.19.11" + "@esbuild/openbsd-x64" "0.19.11" + "@esbuild/sunos-x64" "0.19.11" + "@esbuild/win32-arm64" "0.19.11" + "@esbuild/win32-ia32" "0.19.11" + "@esbuild/win32-x64" "0.19.11" + +fflate@^0.7.3: + version "0.7.4" + resolved "https://registry.yarnpkg.com/fflate/-/fflate-0.7.4.tgz#61587e5d958fdabb5a9368a302c25363f4f69f50" + integrity sha512-5u2V/CDW15QM1XbbgS+0DfPxVB+jUKhWEKuuFuHncbk3tEEqzmoXL+2KyOFuKGqOnmdIy0/davWF1CkuwtibCw== + +fs-monkey@^1.0.0, fs-monkey@^1.0.3: + version "1.0.5" + resolved "https://registry.yarnpkg.com/fs-monkey/-/fs-monkey-1.0.5.tgz#fe450175f0db0d7ea758102e1d84096acb925788" + integrity sha512-8uMbBjrhzW76TYgEV27Y5E//W2f/lTFmx78P2w19FZSxarhI/798APGQyuGCwmkNxgwGRhrLfvWyLBvNtuOmew== + make-error@^1.1.1: version "1.3.6" - resolved "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz" + resolved "https://registry.yarnpkg.com/make-error/-/make-error-1.3.6.tgz#2eb2e37ea9b67c4891f684a1394799af484cf7a2" integrity sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw== +ms@2.1.2: + version "2.1.2" + resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009" + integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w== + +path-browserify@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/path-browserify/-/path-browserify-1.0.1.tgz#d98454a9c3753d5790860f16f68867b9e46be1fd" + integrity sha512-b7uo2UCUOYZcnF/3ID0lulOJi/bafxa1xPe7ZPsammBSpjSWQkjNxlt635YGS2MiR9GjvuXCtz2emr3jbsz98g== + +randombytes@^2.0.5: + version "2.1.0" + resolved "https://registry.yarnpkg.com/randombytes/-/randombytes-2.1.0.tgz#df6f84372f0270dc65cdf6291349ab7a473d4f2a" + integrity sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ== + dependencies: + safe-buffer "^5.1.0" + +randomfill@^1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/randomfill/-/randomfill-1.0.4.tgz#c92196fc86ab42be983f1bf31778224931d61458" + integrity sha512-87lcbR8+MhcWcUiQ+9e+Rwx8MyR2P7qnt15ynUlbm3TU/fjbgz4GsvfSUDTemtCCtVCqb4ZcEFlyPNTh9bBTLw== + dependencies: + randombytes "^2.0.5" + safe-buffer "^5.1.0" + +safe-buffer@^5.1.0: + version "5.2.1" + resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.1.tgz#1eaf9fa9bdb1fdd4ec75f58f9cdb4e6b7827eec6" + integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ== + source-map-support@^0.5.17: - version "0.5.19" - resolved "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.19.tgz" - integrity sha512-Wonm7zOCIJzBGQdB+thsPar0kYuCIzYvxZwlBa87yi/Mdjv7Tip2cyVbLj5o0cFPN4EVkuTwb3GDDyUx2DGnGw== + version "0.5.21" + resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.21.tgz#04fe7c7f9e1ed2d662233c28cb2b35b9f63f6e4f" + integrity sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w== dependencies: buffer-from "^1.0.0" source-map "^0.6.0" source-map@^0.6.0: version "0.6.1" - resolved "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz" + resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263" integrity sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g== ts-node@^9.1.1: version "9.1.1" - resolved "https://registry.npmjs.org/ts-node/-/ts-node-9.1.1.tgz" + resolved "https://registry.yarnpkg.com/ts-node/-/ts-node-9.1.1.tgz#51a9a450a3e959401bda5f004a72d54b936d376d" integrity sha512-hPlt7ZACERQGf03M253ytLY3dHbGNGrAq9qIHWUY9XHYl1z7wYngSr3OQ5xmui8o2AaxsONxIzjafLUiWBo1Fg== dependencies: arg "^4.1.0" @@ -57,12 +264,35 @@ ts-node@^9.1.1: source-map-support "^0.5.17" yn "3.1.1" +typedarray-to-buffer@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/typedarray-to-buffer/-/typedarray-to-buffer-4.0.0.tgz#cdd2933c61dd3f5f02eda5d012d441f95bfeb50a" + integrity sha512-6dOYeZfS3O9RtRD1caom0sMxgK59b27+IwoNy8RDPsmslSGOyU+mpTamlaIW7aNKi90ZQZ9DFaZL3YRoiSCULQ== + typescript@^5.1.3: - version "5.1.3" - resolved "https://registry.yarnpkg.com/typescript/-/typescript-5.1.3.tgz#8d84219244a6b40b6fb2b33cc1c062f715b9e826" - integrity sha512-XH627E9vkeqhlZFQuL+UsyAXEnibT0kWR2FWONlr4sTjvxyJYnyefgrkyECLzM5NenmKzRAy2rR/OlYLA1HkZw== + version "5.3.3" + resolved "https://registry.yarnpkg.com/typescript/-/typescript-5.3.3.tgz#b3ce6ba258e72e6305ba66f5c9b452aaee3ffe37" + integrity sha512-pXWcraxM0uxAS+tN0AG/BF2TyqmHO014Z070UsJ+pFvYuRSq8KH8DmWpnbXe0pEPDHXZV3FcAbJkijJ5oNEnWw== + +undici-types@~5.26.4: + version "5.26.5" + resolved "https://registry.yarnpkg.com/undici-types/-/undici-types-5.26.5.tgz#bcd539893d00b56e964fd2657a4866b221a65617" + integrity sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA== + +wasi-js@^1.7.3: + version "1.7.3" + resolved "https://registry.yarnpkg.com/wasi-js/-/wasi-js-1.7.3.tgz#0831c5f1656de78f4c26bfdccfde45521688e6dc" + integrity sha512-4oq6iVdY4nlsqFviS98ltEdWnhYQoeZwz3/5AxQ3bs0fd/YBzBMS162p/n0DHEIJFev1Jvk3gnJwck1s4a8mVQ== + dependencies: + "@cowasm/memfs" "^3.5.1" + "@wapython/unionfs" "^4.5.7" + debug "^4.3.4" + fflate "^0.7.3" + path-browserify "^1.0.0" + randomfill "^1.0.4" + typedarray-to-buffer "^4.0.0" yn@3.1.1: version "3.1.1" - resolved "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz" + resolved "https://registry.yarnpkg.com/yn/-/yn-3.1.1.tgz#1e87401a09d767c1d5eab26a6e4c185182d2eb50" integrity sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q== diff --git a/Tools/ps/install.ps1 b/Tools/ps/install.ps1 index 98c0708c..77c9ad3a 100644 --- a/Tools/ps/install.ps1 +++ b/Tools/ps/install.ps1 @@ -164,7 +164,7 @@ function Test-BebopcInstalled { function Test-BebopcVersion { $compilerPath = "$env:PROGRAMDATA\bebop\bebopc.exe" - $installedVersion = ([string](& "$compilerPath" --version)).Split(' ')[1].Trim() + $installedVersion = ([string](& "$compilerPath" --version)).Trim() $remoteVersion = $bebopcVersion if ([System.Version]$installedVersion -gt [System.Version]$remoteVersion) { # Installed version is greater than the version to install diff --git a/Tools/vs/bebop-tools.nuspec b/Tools/vs/bebop-tools.nuspec index eb90ac2d..f7217d1b 100644 --- a/Tools/vs/bebop-tools.nuspec +++ b/Tools/vs/bebop-tools.nuspec @@ -10,7 +10,7 @@ false Apache-2.0 https://licenses.nuget.org/Apache-2.0 - https://github.com/RainwayApp/bebop + https://github.com/betwixt-labs/bebop The Bebop compiler for managed C# projects and native C++ projects. Add this package to a project that contains .bop files to be compiled to code. diff --git a/Tools/vs/build/bebop-tools.targets b/Tools/vs/build/bebop-tools.targets index 9d831bd8..fc242dc6 100644 --- a/Tools/vs/build/bebop-tools.targets +++ b/Tools/vs/build/bebop-tools.targets @@ -19,6 +19,7 @@ MSBuild + 9.0 @@ -44,6 +45,7 @@ + @@ -62,7 +64,7 @@ com.apple.security.cs.allow-jit + com.apple.security.cs.disable-library-validation + + com.apple.security.cs.allow-dyld-environment-variables + + com.apple.security.cs.allow-unsigned-executable-memory + \ No newline at end of file diff --git a/docs/.astro/types.d.ts b/docs/.astro/types.d.ts new file mode 100644 index 00000000..4224b8d3 --- /dev/null +++ b/docs/.astro/types.d.ts @@ -0,0 +1,465 @@ +declare module 'astro:content' { + interface Render { + '.mdx': Promise<{ + Content: import('astro').MarkdownInstance<{}>['Content']; + headings: import('astro').MarkdownHeading[]; + remarkPluginFrontmatter: Record; + }>; + } +} + +declare module 'astro:content' { + interface Render { + '.md': Promise<{ + Content: import('astro').MarkdownInstance<{}>['Content']; + headings: import('astro').MarkdownHeading[]; + remarkPluginFrontmatter: Record; + }>; + } +} + +declare module 'astro:content' { + export { z } from 'astro/zod'; + + type Flatten = T extends { [K: string]: infer U } ? U : never; + + export type CollectionKey = keyof AnyEntryMap; + export type CollectionEntry = Flatten; + + export type ContentCollectionKey = keyof ContentEntryMap; + export type DataCollectionKey = keyof DataEntryMap; + + // This needs to be in sync with ImageMetadata + export type ImageFunction = () => import('astro/zod').ZodObject<{ + src: import('astro/zod').ZodString; + width: import('astro/zod').ZodNumber; + height: import('astro/zod').ZodNumber; + format: import('astro/zod').ZodUnion< + [ + import('astro/zod').ZodLiteral<'png'>, + import('astro/zod').ZodLiteral<'jpg'>, + import('astro/zod').ZodLiteral<'jpeg'>, + import('astro/zod').ZodLiteral<'tiff'>, + import('astro/zod').ZodLiteral<'webp'>, + import('astro/zod').ZodLiteral<'gif'>, + import('astro/zod').ZodLiteral<'svg'>, + import('astro/zod').ZodLiteral<'avif'>, + ] + >; + }>; + + type BaseSchemaWithoutEffects = + | import('astro/zod').AnyZodObject + | import('astro/zod').ZodUnion<[BaseSchemaWithoutEffects, ...BaseSchemaWithoutEffects[]]> + | import('astro/zod').ZodDiscriminatedUnion + | import('astro/zod').ZodIntersection; + + type BaseSchema = + | BaseSchemaWithoutEffects + | import('astro/zod').ZodEffects; + + export type SchemaContext = { image: ImageFunction }; + + type DataCollectionConfig = { + type: 'data'; + schema?: S | ((context: SchemaContext) => S); + }; + + type ContentCollectionConfig = { + type?: 'content'; + schema?: S | ((context: SchemaContext) => S); + }; + + type CollectionConfig = ContentCollectionConfig | DataCollectionConfig; + + export function defineCollection( + input: CollectionConfig + ): CollectionConfig; + + type AllValuesOf = T extends any ? T[keyof T] : never; + type ValidContentEntrySlug = AllValuesOf< + ContentEntryMap[C] + >['slug']; + + export function getEntryBySlug< + C extends keyof ContentEntryMap, + E extends ValidContentEntrySlug | (string & {}), + >( + collection: C, + // Note that this has to accept a regular string too, for SSR + entrySlug: E + ): E extends ValidContentEntrySlug + ? Promise> + : Promise | undefined>; + + export function getDataEntryById( + collection: C, + entryId: E + ): Promise>; + + export function getCollection>( + collection: C, + filter?: (entry: CollectionEntry) => entry is E + ): Promise; + export function getCollection( + collection: C, + filter?: (entry: CollectionEntry) => unknown + ): Promise[]>; + + export function getEntry< + C extends keyof ContentEntryMap, + E extends ValidContentEntrySlug | (string & {}), + >(entry: { + collection: C; + slug: E; + }): E extends ValidContentEntrySlug + ? Promise> + : Promise | undefined>; + export function getEntry< + C extends keyof DataEntryMap, + E extends keyof DataEntryMap[C] | (string & {}), + >(entry: { + collection: C; + id: E; + }): E extends keyof DataEntryMap[C] + ? Promise + : Promise | undefined>; + export function getEntry< + C extends keyof ContentEntryMap, + E extends ValidContentEntrySlug | (string & {}), + >( + collection: C, + slug: E + ): E extends ValidContentEntrySlug + ? Promise> + : Promise | undefined>; + export function getEntry< + C extends keyof DataEntryMap, + E extends keyof DataEntryMap[C] | (string & {}), + >( + collection: C, + id: E + ): E extends keyof DataEntryMap[C] + ? Promise + : Promise | undefined>; + + /** Resolve an array of entry references from the same collection */ + export function getEntries( + entries: { + collection: C; + slug: ValidContentEntrySlug; + }[] + ): Promise[]>; + export function getEntries( + entries: { + collection: C; + id: keyof DataEntryMap[C]; + }[] + ): Promise[]>; + + export function reference( + collection: C + ): import('astro/zod').ZodEffects< + import('astro/zod').ZodString, + C extends keyof ContentEntryMap + ? { + collection: C; + slug: ValidContentEntrySlug; + } + : { + collection: C; + id: keyof DataEntryMap[C]; + } + >; + // Allow generic `string` to avoid excessive type errors in the config + // if `dev` is not running to update as you edit. + // Invalid collection names will be caught at build time. + export function reference( + collection: C + ): import('astro/zod').ZodEffects; + + type ReturnTypeOrOriginal = T extends (...args: any[]) => infer R ? R : T; + type InferEntrySchema = import('astro/zod').infer< + ReturnTypeOrOriginal['schema']> + >; + + type ContentEntryMap = { + "docs": { +"chords/chord-json.md": { + id: "chords/chord-json.md"; + slug: "chords/chord-json"; + body: string; + collection: "docs"; + data: InferEntrySchema<"docs"> +} & { render(): Render[".md"] }; +"chords/chordc.mdx": { + id: "chords/chordc.mdx"; + slug: "chords/chordc"; + body: string; + collection: "docs"; + data: InferEntrySchema<"docs"> +} & { render(): Render[".mdx"] }; +"chords/installing.mdx": { + id: "chords/installing.mdx"; + slug: "chords/installing"; + body: string; + collection: "docs"; + data: InferEntrySchema<"docs"> +} & { render(): Render[".mdx"] }; +"chords/publishing.mdx": { + id: "chords/publishing.mdx"; + slug: "chords/publishing"; + body: string; + collection: "docs"; + data: InferEntrySchema<"docs"> +} & { render(): Render[".mdx"] }; +"chords/what-are-chords.mdx": { + id: "chords/what-are-chords.mdx"; + slug: "chords/what-are-chords"; + body: string; + collection: "docs"; + data: InferEntrySchema<"docs"> +} & { render(): Render[".mdx"] }; +"configuration/bebop-config.mdx": { + id: "configuration/bebop-config.mdx"; + slug: "configuration/bebop-config"; + body: string; + collection: "docs"; + data: InferEntrySchema<"docs"> +} & { render(): Render[".mdx"] }; +"faq.mdx": { + id: "faq.mdx"; + slug: "faq"; + body: string; + collection: "docs"; + data: InferEntrySchema<"docs"> +} & { render(): Render[".mdx"] }; +"guide/getting-started-csharp.mdx": { + id: "guide/getting-started-csharp.mdx"; + slug: "guide/getting-started-csharp"; + body: string; + collection: "docs"; + data: InferEntrySchema<"docs"> +} & { render(): Render[".mdx"] }; +"guide/getting-started-rust.mdx": { + id: "guide/getting-started-rust.mdx"; + slug: "guide/getting-started-rust"; + body: string; + collection: "docs"; + data: InferEntrySchema<"docs"> +} & { render(): Render[".mdx"] }; +"guide/getting-started-typescript.mdx": { + id: "guide/getting-started-typescript.mdx"; + slug: "guide/getting-started-typescript"; + body: string; + collection: "docs"; + data: InferEntrySchema<"docs"> +} & { render(): Render[".mdx"] }; +"guide/getting-started.mdx": { + id: "guide/getting-started.mdx"; + slug: "guide/getting-started"; + body: string; + collection: "docs"; + data: InferEntrySchema<"docs"> +} & { render(): Render[".mdx"] }; +"guide/installation.mdx": { + id: "guide/installation.mdx"; + slug: "guide/installation"; + body: string; + collection: "docs"; + data: InferEntrySchema<"docs"> +} & { render(): Render[".mdx"] }; +"guide/playground.mdx": { + id: "guide/playground.mdx"; + slug: "guide/playground"; + body: string; + collection: "docs"; + data: InferEntrySchema<"docs"> +} & { render(): Render[".mdx"] }; +"index.mdx": { + id: "index.mdx"; + slug: "index"; + body: string; + collection: "docs"; + data: InferEntrySchema<"docs"> +} & { render(): Render[".mdx"] }; +"integrations.mdx": { + id: "integrations.mdx"; + slug: "integrations"; + body: string; + collection: "docs"; + data: InferEntrySchema<"docs"> +} & { render(): Render[".mdx"] }; +"known-issues.mdx": { + id: "known-issues.mdx"; + slug: "known-issues"; + body: string; + collection: "docs"; + data: InferEntrySchema<"docs"> +} & { render(): Render[".mdx"] }; +"reference/bebop-json.md": { + id: "reference/bebop-json.md"; + slug: "reference/bebop-json"; + body: string; + collection: "docs"; + data: InferEntrySchema<"docs"> +} & { render(): Render[".md"] }; +"reference/bebopc.md": { + id: "reference/bebopc.md"; + slug: "reference/bebopc"; + body: string; + collection: "docs"; + data: InferEntrySchema<"docs"> +} & { render(): Render[".md"] }; +"reference/binary-schema.mdx": { + id: "reference/binary-schema.mdx"; + slug: "reference/binary-schema"; + body: string; + collection: "docs"; + data: InferEntrySchema<"docs"> +} & { render(): Render[".mdx"] }; +"reference/comments.md": { + id: "reference/comments.md"; + slug: "reference/comments"; + body: string; + collection: "docs"; + data: InferEntrySchema<"docs"> +} & { render(): Render[".md"] }; +"reference/const.mdx": { + id: "reference/const.mdx"; + slug: "reference/const"; + body: string; + collection: "docs"; + data: InferEntrySchema<"docs"> +} & { render(): Render[".mdx"] }; +"reference/decorators.mdx": { + id: "reference/decorators.mdx"; + slug: "reference/decorators"; + body: string; + collection: "docs"; + data: InferEntrySchema<"docs"> +} & { render(): Render[".mdx"] }; +"reference/enum.mdx": { + id: "reference/enum.mdx"; + slug: "reference/enum"; + body: string; + collection: "docs"; + data: InferEntrySchema<"docs"> +} & { render(): Render[".mdx"] }; +"reference/import.mdx": { + id: "reference/import.mdx"; + slug: "reference/import"; + body: string; + collection: "docs"; + data: InferEntrySchema<"docs"> +} & { render(): Render[".mdx"] }; +"reference/json-over-bebop.mdx": { + id: "reference/json-over-bebop.mdx"; + slug: "reference/json-over-bebop"; + body: string; + collection: "docs"; + data: InferEntrySchema<"docs"> +} & { render(): Render[".mdx"] }; +"reference/message.mdx": { + id: "reference/message.mdx"; + slug: "reference/message"; + body: string; + collection: "docs"; + data: InferEntrySchema<"docs"> +} & { render(): Render[".mdx"] }; +"reference/records.mdx": { + id: "reference/records.mdx"; + slug: "reference/records"; + body: string; + collection: "docs"; + data: InferEntrySchema<"docs"> +} & { render(): Render[".mdx"] }; +"reference/service.mdx": { + id: "reference/service.mdx"; + slug: "reference/service"; + body: string; + collection: "docs"; + data: InferEntrySchema<"docs"> +} & { render(): Render[".mdx"] }; +"reference/struct.mdx": { + id: "reference/struct.mdx"; + slug: "reference/struct"; + body: string; + collection: "docs"; + data: InferEntrySchema<"docs"> +} & { render(): Render[".mdx"] }; +"reference/types.mdx": { + id: "reference/types.mdx"; + slug: "reference/types"; + body: string; + collection: "docs"; + data: InferEntrySchema<"docs"> +} & { render(): Render[".mdx"] }; +"reference/union.md": { + id: "reference/union.md"; + slug: "reference/union"; + body: string; + collection: "docs"; + data: InferEntrySchema<"docs"> +} & { render(): Render[".md"] }; +"reference/wire-format.mdx": { + id: "reference/wire-format.mdx"; + slug: "reference/wire-format"; + body: string; + collection: "docs"; + data: InferEntrySchema<"docs"> +} & { render(): Render[".mdx"] }; +"tempo/TypeScript/client.mdx": { + id: "tempo/TypeScript/client.mdx"; + slug: "tempo/typescript/client"; + body: string; + collection: "docs"; + data: InferEntrySchema<"docs"> +} & { render(): Render[".mdx"] }; +"tempo/TypeScript/cloudflare-workers.mdx": { + id: "tempo/TypeScript/cloudflare-workers.mdx"; + slug: "tempo/typescript/cloudflare-workers"; + body: string; + collection: "docs"; + data: InferEntrySchema<"docs"> +} & { render(): Render[".mdx"] }; +"tempo/TypeScript/getting-started.mdx": { + id: "tempo/TypeScript/getting-started.mdx"; + slug: "tempo/typescript/getting-started"; + body: string; + collection: "docs"; + data: InferEntrySchema<"docs"> +} & { render(): Render[".mdx"] }; +"tempo/TypeScript/node-http.mdx": { + id: "tempo/TypeScript/node-http.mdx"; + slug: "tempo/typescript/node-http"; + body: string; + collection: "docs"; + data: InferEntrySchema<"docs"> +} & { render(): Render[".mdx"] }; +"tempo/faq.mdx": { + id: "tempo/faq.mdx"; + slug: "tempo/faq"; + body: string; + collection: "docs"; + data: InferEntrySchema<"docs"> +} & { render(): Render[".mdx"] }; +"tempo/index.mdx": { + id: "tempo/index.mdx"; + slug: "tempo"; + body: string; + collection: "docs"; + data: InferEntrySchema<"docs"> +} & { render(): Render[".mdx"] }; +}; + + }; + + type DataEntryMap = { + + }; + + type AnyEntryMap = ContentEntryMap & DataEntryMap; + + type ContentConfig = typeof import("../src/content/config"); +} diff --git a/docs/README.md b/docs/README.md new file mode 100644 index 00000000..5836cf7f --- /dev/null +++ b/docs/README.md @@ -0,0 +1,12 @@ +# Bebop docs + +This powers docs.bebop.sh + +You will find markdown files within src/content/docs + +Get started with + +``` +npm install +npm run dev +``` diff --git a/docs/astro.config.mjs b/docs/astro.config.mjs new file mode 100644 index 00000000..6c79657c --- /dev/null +++ b/docs/astro.config.mjs @@ -0,0 +1,84 @@ +import { defineConfig } from "astro/config"; +import starlight from "@astrojs/starlight"; + +// https://astro.build/config +export default defineConfig({ + site: "https://bebop.sh", + integrations: [ + starlight({ + title: "Bebop Docs", + favicon: "./src/assets/favicon-32.png", + expressiveCode: true, + /* head: [ + { + tag: "script", + attrs: { + src: "https://scripts.simpleanalyticscdn.com/latest.js", + defer: true, + "data-domain": "docs.bebop.sh", + }, + }, + ],*/ + + logo: { + light: "./src/assets/logo.svg", + dark: "./src/assets/logo.svg", + replacesTitle: true, + }, + + social: { + github: "https://github.com/betwixt-labs/bebop", + discord: "https://discord.gg/jVfz9sMPWv", + twitter: "https://twitter.com/BetwixtLabs", + }, + + defaultLocale: "root", + locales: { + root: { label: "English", lang: "en" }, + }, + + sidebar: [ + { + label: "Guide", + items: [ + { label: "Installation", link: "/guide/installation" }, + { + label: "Playgrounds / REPL", + link: "/guide/playground", + }, + { + label: "Getting Started (C#)", + link: "/guide/getting-started-csharp", + }, + { + label: "Getting Started (Rust)", + link: "/guide/getting-started-rust", + }, + { + label: "Getting Started (TypeScript)", + link: "/guide/getting-started-typescript", + }, + ], + }, + { + label: "Project Configuration", + autogenerate: { directory: "configuration" }, + }, + { + label: "Reference", + autogenerate: { directory: "reference" }, + }, + { + label: "Chords (Extensions)", + autogenerate: { directory: "chords" }, + }, + { + label: "Tempo (RPC)", + autogenerate: { directory: "tempo" }, + }, + { label: "Known issues", link: "/known-issues" }, + { label: "FAQ", link: "/faq" }, + ], + }), + ], +}); diff --git a/docs/package.json b/docs/package.json new file mode 100644 index 00000000..7985345d --- /dev/null +++ b/docs/package.json @@ -0,0 +1,19 @@ +{ + "name": "", + "type": "module", + "version": "0.0.1", + "scripts": { + "dev": "astro dev", + "start": "astro dev", + "build": "astro check && astro build", + "preview": "astro preview", + "astro": "astro" + }, + "dependencies": { + "@astrojs/check": "^0.3.4", + "@astrojs/starlight": "^0.15.2", + "astro": "^4.0.1", + "sharp": "^0.32.5", + "typescript": "^5.3.3" + } +} diff --git a/docs/public/favicon.svg b/docs/public/favicon.svg new file mode 100644 index 00000000..cba5ac14 --- /dev/null +++ b/docs/public/favicon.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/docs/src/assets/favicon-16.png b/docs/src/assets/favicon-16.png new file mode 100644 index 00000000..89e85c29 Binary files /dev/null and b/docs/src/assets/favicon-16.png differ diff --git a/docs/src/assets/favicon-32.png b/docs/src/assets/favicon-32.png new file mode 100644 index 00000000..a3655436 Binary files /dev/null and b/docs/src/assets/favicon-32.png differ diff --git a/docs/src/assets/houston.webp b/docs/src/assets/houston.webp new file mode 100644 index 00000000..930c1649 Binary files /dev/null and b/docs/src/assets/houston.webp differ diff --git a/docs/src/assets/logo.svg b/docs/src/assets/logo.svg new file mode 100644 index 00000000..68776c8b --- /dev/null +++ b/docs/src/assets/logo.svg @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/docs/src/content/config.ts b/docs/src/content/config.ts new file mode 100644 index 00000000..f043fb40 --- /dev/null +++ b/docs/src/content/config.ts @@ -0,0 +1,7 @@ +import { defineCollection } from "astro:content"; +import { docsSchema, i18nSchema } from "@astrojs/starlight/schema"; + +export const collections = { + docs: defineCollection({ schema: docsSchema() }), + i18n: defineCollection({ type: "data", schema: i18nSchema() }), +}; diff --git a/docs/src/content/docs/chords/chord-json.md b/docs/src/content/docs/chords/chord-json.md new file mode 100644 index 00000000..6c3e3d7f --- /dev/null +++ b/docs/src/content/docs/chords/chord-json.md @@ -0,0 +1,60 @@ +--- +title: What is a chord.json +--- + +Similar to `package.json` in JavaScript projects, `chord.json` is the manifest file for Chord extensions. + +It contains metadata about the extension, such as its name, version, and instructions on how to build it. + +It also defines contributions the extension makes, such as the generator and any decorators it provides. + +## Example + +```json +{ + "name": "typescript-template", + "private": true, + "description": "A template for chords built with typescript", + "version": "1.0.0", + "repository": "https://github.com/betwixt-labs/bebopc", + "license": "Apache-2.0", + "author": { + "name": "Betwixt Labs", + "email": "code@betwixtlabs.com" + }, + "bin": "dist/index.wasm", + "build": { + "script": "yarn build", + "compiler": "javy" + }, + "contributes": { + "generator": { + "alias": "acme", + "name": "ACME Generator" + }, + "decorators": { + "example": { + "description": "This decorator acts as an example for how to define a decorator", + "parameters": { + "floor": { + "description": "Specifies the floor of something idk", + "type": "int32", + "required": true + }, + "ceiling": { + "description": "Specifies the ceiling of something idk", + "type": "int32", + "required": false, + "default": 100 + } + }, + "targets": "all" + } + } + }, + "engine": { + "bebopc": "^3.0.0" + }, + "readme": "README.md" +} +``` diff --git a/docs/src/content/docs/chords/chordc.mdx b/docs/src/content/docs/chords/chordc.mdx new file mode 100644 index 00000000..45cd1e76 --- /dev/null +++ b/docs/src/content/docs/chords/chordc.mdx @@ -0,0 +1,18 @@ +--- +title: chordc +--- + +`chordc` is a standalone command line tool for building, testing, packaging, publishing, and installing extensions. + +Until the extension ABI is stable, `chordc` will remain seperate from `bebopc`. + +``` +Usage: chordc [command] [options] + +Commands: + build Build the chord extension + pack Package the chord extension + test Test the chord extension + install Install the chord extension + help Display help +``` \ No newline at end of file diff --git a/docs/src/content/docs/chords/installing.mdx b/docs/src/content/docs/chords/installing.mdx new file mode 100644 index 00000000..6aaefb11 --- /dev/null +++ b/docs/src/content/docs/chords/installing.mdx @@ -0,0 +1,17 @@ +--- +title: Installing +--- + +To install an extension you need to have `chordc` on your system. You can download it from the [releases page](https://github.com/betwixt-labs/bebop/releases)._createMdxContent + +Then, you simply need to run the following command: + +```sh +chordc install +``` + +Alternatively you can also install a specific version of an extension: + +```sh +chordc install @ +``` \ No newline at end of file diff --git a/docs/src/content/docs/chords/publishing.mdx b/docs/src/content/docs/chords/publishing.mdx new file mode 100644 index 00000000..a1853051 --- /dev/null +++ b/docs/src/content/docs/chords/publishing.mdx @@ -0,0 +1,6 @@ +--- +title: Publishing +--- + + +Publishing is not currently generally avaliable. If you would like to publish an extension to the registry, please join the Discord. \ No newline at end of file diff --git a/docs/src/content/docs/chords/what-are-chords.mdx b/docs/src/content/docs/chords/what-are-chords.mdx new file mode 100644 index 00000000..ad9a7791 --- /dev/null +++ b/docs/src/content/docs/chords/what-are-chords.mdx @@ -0,0 +1,28 @@ +--- +title: What are chords? +--- + +Extensions for the Bebop compiler are known as chords. They are written in any programming language that compiles to WebAssembly. + +A chord can define a new generator, decorators, and extend the behavior of other generators. + +When installing `bebopc` it also installs [chordc](../chordc), the official compiler for `chord` binaries which takes source code / WebAssembly modules and prepares them for use with `bebopc`. + +In addition, `chordc` enables [publishing](../publishing) and [installing](../installing) of chords to and from the [Chord Registry]() respectively. + +If you're interested in writing an extension for the Bebop compiler, we provide extension development kits (EDKs) for: + +- [TypeScript](https://github.com/betwixt-labs/bebop/tree/vnext/extensions/edks/typescript) +- [TinyGo](https://github.com/betwixt-labs/bebop/tree/vnext/extensions/edks/tinygo/) +- [AssemblyScript](https://github.com/betwixt-labs/bebop/tree/vnext/extensions/edks/assemblyscript) + +:::note +If you're interested in writing a chord in a language that isn't listed above, please [open an issue]() and we'll work with you to get it added. +::: + +:::caution +Compiler extensions are still experimental and the ABI is subject to change. +::: + + + diff --git a/docs/src/content/docs/configuration/bebop-config.mdx b/docs/src/content/docs/configuration/bebop-config.mdx new file mode 100644 index 00000000..22ac6fc9 --- /dev/null +++ b/docs/src/content/docs/configuration/bebop-config.mdx @@ -0,0 +1,59 @@ +--- +title: What is a bebop.json +--- + +The presence of a `bebop.json` file in a directory indicates that the directory is the root of a Bebop project. The bebop.json file specifies the root files and the compiler options required to compile schemas in the project. + +Schemas in a project are compiled in one of the following ways: +- By invoking `bebopc` with no input files, in which case the compiler searches for the `bebop.json` file starting in the current directory and continuing up the parent directory chain. +- By invoking `bebopc` with no input files and a `--config` (or just `-c`) command line option that specifies the path of a `bebopc.json` file containing the configuration. + +When input files are specified on the command line, includes in the `bebop.json` file are ignored. This is true for any configuration that can be specified in the `bebop.json` file and on the command line. + +## Examples + +- Using the `include` property +```json +{ + "include": [ + "**/*.bop" + ] +} +``` + +- Using the `exclude` property +```json +{ + "include": [ + "**/*.bop" + ], + "exclude": [ + "node_modules/**/*.bop", + ] +} +``` + +- Generating TypeScript code +```json +{ + "include": [ + "**/*.bop" + ], + "exclude": [ + "node_modules/**/*.bop", + ], + "generators": { + "ts": { + "outFile": "dist/bops.gen.ts" + } + } +} +``` + + +## Bebop Config Reference +To learn more about the configuration options in the [bebop.json Reference](/reference/bebop-json/). + +## Schema + +The bebop.json Schema can be found at the JSON Schema Store. \ No newline at end of file diff --git a/docs/src/content/docs/faq.mdx b/docs/src/content/docs/faq.mdx new file mode 100644 index 00000000..d482b7ba --- /dev/null +++ b/docs/src/content/docs/faq.mdx @@ -0,0 +1,3 @@ +--- +title: FAQ +--- \ No newline at end of file diff --git a/docs/src/content/docs/guide/getting-started-csharp.mdx b/docs/src/content/docs/guide/getting-started-csharp.mdx new file mode 100644 index 00000000..41b413e1 --- /dev/null +++ b/docs/src/content/docs/guide/getting-started-csharp.mdx @@ -0,0 +1,87 @@ +--- +title: Getting started (C#) +--- + +import {Tabs, TabItem, LinkCard} from "@astrojs/starlight/components"; + +## Supported Runtimes +- .NET Framework 4.7.2 +- .NET Framework 4.8 +- .NET Core 3.1 +- .NET 5+ + +## Install the Runtime +To install the Bebop .NET runtime you can use the following commands: + +``` +dotnet add package bebop +``` + +| Package | NuGet Stable | Downloads | +| :--------------------------------------------- | :------------------------------------------------------------------------------------------ | :------------------------------------------------------------------------------------------- | +| [bebop](https://www.nuget.org/packages/bebop/) | [![bebop](https://img.shields.io/nuget/v/bebop.svg)](https://www.nuget.org/packages/bebop/) | [![bebop](https://img.shields.io/nuget/dt/bebop.svg)](https://www.nuget.org/packages/bebop/) | + +## Install the Compiler Tools +To install the Bebop Compiler tools you can use the following command: + +``` +dotnet add package bebop-tools +``` +| Package | NuGet Stable | Downloads | +| :--------------------------------------------------------- | :------------------------------------------------------------------------------------------------------------ | :------------------------------------------------------------------------------------------------------------- | +| [bebop-tools](https://www.nuget.org/packages/bebop-tools/) | [![bebop-tools](https://img.shields.io/nuget/v/bebop-tools.svg)](https://www.nuget.org/packages/bebop-tools/) | [![bebop-tools](https://img.shields.io/nuget/dt/bebop-tools.svg)](https://www.nuget.org/packages/bebop-tools/) | + +## Configuring the Compiler Tools + +Inside of your project file add a new `ItemGroup` + +```xml + + + +``` + +When the `` item group is present in your project all schemas that match the provided `Include` path are compiled in accordance with the output parameters defined whenever you build your project (meaning that schema changes reflect immediately in your project.) + +Any issues encountered while compiling your schemas can now also be inspected from the error list. +![](https://i.imgur.com/H5jTYtJ.png) + +## Using Bebop Mirroring + +Mirroring is a Bebop feature supported by the .NET runtime implementation that allows for records to be access and handled dynamically. For instance, you can define a class as a `RecordHandler` and bind it's methods to be invoked whenever a record of a certain type is decoded. + +It takes advantage of the [opcode](../reference/decorators#opcode) decorator to determine which method to invoke. + +```csharp + [RecordHandler] + public class ExampleHandler + { + + [BindRecord(typeof(BebopRecord))] + public async Task HandleChat(object state, ChatMessage chat) + { + ... + } + } +``` +Arbitrary data can then be decoded dynamically by leveraging the records opcode, and the method bound to it is fired. + +```csharp +// fired when a message is received from the network. +public void OnMessage(byte[] data) { + // decodes our network messages + var networkMessage = NetworkMessage.Decode(data); + // decodes and invokes the handler (if any) for the decoded record + // if the record is ChatMessage then HandleChat is invoked.. + BebopMirror.HandleRecord(networkMessage.IncomingRecordData, networkMessage.IncomingOpcode, stateObject); +} +``` +:::note +- **If your `RecordHandler` is non-static the Bebop runtime will create a new instance of that class and hold onto the reference.** +- **Bound methods currently cannot return any values and use an event driven pattern. Invoked methods do not block.** +- **Mirroring uses reflection and may not be AOT friendly** +::: +:::caution +Mirroring is a feature that will likely be removed in the future in favor of a C# implementation of [Tempo](../tempo). +::: + diff --git a/docs/src/content/docs/guide/getting-started-rust.mdx b/docs/src/content/docs/guide/getting-started-rust.mdx new file mode 100644 index 00000000..c2652a54 --- /dev/null +++ b/docs/src/content/docs/guide/getting-started-rust.mdx @@ -0,0 +1,69 @@ +--- +title: Getting started (Rust) +--- + +import {Tabs, TabItem, LinkCard} from "@astrojs/starlight/components"; + +| Package | Cargo Latest | Downloads | +| :--------------------------------------------- | :------------------------------------------------------------------------------------------ | :------------------------------------------------------------------------------------------- | +| [bebop](https://crates.io/crates/bebop) | [![bebop](https://img.shields.io/crates/dv/bebop)](https://crates.io/crates/bebop) | ![bebop](https://img.shields.io/crates/d/bebop) +| [bebop-tools](https://crates.io/crates/bebop-tools) | [![bebop](https://img.shields.io/crates/dv/bebop-tools)](https://crates.io/crates/bebop-tools) | ![bebop](https://img.shields.io/crates/d/bebop-tools) + + +There are two packages to be aware of, the Rust runtime [`bebop`](https://crates.io/crates/bebop) and the compiler-helper [`bebop-tools`](https://crates.io/crates/bebop-tools). + +So to get started, add the `bebop` package to your `dependencies` and add `bebop-tools` to your `build-dependencies` if you plan to use a cargo build script for generating the Rust code. Next, add the necessary steps to your build script to build the schema, this is an example buildscript: + +```rust +// build.rs +use bebop_tools as bebop; + +fn main() { + // download the bebop binary automatically and cache it into your target directory + // it will automatically download the same version as the package you installed + bebop::download_bebopc( + PathBuf::from("target").join("bebopc"), + ); + // build all `.bop` schemas in `schemas` dir and make a new module `generated` in `src` with all of them. + bebop::build_schema_dir("schemas", "src/generated"); +} +``` + +Right now `bebop-tools` is pretty bare-bones as far as what it supports, but at least with auto-downloading it should help with automation—if not do it for you. + +Now assuming you have the schema +```c +// schemas/mystruct.bop +struct MyStruct { + int32 a; + int32 b; +} +``` + +To actually build the generated file run `cargo build` and you should see `src/generated/mystruct.rs` is created automatically. You may want to add the generated directory to your git ignore. Also be aware that any changes to files in generated will be wiped out on _every single_ build. Or if you have an IDE that is constantly running in the background like I do, after any change you save... + +Also `src/generated/mod.rs` is generated automatically for you and will publicly declare all the schemas as modules. + +In your code, you can now write something like this: +```rust +// main.rs +pub mod generated; + +use bebop::prelude::*; +use generated::mystruct::MyStruct; + +fn main() { + let mut buf = Vec::with_capacity(MyStruct::SERIALIZED_SIZE); + let s1 = MyStruct { + a: 123, + b: -6232 + }; + s1.serialize(&mut buf).unwrap(); + let s2 = MyStruct::deserialize(&buf).unwrap(); + assert_eq!(s1, s2); +} +``` + +Pro-tip: Check out the `owned` module which is generated behind a feature flag. If you enable `bebop-owned-all` or the appropriate namespace flag (only if you generated with a namespace) you can then access versions of all Records which do not borrow the data but simply take ownership/clone the data. These can be useful if you want to use bebop types throughout your codebase and not just for serialization. + +That should be enough to get you kick-started. Checkout the crate docs for the runtime [here](https://docs.rs/bebop/latest/bebop/) and pay extra close attention to the [`Record`](https://docs.rs/bebop/latest/bebop/trait.Record.html) trait which is the main bebop sterilization definition. \ No newline at end of file diff --git a/docs/src/content/docs/guide/getting-started-typescript.mdx b/docs/src/content/docs/guide/getting-started-typescript.mdx new file mode 100644 index 00000000..ae6e6db2 --- /dev/null +++ b/docs/src/content/docs/guide/getting-started-typescript.mdx @@ -0,0 +1,85 @@ +--- +title: Getting started (TypeScript) +--- + +import {Tabs, TabItem, LinkCard} from "@astrojs/starlight/components"; + +| Package | Downloads +| :--------------------------------------------- | :------------------------------------------------------------------------------------------ | +| [bebop](https://www.npmjs.com/package/bebop) | ![bebop](https://img.shields.io/npm/dt/bebop) +| [bebop-tools](https://www.npmjs.com/package/bebop) | ![bebop](https://img.shields.io/npm/dt/bebop-tools) + + +First, install the Bebop runtime: + + + ```bash + npm add bebop + ``` + + + + ```bash + yarn add bebop + ``` + + + +Followed by the Bebop compiler: + + + ```bash + npm add bebop-tools + ``` + + + + ```bash + yarn add bebop-tools + ``` + + + +Next, init your project: + + + ```bash + npx bebopc init + ``` + + + + ```bash + yarn bebopc init + ``` + + + +Open the generated `bebop.json` file and add a TypeScript generator: +```json +{ + "include": [ + "**/*.bop" + ], + "generators": { + "ts": { + "outFile": "src/bops.gen.ts" + } + } +} +``` + +Now, you can compile your Bebop schemas to TypeScript: + + + ```bash + npx bebopc build + ``` + + + + ```bash + yarn bebopc build + ``` + + \ No newline at end of file diff --git a/docs/src/content/docs/guide/getting-started.mdx b/docs/src/content/docs/guide/getting-started.mdx new file mode 100644 index 00000000..1dfc58d2 --- /dev/null +++ b/docs/src/content/docs/guide/getting-started.mdx @@ -0,0 +1,60 @@ +--- +title: Getting started +--- + +import {Tabs, TabItem, LinkCard} from "@astrojs/starlight/components"; + +## What is Bebop? + +**Bebop** is a cutting-edge data serialization format, crafted for **speed**, **safety**, and **simplicity**. With the Bebop schema language, you can create highly efficient code across various programming languages, forming the backbone of your application's architecture. + +## Why Choose Bebop? + +Bebop stands out in the crowded field of serialization formats. It's engineered for diverse contexts - from intricate embedded systems to expansive distributed backends and dynamic front-end web applications. Key highlights include: + +- **Blazing Fast**: Bebop is 10-100x faster than JSON, Protocol Buffers, and other alternatives, making it a turbocharger for your data handling. +- **Versatile and Safe**: Whether you're working on a resource-constrained embedded system or a complex cloud-based solution, Bebop ensures top-notch performance without compromising on safety. +- **Seamless Interoperability**: Effortlessly integrate with various languages and systems, bridging gaps in your development process. +- **Developer-Centric Design**: We prioritize your experience as a developer, making Bebop intuitive, easy to learn, and a joy to use. + +![Bebop in Action](https://user-images.githubusercontent.com/1297077/235745675-fc8a18e2-361f-4b7b-b9c9-47155e511b0a.png) + +Born in the cloud gaming arena, where every millisecond is crucial, Bebop has evolved into a versatile tool. It's not just a serialization format; it's a key to unlocking new potentials in software development. Whether you're building the next groundbreaking application or optimizing an existing system, Bebop is your go-to solution. + +#### Supported Languages + +- C# +- TypeScript/JavaScript +- Rust +- Dart +- Python +- C++ +- Go + +Is your favorite programming language missing? You can add it via our extension framework. See the [extensions](/guide/extensions/) guide for more information. + +## Quickstart + +Please do try and read this guide, but if you're in a hurry and want to get +started quickly: + + + + ```bash + bash -c "$(curl https://bebop.sh)" + ``` + + + + ```powershell + irm https://bebop.sh | iex + ``` + + + + + diff --git a/docs/src/content/docs/guide/installation.mdx b/docs/src/content/docs/guide/installation.mdx new file mode 100644 index 00000000..eefe5a10 --- /dev/null +++ b/docs/src/content/docs/guide/installation.mdx @@ -0,0 +1,79 @@ +--- +title: Installation +--- + +import {Tabs, TabItem, LinkCard} from "@astrojs/starlight/components"; + + +To install bebopc globally, run the following command: + + + + ```bash + bash -c "$(curl https://bebop.sh)" + ``` + + + + ```powershell + irm https://bebop.sh | iex + ``` + + + + + +Alternatively, you can install it locally to your project: + + + ```bash + npm add bebop-tools + ``` + + + + ```bash + yarn add bebop-tools + ``` + + + + ```bash + dotnet add package bebop-tools + ``` + + + + ```bash + cargo add bebop-tools + ``` + + + + +:::note +Installing `bebopc` will also install `chordc` which is used for extension development. +::: + + +## VSCode Extension + +You can install the VSCode extension by clicking one of the buttons below: +

+ +

+

+ +

+We also cross-publish to [Open VSX](https://open-vsx.org/extension/betwixt/bebop-lang) if you prefer to install from there. + +:::note +The VSCode extension ships with `bebopc` and its version is tied to the version of the extension. +::: + + +For information on getting started in a specific language, see: + + + +--- diff --git a/docs/src/content/docs/guide/playground.mdx b/docs/src/content/docs/guide/playground.mdx new file mode 100644 index 00000000..50471bf8 --- /dev/null +++ b/docs/src/content/docs/guide/playground.mdx @@ -0,0 +1,23 @@ +--- +title: Playgrounds / REPL +--- +import { Card, CardGrid } from '@astrojs/starlight/components'; + +Writing your first _bop_ isn't a daunting task. In fact, it's quite easy. + +Below are a number of ways you can experiment with Bebop, preview code generation, and get an understanding of how it might integrate into your projects. + + +## Playgrounds + +Playgrounds allow you to run `bebopc` **in your browser, without installing anything!** + +They are mostly useful for: +- Testing Bebop +- Reporting bugs + +Choose one of the available options below: + +The official Bebop playground is the best way to get started with Bebop. It's an online version of `bebopc` that allows you to write Bebop IDL and see the generated code right away without installing anything. + +[Open the Bebop Playground](https://play.bebop.sh/) \ No newline at end of file diff --git a/docs/src/content/docs/index.mdx b/docs/src/content/docs/index.mdx new file mode 100644 index 00000000..ab48dfb5 --- /dev/null +++ b/docs/src/content/docs/index.mdx @@ -0,0 +1,61 @@ +--- +title: Getting started +--- + +import {Tabs, TabItem, LinkCard} from "@astrojs/starlight/components"; + +## What is Bebop? + +**Bebop** is a cutting-edge data serialization format, crafted for **speed**, **safety**, and **simplicity**. With the Bebop schema language, you can create highly efficient code across various programming languages, forming the backbone of your application's architecture. + +## Why Choose Bebop? + +Bebop stands out in the crowded field of serialization formats. It's engineered for diverse contexts - from intricate embedded systems to expansive distributed backends and dynamic front-end web applications. Key highlights include: + +- **Blazing Fast**: Bebop is 10-100x faster than JSON, Protocol Buffers, and other alternatives, making it a turbocharger for your data handling. +- **Versatile and Safe**: Whether you're working on a resource-constrained embedded system or a complex cloud-based solution, Bebop ensures top-notch performance without compromising on safety. +- **Seamless Interoperability**: Effortlessly integrate with various languages and systems, bridging gaps in your development process. +- **Developer-Centric Design**: We prioritize your experience as a developer, making Bebop intuitive, easy to learn, and a joy to use. + +![Bebop in Action](https://user-images.githubusercontent.com/1297077/235745675-fc8a18e2-361f-4b7b-b9c9-47155e511b0a.png) + +Born in the cloud gaming arena, where every millisecond is crucial, Bebop has evolved into a versatile tool. It's not just a serialization format; it's a key to unlocking new potentials in software development. Whether you're building the next groundbreaking application or optimizing an existing system, Bebop is your go-to solution. + +#### Supported Languages + +- C# +- TypeScript/JavaScript +- Rust +- Dart +- Python +- C++ +- Go + +Is your favorite programming language missing? You can add it via our extension framework. See the [extensions](/guide/extensions/) guide for more information. + +## Quickstart + +Please do try and read this guide, but if you're in a hurry and want to get +started quickly: + + + + ```bash + bash -c "$(curl https://bebop.sh)" + ``` + + + + ```powershell + irm https://bebop.sh | iex + ``` + + + + + + diff --git a/docs/src/content/docs/integrations.mdx b/docs/src/content/docs/integrations.mdx new file mode 100644 index 00000000..e3b35747 --- /dev/null +++ b/docs/src/content/docs/integrations.mdx @@ -0,0 +1,5 @@ +--- +title: Integrations +--- + +TODO diff --git a/docs/src/content/docs/known-issues.mdx b/docs/src/content/docs/known-issues.mdx new file mode 100644 index 00000000..f5fd34b9 --- /dev/null +++ b/docs/src/content/docs/known-issues.mdx @@ -0,0 +1,4 @@ +--- +title: Known Issues +--- + TODO \ No newline at end of file diff --git a/docs/src/content/docs/reference/bebop-json.md b/docs/src/content/docs/reference/bebop-json.md new file mode 100644 index 00000000..f13f4fb3 --- /dev/null +++ b/docs/src/content/docs/reference/bebop-json.md @@ -0,0 +1,244 @@ +--- +title: bebop.json +--- + +A `bebop.json` file in a directory indicates that the directory is the root of a Bebop project. + +This page covers all of the different options available inside a `bebop.json` file. There are five main sections: + +- The [root fields](#root-fields) for letting the compiler know what files are available +- The [generators](#generators---generators) section for defining how to generate code +- The [watchOptions](#watch-options---watchoptions) fields, for tweaking the watch mode +- The [extensions](#extensions---extensions) section, for defining custom extensions + +## Root Fields + +### Include - `include` +Specifies an array of filenames or patterns to include in the compiler. These filenames are resolved relative to the directory containing the bebop.json file. + +```json +{ + "include": [ + "src/**/*.bop" + ] +} +``` + +Which would include all `.bop` files in the `src` directory and all subdirectories. + +``` +├── scripts ⨯ +│ ├── lint.ts ⨯ +│ ├── update_deps.ts ⨯ +│ └── utils.ts ⨯ +├── src ✓ +│ ├── schemas ✓ +│ │ ├── client.bop ✓ +│ │ └── server.bop ✓ +├── tests ⨯ +│ ├── dummy.bop ⨯ +``` +`include` and `exclude` support wildcard characters to make glob patterns: +- `*` matches zero or more characters (excluding directory separators) +- `?` matches any one character (excluding directory separators) +- `**/` matches any directory nested to any level + +If the last path segment in a pattern does not contain a file extension or wildcard character, then it is treated as a directory, and files with supported extensions inside that directory are included (e.g. `.bop`). + +### Exclude - `exclude` + +Specifies an array of filenames or patterns that should be skipped when resolving [include](#include---include). + +:::note +`exclude` _only_ changes which files are included as a result of the include setting. A file specified by exclude can still become part of your codebase due to an import statement in your schema. +::: + +### No Warn - `noWarn` +Specifies an array of warning codes to silence during compilation. + +```json +{ + "noWarn": [ + 1000 + ] +} +``` + +### No Emit - `noEmit` +Disable emitting files from a compilation. + +```json +{ + "noEmit": true +} +``` + + +## Generators - `generators` + +Specifies code generators to use for compilation. + +Built-in generators alias are: +- `ts` - Generates TypeScript code +- `cs` - Generates C# code +- `cpp` - Generates C++ code +- `py` - Generates Python code +- `rust` - Generates Rust code +- `dart` - Generates Dart code + + + +### Out File - `outFile` + +Specifies the output file for the generated code. This field is **required** for all generators. + +```json +{ + "generators": { + "ts": { + "outFile": "bops.gen.ts" + } + } +} +``` + +### Services - `services` + +By default, the compiler generates a concrete client and a service base class. This property can be used to limit compiler asset generation. + +Valid options are: +- `none` - Do not generate any service assets +- `client` - Generate a concrete client +- `server` - Generate a server base classes +- `both` - Generate both client and server assets + +```json +{ + "generators": { + "ts": { + "services": "both" + } + } +} +``` + +### Emit Notice - `emitNotice` +Specify if the code generator should produce a notice stating code was auto-generated. (default `true`). + +```json +{ + "generators": { + "ts": { + "emitNotice": false + } + } +} +``` + +### Emit Binary Schema - `emitBinarySchema` +Specify if the code generator should emit a binary schema in the output file that can be used for dynamic (de)serialization. (default `true`). + +```json +{ + "generators": { + "ts": { + "emitBinarySchema": false + } + } +} +``` + +### Namespace - `namespace` + +Specify a namespace for the generated code. Casing will be adjusted to match the target language. + +```json +{ + "generators": { + "ts": { + "namespace": "MyNamespace" + } + } +} +``` + +### Options - `options` + +Specify custom options for the code generator. + +```json +{ + "generators": { + "cs": { + "options": { + "languageVersion": "8.0" + } + } + } +} +``` + +## Watch Options - `watchOptions` + +Settings for the watch mode of bebopc. + +### Exclude Files - `excludeFiles` + +Remove a list of files from the watch mode's processing. + +```json +{ + "watchOptions": { + "excludeFiles": [ + "test/**/*.bop" + ] + } +} +``` + +### Exclude Directories - `excludeDirectories` +Remove a list of directories from the watch process. + +```json +{ + "watchOptions": { + "excludeDirectories": [ + "test" + ] + } +} +``` + + +## Extensions - `extensions` +An object of extensions the compiler should load. + +```json +{ + "extensions": { + "custom-generator": "1.2.3" + } +} +``` + + +## Full Example + +```json +{ + "include": [ + "src/**/*.bop" + ], + "exclude": [ + "src/**/test.bop" + ], + "generators": { + "ts": { + "outFile": "bops.gen.ts", + "services": "both", + "emitNotice": true, + "emitBinarySchema": false + } + } +} +``` \ No newline at end of file diff --git a/docs/src/content/docs/reference/bebopc.md b/docs/src/content/docs/reference/bebopc.md new file mode 100644 index 00000000..79d00a30 --- /dev/null +++ b/docs/src/content/docs/reference/bebopc.md @@ -0,0 +1,32 @@ +--- +title: bebopc +--- +``` +Description: + The Bebop schema language compiler + +Usage: + bebopc [command] [options] + +Options: + -?, -h, --help Show help and usage information + --version Show version information + -df, --diagnostic-format Specifies the format which diagnostic messages are printed in. [default: Enhanced] + -c, --config Compile the project given the path to its configuration file. [default: Core.Meta.BebopConfig] + -i, --include Specifies an array of filenames or patterns to include in the compiler. These filenames are resolved relative to the directory + containing the bebop.json file. + -e, --exclude Specifies an array of filenames or patterns that should be skipped when resolving include. + --init Initializes a Bebop project and creates a bebop.json file. + --list-schemas-only Print names of schemas that are part of the compilation and then stop processing. + --show-config Print the final configuration instead of building. + --locale Set the language of the messaging from bebopc. This does not affect emit. + --trace Enable tracing of the compiler. + +Commands: + build Build schemas into one or more target languages. + watch Watch input files. + convert Convert schema files from one format to another. + +``` + +To get help on a specific command, use `bebopc --help`. \ No newline at end of file diff --git a/docs/src/content/docs/reference/binary-schema.mdx b/docs/src/content/docs/reference/binary-schema.mdx new file mode 100644 index 00000000..bff851eb --- /dev/null +++ b/docs/src/content/docs/reference/binary-schema.mdx @@ -0,0 +1,412 @@ +--- +title: Binary Schema +--- + +Simply, a binary schema is the parsed textual schema in a condensed binary format that enables dynamic encoding and decoding of binary records at runtime. + +Bebop already offers backwards and forwards compatibility for data. However, sometimes an application needs to be able to encode and decode data in a context where it doesn't have access to the source schema. For example, observation and dev tooling that needs to be able to inspect ingested request and responses; the tooling itself does not have access to the source textual schema and thus cannot generate any code for [records](../records) it receives. + +Without the compiler emitted code an application cannot make any guarantees on type safety until it hits an integration (which will throw an error) and is something Bebop should avoid altogether. + +## Implementation + +The compiler emits a static byte array into generated code that represent the textual schemas that created it; this data can be appended to request/responses and each runtime implements a method to parse that binary schema which can then be used to encode and decode records. + +The encoded schema looks like: + +```hex +0000-0010: 03 05 00 00-00 52 69 67-68 74 73 00-04 01 66 6c .....Rig hts...fl +0000-0020: 61 67 73 00-00 fe ff ff-ff 01 01 00-00 00 04 44 ags..... .......D +0000-0030: 65 66 61 75-6c 74 00 00-00 55 73 65-72 00 00 01 efault.. .User... +0000-0040: 4d 6f 64 00-00 02 41 64-6d 69 6e 00-00 03 41 70 Mod...Ad min...Ap +0000-0050: 69 52 65 71-75 65 73 74-00 01 00 00-0c 00 00 00 iRequest ........ +0000-0060: 00 02 75 72-69 00 f5 ff-ff ff 00 74-72 61 63 65 ..uri... ...trace +0000-0070: 00 f9 ff ff-ff 00 44 65-66 61 75 6c-74 52 65 73 ......De faultRes +0000-0080: 70 6f 6e 73-65 00 01 00-00 05 00 00-00 00 02 72 ponse... .......r +0000-0090: 69 67 68 74-73 00 00 00-00 00 00 75-73 65 72 6e ights... ...usern +0000-00a0: 61 6d 65 00-f5 ff ff ff-00 45 72 72-6f 72 52 65 ame..... .ErrorRe +0000-00b0: 73 70 6f 6e-73 65 00 02-00 05 00 00-00 03 72 65 sponse.. ......re +0000-00c0: 61 73 6f 6e-00 f5 ff ff-ff 00 01 63-6f 64 65 00 ason.... ...code. +0000-00d0: fb ff ff ff-00 02 69 64-00 f4 ff ff-ff 01 64 65 ......id ......de +0000-00e0: 70 72 65 63-61 74 65 64-00 01 72 65-61 73 6f 6e precated ..reason +0000-00f0: 00 f5 ff ff-ff 6a 75 73-74 20 62 65-63 61 75 73 .....jus t.becaus +0000-0100: 65 00 03 41-70 69 52 65-73 70 6f 6e-73 65 00 03 e..ApiRe sponse.. +0000-0110: 00 0a 00 00-00 02 01 02-00 00 00 02-03 00 00 00 ........ ........ +0000-0120: 01 00 00 00-47 72 65 65-74 65 72 00-00 01 00 00 ....Gree ter..... +0000-0130: 00 73 61 79-48 65 6c 6c-6f 00 00 00-01 00 00 00 .sayHell o....... +0000-0138: 04 00 00 00-55 f6 fe 4d ....U..M +``` + +When read, you end up with a new intemediary representation of the schema that can be used to encode and decode records: +```json +{ + "bebopVersion": 3, + "definitions": { + "Rights": { + "index": 0, + "name": "Rights", + "isBitFlags": true, + "kind": 4, + "decorators": { + "flags": { + "arguments": {} + } + }, + "minimalEncodeSize": 1, + "baseType": -2, + "members": { + "Default": { + "name": "Default", + "decorators": {}, + "value": 0 + }, + "User": { + "name": "User", + "decorators": {}, + "value": 1 + }, + "Mod": { + "name": "Mod", + "decorators": {}, + "value": 2 + }, + "Admin": { + "name": "Admin", + "decorators": {}, + "value": 3 + } + } + }, + "ApiRequest": { + "index": 1, + "name": "ApiRequest", + "kind": 1, + "decorators": {}, + "isMutable": false, + "minimalEncodeSize": 12, + "isFixedSize": false, + "fields": { + "uri": { + "name": "uri", + "typeId": -11, + "fieldProperties": { + "type": "scalar" + }, + "decorators": {}, + "constantValue": null + }, + "trace": { + "name": "trace", + "typeId": -7, + "fieldProperties": { + "type": "scalar" + }, + "decorators": {}, + "constantValue": null + } + } + }, + "DefaultResponse": { + "index": 2, + "name": "DefaultResponse", + "kind": 1, + "decorators": {}, + "isMutable": false, + "minimalEncodeSize": 5, + "isFixedSize": false, + "fields": { + "rights": { + "name": "rights", + "typeId": 0, + "fieldProperties": { + "type": "scalar" + }, + "decorators": {}, + "constantValue": null + }, + "username": { + "name": "username", + "typeId": -11, + "fieldProperties": { + "type": "scalar" + }, + "decorators": {}, + "constantValue": null + } + } + }, + "ErrorResponse": { + "index": 3, + "minimalEncodeSize": 5, + "name": "ErrorResponse", + "kind": 2, + "decorators": {}, + "fields": { + "reason": { + "name": "reason", + "typeId": -11, + "fieldProperties": { + "type": "scalar" + }, + "decorators": {}, + "constantValue": 1 + }, + "code": { + "name": "code", + "typeId": -5, + "fieldProperties": { + "type": "scalar" + }, + "decorators": {}, + "constantValue": 2 + }, + "id": { + "name": "id", + "typeId": -12, + "fieldProperties": { + "type": "scalar" + }, + "decorators": { + "deprecated": { + "arguments": { + "reason": { + "typeId": -11, + "value": "just because" + } + } + } + }, + "constantValue": 3 + } + } + }, + "ApiResponse": { + "index": 4, + "name": "ApiResponse", + "kind": 3, + "decorators": {}, + "minimalEncodeSize": 10, + "branchCount": 2, + "branches": [ + { + "discriminator": 1, + "typeId": 2 + }, + { + "discriminator": 2, + "typeId": 3 + } + ] + } + }, + "services": { + "Greeter": { + "name": "Greeter", + "decorators": {}, + "methods": { + "sayHello": { + "name": "sayHello", + "decorators": {}, + "methodType": 0, + "requestTypeId": 1, + "responseTypeId": 4, + "id": 1308554837 + } + } + } + } +} +``` + +:::note +The compiler omits any `const` definitions to avoid leaking sensitive information. +::: + +## Wire Format + +- **Schema Version**: 1-byte integer (`byte`). + +- **Definition Count**: 4-byte integer (`uint32`). + + For each defined type: + + - **Definition Name**: UTF-8 encoded string. It's null terminated. + + - **Kind**: 1-byte integer (`byte`). Where 1=Struct, 2=Message, 3=Union, 4=Enum. + + - ** Decorators**: see [Decorators](#decorators). + + - **Definition**: Depends on the kind. +- **Service Count**: 1-byte integer (`byte`). + + For each service: + + - **Service Name**: UTF-8 encoded string. It's null terminated. + + - **Decorators**: see [Decorators](#decorators). + + - **Methods Count**: 1-byte integer (`byte`). + + For each method: + + - **Method Name**: UTF-8 encoded string. It's null terminated. + + - **Decorators**: see [Decorators](#decorators). + + - **Method Type**: 1-byte integer (`byte`). Where 0=Unary, 1=Server Streaming, 2=Client Streaming, 3=Duplex Streaming. + + - **Request Type ID**: 4-byte integer (`int32`). + + - **Response Type ID**: 4-byte integer (`int32`). + + - **Method ID**: 4-byte integer (`uint32`). + +### Decorators + +- **Decorators**: It begins with the count as a 1-byte integer (`byte`). Each decorator is a sequence of: + + - **Name**: a null terminated UTF-8 encoded string. + + - **Argument Count**: 1-byte integer (`byte`). + + - **Arguments**: For each argument: + + - **Name**: a null terminated UTF-8 encoded string. + + - **Type**: 4-byte signed integer (`int32`). + + - **Value**: Depends on the type. + +### Struct +A struct definition is encoded as follows: +- **Is Mutable**: 1-byte boolean. +- **Minimal Encode Size**: 4-byte integer (`int32`). +- **Is Fixed Size**: 1-byte boolean. + +- **Fields Count**: 1-byte integer (`byte`). + + For each field: + + - **Name**: a null terminated UTF-8 encoded string. + + - **Type ID**: 4-byte integer (`int32`). + + - **Decorators**: see [Decorators](#decorators). + +### Message +- **Minimal Encode Size**: 4-byte integer (`int32`). + +- **Fields Count**: 1-byte integer (`byte`). + For each field: + + - **Name**: a null terminated UTF-8 encoded string. + + - **Type ID**: 4-byte integer (`int32`). + + - **Decorators**: see [Decorators](#decorators). + + - **Constant Value**: 1-byte integer (`byte`). This is the index of the field. + +### Union +- **Minimal Encode Size**: 4-byte integer (`int32`). +- **Branch Count**: 1-byte integer (`byte`). + + For each branch: + + - **Discriminator**: 1-byte integer (`byte`). + + - **Type ID**: 4-byte integer (`int32`). + +### Enum + +- **Base Type**: 4-byte integer (`int32`). +- **Is Bit Flags**: 1-byte boolean. +- **Minimal Encode Size**: 4-byte integer (`int32`). + +- **Member Count**: 1-byte integer (`byte`). + + For each member: + + - **Name**: a null terminated UTF-8 encoded string. + + - **Decorators**: see [Decorators](#decorators). + + - **Value**: depends on the base type. + + +### Type ID Encoding + +Type IDs are encoded as follows: + +- **Defined Type**: The index of the type definition in the defined types list. +- **Scalar Type**: The bitwise negation of the index of the BaseType in the list of base types. This effectively results in: + - `Bool = -1` + - `Byte = -2` + - `UInt16 = -3` + - `Int16 = -4` + - `UInt32 = -5` + - `Int32 = -6` + - `UInt64 = -7` + - `Int64 = -8` + - `Float32 = -9` + - `Float64 = -10` + - `String = -11` + - `Guid = -12` + - `Date = -13` +- **Array Type**: `-14` +- **Map Type**: `-15` + + + Type IDs correspond to the type of a field, a member of an enum, the type of a branch in a union, or the return. + +## Usage + +```typescript +import { BebopRuntimeError, BinarySchema, BebopJson } from "bebop"; + +const schemaData = new Uint8Array([ + 3, 5, 0, 0, 0, 82, 105, 103, 104, 116, 115, 0, 4, 1, 102, 108, 97, 103, 115, + 0, 0, 254, 255, 255, 255, 1, 1, 0, 0, 0, 4, 68, 101, 102, 97, 117, 108, 116, + 0, 0, 0, 85, 115, 101, 114, 0, 0, 1, 77, 111, 100, 0, 0, 2, 65, 100, 109, 105, + 110, 0, 0, 3, 65, 112, 105, 82, 101, 113, 117, 101, 115, 116, 0, 1, 0, 0, 12, + 0, 0, 0, 0, 2, 117, 114, 105, 0, 245, 255, 255, 255, 0, 116, 114, 97, 99, 101, + 0, 249, 255, 255, 255, 0, 68, 101, 102, 97, 117, 108, 116, 82, 101, 115, 112, + 111, 110, 115, 101, 0, 1, 0, 0, 5, 0, 0, 0, 0, 2, 114, 105, 103, 104, 116, + 115, 0, 0, 0, 0, 0, 0, 117, 115, 101, 114, 110, 97, 109, 101, 0, 245, 255, + 255, 255, 0, 69, 114, 114, 111, 114, 82, 101, 115, 112, 111, 110, 115, 101, 0, + 2, 0, 5, 0, 0, 0, 3, 114, 101, 97, 115, 111, 110, 0, 245, 255, 255, 255, 0, 1, + 99, 111, 100, 101, 0, 251, 255, 255, 255, 0, 2, 105, 100, 0, 244, 255, 255, + 255, 1, 100, 101, 112, 114, 101, 99, 97, 116, 101, 100, 0, 1, 114, 101, 97, + 115, 111, 110, 0, 245, 255, 255, 255, 106, 117, 115, 116, 32, 98, 101, 99, 97, + 117, 115, 101, 0, 3, 65, 112, 105, 82, 101, 115, 112, 111, 110, 115, 101, 0, + 3, 0, 10, 0, 0, 0, 2, 1, 2, 0, 0, 0, 2, 3, 0, 0, 0, 1, 0, 0, 0, 71, 114, 101, + 101, 116, 101, 114, 0, 0, 1, 0, 0, 0, 115, 97, 121, 72, 101, 108, 108, 111, 0, + 0, 0, 1, 0, 0, 0, 4, 0, 0, 0, 85, 246, 254, 77, +]); + +const recordData = new Uint8Array([ + 9, 0, 0, 0, 1, 3, 4, 0, 0, 0, 116, 101, 115, 116, +]); + +const schema = new BinarySchema(schemaData); +schema.get(); +// dump the AST +console.log(JSON.stringify(ast, BebopJson.replacer, 2)); + +// reading a record dynamically +const record = schema.reader.read("ApiResponse", recordData); +console.log(record.discriminator); +if (record.discriminator === 1) { + console.log(record.value.rights); + console.log(record.value.username); +} + +// writing a record dynamically +const data = schema.writer.write("ApiResponse", record); +console.log(data === recordData); // true +``` + + +## Tradeoffs + +Type inference is lost. The objects you work with will be dynamic; mistakes can be caught on the client-side still via proxies or other means, but it is up to the user to know the type. This is fine as this method is only used in extreme edge cases. diff --git a/docs/src/content/docs/reference/comments.md b/docs/src/content/docs/reference/comments.md new file mode 100644 index 00000000..39c12860 --- /dev/null +++ b/docs/src/content/docs/reference/comments.md @@ -0,0 +1,19 @@ +--- +title: Comments +--- + +As in many C-like languages, `//` starts a comment until the end of the line, whereas `/*` and `*/` delimit a block comment. + +If a block comment is placed directly before a definition, field, or method declaration, it is considered a documentation comment. The contents of such a comment are parsed and included in the generated documentation. + +```go +// This is a line comment. + +/* This is a block comment. */ +struct Point { + /* the x coordinate */ + uint32 x; + /* the y coordinate */ + uint32 y; +} +``` \ No newline at end of file diff --git a/docs/src/content/docs/reference/const.mdx b/docs/src/content/docs/reference/const.mdx new file mode 100644 index 00000000..70790323 --- /dev/null +++ b/docs/src/content/docs/reference/const.mdx @@ -0,0 +1,40 @@ +--- +title: Const +--- + +A `const` definition defines a single typed value. You can't refer to these values in the rest of a Bebop schema, but they come in handy when the parties using your schemas also need to agree on certain application-wide parameters. + +Currently, valid const types are: `boolean`, `integers`, `floats` (including `inf`, `-inf`, `nan`), strings, and `GUIDs`. + +For example: + +```typescript +const int32 PianoKeys = 88; +const guid ImportantProductID = "a3628ec7-28d4-4546-ad4a-f6ebf5375c96"; +``` + +## Environment Variables & String Substitutions + +A `const` definition can also be used to define a string that is substituted at compile time. This is useful for defining environment variables that are used in your application, but that you don't want to hard-code into your schema. + +For example: + +```typescript +const string ApiUrl = ${API_URL}; +``` + +When you compile this schema, the compiler will look for the `API_URL` environment variable and substitute it into the schema. If the environment variable is not set, the compiler will throw an error. + +You can also use string substitution to build strings which contain environment variables. For example: + +```typescript +const string ApiUrl = "${BASE_API_URL}/v1"; +``` + +## .dev.vars +When developing locally, add environment variables by creating a .dev.vars file in the root directory of your project alongside `bebop.json`. Then you can populate like so: +```env +API_URL=https://api.example.com +``` + +TODO embed playground \ No newline at end of file diff --git a/docs/src/content/docs/reference/decorators.mdx b/docs/src/content/docs/reference/decorators.mdx new file mode 100644 index 00000000..54cb420b --- /dev/null +++ b/docs/src/content/docs/reference/decorators.mdx @@ -0,0 +1,62 @@ +--- +title: Decorators +--- + + +Bebop provides a metadata mechanism in the form of decorators. Decorators are placed on definitions, fields, and methods to modify the behavior of generated code. + +The compiler provides some built-in decorators, and through the extension framework, you can add your own. + +## Built-in decorators + +### `@deprecated` + +You use the `deprecated` decorator to mark a definition, field, or method as deprecated. The compiler will emit generated code with deprecation warnings / documentation so it is picked up by your IDE. + +Further, when placed on a `message` field, during encoding that field will be skipped. + +```go ins="@deprecated("We no longer use this")" +message Song { + @deprecated("We no longer use this") + 1 -> string title; + 2 -> uint16 year; +} +``` + +:::note +The `deprecated` decorator cannot be used on the fields of a struct, but only on the struct itself. +::: + +### `@opcode` + +Use the `opcode` decorator before a record definition to associate an identifying "opcode" with it. You can either specify a 32-bit unsigned integer: + +```go ins="@opcode(0x12345678)" +@opcode(0x12345678) +message Song { + 1 -> string title; + 2 -> uint16 year; +} +``` + +Or a [FourCC](https://en.wikipedia.org/wiki/FourCC) string: + +```go ins="@opcode("SONG")" +@opcode("SONG") +message Song { + 1 -> string title; + 2 -> uint16 year; +} +``` + +Strictly speaking, Bebop is not opinionated about what you do with these opcodes. You _can_ roll your own bespoke RPC or dispatcher (see [Bebop Mirrors](https://github.com/betwixt-labs/bebop/wiki/Getting-Started-with-.NET#using-bebop-mirroring)), however, we recommend using Tempo (our offical RPC framework). + +To compiler verifies that within the context of all the schemas in your project that no opcode is used twice. This is to prevent collisions. + +### `@flags` + +See [Flags](../enum/#flags) in the `enum` reference. + +## Contributed Decorators + +Extensions can contribute their own decorators. See the [Extensions](../extensions/chord-json) section for more information. \ No newline at end of file diff --git a/docs/src/content/docs/reference/enum.mdx b/docs/src/content/docs/reference/enum.mdx new file mode 100644 index 00000000..9c9df205 --- /dev/null +++ b/docs/src/content/docs/reference/enum.mdx @@ -0,0 +1,101 @@ +--- +title: Enum +--- + +An `enum` defines a type that acts as a wrapper around an integer type (defaults to `uint32`), with certain named constants, each having a corresponding underlying integer value. It is used much like an enum in C. + +```csharp +enum Flavor +{ + Vanilla = 1; + Chocolate = 2; + Mint = 3; +} +``` + +## Flags + +By default, a Bebop enum type is not supposed to represent any underlying values outside of the ones listed. + +If you want a more C-like, bitflags-like behavior, add a `flags` decorator to the enum: + +```csharp ins="@flags" +@flags +enum Permissions { + Read = 0x01; + Write = 0x02; + Comment = 0x04; +} +``` +Defined this way, `Permissions` values like `0` (no permissions) and `3` (`Read` + `Write`) are valid too. + +## Underlying Type + +By default, an enum is backed by a `uint32`. You can change this by specifying a different underlying type: + +```csharp ins="uint8" +enum Flavor : uint8 +{ + Vanilla = 1; + Chocolate = 2; + Mint = 3; +} +``` + +With the above definition, `Flavor` values can only be in the range `0` to `255` and takes up only 1 byte in the serialized form. + +## Best Practices +- Unlike in C, all constants must be explicitly given an integer literal value. +- You should never change or remove a constant from an enum definition. Instead, put `deprecated` decorator in front of the member. +```csharp ins="@deprecated("eat Mint instead")" +enum Flavor +{ + Vanilla = 1; + @deprecated("eat Mint instead") + Chocolate = 2; + Mint = 3; +} +``` +- You're free to add new constants to an enum at any point in the future. + + ### Why should 0 be a "boring" value in an enum + Bebop makes the unusual choice of being opinionated about what the `0` member of an `enum` is called. For all but a select set of identifiers, the compiler issues a warning like so: + > Bebop recommends that 0 in an enum be reserved for a value named Unknown, Default, or similar. + + The full list of acceptable names is: `Default`, `Unknown`, `Invalid`, `Null`, `None`, `Zero`, `False`. + + This warning was inspired by news of the [OMIGOD vulnerability](https://www.wiz.io/blog/omigod-critical-vulnerabilities-in-omi-azure), where an attacker could achieve root privileges because of a bug involving an uninitialized `authInfo` struct in a C program. + + Before [this fix](https://github.com/microsoft/omi/commit/4ce2cf1cb0aa656b8eb934c5acc3f4d6a6796bfa#diff-82eb9e2af56b992b6489a56d3c847b7646c7864baa4bea27d3e05122b6d9cc88R1466), the uninitialized `uid` and `gid` values were filled with… zero. (The memory here was allocated by `calloc`.) And `uid=0` `gid=0` happens to mean "root"! + + This sort of bug is easy to write, and OMIGOD is far from an isolated incident. As long as there are popular languages where it's easy for memory to accidentally be zeroed out, we believe there's a security benefit in ensuring that 0 always means something _boring_. + + That's why Bebop encourages you to leave `0` explicitly reserved to mean "invalid" or "default", and starting the "interesting" values from `1`. + + + + +
DiscouragedEncouraged
+ +```c +enum UserType { + Admin = 0; // uh-oh + Musician = 1; + Listener = 2; +} +``` + + +```c +enum UserType { + Invalid = 0; // whew! + Admin = 1; + Musician = 2; + Listener = 3; +} +``` +
+ + :::note + You can disable this warning with the --no-warn 200 compiler flag. + ::: \ No newline at end of file diff --git a/docs/src/content/docs/reference/import.mdx b/docs/src/content/docs/reference/import.mdx new file mode 100644 index 00000000..30d5f237 --- /dev/null +++ b/docs/src/content/docs/reference/import.mdx @@ -0,0 +1,17 @@ +--- +title: Import +--- + +A Bebop schema may include all the definitions of other schemas, by listing `import` statements at the top of the schema. + +Such a statement consists of `import`, followed by a quoted relative path: + +```go +import "../other.bop" + +struct MyStruct { + OtherType other; +} +``` + +The imported schema is then available for use in the current schema. \ No newline at end of file diff --git a/docs/src/content/docs/reference/json-over-bebop.mdx b/docs/src/content/docs/reference/json-over-bebop.mdx new file mode 100644 index 00000000..4faceb86 --- /dev/null +++ b/docs/src/content/docs/reference/json-over-bebop.mdx @@ -0,0 +1,93 @@ +--- +title: JSON-Over-Bebop +--- + +The JSON-Over-Bebop format is designed as a compatibility layer that enables Bebop's type validation on JSON data and (de)serializes JSON into generated Bebop [records](../records). + +This compatibility is achieved by annotating JSON with type information within the JSON, specifically for types that are not natively supported by JSON and for types that require explicit clarification. + +:::note +JSON-Over-Bebop is currently only supported by the TypeScript runtime. +::: + + +## Annotated JSON + +The following illustrates a typical JSON-Over-Bebop data format: + +```json +{ + "a_int64": { + "#btype": 4, + "value": "6" + }, + "a_date": { + "#btype": 2, + "value": "629592660000000000" + }, + "a_map": { + "#btype": 1, + "#ktype": 7, + "value": { + "false": true, + "true": false + } + } +} +``` + +- `#btype` (Bebop Type): This marker is used to associate the data with a specific Bebop type that helps in the marshaling process. +- `#ktype` (Key Type): This marker is utilized in maps to revive the actual type of the map keys. + +## Native JSON + +It's important to note that `#btype` and `#ktype` are not mandatory if the data object is natively supported by JSON. In this case, a regular JSON is valid JSON-Over-Bebop: +```json +{ + "title":"The Song", + "year":1996, + "performers":[{"name":"The Performer","plays":0}] +} +``` + +Runtime checks will still be performed to validate data types, ensuring alignment with the schema. If any members that are required or known do not have the correct runtime type, a `BebopRuntimeError` is raised, indicating that the JSON was invalid. + +## Type Tags + +The JSON-Over-Bebop format supports the following type tags: + +- `bigIntTag` (4): For BigInt (long/ulong) values. +- `boolTag` (7): For Boolean values. +- `stringTag` (8): For String values. +- `numberTag` (9): For Number values. +- `dateTag` (2): For Date values. +- `uint8ArrayTag` (3): For Uint8Array/byte[] values. +- `guidTag` (5): For Guid values. +- `mapTag` (1): For Map values. +- `mapGuidTag` (6): For GuidMap values. + +```json +{ + "a_bigInt": { + "#btype": 4, + "value": "123456789123456789" + }, + "a_map": { + "#btype": 1, + "#ktype": 8, + "value": { + "key1": "value1", + "key2": "value2" + } + } +} +``` + +## Implementation + +The functionality of this format is implemented through a custom replacer and reviver function for `encodeToJson` and `fromJson` respectively. These functions will handle the marshaling of Bebop records. + +Additionally, a `BebopRuntimeError` is raised if the provided JSON does not comply with the defined schema for a given record, ensuring a robust and reliable JSON (de)serialization process. + +The reviver function also supports the conversion of values back into their respective runtime types, with the help of type markers (`#btype` and `#ktype`). + diff --git a/docs/src/content/docs/reference/message.mdx b/docs/src/content/docs/reference/message.mdx new file mode 100644 index 00000000..e8653790 --- /dev/null +++ b/docs/src/content/docs/reference/message.mdx @@ -0,0 +1,16 @@ +--- +title: Message +--- + +A `message` defines an indexed aggregation of fields containing typed values, each of which may be absent. It might correspond to something like a class in Java, or a JSON object. + +```go +message Song { + 1 -> string title; + 2 -> uint16 year; +} +``` + +In the binary representation of a message, the message is prefixed with its length, and each field is prefixed with its index. + +It is okay to add fields to a `message` with new indices later — in fact, this is the whole point of `message`. When an unrecognized field index is encountered in the process of decoding a message, it is skipped over. This allows for compatibility with versions of your app that use an older version of the schema. \ No newline at end of file diff --git a/docs/src/content/docs/reference/records.mdx b/docs/src/content/docs/reference/records.mdx new file mode 100644 index 00000000..740dca08 --- /dev/null +++ b/docs/src/content/docs/reference/records.mdx @@ -0,0 +1,9 @@ +--- +title: Records +--- + +When talking about Bebop, the word "**record**" is used to mean "either a `struct`, `message`, or `union`". + +The word "**aggregate**" is used to mean "either a `struct` or a `message`" — as these are direct aggregations of fields (data). + +In an aggregate definition, each field is specified by giving the name of the type of the field, followed by the name of the field, followed by `;`. \ No newline at end of file diff --git a/docs/src/content/docs/reference/service.mdx b/docs/src/content/docs/reference/service.mdx new file mode 100644 index 00000000..01679e3f --- /dev/null +++ b/docs/src/content/docs/reference/service.mdx @@ -0,0 +1,48 @@ +--- +title: Service +--- + +In Bebop a `service` defines an aggregate of methods that can be called by a client. + +There are for different types of service methods: + +- `unary` - a single request and response +```proto +service Greeter { + sayHello(MyRequest): MyResponse ; +} +``` +- `server streaming` - a single request and a stream of responses from the server +```proto ins="stream" +service Greeter { + sayHello(MyRequest): stream MyResponse; +} +``` +- `client streaming` - a stream of requests and a single response from the server +```proto ins="stream" +service Greeter { + sayHello(stream MyRequest): MyResponse; +} +``` +:::note +Client streaming is not supported in browser environments. +::: +- `duplex streaming` - a stream of requests and a stream of responses from the server +```proto ins="stream" +service Greeter { + sayHello(stream MyRequest): stream MyResponse; +} +``` + +## Code Generation + +[Tempo](https://github.com/betwixt-labs/tempo) is the offical RPC library for Bebop. + +When the compiler emits code related to services, it will do so to match the implementation of Tempo in the target language. + +For more information see the [Tempo documentation](). + +To control the types of service code that are generated (client vs. server) you can use + +```json +``` \ No newline at end of file diff --git a/docs/src/content/docs/reference/struct.mdx b/docs/src/content/docs/reference/struct.mdx new file mode 100644 index 00000000..7b695715 --- /dev/null +++ b/docs/src/content/docs/reference/struct.mdx @@ -0,0 +1,33 @@ +--- +title: Struct +--- + +A `struct` defines an aggregation of "fields", containing typed values in a fixed order. All values are always present. It is used much like a struct in C. + +```go +struct Point { + int32 x; + int32 y; +} +``` + +The binary representation of a struct is simply that of all field values in order. This means it's more compact and efficient than a `message`. + +When you define a `struct`, you're promising to never add or remove fields from it. If you do so, you'll break compatibility with all existing code that uses the struct. If you need to add or remove fields, you should instead define a new struct with a new name and deprecate the old one. + +## Mutability + +By default all fields of a `struct` are immutable. You can make a field mutable by adding the `mut` keyword before its type. + +```go ins="mut" +mut struct Point { + int32 x; + int32 y; +} +``` + +This will allow you to mutate the field in place, rather than having to copy the entire struct. + +:::note +The promise of 'mutablity' is largely up to the runtime to enforce and is limited by the capabilities of the target language. For example, generated C# code will go as far as to make an array read-only, whereas in TypeScript generated fields in code are only marked as read-only, and in Rust the runtime will not enforce mutability at all. +::: \ No newline at end of file diff --git a/docs/src/content/docs/reference/types.mdx b/docs/src/content/docs/reference/types.mdx new file mode 100644 index 00000000..8d436513 --- /dev/null +++ b/docs/src/content/docs/reference/types.mdx @@ -0,0 +1,31 @@ +--- +title: Types +--- + +Bebop has builtin support for the following data types: + +| Name | Description | +|---|---| +| `bool` | A Boolean value, true or false. | +| `byte` | An unsigned 8-bit integer. `uint8` is an alias. | +| `uint16` | An unsigned 16-bit integer. | +| `int16` | A signed 16-bit integer. | +| `uint32` | An unsigned 32-bit integer. | +| `int32` | A signed 32-bit integer. | +| `uint64` | An unsigned 64-bit integer. | +| `int64` | A signed 64-bit integer. | +| `float32` | A 32-bit IEEE [single-precision floating point number](https://en.wikipedia.org/wiki/Single-precision_floating-point_format). | +| `float64` | A 64-bit IEEE [double-precision floating point number](https://en.wikipedia.org/wiki/Double-precision_floating-point_format). | +| `string` | A length-prefixed UTF-8-encoded string. | +| `guid` | A [GUID](https://en.wikipedia.org/wiki/Universally_unique_identifier). | +| `date` | A [UTC](https://en.wikipedia.org/wiki/Coordinated_Universal_Time) date / timestamp. | +| `T[]` | A length-prefixed array of `T` values. `array[T]` is an alias. | +| `map[T1, T2]` | A map, as a length-prefixed array of (`T1`, `T2`) association pairs. | + +You may also use user-defined types (`enum`s and other records) as field types. + +A string is stored as a length-prefixed array of bytes. All length-prefixes are 32-bit unsigned integers, which means the maximum number of bytes in a string, or entries in an array or map, is about 4 billion (2^32). + +A `guid` is stored as 16 bytes, in [Guid.ToByteArray](https://docs.microsoft.com/en-us/dotnet/api/system.guid.tobytearray?view=net-5.0) order. + +A `date` is stored as a 64-bit integer amount of “ticks” since 00:00:00 UTC on January 1 of year 1 A.D. in the Gregorian calendar, where a “tick” is 100 nanoseconds. \ No newline at end of file diff --git a/docs/src/content/docs/reference/union.md b/docs/src/content/docs/reference/union.md new file mode 100644 index 00000000..4feea531 --- /dev/null +++ b/docs/src/content/docs/reference/union.md @@ -0,0 +1,22 @@ +--- +title: Union +--- + +A `union` defines a tagged union of one or more inline struct or message definitions. Each is preceded by a "discriminator" or "tag" value. This defines a type whose values may assume any one of the aggregate layouts defined inside. It corresponds to something like C++'s std::variant or Rust enum. + +```go +union U { + 1 -> message A { } + 2 -> struct B { } +} +``` + +The binary representation of a `U` value is then: a length prefix, followed by either (a) a `01` byte followed by an encoding of an `A` message, or (b) a `02` byte followed by an encoding of a `B` struct. + +Just like with messages, new branches may be added to a union later. When an unrecognized discriminator value is encountered, the length prefix is used to skip over the body, and decoding fails in a way your program may catch. + +Nested types are not available globally but do reserve the identifier globally. E.g. in the above you cannot define struct `Other { A x; }` because `A` is private to `U` but you also cannot define `struct A { }` because A is reserved globally. + +:::note +Unions are not yet implemented in some runtimes such as Dart, but are supported even in languages without native union support such as C# +::: \ No newline at end of file diff --git a/docs/src/content/docs/reference/wire-format.mdx b/docs/src/content/docs/reference/wire-format.mdx new file mode 100644 index 00000000..441db0fb --- /dev/null +++ b/docs/src/content/docs/reference/wire-format.mdx @@ -0,0 +1,73 @@ +--- +title: Wire format +--- +This document describes the binary format that Bebop encodes into and out of (the bytes that go “over the wire”). + +## Values +First, let's look at how the typed values inside messages are encoded. + +### Numbers, bools, and enums +Integer types are encoded little-endian, using a fixed number of bytes. For example, the `uint16` value 10 is represented on the wire as `0a 00`. In fact, everything is little-endian in Bebop. + +Floating point numbers are also little-endian, encoded in the usual [IEEE 754](https://en.wikipedia.org/wiki/IEEE_754) format, as either 4 or 8 bytes. + +A `bool` is encoded as a single byte: either `00` (representing false) or `01` (representing true). + +Enum values are encoded by their underlying integer value. (If you define `enum Flavor { Chocolate = 2; }` then the value _Chocolate_ is encoded as `02 00 00 00`, because the default underlying type is `uint32`. If you define `enum Color: uint16 { Blue = 3; }` then the value _Blue_ is encoded as `03 00`.) + +### Arrays +Arrays (`T[]`) are encoded as a `uint32` number of members, followed by that many encodings of `T` concatenated together. + +### Maps +Maps (`map[K, V]`) are encoded as a `uint32` number of key-value pairs, followed by that many pairs of a `K` followed by a `V`. + +### Strings +Strings are encoded as a `uint32` number of bytes, followed by that many bytes of [UTF-8](https://en.wikipedia.org/wiki/UTF-8). + +### GUIDs +GUIDs or UUIDs (`guid`) are encoded as sixteen bytes, in the .NET [Guid.ToByteArray](https://docs.microsoft.com/en-us/dotnet/api/system.guid.tobytearray) order. + +### Dates +Dates are encoded as a `uint64` number of 100 nanosecond units since 00:00:00 UTC on January 1 of year 1 A.D. in the Gregorian calendar. These are called [ticks](https://docs.microsoft.com/en-us/dotnet/api/system.datetime.ticks?view=net-5.0) in .NET. + +The top two bits of this value are ignored by Bebop. In .NET, they are used to specify whether a date is in UTC or local to the current time zone. But in Bebop, all date-times on the wire are in UTC. + +## Records +Recall that a “record” is either a **struct**, **message** or **union**. Encode and decode methods are generated for all records. The wire format is a little different between them: + +### Structs +The encoding of a **struct** consists of a straightforward concatenation of the encodings of its fields. The fields don't have indices, because structs are never supposed to have new fields added or get old fields deprecated. + +### Messages +The encoding of a **message** consists of a `uint32` length, followed by a “body” of that many bytes. + +The body consists of a series of “indexed fields”, followed by a final `00` byte. Fields in a message may be absent, so the indices indicate which fields are or aren't filled in. An indexed field consists of a single byte field-index, followed by an encoding of that field's type. + +For example, given `message M { 1 -> byte x; 2 -> int16 y; 3 -> int32 z; }` the encoding of `{x=15, z=5}` would be: + +``` + body length body + _________ _____________________ + / \ / \ + 08 00 00 00 01 0f 03 05 00 00 00 00 + \___/ \____________/ \_/ + x=15 z=5 end +``` + +The body length prefix is necessary for forwards compatibility: if a client parses a message that has a field index it doesn't recognize (because the client is on an old version of a schema that doesn't have this field), it can skip to the end of the message. + +The smallest possible message is when all fields are not present, this is encoded as: +``` + body length body + _________ _ + / \ / \ + 01 00 00 00 00 + \_/ + end +``` +### Unions +The encoding of a **union** consists of a `uint32` length, followed by a `uint8` discriminator, followed by a “body” of _length_ bytes. + +The struct or message definition corresponding to the discriminator value is used to decode the body. + +Again, the body length prefix is for forwards compatibility: if a client parses a union that has a discriminator it doesn't recognize (because the client is on an old version of a schema that doesn't have this field), it can skip to the end of the message. \ No newline at end of file diff --git a/docs/src/content/docs/tempo/TypeScript/client.mdx b/docs/src/content/docs/tempo/TypeScript/client.mdx new file mode 100644 index 00000000..79c25fe6 --- /dev/null +++ b/docs/src/content/docs/tempo/TypeScript/client.mdx @@ -0,0 +1,29 @@ +--- +title: Using a Client +--- + +Assuming you already have `@tempojs/client` as a dependency in your project, then all you need to do is import models from your shared codebase to use them: +```typescript +import { GreeterClient } from '../shared'; +// creates a new channel pointing at the Tempo server running locally +const channel = TempoChannel.forAddress('http://localhost:3000'); +channel + .getClient(GreeterClient) + .sayHello({ name: 'World' }) + .then((response) => { + console.log(response.serviceMessage); + }) + .catch((e) => { + if (e instanceof TempoError) { + console.error(e); + } + }); +``` +or more simply if your project supports top-level await +```typescript +const channel = TempoChannel.forAddress("http://localhost:3000"); +const client = channel.getClient(GreeterClient); +const response = await client.sayHello({name: "World"}); +console.log(response.serviceMessage); +``` + \ No newline at end of file diff --git a/docs/src/content/docs/tempo/TypeScript/cloudflare-workers.mdx b/docs/src/content/docs/tempo/TypeScript/cloudflare-workers.mdx new file mode 100644 index 00000000..cf0336ea --- /dev/null +++ b/docs/src/content/docs/tempo/TypeScript/cloudflare-workers.mdx @@ -0,0 +1,29 @@ +--- +title: Using Tempo with Cloudflare Workers +--- + + +## Quick Start + +A deployable template is available on Github [here](https://github.com/betwixt-labs/template-worker-tempo). + +## Basic implementation + +```typescript +import { ConsoleLogger } from '@tempojs/common'; +import { TempoRouter } from '@tempojs/cloudflare-worker-router'; +import * as Services from './services'; + +// bindings interface +interface Env {} + +const logger = new ConsoleLogger('Router'); +const registry = new Services.TempoServiceRegistry(logger); +const router = new TempoRouter(logger, registry); + +export default { + async fetch(request: Request, env: Env) { + return await router.handle(request, env); + }, +}; +``` \ No newline at end of file diff --git a/docs/src/content/docs/tempo/TypeScript/getting-started.mdx b/docs/src/content/docs/tempo/TypeScript/getting-started.mdx new file mode 100644 index 00000000..a6346d1d --- /dev/null +++ b/docs/src/content/docs/tempo/TypeScript/getting-started.mdx @@ -0,0 +1,142 @@ +--- +title: Getting started +--- + +:::note +This documentation is a WIP. Please feel free to suggest improvements or share feedback. We want to make getting started painless. +::: +This tutorial assumes you've already set up a monorepo for your project, for example: +``` +. +├── client +│ ├── src +│ │ ├── components +│ │ └── index.ts +│ ├── public +│ └── ... +├── server +│ ├── src +│ │ ├── services +│ │ ├── validators +│ │ └── index.ts +│ ├── ... +└── shared + ├── src + │ ├── types + │ ├── utils + │ └── index.ts + └── ... +``` + +## TSConfig + +Tempo utilizes decorators. As such, you'll need to set `experimentalDecorators` and `emitDecoratorMetadata` to `true` in your tsconfig.json: + +```json +{ + "compilerOptions": { + "target": "esnext", + "module": "esnext", + "moduleResolution": "node", + "esModuleInterop": true, + "strict": true, + "outDir": "dist", + "experimentalDecorators": true, + "emitDecoratorMetadata": true + }, + "include": ["server", "client", "shared"] +} +``` + +## Code Generation + +Tempo uses [Bebop](https://github.com/betwixt-labs/bebop) for generating client and service code. To install it, follow the commands below: +``` +yarn add bebop +yarn add bebop-tools --dev +``` + +By default, `bebopc` produces client and server code in the same output. You'll need to add the following dependencies to your project: +``` +yarn add @tempojs/client @tempojs/server +``` + +If your client and server code lives in the same project, you can install Tempo into it. + +```json +{ + "include": [ + "**/*.bop" + ], + "generators": { + "ts": { + "outFile": "./shared/index.ts" + } + } +} +``` +Now, you can create your first service. Create a 'greeter.bop' file in your shared project with the following contents: + +```go +/** + * `HelloRequest` is a struct representing a request to the Greeter service. + */ + struct HelloRequest { + /** + * The name to be used in the greeting, of type string. + */ + string name; +} + +/** + * `HelloResponse` is a struct representing the response from the Greeter service. + */ +struct HelloResponse { + /** + * The greeting message generated by the service, of type string. + */ + string serviceMessage; +} + +/** + * `Greeter` is a service that provides a method for generating greeting messages. + */ +service Greeter { + /** + * `sayHello` is a method that takes a `HelloRequest` and returns a `HelloResponse`. + */ + sayHello(HelloRequest): HelloResponse; +} +``` + +Now, just run `yarn bebopc build` and it will generate all the shared code into `index.ts`. + +## Implementing a Service + +The Bebop compiler produces concrete client implementations and abstract base service for you to implement. For example: +```typescript +// registers 'GreeterService' with the 'TempoServiceRegistry' +// we use 'BaseGreeterService.serviceName' as this is how +// the service is referenced by generated code +@TempoServiceRegistry.register(BaseGreeterService.serviceName) +export class GreeterService extends BaseGreeterService { + public sayHello( + record: IHelloRequest, + context: ServerContext + ): Promise { + return Promise.resolve({ serviceMessage: `Hello ${record.name}` }); + } +} +``` + +A service that you wish to expose to clients _must_ be registered using the `TempoServiceRegistry.register` decorator as shown above. The parameter passed to `TempoServiceRegistry.register` is always the `serviceName` present on the base class of the service. Failure to register a service that was defined in a schema will result in a runtime error when initializing the server. At this time it is recommended that you reexport all services under a single export: +```typescript +//index.ts +import { GreeterService } from './greeeter' + +export { + GreeterService +} +``` + +## \ No newline at end of file diff --git a/docs/src/content/docs/tempo/TypeScript/node-http.mdx b/docs/src/content/docs/tempo/TypeScript/node-http.mdx new file mode 100644 index 00000000..6e25d287 --- /dev/null +++ b/docs/src/content/docs/tempo/TypeScript/node-http.mdx @@ -0,0 +1,67 @@ +--- +title: Using Tempo with Node HTTP +--- +import { Tabs, TabItem, LinkCard } from "@astrojs/starlight/components"; + +## Quick + +Tempo supports any server that can use Node HTTP, so you don't have to use a serverless platform to use it. This example shows how to use Tempo with Node's HTTP server. + +For a see the online example, see the [Node HTTP example](https://stackblitz.com/edit/node-tempo) on StackBlitz. + + +First, install the Tempo router for Node into your project: + + + + ```bash + npm install @tempojs/node-http-router + ``` + + + + ```bash + yarn add @tempojs/node-http-router + ``` + + + +Next, inside your `index.ts` (or wherever your server handler resides) do the following steps: + + +1. Import your services +```typescript +import * as Services from './services'; +``` +2. Add this line after your import call +```typescript +typeof Services; +``` + +:::note +This is a temporary workaround to decorators not being triggered unless there is a reference made to them +::: + +Now you're ready to create the router: +```typescript +// creates a logger that will be used by Tempo +// can be anything that implements `TempoLogger`, `ConsoleLogger` is just built in. +const logger = new ConsoleLogger('worker', TempoLogLevel.Debug); +// initialize the service registry. this class is generated for you. +const registry = new TempoServiceRegistry(logger); +// create a new router +// `any` refers to our environment objects type; it is recommended to set it an explicit type (MyEnvObj, etc.) +const router = new TempoRouter( + logger, + registry +); + +const server = createServer( + // feed incoming messages into the router + async (req: IncomingMessage, res: ServerResponse) => { + await router.process(req, res, {})); + } +); +server.listen(3000); +``` + diff --git a/docs/src/content/docs/tempo/faq.mdx b/docs/src/content/docs/tempo/faq.mdx new file mode 100644 index 00000000..e1f68638 --- /dev/null +++ b/docs/src/content/docs/tempo/faq.mdx @@ -0,0 +1,40 @@ +--- +title: Tempo FAQ +--- + +Learn about the motivations and design choices behind Tempo. + +# Why We Created Tempo + +A few years ago, the team at Rainway needed a fast, memory-efficient, and low-latency way to serialize data. Existing solutions like Protocol Buffers couldn't meet their performance requirements (especially in the browser), so they created [Bebop](https://github.com/betwixt-labs/bebop). + +Before Tempo, the only way to utilize Bebop for communication between apps and services was to build a custom RPC around it. This pattern was so common among Bebop users that some runtimes included special functionality to facilitate it, like [Bebop Mirroring](../../guide/getting-started-csharp#using-bebop-mirroring). + +Forcing users of a technology, which should ideally be their go-to choice, to implement complex systems results in a poor developer experience (DX) and doesn't scale well. With an ever-growing demand among Bebop users for a formalized RPC mechanism, we created Tempo to address this need, while providing clear and immediate performance gains for your applications, APIs, and services. 🚀 + + +# Schemas & Code Generation vs. the World + +Code generation, especially in the JavaScript ecosystem, often gets a bad rap. It's hard to blame anyone for being hesitant to adopt it – if your first exposure as a developer to the idea of "code gen" was working with Protocol Buffers and gRPC, you've experienced one of the worst developer experiences in modern development paradigms. Rising projects like [tRPC](https://github.com/trpc/trpc) even tout their lack of code generation as a feature. + +Ultimately, whether you write a TypeScript interface or a struct in your Bebop schema, you're doing the same thing – defining the structure of data. The question we ask ourselves in this situation is: how portable and accurate are those definitions? + +In the case of something like tRPC, where interfaces (object literals) are the norm, you get _some_ type guarantees during development, but you won't receive any type checking or guarantees during runtime. This is to be expected due to the dynamic nature of JavaScript and JSON; a call to `JSON.parse` is naive and can't validate the data it's parsing. Your code must assume (hopefully with caution) that the object returned matches the structure of data you defined elsewhere. + +This yields another issue – in many JavaScript runtimes, certain types (such as Map, Set, etc.) are not natively serializable by `JSON.stringify`. As a result, data must be transformed to even be sent on the wire. This means the data will deviate from its definition (even if only temporarily) and introduces further complexity and considerations into the manually written deserialization step of an RPC, potentially leading to hard-to-diagnose bugs of varying severity. + +The surface area of (de)serialization issues increases in tandem with the complexity of your application and its stack. Your browser app might communicate with a serverless function written in Go, which itself communicates with a microservice written in C# (a real example from yours truly). If you're modifying data to go on the wire, then you have to consider this in every language that might touch that data. + +In contrast, Tempo, by utilizing Bebop's code generation and runtimes, avoids these issues. We get the benefits of development and runtime type safety (even in JavaScript), a single wire-format that is interoperable between environments and domains, and highly efficient (de)serialization that is CPU cacheable due to the single scan nature of the data, making it perfect for serverless environments with memory and CPU constraints. Of course, the biggest benefit is that the definition of the data and its serialized format is constant, effectively allowing you to encode a message once and have it repeated across any number of services in an application's stack reliably. + +All of this is to say that any significantly complex application will eventually need some sort of code generation, and when it does, Tempo and, by extension, Bebop will be there to provide the DX you deserve. 🚀 + + +# The Right Abstraction + +If you haven't read it already, we highly recommend reading ["The Wrong Abstraction"](https://sandimetz.com/blog/2016/1/20/the-wrong-abstraction) by Sandi Metz. + +To achieve the right abstraction, both for Tempo and RPC as a whole, we're not committed to any particular design prior to 1.0; breaking changes should be expected. The goals of Tempo's abstraction are: +- To be consistent across languages. If you've ever implemented Tempo on the server-side in TypeScript, it should be just as straightforward in C#. +- To avoid ambiguity. Names for types should be clear. Implementing extended functionality such as authorization should be obvious. Adding support for new backends should be consistently painless. +- To ensure most of a user's time is spent on building the services for their API, rather than wrestling with complex configurations or unnecessary overhead. 🎯 \ No newline at end of file diff --git a/docs/src/content/docs/tempo/index.mdx b/docs/src/content/docs/tempo/index.mdx new file mode 100644 index 00000000..2212c9a2 --- /dev/null +++ b/docs/src/content/docs/tempo/index.mdx @@ -0,0 +1,28 @@ +--- +title: Intro +--- + +Tempo is the official RPC framework from Betwixt Labs built on top of Bebop to replace your gRPC and REST APIs. It's designed to be simple, fast, and easy to use. + +## 🎨  Design Decisions + +Learn about the motivations and design choices behind Tempo, including: +- [Why We Created Tempo](https://github.com/betwixt-labs/tempo/wiki/Design-Decisions#why-we-created-tempo) +- [Schemas & Code Generation vs. the World](https://github.com/betwixt-labs/tempo/wiki/Design-Decisions#why-we-created-tempo) +- [More design-related articles...](https://github.com/betwixt-labs/tempo/wiki/Design-Decisions) + +## 🚀  Getting Started Guides + +Find language-specific and platform-specific getting started guides below: + +### Typescript +- [Getting started with Tempo in TypeScript](https://github.com/betwixt-labs/tempo/wiki/Getting-Started:-Typescript#getting-started-typescript) +- [Using Tempo with Cloudflare Workers](https://github.com/betwixt-labs/tempo/wiki/Getting-Started:-Typescript#using-tempo-with-cloudflare-workers) +- [Using Tempo with Node HTTP](https://github.com/betwixt-labs/tempo/wiki/Getting-Started:-Typescript#using-tempo-with-node-http) + +### More languages and platforms coming soon... + +## 📢  Community Discussions and RFCs + +We're actively seeking feedback and improvements on certain abstractions and APIs. Join the discussion and contribute to the future of Tempo: +- [Tempo RFCs and Community Discussions](https://github.com/betwixt-labs/tempo/discussions/categories/rfc-ideas) \ No newline at end of file diff --git a/docs/src/env.d.ts b/docs/src/env.d.ts new file mode 100644 index 00000000..acef35f1 --- /dev/null +++ b/docs/src/env.d.ts @@ -0,0 +1,2 @@ +/// +/// diff --git a/docs/tsconfig.json b/docs/tsconfig.json new file mode 100644 index 00000000..77da9dd0 --- /dev/null +++ b/docs/tsconfig.json @@ -0,0 +1,3 @@ +{ + "extends": "astro/tsconfigs/strict" +} \ No newline at end of file diff --git a/docs/yarn.lock b/docs/yarn.lock new file mode 100644 index 00000000..f718f856 --- /dev/null +++ b/docs/yarn.lock @@ -0,0 +1,4506 @@ +# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. +# yarn lockfile v1 + + +"@ampproject/remapping@^2.2.0": + version "2.2.1" + resolved "https://registry.yarnpkg.com/@ampproject/remapping/-/remapping-2.2.1.tgz#99e8e11851128b8702cd57c33684f1d0f260b630" + integrity sha512-lFMjJTrFL3j7L9yBxwYfCq2k6qqwHyzuUl/XBnif78PWTJYyL/dfowQHWE3sp6U6ZzqWiiIZnpTMO96zhkjwtg== + dependencies: + "@jridgewell/gen-mapping" "^0.3.0" + "@jridgewell/trace-mapping" "^0.3.9" + +"@astrojs/check@^0.3.4": + version "0.3.4" + resolved "https://registry.yarnpkg.com/@astrojs/check/-/check-0.3.4.tgz#b09d04f50570b4f579e336a30e7237ee9fd839bf" + integrity sha512-Wi4KwW38J3GCd/U6LH2UuU4uc4P/K1WYaqhoKm2o7rVoGhQfO+RWrSO26rUPRXYbmae8JugAgpCmsHC8bt5RlA== + dependencies: + "@astrojs/language-server" "^2.5.5" + chokidar "^3.5.3" + fast-glob "^3.3.1" + kleur "^4.1.5" + yargs "^17.7.2" + +"@astrojs/compiler@^2.3.4", "@astrojs/compiler@^2.4.0": + version "2.5.0" + resolved "https://registry.yarnpkg.com/@astrojs/compiler/-/compiler-2.5.0.tgz#dba7a7a936aed98089b93505dda1c1011ba82746" + integrity sha512-ZDluNgMIJT+z+HJcZ6QEJ/KqaFkTkrb+Za6c6VZs8G/nb1LBErL14/iU5EVJ9yu25i4QCLweuBJ3m5df34gZJg== + +"@astrojs/internal-helpers@0.2.1": + version "0.2.1" + resolved "https://registry.yarnpkg.com/@astrojs/internal-helpers/-/internal-helpers-0.2.1.tgz#4e2e6aabaa9819f17119aa10f413c4d6122c94cf" + integrity sha512-06DD2ZnItMwUnH81LBLco3tWjcZ1lGU9rLCCBaeUCGYe9cI0wKyY2W3kDyoW1I6GmcWgt1fu+D1CTvz+FIKf8A== + +"@astrojs/language-server@^2.5.5": + version "2.6.2" + resolved "https://registry.yarnpkg.com/@astrojs/language-server/-/language-server-2.6.2.tgz#d83ff8966041b22fbcf1c1bf858c292219a6d131" + integrity sha512-RYzPRhS/WBXK5JtfR+0+nGj+N3VbJd5jU/uSNUev9baUx/RLmUwDk1f6Oy8QDEfDDLAr76Ig8YeDD/nxPdBSLw== + dependencies: + "@astrojs/compiler" "^2.4.0" + "@jridgewell/sourcemap-codec" "^1.4.15" + "@volar/kit" "~1.11.1" + "@volar/language-core" "~1.11.1" + "@volar/language-server" "~1.11.1" + "@volar/language-service" "~1.11.1" + "@volar/source-map" "~1.11.1" + "@volar/typescript" "~1.11.1" + fast-glob "^3.2.12" + muggle-string "^0.3.1" + volar-service-css "0.0.17" + volar-service-emmet "0.0.17" + volar-service-html "0.0.17" + volar-service-prettier "0.0.17" + volar-service-typescript "0.0.17" + volar-service-typescript-twoslash-queries "0.0.17" + vscode-html-languageservice "^5.1.0" + vscode-uri "^3.0.8" + +"@astrojs/markdown-remark@4.1.0": + version "4.1.0" + resolved "https://registry.yarnpkg.com/@astrojs/markdown-remark/-/markdown-remark-4.1.0.tgz#17cf55d38064859595ad6dfac5b951a1c22e0da9" + integrity sha512-JnIy6zk+6f/SgSVMZecZFxQt0faT1uBckwYCuBxKH1hYYJsal8OOe+tx6JxfnyaV+xViyjUvQ28mmn+p/F5LkQ== + dependencies: + "@astrojs/prism" "^3.0.0" + github-slugger "^2.0.0" + import-meta-resolve "^4.0.0" + mdast-util-definitions "^6.0.0" + rehype-raw "^7.0.0" + rehype-stringify "^10.0.0" + remark-gfm "^4.0.0" + remark-parse "^11.0.0" + remark-rehype "^11.0.0" + remark-smartypants "^2.0.0" + shikiji "^0.9.18" + unified "^11.0.4" + unist-util-visit "^5.0.0" + vfile "^6.0.1" + +"@astrojs/mdx@^2.0.4": + version "2.0.5" + resolved "https://registry.yarnpkg.com/@astrojs/mdx/-/mdx-2.0.5.tgz#539af0ee2258ff827c90e9bf74d131f87fd08117" + integrity sha512-3+1E11J6Yfb8XZIyaEBtJNdc4Dn9YbforPgmxiY5SYa0C5+f6KEUQi30Hd9I/tA8ytS+1MXw6ZE1BXbaRUo3ZA== + dependencies: + "@astrojs/markdown-remark" "4.1.0" + "@mdx-js/mdx" "^3.0.0" + acorn "^8.11.2" + es-module-lexer "^1.4.1" + estree-util-visit "^2.0.0" + github-slugger "^2.0.0" + gray-matter "^4.0.3" + hast-util-to-html "^9.0.0" + kleur "^4.1.4" + rehype-raw "^7.0.0" + remark-gfm "^4.0.0" + remark-smartypants "^2.0.0" + source-map "^0.7.4" + unist-util-visit "^5.0.0" + vfile "^6.0.1" + +"@astrojs/prism@^3.0.0": + version "3.0.0" + resolved "https://registry.yarnpkg.com/@astrojs/prism/-/prism-3.0.0.tgz#c9443e4cbf435acf0b5adc2c627d9789991514e7" + integrity sha512-g61lZupWq1bYbcBnYZqdjndShr/J3l/oFobBKPA3+qMat146zce3nz2kdO4giGbhYDt4gYdhmoBz0vZJ4sIurQ== + dependencies: + prismjs "^1.29.0" + +"@astrojs/sitemap@^3.0.4": + version "3.0.5" + resolved "https://registry.yarnpkg.com/@astrojs/sitemap/-/sitemap-3.0.5.tgz#f7313c21f4eba2ba6d22936326af70f889c53414" + integrity sha512-60eLzNjMza3ABypiQPUC6ElOSZNZeY5CwSwgJ03hfeonl+Db9x12CCzBFdTw7A5Mq+O54xEZVUrR0tB+yWgX8w== + dependencies: + sitemap "^7.1.1" + zod "^3.22.4" + +"@astrojs/starlight@^0.15.2": + version "0.15.4" + resolved "https://registry.yarnpkg.com/@astrojs/starlight/-/starlight-0.15.4.tgz#623c22b5c8024f58b00a3544dcfd90dcd64d997b" + integrity sha512-o3heYH+RltsCsvO3L0qLnnFJEakwLSRoxW4wFX2zDeDWta9BIpdSOo7+Zg+sSn7k9RPOhI9SGvdFx67B53I18Q== + dependencies: + "@astrojs/mdx" "^2.0.4" + "@astrojs/sitemap" "^3.0.4" + "@pagefind/default-ui" "^1.0.3" + "@types/hast" "^3.0.3" + "@types/mdast" "^4.0.3" + astro-expressive-code "^0.31.0" + bcp-47 "^2.1.0" + hast-util-select "^6.0.2" + hastscript "^8.0.0" + mdast-util-directive "^3.0.0" + pagefind "^1.0.3" + rehype "^13.0.1" + remark-directive "^3.0.0" + unified "^11.0.4" + unist-util-remove "^4.0.0" + unist-util-visit "^5.0.0" + vfile "^6.0.1" + +"@astrojs/telemetry@3.0.4": + version "3.0.4" + resolved "https://registry.yarnpkg.com/@astrojs/telemetry/-/telemetry-3.0.4.tgz#566257082c87df84fcc136db23e071e1104b13fd" + integrity sha512-A+0c7k/Xy293xx6odsYZuXiaHO0PL+bnDoXOc47sGDF5ffIKdKQGRPFl2NMlCF4L0NqN4Ynbgnaip+pPF0s7pQ== + dependencies: + ci-info "^3.8.0" + debug "^4.3.4" + dlv "^1.1.3" + dset "^3.1.2" + is-docker "^3.0.0" + is-wsl "^3.0.0" + which-pm-runs "^1.1.0" + +"@babel/code-frame@^7.22.13", "@babel/code-frame@^7.23.5": + version "7.23.5" + resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.23.5.tgz#9009b69a8c602293476ad598ff53e4562e15c244" + integrity sha512-CgH3s1a96LipHCmSUmYFPwY7MNx8C3avkq7i4Wl3cfa662ldtUe4VM1TPXX70pfmrlWTb6jLqTYrZyT2ZTJBgA== + dependencies: + "@babel/highlight" "^7.23.4" + chalk "^2.4.2" + +"@babel/compat-data@^7.23.5": + version "7.23.5" + resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.23.5.tgz#ffb878728bb6bdcb6f4510aa51b1be9afb8cfd98" + integrity sha512-uU27kfDRlhfKl+w1U6vp16IuvSLtjAxdArVXPa9BvLkrr7CYIsxH5adpHObeAGY/41+syctUWOZ140a2Rvkgjw== + +"@babel/core@^7.23.3": + version "7.23.7" + resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.23.7.tgz#4d8016e06a14b5f92530a13ed0561730b5c6483f" + integrity sha512-+UpDgowcmqe36d4NwqvKsyPMlOLNGMsfMmQ5WGCu+siCe3t3dfe9njrzGfdN4qq+bcNUt0+Vw6haRxBOycs4dw== + dependencies: + "@ampproject/remapping" "^2.2.0" + "@babel/code-frame" "^7.23.5" + "@babel/generator" "^7.23.6" + "@babel/helper-compilation-targets" "^7.23.6" + "@babel/helper-module-transforms" "^7.23.3" + "@babel/helpers" "^7.23.7" + "@babel/parser" "^7.23.6" + "@babel/template" "^7.22.15" + "@babel/traverse" "^7.23.7" + "@babel/types" "^7.23.6" + convert-source-map "^2.0.0" + debug "^4.1.0" + gensync "^1.0.0-beta.2" + json5 "^2.2.3" + semver "^6.3.1" + +"@babel/generator@^7.23.3", "@babel/generator@^7.23.6": + version "7.23.6" + resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.23.6.tgz#9e1fca4811c77a10580d17d26b57b036133f3c2e" + integrity sha512-qrSfCYxYQB5owCmGLbl8XRpX1ytXlpueOb0N0UmQwA073KZxejgQTzAmJezxvpwQD9uGtK2shHdi55QT+MbjIw== + dependencies: + "@babel/types" "^7.23.6" + "@jridgewell/gen-mapping" "^0.3.2" + "@jridgewell/trace-mapping" "^0.3.17" + jsesc "^2.5.1" + +"@babel/helper-annotate-as-pure@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.22.5.tgz#e7f06737b197d580a01edf75d97e2c8be99d3882" + integrity sha512-LvBTxu8bQSQkcyKOU+a1btnNFQ1dMAd0R6PyW3arXes06F6QLWLIrd681bxRPIXlrMGR3XYnW9JyML7dP3qgxg== + dependencies: + "@babel/types" "^7.22.5" + +"@babel/helper-compilation-targets@^7.23.6": + version "7.23.6" + resolved "https://registry.yarnpkg.com/@babel/helper-compilation-targets/-/helper-compilation-targets-7.23.6.tgz#4d79069b16cbcf1461289eccfbbd81501ae39991" + integrity sha512-9JB548GZoQVmzrFgp8o7KxdgkTGm6xs9DW0o/Pim72UDjzr5ObUQ6ZzYPqA+g9OTS2bBQoctLJrky0RDCAWRgQ== + dependencies: + "@babel/compat-data" "^7.23.5" + "@babel/helper-validator-option" "^7.23.5" + browserslist "^4.22.2" + lru-cache "^5.1.1" + semver "^6.3.1" + +"@babel/helper-environment-visitor@^7.22.20": + version "7.22.20" + resolved "https://registry.yarnpkg.com/@babel/helper-environment-visitor/-/helper-environment-visitor-7.22.20.tgz#96159db61d34a29dba454c959f5ae4a649ba9167" + integrity sha512-zfedSIzFhat/gFhWfHtgWvlec0nqB9YEIVrpuwjruLlXfUSnA8cJB0miHKwqDnQ7d32aKo2xt88/xZptwxbfhA== + +"@babel/helper-function-name@^7.23.0": + version "7.23.0" + resolved "https://registry.yarnpkg.com/@babel/helper-function-name/-/helper-function-name-7.23.0.tgz#1f9a3cdbd5b2698a670c30d2735f9af95ed52759" + integrity sha512-OErEqsrxjZTJciZ4Oo+eoZqeW9UIiOcuYKRJA4ZAgV9myA+pOXhhmpfNCKjEH/auVfEYVFJ6y1Tc4r0eIApqiw== + dependencies: + "@babel/template" "^7.22.15" + "@babel/types" "^7.23.0" + +"@babel/helper-hoist-variables@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/helper-hoist-variables/-/helper-hoist-variables-7.22.5.tgz#c01a007dac05c085914e8fb652b339db50d823bb" + integrity sha512-wGjk9QZVzvknA6yKIUURb8zY3grXCcOZt+/7Wcy8O2uctxhplmUPkOdlgoNhmdVee2c92JXbf1xpMtVNbfoxRw== + dependencies: + "@babel/types" "^7.22.5" + +"@babel/helper-module-imports@^7.22.15": + version "7.22.15" + resolved "https://registry.yarnpkg.com/@babel/helper-module-imports/-/helper-module-imports-7.22.15.tgz#16146307acdc40cc00c3b2c647713076464bdbf0" + integrity sha512-0pYVBnDKZO2fnSPCrgM/6WMc7eS20Fbok+0r88fp+YtWVLZrp4CkafFGIp+W0VKw4a22sgebPT99y+FDNMdP4w== + dependencies: + "@babel/types" "^7.22.15" + +"@babel/helper-module-transforms@^7.23.3": + version "7.23.3" + resolved "https://registry.yarnpkg.com/@babel/helper-module-transforms/-/helper-module-transforms-7.23.3.tgz#d7d12c3c5d30af5b3c0fcab2a6d5217773e2d0f1" + integrity sha512-7bBs4ED9OmswdfDzpz4MpWgSrV7FXlc3zIagvLFjS5H+Mk7Snr21vQ6QwrsoCGMfNC4e4LQPdoULEt4ykz0SRQ== + dependencies: + "@babel/helper-environment-visitor" "^7.22.20" + "@babel/helper-module-imports" "^7.22.15" + "@babel/helper-simple-access" "^7.22.5" + "@babel/helper-split-export-declaration" "^7.22.6" + "@babel/helper-validator-identifier" "^7.22.20" + +"@babel/helper-plugin-utils@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/helper-plugin-utils/-/helper-plugin-utils-7.22.5.tgz#dd7ee3735e8a313b9f7b05a773d892e88e6d7295" + integrity sha512-uLls06UVKgFG9QD4OeFYLEGteMIAa5kpTPcFL28yuCIIzsf6ZyKZMllKVOCZFhiZ5ptnwX4mtKdWCBE/uT4amg== + +"@babel/helper-simple-access@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/helper-simple-access/-/helper-simple-access-7.22.5.tgz#4938357dc7d782b80ed6dbb03a0fba3d22b1d5de" + integrity sha512-n0H99E/K+Bika3++WNL17POvo4rKWZ7lZEp1Q+fStVbUi8nxPQEBOlTmCOxW/0JsS56SKKQ+ojAe2pHKJHN35w== + dependencies: + "@babel/types" "^7.22.5" + +"@babel/helper-split-export-declaration@^7.22.6": + version "7.22.6" + resolved "https://registry.yarnpkg.com/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.22.6.tgz#322c61b7310c0997fe4c323955667f18fcefb91c" + integrity sha512-AsUnxuLhRYsisFiaJwvp1QF+I3KjD5FOxut14q/GzovUe6orHLesW2C7d754kRm53h5gqrz6sFl6sxc4BVtE/g== + dependencies: + "@babel/types" "^7.22.5" + +"@babel/helper-string-parser@^7.23.4": + version "7.23.4" + resolved "https://registry.yarnpkg.com/@babel/helper-string-parser/-/helper-string-parser-7.23.4.tgz#9478c707febcbbe1ddb38a3d91a2e054ae622d83" + integrity sha512-803gmbQdqwdf4olxrX4AJyFBV/RTr3rSmOj0rKwesmzlfhYNDEs+/iOcznzpNWlJlIlTJC2QfPFcHB6DlzdVLQ== + +"@babel/helper-validator-identifier@^7.22.20": + version "7.22.20" + resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.20.tgz#c4ae002c61d2879e724581d96665583dbc1dc0e0" + integrity sha512-Y4OZ+ytlatR8AI+8KZfKuL5urKp7qey08ha31L8b3BwewJAoJamTzyvxPR/5D+KkdJCGPq/+8TukHBlY10FX9A== + +"@babel/helper-validator-option@^7.23.5": + version "7.23.5" + resolved "https://registry.yarnpkg.com/@babel/helper-validator-option/-/helper-validator-option-7.23.5.tgz#907a3fbd4523426285365d1206c423c4c5520307" + integrity sha512-85ttAOMLsr53VgXkTbkx8oA6YTfT4q7/HzXSLEYmjcSTJPMPQtvq1BD79Byep5xMUYbGRzEpDsjUf3dyp54IKw== + +"@babel/helpers@^7.23.7": + version "7.23.8" + resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.23.8.tgz#fc6b2d65b16847fd50adddbd4232c76378959e34" + integrity sha512-KDqYz4PiOWvDFrdHLPhKtCThtIcKVy6avWD2oG4GEvyQ+XDZwHD4YQd+H2vNMnq2rkdxsDkU82T+Vk8U/WXHRQ== + dependencies: + "@babel/template" "^7.22.15" + "@babel/traverse" "^7.23.7" + "@babel/types" "^7.23.6" + +"@babel/highlight@^7.23.4": + version "7.23.4" + resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.23.4.tgz#edaadf4d8232e1a961432db785091207ead0621b" + integrity sha512-acGdbYSfp2WheJoJm/EBBBLh/ID8KDc64ISZ9DYtBmC8/Q204PZJLHyzeB5qMzJ5trcOkybd78M4x2KWsUq++A== + dependencies: + "@babel/helper-validator-identifier" "^7.22.20" + chalk "^2.4.2" + js-tokens "^4.0.0" + +"@babel/parser@^7.1.0", "@babel/parser@^7.20.7", "@babel/parser@^7.22.15", "@babel/parser@^7.23.3", "@babel/parser@^7.23.6": + version "7.23.6" + resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.23.6.tgz#ba1c9e512bda72a47e285ae42aff9d2a635a9e3b" + integrity sha512-Z2uID7YJ7oNvAI20O9X0bblw7Qqs8Q2hFy0R9tAfnfLkp5MW0UH9eUvnDSnFwKZ0AvgS1ucqR4KzvVHgnke1VQ== + +"@babel/plugin-syntax-jsx@^7.23.3": + version "7.23.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.23.3.tgz#8f2e4f8a9b5f9aa16067e142c1ac9cd9f810f473" + integrity sha512-EB2MELswq55OHUoRZLGg/zC7QWUKfNLpE57m/S2yr1uEneIgsTgrSzXP3NXEsMkVn76OlaVVnzN+ugObuYGwhg== + dependencies: + "@babel/helper-plugin-utils" "^7.22.5" + +"@babel/plugin-transform-react-jsx@^7.22.5": + version "7.23.4" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-jsx/-/plugin-transform-react-jsx-7.23.4.tgz#393f99185110cea87184ea47bcb4a7b0c2e39312" + integrity sha512-5xOpoPguCZCRbo/JeHlloSkTA8Bld1J/E1/kLfD1nsuiW1m8tduTA1ERCgIZokDflX/IBzKcqR3l7VlRgiIfHA== + dependencies: + "@babel/helper-annotate-as-pure" "^7.22.5" + "@babel/helper-module-imports" "^7.22.15" + "@babel/helper-plugin-utils" "^7.22.5" + "@babel/plugin-syntax-jsx" "^7.23.3" + "@babel/types" "^7.23.4" + +"@babel/template@^7.22.15": + version "7.22.15" + resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.22.15.tgz#09576efc3830f0430f4548ef971dde1350ef2f38" + integrity sha512-QPErUVm4uyJa60rkI73qneDacvdvzxshT3kksGqlGWYdOTIUOwJ7RDUL8sGqslY1uXWSL6xMFKEXDS3ox2uF0w== + dependencies: + "@babel/code-frame" "^7.22.13" + "@babel/parser" "^7.22.15" + "@babel/types" "^7.22.15" + +"@babel/traverse@^7.23.3", "@babel/traverse@^7.23.7": + version "7.23.7" + resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.23.7.tgz#9a7bf285c928cb99b5ead19c3b1ce5b310c9c305" + integrity sha512-tY3mM8rH9jM0YHFGyfC0/xf+SB5eKUu7HPj7/k3fpi9dAlsMc5YbQvDi0Sh2QTPXqMhyaAtzAr807TIyfQrmyg== + dependencies: + "@babel/code-frame" "^7.23.5" + "@babel/generator" "^7.23.6" + "@babel/helper-environment-visitor" "^7.22.20" + "@babel/helper-function-name" "^7.23.0" + "@babel/helper-hoist-variables" "^7.22.5" + "@babel/helper-split-export-declaration" "^7.22.6" + "@babel/parser" "^7.23.6" + "@babel/types" "^7.23.6" + debug "^4.3.1" + globals "^11.1.0" + +"@babel/types@^7.0.0", "@babel/types@^7.20.7", "@babel/types@^7.22.15", "@babel/types@^7.22.5", "@babel/types@^7.23.0", "@babel/types@^7.23.3", "@babel/types@^7.23.4", "@babel/types@^7.23.6": + version "7.23.6" + resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.23.6.tgz#be33fdb151e1f5a56877d704492c240fc71c7ccd" + integrity sha512-+uarb83brBzPKN38NX1MkB6vb6+mwvR6amUulqAE7ccQw1pEl+bCia9TbdG1lsnFP7lZySvUn37CHyXQdfTwzg== + dependencies: + "@babel/helper-string-parser" "^7.23.4" + "@babel/helper-validator-identifier" "^7.22.20" + to-fast-properties "^2.0.0" + +"@ctrl/tinycolor@^3.6.0": + version "3.6.1" + resolved "https://registry.yarnpkg.com/@ctrl/tinycolor/-/tinycolor-3.6.1.tgz#b6c75a56a1947cc916ea058772d666a2c8932f31" + integrity sha512-SITSV6aIXsuVNV3f3O0f2n/cgyEDWoSqtZMYiAmcsYHydcKrOz3gUxB/iXd/Qf08+IZX4KpgNbvUdMBmWz+kcA== + +"@emmetio/abbreviation@^2.3.3": + version "2.3.3" + resolved "https://registry.yarnpkg.com/@emmetio/abbreviation/-/abbreviation-2.3.3.tgz#ed2b88fe37b972292d6026c7c540aaf887cecb6e" + integrity sha512-mgv58UrU3rh4YgbE/TzgLQwJ3pFsHHhCLqY20aJq+9comytTXUDNGG/SMtSeMJdkpxgXSXunBGLD8Boka3JyVA== + dependencies: + "@emmetio/scanner" "^1.0.4" + +"@emmetio/css-abbreviation@^2.1.8": + version "2.1.8" + resolved "https://registry.yarnpkg.com/@emmetio/css-abbreviation/-/css-abbreviation-2.1.8.tgz#b785313486eba6cb7eb623ad39378c4e1063dc00" + integrity sha512-s9yjhJ6saOO/uk1V74eifykk2CBYi01STTK3WlXWGOepyKa23ymJ053+DNQjpFcy1ingpaO7AxCcwLvHFY9tuw== + dependencies: + "@emmetio/scanner" "^1.0.4" + +"@emmetio/scanner@^1.0.4": + version "1.0.4" + resolved "https://registry.yarnpkg.com/@emmetio/scanner/-/scanner-1.0.4.tgz#e9cdc67194fd91f8b7eb141014be4f2d086c15f1" + integrity sha512-IqRuJtQff7YHHBk4G8YZ45uB9BaAGcwQeVzgj/zj8/UdOhtQpEIupUhSk8dys6spFIWVZVeK20CzGEnqR5SbqA== + +"@esbuild/aix-ppc64@0.19.11": + version "0.19.11" + resolved "https://registry.yarnpkg.com/@esbuild/aix-ppc64/-/aix-ppc64-0.19.11.tgz#2acd20be6d4f0458bc8c784103495ff24f13b1d3" + integrity sha512-FnzU0LyE3ySQk7UntJO4+qIiQgI7KoODnZg5xzXIrFJlKd2P2gwHsHY4927xj9y5PJmJSzULiUCWmv7iWnNa7g== + +"@esbuild/android-arm64@0.19.11": + version "0.19.11" + resolved "https://registry.yarnpkg.com/@esbuild/android-arm64/-/android-arm64-0.19.11.tgz#b45d000017385c9051a4f03e17078abb935be220" + integrity sha512-aiu7K/5JnLj//KOnOfEZ0D90obUkRzDMyqd/wNAUQ34m4YUPVhRZpnqKV9uqDGxT7cToSDnIHsGooyIczu9T+Q== + +"@esbuild/android-arm@0.19.11": + version "0.19.11" + resolved "https://registry.yarnpkg.com/@esbuild/android-arm/-/android-arm-0.19.11.tgz#f46f55414e1c3614ac682b29977792131238164c" + integrity sha512-5OVapq0ClabvKvQ58Bws8+wkLCV+Rxg7tUVbo9xu034Nm536QTII4YzhaFriQ7rMrorfnFKUsArD2lqKbFY4vw== + +"@esbuild/android-x64@0.19.11": + version "0.19.11" + resolved "https://registry.yarnpkg.com/@esbuild/android-x64/-/android-x64-0.19.11.tgz#bfc01e91740b82011ef503c48f548950824922b2" + integrity sha512-eccxjlfGw43WYoY9QgB82SgGgDbibcqyDTlk3l3C0jOVHKxrjdc9CTwDUQd0vkvYg5um0OH+GpxYvp39r+IPOg== + +"@esbuild/darwin-arm64@0.19.11": + version "0.19.11" + resolved "https://registry.yarnpkg.com/@esbuild/darwin-arm64/-/darwin-arm64-0.19.11.tgz#533fb7f5a08c37121d82c66198263dcc1bed29bf" + integrity sha512-ETp87DRWuSt9KdDVkqSoKoLFHYTrkyz2+65fj9nfXsaV3bMhTCjtQfw3y+um88vGRKRiF7erPrh/ZuIdLUIVxQ== + +"@esbuild/darwin-x64@0.19.11": + version "0.19.11" + resolved "https://registry.yarnpkg.com/@esbuild/darwin-x64/-/darwin-x64-0.19.11.tgz#62f3819eff7e4ddc656b7c6815a31cf9a1e7d98e" + integrity sha512-fkFUiS6IUK9WYUO/+22omwetaSNl5/A8giXvQlcinLIjVkxwTLSktbF5f/kJMftM2MJp9+fXqZ5ezS7+SALp4g== + +"@esbuild/freebsd-arm64@0.19.11": + version "0.19.11" + resolved "https://registry.yarnpkg.com/@esbuild/freebsd-arm64/-/freebsd-arm64-0.19.11.tgz#d478b4195aa3ca44160272dab85ef8baf4175b4a" + integrity sha512-lhoSp5K6bxKRNdXUtHoNc5HhbXVCS8V0iZmDvyWvYq9S5WSfTIHU2UGjcGt7UeS6iEYp9eeymIl5mJBn0yiuxA== + +"@esbuild/freebsd-x64@0.19.11": + version "0.19.11" + resolved "https://registry.yarnpkg.com/@esbuild/freebsd-x64/-/freebsd-x64-0.19.11.tgz#7bdcc1917409178257ca6a1a27fe06e797ec18a2" + integrity sha512-JkUqn44AffGXitVI6/AbQdoYAq0TEullFdqcMY/PCUZ36xJ9ZJRtQabzMA+Vi7r78+25ZIBosLTOKnUXBSi1Kw== + +"@esbuild/linux-arm64@0.19.11": + version "0.19.11" + resolved "https://registry.yarnpkg.com/@esbuild/linux-arm64/-/linux-arm64-0.19.11.tgz#58ad4ff11685fcc735d7ff4ca759ab18fcfe4545" + integrity sha512-LneLg3ypEeveBSMuoa0kwMpCGmpu8XQUh+mL8XXwoYZ6Be2qBnVtcDI5azSvh7vioMDhoJFZzp9GWp9IWpYoUg== + +"@esbuild/linux-arm@0.19.11": + version "0.19.11" + resolved "https://registry.yarnpkg.com/@esbuild/linux-arm/-/linux-arm-0.19.11.tgz#ce82246d873b5534d34de1e5c1b33026f35e60e3" + integrity sha512-3CRkr9+vCV2XJbjwgzjPtO8T0SZUmRZla+UL1jw+XqHZPkPgZiyWvbDvl9rqAN8Zl7qJF0O/9ycMtjU67HN9/Q== + +"@esbuild/linux-ia32@0.19.11": + version "0.19.11" + resolved "https://registry.yarnpkg.com/@esbuild/linux-ia32/-/linux-ia32-0.19.11.tgz#cbae1f313209affc74b80f4390c4c35c6ab83fa4" + integrity sha512-caHy++CsD8Bgq2V5CodbJjFPEiDPq8JJmBdeyZ8GWVQMjRD0sU548nNdwPNvKjVpamYYVL40AORekgfIubwHoA== + +"@esbuild/linux-loong64@0.19.11": + version "0.19.11" + resolved "https://registry.yarnpkg.com/@esbuild/linux-loong64/-/linux-loong64-0.19.11.tgz#5f32aead1c3ec8f4cccdb7ed08b166224d4e9121" + integrity sha512-ppZSSLVpPrwHccvC6nQVZaSHlFsvCQyjnvirnVjbKSHuE5N24Yl8F3UwYUUR1UEPaFObGD2tSvVKbvR+uT1Nrg== + +"@esbuild/linux-mips64el@0.19.11": + version "0.19.11" + resolved "https://registry.yarnpkg.com/@esbuild/linux-mips64el/-/linux-mips64el-0.19.11.tgz#38eecf1cbb8c36a616261de858b3c10d03419af9" + integrity sha512-B5x9j0OgjG+v1dF2DkH34lr+7Gmv0kzX6/V0afF41FkPMMqaQ77pH7CrhWeR22aEeHKaeZVtZ6yFwlxOKPVFyg== + +"@esbuild/linux-ppc64@0.19.11": + version "0.19.11" + resolved "https://registry.yarnpkg.com/@esbuild/linux-ppc64/-/linux-ppc64-0.19.11.tgz#9c5725a94e6ec15b93195e5a6afb821628afd912" + integrity sha512-MHrZYLeCG8vXblMetWyttkdVRjQlQUb/oMgBNurVEnhj4YWOr4G5lmBfZjHYQHHN0g6yDmCAQRR8MUHldvvRDA== + +"@esbuild/linux-riscv64@0.19.11": + version "0.19.11" + resolved "https://registry.yarnpkg.com/@esbuild/linux-riscv64/-/linux-riscv64-0.19.11.tgz#2dc4486d474a2a62bbe5870522a9a600e2acb916" + integrity sha512-f3DY++t94uVg141dozDu4CCUkYW+09rWtaWfnb3bqe4w5NqmZd6nPVBm+qbz7WaHZCoqXqHz5p6CM6qv3qnSSQ== + +"@esbuild/linux-s390x@0.19.11": + version "0.19.11" + resolved "https://registry.yarnpkg.com/@esbuild/linux-s390x/-/linux-s390x-0.19.11.tgz#4ad8567df48f7dd4c71ec5b1753b6f37561a65a8" + integrity sha512-A5xdUoyWJHMMlcSMcPGVLzYzpcY8QP1RtYzX5/bS4dvjBGVxdhuiYyFwp7z74ocV7WDc0n1harxmpq2ePOjI0Q== + +"@esbuild/linux-x64@0.19.11": + version "0.19.11" + resolved "https://registry.yarnpkg.com/@esbuild/linux-x64/-/linux-x64-0.19.11.tgz#b7390c4d5184f203ebe7ddaedf073df82a658766" + integrity sha512-grbyMlVCvJSfxFQUndw5mCtWs5LO1gUlwP4CDi4iJBbVpZcqLVT29FxgGuBJGSzyOxotFG4LoO5X+M1350zmPA== + +"@esbuild/netbsd-x64@0.19.11": + version "0.19.11" + resolved "https://registry.yarnpkg.com/@esbuild/netbsd-x64/-/netbsd-x64-0.19.11.tgz#d633c09492a1721377f3bccedb2d821b911e813d" + integrity sha512-13jvrQZJc3P230OhU8xgwUnDeuC/9egsjTkXN49b3GcS5BKvJqZn86aGM8W9pd14Kd+u7HuFBMVtrNGhh6fHEQ== + +"@esbuild/openbsd-x64@0.19.11": + version "0.19.11" + resolved "https://registry.yarnpkg.com/@esbuild/openbsd-x64/-/openbsd-x64-0.19.11.tgz#17388c76e2f01125bf831a68c03a7ffccb65d1a2" + integrity sha512-ysyOGZuTp6SNKPE11INDUeFVVQFrhcNDVUgSQVDzqsqX38DjhPEPATpid04LCoUr2WXhQTEZ8ct/EgJCUDpyNw== + +"@esbuild/sunos-x64@0.19.11": + version "0.19.11" + resolved "https://registry.yarnpkg.com/@esbuild/sunos-x64/-/sunos-x64-0.19.11.tgz#e320636f00bb9f4fdf3a80e548cb743370d41767" + integrity sha512-Hf+Sad9nVwvtxy4DXCZQqLpgmRTQqyFyhT3bZ4F2XlJCjxGmRFF0Shwn9rzhOYRB61w9VMXUkxlBy56dk9JJiQ== + +"@esbuild/win32-arm64@0.19.11": + version "0.19.11" + resolved "https://registry.yarnpkg.com/@esbuild/win32-arm64/-/win32-arm64-0.19.11.tgz#c778b45a496e90b6fc373e2a2bb072f1441fe0ee" + integrity sha512-0P58Sbi0LctOMOQbpEOvOL44Ne0sqbS0XWHMvvrg6NE5jQ1xguCSSw9jQeUk2lfrXYsKDdOe6K+oZiwKPilYPQ== + +"@esbuild/win32-ia32@0.19.11": + version "0.19.11" + resolved "https://registry.yarnpkg.com/@esbuild/win32-ia32/-/win32-ia32-0.19.11.tgz#481a65fee2e5cce74ec44823e6b09ecedcc5194c" + integrity sha512-6YOrWS+sDJDmshdBIQU+Uoyh7pQKrdykdefC1avn76ss5c+RN6gut3LZA4E2cH5xUEp5/cA0+YxRaVtRAb0xBg== + +"@esbuild/win32-x64@0.19.11": + version "0.19.11" + resolved "https://registry.yarnpkg.com/@esbuild/win32-x64/-/win32-x64-0.19.11.tgz#a5d300008960bb39677c46bf16f53ec70d8dee04" + integrity sha512-vfkhltrjCAb603XaFhqhAF4LGDi2M4OrCRrFusyQ+iTLQ/o60QQXxc9cZC/FFpihBI9N1Grn6SMKVJ4KP7Fuiw== + +"@expressive-code/core@^0.31.0": + version "0.31.0" + resolved "https://registry.yarnpkg.com/@expressive-code/core/-/core-0.31.0.tgz#f54a5975fe0239602a911853916d6adc91f98772" + integrity sha512-zeCuojWRYeFs0UDOhzpKMzpjI/tJPCQna4jcVp5SJLMn4qNtHXgVmz3AngoMFoFcAlK6meE3wxzy//0d6K4NPw== + dependencies: + "@ctrl/tinycolor" "^3.6.0" + hast-util-to-html "^8.0.4" + hastscript "^7.2.0" + postcss "^8.4.21" + postcss-nested "^6.0.1" + +"@expressive-code/plugin-frames@^0.31.0": + version "0.31.0" + resolved "https://registry.yarnpkg.com/@expressive-code/plugin-frames/-/plugin-frames-0.31.0.tgz#fe3325fb073edbbf5a3f0db0550cad8ee3cb1706" + integrity sha512-eYWfK3i4w2gSpOGBFNnu05JKSXC90APgUNdam8y5i0Ie2CVAwpxDtEp0NRqugvEKC0aMJe6ZmHN5Hu2WAVJmig== + dependencies: + "@expressive-code/core" "^0.31.0" + hastscript "^7.2.0" + +"@expressive-code/plugin-shiki@^0.31.0": + version "0.31.0" + resolved "https://registry.yarnpkg.com/@expressive-code/plugin-shiki/-/plugin-shiki-0.31.0.tgz#bdaf83ba4ec0a0aa91b0416668bd437f20d1eec0" + integrity sha512-fU5wPPfV1LGcS+Z1wcEkzI1fzBq9IAdt0DN0ni8sT7E+gpkULda4GA4IFD9iWKCGIhSDsBbG+bjc9hrYoJsDIQ== + dependencies: + "@expressive-code/core" "^0.31.0" + shikiji "^0.8.0" + +"@expressive-code/plugin-text-markers@^0.31.0": + version "0.31.0" + resolved "https://registry.yarnpkg.com/@expressive-code/plugin-text-markers/-/plugin-text-markers-0.31.0.tgz#b5b9c25ca484ce4d62aa88ee4e2b5623a1e016e9" + integrity sha512-32o3pPMBq6bVUfRsAfFyqNpHbD1Z3iftoX9yt95F5zakLMsmHzZL4f0jyNr8XpXe7qcTnl0kIijBkUpvS6Pxfg== + dependencies: + "@expressive-code/core" "^0.31.0" + hastscript "^7.2.0" + unist-util-visit-parents "^5.1.3" + +"@jridgewell/gen-mapping@^0.3.0", "@jridgewell/gen-mapping@^0.3.2": + version "0.3.3" + resolved "https://registry.yarnpkg.com/@jridgewell/gen-mapping/-/gen-mapping-0.3.3.tgz#7e02e6eb5df901aaedb08514203b096614024098" + integrity sha512-HLhSWOLRi875zjjMG/r+Nv0oCW8umGb0BgEhyX3dDX3egwZtB8PqLnjz3yedt8R5StBrzcg4aBpnh8UA9D1BoQ== + dependencies: + "@jridgewell/set-array" "^1.0.1" + "@jridgewell/sourcemap-codec" "^1.4.10" + "@jridgewell/trace-mapping" "^0.3.9" + +"@jridgewell/resolve-uri@^3.1.0": + version "3.1.1" + resolved "https://registry.yarnpkg.com/@jridgewell/resolve-uri/-/resolve-uri-3.1.1.tgz#c08679063f279615a3326583ba3a90d1d82cc721" + integrity sha512-dSYZh7HhCDtCKm4QakX0xFpsRDqjjtZf/kjI/v3T3Nwt5r8/qz/M19F9ySyOqU94SXBmeG9ttTul+YnR4LOxFA== + +"@jridgewell/set-array@^1.0.1": + version "1.1.2" + resolved "https://registry.yarnpkg.com/@jridgewell/set-array/-/set-array-1.1.2.tgz#7c6cf998d6d20b914c0a55a91ae928ff25965e72" + integrity sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw== + +"@jridgewell/sourcemap-codec@^1.4.10", "@jridgewell/sourcemap-codec@^1.4.14", "@jridgewell/sourcemap-codec@^1.4.15": + version "1.4.15" + resolved "https://registry.yarnpkg.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz#d7c6e6755c78567a951e04ab52ef0fd26de59f32" + integrity sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg== + +"@jridgewell/trace-mapping@^0.3.17", "@jridgewell/trace-mapping@^0.3.9": + version "0.3.22" + resolved "https://registry.yarnpkg.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.22.tgz#72a621e5de59f5f1ef792d0793a82ee20f645e4c" + integrity sha512-Wf963MzWtA2sjrNt+g18IAln9lKnlRp+K2eH4jjIoF1wYeq3aMREpG09xhlhdzS0EjwU7qmUJYangWa+151vZw== + dependencies: + "@jridgewell/resolve-uri" "^3.1.0" + "@jridgewell/sourcemap-codec" "^1.4.14" + +"@mdx-js/mdx@^3.0.0": + version "3.0.0" + resolved "https://registry.yarnpkg.com/@mdx-js/mdx/-/mdx-3.0.0.tgz#37ef87685143fafedf1165f0a79e9fe95fbe5154" + integrity sha512-Icm0TBKBLYqroYbNW3BPnzMGn+7mwpQOK310aZ7+fkCtiU3aqv2cdcX+nd0Ydo3wI5Rx8bX2Z2QmGb/XcAClCw== + dependencies: + "@types/estree" "^1.0.0" + "@types/estree-jsx" "^1.0.0" + "@types/hast" "^3.0.0" + "@types/mdx" "^2.0.0" + collapse-white-space "^2.0.0" + devlop "^1.0.0" + estree-util-build-jsx "^3.0.0" + estree-util-is-identifier-name "^3.0.0" + estree-util-to-js "^2.0.0" + estree-walker "^3.0.0" + hast-util-to-estree "^3.0.0" + hast-util-to-jsx-runtime "^2.0.0" + markdown-extensions "^2.0.0" + periscopic "^3.0.0" + remark-mdx "^3.0.0" + remark-parse "^11.0.0" + remark-rehype "^11.0.0" + source-map "^0.7.0" + unified "^11.0.0" + unist-util-position-from-estree "^2.0.0" + unist-util-stringify-position "^4.0.0" + unist-util-visit "^5.0.0" + vfile "^6.0.0" + +"@nodelib/fs.scandir@2.1.5": + version "2.1.5" + resolved "https://registry.yarnpkg.com/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz#7619c2eb21b25483f6d167548b4cfd5a7488c3d5" + integrity sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g== + dependencies: + "@nodelib/fs.stat" "2.0.5" + run-parallel "^1.1.9" + +"@nodelib/fs.stat@2.0.5", "@nodelib/fs.stat@^2.0.2": + version "2.0.5" + resolved "https://registry.yarnpkg.com/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz#5bd262af94e9d25bd1e71b05deed44876a222e8b" + integrity sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A== + +"@nodelib/fs.walk@^1.2.3": + version "1.2.8" + resolved "https://registry.yarnpkg.com/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz#e95737e8bb6746ddedf69c556953494f196fe69a" + integrity sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg== + dependencies: + "@nodelib/fs.scandir" "2.1.5" + fastq "^1.6.0" + +"@pagefind/darwin-arm64@1.0.4": + version "1.0.4" + resolved "https://registry.yarnpkg.com/@pagefind/darwin-arm64/-/darwin-arm64-1.0.4.tgz#01cf61eeeb5ed3f2ce42a9cddfa6826fa30164c2" + integrity sha512-2OcthvceX2xhm5XbgOmW+lT45oLuHqCmvFeFtxh1gsuP5cO8vcD8ZH8Laj4pXQFCcK6eAdSShx+Ztx/LsQWZFQ== + +"@pagefind/darwin-x64@1.0.4": + version "1.0.4" + resolved "https://registry.yarnpkg.com/@pagefind/darwin-x64/-/darwin-x64-1.0.4.tgz#7d8a63503c8ad39fb10faefa6838162b6845d4e7" + integrity sha512-xkdvp0D9Ld/ZKsjo/y1bgfhTEU72ITimd2PMMQtts7jf6JPIOJbsiErCvm37m/qMFuPGEq/8d+fZ4pydOj08HQ== + +"@pagefind/default-ui@^1.0.3": + version "1.0.4" + resolved "https://registry.yarnpkg.com/@pagefind/default-ui/-/default-ui-1.0.4.tgz#fd12947c17f5f4ecd3b414bfd532c30a4326de83" + integrity sha512-edkcaPSKq67C49Vehjo+LQCpT615v4d7JRhfGzFPccePvdklaL+VXrfghN/uIfsdoG+HoLI1PcYy2iFcB9CTkw== + +"@pagefind/linux-arm64@1.0.4": + version "1.0.4" + resolved "https://registry.yarnpkg.com/@pagefind/linux-arm64/-/linux-arm64-1.0.4.tgz#d755a104125eaac32201b1359e3cd575a6382674" + integrity sha512-jGBrcCzIrMnNxLKVtogaQyajVfTAXM59KlBEwg6vTn8NW4fQ6nuFbbhlG4dTIsaamjEM5e8ZBEAKZfTB/qd9xw== + +"@pagefind/linux-x64@1.0.4": + version "1.0.4" + resolved "https://registry.yarnpkg.com/@pagefind/linux-x64/-/linux-x64-1.0.4.tgz#f8992b9a46d382d698c85cfa8c2e909d8e5b79ba" + integrity sha512-LIn/QcvcEtLEBqKe5vpSbSC2O3fvqbRCWOTIklslqSORisCsvzsWbP6j+LYxE9q0oWIfkdMoWV1vrE/oCKRxHg== + +"@pagefind/windows-x64@1.0.4": + version "1.0.4" + resolved "https://registry.yarnpkg.com/@pagefind/windows-x64/-/windows-x64-1.0.4.tgz#43e3dd4a4cb96dc5d0c1a2c239b9d5792b794667" + integrity sha512-QlBCVeZfj9fc9sbUgdOz76ZDbeK4xZihOBAFqGuRJeChfM8pnVeH9iqSnXgO3+m9oITugTf7PicyRUFAG76xeQ== + +"@rollup/rollup-android-arm-eabi@4.9.6": + version "4.9.6" + resolved "https://registry.yarnpkg.com/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.9.6.tgz#66b8d9cb2b3a474d115500f9ebaf43e2126fe496" + integrity sha512-MVNXSSYN6QXOulbHpLMKYi60ppyO13W9my1qogeiAqtjb2yR4LSmfU2+POvDkLzhjYLXz9Rf9+9a3zFHW1Lecg== + +"@rollup/rollup-android-arm64@4.9.6": + version "4.9.6" + resolved "https://registry.yarnpkg.com/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.9.6.tgz#46327d5b86420d2307946bec1535fdf00356e47d" + integrity sha512-T14aNLpqJ5wzKNf5jEDpv5zgyIqcpn1MlwCrUXLrwoADr2RkWA0vOWP4XxbO9aiO3dvMCQICZdKeDrFl7UMClw== + +"@rollup/rollup-darwin-arm64@4.9.6": + version "4.9.6" + resolved "https://registry.yarnpkg.com/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.9.6.tgz#166987224d2f8b1e2fd28ee90c447d52271d5e90" + integrity sha512-CqNNAyhRkTbo8VVZ5R85X73H3R5NX9ONnKbXuHisGWC0qRbTTxnF1U4V9NafzJbgGM0sHZpdO83pLPzq8uOZFw== + +"@rollup/rollup-darwin-x64@4.9.6": + version "4.9.6" + resolved "https://registry.yarnpkg.com/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.9.6.tgz#a2e6e096f74ccea6e2f174454c26aef6bcdd1274" + integrity sha512-zRDtdJuRvA1dc9Mp6BWYqAsU5oeLixdfUvkTHuiYOHwqYuQ4YgSmi6+/lPvSsqc/I0Omw3DdICx4Tfacdzmhog== + +"@rollup/rollup-linux-arm-gnueabihf@4.9.6": + version "4.9.6" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.9.6.tgz#09fcd4c55a2d6160c5865fec708a8e5287f30515" + integrity sha512-oNk8YXDDnNyG4qlNb6is1ojTOGL/tRhbbKeE/YuccItzerEZT68Z9gHrY3ROh7axDc974+zYAPxK5SH0j/G+QQ== + +"@rollup/rollup-linux-arm64-gnu@4.9.6": + version "4.9.6" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.9.6.tgz#19a3c0b6315c747ca9acf86e9b710cc2440f83c9" + integrity sha512-Z3O60yxPtuCYobrtzjo0wlmvDdx2qZfeAWTyfOjEDqd08kthDKexLpV97KfAeUXPosENKd8uyJMRDfFMxcYkDQ== + +"@rollup/rollup-linux-arm64-musl@4.9.6": + version "4.9.6" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.9.6.tgz#94aaf95fdaf2ad9335983a4552759f98e6b2e850" + integrity sha512-gpiG0qQJNdYEVad+1iAsGAbgAnZ8j07FapmnIAQgODKcOTjLEWM9sRb+MbQyVsYCnA0Im6M6QIq6ax7liws6eQ== + +"@rollup/rollup-linux-riscv64-gnu@4.9.6": + version "4.9.6" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.9.6.tgz#160510e63f4b12618af4013bddf1761cf9fc9880" + integrity sha512-+uCOcvVmFUYvVDr27aiyun9WgZk0tXe7ThuzoUTAukZJOwS5MrGbmSlNOhx1j80GdpqbOty05XqSl5w4dQvcOA== + +"@rollup/rollup-linux-x64-gnu@4.9.6": + version "4.9.6" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.9.6.tgz#5ac5d068ce0726bd0a96ca260d5bd93721c0cb98" + integrity sha512-HUNqM32dGzfBKuaDUBqFB7tP6VMN74eLZ33Q9Y1TBqRDn+qDonkAUyKWwF9BR9unV7QUzffLnz9GrnKvMqC/fw== + +"@rollup/rollup-linux-x64-musl@4.9.6": + version "4.9.6" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.9.6.tgz#bafa759ab43e8eab9edf242a8259ffb4f2a57a5d" + integrity sha512-ch7M+9Tr5R4FK40FHQk8VnML0Szi2KRujUgHXd/HjuH9ifH72GUmw6lStZBo3c3GB82vHa0ZoUfjfcM7JiiMrQ== + +"@rollup/rollup-win32-arm64-msvc@4.9.6": + version "4.9.6" + resolved "https://registry.yarnpkg.com/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.9.6.tgz#1cc3416682e5a20d8f088f26657e6e47f8db468e" + integrity sha512-VD6qnR99dhmTQ1mJhIzXsRcTBvTjbfbGGwKAHcu+52cVl15AC/kplkhxzW/uT0Xl62Y/meBKDZvoJSJN+vTeGA== + +"@rollup/rollup-win32-ia32-msvc@4.9.6": + version "4.9.6" + resolved "https://registry.yarnpkg.com/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.9.6.tgz#7d2251e1aa5e8a1e47c86891fe4547a939503461" + integrity sha512-J9AFDq/xiRI58eR2NIDfyVmTYGyIZmRcvcAoJ48oDld/NTR8wyiPUu2X/v1navJ+N/FGg68LEbX3Ejd6l8B7MQ== + +"@rollup/rollup-win32-x64-msvc@4.9.6": + version "4.9.6" + resolved "https://registry.yarnpkg.com/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.9.6.tgz#2c1fb69e02a3f1506f52698cfdc3a8b6386df9a6" + integrity sha512-jqzNLhNDvIZOrt69Ce4UjGRpXJBzhUBzawMwnaDAwyHriki3XollsewxWzOzz+4yOFDkuJHtTsZFwMxhYJWmLQ== + +"@types/acorn@^4.0.0": + version "4.0.6" + resolved "https://registry.yarnpkg.com/@types/acorn/-/acorn-4.0.6.tgz#d61ca5480300ac41a7d973dd5b84d0a591154a22" + integrity sha512-veQTnWP+1D/xbxVrPC3zHnCZRjSrKfhbMUlEA43iMZLu7EsnTtkJklIuwrCPbOi8YkvDQAiW05VQQFvvz9oieQ== + dependencies: + "@types/estree" "*" + +"@types/babel__core@^7.20.4": + version "7.20.5" + resolved "https://registry.yarnpkg.com/@types/babel__core/-/babel__core-7.20.5.tgz#3df15f27ba85319caa07ba08d0721889bb39c017" + integrity sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA== + dependencies: + "@babel/parser" "^7.20.7" + "@babel/types" "^7.20.7" + "@types/babel__generator" "*" + "@types/babel__template" "*" + "@types/babel__traverse" "*" + +"@types/babel__generator@*": + version "7.6.8" + resolved "https://registry.yarnpkg.com/@types/babel__generator/-/babel__generator-7.6.8.tgz#f836c61f48b1346e7d2b0d93c6dacc5b9535d3ab" + integrity sha512-ASsj+tpEDsEiFr1arWrlN6V3mdfjRMZt6LtK/Vp/kreFLnr5QH5+DhvD5nINYZXzwJvXeGq+05iUXcAzVrqWtw== + dependencies: + "@babel/types" "^7.0.0" + +"@types/babel__template@*": + version "7.4.4" + resolved "https://registry.yarnpkg.com/@types/babel__template/-/babel__template-7.4.4.tgz#5672513701c1b2199bc6dad636a9d7491586766f" + integrity sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A== + dependencies: + "@babel/parser" "^7.1.0" + "@babel/types" "^7.0.0" + +"@types/babel__traverse@*": + version "7.20.5" + resolved "https://registry.yarnpkg.com/@types/babel__traverse/-/babel__traverse-7.20.5.tgz#7b7502be0aa80cc4ef22978846b983edaafcd4dd" + integrity sha512-WXCyOcRtH37HAUkpXhUduaxdm82b4GSlyTqajXviN4EfiuPgNYR109xMCKvpl6zPIpua0DGlMEDCq+g8EdoheQ== + dependencies: + "@babel/types" "^7.20.7" + +"@types/debug@^4.0.0": + version "4.1.12" + resolved "https://registry.yarnpkg.com/@types/debug/-/debug-4.1.12.tgz#a155f21690871953410df4b6b6f53187f0500917" + integrity sha512-vIChWdVG3LG1SMxEvI/AK+FWJthlrqlTu7fbrlywTkkaONwk/UAGaULXRlf8vkzFBLVm0zkMdCquhL5aOjhXPQ== + dependencies: + "@types/ms" "*" + +"@types/estree-jsx@^1.0.0": + version "1.0.3" + resolved "https://registry.yarnpkg.com/@types/estree-jsx/-/estree-jsx-1.0.3.tgz#f8aa833ec986d82b8271a294a92ed1565bf2c66a" + integrity sha512-pvQ+TKeRHeiUGRhvYwRrQ/ISnohKkSJR14fT2yqyZ4e9K5vqc7hrtY2Y1Dw0ZwAzQ6DQsxsaCUuSIIi8v0Cq6w== + dependencies: + "@types/estree" "*" + +"@types/estree@*", "@types/estree@1.0.5", "@types/estree@^1.0.0": + version "1.0.5" + resolved "https://registry.yarnpkg.com/@types/estree/-/estree-1.0.5.tgz#a6ce3e556e00fd9895dd872dd172ad0d4bd687f4" + integrity sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw== + +"@types/hast@^2.0.0": + version "2.3.9" + resolved "https://registry.yarnpkg.com/@types/hast/-/hast-2.3.9.tgz#a9a1b5bbce46e8a1312e977364bacabc8e93d2cf" + integrity sha512-pTHyNlaMD/oKJmS+ZZUyFUcsZeBZpC0lmGquw98CqRVNgAdJZJeD7GoeLiT6Xbx5rU9VCjSt0RwEvDgzh4obFw== + dependencies: + "@types/unist" "^2" + +"@types/hast@^3.0.0", "@types/hast@^3.0.3": + version "3.0.3" + resolved "https://registry.yarnpkg.com/@types/hast/-/hast-3.0.3.tgz#7f75e6b43bc3f90316046a287d9ad3888309f7e1" + integrity sha512-2fYGlaDy/qyLlhidX42wAH0KBi2TCjKMH8CHmBXgRlJ3Y+OXTiqsPQ6IWarZKwF1JoUcAJdPogv1d4b0COTpmQ== + dependencies: + "@types/unist" "*" + +"@types/mdast@^4.0.0", "@types/mdast@^4.0.3": + version "4.0.3" + resolved "https://registry.yarnpkg.com/@types/mdast/-/mdast-4.0.3.tgz#1e011ff013566e919a4232d1701ad30d70cab333" + integrity sha512-LsjtqsyF+d2/yFOYaN22dHZI1Cpwkrj+g06G8+qtUKlhovPW89YhqSnfKtMbkgmEtYpH2gydRNULd6y8mciAFg== + dependencies: + "@types/unist" "*" + +"@types/mdx@^2.0.0": + version "2.0.10" + resolved "https://registry.yarnpkg.com/@types/mdx/-/mdx-2.0.10.tgz#0d7b57fb1d83e27656156e4ee0dfba96532930e4" + integrity sha512-Rllzc5KHk0Al5/WANwgSPl1/CwjqCy+AZrGd78zuK+jO9aDM6ffblZ+zIjgPNAaEBmlO0RYDvLNh7wD0zKVgEg== + +"@types/ms@*": + version "0.7.34" + resolved "https://registry.yarnpkg.com/@types/ms/-/ms-0.7.34.tgz#10964ba0dee6ac4cd462e2795b6bebd407303433" + integrity sha512-nG96G3Wp6acyAgJqGasjODb+acrI7KltPiRxzHPXnP3NgI28bpQDRv53olbqGXbfcgF5aiiHmO3xpwEpS5Ld9g== + +"@types/nlcst@^1.0.0": + version "1.0.4" + resolved "https://registry.yarnpkg.com/@types/nlcst/-/nlcst-1.0.4.tgz#3b8a9c279a2367602512588a0ba6a0e93634ee3e" + integrity sha512-ABoYdNQ/kBSsLvZAekMhIPMQ3YUZvavStpKYs7BjLLuKVmIMA0LUgZ7b54zzuWJRbHF80v1cNf4r90Vd6eMQDg== + dependencies: + "@types/unist" "^2" + +"@types/node@*": + version "20.11.5" + resolved "https://registry.yarnpkg.com/@types/node/-/node-20.11.5.tgz#be10c622ca7fcaa3cf226cf80166abc31389d86e" + integrity sha512-g557vgQjUUfN76MZAN/dt1z3dzcUsimuysco0KeluHgrPdJXkP/XdAURgyO2W9fZWHRtRBiVKzKn8vyOAwlG+w== + dependencies: + undici-types "~5.26.4" + +"@types/node@^17.0.5": + version "17.0.45" + resolved "https://registry.yarnpkg.com/@types/node/-/node-17.0.45.tgz#2c0fafd78705e7a18b7906b5201a522719dc5190" + integrity sha512-w+tIMs3rq2afQdsPJlODhoUEKzFP1ayaoyl1CcnwtIlsVe7K7bA1NGm4s3PraqTLlXnbIN84zuBlxBWo1u9BLw== + +"@types/parse5@^6.0.0": + version "6.0.3" + resolved "https://registry.yarnpkg.com/@types/parse5/-/parse5-6.0.3.tgz#705bb349e789efa06f43f128cef51240753424cb" + integrity sha512-SuT16Q1K51EAVPz1K29DJ/sXjhSQ0zjvsypYJ6tlwVsRV9jwW5Adq2ch8Dq8kDBCkYnELS7N7VNCSB5nC56t/g== + +"@types/sax@^1.2.1": + version "1.2.7" + resolved "https://registry.yarnpkg.com/@types/sax/-/sax-1.2.7.tgz#ba5fe7df9aa9c89b6dff7688a19023dd2963091d" + integrity sha512-rO73L89PJxeYM3s3pPPjiPgVVcymqU490g0YO5n5By0k2Erzj6tay/4lr1CHAAU4JyOWd1rpQ8bCf6cZfHU96A== + dependencies: + "@types/node" "*" + +"@types/unist@*", "@types/unist@^3.0.0": + version "3.0.2" + resolved "https://registry.yarnpkg.com/@types/unist/-/unist-3.0.2.tgz#6dd61e43ef60b34086287f83683a5c1b2dc53d20" + integrity sha512-dqId9J8K/vGi5Zr7oo212BGii5m3q5Hxlkwy3WpYuKPklmBEvsbMYYyLxAQpSffdLl/gdW0XUpKWFvYmyoWCoQ== + +"@types/unist@^2", "@types/unist@^2.0.0": + version "2.0.10" + resolved "https://registry.yarnpkg.com/@types/unist/-/unist-2.0.10.tgz#04ffa7f406ab628f7f7e97ca23e290cd8ab15efc" + integrity sha512-IfYcSBWE3hLpBg8+X2SEa8LVkJdJEkT2Ese2aaLs3ptGdVtABxndrMaxuFlQ1qdFf9Q5rDvDpxI3WwgvKFAsQA== + +"@ungap/structured-clone@^1.0.0": + version "1.2.0" + resolved "https://registry.yarnpkg.com/@ungap/structured-clone/-/structured-clone-1.2.0.tgz#756641adb587851b5ccb3e095daf27ae581c8406" + integrity sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ== + +"@volar/kit@~1.11.1": + version "1.11.1" + resolved "https://registry.yarnpkg.com/@volar/kit/-/kit-1.11.1.tgz#1b9f0ef4509a213b2053091715ece4d4ef882348" + integrity sha512-nqO+Hl9f1ygOK/3M7Hpnw0lhKvuMFhh823nilStpkTmm5WfrUnE+4WaQkb3dC6LM3TZq74j2m88yxRC+Z3sZZw== + dependencies: + "@volar/language-service" "1.11.1" + typesafe-path "^0.2.2" + vscode-languageserver-textdocument "^1.0.11" + vscode-uri "^3.0.8" + +"@volar/language-core@1.11.1", "@volar/language-core@~1.11.1": + version "1.11.1" + resolved "https://registry.yarnpkg.com/@volar/language-core/-/language-core-1.11.1.tgz#ecdf12ea8dc35fb8549e517991abcbf449a5ad4f" + integrity sha512-dOcNn3i9GgZAcJt43wuaEykSluAuOkQgzni1cuxLxTV0nJKanQztp7FxyswdRILaKH+P2XZMPRp2S4MV/pElCw== + dependencies: + "@volar/source-map" "1.11.1" + +"@volar/language-server@~1.11.1": + version "1.11.1" + resolved "https://registry.yarnpkg.com/@volar/language-server/-/language-server-1.11.1.tgz#59f421b9a4a8700871d970ae56f5865245c28b18" + integrity sha512-XYG4HcML2qimQV9UouQ7c1GuuqQw1NXoNDxAOAcfyYlz43P+HgzGQx4QEou+QMGHJeYIN86foDvkTN3fcopw9A== + dependencies: + "@volar/language-core" "1.11.1" + "@volar/language-service" "1.11.1" + "@volar/typescript" "1.11.1" + "@vscode/l10n" "^0.0.16" + path-browserify "^1.0.1" + request-light "^0.7.0" + vscode-languageserver "^9.0.1" + vscode-languageserver-protocol "^3.17.5" + vscode-languageserver-textdocument "^1.0.11" + vscode-uri "^3.0.8" + +"@volar/language-service@1.11.1", "@volar/language-service@~1.11.1": + version "1.11.1" + resolved "https://registry.yarnpkg.com/@volar/language-service/-/language-service-1.11.1.tgz#076bec41b8bdaa89ce61cb35be7da93df7285d87" + integrity sha512-dKo8z1UzQRPHnlXxwfONGrasS1wEWXMoLQiohZ8KgWqZALbekZCwdGImLZD4DeFGNjk3HTTdfeCzo3KjwohjEQ== + dependencies: + "@volar/language-core" "1.11.1" + "@volar/source-map" "1.11.1" + vscode-languageserver-protocol "^3.17.5" + vscode-languageserver-textdocument "^1.0.11" + vscode-uri "^3.0.8" + +"@volar/source-map@1.11.1", "@volar/source-map@~1.11.1": + version "1.11.1" + resolved "https://registry.yarnpkg.com/@volar/source-map/-/source-map-1.11.1.tgz#535b0328d9e2b7a91dff846cab4058e191f4452f" + integrity sha512-hJnOnwZ4+WT5iupLRnuzbULZ42L7BWWPMmruzwtLhJfpDVoZLjNBxHDi2sY2bgZXCKlpU5XcsMFoYrsQmPhfZg== + dependencies: + muggle-string "^0.3.1" + +"@volar/typescript@1.11.1", "@volar/typescript@~1.11.1": + version "1.11.1" + resolved "https://registry.yarnpkg.com/@volar/typescript/-/typescript-1.11.1.tgz#ba86c6f326d88e249c7f5cfe4b765be3946fd627" + integrity sha512-iU+t2mas/4lYierSnoFOeRFQUhAEMgsFuQxoxvwn5EdQopw43j+J27a4lt9LMInx1gLJBC6qL14WYGlgymaSMQ== + dependencies: + "@volar/language-core" "1.11.1" + path-browserify "^1.0.1" + +"@vscode/emmet-helper@^2.9.2": + version "2.9.2" + resolved "https://registry.yarnpkg.com/@vscode/emmet-helper/-/emmet-helper-2.9.2.tgz#cd5d1e64e7138ad76300e8cba5fd84f1c03e13ee" + integrity sha512-MaGuyW+fa13q3aYsluKqclmh62Hgp0BpKIqS66fCxfOaBcVQ1OnMQxRRgQUYnCkxFISAQlkJ0qWWPyXjro1Qrg== + dependencies: + emmet "^2.4.3" + jsonc-parser "^2.3.0" + vscode-languageserver-textdocument "^1.0.1" + vscode-languageserver-types "^3.15.1" + vscode-uri "^2.1.2" + +"@vscode/l10n@^0.0.16": + version "0.0.16" + resolved "https://registry.yarnpkg.com/@vscode/l10n/-/l10n-0.0.16.tgz#f075db346d0b08419a12540171b230bd803c42be" + integrity sha512-JT5CvrIYYCrmB+dCana8sUqJEcGB1ZDXNLMQ2+42bW995WmNoenijWMUdZfwmuQUTQcEVVIa2OecZzTYWUW9Cg== + +acorn-jsx@^5.0.0: + version "5.3.2" + resolved "https://registry.yarnpkg.com/acorn-jsx/-/acorn-jsx-5.3.2.tgz#7ed5bb55908b3b2f1bc55c6af1653bada7f07937" + integrity sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ== + +acorn@^8.0.0, acorn@^8.11.2: + version "8.11.3" + resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.11.3.tgz#71e0b14e13a4ec160724b38fb7b0f233b1b81d7a" + integrity sha512-Y9rRfJG5jcKOE0CLisYbojUjIrIEE7AGMzA/Sm4BslANhbS+cDMpgBdcPT91oJ7OuJ9hYJBx59RjbhxVnrF8Xg== + +ansi-align@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/ansi-align/-/ansi-align-3.0.1.tgz#0cdf12e111ace773a86e9a1fad1225c43cb19a59" + integrity sha512-IOfwwBF5iczOjp/WeY4YxyjqAFMQoZufdQWDd19SEExbVLNXqvpzSJ/M7Za4/sCPmQ0+GRquoA7bGcINcxew6w== + dependencies: + string-width "^4.1.0" + +ansi-regex@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-5.0.1.tgz#082cb2c89c9fe8659a311a53bd6a4dc5301db304" + integrity sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ== + +ansi-regex@^6.0.1: + version "6.0.1" + resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-6.0.1.tgz#3183e38fae9a65d7cb5e53945cd5897d0260a06a" + integrity sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA== + +ansi-styles@^3.2.1: + version "3.2.1" + resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-3.2.1.tgz#41fbb20243e50b12be0f04b8dedbf07520ce841d" + integrity sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA== + dependencies: + color-convert "^1.9.0" + +ansi-styles@^4.0.0: + version "4.3.0" + resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-4.3.0.tgz#edd803628ae71c04c85ae7a0906edad34b648937" + integrity sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg== + dependencies: + color-convert "^2.0.1" + +ansi-styles@^6.1.0: + version "6.2.1" + resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-6.2.1.tgz#0e62320cf99c21afff3b3012192546aacbfb05c5" + integrity sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug== + +anymatch@~3.1.2: + version "3.1.3" + resolved "https://registry.yarnpkg.com/anymatch/-/anymatch-3.1.3.tgz#790c58b19ba1720a84205b57c618d5ad8524973e" + integrity sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw== + dependencies: + normalize-path "^3.0.0" + picomatch "^2.0.4" + +arg@^5.0.0: + version "5.0.2" + resolved "https://registry.yarnpkg.com/arg/-/arg-5.0.2.tgz#c81433cc427c92c4dcf4865142dbca6f15acd59c" + integrity sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg== + +argparse@^1.0.7: + version "1.0.10" + resolved "https://registry.yarnpkg.com/argparse/-/argparse-1.0.10.tgz#bcd6791ea5ae09725e17e5ad988134cd40b3d911" + integrity sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg== + dependencies: + sprintf-js "~1.0.2" + +argparse@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/argparse/-/argparse-2.0.1.tgz#246f50f3ca78a3240f6c997e8a9bd1eac49e4b38" + integrity sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q== + +aria-query@^5.3.0: + version "5.3.0" + resolved "https://registry.yarnpkg.com/aria-query/-/aria-query-5.3.0.tgz#650c569e41ad90b51b3d7df5e5eed1c7549c103e" + integrity sha512-b0P0sZPKtyu8HkeRAfCq0IfURZK+SuwMjY1UXGBU27wpAiTwQAIlq56IbIO+ytk/JjS1fMR14ee5WBBfKi5J6A== + dependencies: + dequal "^2.0.3" + +array-iterate@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/array-iterate/-/array-iterate-2.0.1.tgz#6efd43f8295b3fee06251d3d62ead4bd9805dd24" + integrity sha512-I1jXZMjAgCMmxT4qxXfPXa6SthSoE8h6gkSI9BGGNv8mP8G/v0blc+qFnZu6K42vTOiuME596QaLO0TP3Lk0xg== + +astring@^1.8.0: + version "1.8.6" + resolved "https://registry.yarnpkg.com/astring/-/astring-1.8.6.tgz#2c9c157cf1739d67561c56ba896e6948f6b93731" + integrity sha512-ISvCdHdlTDlH5IpxQJIex7BWBywFWgjJSVdwst+/iQCoEYnyOaQ95+X1JGshuBjGp6nxKUy1jMgE3zPqN7fQdg== + +astro-expressive-code@^0.31.0: + version "0.31.0" + resolved "https://registry.yarnpkg.com/astro-expressive-code/-/astro-expressive-code-0.31.0.tgz#0aeabace7b07d0be556d0f9e5d3ac05277f8b3b0" + integrity sha512-o6eFrRSYLnlM/2FKkO3MgkbmVxT8N6DJcKvbRf1wbUcRXpz7s1KfugbdsaGw3ABEWUBuQIBsRppcGGw2L816Vg== + dependencies: + remark-expressive-code "^0.31.0" + +astro@^4.0.1: + version "4.2.1" + resolved "https://registry.yarnpkg.com/astro/-/astro-4.2.1.tgz#c0676bdd63cdd8ac2e46f53b7a72229ad302b24b" + integrity sha512-TcrveW2/lohmljrbTUgcDxajEdF1yK+zBvb7SXroloGix/d4jkegO6GANFgvyy0zprMyajW7qgJEFyhWUX86Vw== + dependencies: + "@astrojs/compiler" "^2.3.4" + "@astrojs/internal-helpers" "0.2.1" + "@astrojs/markdown-remark" "4.1.0" + "@astrojs/telemetry" "3.0.4" + "@babel/core" "^7.23.3" + "@babel/generator" "^7.23.3" + "@babel/parser" "^7.23.3" + "@babel/plugin-transform-react-jsx" "^7.22.5" + "@babel/traverse" "^7.23.3" + "@babel/types" "^7.23.3" + "@types/babel__core" "^7.20.4" + acorn "^8.11.2" + aria-query "^5.3.0" + axobject-query "^4.0.0" + boxen "^7.1.1" + chokidar "^3.5.3" + ci-info "^4.0.0" + clsx "^2.0.0" + common-ancestor-path "^1.0.1" + cookie "^0.6.0" + debug "^4.3.4" + deterministic-object-hash "^2.0.1" + devalue "^4.3.2" + diff "^5.1.0" + dlv "^1.1.3" + dset "^3.1.3" + es-module-lexer "^1.4.1" + esbuild "^0.19.6" + estree-walker "^3.0.3" + execa "^8.0.1" + fast-glob "^3.3.2" + flattie "^1.1.0" + github-slugger "^2.0.0" + gray-matter "^4.0.3" + html-escaper "^3.0.3" + http-cache-semantics "^4.1.1" + js-yaml "^4.1.0" + kleur "^4.1.4" + magic-string "^0.30.3" + mdast-util-to-hast "13.0.2" + mime "^3.0.0" + ora "^7.0.1" + p-limit "^5.0.0" + p-queue "^8.0.1" + path-to-regexp "^6.2.1" + preferred-pm "^3.1.2" + probe-image-size "^7.2.3" + prompts "^2.4.2" + rehype "^13.0.1" + resolve "^1.22.4" + semver "^7.5.4" + server-destroy "^1.0.1" + shikiji "^0.9.18" + string-width "^7.0.0" + strip-ansi "^7.1.0" + tsconfck "^3.0.0" + unist-util-visit "^5.0.0" + vfile "^6.0.1" + vite "^5.0.10" + vitefu "^0.2.5" + which-pm "^2.1.1" + yargs-parser "^21.1.1" + zod "^3.22.4" + optionalDependencies: + sharp "^0.32.6" + +axobject-query@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/axobject-query/-/axobject-query-4.0.0.tgz#04a4c90dce33cc5d606c76d6216e3b250ff70dab" + integrity sha512-+60uv1hiVFhHZeO+Lz0RYzsVHy5Wr1ayX0mwda9KPDVLNJgZ1T9Ny7VmFbLDzxsH0D87I86vgj3gFrjTJUYznw== + dependencies: + dequal "^2.0.3" + +b4a@^1.6.4: + version "1.6.4" + resolved "https://registry.yarnpkg.com/b4a/-/b4a-1.6.4.tgz#ef1c1422cae5ce6535ec191baeed7567443f36c9" + integrity sha512-fpWrvyVHEKyeEvbKZTVOeZF3VSKKWtJxFIxX/jaVPf+cLbGUSitjb49pHLqPV2BUNNZ0LcoeEGfE/YCpyDYHIw== + +bail@^2.0.0: + version "2.0.2" + resolved "https://registry.yarnpkg.com/bail/-/bail-2.0.2.tgz#d26f5cd8fe5d6f832a31517b9f7c356040ba6d5d" + integrity sha512-0xO6mYd7JB2YesxDKplafRpsiOzPt9V02ddPCLbY1xYGPOX24NTyN50qnUxgCPcSoYMhKpAuBTjQoRZCAkUDRw== + +base-64@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/base-64/-/base-64-1.0.0.tgz#09d0f2084e32a3fd08c2475b973788eee6ae8f4a" + integrity sha512-kwDPIFCGx0NZHog36dj+tHiwP4QMzsZ3AgMViUBKI0+V5n4U0ufTCUMhnQ04diaRI8EX/QcPfql7zlhZ7j4zgg== + +base64-js@^1.3.1: + version "1.5.1" + resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.5.1.tgz#1b1b440160a5bf7ad40b650f095963481903930a" + integrity sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA== + +bcp-47-match@^2.0.0: + version "2.0.3" + resolved "https://registry.yarnpkg.com/bcp-47-match/-/bcp-47-match-2.0.3.tgz#603226f6e5d3914a581408be33b28a53144b09d0" + integrity sha512-JtTezzbAibu8G0R9op9zb3vcWZd9JF6M0xOYGPn0fNCd7wOpRB1mU2mH9T8gaBGbAAyIIVgB2G7xG0GP98zMAQ== + +bcp-47@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/bcp-47/-/bcp-47-2.1.0.tgz#7e80734c3338fe8320894981dccf4968c3092df6" + integrity sha512-9IIS3UPrvIa1Ej+lVDdDwO7zLehjqsaByECw0bu2RRGP73jALm6FYbzI5gWbgHLvNdkvfXB5YrSbocZdOS0c0w== + dependencies: + is-alphabetical "^2.0.0" + is-alphanumerical "^2.0.0" + is-decimal "^2.0.0" + +binary-extensions@^2.0.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-2.2.0.tgz#75f502eeaf9ffde42fc98829645be4ea76bd9e2d" + integrity sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA== + +bl@^4.0.3: + version "4.1.0" + resolved "https://registry.yarnpkg.com/bl/-/bl-4.1.0.tgz#451535264182bec2fbbc83a62ab98cf11d9f7b3a" + integrity sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w== + dependencies: + buffer "^5.5.0" + inherits "^2.0.4" + readable-stream "^3.4.0" + +bl@^5.0.0: + version "5.1.0" + resolved "https://registry.yarnpkg.com/bl/-/bl-5.1.0.tgz#183715f678c7188ecef9fe475d90209400624273" + integrity sha512-tv1ZJHLfTDnXE6tMHv73YgSJaWR2AFuPwMntBe7XL/GBFHnT0CLnsHMogfk5+GzCDC5ZWarSCYaIGATZt9dNsQ== + dependencies: + buffer "^6.0.3" + inherits "^2.0.4" + readable-stream "^3.4.0" + +boolbase@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/boolbase/-/boolbase-1.0.0.tgz#68dff5fbe60c51eb37725ea9e3ed310dcc1e776e" + integrity sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww== + +boxen@^7.1.1: + version "7.1.1" + resolved "https://registry.yarnpkg.com/boxen/-/boxen-7.1.1.tgz#f9ba525413c2fec9cdb88987d835c4f7cad9c8f4" + integrity sha512-2hCgjEmP8YLWQ130n2FerGv7rYpfBmnmp9Uy2Le1vge6X3gZIfSmEzP5QTDElFxcvVcXlEn8Aq6MU/PZygIOog== + dependencies: + ansi-align "^3.0.1" + camelcase "^7.0.1" + chalk "^5.2.0" + cli-boxes "^3.0.0" + string-width "^5.1.2" + type-fest "^2.13.0" + widest-line "^4.0.1" + wrap-ansi "^8.1.0" + +braces@^3.0.2, braces@~3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/braces/-/braces-3.0.2.tgz#3454e1a462ee8d599e236df336cd9ea4f8afe107" + integrity sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A== + dependencies: + fill-range "^7.0.1" + +browserslist@^4.22.2: + version "4.22.2" + resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.22.2.tgz#704c4943072bd81ea18997f3bd2180e89c77874b" + integrity sha512-0UgcrvQmBDvZHFGdYUehrCNIazki7/lUP3kkoi/r3YB2amZbFM9J43ZRkJTXBUZK4gmx56+Sqk9+Vs9mwZx9+A== + dependencies: + caniuse-lite "^1.0.30001565" + electron-to-chromium "^1.4.601" + node-releases "^2.0.14" + update-browserslist-db "^1.0.13" + +buffer@^5.5.0: + version "5.7.1" + resolved "https://registry.yarnpkg.com/buffer/-/buffer-5.7.1.tgz#ba62e7c13133053582197160851a8f648e99eed0" + integrity sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ== + dependencies: + base64-js "^1.3.1" + ieee754 "^1.1.13" + +buffer@^6.0.3: + version "6.0.3" + resolved "https://registry.yarnpkg.com/buffer/-/buffer-6.0.3.tgz#2ace578459cc8fbe2a70aaa8f52ee63b6a74c6c6" + integrity sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA== + dependencies: + base64-js "^1.3.1" + ieee754 "^1.2.1" + +camelcase@^7.0.1: + version "7.0.1" + resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-7.0.1.tgz#f02e50af9fd7782bc8b88a3558c32fd3a388f048" + integrity sha512-xlx1yCK2Oc1APsPXDL2LdlNP6+uu8OCDdhOBSVT279M/S+y75O30C2VuD8T2ogdePBBl7PfPF4504tnLgX3zfw== + +caniuse-lite@^1.0.30001565: + version "1.0.30001579" + resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001579.tgz#45c065216110f46d6274311a4b3fcf6278e0852a" + integrity sha512-u5AUVkixruKHJjw/pj9wISlcMpgFWzSrczLZbrqBSxukQixmg0SJ5sZTpvaFvxU0HoQKd4yoyAogyrAz9pzJnA== + +ccount@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/ccount/-/ccount-2.0.1.tgz#17a3bf82302e0870d6da43a01311a8bc02a3ecf5" + integrity sha512-eyrF0jiFpY+3drT6383f1qhkbGsLSifNAjA61IUjZjmLCWjItY6LB9ft9YhoDgwfmclB2zhu51Lc7+95b8NRAg== + +chalk@^2.4.2: + version "2.4.2" + resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.4.2.tgz#cd42541677a54333cf541a49108c1432b44c9424" + integrity sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ== + dependencies: + ansi-styles "^3.2.1" + escape-string-regexp "^1.0.5" + supports-color "^5.3.0" + +chalk@^5.0.0, chalk@^5.2.0, chalk@^5.3.0: + version "5.3.0" + resolved "https://registry.yarnpkg.com/chalk/-/chalk-5.3.0.tgz#67c20a7ebef70e7f3970a01f90fa210cb6860385" + integrity sha512-dLitG79d+GV1Nb/VYcCDFivJeK1hiukt9QjRNVOsUtTy1rR1YJsmpGGTZ3qJos+uw7WmWF4wUwBd9jxjocFC2w== + +character-entities-html4@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/character-entities-html4/-/character-entities-html4-2.1.0.tgz#1f1adb940c971a4b22ba39ddca6b618dc6e56b2b" + integrity sha512-1v7fgQRj6hnSwFpq1Eu0ynr/CDEw0rXo2B61qXrLNdHZmPKgb7fqS1a2JwF0rISo9q77jDI8VMEHoApn8qDoZA== + +character-entities-legacy@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/character-entities-legacy/-/character-entities-legacy-3.0.0.tgz#76bc83a90738901d7bc223a9e93759fdd560125b" + integrity sha512-RpPp0asT/6ufRm//AJVwpViZbGM/MkjQFxJccQRHmISF/22NBtsHqAWmL+/pmkPWoIUJdWyeVleTl1wydHATVQ== + +character-entities@^2.0.0: + version "2.0.2" + resolved "https://registry.yarnpkg.com/character-entities/-/character-entities-2.0.2.tgz#2d09c2e72cd9523076ccb21157dff66ad43fcc22" + integrity sha512-shx7oQ0Awen/BRIdkjkvz54PnEEI/EjwXDSIZp86/KKdbafHh1Df/RYGBhn4hbe2+uKC9FnT5UCEdyPz3ai9hQ== + +character-reference-invalid@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/character-reference-invalid/-/character-reference-invalid-2.0.1.tgz#85c66b041e43b47210faf401278abf808ac45cb9" + integrity sha512-iBZ4F4wRbyORVsu0jPV7gXkOsGYjGHPmAyv+HiHG8gi5PtC9KI2j1+v8/tlibRvjoWX027ypmG/n0HtO5t7unw== + +chokidar@^3.5.3: + version "3.5.3" + resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-3.5.3.tgz#1cf37c8707b932bd1af1ae22c0432e2acd1903bd" + integrity sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw== + dependencies: + anymatch "~3.1.2" + braces "~3.0.2" + glob-parent "~5.1.2" + is-binary-path "~2.1.0" + is-glob "~4.0.1" + normalize-path "~3.0.0" + readdirp "~3.6.0" + optionalDependencies: + fsevents "~2.3.2" + +chownr@^1.1.1: + version "1.1.4" + resolved "https://registry.yarnpkg.com/chownr/-/chownr-1.1.4.tgz#6fc9d7b42d32a583596337666e7d08084da2cc6b" + integrity sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg== + +ci-info@^3.8.0: + version "3.9.0" + resolved "https://registry.yarnpkg.com/ci-info/-/ci-info-3.9.0.tgz#4279a62028a7b1f262f3473fc9605f5e218c59b4" + integrity sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ== + +ci-info@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/ci-info/-/ci-info-4.0.0.tgz#65466f8b280fc019b9f50a5388115d17a63a44f2" + integrity sha512-TdHqgGf9odd8SXNuxtUBVx8Nv+qZOejE6qyqiy5NtbYYQOeFa6zmHkxlPzmaLxWWHsU6nJmB7AETdVPi+2NBUg== + +cli-boxes@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/cli-boxes/-/cli-boxes-3.0.0.tgz#71a10c716feeba005e4504f36329ef0b17cf3145" + integrity sha512-/lzGpEWL/8PfI0BmBOPRwp0c/wFNX1RdUML3jK/RcSBA9T8mZDdQpqYBKtCFTOfQbwPqWEOpjqW+Fnayc0969g== + +cli-cursor@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/cli-cursor/-/cli-cursor-4.0.0.tgz#3cecfe3734bf4fe02a8361cbdc0f6fe28c6a57ea" + integrity sha512-VGtlMu3x/4DOtIUwEkRezxUZ2lBacNJCHash0N0WeZDBS+7Ux1dm3XWAgWYxLJFMMdOeXMHXorshEFhbMSGelg== + dependencies: + restore-cursor "^4.0.0" + +cli-spinners@^2.9.0: + version "2.9.2" + resolved "https://registry.yarnpkg.com/cli-spinners/-/cli-spinners-2.9.2.tgz#1773a8f4b9c4d6ac31563df53b3fc1d79462fe41" + integrity sha512-ywqV+5MmyL4E7ybXgKys4DugZbX0FC6LnwrhjuykIjnK9k8OQacQ7axGKnjDXWNhns0xot3bZI5h55H8yo9cJg== + +cliui@^8.0.1: + version "8.0.1" + resolved "https://registry.yarnpkg.com/cliui/-/cliui-8.0.1.tgz#0c04b075db02cbfe60dc8e6cf2f5486b1a3608aa" + integrity sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ== + dependencies: + string-width "^4.2.0" + strip-ansi "^6.0.1" + wrap-ansi "^7.0.0" + +clsx@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/clsx/-/clsx-2.1.0.tgz#e851283bcb5c80ee7608db18487433f7b23f77cb" + integrity sha512-m3iNNWpd9rl3jvvcBnu70ylMdrXt8Vlq4HYadnU5fwcOtvkSQWPmj7amUcDT2qYI7risszBjI5AUIUox9D16pg== + +collapse-white-space@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/collapse-white-space/-/collapse-white-space-2.1.0.tgz#640257174f9f42c740b40f3b55ee752924feefca" + integrity sha512-loKTxY1zCOuG4j9f6EPnuyyYkf58RnhhWTvRoZEokgB+WbdXehfjFviyOVYkqzEWz1Q5kRiZdBYS5SwxbQYwzw== + +color-convert@^1.9.0: + version "1.9.3" + resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-1.9.3.tgz#bb71850690e1f136567de629d2d5471deda4c1e8" + integrity sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg== + dependencies: + color-name "1.1.3" + +color-convert@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-2.0.1.tgz#72d3a68d598c9bdb3af2ad1e84f21d896abd4de3" + integrity sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ== + dependencies: + color-name "~1.1.4" + +color-name@1.1.3: + version "1.1.3" + resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.3.tgz#a7d0558bd89c42f795dd42328f740831ca53bc25" + integrity sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw== + +color-name@^1.0.0, color-name@~1.1.4: + version "1.1.4" + resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.4.tgz#c2a09a87acbde69543de6f63fa3995c826c536a2" + integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA== + +color-string@^1.9.0: + version "1.9.1" + resolved "https://registry.yarnpkg.com/color-string/-/color-string-1.9.1.tgz#4467f9146f036f855b764dfb5bf8582bf342c7a4" + integrity sha512-shrVawQFojnZv6xM40anx4CkoDP+fZsw/ZerEMsW/pyzsRbElpsL/DBVW7q3ExxwusdNXI3lXpuhEZkzs8p5Eg== + dependencies: + color-name "^1.0.0" + simple-swizzle "^0.2.2" + +color@^4.2.3: + version "4.2.3" + resolved "https://registry.yarnpkg.com/color/-/color-4.2.3.tgz#d781ecb5e57224ee43ea9627560107c0e0c6463a" + integrity sha512-1rXeuUUiGGrykh+CeBdu5Ie7OJwinCgQY0bc7GCRxy5xVHy+moaqkpL/jqQq0MtQOeYcrqEz4abc5f0KtU7W4A== + dependencies: + color-convert "^2.0.1" + color-string "^1.9.0" + +comma-separated-tokens@^2.0.0: + version "2.0.3" + resolved "https://registry.yarnpkg.com/comma-separated-tokens/-/comma-separated-tokens-2.0.3.tgz#4e89c9458acb61bc8fef19f4529973b2392839ee" + integrity sha512-Fu4hJdvzeylCfQPp9SGWidpzrMs7tTrlu6Vb8XGaRGck8QSNZJJp538Wrb60Lax4fPwR64ViY468OIUTbRlGZg== + +common-ancestor-path@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/common-ancestor-path/-/common-ancestor-path-1.0.1.tgz#4f7d2d1394d91b7abdf51871c62f71eadb0182a7" + integrity sha512-L3sHRo1pXXEqX8VU28kfgUY+YGsk09hPqZiZmLacNib6XNTCM8ubYeT7ryXQw8asB1sKgcU5lkB7ONug08aB8w== + +convert-source-map@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-2.0.0.tgz#4b560f649fc4e918dd0ab75cf4961e8bc882d82a" + integrity sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg== + +cookie@^0.6.0: + version "0.6.0" + resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.6.0.tgz#2798b04b071b0ecbff0dbb62a505a8efa4e19051" + integrity sha512-U71cyTamuh1CRNCfpGY6to28lxvNwPG4Guz/EVjgf3Jmzv0vlDp1atT9eS5dDjMYHucpHbWns6Lwf3BKz6svdw== + +cross-spawn@^7.0.3: + version "7.0.3" + resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.3.tgz#f73a85b9d5d41d045551c177e2882d4ac85728a6" + integrity sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w== + dependencies: + path-key "^3.1.0" + shebang-command "^2.0.0" + which "^2.0.1" + +css-selector-parser@^3.0.0: + version "3.0.4" + resolved "https://registry.yarnpkg.com/css-selector-parser/-/css-selector-parser-3.0.4.tgz#1cabd23f3d83ebd5a752c1c9e72a2f37f6d904fa" + integrity sha512-pnmS1dbKsz6KA4EW4BznyPL2xxkNDRg62hcD0v8g6DEw2W7hxOln5M953jsp9hmw5Dg57S6o/A8GOn37mbAgcQ== + +cssesc@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/cssesc/-/cssesc-3.0.0.tgz#37741919903b868565e1c09ea747445cd18983ee" + integrity sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg== + +debug@2: + version "2.6.9" + resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f" + integrity sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA== + dependencies: + ms "2.0.0" + +debug@^3.2.6: + version "3.2.7" + resolved "https://registry.yarnpkg.com/debug/-/debug-3.2.7.tgz#72580b7e9145fb39b6676f9c5e5fb100b934179a" + integrity sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ== + dependencies: + ms "^2.1.1" + +debug@^4.0.0, debug@^4.1.0, debug@^4.3.1, debug@^4.3.4: + version "4.3.4" + resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.4.tgz#1319f6579357f2338d3337d2cdd4914bb5dcc865" + integrity sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ== + dependencies: + ms "2.1.2" + +decode-named-character-reference@^1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/decode-named-character-reference/-/decode-named-character-reference-1.0.2.tgz#daabac9690874c394c81e4162a0304b35d824f0e" + integrity sha512-O8x12RzrUF8xyVcY0KJowWsmaJxQbmy0/EtnNtHRpsOcT7dFk5W598coHqBVpmWo1oQQfsCqfCmkZN5DJrZVdg== + dependencies: + character-entities "^2.0.0" + +decompress-response@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/decompress-response/-/decompress-response-6.0.0.tgz#ca387612ddb7e104bd16d85aab00d5ecf09c66fc" + integrity sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ== + dependencies: + mimic-response "^3.1.0" + +deep-extend@^0.6.0: + version "0.6.0" + resolved "https://registry.yarnpkg.com/deep-extend/-/deep-extend-0.6.0.tgz#c4fa7c95404a17a9c3e8ca7e1537312b736330ac" + integrity sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA== + +dequal@^2.0.0, dequal@^2.0.3: + version "2.0.3" + resolved "https://registry.yarnpkg.com/dequal/-/dequal-2.0.3.tgz#2644214f1997d39ed0ee0ece72335490a7ac67be" + integrity sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA== + +detect-libc@^2.0.0, detect-libc@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/detect-libc/-/detect-libc-2.0.2.tgz#8ccf2ba9315350e1241b88d0ac3b0e1fbd99605d" + integrity sha512-UX6sGumvvqSaXgdKGUsgZWqcUyIXZ/vZTrlRT/iobiKhGL0zL4d3osHj3uqllWJK+i+sixDS/3COVEOFbupFyw== + +deterministic-object-hash@^2.0.1: + version "2.0.2" + resolved "https://registry.yarnpkg.com/deterministic-object-hash/-/deterministic-object-hash-2.0.2.tgz#b251ddc801443905f0e9fef08816a46bc9fe3807" + integrity sha512-KxektNH63SrbfUyDiwXqRb1rLwKt33AmMv+5Nhsw1kqZ13SJBRTgZHtGbE+hH3a1mVW1cz+4pqSWVPAtLVXTzQ== + dependencies: + base-64 "^1.0.0" + +devalue@^4.3.2: + version "4.3.2" + resolved "https://registry.yarnpkg.com/devalue/-/devalue-4.3.2.tgz#cc44e4cf3872ac5a78229fbce3b77e57032727b5" + integrity sha512-KqFl6pOgOW+Y6wJgu80rHpo2/3H07vr8ntR9rkkFIRETewbf5GaYYcakYfiKz89K+sLsuPkQIZaXDMjUObZwWg== + +devlop@^1.0.0, devlop@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/devlop/-/devlop-1.1.0.tgz#4db7c2ca4dc6e0e834c30be70c94bbc976dc7018" + integrity sha512-RWmIqhcFf1lRYBvNmr7qTNuyCt/7/ns2jbpp1+PalgE/rDQcBT0fioSMUpJ93irlUhC5hrg4cYqe6U+0ImW0rA== + dependencies: + dequal "^2.0.0" + +diff@^5.1.0: + version "5.1.0" + resolved "https://registry.yarnpkg.com/diff/-/diff-5.1.0.tgz#bc52d298c5ea8df9194800224445ed43ffc87e40" + integrity sha512-D+mk+qE8VC/PAUrlAU34N+VfXev0ghe5ywmpqrawphmVZc1bEfn56uo9qpyGp1p4xpzOHkSW4ztBd6L7Xx4ACw== + +direction@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/direction/-/direction-2.0.1.tgz#71800dd3c4fa102406502905d3866e65bdebb985" + integrity sha512-9S6m9Sukh1cZNknO1CWAr2QAWsbKLafQiyM5gZ7VgXHeuaoUwffKN4q6NC4A/Mf9iiPlOXQEKW/Mv/mh9/3YFA== + +dlv@^1.1.3: + version "1.1.3" + resolved "https://registry.yarnpkg.com/dlv/-/dlv-1.1.3.tgz#5c198a8a11453596e751494d49874bc7732f2e79" + integrity sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA== + +dset@^3.1.2, dset@^3.1.3: + version "3.1.3" + resolved "https://registry.yarnpkg.com/dset/-/dset-3.1.3.tgz#c194147f159841148e8e34ca41f638556d9542d2" + integrity sha512-20TuZZHCEZ2O71q9/+8BwKwZ0QtD9D8ObhrihJPr+vLLYlSuAU3/zL4cSlgbfeoGHTjCSJBa7NGcrF9/Bx/WJQ== + +eastasianwidth@^0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/eastasianwidth/-/eastasianwidth-0.2.0.tgz#696ce2ec0aa0e6ea93a397ffcf24aa7840c827cb" + integrity sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA== + +electron-to-chromium@^1.4.601: + version "1.4.640" + resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.4.640.tgz#76290a36fa4b5f1f4cadaf1fc582478ebb3ac246" + integrity sha512-z/6oZ/Muqk4BaE7P69bXhUhpJbUM9ZJeka43ZwxsDshKtePns4mhBlh8bU5+yrnOnz3fhG82XLzGUXazOmsWnA== + +emmet@^2.4.3: + version "2.4.6" + resolved "https://registry.yarnpkg.com/emmet/-/emmet-2.4.6.tgz#f975094fb0fb5d2e78a7c94a65c308bdfd6436aa" + integrity sha512-dJfbdY/hfeTyf/Ef7Y7ubLYzkBvPQ912wPaeVYpAxvFxkEBf/+hJu4H6vhAvFN6HlxqedlfVn2x1S44FfQ97pg== + dependencies: + "@emmetio/abbreviation" "^2.3.3" + "@emmetio/css-abbreviation" "^2.1.8" + +emoji-regex@^10.2.1, emoji-regex@^10.3.0: + version "10.3.0" + resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-10.3.0.tgz#76998b9268409eb3dae3de989254d456e70cfe23" + integrity sha512-QpLs9D9v9kArv4lfDEgg1X/gN5XLnf/A6l9cs8SPZLRZR3ZkY9+kwIQTxm+fsSej5UMYGE8fdoaZVIBlqG0XTw== + +emoji-regex@^8.0.0: + version "8.0.0" + resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-8.0.0.tgz#e818fd69ce5ccfcb404594f842963bf53164cc37" + integrity sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A== + +emoji-regex@^9.2.2: + version "9.2.2" + resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-9.2.2.tgz#840c8803b0d8047f4ff0cf963176b32d4ef3ed72" + integrity sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg== + +end-of-stream@^1.1.0, end-of-stream@^1.4.1: + version "1.4.4" + resolved "https://registry.yarnpkg.com/end-of-stream/-/end-of-stream-1.4.4.tgz#5ae64a5f45057baf3626ec14da0ca5e4b2431eb0" + integrity sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q== + dependencies: + once "^1.4.0" + +entities@^4.4.0: + version "4.5.0" + resolved "https://registry.yarnpkg.com/entities/-/entities-4.5.0.tgz#5d268ea5e7113ec74c4d033b79ea5a35a488fb48" + integrity sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw== + +es-module-lexer@^1.4.1: + version "1.4.1" + resolved "https://registry.yarnpkg.com/es-module-lexer/-/es-module-lexer-1.4.1.tgz#41ea21b43908fe6a287ffcbe4300f790555331f5" + integrity sha512-cXLGjP0c4T3flZJKQSuziYoq7MlT+rnvfZjfp7h+I7K9BNX54kP9nyWvdbwjQ4u1iWbOL4u96fgeZLToQlZC7w== + +esbuild@^0.19.3, esbuild@^0.19.6: + version "0.19.11" + resolved "https://registry.yarnpkg.com/esbuild/-/esbuild-0.19.11.tgz#4a02dca031e768b5556606e1b468fe72e3325d96" + integrity sha512-HJ96Hev2hX/6i5cDVwcqiJBBtuo9+FeIJOtZ9W1kA5M6AMJRHUZlpYZ1/SbEwtO0ioNAW8rUooVpC/WehY2SfA== + optionalDependencies: + "@esbuild/aix-ppc64" "0.19.11" + "@esbuild/android-arm" "0.19.11" + "@esbuild/android-arm64" "0.19.11" + "@esbuild/android-x64" "0.19.11" + "@esbuild/darwin-arm64" "0.19.11" + "@esbuild/darwin-x64" "0.19.11" + "@esbuild/freebsd-arm64" "0.19.11" + "@esbuild/freebsd-x64" "0.19.11" + "@esbuild/linux-arm" "0.19.11" + "@esbuild/linux-arm64" "0.19.11" + "@esbuild/linux-ia32" "0.19.11" + "@esbuild/linux-loong64" "0.19.11" + "@esbuild/linux-mips64el" "0.19.11" + "@esbuild/linux-ppc64" "0.19.11" + "@esbuild/linux-riscv64" "0.19.11" + "@esbuild/linux-s390x" "0.19.11" + "@esbuild/linux-x64" "0.19.11" + "@esbuild/netbsd-x64" "0.19.11" + "@esbuild/openbsd-x64" "0.19.11" + "@esbuild/sunos-x64" "0.19.11" + "@esbuild/win32-arm64" "0.19.11" + "@esbuild/win32-ia32" "0.19.11" + "@esbuild/win32-x64" "0.19.11" + +escalade@^3.1.1: + version "3.1.1" + resolved "https://registry.yarnpkg.com/escalade/-/escalade-3.1.1.tgz#d8cfdc7000965c5a0174b4a82eaa5c0552742e40" + integrity sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw== + +escape-string-regexp@^1.0.5: + version "1.0.5" + resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4" + integrity sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg== + +escape-string-regexp@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-5.0.0.tgz#4683126b500b61762f2dbebace1806e8be31b1c8" + integrity sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw== + +esprima@^4.0.0: + version "4.0.1" + resolved "https://registry.yarnpkg.com/esprima/-/esprima-4.0.1.tgz#13b04cdb3e6c5d19df91ab6987a8695619b0aa71" + integrity sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A== + +estree-util-attach-comments@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/estree-util-attach-comments/-/estree-util-attach-comments-3.0.0.tgz#344bde6a64c8a31d15231e5ee9e297566a691c2d" + integrity sha512-cKUwm/HUcTDsYh/9FgnuFqpfquUbwIqwKM26BVCGDPVgvaCl/nDCCjUfiLlx6lsEZ3Z4RFxNbOQ60pkaEwFxGw== + dependencies: + "@types/estree" "^1.0.0" + +estree-util-build-jsx@^3.0.0: + version "3.0.1" + resolved "https://registry.yarnpkg.com/estree-util-build-jsx/-/estree-util-build-jsx-3.0.1.tgz#b6d0bced1dcc4f06f25cf0ceda2b2dcaf98168f1" + integrity sha512-8U5eiL6BTrPxp/CHbs2yMgP8ftMhR5ww1eIKoWRMlqvltHF8fZn5LRDvTKuxD3DUn+shRbLGqXemcP51oFCsGQ== + dependencies: + "@types/estree-jsx" "^1.0.0" + devlop "^1.0.0" + estree-util-is-identifier-name "^3.0.0" + estree-walker "^3.0.0" + +estree-util-is-identifier-name@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/estree-util-is-identifier-name/-/estree-util-is-identifier-name-3.0.0.tgz#0b5ef4c4ff13508b34dcd01ecfa945f61fce5dbd" + integrity sha512-hFtqIDZTIUZ9BXLb8y4pYGyk6+wekIivNVTcmvk8NoOh+VeRn5y6cEHzbURrWbfp1fIqdVipilzj+lfaadNZmg== + +estree-util-to-js@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/estree-util-to-js/-/estree-util-to-js-2.0.0.tgz#10a6fb924814e6abb62becf0d2bc4dea51d04f17" + integrity sha512-WDF+xj5rRWmD5tj6bIqRi6CkLIXbbNQUcxQHzGysQzvHmdYG2G7p/Tf0J0gpxGgkeMZNTIjT/AoSvC9Xehcgdg== + dependencies: + "@types/estree-jsx" "^1.0.0" + astring "^1.8.0" + source-map "^0.7.0" + +estree-util-visit@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/estree-util-visit/-/estree-util-visit-2.0.0.tgz#13a9a9f40ff50ed0c022f831ddf4b58d05446feb" + integrity sha512-m5KgiH85xAhhW8Wta0vShLcUvOsh3LLPI2YVwcbio1l7E09NTLL1EyMZFM1OyWowoH0skScNbhOPl4kcBgzTww== + dependencies: + "@types/estree-jsx" "^1.0.0" + "@types/unist" "^3.0.0" + +estree-walker@^3.0.0, estree-walker@^3.0.3: + version "3.0.3" + resolved "https://registry.yarnpkg.com/estree-walker/-/estree-walker-3.0.3.tgz#67c3e549ec402a487b4fc193d1953a524752340d" + integrity sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g== + dependencies: + "@types/estree" "^1.0.0" + +eventemitter3@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/eventemitter3/-/eventemitter3-5.0.1.tgz#53f5ffd0a492ac800721bb42c66b841de96423c4" + integrity sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA== + +execa@^8.0.1: + version "8.0.1" + resolved "https://registry.yarnpkg.com/execa/-/execa-8.0.1.tgz#51f6a5943b580f963c3ca9c6321796db8cc39b8c" + integrity sha512-VyhnebXciFV2DESc+p6B+y0LjSm0krU4OgJN44qFAhBY0TJ+1V61tYD2+wHusZ6F9n5K+vl8k0sTy7PEfV4qpg== + dependencies: + cross-spawn "^7.0.3" + get-stream "^8.0.1" + human-signals "^5.0.0" + is-stream "^3.0.0" + merge-stream "^2.0.0" + npm-run-path "^5.1.0" + onetime "^6.0.0" + signal-exit "^4.1.0" + strip-final-newline "^3.0.0" + +expand-template@^2.0.3: + version "2.0.3" + resolved "https://registry.yarnpkg.com/expand-template/-/expand-template-2.0.3.tgz#6e14b3fcee0f3a6340ecb57d2e8918692052a47c" + integrity sha512-XYfuKMvj4O35f/pOXLObndIRvyQ+/+6AhODh+OKWj9S9498pHHn/IMszH+gt0fBCRWMNfk1ZSp5x3AifmnI2vg== + +expressive-code@^0.31.0: + version "0.31.0" + resolved "https://registry.yarnpkg.com/expressive-code/-/expressive-code-0.31.0.tgz#80954d64c07f162fc2e7c9c227d10d77f7e27df8" + integrity sha512-rxKGYS8iRwNUbRNfyCyoe3XQvBLTtGdXbNKM+ODDWCn4VL2DVT1gD1M2N2Alg8HQHIWZJsZIMsYbziO0MRjPlw== + dependencies: + "@expressive-code/core" "^0.31.0" + "@expressive-code/plugin-frames" "^0.31.0" + "@expressive-code/plugin-shiki" "^0.31.0" + "@expressive-code/plugin-text-markers" "^0.31.0" + +extend-shallow@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/extend-shallow/-/extend-shallow-2.0.1.tgz#51af7d614ad9a9f610ea1bafbb989d6b1c56890f" + integrity sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug== + dependencies: + is-extendable "^0.1.0" + +extend@^3.0.0: + version "3.0.2" + resolved "https://registry.yarnpkg.com/extend/-/extend-3.0.2.tgz#f8b1136b4071fbd8eb140aff858b1019ec2915fa" + integrity sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g== + +fast-fifo@^1.1.0, fast-fifo@^1.2.0: + version "1.3.2" + resolved "https://registry.yarnpkg.com/fast-fifo/-/fast-fifo-1.3.2.tgz#286e31de96eb96d38a97899815740ba2a4f3640c" + integrity sha512-/d9sfos4yxzpwkDkuN7k2SqFKtYNmCTzgfEpz82x34IM9/zc8KGxQoXg1liNC/izpRM/MBdt44Nmx41ZWqk+FQ== + +fast-glob@^3.2.12, fast-glob@^3.3.1, fast-glob@^3.3.2: + version "3.3.2" + resolved "https://registry.yarnpkg.com/fast-glob/-/fast-glob-3.3.2.tgz#a904501e57cfdd2ffcded45e99a54fef55e46129" + integrity sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow== + dependencies: + "@nodelib/fs.stat" "^2.0.2" + "@nodelib/fs.walk" "^1.2.3" + glob-parent "^5.1.2" + merge2 "^1.3.0" + micromatch "^4.0.4" + +fastq@^1.6.0: + version "1.16.0" + resolved "https://registry.yarnpkg.com/fastq/-/fastq-1.16.0.tgz#83b9a9375692db77a822df081edb6a9cf6839320" + integrity sha512-ifCoaXsDrsdkWTtiNJX5uzHDsrck5TzfKKDcuFFTIrrc/BS076qgEIfoIy1VeZqViznfKiysPYTh/QeHtnIsYA== + dependencies: + reusify "^1.0.4" + +fill-range@^7.0.1: + version "7.0.1" + resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-7.0.1.tgz#1919a6a7c75fe38b2c7c77e5198535da9acdda40" + integrity sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ== + dependencies: + to-regex-range "^5.0.1" + +find-up@^4.0.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/find-up/-/find-up-4.1.0.tgz#97afe7d6cdc0bc5928584b7c8d7b16e8a9aa5d19" + integrity sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw== + dependencies: + locate-path "^5.0.0" + path-exists "^4.0.0" + +find-up@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/find-up/-/find-up-5.0.0.tgz#4c92819ecb7083561e4f4a240a86be5198f536fc" + integrity sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng== + dependencies: + locate-path "^6.0.0" + path-exists "^4.0.0" + +find-yarn-workspace-root2@1.2.16: + version "1.2.16" + resolved "https://registry.yarnpkg.com/find-yarn-workspace-root2/-/find-yarn-workspace-root2-1.2.16.tgz#60287009dd2f324f59646bdb4b7610a6b301c2a9" + integrity sha512-hr6hb1w8ePMpPVUK39S4RlwJzi+xPLuVuG8XlwXU3KD5Yn3qgBWVfy3AzNlDhWvE1EORCE65/Qm26rFQt3VLVA== + dependencies: + micromatch "^4.0.2" + pkg-dir "^4.2.0" + +flattie@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/flattie/-/flattie-1.1.0.tgz#1459504209f2001c478751b4e2fb69d6b1ee3241" + integrity sha512-xU99gDEnciIwJdGcBmNHnzTJ/w5AT+VFJOu6sTB6WM8diOYNA3Sa+K1DiEBQ7XH4QikQq3iFW1U+jRVcotQnBw== + +fs-constants@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/fs-constants/-/fs-constants-1.0.0.tgz#6be0de9be998ce16af8afc24497b9ee9b7ccd9ad" + integrity sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow== + +fsevents@~2.3.2, fsevents@~2.3.3: + version "2.3.3" + resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.3.3.tgz#cac6407785d03675a2a5e1a5305c697b347d90d6" + integrity sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw== + +function-bind@^1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.2.tgz#2c02d864d97f3ea6c8830c464cbd11ab6eab7a1c" + integrity sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA== + +gensync@^1.0.0-beta.2: + version "1.0.0-beta.2" + resolved "https://registry.yarnpkg.com/gensync/-/gensync-1.0.0-beta.2.tgz#32a6ee76c3d7f52d46b2b1ae5d93fea8580a25e0" + integrity sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg== + +get-caller-file@^2.0.5: + version "2.0.5" + resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-2.0.5.tgz#4f94412a82db32f36e3b0b9741f8a97feb031f7e" + integrity sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg== + +get-east-asian-width@^1.0.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/get-east-asian-width/-/get-east-asian-width-1.2.0.tgz#5e6ebd9baee6fb8b7b6bd505221065f0cd91f64e" + integrity sha512-2nk+7SIVb14QrgXFHcm84tD4bKQz0RxPuMT8Ag5KPOq7J5fEmAg0UbXdTOSHqNuHSU28k55qnceesxXRZGzKWA== + +get-stream@^8.0.1: + version "8.0.1" + resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-8.0.1.tgz#def9dfd71742cd7754a7761ed43749a27d02eca2" + integrity sha512-VaUJspBffn/LMCJVoMvSAdmscJyS1auj5Zulnn5UoYcY531UWmdwhRWkcGKnGU93m5HSXP9LP2usOryrBtQowA== + +github-from-package@0.0.0: + version "0.0.0" + resolved "https://registry.yarnpkg.com/github-from-package/-/github-from-package-0.0.0.tgz#97fb5d96bfde8973313f20e8288ef9a167fa64ce" + integrity sha512-SyHy3T1v2NUXn29OsWdxmK6RwHD+vkj3v8en8AOBZ1wBQ/hCAQ5bAQTD02kW4W9tUp/3Qh6J8r9EvntiyCmOOw== + +github-slugger@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/github-slugger/-/github-slugger-2.0.0.tgz#52cf2f9279a21eb6c59dd385b410f0c0adda8f1a" + integrity sha512-IaOQ9puYtjrkq7Y0Ygl9KDZnrf/aiUJYUpVf89y8kyaxbRG7Y1SrX/jaumrv81vc61+kiMempujsM3Yw7w5qcw== + +glob-parent@^5.1.2, glob-parent@~5.1.2: + version "5.1.2" + resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-5.1.2.tgz#869832c58034fe68a4093c17dc15e8340d8401c4" + integrity sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow== + dependencies: + is-glob "^4.0.1" + +globals@^11.1.0: + version "11.12.0" + resolved "https://registry.yarnpkg.com/globals/-/globals-11.12.0.tgz#ab8795338868a0babd8525758018c2a7eb95c42e" + integrity sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA== + +graceful-fs@^4.1.5: + version "4.2.11" + resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.11.tgz#4183e4e8bf08bb6e05bbb2f7d2e0c8f712ca40e3" + integrity sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ== + +gray-matter@^4.0.3: + version "4.0.3" + resolved "https://registry.yarnpkg.com/gray-matter/-/gray-matter-4.0.3.tgz#e893c064825de73ea1f5f7d88c7a9f7274288798" + integrity sha512-5v6yZd4JK3eMI3FqqCouswVqwugaA9r4dNZB1wwcmrD02QkV5H0y7XBQW8QwQqEaZY1pM9aqORSORhJRdNK44Q== + dependencies: + js-yaml "^3.13.1" + kind-of "^6.0.2" + section-matter "^1.0.0" + strip-bom-string "^1.0.0" + +has-flag@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-3.0.0.tgz#b5d454dc2199ae225699f3467e5a07f3b955bafd" + integrity sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw== + +hasown@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/hasown/-/hasown-2.0.0.tgz#f4c513d454a57b7c7e1650778de226b11700546c" + integrity sha512-vUptKVTpIJhcczKBbgnS+RtcuYMB8+oNzPK2/Hp3hanz8JmpATdmmgLgSaadVREkDm+e2giHwY3ZRkyjSIDDFA== + dependencies: + function-bind "^1.1.2" + +hast-util-from-html@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/hast-util-from-html/-/hast-util-from-html-2.0.1.tgz#9cd38ee81bf40b2607368b92a04b0905fa987488" + integrity sha512-RXQBLMl9kjKVNkJTIO6bZyb2n+cUH8LFaSSzo82jiLT6Tfc+Pt7VQCS+/h3YwG4jaNE2TA2sdJisGWR+aJrp0g== + dependencies: + "@types/hast" "^3.0.0" + devlop "^1.1.0" + hast-util-from-parse5 "^8.0.0" + parse5 "^7.0.0" + vfile "^6.0.0" + vfile-message "^4.0.0" + +hast-util-from-parse5@^7.0.0: + version "7.1.2" + resolved "https://registry.yarnpkg.com/hast-util-from-parse5/-/hast-util-from-parse5-7.1.2.tgz#aecfef73e3ceafdfa4550716443e4eb7b02e22b0" + integrity sha512-Nz7FfPBuljzsN3tCQ4kCBKqdNhQE2l0Tn+X1ubgKBPRoiDIu1mL08Cfw4k7q71+Duyaw7DXDN+VTAp4Vh3oCOw== + dependencies: + "@types/hast" "^2.0.0" + "@types/unist" "^2.0.0" + hastscript "^7.0.0" + property-information "^6.0.0" + vfile "^5.0.0" + vfile-location "^4.0.0" + web-namespaces "^2.0.0" + +hast-util-from-parse5@^8.0.0: + version "8.0.1" + resolved "https://registry.yarnpkg.com/hast-util-from-parse5/-/hast-util-from-parse5-8.0.1.tgz#654a5676a41211e14ee80d1b1758c399a0327651" + integrity sha512-Er/Iixbc7IEa7r/XLtuG52zoqn/b3Xng/w6aZQ0xGVxzhw5xUFxcRqdPzP6yFi/4HBYRaifaI5fQ1RH8n0ZeOQ== + dependencies: + "@types/hast" "^3.0.0" + "@types/unist" "^3.0.0" + devlop "^1.0.0" + hastscript "^8.0.0" + property-information "^6.0.0" + vfile "^6.0.0" + vfile-location "^5.0.0" + web-namespaces "^2.0.0" + +hast-util-has-property@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/hast-util-has-property/-/hast-util-has-property-3.0.0.tgz#4e595e3cddb8ce530ea92f6fc4111a818d8e7f93" + integrity sha512-MNilsvEKLFpV604hwfhVStK0usFY/QmM5zX16bo7EjnAEGofr5YyI37kzopBlZJkHD4t887i+q/C8/tr5Q94cA== + dependencies: + "@types/hast" "^3.0.0" + +hast-util-parse-selector@^3.0.0: + version "3.1.1" + resolved "https://registry.yarnpkg.com/hast-util-parse-selector/-/hast-util-parse-selector-3.1.1.tgz#25ab00ae9e75cbc62cf7a901f68a247eade659e2" + integrity sha512-jdlwBjEexy1oGz0aJ2f4GKMaVKkA9jwjr4MjAAI22E5fM/TXVZHuS5OpONtdeIkRKqAaryQ2E9xNQxijoThSZA== + dependencies: + "@types/hast" "^2.0.0" + +hast-util-parse-selector@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/hast-util-parse-selector/-/hast-util-parse-selector-4.0.0.tgz#352879fa86e25616036037dd8931fb5f34cb4a27" + integrity sha512-wkQCkSYoOGCRKERFWcxMVMOcYE2K1AaNLU8DXS9arxnLOUEWbOXKXiJUNzEpqZ3JOKpnha3jkFrumEjVliDe7A== + dependencies: + "@types/hast" "^3.0.0" + +hast-util-raw@^7.0.0: + version "7.2.3" + resolved "https://registry.yarnpkg.com/hast-util-raw/-/hast-util-raw-7.2.3.tgz#dcb5b22a22073436dbdc4aa09660a644f4991d99" + integrity sha512-RujVQfVsOrxzPOPSzZFiwofMArbQke6DJjnFfceiEbFh7S05CbPt0cYN+A5YeD3pso0JQk6O1aHBnx9+Pm2uqg== + dependencies: + "@types/hast" "^2.0.0" + "@types/parse5" "^6.0.0" + hast-util-from-parse5 "^7.0.0" + hast-util-to-parse5 "^7.0.0" + html-void-elements "^2.0.0" + parse5 "^6.0.0" + unist-util-position "^4.0.0" + unist-util-visit "^4.0.0" + vfile "^5.0.0" + web-namespaces "^2.0.0" + zwitch "^2.0.0" + +hast-util-raw@^9.0.0: + version "9.0.2" + resolved "https://registry.yarnpkg.com/hast-util-raw/-/hast-util-raw-9.0.2.tgz#39b4a4886bd9f0a5dd42e86d02c966c2c152884c" + integrity sha512-PldBy71wO9Uq1kyaMch9AHIghtQvIwxBUkv823pKmkTM3oV1JxtsTNYdevMxvUHqcnOAuO65JKU2+0NOxc2ksA== + dependencies: + "@types/hast" "^3.0.0" + "@types/unist" "^3.0.0" + "@ungap/structured-clone" "^1.0.0" + hast-util-from-parse5 "^8.0.0" + hast-util-to-parse5 "^8.0.0" + html-void-elements "^3.0.0" + mdast-util-to-hast "^13.0.0" + parse5 "^7.0.0" + unist-util-position "^5.0.0" + unist-util-visit "^5.0.0" + vfile "^6.0.0" + web-namespaces "^2.0.0" + zwitch "^2.0.0" + +hast-util-select@^6.0.2: + version "6.0.2" + resolved "https://registry.yarnpkg.com/hast-util-select/-/hast-util-select-6.0.2.tgz#f1e6c583ab6227cb510383471328734342bd1d1c" + integrity sha512-hT/SD/d/Meu+iobvgkffo1QecV8WeKWxwsNMzcTJsKw1cKTQKSR/7ArJeURLNJF9HDjp9nVoORyNNJxrvBye8Q== + dependencies: + "@types/hast" "^3.0.0" + "@types/unist" "^3.0.0" + bcp-47-match "^2.0.0" + comma-separated-tokens "^2.0.0" + css-selector-parser "^3.0.0" + devlop "^1.0.0" + direction "^2.0.0" + hast-util-has-property "^3.0.0" + hast-util-to-string "^3.0.0" + hast-util-whitespace "^3.0.0" + not "^0.1.0" + nth-check "^2.0.0" + property-information "^6.0.0" + space-separated-tokens "^2.0.0" + unist-util-visit "^5.0.0" + zwitch "^2.0.0" + +hast-util-to-estree@^3.0.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/hast-util-to-estree/-/hast-util-to-estree-3.1.0.tgz#f2afe5e869ddf0cf690c75f9fc699f3180b51b19" + integrity sha512-lfX5g6hqVh9kjS/B9E2gSkvHH4SZNiQFiqWS0x9fENzEl+8W12RqdRxX6d/Cwxi30tPQs3bIO+aolQJNp1bIyw== + dependencies: + "@types/estree" "^1.0.0" + "@types/estree-jsx" "^1.0.0" + "@types/hast" "^3.0.0" + comma-separated-tokens "^2.0.0" + devlop "^1.0.0" + estree-util-attach-comments "^3.0.0" + estree-util-is-identifier-name "^3.0.0" + hast-util-whitespace "^3.0.0" + mdast-util-mdx-expression "^2.0.0" + mdast-util-mdx-jsx "^3.0.0" + mdast-util-mdxjs-esm "^2.0.0" + property-information "^6.0.0" + space-separated-tokens "^2.0.0" + style-to-object "^0.4.0" + unist-util-position "^5.0.0" + zwitch "^2.0.0" + +hast-util-to-html@^8.0.4: + version "8.0.4" + resolved "https://registry.yarnpkg.com/hast-util-to-html/-/hast-util-to-html-8.0.4.tgz#0269ef33fa3f6599b260a8dc94f733b8e39e41fc" + integrity sha512-4tpQTUOr9BMjtYyNlt0P50mH7xj0Ks2xpo8M943Vykljf99HW6EzulIoJP1N3eKOSScEHzyzi9dm7/cn0RfGwA== + dependencies: + "@types/hast" "^2.0.0" + "@types/unist" "^2.0.0" + ccount "^2.0.0" + comma-separated-tokens "^2.0.0" + hast-util-raw "^7.0.0" + hast-util-whitespace "^2.0.0" + html-void-elements "^2.0.0" + property-information "^6.0.0" + space-separated-tokens "^2.0.0" + stringify-entities "^4.0.0" + zwitch "^2.0.4" + +hast-util-to-html@^9.0.0: + version "9.0.0" + resolved "https://registry.yarnpkg.com/hast-util-to-html/-/hast-util-to-html-9.0.0.tgz#51c0ae2a3550b9aa988c094c4fc4e327af0dddd1" + integrity sha512-IVGhNgg7vANuUA2XKrT6sOIIPgaYZnmLx3l/CCOAK0PtgfoHrZwX7jCSYyFxHTrGmC6S9q8aQQekjp4JPZF+cw== + dependencies: + "@types/hast" "^3.0.0" + "@types/unist" "^3.0.0" + ccount "^2.0.0" + comma-separated-tokens "^2.0.0" + hast-util-raw "^9.0.0" + hast-util-whitespace "^3.0.0" + html-void-elements "^3.0.0" + mdast-util-to-hast "^13.0.0" + property-information "^6.0.0" + space-separated-tokens "^2.0.0" + stringify-entities "^4.0.0" + zwitch "^2.0.4" + +hast-util-to-jsx-runtime@^2.0.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/hast-util-to-jsx-runtime/-/hast-util-to-jsx-runtime-2.3.0.tgz#3ed27caf8dc175080117706bf7269404a0aa4f7c" + integrity sha512-H/y0+IWPdsLLS738P8tDnrQ8Z+dj12zQQ6WC11TIM21C8WFVoIxcqWXf2H3hiTVZjF1AWqoimGwrTWecWrnmRQ== + dependencies: + "@types/estree" "^1.0.0" + "@types/hast" "^3.0.0" + "@types/unist" "^3.0.0" + comma-separated-tokens "^2.0.0" + devlop "^1.0.0" + estree-util-is-identifier-name "^3.0.0" + hast-util-whitespace "^3.0.0" + mdast-util-mdx-expression "^2.0.0" + mdast-util-mdx-jsx "^3.0.0" + mdast-util-mdxjs-esm "^2.0.0" + property-information "^6.0.0" + space-separated-tokens "^2.0.0" + style-to-object "^1.0.0" + unist-util-position "^5.0.0" + vfile-message "^4.0.0" + +hast-util-to-parse5@^7.0.0: + version "7.1.0" + resolved "https://registry.yarnpkg.com/hast-util-to-parse5/-/hast-util-to-parse5-7.1.0.tgz#c49391bf8f151973e0c9adcd116b561e8daf29f3" + integrity sha512-YNRgAJkH2Jky5ySkIqFXTQiaqcAtJyVE+D5lkN6CdtOqrnkLfGYYrEcKuHOJZlp+MwjSwuD3fZuawI+sic/RBw== + dependencies: + "@types/hast" "^2.0.0" + comma-separated-tokens "^2.0.0" + property-information "^6.0.0" + space-separated-tokens "^2.0.0" + web-namespaces "^2.0.0" + zwitch "^2.0.0" + +hast-util-to-parse5@^8.0.0: + version "8.0.0" + resolved "https://registry.yarnpkg.com/hast-util-to-parse5/-/hast-util-to-parse5-8.0.0.tgz#477cd42d278d4f036bc2ea58586130f6f39ee6ed" + integrity sha512-3KKrV5ZVI8if87DVSi1vDeByYrkGzg4mEfeu4alwgmmIeARiBLKCZS2uw5Gb6nU9x9Yufyj3iudm6i7nl52PFw== + dependencies: + "@types/hast" "^3.0.0" + comma-separated-tokens "^2.0.0" + devlop "^1.0.0" + property-information "^6.0.0" + space-separated-tokens "^2.0.0" + web-namespaces "^2.0.0" + zwitch "^2.0.0" + +hast-util-to-string@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/hast-util-to-string/-/hast-util-to-string-3.0.0.tgz#2a131948b4b1b26461a2c8ac876e2c88d02946bd" + integrity sha512-OGkAxX1Ua3cbcW6EJ5pT/tslVb90uViVkcJ4ZZIMW/R33DX/AkcJcRrPebPwJkHYwlDHXz4aIwvAAaAdtrACFA== + dependencies: + "@types/hast" "^3.0.0" + +hast-util-whitespace@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/hast-util-whitespace/-/hast-util-whitespace-2.0.1.tgz#0ec64e257e6fc216c7d14c8a1b74d27d650b4557" + integrity sha512-nAxA0v8+vXSBDt3AnRUNjyRIQ0rD+ntpbAp4LnPkumc5M9yUbSMa4XDU9Q6etY4f1Wp4bNgvc1yjiZtsTTrSng== + +hast-util-whitespace@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/hast-util-whitespace/-/hast-util-whitespace-3.0.0.tgz#7778ed9d3c92dd9e8c5c8f648a49c21fc51cb621" + integrity sha512-88JUN06ipLwsnv+dVn+OIYOvAuvBMy/Qoi6O7mQHxdPXpjy+Cd6xRkWwux7DKO+4sYILtLBRIKgsdpS2gQc7qw== + dependencies: + "@types/hast" "^3.0.0" + +hastscript@^7.0.0, hastscript@^7.2.0: + version "7.2.0" + resolved "https://registry.yarnpkg.com/hastscript/-/hastscript-7.2.0.tgz#0eafb7afb153d047077fa2a833dc9b7ec604d10b" + integrity sha512-TtYPq24IldU8iKoJQqvZOuhi5CyCQRAbvDOX0x1eW6rsHSxa/1i2CCiptNTotGHJ3VoHRGmqiv6/D3q113ikkw== + dependencies: + "@types/hast" "^2.0.0" + comma-separated-tokens "^2.0.0" + hast-util-parse-selector "^3.0.0" + property-information "^6.0.0" + space-separated-tokens "^2.0.0" + +hastscript@^8.0.0: + version "8.0.0" + resolved "https://registry.yarnpkg.com/hastscript/-/hastscript-8.0.0.tgz#4ef795ec8dee867101b9f23cc830d4baf4fd781a" + integrity sha512-dMOtzCEd3ABUeSIISmrETiKuyydk1w0pa+gE/uormcTpSYuaNJPbX1NU3JLyscSLjwAQM8bWMhhIlnCqnRvDTw== + dependencies: + "@types/hast" "^3.0.0" + comma-separated-tokens "^2.0.0" + hast-util-parse-selector "^4.0.0" + property-information "^6.0.0" + space-separated-tokens "^2.0.0" + +html-escaper@^3.0.3: + version "3.0.3" + resolved "https://registry.yarnpkg.com/html-escaper/-/html-escaper-3.0.3.tgz#4d336674652beb1dcbc29ef6b6ba7f6be6fdfed6" + integrity sha512-RuMffC89BOWQoY0WKGpIhn5gX3iI54O6nRA0yC124NYVtzjmFWBIiFd8M0x+ZdX0P9R4lADg1mgP8C7PxGOWuQ== + +html-void-elements@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/html-void-elements/-/html-void-elements-2.0.1.tgz#29459b8b05c200b6c5ee98743c41b979d577549f" + integrity sha512-0quDb7s97CfemeJAnW9wC0hw78MtW7NU3hqtCD75g2vFlDLt36llsYD7uB7SUzojLMP24N5IatXf7ylGXiGG9A== + +html-void-elements@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/html-void-elements/-/html-void-elements-3.0.0.tgz#fc9dbd84af9e747249034d4d62602def6517f1d7" + integrity sha512-bEqo66MRXsUGxWHV5IP0PUiAWwoEjba4VCzg0LjFJBpchPaTfyfCKTG6bc5F8ucKec3q5y6qOdGyYTSBEvhCrg== + +http-cache-semantics@^4.1.1: + version "4.1.1" + resolved "https://registry.yarnpkg.com/http-cache-semantics/-/http-cache-semantics-4.1.1.tgz#abe02fcb2985460bf0323be664436ec3476a6d5a" + integrity sha512-er295DKPVsV82j5kw1Gjt+ADA/XYHsajl82cGNQG2eyoPkvgUhX+nDIyelzhIWbbsXP39EHcI6l5tYs2FYqYXQ== + +human-signals@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/human-signals/-/human-signals-5.0.0.tgz#42665a284f9ae0dade3ba41ebc37eb4b852f3a28" + integrity sha512-AXcZb6vzzrFAUE61HnN4mpLqd/cSIwNQjtNWR0euPm6y0iqx3G4gOXaIDdtdDwZmhwe82LA6+zinmW4UBWVePQ== + +iconv-lite@^0.4.4: + version "0.4.24" + resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.24.tgz#2022b4b25fbddc21d2f524974a474aafe733908b" + integrity sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA== + dependencies: + safer-buffer ">= 2.1.2 < 3" + +ieee754@^1.1.13, ieee754@^1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/ieee754/-/ieee754-1.2.1.tgz#8eb7a10a63fff25d15a57b001586d177d1b0d352" + integrity sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA== + +import-meta-resolve@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/import-meta-resolve/-/import-meta-resolve-4.0.0.tgz#0b1195915689f60ab00f830af0f15cc841e8919e" + integrity sha512-okYUR7ZQPH+efeuMJGlq4f8ubUgO50kByRPyt/Cy1Io4PSRsPjxME+YlVaCOx+NIToW7hCsZNFJyTPFFKepRSA== + +inherits@^2.0.3, inherits@^2.0.4: + version "2.0.4" + resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c" + integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== + +ini@~1.3.0: + version "1.3.8" + resolved "https://registry.yarnpkg.com/ini/-/ini-1.3.8.tgz#a29da425b48806f34767a4efce397269af28432c" + integrity sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew== + +inline-style-parser@0.1.1: + version "0.1.1" + resolved "https://registry.yarnpkg.com/inline-style-parser/-/inline-style-parser-0.1.1.tgz#ec8a3b429274e9c0a1f1c4ffa9453a7fef72cea1" + integrity sha512-7NXolsK4CAS5+xvdj5OMMbI962hU/wvwoxk+LWR9Ek9bVtyuuYScDN6eS0rUm6TxApFpw7CX1o4uJzcd4AyD3Q== + +inline-style-parser@0.2.2: + version "0.2.2" + resolved "https://registry.yarnpkg.com/inline-style-parser/-/inline-style-parser-0.2.2.tgz#d498b4e6de0373458fc610ff793f6b14ebf45633" + integrity sha512-EcKzdTHVe8wFVOGEYXiW9WmJXPjqi1T+234YpJr98RiFYKHV3cdy1+3mkTE+KHTHxFFLH51SfaGOoUdW+v7ViQ== + +is-alphabetical@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/is-alphabetical/-/is-alphabetical-2.0.1.tgz#01072053ea7c1036df3c7d19a6daaec7f19e789b" + integrity sha512-FWyyY60MeTNyeSRpkM2Iry0G9hpr7/9kD40mD/cGQEuilcZYS4okz8SN2Q6rLCJ8gbCt6fN+rC+6tMGS99LaxQ== + +is-alphanumerical@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/is-alphanumerical/-/is-alphanumerical-2.0.1.tgz#7c03fbe96e3e931113e57f964b0a368cc2dfd875" + integrity sha512-hmbYhX/9MUMF5uh7tOXyK/n0ZvWpad5caBA17GsC6vyuCqaWliRG5K1qS9inmUhEMaOBIW7/whAnSwveW/LtZw== + dependencies: + is-alphabetical "^2.0.0" + is-decimal "^2.0.0" + +is-arrayish@^0.3.1: + version "0.3.2" + resolved "https://registry.yarnpkg.com/is-arrayish/-/is-arrayish-0.3.2.tgz#4574a2ae56f7ab206896fb431eaeed066fdf8f03" + integrity sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ== + +is-binary-path@~2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/is-binary-path/-/is-binary-path-2.1.0.tgz#ea1f7f3b80f064236e83470f86c09c254fb45b09" + integrity sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw== + dependencies: + binary-extensions "^2.0.0" + +is-buffer@^2.0.0: + version "2.0.5" + resolved "https://registry.yarnpkg.com/is-buffer/-/is-buffer-2.0.5.tgz#ebc252e400d22ff8d77fa09888821a24a658c191" + integrity sha512-i2R6zNFDwgEHJyQUtJEk0XFi1i0dPFn/oqjK3/vPCcDeJvW5NQ83V8QbicfF1SupOaB0h8ntgBC2YiE7dfyctQ== + +is-core-module@^2.13.0: + version "2.13.1" + resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.13.1.tgz#ad0d7532c6fea9da1ebdc82742d74525c6273384" + integrity sha512-hHrIjvZsftOsvKSn2TRYl63zvxsgE0K+0mYMoH6gD4omR5IWB2KynivBQczo3+wF1cCkjzvptnI9Q0sPU66ilw== + dependencies: + hasown "^2.0.0" + +is-decimal@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/is-decimal/-/is-decimal-2.0.1.tgz#9469d2dc190d0214fd87d78b78caecc0cc14eef7" + integrity sha512-AAB9hiomQs5DXWcRB1rqsxGUstbRroFOPPVAomNk/3XHR5JyEZChOyTWe2oayKnsSsr/kcGqF+z6yuH6HHpN0A== + +is-docker@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/is-docker/-/is-docker-3.0.0.tgz#90093aa3106277d8a77a5910dbae71747e15a200" + integrity sha512-eljcgEDlEns/7AXFosB5K/2nCM4P7FQPkGc/DWLy5rmFEWvZayGrik1d9/QIY5nJ4f9YsVvBkA6kJpHn9rISdQ== + +is-extendable@^0.1.0: + version "0.1.1" + resolved "https://registry.yarnpkg.com/is-extendable/-/is-extendable-0.1.1.tgz#62b110e289a471418e3ec36a617d472e301dfc89" + integrity sha512-5BMULNob1vgFX6EjQw5izWDxrecWK9AM72rugNr0TFldMOi0fj6Jk+zeKIt0xGj4cEfQIJth4w3OKWOJ4f+AFw== + +is-extglob@^2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/is-extglob/-/is-extglob-2.1.1.tgz#a88c02535791f02ed37c76a1b9ea9773c833f8c2" + integrity sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ== + +is-fullwidth-code-point@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz#f116f8064fe90b3f7844a38997c0b75051269f1d" + integrity sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg== + +is-glob@^4.0.1, is-glob@~4.0.1: + version "4.0.3" + resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-4.0.3.tgz#64f61e42cbbb2eec2071a9dac0b28ba1e65d5084" + integrity sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg== + dependencies: + is-extglob "^2.1.1" + +is-hexadecimal@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/is-hexadecimal/-/is-hexadecimal-2.0.1.tgz#86b5bf668fca307498d319dfc03289d781a90027" + integrity sha512-DgZQp241c8oO6cA1SbTEWiXeoxV42vlcJxgH+B3hi1AiqqKruZR3ZGF8In3fj4+/y/7rHvlOZLZtgJ/4ttYGZg== + +is-inside-container@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/is-inside-container/-/is-inside-container-1.0.0.tgz#e81fba699662eb31dbdaf26766a61d4814717ea4" + integrity sha512-KIYLCCJghfHZxqjYBE7rEy0OBuTd5xCHS7tHVgvCLkx7StIoaxwNW3hCALgEUjFfeRk+MG/Qxmp/vtETEF3tRA== + dependencies: + is-docker "^3.0.0" + +is-interactive@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/is-interactive/-/is-interactive-2.0.0.tgz#40c57614593826da1100ade6059778d597f16e90" + integrity sha512-qP1vozQRI+BMOPcjFzrjXuQvdak2pHNUMZoeG2eRbiSqyvbEf/wQtEOTOX1guk6E3t36RkaqiSt8A/6YElNxLQ== + +is-number@^7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/is-number/-/is-number-7.0.0.tgz#7535345b896734d5f80c4d06c50955527a14f12b" + integrity sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng== + +is-plain-obj@^4.0.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/is-plain-obj/-/is-plain-obj-4.1.0.tgz#d65025edec3657ce032fd7db63c97883eaed71f0" + integrity sha512-+Pgi+vMuUNkJyExiMBt5IlFoMyKnr5zhJ4Uspz58WOhBF5QoIZkFyNHIbBAtHwzVAgk5RtndVNsDRN61/mmDqg== + +is-reference@^3.0.0: + version "3.0.2" + resolved "https://registry.yarnpkg.com/is-reference/-/is-reference-3.0.2.tgz#154747a01f45cd962404ee89d43837af2cba247c" + integrity sha512-v3rht/LgVcsdZa3O2Nqs+NMowLOxeOm7Ay9+/ARQ2F+qEoANRcqrjAZKGN0v8ymUetZGgkp26LTnGT7H0Qo9Pg== + dependencies: + "@types/estree" "*" + +is-stream@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-3.0.0.tgz#e6bfd7aa6bef69f4f472ce9bb681e3e57b4319ac" + integrity sha512-LnQR4bZ9IADDRSkvpqMGvt/tEJWclzklNgSw48V5EAaAeDd6qGvN8ei6k5p0tvxSR171VmGyHuTiAOfxAbr8kA== + +is-unicode-supported@^1.1.0, is-unicode-supported@^1.3.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/is-unicode-supported/-/is-unicode-supported-1.3.0.tgz#d824984b616c292a2e198207d4a609983842f714" + integrity sha512-43r2mRvz+8JRIKnWJ+3j8JtjRKZ6GmjzfaE/qiBJnikNnYv/6bagRJ1kUhNk8R5EX/GkobD+r+sfxCPJsiKBLQ== + +is-wsl@^3.0.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/is-wsl/-/is-wsl-3.1.0.tgz#e1c657e39c10090afcbedec61720f6b924c3cbd2" + integrity sha512-UcVfVfaK4Sc4m7X3dUSoHoozQGBEFeDC+zVo06t98xe8CzHSZZBekNXH+tu0NalHolcJ/QAGqS46Hef7QXBIMw== + dependencies: + is-inside-container "^1.0.0" + +isexe@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10" + integrity sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw== + +js-tokens@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499" + integrity sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ== + +js-yaml@^3.13.0, js-yaml@^3.13.1: + version "3.14.1" + resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.14.1.tgz#dae812fdb3825fa306609a8717383c50c36a0537" + integrity sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g== + dependencies: + argparse "^1.0.7" + esprima "^4.0.0" + +js-yaml@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-4.1.0.tgz#c1fb65f8f5017901cdd2c951864ba18458a10602" + integrity sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA== + dependencies: + argparse "^2.0.1" + +jsesc@^2.5.1: + version "2.5.2" + resolved "https://registry.yarnpkg.com/jsesc/-/jsesc-2.5.2.tgz#80564d2e483dacf6e8ef209650a67df3f0c283a4" + integrity sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA== + +json5@^2.2.3: + version "2.2.3" + resolved "https://registry.yarnpkg.com/json5/-/json5-2.2.3.tgz#78cd6f1a19bdc12b73db5ad0c61efd66c1e29283" + integrity sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg== + +jsonc-parser@^2.3.0: + version "2.3.1" + resolved "https://registry.yarnpkg.com/jsonc-parser/-/jsonc-parser-2.3.1.tgz#59549150b133f2efacca48fe9ce1ec0659af2342" + integrity sha512-H8jvkz1O50L3dMZCsLqiuB2tA7muqbSg1AtGEkN0leAqGjsUzDJir3Zwr02BhqdcITPg3ei3mZ+HjMocAknhhg== + +kind-of@^6.0.0, kind-of@^6.0.2: + version "6.0.3" + resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-6.0.3.tgz#07c05034a6c349fa06e24fa35aa76db4580ce4dd" + integrity sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw== + +kleur@^3.0.3: + version "3.0.3" + resolved "https://registry.yarnpkg.com/kleur/-/kleur-3.0.3.tgz#a79c9ecc86ee1ce3fa6206d1216c501f147fc07e" + integrity sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w== + +kleur@^4.1.4, kleur@^4.1.5: + version "4.1.5" + resolved "https://registry.yarnpkg.com/kleur/-/kleur-4.1.5.tgz#95106101795f7050c6c650f350c683febddb1780" + integrity sha512-o+NO+8WrRiQEE4/7nwRJhN1HWpVmJm511pBHUxPLtp0BUISzlBplORYSmTclCnJvQq2tKu/sgl3xVpkc7ZWuQQ== + +load-yaml-file@^0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/load-yaml-file/-/load-yaml-file-0.2.0.tgz#af854edaf2bea89346c07549122753c07372f64d" + integrity sha512-OfCBkGEw4nN6JLtgRidPX6QxjBQGQf72q3si2uvqyFEMbycSFFHwAZeXx6cJgFM9wmLrf9zBwCP3Ivqa+LLZPw== + dependencies: + graceful-fs "^4.1.5" + js-yaml "^3.13.0" + pify "^4.0.1" + strip-bom "^3.0.0" + +locate-path@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-5.0.0.tgz#1afba396afd676a6d42504d0a67a3a7eb9f62aa0" + integrity sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g== + dependencies: + p-locate "^4.1.0" + +locate-path@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-6.0.0.tgz#55321eb309febbc59c4801d931a72452a681d286" + integrity sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw== + dependencies: + p-locate "^5.0.0" + +lodash.merge@^4.6.2: + version "4.6.2" + resolved "https://registry.yarnpkg.com/lodash.merge/-/lodash.merge-4.6.2.tgz#558aa53b43b661e1925a0afdfa36a9a1085fe57a" + integrity sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ== + +log-symbols@^5.1.0: + version "5.1.0" + resolved "https://registry.yarnpkg.com/log-symbols/-/log-symbols-5.1.0.tgz#a20e3b9a5f53fac6aeb8e2bb22c07cf2c8f16d93" + integrity sha512-l0x2DvrW294C9uDCoQe1VSU4gf529FkSZ6leBl4TiqZH/e+0R7hSfHQBNut2mNygDgHwvYHfFLn6Oxb3VWj2rA== + dependencies: + chalk "^5.0.0" + is-unicode-supported "^1.1.0" + +longest-streak@^3.0.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/longest-streak/-/longest-streak-3.1.0.tgz#62fa67cd958742a1574af9f39866364102d90cd4" + integrity sha512-9Ri+o0JYgehTaVBBDoMqIl8GXtbWg711O3srftcHhZ0dqnETqLaoIK0x17fUw9rFSlK/0NlsKe0Ahhyl5pXE2g== + +lru-cache@^5.1.1: + version "5.1.1" + resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-5.1.1.tgz#1da27e6710271947695daf6848e847f01d84b920" + integrity sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w== + dependencies: + yallist "^3.0.2" + +lru-cache@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-6.0.0.tgz#6d6fe6570ebd96aaf90fcad1dafa3b2566db3a94" + integrity sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA== + dependencies: + yallist "^4.0.0" + +magic-string@^0.30.3: + version "0.30.5" + resolved "https://registry.yarnpkg.com/magic-string/-/magic-string-0.30.5.tgz#1994d980bd1c8835dc6e78db7cbd4ae4f24746f9" + integrity sha512-7xlpfBaQaP/T6Vh8MO/EqXSW5En6INHEvEXQiuff7Gku0PWjU3uf6w/j9o7O+SpB5fOAkrI5HeoNgwjEO0pFsA== + dependencies: + "@jridgewell/sourcemap-codec" "^1.4.15" + +markdown-extensions@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/markdown-extensions/-/markdown-extensions-2.0.0.tgz#34bebc83e9938cae16e0e017e4a9814a8330d3c4" + integrity sha512-o5vL7aDWatOTX8LzaS1WMoaoxIiLRQJuIKKe2wAw6IeULDHaqbiqiggmx+pKvZDb1Sj+pE46Sn1T7lCqfFtg1Q== + +markdown-table@^3.0.0: + version "3.0.3" + resolved "https://registry.yarnpkg.com/markdown-table/-/markdown-table-3.0.3.tgz#e6331d30e493127e031dd385488b5bd326e4a6bd" + integrity sha512-Z1NL3Tb1M9wH4XESsCDEksWoKTdlUafKc4pt0GRwjUyXaCFZ+dc3g2erqB6zm3szA2IUSi7VnPI+o/9jnxh9hw== + +mdast-util-definitions@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/mdast-util-definitions/-/mdast-util-definitions-6.0.0.tgz#c1bb706e5e76bb93f9a09dd7af174002ae69ac24" + integrity sha512-scTllyX6pnYNZH/AIp/0ePz6s4cZtARxImwoPJ7kS42n+MnVsI4XbnG6d4ibehRIldYMWM2LD7ImQblVhUejVQ== + dependencies: + "@types/mdast" "^4.0.0" + "@types/unist" "^3.0.0" + unist-util-visit "^5.0.0" + +mdast-util-directive@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/mdast-util-directive/-/mdast-util-directive-3.0.0.tgz#3fb1764e705bbdf0afb0d3f889e4404c3e82561f" + integrity sha512-JUpYOqKI4mM3sZcNxmF/ox04XYFFkNwr0CFlrQIkCwbvH0xzMCqkMqAde9wRd80VAhaUrwFwKm2nxretdT1h7Q== + dependencies: + "@types/mdast" "^4.0.0" + "@types/unist" "^3.0.0" + devlop "^1.0.0" + mdast-util-from-markdown "^2.0.0" + mdast-util-to-markdown "^2.0.0" + parse-entities "^4.0.0" + stringify-entities "^4.0.0" + unist-util-visit-parents "^6.0.0" + +mdast-util-find-and-replace@^3.0.0: + version "3.0.1" + resolved "https://registry.yarnpkg.com/mdast-util-find-and-replace/-/mdast-util-find-and-replace-3.0.1.tgz#a6fc7b62f0994e973490e45262e4bc07607b04e0" + integrity sha512-SG21kZHGC3XRTSUhtofZkBzZTJNM5ecCi0SK2IMKmSXR8vO3peL+kb1O0z7Zl83jKtutG4k5Wv/W7V3/YHvzPA== + dependencies: + "@types/mdast" "^4.0.0" + escape-string-regexp "^5.0.0" + unist-util-is "^6.0.0" + unist-util-visit-parents "^6.0.0" + +mdast-util-from-markdown@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/mdast-util-from-markdown/-/mdast-util-from-markdown-2.0.0.tgz#52f14815ec291ed061f2922fd14d6689c810cb88" + integrity sha512-n7MTOr/z+8NAX/wmhhDji8O3bRvPTV/U0oTCaZJkjhPSKTPhS3xufVhKGF8s1pJ7Ox4QgoIU7KHseh09S+9rTA== + dependencies: + "@types/mdast" "^4.0.0" + "@types/unist" "^3.0.0" + decode-named-character-reference "^1.0.0" + devlop "^1.0.0" + mdast-util-to-string "^4.0.0" + micromark "^4.0.0" + micromark-util-decode-numeric-character-reference "^2.0.0" + micromark-util-decode-string "^2.0.0" + micromark-util-normalize-identifier "^2.0.0" + micromark-util-symbol "^2.0.0" + micromark-util-types "^2.0.0" + unist-util-stringify-position "^4.0.0" + +mdast-util-gfm-autolink-literal@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/mdast-util-gfm-autolink-literal/-/mdast-util-gfm-autolink-literal-2.0.0.tgz#5baf35407421310a08e68c15e5d8821e8898ba2a" + integrity sha512-FyzMsduZZHSc3i0Px3PQcBT4WJY/X/RCtEJKuybiC6sjPqLv7h1yqAkmILZtuxMSsUyaLUWNp71+vQH2zqp5cg== + dependencies: + "@types/mdast" "^4.0.0" + ccount "^2.0.0" + devlop "^1.0.0" + mdast-util-find-and-replace "^3.0.0" + micromark-util-character "^2.0.0" + +mdast-util-gfm-footnote@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/mdast-util-gfm-footnote/-/mdast-util-gfm-footnote-2.0.0.tgz#25a1753c7d16db8bfd53cd84fe50562bd1e6d6a9" + integrity sha512-5jOT2boTSVkMnQ7LTrd6n/18kqwjmuYqo7JUPe+tRCY6O7dAuTFMtTPauYYrMPpox9hlN0uOx/FL8XvEfG9/mQ== + dependencies: + "@types/mdast" "^4.0.0" + devlop "^1.1.0" + mdast-util-from-markdown "^2.0.0" + mdast-util-to-markdown "^2.0.0" + micromark-util-normalize-identifier "^2.0.0" + +mdast-util-gfm-strikethrough@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/mdast-util-gfm-strikethrough/-/mdast-util-gfm-strikethrough-2.0.0.tgz#d44ef9e8ed283ac8c1165ab0d0dfd058c2764c16" + integrity sha512-mKKb915TF+OC5ptj5bJ7WFRPdYtuHv0yTRxK2tJvi+BDqbkiG7h7u/9SI89nRAYcmap2xHQL9D+QG/6wSrTtXg== + dependencies: + "@types/mdast" "^4.0.0" + mdast-util-from-markdown "^2.0.0" + mdast-util-to-markdown "^2.0.0" + +mdast-util-gfm-table@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/mdast-util-gfm-table/-/mdast-util-gfm-table-2.0.0.tgz#7a435fb6223a72b0862b33afbd712b6dae878d38" + integrity sha512-78UEvebzz/rJIxLvE7ZtDd/vIQ0RHv+3Mh5DR96p7cS7HsBhYIICDBCu8csTNWNO6tBWfqXPWekRuj2FNOGOZg== + dependencies: + "@types/mdast" "^4.0.0" + devlop "^1.0.0" + markdown-table "^3.0.0" + mdast-util-from-markdown "^2.0.0" + mdast-util-to-markdown "^2.0.0" + +mdast-util-gfm-task-list-item@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/mdast-util-gfm-task-list-item/-/mdast-util-gfm-task-list-item-2.0.0.tgz#e68095d2f8a4303ef24094ab642e1047b991a936" + integrity sha512-IrtvNvjxC1o06taBAVJznEnkiHxLFTzgonUdy8hzFVeDun0uTjxxrRGVaNFqkU1wJR3RBPEfsxmU6jDWPofrTQ== + dependencies: + "@types/mdast" "^4.0.0" + devlop "^1.0.0" + mdast-util-from-markdown "^2.0.0" + mdast-util-to-markdown "^2.0.0" + +mdast-util-gfm@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/mdast-util-gfm/-/mdast-util-gfm-3.0.0.tgz#3f2aecc879785c3cb6a81ff3a243dc11eca61095" + integrity sha512-dgQEX5Amaq+DuUqf26jJqSK9qgixgd6rYDHAv4aTBuA92cTknZlKpPfa86Z/s8Dj8xsAQpFfBmPUHWJBWqS4Bw== + dependencies: + mdast-util-from-markdown "^2.0.0" + mdast-util-gfm-autolink-literal "^2.0.0" + mdast-util-gfm-footnote "^2.0.0" + mdast-util-gfm-strikethrough "^2.0.0" + mdast-util-gfm-table "^2.0.0" + mdast-util-gfm-task-list-item "^2.0.0" + mdast-util-to-markdown "^2.0.0" + +mdast-util-mdx-expression@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/mdast-util-mdx-expression/-/mdast-util-mdx-expression-2.0.0.tgz#4968b73724d320a379110d853e943a501bfd9d87" + integrity sha512-fGCu8eWdKUKNu5mohVGkhBXCXGnOTLuFqOvGMvdikr+J1w7lDJgxThOKpwRWzzbyXAU2hhSwsmssOY4yTokluw== + dependencies: + "@types/estree-jsx" "^1.0.0" + "@types/hast" "^3.0.0" + "@types/mdast" "^4.0.0" + devlop "^1.0.0" + mdast-util-from-markdown "^2.0.0" + mdast-util-to-markdown "^2.0.0" + +mdast-util-mdx-jsx@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/mdast-util-mdx-jsx/-/mdast-util-mdx-jsx-3.0.0.tgz#f73631fa5bb7a36712ff1e9cedec0cafed03401c" + integrity sha512-XZuPPzQNBPAlaqsTTgRrcJnyFbSOBovSadFgbFu8SnuNgm+6Bdx1K+IWoitsmj6Lq6MNtI+ytOqwN70n//NaBA== + dependencies: + "@types/estree-jsx" "^1.0.0" + "@types/hast" "^3.0.0" + "@types/mdast" "^4.0.0" + "@types/unist" "^3.0.0" + ccount "^2.0.0" + devlop "^1.1.0" + mdast-util-from-markdown "^2.0.0" + mdast-util-to-markdown "^2.0.0" + parse-entities "^4.0.0" + stringify-entities "^4.0.0" + unist-util-remove-position "^5.0.0" + unist-util-stringify-position "^4.0.0" + vfile-message "^4.0.0" + +mdast-util-mdx@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/mdast-util-mdx/-/mdast-util-mdx-3.0.0.tgz#792f9cf0361b46bee1fdf1ef36beac424a099c41" + integrity sha512-JfbYLAW7XnYTTbUsmpu0kdBUVe+yKVJZBItEjwyYJiDJuZ9w4eeaqks4HQO+R7objWgS2ymV60GYpI14Ug554w== + dependencies: + mdast-util-from-markdown "^2.0.0" + mdast-util-mdx-expression "^2.0.0" + mdast-util-mdx-jsx "^3.0.0" + mdast-util-mdxjs-esm "^2.0.0" + mdast-util-to-markdown "^2.0.0" + +mdast-util-mdxjs-esm@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/mdast-util-mdxjs-esm/-/mdast-util-mdxjs-esm-2.0.1.tgz#019cfbe757ad62dd557db35a695e7314bcc9fa97" + integrity sha512-EcmOpxsZ96CvlP03NghtH1EsLtr0n9Tm4lPUJUBccV9RwUOneqSycg19n5HGzCf+10LozMRSObtVr3ee1WoHtg== + dependencies: + "@types/estree-jsx" "^1.0.0" + "@types/hast" "^3.0.0" + "@types/mdast" "^4.0.0" + devlop "^1.0.0" + mdast-util-from-markdown "^2.0.0" + mdast-util-to-markdown "^2.0.0" + +mdast-util-phrasing@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/mdast-util-phrasing/-/mdast-util-phrasing-4.0.0.tgz#468cbbb277375523de807248b8ad969feb02a5c7" + integrity sha512-xadSsJayQIucJ9n053dfQwVu1kuXg7jCTdYsMK8rqzKZh52nLfSH/k0sAxE0u+pj/zKZX+o5wB+ML5mRayOxFA== + dependencies: + "@types/mdast" "^4.0.0" + unist-util-is "^6.0.0" + +mdast-util-to-hast@13.0.2: + version "13.0.2" + resolved "https://registry.yarnpkg.com/mdast-util-to-hast/-/mdast-util-to-hast-13.0.2.tgz#74c0a9f014bb2340cae6118f6fccd75467792be7" + integrity sha512-U5I+500EOOw9e3ZrclN3Is3fRpw8c19SMyNZlZ2IS+7vLsNzb2Om11VpIVOR+/0137GhZsFEF6YiKD5+0Hr2Og== + dependencies: + "@types/hast" "^3.0.0" + "@types/mdast" "^4.0.0" + "@ungap/structured-clone" "^1.0.0" + devlop "^1.0.0" + micromark-util-sanitize-uri "^2.0.0" + trim-lines "^3.0.0" + unist-util-position "^5.0.0" + unist-util-visit "^5.0.0" + +mdast-util-to-hast@^13.0.0: + version "13.1.0" + resolved "https://registry.yarnpkg.com/mdast-util-to-hast/-/mdast-util-to-hast-13.1.0.tgz#1ae54d903150a10fe04d59f03b2b95fd210b2124" + integrity sha512-/e2l/6+OdGp/FB+ctrJ9Avz71AN/GRH3oi/3KAx/kMnoUsD6q0woXlDT8lLEeViVKE7oZxE7RXzvO3T8kF2/sA== + dependencies: + "@types/hast" "^3.0.0" + "@types/mdast" "^4.0.0" + "@ungap/structured-clone" "^1.0.0" + devlop "^1.0.0" + micromark-util-sanitize-uri "^2.0.0" + trim-lines "^3.0.0" + unist-util-position "^5.0.0" + unist-util-visit "^5.0.0" + vfile "^6.0.0" + +mdast-util-to-markdown@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/mdast-util-to-markdown/-/mdast-util-to-markdown-2.1.0.tgz#9813f1d6e0cdaac7c244ec8c6dabfdb2102ea2b4" + integrity sha512-SR2VnIEdVNCJbP6y7kVTJgPLifdr8WEU440fQec7qHoHOUz/oJ2jmNRqdDQ3rbiStOXb2mCDGTuwsK5OPUgYlQ== + dependencies: + "@types/mdast" "^4.0.0" + "@types/unist" "^3.0.0" + longest-streak "^3.0.0" + mdast-util-phrasing "^4.0.0" + mdast-util-to-string "^4.0.0" + micromark-util-decode-string "^2.0.0" + unist-util-visit "^5.0.0" + zwitch "^2.0.0" + +mdast-util-to-string@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/mdast-util-to-string/-/mdast-util-to-string-4.0.0.tgz#7a5121475556a04e7eddeb67b264aae79d312814" + integrity sha512-0H44vDimn51F0YwvxSJSm0eCDOJTRlmN0R1yBh4HLj9wiV1Dn0QoXGbvFAWj2hSItVTlCmBF1hqKlIyUBVFLPg== + dependencies: + "@types/mdast" "^4.0.0" + +merge-stream@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/merge-stream/-/merge-stream-2.0.0.tgz#52823629a14dd00c9770fb6ad47dc6310f2c1f60" + integrity sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w== + +merge2@^1.3.0: + version "1.4.1" + resolved "https://registry.yarnpkg.com/merge2/-/merge2-1.4.1.tgz#4368892f885e907455a6fd7dc55c0c9d404990ae" + integrity sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg== + +micromark-core-commonmark@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/micromark-core-commonmark/-/micromark-core-commonmark-2.0.0.tgz#50740201f0ee78c12a675bf3e68ffebc0bf931a3" + integrity sha512-jThOz/pVmAYUtkroV3D5c1osFXAMv9e0ypGDOIZuCeAe91/sD6BoE2Sjzt30yuXtwOYUmySOhMas/PVyh02itA== + dependencies: + decode-named-character-reference "^1.0.0" + devlop "^1.0.0" + micromark-factory-destination "^2.0.0" + micromark-factory-label "^2.0.0" + micromark-factory-space "^2.0.0" + micromark-factory-title "^2.0.0" + micromark-factory-whitespace "^2.0.0" + micromark-util-character "^2.0.0" + micromark-util-chunked "^2.0.0" + micromark-util-classify-character "^2.0.0" + micromark-util-html-tag-name "^2.0.0" + micromark-util-normalize-identifier "^2.0.0" + micromark-util-resolve-all "^2.0.0" + micromark-util-subtokenize "^2.0.0" + micromark-util-symbol "^2.0.0" + micromark-util-types "^2.0.0" + +micromark-extension-directive@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/micromark-extension-directive/-/micromark-extension-directive-3.0.0.tgz#527869de497a6de9024138479091bc885dae076b" + integrity sha512-61OI07qpQrERc+0wEysLHMvoiO3s2R56x5u7glHq2Yqq6EHbH4dW25G9GfDdGCDYqA21KE6DWgNSzxSwHc2hSg== + dependencies: + devlop "^1.0.0" + micromark-factory-space "^2.0.0" + micromark-factory-whitespace "^2.0.0" + micromark-util-character "^2.0.0" + micromark-util-symbol "^2.0.0" + micromark-util-types "^2.0.0" + parse-entities "^4.0.0" + +micromark-extension-gfm-autolink-literal@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/micromark-extension-gfm-autolink-literal/-/micromark-extension-gfm-autolink-literal-2.0.0.tgz#f1e50b42e67d441528f39a67133eddde2bbabfd9" + integrity sha512-rTHfnpt/Q7dEAK1Y5ii0W8bhfJlVJFnJMHIPisfPK3gpVNuOP0VnRl96+YJ3RYWV/P4gFeQoGKNlT3RhuvpqAg== + dependencies: + micromark-util-character "^2.0.0" + micromark-util-sanitize-uri "^2.0.0" + micromark-util-symbol "^2.0.0" + micromark-util-types "^2.0.0" + +micromark-extension-gfm-footnote@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/micromark-extension-gfm-footnote/-/micromark-extension-gfm-footnote-2.0.0.tgz#91afad310065a94b636ab1e9dab2c60d1aab953c" + integrity sha512-6Rzu0CYRKDv3BfLAUnZsSlzx3ak6HAoI85KTiijuKIz5UxZxbUI+pD6oHgw+6UtQuiRwnGRhzMmPRv4smcz0fg== + dependencies: + devlop "^1.0.0" + micromark-core-commonmark "^2.0.0" + micromark-factory-space "^2.0.0" + micromark-util-character "^2.0.0" + micromark-util-normalize-identifier "^2.0.0" + micromark-util-sanitize-uri "^2.0.0" + micromark-util-symbol "^2.0.0" + micromark-util-types "^2.0.0" + +micromark-extension-gfm-strikethrough@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/micromark-extension-gfm-strikethrough/-/micromark-extension-gfm-strikethrough-2.0.0.tgz#6917db8e320da70e39ffbf97abdbff83e6783e61" + integrity sha512-c3BR1ClMp5fxxmwP6AoOY2fXO9U8uFMKs4ADD66ahLTNcwzSCyRVU4k7LPV5Nxo/VJiR4TdzxRQY2v3qIUceCw== + dependencies: + devlop "^1.0.0" + micromark-util-chunked "^2.0.0" + micromark-util-classify-character "^2.0.0" + micromark-util-resolve-all "^2.0.0" + micromark-util-symbol "^2.0.0" + micromark-util-types "^2.0.0" + +micromark-extension-gfm-table@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/micromark-extension-gfm-table/-/micromark-extension-gfm-table-2.0.0.tgz#2cf3fe352d9e089b7ef5fff003bdfe0da29649b7" + integrity sha512-PoHlhypg1ItIucOaHmKE8fbin3vTLpDOUg8KAr8gRCF1MOZI9Nquq2i/44wFvviM4WuxJzc3demT8Y3dkfvYrw== + dependencies: + devlop "^1.0.0" + micromark-factory-space "^2.0.0" + micromark-util-character "^2.0.0" + micromark-util-symbol "^2.0.0" + micromark-util-types "^2.0.0" + +micromark-extension-gfm-tagfilter@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/micromark-extension-gfm-tagfilter/-/micromark-extension-gfm-tagfilter-2.0.0.tgz#f26d8a7807b5985fba13cf61465b58ca5ff7dc57" + integrity sha512-xHlTOmuCSotIA8TW1mDIM6X2O1SiX5P9IuDtqGonFhEK0qgRI4yeC6vMxEV2dgyr2TiD+2PQ10o+cOhdVAcwfg== + dependencies: + micromark-util-types "^2.0.0" + +micromark-extension-gfm-task-list-item@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/micromark-extension-gfm-task-list-item/-/micromark-extension-gfm-task-list-item-2.0.1.tgz#ee8b208f1ced1eb9fb11c19a23666e59d86d4838" + integrity sha512-cY5PzGcnULaN5O7T+cOzfMoHjBW7j+T9D2sucA5d/KbsBTPcYdebm9zUd9zzdgJGCwahV+/W78Z3nbulBYVbTw== + dependencies: + devlop "^1.0.0" + micromark-factory-space "^2.0.0" + micromark-util-character "^2.0.0" + micromark-util-symbol "^2.0.0" + micromark-util-types "^2.0.0" + +micromark-extension-gfm@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/micromark-extension-gfm/-/micromark-extension-gfm-3.0.0.tgz#3e13376ab95dd7a5cfd0e29560dfe999657b3c5b" + integrity sha512-vsKArQsicm7t0z2GugkCKtZehqUm31oeGBV/KVSorWSy8ZlNAv7ytjFhvaryUiCUJYqs+NoE6AFhpQvBTM6Q4w== + dependencies: + micromark-extension-gfm-autolink-literal "^2.0.0" + micromark-extension-gfm-footnote "^2.0.0" + micromark-extension-gfm-strikethrough "^2.0.0" + micromark-extension-gfm-table "^2.0.0" + micromark-extension-gfm-tagfilter "^2.0.0" + micromark-extension-gfm-task-list-item "^2.0.0" + micromark-util-combine-extensions "^2.0.0" + micromark-util-types "^2.0.0" + +micromark-extension-mdx-expression@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/micromark-extension-mdx-expression/-/micromark-extension-mdx-expression-3.0.0.tgz#1407b9ce69916cf5e03a196ad9586889df25302a" + integrity sha512-sI0nwhUDz97xyzqJAbHQhp5TfaxEvZZZ2JDqUo+7NvyIYG6BZ5CPPqj2ogUoPJlmXHBnyZUzISg9+oUmU6tUjQ== + dependencies: + "@types/estree" "^1.0.0" + devlop "^1.0.0" + micromark-factory-mdx-expression "^2.0.0" + micromark-factory-space "^2.0.0" + micromark-util-character "^2.0.0" + micromark-util-events-to-acorn "^2.0.0" + micromark-util-symbol "^2.0.0" + micromark-util-types "^2.0.0" + +micromark-extension-mdx-jsx@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/micromark-extension-mdx-jsx/-/micromark-extension-mdx-jsx-3.0.0.tgz#4aba0797c25efb2366a3fd2d367c6b1c1159f4f5" + integrity sha512-uvhhss8OGuzR4/N17L1JwvmJIpPhAd8oByMawEKx6NVdBCbesjH4t+vjEp3ZXft9DwvlKSD07fCeI44/N0Vf2w== + dependencies: + "@types/acorn" "^4.0.0" + "@types/estree" "^1.0.0" + devlop "^1.0.0" + estree-util-is-identifier-name "^3.0.0" + micromark-factory-mdx-expression "^2.0.0" + micromark-factory-space "^2.0.0" + micromark-util-character "^2.0.0" + micromark-util-symbol "^2.0.0" + micromark-util-types "^2.0.0" + vfile-message "^4.0.0" + +micromark-extension-mdx-md@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/micromark-extension-mdx-md/-/micromark-extension-mdx-md-2.0.0.tgz#1d252881ea35d74698423ab44917e1f5b197b92d" + integrity sha512-EpAiszsB3blw4Rpba7xTOUptcFeBFi+6PY8VnJ2hhimH+vCQDirWgsMpz7w1XcZE7LVrSAUGb9VJpG9ghlYvYQ== + dependencies: + micromark-util-types "^2.0.0" + +micromark-extension-mdxjs-esm@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/micromark-extension-mdxjs-esm/-/micromark-extension-mdxjs-esm-3.0.0.tgz#de21b2b045fd2059bd00d36746081de38390d54a" + integrity sha512-DJFl4ZqkErRpq/dAPyeWp15tGrcrrJho1hKK5uBS70BCtfrIFg81sqcTVu3Ta+KD1Tk5vAtBNElWxtAa+m8K9A== + dependencies: + "@types/estree" "^1.0.0" + devlop "^1.0.0" + micromark-core-commonmark "^2.0.0" + micromark-util-character "^2.0.0" + micromark-util-events-to-acorn "^2.0.0" + micromark-util-symbol "^2.0.0" + micromark-util-types "^2.0.0" + unist-util-position-from-estree "^2.0.0" + vfile-message "^4.0.0" + +micromark-extension-mdxjs@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/micromark-extension-mdxjs/-/micromark-extension-mdxjs-3.0.0.tgz#b5a2e0ed449288f3f6f6c544358159557549de18" + integrity sha512-A873fJfhnJ2siZyUrJ31l34Uqwy4xIFmvPY1oj+Ean5PHcPBYzEsvqvWGaWcfEIr11O5Dlw3p2y0tZWpKHDejQ== + dependencies: + acorn "^8.0.0" + acorn-jsx "^5.0.0" + micromark-extension-mdx-expression "^3.0.0" + micromark-extension-mdx-jsx "^3.0.0" + micromark-extension-mdx-md "^2.0.0" + micromark-extension-mdxjs-esm "^3.0.0" + micromark-util-combine-extensions "^2.0.0" + micromark-util-types "^2.0.0" + +micromark-factory-destination@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/micromark-factory-destination/-/micromark-factory-destination-2.0.0.tgz#857c94debd2c873cba34e0445ab26b74f6a6ec07" + integrity sha512-j9DGrQLm/Uhl2tCzcbLhy5kXsgkHUrjJHg4fFAeoMRwJmJerT9aw4FEhIbZStWN8A3qMwOp1uzHr4UL8AInxtA== + dependencies: + micromark-util-character "^2.0.0" + micromark-util-symbol "^2.0.0" + micromark-util-types "^2.0.0" + +micromark-factory-label@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/micromark-factory-label/-/micromark-factory-label-2.0.0.tgz#17c5c2e66ce39ad6f4fc4cbf40d972f9096f726a" + integrity sha512-RR3i96ohZGde//4WSe/dJsxOX6vxIg9TimLAS3i4EhBAFx8Sm5SmqVfR8E87DPSR31nEAjZfbt91OMZWcNgdZw== + dependencies: + devlop "^1.0.0" + micromark-util-character "^2.0.0" + micromark-util-symbol "^2.0.0" + micromark-util-types "^2.0.0" + +micromark-factory-mdx-expression@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/micromark-factory-mdx-expression/-/micromark-factory-mdx-expression-2.0.1.tgz#f2a9724ce174f1751173beb2c1f88062d3373b1b" + integrity sha512-F0ccWIUHRLRrYp5TC9ZYXmZo+p2AM13ggbsW4T0b5CRKP8KHVRB8t4pwtBgTxtjRmwrK0Irwm7vs2JOZabHZfg== + dependencies: + "@types/estree" "^1.0.0" + devlop "^1.0.0" + micromark-util-character "^2.0.0" + micromark-util-events-to-acorn "^2.0.0" + micromark-util-symbol "^2.0.0" + micromark-util-types "^2.0.0" + unist-util-position-from-estree "^2.0.0" + vfile-message "^4.0.0" + +micromark-factory-space@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/micromark-factory-space/-/micromark-factory-space-2.0.0.tgz#5e7afd5929c23b96566d0e1ae018ae4fcf81d030" + integrity sha512-TKr+LIDX2pkBJXFLzpyPyljzYK3MtmllMUMODTQJIUfDGncESaqB90db9IAUcz4AZAJFdd8U9zOp9ty1458rxg== + dependencies: + micromark-util-character "^2.0.0" + micromark-util-types "^2.0.0" + +micromark-factory-title@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/micromark-factory-title/-/micromark-factory-title-2.0.0.tgz#726140fc77892af524705d689e1cf06c8a83ea95" + integrity sha512-jY8CSxmpWLOxS+t8W+FG3Xigc0RDQA9bKMY/EwILvsesiRniiVMejYTE4wumNc2f4UbAa4WsHqe3J1QS1sli+A== + dependencies: + micromark-factory-space "^2.0.0" + micromark-util-character "^2.0.0" + micromark-util-symbol "^2.0.0" + micromark-util-types "^2.0.0" + +micromark-factory-whitespace@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/micromark-factory-whitespace/-/micromark-factory-whitespace-2.0.0.tgz#9e92eb0f5468083381f923d9653632b3cfb5f763" + integrity sha512-28kbwaBjc5yAI1XadbdPYHX/eDnqaUFVikLwrO7FDnKG7lpgxnvk/XGRhX/PN0mOZ+dBSZ+LgunHS+6tYQAzhA== + dependencies: + micromark-factory-space "^2.0.0" + micromark-util-character "^2.0.0" + micromark-util-symbol "^2.0.0" + micromark-util-types "^2.0.0" + +micromark-util-character@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/micromark-util-character/-/micromark-util-character-2.0.1.tgz#52b824c2e2633b6fb33399d2ec78ee2a90d6b298" + integrity sha512-3wgnrmEAJ4T+mGXAUfMvMAbxU9RDG43XmGce4j6CwPtVxB3vfwXSZ6KhFwDzZ3mZHhmPimMAXg71veiBGzeAZw== + dependencies: + micromark-util-symbol "^2.0.0" + micromark-util-types "^2.0.0" + +micromark-util-chunked@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/micromark-util-chunked/-/micromark-util-chunked-2.0.0.tgz#e51f4db85fb203a79dbfef23fd41b2f03dc2ef89" + integrity sha512-anK8SWmNphkXdaKgz5hJvGa7l00qmcaUQoMYsBwDlSKFKjc6gjGXPDw3FNL3Nbwq5L8gE+RCbGqTw49FK5Qyvg== + dependencies: + micromark-util-symbol "^2.0.0" + +micromark-util-classify-character@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/micromark-util-classify-character/-/micromark-util-classify-character-2.0.0.tgz#8c7537c20d0750b12df31f86e976d1d951165f34" + integrity sha512-S0ze2R9GH+fu41FA7pbSqNWObo/kzwf8rN/+IGlW/4tC6oACOs8B++bh+i9bVyNnwCcuksbFwsBme5OCKXCwIw== + dependencies: + micromark-util-character "^2.0.0" + micromark-util-symbol "^2.0.0" + micromark-util-types "^2.0.0" + +micromark-util-combine-extensions@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/micromark-util-combine-extensions/-/micromark-util-combine-extensions-2.0.0.tgz#75d6ab65c58b7403616db8d6b31315013bfb7ee5" + integrity sha512-vZZio48k7ON0fVS3CUgFatWHoKbbLTK/rT7pzpJ4Bjp5JjkZeasRfrS9wsBdDJK2cJLHMckXZdzPSSr1B8a4oQ== + dependencies: + micromark-util-chunked "^2.0.0" + micromark-util-types "^2.0.0" + +micromark-util-decode-numeric-character-reference@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/micromark-util-decode-numeric-character-reference/-/micromark-util-decode-numeric-character-reference-2.0.1.tgz#2698bbb38f2a9ba6310e359f99fcb2b35a0d2bd5" + integrity sha512-bmkNc7z8Wn6kgjZmVHOX3SowGmVdhYS7yBpMnuMnPzDq/6xwVA604DuOXMZTO1lvq01g+Adfa0pE2UKGlxL1XQ== + dependencies: + micromark-util-symbol "^2.0.0" + +micromark-util-decode-string@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/micromark-util-decode-string/-/micromark-util-decode-string-2.0.0.tgz#7dfa3a63c45aecaa17824e656bcdb01f9737154a" + integrity sha512-r4Sc6leeUTn3P6gk20aFMj2ntPwn6qpDZqWvYmAG6NgvFTIlj4WtrAudLi65qYoaGdXYViXYw2pkmn7QnIFasA== + dependencies: + decode-named-character-reference "^1.0.0" + micromark-util-character "^2.0.0" + micromark-util-decode-numeric-character-reference "^2.0.0" + micromark-util-symbol "^2.0.0" + +micromark-util-encode@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/micromark-util-encode/-/micromark-util-encode-2.0.0.tgz#0921ac7953dc3f1fd281e3d1932decfdb9382ab1" + integrity sha512-pS+ROfCXAGLWCOc8egcBvT0kf27GoWMqtdarNfDcjb6YLuV5cM3ioG45Ys2qOVqeqSbjaKg72vU+Wby3eddPsA== + +micromark-util-events-to-acorn@^2.0.0: + version "2.0.2" + resolved "https://registry.yarnpkg.com/micromark-util-events-to-acorn/-/micromark-util-events-to-acorn-2.0.2.tgz#4275834f5453c088bd29cd72dfbf80e3327cec07" + integrity sha512-Fk+xmBrOv9QZnEDguL9OI9/NQQp6Hz4FuQ4YmCb/5V7+9eAh1s6AYSvL20kHkD67YIg7EpE54TiSlcsf3vyZgA== + dependencies: + "@types/acorn" "^4.0.0" + "@types/estree" "^1.0.0" + "@types/unist" "^3.0.0" + devlop "^1.0.0" + estree-util-visit "^2.0.0" + micromark-util-symbol "^2.0.0" + micromark-util-types "^2.0.0" + vfile-message "^4.0.0" + +micromark-util-html-tag-name@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/micromark-util-html-tag-name/-/micromark-util-html-tag-name-2.0.0.tgz#ae34b01cbe063363847670284c6255bb12138ec4" + integrity sha512-xNn4Pqkj2puRhKdKTm8t1YHC/BAjx6CEwRFXntTaRf/x16aqka6ouVoutm+QdkISTlT7e2zU7U4ZdlDLJd2Mcw== + +micromark-util-normalize-identifier@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/micromark-util-normalize-identifier/-/micromark-util-normalize-identifier-2.0.0.tgz#91f9a4e65fe66cc80c53b35b0254ad67aa431d8b" + integrity sha512-2xhYT0sfo85FMrUPtHcPo2rrp1lwbDEEzpx7jiH2xXJLqBuy4H0GgXk5ToU8IEwoROtXuL8ND0ttVa4rNqYK3w== + dependencies: + micromark-util-symbol "^2.0.0" + +micromark-util-resolve-all@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/micromark-util-resolve-all/-/micromark-util-resolve-all-2.0.0.tgz#189656e7e1a53d0c86a38a652b284a252389f364" + integrity sha512-6KU6qO7DZ7GJkaCgwBNtplXCvGkJToU86ybBAUdavvgsCiG8lSSvYxr9MhwmQ+udpzywHsl4RpGJsYWG1pDOcA== + dependencies: + micromark-util-types "^2.0.0" + +micromark-util-sanitize-uri@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/micromark-util-sanitize-uri/-/micromark-util-sanitize-uri-2.0.0.tgz#ec8fbf0258e9e6d8f13d9e4770f9be64342673de" + integrity sha512-WhYv5UEcZrbAtlsnPuChHUAsu/iBPOVaEVsntLBIdpibO0ddy8OzavZz3iL2xVvBZOpolujSliP65Kq0/7KIYw== + dependencies: + micromark-util-character "^2.0.0" + micromark-util-encode "^2.0.0" + micromark-util-symbol "^2.0.0" + +micromark-util-subtokenize@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/micromark-util-subtokenize/-/micromark-util-subtokenize-2.0.0.tgz#9f412442d77e0c5789ffdf42377fa8a2bcbdf581" + integrity sha512-vc93L1t+gpR3p8jxeVdaYlbV2jTYteDje19rNSS/H5dlhxUYll5Fy6vJ2cDwP8RnsXi818yGty1ayP55y3W6fg== + dependencies: + devlop "^1.0.0" + micromark-util-chunked "^2.0.0" + micromark-util-symbol "^2.0.0" + micromark-util-types "^2.0.0" + +micromark-util-symbol@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/micromark-util-symbol/-/micromark-util-symbol-2.0.0.tgz#12225c8f95edf8b17254e47080ce0862d5db8044" + integrity sha512-8JZt9ElZ5kyTnO94muPxIGS8oyElRJaiJO8EzV6ZSyGQ1Is8xwl4Q45qU5UOg+bGH4AikWziz0iN4sFLWs8PGw== + +micromark-util-types@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/micromark-util-types/-/micromark-util-types-2.0.0.tgz#63b4b7ffeb35d3ecf50d1ca20e68fc7caa36d95e" + integrity sha512-oNh6S2WMHWRZrmutsRmDDfkzKtxF+bc2VxLC9dvtrDIRFln627VsFP6fLMgTryGDljgLPjkrzQSDcPrjPyDJ5w== + +micromark@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/micromark/-/micromark-4.0.0.tgz#84746a249ebd904d9658cfabc1e8e5f32cbc6249" + integrity sha512-o/sd0nMof8kYff+TqcDx3VSrgBTcZpSvYcAHIfHhv5VAuNmisCxjhx6YmxS8PFEpb9z5WKWKPdzf0jM23ro3RQ== + dependencies: + "@types/debug" "^4.0.0" + debug "^4.0.0" + decode-named-character-reference "^1.0.0" + devlop "^1.0.0" + micromark-core-commonmark "^2.0.0" + micromark-factory-space "^2.0.0" + micromark-util-character "^2.0.0" + micromark-util-chunked "^2.0.0" + micromark-util-combine-extensions "^2.0.0" + micromark-util-decode-numeric-character-reference "^2.0.0" + micromark-util-encode "^2.0.0" + micromark-util-normalize-identifier "^2.0.0" + micromark-util-resolve-all "^2.0.0" + micromark-util-sanitize-uri "^2.0.0" + micromark-util-subtokenize "^2.0.0" + micromark-util-symbol "^2.0.0" + micromark-util-types "^2.0.0" + +micromatch@^4.0.2, micromatch@^4.0.4: + version "4.0.5" + resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-4.0.5.tgz#bc8999a7cbbf77cdc89f132f6e467051b49090c6" + integrity sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA== + dependencies: + braces "^3.0.2" + picomatch "^2.3.1" + +mime@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/mime/-/mime-3.0.0.tgz#b374550dca3a0c18443b0c950a6a58f1931cf7a7" + integrity sha512-jSCU7/VB1loIWBZe14aEYHU/+1UMEHoaO7qxCOVJOw9GgH72VAWppxNcjU+x9a2k3GSIBXNKxXQFqRvvZ7vr3A== + +mimic-fn@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-2.1.0.tgz#7ed2c2ccccaf84d3ffcb7a69b57711fc2083401b" + integrity sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg== + +mimic-fn@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-4.0.0.tgz#60a90550d5cb0b239cca65d893b1a53b29871ecc" + integrity sha512-vqiC06CuhBTUdZH+RYl8sFrL096vA45Ok5ISO6sE/Mr1jRbGH4Csnhi8f3wKVl7x8mO4Au7Ir9D3Oyv1VYMFJw== + +mimic-response@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/mimic-response/-/mimic-response-3.1.0.tgz#2d1d59af9c1b129815accc2c46a022a5ce1fa3c9" + integrity sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ== + +minimist@^1.2.0, minimist@^1.2.3: + version "1.2.8" + resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.8.tgz#c1a464e7693302e082a075cee0c057741ac4772c" + integrity sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA== + +mkdirp-classic@^0.5.2, mkdirp-classic@^0.5.3: + version "0.5.3" + resolved "https://registry.yarnpkg.com/mkdirp-classic/-/mkdirp-classic-0.5.3.tgz#fa10c9115cc6d8865be221ba47ee9bed78601113" + integrity sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A== + +ms@2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8" + integrity sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A== + +ms@2.1.2: + version "2.1.2" + resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009" + integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w== + +ms@^2.1.1: + version "2.1.3" + resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.3.tgz#574c8138ce1d2b5861f0b44579dbadd60c6615b2" + integrity sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA== + +muggle-string@^0.3.1: + version "0.3.1" + resolved "https://registry.yarnpkg.com/muggle-string/-/muggle-string-0.3.1.tgz#e524312eb1728c63dd0b2ac49e3282e6ed85963a" + integrity sha512-ckmWDJjphvd/FvZawgygcUeQCxzvohjFO5RxTjj4eq8kw359gFF3E1brjfI+viLMxss5JrHTDRHZvu2/tuy0Qg== + +nanoid@^3.3.7: + version "3.3.7" + resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.3.7.tgz#d0c301a691bc8d54efa0a2226ccf3fe2fd656bd8" + integrity sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g== + +napi-build-utils@^1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/napi-build-utils/-/napi-build-utils-1.0.2.tgz#b1fddc0b2c46e380a0b7a76f984dd47c41a13806" + integrity sha512-ONmRUqK7zj7DWX0D9ADe03wbwOBZxNAfF20PlGfCWQcD3+/MakShIHrMqx9YwPTfxDdF1zLeL+RGZiR9kGMLdg== + +needle@^2.5.2: + version "2.9.1" + resolved "https://registry.yarnpkg.com/needle/-/needle-2.9.1.tgz#22d1dffbe3490c2b83e301f7709b6736cd8f2684" + integrity sha512-6R9fqJ5Zcmf+uYaFgdIHmLwNldn5HbK8L5ybn7Uz+ylX/rnOsSp1AHcvQSrCaFN+qNM1wpymHqD7mVasEOlHGQ== + dependencies: + debug "^3.2.6" + iconv-lite "^0.4.4" + sax "^1.2.4" + +nlcst-to-string@^3.0.0: + version "3.1.1" + resolved "https://registry.yarnpkg.com/nlcst-to-string/-/nlcst-to-string-3.1.1.tgz#83b90f2e1ee2081e14701317efc26d3bbadc806e" + integrity sha512-63mVyqaqt0cmn2VcI2aH6kxe1rLAmSROqHMA0i4qqg1tidkfExgpb0FGMikMCn86mw5dFtBtEANfmSSK7TjNHw== + dependencies: + "@types/nlcst" "^1.0.0" + +node-abi@^3.3.0: + version "3.54.0" + resolved "https://registry.yarnpkg.com/node-abi/-/node-abi-3.54.0.tgz#f6386f7548817acac6434c6cba02999c9aebcc69" + integrity sha512-p7eGEiQil0YUV3ItH4/tBb781L5impVmmx2E9FRKF7d18XXzp4PGT2tdYMFY6wQqgxD0IwNZOiSJ0/K0fSi/OA== + dependencies: + semver "^7.3.5" + +node-addon-api@^6.1.0: + version "6.1.0" + resolved "https://registry.yarnpkg.com/node-addon-api/-/node-addon-api-6.1.0.tgz#ac8470034e58e67d0c6f1204a18ae6995d9c0d76" + integrity sha512-+eawOlIgy680F0kBzPUNFhMZGtJ1YmqM6l4+Crf4IkImjYrO/mqPwRMh352g23uIaQKFItcQ64I7KMaJxHgAVA== + +node-releases@^2.0.14: + version "2.0.14" + resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-2.0.14.tgz#2ffb053bceb8b2be8495ece1ab6ce600c4461b0b" + integrity sha512-y10wOWt8yZpqXmOgRo77WaHEmhYQYGNA6y421PKsKYWEK8aW+cqAphborZDhqfyKrbZEN92CN1X2KbafY2s7Yw== + +normalize-path@^3.0.0, normalize-path@~3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-3.0.0.tgz#0dcd69ff23a1c9b11fd0978316644a0388216a65" + integrity sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA== + +not@^0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/not/-/not-0.1.0.tgz#c9691c1746c55dcfbe54cbd8bd4ff041bc2b519d" + integrity sha512-5PDmaAsVfnWUgTUbJ3ERwn7u79Z0dYxN9ErxCpVJJqe2RK0PJ3z+iFUxuqjwtlDDegXvtWoxD/3Fzxox7tFGWA== + +npm-run-path@^5.1.0: + version "5.2.0" + resolved "https://registry.yarnpkg.com/npm-run-path/-/npm-run-path-5.2.0.tgz#224cdd22c755560253dd71b83a1ef2f758b2e955" + integrity sha512-W4/tgAXFqFA0iL7fk0+uQ3g7wkL8xJmx3XdK0VGb4cHW//eZTtKGvFBBoRKVTpY7n6ze4NL9ly7rgXcHufqXKg== + dependencies: + path-key "^4.0.0" + +nth-check@^2.0.0: + version "2.1.1" + resolved "https://registry.yarnpkg.com/nth-check/-/nth-check-2.1.1.tgz#c9eab428effce36cd6b92c924bdb000ef1f1ed1d" + integrity sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w== + dependencies: + boolbase "^1.0.0" + +once@^1.3.1, once@^1.4.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1" + integrity sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w== + dependencies: + wrappy "1" + +onetime@^5.1.0: + version "5.1.2" + resolved "https://registry.yarnpkg.com/onetime/-/onetime-5.1.2.tgz#d0e96ebb56b07476df1dd9c4806e5237985ca45e" + integrity sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg== + dependencies: + mimic-fn "^2.1.0" + +onetime@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/onetime/-/onetime-6.0.0.tgz#7c24c18ed1fd2e9bca4bd26806a33613c77d34b4" + integrity sha512-1FlR+gjXK7X+AsAHso35MnyN5KqGwJRi/31ft6x0M194ht7S+rWAvd7PHss9xSKMzE0asv1pyIHaJYq+BbacAQ== + dependencies: + mimic-fn "^4.0.0" + +ora@^7.0.1: + version "7.0.1" + resolved "https://registry.yarnpkg.com/ora/-/ora-7.0.1.tgz#cdd530ecd865fe39e451a0e7697865669cb11930" + integrity sha512-0TUxTiFJWv+JnjWm4o9yvuskpEJLXTcng8MJuKd+SzAzp2o+OP3HWqNhB4OdJRt1Vsd9/mR0oyaEYlOnL7XIRw== + dependencies: + chalk "^5.3.0" + cli-cursor "^4.0.0" + cli-spinners "^2.9.0" + is-interactive "^2.0.0" + is-unicode-supported "^1.3.0" + log-symbols "^5.1.0" + stdin-discarder "^0.1.0" + string-width "^6.1.0" + strip-ansi "^7.1.0" + +p-limit@^2.2.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-2.3.0.tgz#3dd33c647a214fdfffd835933eb086da0dc21db1" + integrity sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w== + dependencies: + p-try "^2.0.0" + +p-limit@^3.0.2: + version "3.1.0" + resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-3.1.0.tgz#e1daccbe78d0d1388ca18c64fea38e3e57e3706b" + integrity sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ== + dependencies: + yocto-queue "^0.1.0" + +p-limit@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-5.0.0.tgz#6946d5b7140b649b7a33a027d89b4c625b3a5985" + integrity sha512-/Eaoq+QyLSiXQ4lyYV23f14mZRQcXnxfHrN0vCai+ak9G0pp9iEQukIIZq5NccEvwRB8PUnZT0KsOoDCINS1qQ== + dependencies: + yocto-queue "^1.0.0" + +p-locate@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-4.1.0.tgz#a3428bb7088b3a60292f66919278b7c297ad4f07" + integrity sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A== + dependencies: + p-limit "^2.2.0" + +p-locate@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-5.0.0.tgz#83c8315c6785005e3bd021839411c9e110e6d834" + integrity sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw== + dependencies: + p-limit "^3.0.2" + +p-queue@^8.0.1: + version "8.0.1" + resolved "https://registry.yarnpkg.com/p-queue/-/p-queue-8.0.1.tgz#718b7f83836922ef213ddec263ff4223ce70bef8" + integrity sha512-NXzu9aQJTAzbBqOt2hwsR63ea7yvxJc0PwN/zobNAudYfb1B7R08SzB4TsLeSbUCuG467NhnoT0oO6w1qRO+BA== + dependencies: + eventemitter3 "^5.0.1" + p-timeout "^6.1.2" + +p-timeout@^6.1.2: + version "6.1.2" + resolved "https://registry.yarnpkg.com/p-timeout/-/p-timeout-6.1.2.tgz#22b8d8a78abf5e103030211c5fc6dee1166a6aa5" + integrity sha512-UbD77BuZ9Bc9aABo74gfXhNvzC9Tx7SxtHSh1fxvx3jTLLYvmVhiQZZrJzqqU0jKbN32kb5VOKiLEQI/3bIjgQ== + +p-try@^2.0.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/p-try/-/p-try-2.2.0.tgz#cb2868540e313d61de58fafbe35ce9004d5540e6" + integrity sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ== + +pagefind@^1.0.3: + version "1.0.4" + resolved "https://registry.yarnpkg.com/pagefind/-/pagefind-1.0.4.tgz#ada6a17fefdecfaa654e730544f2d827ee35fcc7" + integrity sha512-oRIizYe+zSI2Jw4zcMU0ebDZm27751hRFiSOBLwc1OIYMrsZKk+3m8p9EVaOmc6zZdtqwwdilNUNxXvBeHcP9w== + optionalDependencies: + "@pagefind/darwin-arm64" "1.0.4" + "@pagefind/darwin-x64" "1.0.4" + "@pagefind/linux-arm64" "1.0.4" + "@pagefind/linux-x64" "1.0.4" + "@pagefind/windows-x64" "1.0.4" + +parse-entities@^4.0.0: + version "4.0.1" + resolved "https://registry.yarnpkg.com/parse-entities/-/parse-entities-4.0.1.tgz#4e2a01111fb1c986549b944af39eeda258fc9e4e" + integrity sha512-SWzvYcSJh4d/SGLIOQfZ/CoNv6BTlI6YEQ7Nj82oDVnRpwe/Z/F1EMx42x3JAOwGBlCjeCH0BRJQbQ/opHL17w== + dependencies: + "@types/unist" "^2.0.0" + character-entities "^2.0.0" + character-entities-legacy "^3.0.0" + character-reference-invalid "^2.0.0" + decode-named-character-reference "^1.0.0" + is-alphanumerical "^2.0.0" + is-decimal "^2.0.0" + is-hexadecimal "^2.0.0" + +parse-latin@^5.0.0: + version "5.0.1" + resolved "https://registry.yarnpkg.com/parse-latin/-/parse-latin-5.0.1.tgz#f3b4fac54d06f6a0501cf8b8ecfafa4cbb4f2f47" + integrity sha512-b/K8ExXaWC9t34kKeDV8kGXBkXZ1HCSAZRYE7HR14eA1GlXX5L8iWhs8USJNhQU9q5ci413jCKF0gOyovvyRBg== + dependencies: + nlcst-to-string "^3.0.0" + unist-util-modify-children "^3.0.0" + unist-util-visit-children "^2.0.0" + +parse5@^6.0.0: + version "6.0.1" + resolved "https://registry.yarnpkg.com/parse5/-/parse5-6.0.1.tgz#e1a1c085c569b3dc08321184f19a39cc27f7c30b" + integrity sha512-Ofn/CTFzRGTTxwpNEs9PP93gXShHcTq255nzRYSKe8AkVpZY7e1fpmTfOyoIvjP5HG7Z2ZM7VS9PPhQGW2pOpw== + +parse5@^7.0.0: + version "7.1.2" + resolved "https://registry.yarnpkg.com/parse5/-/parse5-7.1.2.tgz#0736bebbfd77793823240a23b7fc5e010b7f8e32" + integrity sha512-Czj1WaSVpaoj0wbhMzLmWD69anp2WH7FXMB9n1Sy8/ZFF9jolSQVMu1Ij5WIyGmcBmhk7EOndpO4mIpihVqAXw== + dependencies: + entities "^4.4.0" + +path-browserify@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/path-browserify/-/path-browserify-1.0.1.tgz#d98454a9c3753d5790860f16f68867b9e46be1fd" + integrity sha512-b7uo2UCUOYZcnF/3ID0lulOJi/bafxa1xPe7ZPsammBSpjSWQkjNxlt635YGS2MiR9GjvuXCtz2emr3jbsz98g== + +path-exists@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-4.0.0.tgz#513bdbe2d3b95d7762e8c1137efa195c6c61b5b3" + integrity sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w== + +path-key@^3.1.0: + version "3.1.1" + resolved "https://registry.yarnpkg.com/path-key/-/path-key-3.1.1.tgz#581f6ade658cbba65a0d3380de7753295054f375" + integrity sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q== + +path-key@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/path-key/-/path-key-4.0.0.tgz#295588dc3aee64154f877adb9d780b81c554bf18" + integrity sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ== + +path-parse@^1.0.7: + version "1.0.7" + resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.7.tgz#fbc114b60ca42b30d9daf5858e4bd68bbedb6735" + integrity sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw== + +path-to-regexp@^6.2.1: + version "6.2.1" + resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-6.2.1.tgz#d54934d6798eb9e5ef14e7af7962c945906918e5" + integrity sha512-JLyh7xT1kizaEvcaXOQwOc2/Yhw6KZOvPf1S8401UyLk86CU79LN3vl7ztXGm/pZ+YjoyAJ4rxmHwbkBXJX+yw== + +periscopic@^3.0.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/periscopic/-/periscopic-3.1.0.tgz#7e9037bf51c5855bd33b48928828db4afa79d97a" + integrity sha512-vKiQ8RRtkl9P+r/+oefh25C3fhybptkHKCZSPlcXiJux2tJF55GnEj3BVn4A5gKfq9NWWXXrxkHBwVPUfH0opw== + dependencies: + "@types/estree" "^1.0.0" + estree-walker "^3.0.0" + is-reference "^3.0.0" + +picocolors@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/picocolors/-/picocolors-1.0.0.tgz#cb5bdc74ff3f51892236eaf79d68bc44564ab81c" + integrity sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ== + +picomatch@^2.0.4, picomatch@^2.2.1, picomatch@^2.3.1: + version "2.3.1" + resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.3.1.tgz#3ba3833733646d9d3e4995946c1365a67fb07a42" + integrity sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA== + +pify@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/pify/-/pify-4.0.1.tgz#4b2cd25c50d598735c50292224fd8c6df41e3231" + integrity sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g== + +pkg-dir@^4.2.0: + version "4.2.0" + resolved "https://registry.yarnpkg.com/pkg-dir/-/pkg-dir-4.2.0.tgz#f099133df7ede422e81d1d8448270eeb3e4261f3" + integrity sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ== + dependencies: + find-up "^4.0.0" + +postcss-nested@^6.0.1: + version "6.0.1" + resolved "https://registry.yarnpkg.com/postcss-nested/-/postcss-nested-6.0.1.tgz#f83dc9846ca16d2f4fa864f16e9d9f7d0961662c" + integrity sha512-mEp4xPMi5bSWiMbsgoPfcP74lsWLHkQbZc3sY+jWYd65CUwXrUaTp0fmNpa01ZcETKlIgUdFN/MpS2xZtqL9dQ== + dependencies: + postcss-selector-parser "^6.0.11" + +postcss-selector-parser@^6.0.11: + version "6.0.15" + resolved "https://registry.yarnpkg.com/postcss-selector-parser/-/postcss-selector-parser-6.0.15.tgz#11cc2b21eebc0b99ea374ffb9887174855a01535" + integrity sha512-rEYkQOMUCEMhsKbK66tbEU9QVIxbhN18YiniAwA7XQYTVBqrBy+P2p5JcdqsHgKM2zWylp8d7J6eszocfds5Sw== + dependencies: + cssesc "^3.0.0" + util-deprecate "^1.0.2" + +postcss@^8.4.21, postcss@^8.4.32: + version "8.4.33" + resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.4.33.tgz#1378e859c9f69bf6f638b990a0212f43e2aaa742" + integrity sha512-Kkpbhhdjw2qQs2O2DGX+8m5OVqEcbB9HRBvuYM9pgrjEFUg30A9LmXNlTAUj4S9kgtGyrMbTzVjH7E+s5Re2yg== + dependencies: + nanoid "^3.3.7" + picocolors "^1.0.0" + source-map-js "^1.0.2" + +prebuild-install@^7.1.1: + version "7.1.1" + resolved "https://registry.yarnpkg.com/prebuild-install/-/prebuild-install-7.1.1.tgz#de97d5b34a70a0c81334fd24641f2a1702352e45" + integrity sha512-jAXscXWMcCK8GgCoHOfIr0ODh5ai8mj63L2nWrjuAgXE6tDyYGnx4/8o/rCgU+B4JSyZBKbeZqzhtwtC3ovxjw== + dependencies: + detect-libc "^2.0.0" + expand-template "^2.0.3" + github-from-package "0.0.0" + minimist "^1.2.3" + mkdirp-classic "^0.5.3" + napi-build-utils "^1.0.1" + node-abi "^3.3.0" + pump "^3.0.0" + rc "^1.2.7" + simple-get "^4.0.0" + tar-fs "^2.0.0" + tunnel-agent "^0.6.0" + +preferred-pm@^3.1.2: + version "3.1.2" + resolved "https://registry.yarnpkg.com/preferred-pm/-/preferred-pm-3.1.2.tgz#aedb70550734a574dffcbf2ce82642bd1753bdd6" + integrity sha512-nk7dKrcW8hfCZ4H6klWcdRknBOXWzNQByJ0oJyX97BOupsYD+FzLS4hflgEu/uPUEHZCuRfMxzCBsuWd7OzT8Q== + dependencies: + find-up "^5.0.0" + find-yarn-workspace-root2 "1.2.16" + path-exists "^4.0.0" + which-pm "2.0.0" + +prismjs@^1.29.0: + version "1.29.0" + resolved "https://registry.yarnpkg.com/prismjs/-/prismjs-1.29.0.tgz#f113555a8fa9b57c35e637bba27509dcf802dd12" + integrity sha512-Kx/1w86q/epKcmte75LNrEoT+lX8pBpavuAbvJWRXar7Hz8jrtF+e3vY751p0R8H9HdArwaCTNDDzHg/ScJK1Q== + +probe-image-size@^7.2.3: + version "7.2.3" + resolved "https://registry.yarnpkg.com/probe-image-size/-/probe-image-size-7.2.3.tgz#d49c64be540ec8edea538f6f585f65a9b3ab4309" + integrity sha512-HubhG4Rb2UH8YtV4ba0Vp5bQ7L78RTONYu/ujmCu5nBI8wGv24s4E9xSKBi0N1MowRpxk76pFCpJtW0KPzOK0w== + dependencies: + lodash.merge "^4.6.2" + needle "^2.5.2" + stream-parser "~0.3.1" + +prompts@^2.4.2: + version "2.4.2" + resolved "https://registry.yarnpkg.com/prompts/-/prompts-2.4.2.tgz#7b57e73b3a48029ad10ebd44f74b01722a4cb069" + integrity sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q== + dependencies: + kleur "^3.0.3" + sisteransi "^1.0.5" + +property-information@^6.0.0: + version "6.4.0" + resolved "https://registry.yarnpkg.com/property-information/-/property-information-6.4.0.tgz#6bc4c618b0c2d68b3bb8b552cbb97f8e300a0f82" + integrity sha512-9t5qARVofg2xQqKtytzt+lZ4d1Qvj8t5B8fEwXK6qOfgRLgH/b13QlgEyDh033NOS31nXeFbYv7CLUDG1CeifQ== + +pump@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/pump/-/pump-3.0.0.tgz#b4a2116815bde2f4e1ea602354e8c75565107a64" + integrity sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww== + dependencies: + end-of-stream "^1.1.0" + once "^1.3.1" + +queue-microtask@^1.2.2: + version "1.2.3" + resolved "https://registry.yarnpkg.com/queue-microtask/-/queue-microtask-1.2.3.tgz#4929228bbc724dfac43e0efb058caf7b6cfb6243" + integrity sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A== + +queue-tick@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/queue-tick/-/queue-tick-1.0.1.tgz#f6f07ac82c1fd60f82e098b417a80e52f1f4c142" + integrity sha512-kJt5qhMxoszgU/62PLP1CJytzd2NKetjSRnyuj31fDd3Rlcz3fzlFdFLD1SItunPwyqEOkca6GbV612BWfaBag== + +rc@^1.2.7: + version "1.2.8" + resolved "https://registry.yarnpkg.com/rc/-/rc-1.2.8.tgz#cd924bf5200a075b83c188cd6b9e211b7fc0d3ed" + integrity sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw== + dependencies: + deep-extend "^0.6.0" + ini "~1.3.0" + minimist "^1.2.0" + strip-json-comments "~2.0.1" + +readable-stream@^3.1.1, readable-stream@^3.4.0: + version "3.6.2" + resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-3.6.2.tgz#56a9b36ea965c00c5a93ef31eb111a0f11056967" + integrity sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA== + dependencies: + inherits "^2.0.3" + string_decoder "^1.1.1" + util-deprecate "^1.0.1" + +readdirp@~3.6.0: + version "3.6.0" + resolved "https://registry.yarnpkg.com/readdirp/-/readdirp-3.6.0.tgz#74a370bd857116e245b29cc97340cd431a02a6c7" + integrity sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA== + dependencies: + picomatch "^2.2.1" + +rehype-parse@^9.0.0: + version "9.0.0" + resolved "https://registry.yarnpkg.com/rehype-parse/-/rehype-parse-9.0.0.tgz#3949faeec6f466ec57774215661e0d75469195d9" + integrity sha512-WG7nfvmWWkCR++KEkZevZb/uw41E8TsH4DsY9UxsTbIXCVGbAs4S+r8FrQ+OtH5EEQAs+5UxKC42VinkmpA1Yw== + dependencies: + "@types/hast" "^3.0.0" + hast-util-from-html "^2.0.0" + unified "^11.0.0" + +rehype-raw@^7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/rehype-raw/-/rehype-raw-7.0.0.tgz#59d7348fd5dbef3807bbaa1d443efd2dd85ecee4" + integrity sha512-/aE8hCfKlQeA8LmyeyQvQF3eBiLRGNlfBJEvWH7ivp9sBqs7TNqBL5X3v157rM4IFETqDnIOO+z5M/biZbo9Ww== + dependencies: + "@types/hast" "^3.0.0" + hast-util-raw "^9.0.0" + vfile "^6.0.0" + +rehype-stringify@^10.0.0: + version "10.0.0" + resolved "https://registry.yarnpkg.com/rehype-stringify/-/rehype-stringify-10.0.0.tgz#2031cf6fdd0355393706f0474ec794c75e5492f2" + integrity sha512-1TX1i048LooI9QoecrXy7nGFFbFSufxVRAfc6Y9YMRAi56l+oB0zP51mLSV312uRuvVLPV1opSlJmslozR1XHQ== + dependencies: + "@types/hast" "^3.0.0" + hast-util-to-html "^9.0.0" + unified "^11.0.0" + +rehype@^13.0.1: + version "13.0.1" + resolved "https://registry.yarnpkg.com/rehype/-/rehype-13.0.1.tgz#56384ba83955e2f3aa7eca1975b406c67d9dbd5e" + integrity sha512-AcSLS2mItY+0fYu9xKxOu1LhUZeBZZBx8//5HKzF+0XP+eP8+6a5MXn2+DW2kfXR6Dtp1FEXMVrjyKAcvcU8vg== + dependencies: + "@types/hast" "^3.0.0" + rehype-parse "^9.0.0" + rehype-stringify "^10.0.0" + unified "^11.0.0" + +remark-directive@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/remark-directive/-/remark-directive-3.0.0.tgz#34452d951b37e6207d2e2a4f830dc33442923268" + integrity sha512-l1UyWJ6Eg1VPU7Hm/9tt0zKtReJQNOA4+iDMAxTyZNWnJnFlbS/7zhiel/rogTLQ2vMYwDzSJa4BiVNqGlqIMA== + dependencies: + "@types/mdast" "^4.0.0" + mdast-util-directive "^3.0.0" + micromark-extension-directive "^3.0.0" + unified "^11.0.0" + +remark-expressive-code@^0.31.0: + version "0.31.0" + resolved "https://registry.yarnpkg.com/remark-expressive-code/-/remark-expressive-code-0.31.0.tgz#73f897484db68d174fa8b3621057107ba9132fee" + integrity sha512-ZnKXo9lB0kBUHZIlw2NdqMMgXriVVajEhtQfJ+MWeibMpyM1kuOa28jefNfNFd3FAoNPrc/A3M0fDRkYvWw9Gw== + dependencies: + expressive-code "^0.31.0" + hast-util-to-html "^8.0.4" + unist-util-visit "^4.1.2" + +remark-gfm@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/remark-gfm/-/remark-gfm-4.0.0.tgz#aea777f0744701aa288b67d28c43565c7e8c35de" + integrity sha512-U92vJgBPkbw4Zfu/IiW2oTZLSL3Zpv+uI7My2eq8JxKgqraFdU8YUGicEJCEgSbeaG+QDFqIcwwfMTOEelPxuA== + dependencies: + "@types/mdast" "^4.0.0" + mdast-util-gfm "^3.0.0" + micromark-extension-gfm "^3.0.0" + remark-parse "^11.0.0" + remark-stringify "^11.0.0" + unified "^11.0.0" + +remark-mdx@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/remark-mdx/-/remark-mdx-3.0.0.tgz#146905a3925b078970e05fc89b0e16b9cc3bfddd" + integrity sha512-O7yfjuC6ra3NHPbRVxfflafAj3LTwx3b73aBvkEFU5z4PsD6FD4vrqJAkE5iNGLz71GdjXfgRqm3SQ0h0VuE7g== + dependencies: + mdast-util-mdx "^3.0.0" + micromark-extension-mdxjs "^3.0.0" + +remark-parse@^11.0.0: + version "11.0.0" + resolved "https://registry.yarnpkg.com/remark-parse/-/remark-parse-11.0.0.tgz#aa60743fcb37ebf6b069204eb4da304e40db45a1" + integrity sha512-FCxlKLNGknS5ba/1lmpYijMUzX2esxW5xQqjWxw2eHFfS2MSdaHVINFmhjo+qN1WhZhNimq0dZATN9pH0IDrpA== + dependencies: + "@types/mdast" "^4.0.0" + mdast-util-from-markdown "^2.0.0" + micromark-util-types "^2.0.0" + unified "^11.0.0" + +remark-rehype@^11.0.0: + version "11.1.0" + resolved "https://registry.yarnpkg.com/remark-rehype/-/remark-rehype-11.1.0.tgz#d5f264f42bcbd4d300f030975609d01a1697ccdc" + integrity sha512-z3tJrAs2kIs1AqIIy6pzHmAHlF1hWQ+OdY4/hv+Wxe35EhyLKcajL33iUEn3ScxtFox9nUvRufR/Zre8Q08H/g== + dependencies: + "@types/hast" "^3.0.0" + "@types/mdast" "^4.0.0" + mdast-util-to-hast "^13.0.0" + unified "^11.0.0" + vfile "^6.0.0" + +remark-smartypants@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/remark-smartypants/-/remark-smartypants-2.1.0.tgz#afd26d8ff40def346c6516e38b46994449fb2efe" + integrity sha512-qoF6Vz3BjU2tP6OfZqHOvCU0ACmu/6jhGaINSQRI9mM7wCxNQTKB3JUAN4SVoN2ybElEDTxBIABRep7e569iJw== + dependencies: + retext "^8.1.0" + retext-smartypants "^5.2.0" + unist-util-visit "^5.0.0" + +remark-stringify@^11.0.0: + version "11.0.0" + resolved "https://registry.yarnpkg.com/remark-stringify/-/remark-stringify-11.0.0.tgz#4c5b01dd711c269df1aaae11743eb7e2e7636fd3" + integrity sha512-1OSmLd3awB/t8qdoEOMazZkNsfVTeY4fTsgzcQFdXNq8ToTN4ZGwrMnlda4K6smTFKD+GRV6O48i6Z4iKgPPpw== + dependencies: + "@types/mdast" "^4.0.0" + mdast-util-to-markdown "^2.0.0" + unified "^11.0.0" + +request-light@^0.7.0: + version "0.7.0" + resolved "https://registry.yarnpkg.com/request-light/-/request-light-0.7.0.tgz#885628bb2f8040c26401ebf258ec51c4ae98ac2a" + integrity sha512-lMbBMrDoxgsyO+yB3sDcrDuX85yYt7sS8BfQd11jtbW/z5ZWgLZRcEGLsLoYw7I0WSUGQBs8CC8ScIxkTX1+6Q== + +require-directory@^2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/require-directory/-/require-directory-2.1.1.tgz#8c64ad5fd30dab1c976e2344ffe7f792a6a6df42" + integrity sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q== + +resolve@^1.22.4: + version "1.22.8" + resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.22.8.tgz#b6c87a9f2aa06dfab52e3d70ac8cde321fa5a48d" + integrity sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw== + dependencies: + is-core-module "^2.13.0" + path-parse "^1.0.7" + supports-preserve-symlinks-flag "^1.0.0" + +restore-cursor@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/restore-cursor/-/restore-cursor-4.0.0.tgz#519560a4318975096def6e609d44100edaa4ccb9" + integrity sha512-I9fPXU9geO9bHOt9pHHOhOkYerIMsmVaWB0rA2AI9ERh/+x/i7MV5HKBNrg+ljO5eoPVgCcnFuRjJ9uH6I/3eg== + dependencies: + onetime "^5.1.0" + signal-exit "^3.0.2" + +retext-latin@^3.0.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/retext-latin/-/retext-latin-3.1.0.tgz#72b0176af2c69a373fd0d37eadd3924418bb3a89" + integrity sha512-5MrD1tuebzO8ppsja5eEu+ZbBeUNCjoEarn70tkXOS7Bdsdf6tNahsv2bY0Z8VooFF6cw7/6S+d3yI/TMlMVVQ== + dependencies: + "@types/nlcst" "^1.0.0" + parse-latin "^5.0.0" + unherit "^3.0.0" + unified "^10.0.0" + +retext-smartypants@^5.2.0: + version "5.2.0" + resolved "https://registry.yarnpkg.com/retext-smartypants/-/retext-smartypants-5.2.0.tgz#da9cb79cc60f36aa33a20a462dfc663bec0068b4" + integrity sha512-Do8oM+SsjrbzT2UNIKgheP0hgUQTDDQYyZaIY3kfq0pdFzoPk+ZClYJ+OERNXveog4xf1pZL4PfRxNoVL7a/jw== + dependencies: + "@types/nlcst" "^1.0.0" + nlcst-to-string "^3.0.0" + unified "^10.0.0" + unist-util-visit "^4.0.0" + +retext-stringify@^3.0.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/retext-stringify/-/retext-stringify-3.1.0.tgz#46ed45e077bfc4a8334977f6c2d6611e1d36263a" + integrity sha512-767TLOaoXFXyOnjx/EggXlb37ZD2u4P1n0GJqVdpipqACsQP+20W+BNpMYrlJkq7hxffnFk+jc6mAK9qrbuB8w== + dependencies: + "@types/nlcst" "^1.0.0" + nlcst-to-string "^3.0.0" + unified "^10.0.0" + +retext@^8.1.0: + version "8.1.0" + resolved "https://registry.yarnpkg.com/retext/-/retext-8.1.0.tgz#c43437fb84cd46285ad240a9279142e239bada8d" + integrity sha512-N9/Kq7YTn6ZpzfiGW45WfEGJqFf1IM1q8OsRa1CGzIebCJBNCANDRmOrholiDRGKo/We7ofKR4SEvcGAWEMD3Q== + dependencies: + "@types/nlcst" "^1.0.0" + retext-latin "^3.0.0" + retext-stringify "^3.0.0" + unified "^10.0.0" + +reusify@^1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/reusify/-/reusify-1.0.4.tgz#90da382b1e126efc02146e90845a88db12925d76" + integrity sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw== + +rollup@^4.2.0: + version "4.9.6" + resolved "https://registry.yarnpkg.com/rollup/-/rollup-4.9.6.tgz#4515facb0318ecca254a2ee1315e22e09efc50a0" + integrity sha512-05lzkCS2uASX0CiLFybYfVkwNbKZG5NFQ6Go0VWyogFTXXbR039UVsegViTntkk4OglHBdF54ccApXRRuXRbsg== + dependencies: + "@types/estree" "1.0.5" + optionalDependencies: + "@rollup/rollup-android-arm-eabi" "4.9.6" + "@rollup/rollup-android-arm64" "4.9.6" + "@rollup/rollup-darwin-arm64" "4.9.6" + "@rollup/rollup-darwin-x64" "4.9.6" + "@rollup/rollup-linux-arm-gnueabihf" "4.9.6" + "@rollup/rollup-linux-arm64-gnu" "4.9.6" + "@rollup/rollup-linux-arm64-musl" "4.9.6" + "@rollup/rollup-linux-riscv64-gnu" "4.9.6" + "@rollup/rollup-linux-x64-gnu" "4.9.6" + "@rollup/rollup-linux-x64-musl" "4.9.6" + "@rollup/rollup-win32-arm64-msvc" "4.9.6" + "@rollup/rollup-win32-ia32-msvc" "4.9.6" + "@rollup/rollup-win32-x64-msvc" "4.9.6" + fsevents "~2.3.2" + +run-parallel@^1.1.9: + version "1.2.0" + resolved "https://registry.yarnpkg.com/run-parallel/-/run-parallel-1.2.0.tgz#66d1368da7bdf921eb9d95bd1a9229e7f21a43ee" + integrity sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA== + dependencies: + queue-microtask "^1.2.2" + +safe-buffer@^5.0.1, safe-buffer@~5.2.0: + version "5.2.1" + resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.1.tgz#1eaf9fa9bdb1fdd4ec75f58f9cdb4e6b7827eec6" + integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ== + +"safer-buffer@>= 2.1.2 < 3": + version "2.1.2" + resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a" + integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg== + +sax@^1.2.4: + version "1.3.0" + resolved "https://registry.yarnpkg.com/sax/-/sax-1.3.0.tgz#a5dbe77db3be05c9d1ee7785dbd3ea9de51593d0" + integrity sha512-0s+oAmw9zLl1V1cS9BtZN7JAd0cW5e0QH4W3LWEK6a4LaLEA2OTpGYWDY+6XasBLtz6wkm3u1xRw95mRuJ59WA== + +section-matter@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/section-matter/-/section-matter-1.0.0.tgz#e9041953506780ec01d59f292a19c7b850b84167" + integrity sha512-vfD3pmTzGpufjScBh50YHKzEu2lxBWhVEHsNGoEXmCmn2hKGfeNLYMzCJpe8cD7gqX7TJluOVpBkAequ6dgMmA== + dependencies: + extend-shallow "^2.0.1" + kind-of "^6.0.0" + +semver@^6.3.1: + version "6.3.1" + resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.1.tgz#556d2ef8689146e46dcea4bfdd095f3434dffcb4" + integrity sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA== + +semver@^7.3.5, semver@^7.3.8, semver@^7.5.4: + version "7.5.4" + resolved "https://registry.yarnpkg.com/semver/-/semver-7.5.4.tgz#483986ec4ed38e1c6c48c34894a9182dbff68a6e" + integrity sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA== + dependencies: + lru-cache "^6.0.0" + +server-destroy@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/server-destroy/-/server-destroy-1.0.1.tgz#f13bf928e42b9c3e79383e61cc3998b5d14e6cdd" + integrity sha512-rb+9B5YBIEzYcD6x2VKidaa+cqYBJQKnU4oe4E3ANwRRN56yk/ua1YCJT1n21NTS8w6CcOclAKNP3PhdCXKYtQ== + +sharp@^0.32.5, sharp@^0.32.6: + version "0.32.6" + resolved "https://registry.yarnpkg.com/sharp/-/sharp-0.32.6.tgz#6ad30c0b7cd910df65d5f355f774aa4fce45732a" + integrity sha512-KyLTWwgcR9Oe4d9HwCwNM2l7+J0dUQwn/yf7S0EnTtb0eVS4RxO0eUSvxPtzT4F3SY+C4K6fqdv/DO27sJ/v/w== + dependencies: + color "^4.2.3" + detect-libc "^2.0.2" + node-addon-api "^6.1.0" + prebuild-install "^7.1.1" + semver "^7.5.4" + simple-get "^4.0.1" + tar-fs "^3.0.4" + tunnel-agent "^0.6.0" + +shebang-command@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/shebang-command/-/shebang-command-2.0.0.tgz#ccd0af4f8835fbdc265b82461aaf0c36663f34ea" + integrity sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA== + dependencies: + shebang-regex "^3.0.0" + +shebang-regex@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/shebang-regex/-/shebang-regex-3.0.0.tgz#ae16f1644d873ecad843b0307b143362d4c42172" + integrity sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A== + +shikiji-core@0.9.19: + version "0.9.19" + resolved "https://registry.yarnpkg.com/shikiji-core/-/shikiji-core-0.9.19.tgz#227975e998eb2a579cf83de30977762be3802507" + integrity sha512-AFJu/vcNT21t0e6YrfadZ+9q86gvPum6iywRyt1OtIPjPFe25RQnYJyxHQPMLKCCWA992TPxmEmbNcOZCAJclw== + +shikiji@^0.8.0: + version "0.8.7" + resolved "https://registry.yarnpkg.com/shikiji/-/shikiji-0.8.7.tgz#556647c49546ba14ca6fe32904045ebcc3ae58e2" + integrity sha512-j5usxwI0yHkDTHOuhuSJl9+wT5CNYeYO82dJMSJBlJ/NYT5SIebGcPoL6y9QOyH15wGrJC4LOP2nz5k8mUDGRQ== + dependencies: + hast-util-to-html "^9.0.0" + +shikiji@^0.9.18: + version "0.9.19" + resolved "https://registry.yarnpkg.com/shikiji/-/shikiji-0.9.19.tgz#351a32b291a04cf9a6b69933f8044fe135b70f6f" + integrity sha512-Kw2NHWktdcdypCj1GkKpXH4o6Vxz8B8TykPlPuLHOGSV8VkhoCLcFOH4k19K4LXAQYRQmxg+0X/eM+m2sLhAkg== + dependencies: + shikiji-core "0.9.19" + +signal-exit@^3.0.2: + version "3.0.7" + resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.7.tgz#a9a1767f8af84155114eaabd73f99273c8f59ad9" + integrity sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ== + +signal-exit@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-4.1.0.tgz#952188c1cbd546070e2dd20d0f41c0ae0530cb04" + integrity sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw== + +simple-concat@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/simple-concat/-/simple-concat-1.0.1.tgz#f46976082ba35c2263f1c8ab5edfe26c41c9552f" + integrity sha512-cSFtAPtRhljv69IK0hTVZQ+OfE9nePi/rtJmw5UjHeVyVroEqJXP1sFztKUy1qU+xvz3u/sfYJLa947b7nAN2Q== + +simple-get@^4.0.0, simple-get@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/simple-get/-/simple-get-4.0.1.tgz#4a39db549287c979d352112fa03fd99fd6bc3543" + integrity sha512-brv7p5WgH0jmQJr1ZDDfKDOSeWWg+OVypG99A/5vYGPqJ6pxiaHLy8nxtFjBA7oMa01ebA9gfh1uMCFqOuXxvA== + dependencies: + decompress-response "^6.0.0" + once "^1.3.1" + simple-concat "^1.0.0" + +simple-swizzle@^0.2.2: + version "0.2.2" + resolved "https://registry.yarnpkg.com/simple-swizzle/-/simple-swizzle-0.2.2.tgz#a4da6b635ffcccca33f70d17cb92592de95e557a" + integrity sha512-JA//kQgZtbuY83m+xT+tXJkmJncGMTFT+C+g2h2R9uxkYIrE2yy9sgmcLhCnw57/WSD+Eh3J97FPEDFnbXnDUg== + dependencies: + is-arrayish "^0.3.1" + +sisteransi@^1.0.5: + version "1.0.5" + resolved "https://registry.yarnpkg.com/sisteransi/-/sisteransi-1.0.5.tgz#134d681297756437cc05ca01370d3a7a571075ed" + integrity sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg== + +sitemap@^7.1.1: + version "7.1.1" + resolved "https://registry.yarnpkg.com/sitemap/-/sitemap-7.1.1.tgz#eeed9ad6d95499161a3eadc60f8c6dce4bea2bef" + integrity sha512-mK3aFtjz4VdJN0igpIJrinf3EO8U8mxOPsTBzSsy06UtjZQJ3YY3o3Xa7zSc5nMqcMrRwlChHZ18Kxg0caiPBg== + dependencies: + "@types/node" "^17.0.5" + "@types/sax" "^1.2.1" + arg "^5.0.0" + sax "^1.2.4" + +source-map-js@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/source-map-js/-/source-map-js-1.0.2.tgz#adbc361d9c62df380125e7f161f71c826f1e490c" + integrity sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw== + +source-map@^0.7.0, source-map@^0.7.4: + version "0.7.4" + resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.7.4.tgz#a9bbe705c9d8846f4e08ff6765acf0f1b0898656" + integrity sha512-l3BikUxvPOcn5E74dZiq5BGsTb5yEwhaTSzccU6t4sDOH8NWJCstKO5QT2CvtFoK6F0saL7p9xHAqHOlCPJygA== + +space-separated-tokens@^2.0.0: + version "2.0.2" + resolved "https://registry.yarnpkg.com/space-separated-tokens/-/space-separated-tokens-2.0.2.tgz#1ecd9d2350a3844572c3f4a312bceb018348859f" + integrity sha512-PEGlAwrG8yXGXRjW32fGbg66JAlOAwbObuqVoJpv/mRgoWDQfgH1wDPvtzWyUSNAXBGSk8h755YDbbcEy3SH2Q== + +sprintf-js@~1.0.2: + version "1.0.3" + resolved "https://registry.yarnpkg.com/sprintf-js/-/sprintf-js-1.0.3.tgz#04e6926f662895354f3dd015203633b857297e2c" + integrity sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g== + +stdin-discarder@^0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/stdin-discarder/-/stdin-discarder-0.1.0.tgz#22b3e400393a8e28ebf53f9958f3880622efde21" + integrity sha512-xhV7w8S+bUwlPTb4bAOUQhv8/cSS5offJuX8GQGq32ONF0ZtDWKfkdomM3HMRA+LhX6um/FZ0COqlwsjD53LeQ== + dependencies: + bl "^5.0.0" + +stream-parser@~0.3.1: + version "0.3.1" + resolved "https://registry.yarnpkg.com/stream-parser/-/stream-parser-0.3.1.tgz#1618548694420021a1182ff0af1911c129761773" + integrity sha512-bJ/HgKq41nlKvlhccD5kaCr/P+Hu0wPNKPJOH7en+YrJu/9EgqUF+88w5Jb6KNcjOFMhfX4B2asfeAtIGuHObQ== + dependencies: + debug "2" + +streamx@^2.15.0: + version "2.15.6" + resolved "https://registry.yarnpkg.com/streamx/-/streamx-2.15.6.tgz#28bf36997ebc7bf6c08f9eba958735231b833887" + integrity sha512-q+vQL4AAz+FdfT137VF69Cc/APqUbxy+MDOImRrMvchJpigHj9GksgDU2LYbO9rx7RX6osWgxJB2WxhYv4SZAw== + dependencies: + fast-fifo "^1.1.0" + queue-tick "^1.0.1" + +string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.3: + version "4.2.3" + resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" + integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== + dependencies: + emoji-regex "^8.0.0" + is-fullwidth-code-point "^3.0.0" + strip-ansi "^6.0.1" + +string-width@^5.0.1, string-width@^5.1.2: + version "5.1.2" + resolved "https://registry.yarnpkg.com/string-width/-/string-width-5.1.2.tgz#14f8daec6d81e7221d2a357e668cab73bdbca794" + integrity sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA== + dependencies: + eastasianwidth "^0.2.0" + emoji-regex "^9.2.2" + strip-ansi "^7.0.1" + +string-width@^6.1.0: + version "6.1.0" + resolved "https://registry.yarnpkg.com/string-width/-/string-width-6.1.0.tgz#96488d6ed23f9ad5d82d13522af9e4c4c3fd7518" + integrity sha512-k01swCJAgQmuADB0YIc+7TuatfNvTBVOoaUWJjTB9R4VJzR5vNWzf5t42ESVZFPS8xTySF7CAdV4t/aaIm3UnQ== + dependencies: + eastasianwidth "^0.2.0" + emoji-regex "^10.2.1" + strip-ansi "^7.0.1" + +string-width@^7.0.0: + version "7.1.0" + resolved "https://registry.yarnpkg.com/string-width/-/string-width-7.1.0.tgz#d994252935224729ea3719c49f7206dc9c46550a" + integrity sha512-SEIJCWiX7Kg4c129n48aDRwLbFb2LJmXXFrWBG4NGaRtMQ3myKPKbwrD1BKqQn74oCoNMBVrfDEr5M9YxCsrkw== + dependencies: + emoji-regex "^10.3.0" + get-east-asian-width "^1.0.0" + strip-ansi "^7.1.0" + +string_decoder@^1.1.1: + version "1.3.0" + resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.3.0.tgz#42f114594a46cf1a8e30b0a84f56c78c3edac21e" + integrity sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA== + dependencies: + safe-buffer "~5.2.0" + +stringify-entities@^4.0.0: + version "4.0.3" + resolved "https://registry.yarnpkg.com/stringify-entities/-/stringify-entities-4.0.3.tgz#cfabd7039d22ad30f3cc435b0ca2c1574fc88ef8" + integrity sha512-BP9nNHMhhfcMbiuQKCqMjhDP5yBCAxsPu4pHFFzJ6Alo9dZgY4VLDPutXqIjpRiMoKdp7Av85Gr73Q5uH9k7+g== + dependencies: + character-entities-html4 "^2.0.0" + character-entities-legacy "^3.0.0" + +strip-ansi@^6.0.0, strip-ansi@^6.0.1: + version "6.0.1" + resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" + integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== + dependencies: + ansi-regex "^5.0.1" + +strip-ansi@^7.0.1, strip-ansi@^7.1.0: + version "7.1.0" + resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-7.1.0.tgz#d5b6568ca689d8561370b0707685d22434faff45" + integrity sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ== + dependencies: + ansi-regex "^6.0.1" + +strip-bom-string@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/strip-bom-string/-/strip-bom-string-1.0.0.tgz#e5211e9224369fbb81d633a2f00044dc8cedad92" + integrity sha512-uCC2VHvQRYu+lMh4My/sFNmF2klFymLX1wHJeXnbEJERpV/ZsVuonzerjfrGpIGF7LBVa1O7i9kjiWvJiFck8g== + +strip-bom@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/strip-bom/-/strip-bom-3.0.0.tgz#2334c18e9c759f7bdd56fdef7e9ae3d588e68ed3" + integrity sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA== + +strip-final-newline@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/strip-final-newline/-/strip-final-newline-3.0.0.tgz#52894c313fbff318835280aed60ff71ebf12b8fd" + integrity sha512-dOESqjYr96iWYylGObzd39EuNTa5VJxyvVAEm5Jnh7KGo75V43Hk1odPQkNDyXNmUR6k+gEiDVXnjB8HJ3crXw== + +strip-json-comments@~2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-2.0.1.tgz#3c531942e908c2697c0ec344858c286c7ca0a60a" + integrity sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ== + +style-to-object@^0.4.0: + version "0.4.4" + resolved "https://registry.yarnpkg.com/style-to-object/-/style-to-object-0.4.4.tgz#266e3dfd56391a7eefb7770423612d043c3f33ec" + integrity sha512-HYNoHZa2GorYNyqiCaBgsxvcJIn7OHq6inEga+E6Ke3m5JkoqpQbnFssk4jwe+K7AhGa2fcha4wSOf1Kn01dMg== + dependencies: + inline-style-parser "0.1.1" + +style-to-object@^1.0.0: + version "1.0.5" + resolved "https://registry.yarnpkg.com/style-to-object/-/style-to-object-1.0.5.tgz#5e918349bc3a39eee3a804497d97fcbbf2f0d7c0" + integrity sha512-rDRwHtoDD3UMMrmZ6BzOW0naTjMsVZLIjsGleSKS/0Oz+cgCfAPRspaqJuE8rDzpKha/nEvnM0IF4seEAZUTKQ== + dependencies: + inline-style-parser "0.2.2" + +supports-color@^5.3.0: + version "5.5.0" + resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-5.5.0.tgz#e2e69a44ac8772f78a1ec0b35b689df6530efc8f" + integrity sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow== + dependencies: + has-flag "^3.0.0" + +supports-preserve-symlinks-flag@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz#6eda4bd344a3c94aea376d4cc31bc77311039e09" + integrity sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w== + +tar-fs@^2.0.0: + version "2.1.1" + resolved "https://registry.yarnpkg.com/tar-fs/-/tar-fs-2.1.1.tgz#489a15ab85f1f0befabb370b7de4f9eb5cbe8784" + integrity sha512-V0r2Y9scmbDRLCNex/+hYzvp/zyYjvFbHPNgVTKfQvVrb6guiE/fxP+XblDNR011utopbkex2nM4dHNV6GDsng== + dependencies: + chownr "^1.1.1" + mkdirp-classic "^0.5.2" + pump "^3.0.0" + tar-stream "^2.1.4" + +tar-fs@^3.0.4: + version "3.0.4" + resolved "https://registry.yarnpkg.com/tar-fs/-/tar-fs-3.0.4.tgz#a21dc60a2d5d9f55e0089ccd78124f1d3771dbbf" + integrity sha512-5AFQU8b9qLfZCX9zp2duONhPmZv0hGYiBPJsyUdqMjzq/mqVpy/rEUSeHk1+YitmxugaptgBh5oDGU3VsAJq4w== + dependencies: + mkdirp-classic "^0.5.2" + pump "^3.0.0" + tar-stream "^3.1.5" + +tar-stream@^2.1.4: + version "2.2.0" + resolved "https://registry.yarnpkg.com/tar-stream/-/tar-stream-2.2.0.tgz#acad84c284136b060dc3faa64474aa9aebd77287" + integrity sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ== + dependencies: + bl "^4.0.3" + end-of-stream "^1.4.1" + fs-constants "^1.0.0" + inherits "^2.0.3" + readable-stream "^3.1.1" + +tar-stream@^3.1.5: + version "3.1.7" + resolved "https://registry.yarnpkg.com/tar-stream/-/tar-stream-3.1.7.tgz#24b3fb5eabada19fe7338ed6d26e5f7c482e792b" + integrity sha512-qJj60CXt7IU1Ffyc3NJMjh6EkuCFej46zUqJ4J7pqYlThyd9bO0XBTmcOIhSzZJVWfsLks0+nle/j538YAW9RQ== + dependencies: + b4a "^1.6.4" + fast-fifo "^1.2.0" + streamx "^2.15.0" + +to-fast-properties@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/to-fast-properties/-/to-fast-properties-2.0.0.tgz#dc5e698cbd079265bc73e0377681a4e4e83f616e" + integrity sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog== + +to-regex-range@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/to-regex-range/-/to-regex-range-5.0.1.tgz#1648c44aae7c8d988a326018ed72f5b4dd0392e4" + integrity sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ== + dependencies: + is-number "^7.0.0" + +trim-lines@^3.0.0: + version "3.0.1" + resolved "https://registry.yarnpkg.com/trim-lines/-/trim-lines-3.0.1.tgz#d802e332a07df861c48802c04321017b1bd87338" + integrity sha512-kRj8B+YHZCc9kQYdWfJB2/oUl9rA99qbowYYBtr4ui4mZyAQ2JpvVBd/6U2YloATfqBhBTSMhTpgBHtU0Mf3Rg== + +trough@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/trough/-/trough-2.1.0.tgz#0f7b511a4fde65a46f18477ab38849b22c554876" + integrity sha512-AqTiAOLcj85xS7vQ8QkAV41hPDIJ71XJB4RCUrzo/1GM2CQwhkJGaf9Hgr7BOugMRpgGUrqRg/DrBDl4H40+8g== + +tsconfck@^3.0.0: + version "3.0.1" + resolved "https://registry.yarnpkg.com/tsconfck/-/tsconfck-3.0.1.tgz#803ca0ed8f2f2075639e4061238f04b99ba85e85" + integrity sha512-7ppiBlF3UEddCLeI1JRx5m2Ryq+xk4JrZuq4EuYXykipebaq1dV0Fhgr1hb7CkmHt32QSgOZlcqVLEtHBG4/mg== + +tunnel-agent@^0.6.0: + version "0.6.0" + resolved "https://registry.yarnpkg.com/tunnel-agent/-/tunnel-agent-0.6.0.tgz#27a5dea06b36b04a0a9966774b290868f0fc40fd" + integrity sha512-McnNiV1l8RYeY8tBgEpuodCC1mLUdbSN+CYBL7kJsJNInOP8UjDDEwdk6Mw60vdLLrr5NHKZhMAOSrR2NZuQ+w== + dependencies: + safe-buffer "^5.0.1" + +type-fest@^2.13.0: + version "2.19.0" + resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-2.19.0.tgz#88068015bb33036a598b952e55e9311a60fd3a9b" + integrity sha512-RAH822pAdBgcNMAfWnCBU3CFZcfZ/i1eZjwFU/dsLKumyuuP3niueg2UAukXYF0E2AAoc82ZSSf9J0WQBinzHA== + +typesafe-path@^0.2.2: + version "0.2.2" + resolved "https://registry.yarnpkg.com/typesafe-path/-/typesafe-path-0.2.2.tgz#91a436681b2f514badb114061b6a5e5c2b8943b1" + integrity sha512-OJabfkAg1WLZSqJAJ0Z6Sdt3utnbzr/jh+NAHoyWHJe8CMSy79Gm085094M9nvTPy22KzTVn5Zq5mbapCI/hPA== + +typescript-auto-import-cache@^0.3.0: + version "0.3.2" + resolved "https://registry.yarnpkg.com/typescript-auto-import-cache/-/typescript-auto-import-cache-0.3.2.tgz#89b1ab7cb6786b8e18dda735a30ec1e7d48a2679" + integrity sha512-+laqe5SFL1vN62FPOOJSUDTZxtgsoOXjneYOXIpx5rQ4UMiN89NAtJLpqLqyebv9fgQ/IMeeTX+mQyRnwvJzvg== + dependencies: + semver "^7.3.8" + +typescript@^5.3.3: + version "5.3.3" + resolved "https://registry.yarnpkg.com/typescript/-/typescript-5.3.3.tgz#b3ce6ba258e72e6305ba66f5c9b452aaee3ffe37" + integrity sha512-pXWcraxM0uxAS+tN0AG/BF2TyqmHO014Z070UsJ+pFvYuRSq8KH8DmWpnbXe0pEPDHXZV3FcAbJkijJ5oNEnWw== + +undici-types@~5.26.4: + version "5.26.5" + resolved "https://registry.yarnpkg.com/undici-types/-/undici-types-5.26.5.tgz#bcd539893d00b56e964fd2657a4866b221a65617" + integrity sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA== + +unherit@^3.0.0: + version "3.0.1" + resolved "https://registry.yarnpkg.com/unherit/-/unherit-3.0.1.tgz#65b98bb7cb58cee755d7ec699a49e9e8ff172e23" + integrity sha512-akOOQ/Yln8a2sgcLj4U0Jmx0R5jpIg2IUyRrWOzmEbjBtGzBdHtSeFKgoEcoH4KYIG/Pb8GQ/BwtYm0GCq1Sqg== + +unified@^10.0.0: + version "10.1.2" + resolved "https://registry.yarnpkg.com/unified/-/unified-10.1.2.tgz#b1d64e55dafe1f0b98bb6c719881103ecf6c86df" + integrity sha512-pUSWAi/RAnVy1Pif2kAoeWNBa3JVrx0MId2LASj8G+7AiHWoKZNTomq6LG326T68U7/e263X6fTdcXIy7XnF7Q== + dependencies: + "@types/unist" "^2.0.0" + bail "^2.0.0" + extend "^3.0.0" + is-buffer "^2.0.0" + is-plain-obj "^4.0.0" + trough "^2.0.0" + vfile "^5.0.0" + +unified@^11.0.0, unified@^11.0.4: + version "11.0.4" + resolved "https://registry.yarnpkg.com/unified/-/unified-11.0.4.tgz#f4be0ac0fe4c88cb873687c07c64c49ed5969015" + integrity sha512-apMPnyLjAX+ty4OrNap7yumyVAMlKx5IWU2wlzzUdYJO9A8f1p9m/gywF/GM2ZDFcjQPrx59Mc90KwmxsoklxQ== + dependencies: + "@types/unist" "^3.0.0" + bail "^2.0.0" + devlop "^1.0.0" + extend "^3.0.0" + is-plain-obj "^4.0.0" + trough "^2.0.0" + vfile "^6.0.0" + +unist-util-is@^5.0.0: + version "5.2.1" + resolved "https://registry.yarnpkg.com/unist-util-is/-/unist-util-is-5.2.1.tgz#b74960e145c18dcb6226bc57933597f5486deae9" + integrity sha512-u9njyyfEh43npf1M+yGKDGVPbY/JWEemg5nH05ncKPfi+kBbKBJoTdsogMu33uhytuLlv9y0O7GH7fEdwLdLQw== + dependencies: + "@types/unist" "^2.0.0" + +unist-util-is@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/unist-util-is/-/unist-util-is-6.0.0.tgz#b775956486aff107a9ded971d996c173374be424" + integrity sha512-2qCTHimwdxLfz+YzdGfkqNlH0tLi9xjTnHddPmJwtIG9MGsdbutfTc4P+haPD7l7Cjxf/WZj+we5qfVPvvxfYw== + dependencies: + "@types/unist" "^3.0.0" + +unist-util-modify-children@^3.0.0: + version "3.1.1" + resolved "https://registry.yarnpkg.com/unist-util-modify-children/-/unist-util-modify-children-3.1.1.tgz#c4018b86441aa3b54b3edff1151d0aa062384c82" + integrity sha512-yXi4Lm+TG5VG+qvokP6tpnk+r1EPwyYL04JWDxLvgvPV40jANh7nm3udk65OOWquvbMDe+PL9+LmkxDpTv/7BA== + dependencies: + "@types/unist" "^2.0.0" + array-iterate "^2.0.0" + +unist-util-position-from-estree@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/unist-util-position-from-estree/-/unist-util-position-from-estree-2.0.0.tgz#d94da4df596529d1faa3de506202f0c9a23f2200" + integrity sha512-KaFVRjoqLyF6YXCbVLNad/eS4+OfPQQn2yOd7zF/h5T/CSL2v8NpN6a5TPvtbXthAGw5nG+PuTtq+DdIZr+cRQ== + dependencies: + "@types/unist" "^3.0.0" + +unist-util-position@^4.0.0: + version "4.0.4" + resolved "https://registry.yarnpkg.com/unist-util-position/-/unist-util-position-4.0.4.tgz#93f6d8c7d6b373d9b825844645877c127455f037" + integrity sha512-kUBE91efOWfIVBo8xzh/uZQ7p9ffYRtUbMRZBNFYwf0RK8koUMx6dGUfwylLOKmaT2cs4wSW96QoYUSXAyEtpg== + dependencies: + "@types/unist" "^2.0.0" + +unist-util-position@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/unist-util-position/-/unist-util-position-5.0.0.tgz#678f20ab5ca1207a97d7ea8a388373c9cf896be4" + integrity sha512-fucsC7HjXvkB5R3kTCO7kUjRdrS0BJt3M/FPxmHMBOm8JQi2BsHAHFsy27E0EolP8rp0NzXsJ+jNPyDWvOJZPA== + dependencies: + "@types/unist" "^3.0.0" + +unist-util-remove-position@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/unist-util-remove-position/-/unist-util-remove-position-5.0.0.tgz#fea68a25658409c9460408bc6b4991b965b52163" + integrity sha512-Hp5Kh3wLxv0PHj9m2yZhhLt58KzPtEYKQQ4yxfYFEO7EvHwzyDYnduhHnY1mDxoqr7VUwVuHXk9RXKIiYS1N8Q== + dependencies: + "@types/unist" "^3.0.0" + unist-util-visit "^5.0.0" + +unist-util-remove@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/unist-util-remove/-/unist-util-remove-4.0.0.tgz#94b7d6bbd24e42d2f841e947ed087be5c82b222e" + integrity sha512-b4gokeGId57UVRX/eVKej5gXqGlc9+trkORhFJpu9raqZkZhU0zm8Doi05+HaiBsMEIJowL+2WtQ5ItjsngPXg== + dependencies: + "@types/unist" "^3.0.0" + unist-util-is "^6.0.0" + unist-util-visit-parents "^6.0.0" + +unist-util-stringify-position@^3.0.0: + version "3.0.3" + resolved "https://registry.yarnpkg.com/unist-util-stringify-position/-/unist-util-stringify-position-3.0.3.tgz#03ad3348210c2d930772d64b489580c13a7db39d" + integrity sha512-k5GzIBZ/QatR8N5X2y+drfpWG8IDBzdnVj6OInRNWm1oXrzydiaAT2OQiA8DPRRZyAKb9b6I2a6PxYklZD0gKg== + dependencies: + "@types/unist" "^2.0.0" + +unist-util-stringify-position@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/unist-util-stringify-position/-/unist-util-stringify-position-4.0.0.tgz#449c6e21a880e0855bf5aabadeb3a740314abac2" + integrity sha512-0ASV06AAoKCDkS2+xw5RXJywruurpbC4JZSm7nr7MOt1ojAzvyyaO+UxZf18j8FCF6kmzCZKcAgN/yu2gm2XgQ== + dependencies: + "@types/unist" "^3.0.0" + +unist-util-visit-children@^2.0.0: + version "2.0.2" + resolved "https://registry.yarnpkg.com/unist-util-visit-children/-/unist-util-visit-children-2.0.2.tgz#0f00a5caff567074568da2d89c54b5ee4a8c5440" + integrity sha512-+LWpMFqyUwLGpsQxpumsQ9o9DG2VGLFrpz+rpVXYIEdPy57GSy5HioC0g3bg/8WP9oCLlapQtklOzQ8uLS496Q== + dependencies: + "@types/unist" "^2.0.0" + +unist-util-visit-parents@^5.1.1, unist-util-visit-parents@^5.1.3: + version "5.1.3" + resolved "https://registry.yarnpkg.com/unist-util-visit-parents/-/unist-util-visit-parents-5.1.3.tgz#b4520811b0ca34285633785045df7a8d6776cfeb" + integrity sha512-x6+y8g7wWMyQhL1iZfhIPhDAs7Xwbn9nRosDXl7qoPTSCy0yNxnKc+hWokFifWQIDGi154rdUqKvbCa4+1kLhg== + dependencies: + "@types/unist" "^2.0.0" + unist-util-is "^5.0.0" + +unist-util-visit-parents@^6.0.0: + version "6.0.1" + resolved "https://registry.yarnpkg.com/unist-util-visit-parents/-/unist-util-visit-parents-6.0.1.tgz#4d5f85755c3b8f0dc69e21eca5d6d82d22162815" + integrity sha512-L/PqWzfTP9lzzEa6CKs0k2nARxTdZduw3zyh8d2NVBnsyvHjSX4TWse388YrrQKbvI8w20fGjGlhgT96WwKykw== + dependencies: + "@types/unist" "^3.0.0" + unist-util-is "^6.0.0" + +unist-util-visit@^4.0.0, unist-util-visit@^4.1.2: + version "4.1.2" + resolved "https://registry.yarnpkg.com/unist-util-visit/-/unist-util-visit-4.1.2.tgz#125a42d1eb876283715a3cb5cceaa531828c72e2" + integrity sha512-MSd8OUGISqHdVvfY9TPhyK2VdUrPgxkUtWSuMHF6XAAFuL4LokseigBnZtPnJMu+FbynTkFNnFlyjxpVKujMRg== + dependencies: + "@types/unist" "^2.0.0" + unist-util-is "^5.0.0" + unist-util-visit-parents "^5.1.1" + +unist-util-visit@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/unist-util-visit/-/unist-util-visit-5.0.0.tgz#a7de1f31f72ffd3519ea71814cccf5fd6a9217d6" + integrity sha512-MR04uvD+07cwl/yhVuVWAtw+3GOR/knlL55Nd/wAdblk27GCVt3lqpTivy/tkJcZoNPzTwS1Y+KMojlLDhoTzg== + dependencies: + "@types/unist" "^3.0.0" + unist-util-is "^6.0.0" + unist-util-visit-parents "^6.0.0" + +update-browserslist-db@^1.0.13: + version "1.0.13" + resolved "https://registry.yarnpkg.com/update-browserslist-db/-/update-browserslist-db-1.0.13.tgz#3c5e4f5c083661bd38ef64b6328c26ed6c8248c4" + integrity sha512-xebP81SNcPuNpPP3uzeW1NYXxI3rxyJzF3pD6sH4jE7o/IX+WtSpwnVU+qIsDPyk0d3hmFQ7mjqc6AtV604hbg== + dependencies: + escalade "^3.1.1" + picocolors "^1.0.0" + +util-deprecate@^1.0.1, util-deprecate@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf" + integrity sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw== + +vfile-location@^4.0.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/vfile-location/-/vfile-location-4.1.0.tgz#69df82fb9ef0a38d0d02b90dd84620e120050dd0" + integrity sha512-YF23YMyASIIJXpktBa4vIGLJ5Gs88UB/XePgqPmTa7cDA+JeO3yclbpheQYCHjVHBn/yePzrXuygIL+xbvRYHw== + dependencies: + "@types/unist" "^2.0.0" + vfile "^5.0.0" + +vfile-location@^5.0.0: + version "5.0.2" + resolved "https://registry.yarnpkg.com/vfile-location/-/vfile-location-5.0.2.tgz#220d9ca1ab6f8b2504a4db398f7ebc149f9cb464" + integrity sha512-NXPYyxyBSH7zB5U6+3uDdd6Nybz6o6/od9rk8bp9H8GR3L+cm/fC0uUTbqBmUTnMCUDslAGBOIKNfvvb+gGlDg== + dependencies: + "@types/unist" "^3.0.0" + vfile "^6.0.0" + +vfile-message@^3.0.0: + version "3.1.4" + resolved "https://registry.yarnpkg.com/vfile-message/-/vfile-message-3.1.4.tgz#15a50816ae7d7c2d1fa87090a7f9f96612b59dea" + integrity sha512-fa0Z6P8HUrQN4BZaX05SIVXic+7kE3b05PWAtPuYP9QLHsLKYR7/AlLW3NtOrpXRLeawpDLMsVkmk5DG0NXgWw== + dependencies: + "@types/unist" "^2.0.0" + unist-util-stringify-position "^3.0.0" + +vfile-message@^4.0.0: + version "4.0.2" + resolved "https://registry.yarnpkg.com/vfile-message/-/vfile-message-4.0.2.tgz#c883c9f677c72c166362fd635f21fc165a7d1181" + integrity sha512-jRDZ1IMLttGj41KcZvlrYAaI3CfqpLpfpf+Mfig13viT6NKvRzWZ+lXz0Y5D60w6uJIBAOGq9mSHf0gktF0duw== + dependencies: + "@types/unist" "^3.0.0" + unist-util-stringify-position "^4.0.0" + +vfile@^5.0.0: + version "5.3.7" + resolved "https://registry.yarnpkg.com/vfile/-/vfile-5.3.7.tgz#de0677e6683e3380fafc46544cfe603118826ab7" + integrity sha512-r7qlzkgErKjobAmyNIkkSpizsFPYiUPuJb5pNW1RB4JcYVZhs4lIbVqk8XPk033CV/1z8ss5pkax8SuhGpcG8g== + dependencies: + "@types/unist" "^2.0.0" + is-buffer "^2.0.0" + unist-util-stringify-position "^3.0.0" + vfile-message "^3.0.0" + +vfile@^6.0.0, vfile@^6.0.1: + version "6.0.1" + resolved "https://registry.yarnpkg.com/vfile/-/vfile-6.0.1.tgz#1e8327f41eac91947d4fe9d237a2dd9209762536" + integrity sha512-1bYqc7pt6NIADBJ98UiG0Bn/CHIVOoZ/IyEkqIruLg0mE1BKzkOXY2D6CSqQIcKqgadppE5lrxgWXJmXd7zZJw== + dependencies: + "@types/unist" "^3.0.0" + unist-util-stringify-position "^4.0.0" + vfile-message "^4.0.0" + +vite@^5.0.10: + version "5.0.12" + resolved "https://registry.yarnpkg.com/vite/-/vite-5.0.12.tgz#8a2ffd4da36c132aec4adafe05d7adde38333c47" + integrity sha512-4hsnEkG3q0N4Tzf1+t6NdN9dg/L3BM+q8SWgbSPnJvrgH2kgdyzfVJwbR1ic69/4uMJJ/3dqDZZE5/WwqW8U1w== + dependencies: + esbuild "^0.19.3" + postcss "^8.4.32" + rollup "^4.2.0" + optionalDependencies: + fsevents "~2.3.3" + +vitefu@^0.2.5: + version "0.2.5" + resolved "https://registry.yarnpkg.com/vitefu/-/vitefu-0.2.5.tgz#c1b93c377fbdd3e5ddd69840ea3aa70b40d90969" + integrity sha512-SgHtMLoqaeeGnd2evZ849ZbACbnwQCIwRH57t18FxcXoZop0uQu0uzlIhJBlF/eWVzuce0sHeqPcDo+evVcg8Q== + +volar-service-css@0.0.17: + version "0.0.17" + resolved "https://registry.yarnpkg.com/volar-service-css/-/volar-service-css-0.0.17.tgz#f86283ecc9d81c1f729d3764c5e95184fff7e1ac" + integrity sha512-bEDJykygMzn2+a9ud6KwZZLli9eqarxApAXZuf2CqJJh6Trw1elmbBCo9SlPfqMrIhpFnwV0Sa+Xoc9x5WPeGw== + dependencies: + vscode-css-languageservice "^6.2.10" + vscode-uri "^3.0.8" + +volar-service-emmet@0.0.17: + version "0.0.17" + resolved "https://registry.yarnpkg.com/volar-service-emmet/-/volar-service-emmet-0.0.17.tgz#4fab8364d46249d782ff485c3a0699a4f92f1f5d" + integrity sha512-C6hVnuQL52MqaydkrblYUbzIo5ZmIGo1hR8wmpcCjs5uNcjqn8aPqZRfznhLiUSaPHpFC+zQxJwFcZI9/u2iKQ== + dependencies: + "@vscode/emmet-helper" "^2.9.2" + volar-service-html "0.0.17" + +volar-service-html@0.0.17: + version "0.0.17" + resolved "https://registry.yarnpkg.com/volar-service-html/-/volar-service-html-0.0.17.tgz#de5e95efb8deb7893f4729b5b7168fe3561c068e" + integrity sha512-OGkP+ZTo13j/+enafGe+esXvda/W4eU78YNLbbHxtV3rnX4odVrewenLJmXiECg6wdQz/PG8rLijZqQnDUYkfw== + dependencies: + vscode-html-languageservice "^5.1.0" + vscode-uri "^3.0.8" + +volar-service-prettier@0.0.17: + version "0.0.17" + resolved "https://registry.yarnpkg.com/volar-service-prettier/-/volar-service-prettier-0.0.17.tgz#5f6a395238c62230afe4f563129a1f8d80d1042d" + integrity sha512-YYnzZ+OT0M3Bx+xKuoAfs/uVuxk7ofz4dkZDQqjwa9iC63Ay4YGqONtmHd+xsO3lufkEBXlAQCbofDeZbSz3YQ== + +volar-service-typescript-twoslash-queries@0.0.17: + version "0.0.17" + resolved "https://registry.yarnpkg.com/volar-service-typescript-twoslash-queries/-/volar-service-typescript-twoslash-queries-0.0.17.tgz#e5bf4c3b629f9aa78d7c0161984e3d8da13d5aec" + integrity sha512-6FHXK5AWeFzCL6uGmEcbkZmQsaQ0m9IjbeLdgOIQ4KGvauqT2aA1BhdfDJu6vRAFIfXe7xjEJ85keIlHl72tSA== + +volar-service-typescript@0.0.17: + version "0.0.17" + resolved "https://registry.yarnpkg.com/volar-service-typescript/-/volar-service-typescript-0.0.17.tgz#7dbc7819ddd78c48802b0f2435841598ba902c2e" + integrity sha512-Krs8pOIo2yoBVoJ91hKT1czhWt9ek7EbuK3MxxgvDYdd4HYHOtHi1eOlb7bFnZMNgFcwsL48yQI9vbPm160s9A== + dependencies: + path-browserify "^1.0.1" + semver "^7.5.4" + typescript-auto-import-cache "^0.3.0" + vscode-languageserver-textdocument "^1.0.11" + vscode-nls "^5.2.0" + vscode-uri "^3.0.8" + +vscode-css-languageservice@^6.2.10: + version "6.2.11" + resolved "https://registry.yarnpkg.com/vscode-css-languageservice/-/vscode-css-languageservice-6.2.11.tgz#40de8b34adb6d68ee96795ffb34e34d99fc26801" + integrity sha512-qn49Wa6K94LnizpVxmlYrcPf1Cb36gq1nNueW0COhi4shylXBzET5wuDbH8ZWQlJD0HM5Mmnn7WE9vQVVs+ULA== + dependencies: + "@vscode/l10n" "^0.0.16" + vscode-languageserver-textdocument "^1.0.11" + vscode-languageserver-types "3.17.5" + vscode-uri "^3.0.8" + +vscode-html-languageservice@^5.1.0: + version "5.1.1" + resolved "https://registry.yarnpkg.com/vscode-html-languageservice/-/vscode-html-languageservice-5.1.1.tgz#8e56f7e11c1e3f4a9d56de0f97badea9296f4e04" + integrity sha512-JenrspIIG/Q+93R6G3L6HdK96itSisMynE0glURqHpQbL3dKAKzdm8L40lAHNkwJeBg+BBPpAshZKv/38onrTQ== + dependencies: + "@vscode/l10n" "^0.0.16" + vscode-languageserver-textdocument "^1.0.11" + vscode-languageserver-types "^3.17.5" + vscode-uri "^3.0.8" + +vscode-jsonrpc@8.2.0: + version "8.2.0" + resolved "https://registry.yarnpkg.com/vscode-jsonrpc/-/vscode-jsonrpc-8.2.0.tgz#f43dfa35fb51e763d17cd94dcca0c9458f35abf9" + integrity sha512-C+r0eKJUIfiDIfwJhria30+TYWPtuHJXHtI7J0YlOmKAo7ogxP20T0zxB7HZQIFhIyvoBPwWskjxrvAtfjyZfA== + +vscode-languageserver-protocol@3.17.5, vscode-languageserver-protocol@^3.17.5: + version "3.17.5" + resolved "https://registry.yarnpkg.com/vscode-languageserver-protocol/-/vscode-languageserver-protocol-3.17.5.tgz#864a8b8f390835572f4e13bd9f8313d0e3ac4bea" + integrity sha512-mb1bvRJN8SVznADSGWM9u/b07H7Ecg0I3OgXDuLdn307rl/J3A9YD6/eYOssqhecL27hK1IPZAsaqh00i/Jljg== + dependencies: + vscode-jsonrpc "8.2.0" + vscode-languageserver-types "3.17.5" + +vscode-languageserver-textdocument@^1.0.1, vscode-languageserver-textdocument@^1.0.11: + version "1.0.11" + resolved "https://registry.yarnpkg.com/vscode-languageserver-textdocument/-/vscode-languageserver-textdocument-1.0.11.tgz#0822a000e7d4dc083312580d7575fe9e3ba2e2bf" + integrity sha512-X+8T3GoiwTVlJbicx/sIAF+yuJAqz8VvwJyoMVhwEMoEKE/fkDmrqUgDMyBECcM2A2frVZIUj5HI/ErRXCfOeA== + +vscode-languageserver-types@3.17.5, vscode-languageserver-types@^3.15.1, vscode-languageserver-types@^3.17.5: + version "3.17.5" + resolved "https://registry.yarnpkg.com/vscode-languageserver-types/-/vscode-languageserver-types-3.17.5.tgz#3273676f0cf2eab40b3f44d085acbb7f08a39d8a" + integrity sha512-Ld1VelNuX9pdF39h2Hgaeb5hEZM2Z3jUrrMgWQAu82jMtZp7p3vJT3BzToKtZI7NgQssZje5o0zryOrhQvzQAg== + +vscode-languageserver@^9.0.1: + version "9.0.1" + resolved "https://registry.yarnpkg.com/vscode-languageserver/-/vscode-languageserver-9.0.1.tgz#500aef82097eb94df90d008678b0b6b5f474015b" + integrity sha512-woByF3PDpkHFUreUa7Hos7+pUWdeWMXRd26+ZX2A8cFx6v/JPTtd4/uN0/jB6XQHYaOlHbio03NTHCqrgG5n7g== + dependencies: + vscode-languageserver-protocol "3.17.5" + +vscode-nls@^5.2.0: + version "5.2.0" + resolved "https://registry.yarnpkg.com/vscode-nls/-/vscode-nls-5.2.0.tgz#3cb6893dd9bd695244d8a024bdf746eea665cc3f" + integrity sha512-RAaHx7B14ZU04EU31pT+rKz2/zSl7xMsfIZuo8pd+KZO6PXtQmpevpq3vxvWNcrGbdmhM/rr5Uw5Mz+NBfhVng== + +vscode-uri@^2.1.2: + version "2.1.2" + resolved "https://registry.yarnpkg.com/vscode-uri/-/vscode-uri-2.1.2.tgz#c8d40de93eb57af31f3c715dd650e2ca2c096f1c" + integrity sha512-8TEXQxlldWAuIODdukIb+TR5s+9Ds40eSJrw+1iDDA9IFORPjMELarNQE3myz5XIkWWpdprmJjm1/SxMlWOC8A== + +vscode-uri@^3.0.8: + version "3.0.8" + resolved "https://registry.yarnpkg.com/vscode-uri/-/vscode-uri-3.0.8.tgz#1770938d3e72588659a172d0fd4642780083ff9f" + integrity sha512-AyFQ0EVmsOZOlAnxoFOGOq1SQDWAB7C6aqMGS23svWAllfOaxbuFvcT8D1i8z3Gyn8fraVeZNNmN6e9bxxXkKw== + +web-namespaces@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/web-namespaces/-/web-namespaces-2.0.1.tgz#1010ff7c650eccb2592cebeeaf9a1b253fd40692" + integrity sha512-bKr1DkiNa2krS7qxNtdrtHAmzuYGFQLiQ13TsorsdT6ULTkPLKuu5+GsFpDlg6JFjUTwX2DyhMPG2be8uPrqsQ== + +which-pm-runs@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/which-pm-runs/-/which-pm-runs-1.1.0.tgz#35ccf7b1a0fce87bd8b92a478c9d045785d3bf35" + integrity sha512-n1brCuqClxfFfq/Rb0ICg9giSZqCS+pLtccdag6C2HyufBrh3fBOiy9nb6ggRMvWOVH5GrdJskj5iGTZNxd7SA== + +which-pm@2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/which-pm/-/which-pm-2.0.0.tgz#8245609ecfe64bf751d0eef2f376d83bf1ddb7ae" + integrity sha512-Lhs9Pmyph0p5n5Z3mVnN0yWcbQYUAD7rbQUiMsQxOJ3T57k7RFe35SUwWMf7dsbDZks1uOmw4AecB/JMDj3v/w== + dependencies: + load-yaml-file "^0.2.0" + path-exists "^4.0.0" + +which-pm@^2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/which-pm/-/which-pm-2.1.1.tgz#0be2b70c67e94a32e87b9768a94a7f0954f2dcfa" + integrity sha512-xzzxNw2wMaoCWXiGE8IJ9wuPMU+EYhFksjHxrRT8kMT5SnocBPRg69YAMtyV4D12fP582RA+k3P8H9J5EMdIxQ== + dependencies: + load-yaml-file "^0.2.0" + path-exists "^4.0.0" + +which@^2.0.1: + version "2.0.2" + resolved "https://registry.yarnpkg.com/which/-/which-2.0.2.tgz#7c6a8dd0a636a0327e10b59c9286eee93f3f51b1" + integrity sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA== + dependencies: + isexe "^2.0.0" + +widest-line@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/widest-line/-/widest-line-4.0.1.tgz#a0fc673aaba1ea6f0a0d35b3c2795c9a9cc2ebf2" + integrity sha512-o0cyEG0e8GPzT4iGHphIOh0cJOV8fivsXxddQasHPHfoZf1ZexrfeA21w2NaEN1RHE+fXlfISmOE8R9N3u3Qig== + dependencies: + string-width "^5.0.1" + +wrap-ansi@^7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43" + integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q== + dependencies: + ansi-styles "^4.0.0" + string-width "^4.1.0" + strip-ansi "^6.0.0" + +wrap-ansi@^8.1.0: + version "8.1.0" + resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-8.1.0.tgz#56dc22368ee570face1b49819975d9b9a5ead214" + integrity sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ== + dependencies: + ansi-styles "^6.1.0" + string-width "^5.0.1" + strip-ansi "^7.0.1" + +wrappy@1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" + integrity sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ== + +y18n@^5.0.5: + version "5.0.8" + resolved "https://registry.yarnpkg.com/y18n/-/y18n-5.0.8.tgz#7f4934d0f7ca8c56f95314939ddcd2dd91ce1d55" + integrity sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA== + +yallist@^3.0.2: + version "3.1.1" + resolved "https://registry.yarnpkg.com/yallist/-/yallist-3.1.1.tgz#dbb7daf9bfd8bac9ab45ebf602b8cbad0d5d08fd" + integrity sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g== + +yallist@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/yallist/-/yallist-4.0.0.tgz#9bb92790d9c0effec63be73519e11a35019a3a72" + integrity sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A== + +yargs-parser@^21.1.1: + version "21.1.1" + resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-21.1.1.tgz#9096bceebf990d21bb31fa9516e0ede294a77d35" + integrity sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw== + +yargs@^17.7.2: + version "17.7.2" + resolved "https://registry.yarnpkg.com/yargs/-/yargs-17.7.2.tgz#991df39aca675a192b816e1e0363f9d75d2aa269" + integrity sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w== + dependencies: + cliui "^8.0.1" + escalade "^3.1.1" + get-caller-file "^2.0.5" + require-directory "^2.1.1" + string-width "^4.2.3" + y18n "^5.0.5" + yargs-parser "^21.1.1" + +yocto-queue@^0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/yocto-queue/-/yocto-queue-0.1.0.tgz#0294eb3dee05028d31ee1a5fa2c556a6aaf10a1b" + integrity sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q== + +yocto-queue@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/yocto-queue/-/yocto-queue-1.0.0.tgz#7f816433fb2cbc511ec8bf7d263c3b58a1a3c251" + integrity sha512-9bnSc/HEW2uRy67wc+T8UwauLuPJVn28jb+GtJY16iiKWyvmYJRXVT4UamsAEGQfPohgr2q4Tq0sQbQlxTfi1g== + +zod@^3.22.4: + version "3.22.4" + resolved "https://registry.yarnpkg.com/zod/-/zod-3.22.4.tgz#f31c3a9386f61b1f228af56faa9255e845cf3fff" + integrity sha512-iC+8Io04lddc+mVqQ9AZ7OQ2MrUKGN+oIQyq1vemgt46jwCwLfhq7/pwnBnNXXXZb8VTVLKwp9EDkx+ryxIWmg== + +zwitch@^2.0.0, zwitch@^2.0.4: + version "2.0.4" + resolved "https://registry.yarnpkg.com/zwitch/-/zwitch-2.0.4.tgz#c827d4b0acb76fc3e685a4c6ec2902d51070e9d7" + integrity sha512-bXE4cR/kVZhKZX/RjPEflHaKVhUVl85noU3v6b8apfQEc1x4A+zBxjZ4lN8LqGd6WZ3dl98pY4o717VFmoPp+A== diff --git a/extensions/chord.common/AppDataDirectory.cs b/extensions/chord.common/AppDataDirectory.cs new file mode 100644 index 00000000..17f3d9fa --- /dev/null +++ b/extensions/chord.common/AppDataDirectory.cs @@ -0,0 +1,44 @@ +using System.Runtime.InteropServices; + +namespace Chord.Common; + +public static class StoragePath +{ + public static string BebopcData => FindDataDirectory("betwixt/bebopc"); + public static string FindDataDirectory(string appName) + { + string appDataDir; + // Check for an override in environment variables + var overrideDir = Environment.GetEnvironmentVariable("APP_DATA_DIR_OVERRIDE"); + if (!string.IsNullOrEmpty(overrideDir)) + { + return Path.Combine(overrideDir, appName); + } + // Determine the path based on the OS + if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) + { + appDataDir = Environment.GetEnvironmentVariable("APPDATA") ?? Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.UserProfile), "AppData", "Local"); + } + else if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX)) + { + appDataDir = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.UserProfile), "Library", "Application Support"); + } + else if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux)) + { + appDataDir = "/var"; + } + else if (RuntimeInformation.IsOSPlatform(OSPlatform.FreeBSD)) + { + appDataDir = "/var"; + } + else if (OperatingSystem.IsWasi()) + { + appDataDir = "/"; + } + else + { + throw new PlatformNotSupportedException("Unsupported platform"); + } + return Path.Combine(appDataDir, appName, "extensions"); + } +} \ No newline at end of file diff --git a/extensions/chord.common/BuildShell.cs b/extensions/chord.common/BuildShell.cs new file mode 100644 index 00000000..518d026a --- /dev/null +++ b/extensions/chord.common/BuildShell.cs @@ -0,0 +1,13 @@ +namespace Chord.Common; + +public enum ScriptShell +{ + None, + Bash, + Sh, + Cmd, + Powershell, + Pwsh, + Python, + +} \ No newline at end of file diff --git a/extensions/chord.common/ChordDecoratorTargets.cs b/extensions/chord.common/ChordDecoratorTargets.cs new file mode 100644 index 00000000..703aa395 --- /dev/null +++ b/extensions/chord.common/ChordDecoratorTargets.cs @@ -0,0 +1,15 @@ +namespace Chord.Common; + +[Flags] +public enum ChordDecoratorTargets +{ + None = 0, + Enum = 1 << 0, // 1 + Message = 1 << 1, // 2 + Struct = 1 << 2, // 4 + Union = 1 << 3, // 8 + Field = 1 << 4, // 16 + Service = 1 << 5, // 32 + Method = 1 << 6, // 64 + All = Enum | Message | Struct | Union | Field | Service | Method // Combines all flags +} \ No newline at end of file diff --git a/extensions/chord.common/ChordManifest.cs b/extensions/chord.common/ChordManifest.cs new file mode 100644 index 00000000..210a6f36 --- /dev/null +++ b/extensions/chord.common/ChordManifest.cs @@ -0,0 +1,169 @@ +using System.Text.Json; +using System.Text.RegularExpressions; +using Chord.Common.Wasm; +using Semver; + +namespace Chord.Common +{ + /// + /// Represents the manifest of a extension, detailing its properties such as name, description, version, etc. + /// + public sealed partial record ChordManifest( + /// + /// The name of the extension, part of URL, command line argument, and folder name. Must be URL-safe, no uppercase, <= 214 characters. + /// + string Name, + + bool IsPrivate, + + /// + /// The description of the extension. + /// + string Description, + + /// + /// The version of the extension, in semver format without comparison operators. + /// + SemVersion Version, + + /// + /// License of the extension. Must be one of the specified standard licenses or a string. + /// + string License, + + /// + /// Path to the compiled extension, a valid relative file path. + /// + string Bin, + + /// + /// Defines how to build the extension, including command, args, env, and shell. + /// + BuildCommand Build, + + /// + /// Defines what the extension contributes to bebopc, including generator, , and extends. + /// + ChordContribution Contributions, + + ChordEngine Engine, + + /// + /// An array of authors of the extension. Optional. + /// + ChordAuthor? Author = null, + + /// + /// An object of dependencies with extension name as key and semver range as value. Optional. + /// + ChordDependency[]? Dependencies = null, + + /// + /// Where to report issues about the extension. Optional. + /// + ChordBugTracker? BugTracker = null, + + /// + /// Path to an auxiliary file that will be packaged with the extension. Optional. + /// + Dictionary? Pack = null, + + Uri? Repository = null, + Uri? Homepage = null, + string? ReadMe = null + ) + { + + public static ChordManifest FromFile(string path) => FromJson(System.IO.File.ReadAllText(path)); + + public static ChordManifest FromJson(string json) => + JsonSerializer.Deserialize(json, JsonContext.Default.ChordManifest) + ?? throw new JsonException(); + + public static ChordManifest FromBytes(byte[] bytes) => + JsonSerializer.Deserialize(bytes, JsonContext.Default.ChordManifest) ?? throw new JsonException(); + + public override string ToString() + { + return JsonSerializer.Serialize(this, JsonContext.Default.ChordManifest); + } + } + + public sealed record BuildCommand( + string Script, + WasmCompiler Compiler, + ScriptShell Shell, + string[]? Args = null, + Dictionary? Env = null + ); + + public sealed record ChordDependency( + string Name, + SemVersionRange Version + ); + + public sealed record ChordAuthor( + string Name, + Uri? Url = null, + string? Email = null + ); + + public sealed record ChordBugTracker( + Uri Url, + string? Email = null + ); + + public sealed record ChordPack( + string AuxilaryFile + ); + + public sealed record ChordDecorator( + string Identifier, + string Description, + ChordDecoratorTargets Targets, + bool AllowMultiple, + DecoratorParameter[]? Parameters = null + ); + + public sealed record ChordEngine( + SemVersionRange Bebopc + ); + + public sealed record DefaultValueContainer(object DefaultValue) + { + public T GetValue() where T : struct + { + if (DefaultValue is T t) + return t; + throw new InvalidOperationException($"Value is not of type {typeof(T)}"); + } + + public override string ToString() => DefaultValue.ToString() ?? throw new InvalidOperationException(); + } + + public sealed record DecoratorParameter( + string Identifier, + string Description, + string Type, + bool Required, + DefaultValueContainer? DefaultValue = default, + Regex? Validator = null, + string? ValidationErrorReason = null + ); + + public abstract record ChordContribution( + ContributionType Type, + ChordDecorator[]? Decorators = null + ); + + public sealed record ChordGenerator( + string Name, + string Alias, + ChordDecorator[]? Decorators = null + ) : ChordContribution(ContributionType.Generator, Decorators); + + public sealed record ChordExtender( + string[] Aliases, + ChordDecorator[]? Decorators = null + ) : ChordContribution(ContributionType.Extender, Decorators); +} diff --git a/extensions/chord.common/ChordManifestConverter.Validators.cs b/extensions/chord.common/ChordManifestConverter.Validators.cs new file mode 100644 index 00000000..48bc1608 --- /dev/null +++ b/extensions/chord.common/ChordManifestConverter.Validators.cs @@ -0,0 +1,250 @@ +using System.Text.Json; +using System.Text.RegularExpressions; +using Chord.Common.Wasm; +using Semver; + +namespace Chord.Common; + +internal sealed partial class ChordManifestConverter +{ + private static readonly HashSet ValidTypes = new(StringComparer.OrdinalIgnoreCase) + { + "bool", + "byte", + "uint8", + "uint16", + "int16", + "uint32", + "int32", + "uint64", + "int64", + "float32", + "float64", + "string" + }; + + internal static string ValidateType(string? type) + { + const string message = "Invalid type"; + if (string.IsNullOrWhiteSpace(type)) + throw new JsonException(message, new FormatException("Type cannot be empty.")); + if (!ValidTypes.Contains(type)) + throw new JsonException(message, new FormatException("Unknown or valid type.")); + return type.ToLowerInvariant(); + } + + internal static Uri ValidateUrl(string? url) + { + const string message = "Invalid URL"; + if (string.IsNullOrWhiteSpace(url)) + throw new JsonException(message, new FormatException("URL cannot be empty.")); + if (!Uri.TryCreate(url, UriKind.Absolute, out var uri)) + throw new JsonException(message, new FormatException("URL is not in the correct format.")); + if (!uri.IsAbsoluteUri) + throw new JsonException(message, new FormatException("URL must be absolute.")); + if (uri.Scheme != "https") + throw new JsonException(message, new FormatException("URL must use HTTPS.")); + + return uri; + } + + internal static string ValidateChordName(string? pluginName) + { + const string message = "Invalid extension name"; + if (string.IsNullOrWhiteSpace(pluginName)) + throw new JsonException(message, new FormatException("Extension name cannot be empty.")); + if (pluginName.StartsWith("@", StringComparison.OrdinalIgnoreCase)) + { + throw new JsonException(message, new FormatException("Scoped extension name is not supported.")); + } + if (!ExtensionNameRegex().IsMatch(pluginName)) + throw new JsonException(message, new FormatException("Extension name is not in the correct format.")); + if (pluginName.Length > 214) + throw new JsonException(message, new FormatException("Extension name is too long.")); + return pluginName; + } + + internal static string ValidateDescription(string? description) + { + const string message = "Invalid description"; + if (string.IsNullOrWhiteSpace(description)) + throw new JsonException(message, new FormatException("Description cannot be empty.")); + if (description.Length > 280) + throw new JsonException(message, new FormatException("Description is too long.")); + return description; + } + internal static string ValidateEmail(string? email) + { + const string message = "Invalid email"; + if (string.IsNullOrWhiteSpace(email)) + throw new JsonException(message, new FormatException("Email cannot be empty.")); + if (email.Length > 254) + throw new JsonException(message, new FormatException("Email is too long.")); + if (!EmailRegex().IsMatch(email)) + throw new JsonException(message, new FormatException("Email is not in the correct format.")); + + return email; + } + + internal static SemVersion ValidateChordVersion(string? pluginVersion) + { + const string message = "Invalid version"; + if (string.IsNullOrWhiteSpace(pluginVersion)) + throw new JsonException(message, new FormatException("Version cannot be empty.")); + + if (!Semver.SemVersion.TryParse(pluginVersion, Semver.SemVersionStyles.Strict, out var v)) + throw new JsonException(message, new FormatException("Version is not in the correct SemVer format.")); + return v; + } + + internal static SemVersionRange ValidateVersionRange(string? version) + { + const string message = "Invalid version range"; + if (string.IsNullOrWhiteSpace(version)) + throw new JsonException(message, new FormatException("Version range cannot be empty.")); + if (version.Equals("*", StringComparison.OrdinalIgnoreCase)) + throw new JsonException(message, new FormatException("Version range cannot be *.")); + if (!Semver.SemVersionRange.TryParseNpm(version, true, out var v)) + throw new JsonException(message, new FormatException("Version range is not in the correct SemVer/npm format.")); + return v; + } + + internal static ChordAuthor ValidateAuthor(string? name, Uri? url, string? email) + { + const string message = "Invalid author"; + if (string.IsNullOrWhiteSpace(name)) + throw new JsonException(message, new FormatException("Author name cannot be empty.")); + + if (name.Length > 50) + throw new JsonException(message, new FormatException("Author name is too long.")); + + return new ChordAuthor(name, url, email); + } + + internal static string ValidateContributedAlias(string? contributedAlias) + { + const string message = "Invalid contributed alias"; + if (string.IsNullOrWhiteSpace(contributedAlias)) + throw new JsonException(message, new FormatException("Contributed alias cannot be empty.")); + if (!ContributedGeneratorAliasRegex().IsMatch(contributedAlias)) + throw new JsonException(message, new FormatException("Contributed alias cannot override a built-in generator.")); + if (contributedAlias.Length > 7) + throw new JsonException(message, new FormatException("Contributed alias is too long.")); + return contributedAlias; + } + + internal static Regex ValidateRegex(string? regex) + { + const string message = "Invalid regex"; + if (string.IsNullOrWhiteSpace(regex)) + throw new JsonException(message, new FormatException("Regex cannot be empty.")); + try + { + return new Regex(regex, RegexOptions.Compiled, TimeSpan.FromSeconds(1)); + } + catch (Exception ex) + { + throw new JsonException(message, new FormatException("Unable to create Regex from input.", ex)); + } + } + + internal static WasmCompiler ValidateCompiler(string? compiler) + { + const string message = "Invalid compiler"; + if (string.IsNullOrWhiteSpace(compiler)) + throw new JsonException(message, new FormatException("Compiler cannot be empty.")); + return compiler.ToLowerInvariant() switch + { + "as" => WasmCompiler.AssemblyScript, + "tinygo" => WasmCompiler.TinyGo, + "javy" => WasmCompiler.Javy, + _ => throw new JsonException(message, new FormatException("Unknown compiler.")) + }; + } + + internal static string ValidateGeneratorAlias(string? generatorAlias) + { + const string message = "Invalid generator alias"; + if (string.IsNullOrWhiteSpace(generatorAlias)) + throw new JsonException(message, new FormatException("Generator alias cannot be empty.")); + var alias = generatorAlias.Trim(); + if (!GeneratorAliasRegex().IsMatch(generatorAlias)) + throw new JsonException(message, new FormatException("Generator alias is not in the correct format.")); + if (generatorAlias.Length > 7) + throw new JsonException(message, new FormatException("Generator alias is too long.")); + return alias; + } + + internal static string ValidateIdentifier(string? indentifer) + { + const string message = "Invalid identifier"; + if (string.IsNullOrWhiteSpace(indentifer)) + throw new JsonException(message, new FormatException("Identifier cannot be empty.")); + if (!IdentifierRegex().IsMatch(indentifer)) + throw new JsonException(message, new FormatException("Identifier is not in the correct format.")); + if (indentifer.Length > 32) + throw new JsonException(message, new FormatException("Identifier is too long.")); + return indentifer; + } + + internal static ChordDecoratorTargets ValidateDecoratorTargets(string? decoratorTargets) + { + const string message = "Invalid decorator targets"; + if (string.IsNullOrWhiteSpace(decoratorTargets)) + throw new JsonException(message, new FormatException("Decorator targets cannot be empty.")); + + if (!DecoratorTargetRegex().IsMatch(decoratorTargets)) + throw new JsonException(message, new FormatException("Decorator targets is not in the correct format.")); + + + if (decoratorTargets.Equals("all", StringComparison.OrdinalIgnoreCase)) + { + return ChordDecoratorTargets.All; + } + + ChordDecoratorTargets result = ChordDecoratorTargets.None; + + var values = decoratorTargets.Split('|'); + foreach (var value in values) + { + result |= value.Trim().ToLower() switch + { + "enum" => ChordDecoratorTargets.Enum, + "message" => ChordDecoratorTargets.Message, + "struct" => ChordDecoratorTargets.Struct, + "union" => ChordDecoratorTargets.Union, + "field" => ChordDecoratorTargets.Field, + "service" => ChordDecoratorTargets.Service, + "method" => ChordDecoratorTargets.Method, + _ => throw new JsonException(message, new FormatException("Unknown decorator targets value.")) + }; + } + if (result == ChordDecoratorTargets.None) + throw new JsonException(message, new FormatException("Decorator targets cannot be None.")); + + return result; + } + + + [GeneratedRegex(@"^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$")] + private static partial Regex EmailRegex(); + + [GeneratedRegex(@"^(?:(?:@(?:[a-z0-9-*~][a-z0-9-*._~]*)?/[a-z0-9-._~])|[a-z0-9-~])[a-z0-9-._~]*$")] + private static partial Regex ExtensionNameRegex(); + + [GeneratedRegex(@"^(?!cs$|py$|ts$|rust$|dart$|cpp$)[a-z]+$")] + private static partial Regex ContributedGeneratorAliasRegex(); + + [GeneratedRegex(@"^[a-z]{1,7}$")] + private static partial Regex GeneratorAliasRegex(); + + [GeneratedRegex(@"^[a-zA-Z ]+$")] + internal static partial Regex AlphabeticalRegex(); + + + [GeneratedRegex(@"^(all|((enum|message|struct|union|field|service|method)(\\|(enum|message|struct|union|field|service|method))*))$")] + private static partial Regex DecoratorTargetRegex(); + + [GeneratedRegex(@"^[a-z]+([A-Z][a-z]+)*$")] + private static partial Regex IdentifierRegex(); +} \ No newline at end of file diff --git a/extensions/chord.common/ChordManifestConverter.cs b/extensions/chord.common/ChordManifestConverter.cs new file mode 100644 index 00000000..8d8efdf5 --- /dev/null +++ b/extensions/chord.common/ChordManifestConverter.cs @@ -0,0 +1,945 @@ +using System.Runtime.InteropServices; +using System.Text.Json; +using System.Text.Json.Serialization; +using System.Text.RegularExpressions; +using Semver; +using Chord.Common.Wasm; +using Chord.Common.Extensions; + +namespace Chord.Common; + +internal sealed partial class ChordManifestConverter : JsonConverter +{ + private const string ErrorMessage = "Invalid chord manifest."; + public override ChordManifest Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + { + if (reader.TokenType is not JsonTokenType.StartObject) + { + throw new JsonException(ErrorMessage, new FormatException("Expected StartObject token.")); + } + bool isPrivate = false; + string? name = null; + string? description = null; + SemVersion? version = null; + string? license = null; + string? bin = null; + Uri? repository = null; + Uri? homepage = null; + BuildCommand? build = null; + ChordContribution? contributions = null; + ChordAuthor? author = null; + ChordDependency[]? dependencies = null; + ChordBugTracker? bugTracker = null; + Dictionary? pack = null; + ChordEngine? engine = null; + string? readme = null; + while (reader.Read()) + { + if (reader.TokenType is JsonTokenType.EndObject) + { + break; + } + if (reader.TokenType is not JsonTokenType.PropertyName) + { + throw new JsonException(ErrorMessage, new FormatException("Expected PropertyName token.")); + } + var propertyName = reader.GetString(); + reader.Read(); + switch (propertyName) + { + case "$schema": + reader.Skip(); + break; + case "name": + name = ValidateChordName(reader.GetString()); + break; + case "private": + isPrivate = reader.GetBoolean(); + break; + case "description": + description = ValidateDescription(reader.GetString()); + break; + case "version": + version = ValidateChordVersion(reader.GetString()); + break; + case "license": + license = reader.GetNonNullOrWhiteSpaceString(); + break; + case "bin": + bin = reader.GetNonNullOrWhiteSpaceString(); + break; + case "build": + build = GetBuildCommand(ref reader, options); + break; + case "bugs": + bugTracker = GetBugTracker(ref reader, options); + break; + case "author": + author = GetAuthor(ref reader, options); + break; + case "repository": + repository = ValidateUrl(reader.GetString()); + break; + case "homepage": + homepage = ValidateUrl(reader.GetString()); + break; + case "pack": + pack = GetChordPack(ref reader, options); + break; + case "dependencies": + dependencies = GetChordDependencies(ref reader, options); + break; + case "contributes": + contributions = GetChordContribution(ref reader, options); + break; + case "readme": + readme = reader.GetNonNullOrWhiteSpaceString(); + break; + case "engine": + engine = GetChordEngine(ref reader, options); + break; + default: + throw new JsonException(ErrorMessage, new FormatException($"Property '{propertyName}' is not expected.")); + } + } + + if (string.IsNullOrWhiteSpace(name)) + { + throw new JsonException(ErrorMessage, new FormatException("No 'name' defined in 'chord' manifest.")); + } + if (string.IsNullOrWhiteSpace(description)) + { + throw new JsonException(ErrorMessage, new FormatException("No 'description' defined in 'chord' manifest.")); + } + if (version is null) + { + throw new JsonException(ErrorMessage, new FormatException("No 'version' defined in 'chord' manifest.")); + } + if (string.IsNullOrWhiteSpace(license)) + { + throw new JsonException(ErrorMessage, new FormatException("No 'license' defined in 'chord' manifest.")); + } + + if (string.IsNullOrWhiteSpace(bin)) + { + throw new JsonException(ErrorMessage, new FormatException("No 'bin' defined in 'chord' manifest.")); + } + + if (!bin.IsLegalFilePath(out var index)) + { + throw new JsonException(ErrorMessage, new FormatException($"Bin path is invalid at index {index}")); + } + if (!string.IsNullOrWhiteSpace(readme) && !readme.IsLegalFilePath(out index)) + { + throw new JsonException(ErrorMessage, new FormatException($"Readme path is invalid at index {index}")); + } + if (build is null) + { + throw new JsonException(ErrorMessage, new FormatException("No 'build' defined in 'chord' manifest.")); + } + if (contributions is null) + { + throw new JsonException(ErrorMessage, new FormatException("No 'contributes' defined in 'chord' manifest.")); + } + if (engine is null) + { + throw new JsonException(ErrorMessage, new FormatException("No 'engine' defined in 'chord' manifest.")); + } + + return new ChordManifest( + name, + isPrivate, + description, + version, + license, + bin, + build, + contributions, + engine, + author, + dependencies, + bugTracker, + pack, + repository, + homepage, + readme + ); + } + + private ChordEngine? GetChordEngine(ref Utf8JsonReader reader, JsonSerializerOptions options) + { + if (reader.TokenType != JsonTokenType.StartObject) + { + throw new JsonException(ErrorMessage, new FormatException("expected StartObject token for engine")); + } + SemVersionRange? bebopc = null; + while (reader.Read()) + { + if (reader.TokenType == JsonTokenType.EndObject) + { + break; + } + if (reader.TokenType == JsonTokenType.PropertyName) + { + var propertyName = reader.GetString(); + reader.Read(); + switch (propertyName) + { + case "bebopc": + bebopc = ValidateVersionRange(reader.GetString()); + break; + default: + throw new JsonException(ErrorMessage, new FormatException($"Property '{propertyName}' is not expected.")); + } + } + } + if (bebopc is null) + { + throw new JsonException(ErrorMessage, new FormatException("No 'bebopc' defined in 'engine'")); + } + return new ChordEngine(bebopc); + } + + private static BuildCommand GetBuildCommand(ref Utf8JsonReader reader, JsonSerializerOptions options) + { + if (reader.TokenType != JsonTokenType.StartObject) + { + throw new JsonException(ErrorMessage, new FormatException("expected StartObject token for build")); + } + string? script = null; + WasmCompiler compiler = WasmCompiler.None; + ScriptShell shell = ScriptShell.None; + string[]? args = null; + Dictionary? env = null; + + while (reader.Read()) + { + if (reader.TokenType == JsonTokenType.EndObject) + { + break; + } + if (reader.TokenType == JsonTokenType.PropertyName) + { + var propertyName = reader.GetString(); + reader.Read(); + switch (propertyName) + { + case "script": + script = reader.GetNonNullOrWhiteSpaceString(); + break; + case "args": + args = JsonSerializer.Deserialize(ref reader, JsonContext.Default.StringArray); + break; + case "env": + env = reader.ReadDictionary(); + break; + case "shell": + shell = reader.GetNonNullOrWhiteSpaceString().ToLowerInvariant() switch + { + "bash" => ScriptShell.Bash, + "sh" => ScriptShell.Sh, + "cmd" => ScriptShell.Cmd, + "powershell" => ScriptShell.Powershell, + "pwsh" => ScriptShell.Pwsh, + "python" => ScriptShell.Python, + _ => throw new JsonException(ErrorMessage, new FormatException($"Invalid shell ")) + }; + break; + case "compiler": + compiler = ValidateCompiler(reader.GetString()); + break; + default: + throw new JsonException(ErrorMessage, new FormatException($"Property '{propertyName}' is not expected.")); + } + + } + } + + if (script is null) + { + throw new JsonException(ErrorMessage, new FormatException("No 'command' defined in 'build'")); + } + if (compiler is WasmCompiler.None) + { + throw new JsonException(ErrorMessage, new FormatException("No 'compiler' defined in 'build'")); + } + return new BuildCommand(script, compiler, shell, args, env); + } + + private static ChordAuthor GetAuthor(ref Utf8JsonReader reader, JsonSerializerOptions options) + { + if (reader.TokenType != JsonTokenType.StartObject) + { + throw new JsonException(ErrorMessage, new FormatException("expected StartObject token for author")); + } + string? name = null; + Uri? url = null; + string? email = null; + + while (reader.Read()) + { + if (reader.TokenType == JsonTokenType.EndObject) + { + break; + } + if (reader.TokenType == JsonTokenType.PropertyName) + { + var propertyName = reader.GetString(); + reader.Read(); + switch (propertyName) + { + case "name": + name = reader.GetNonNullOrWhiteSpaceString(); + break; + case "url": + url = ValidateUrl(reader.GetString()); + break; + case "email": + email = ValidateEmail(reader.GetString()); + break; + default: + throw new JsonException(ErrorMessage, new FormatException($"Property '{propertyName}' is not expected.")); + } + } + } + return ValidateAuthor(name, url, email); + } + + private static ChordBugTracker GetBugTracker(ref Utf8JsonReader reader, JsonSerializerOptions options) + { + if (reader.TokenType != JsonTokenType.StartObject) + { + throw new JsonException(ErrorMessage, new FormatException("expected StartObject token for bug tracker")); + } + + Uri? url = null; + string? email = null; + + while (reader.Read()) + { + if (reader.TokenType == JsonTokenType.EndObject) + { + break; + } + if (reader.TokenType == JsonTokenType.PropertyName) + { + var propertyName = reader.GetString(); + reader.Read(); + switch (propertyName) + { + case "url": + url = ValidateUrl(reader.GetString()); + break; + case "email": + email = ValidateEmail(reader.GetString()); + break; + default: + throw new JsonException(ErrorMessage, new FormatException($"Property '{propertyName}' is not expected.")); + } + } + } + if (url is null) + { + throw new JsonException(ErrorMessage, new FormatException("No 'url' defined in 'bugs'")); + } + return new ChordBugTracker(url, email); + } + + private static Dictionary GetChordPack(ref Utf8JsonReader reader, JsonSerializerOptions options) + { + if (reader.TokenType != JsonTokenType.StartObject) + { + throw new JsonException(ErrorMessage, new FormatException("expected StartObject token for author")); + } + + var packs = new Dictionary(); + + while (reader.Read()) + { + if (reader.TokenType == JsonTokenType.EndObject) + { + break; + } + if (reader.TokenType == JsonTokenType.PropertyName) + { + var alias = ValidateGeneratorAlias(reader.GetString()); + reader.Read(); + packs.Add(alias, GetChordPackValue(ref reader, options)); + } + } + if (packs.Count == 0) + { + throw new JsonException(ErrorMessage, new FormatException("Empty 'pack' collection defined in 'contributions'")); + } + if (packs.Count != packs.Select(x => x.Key).Distinct().Count()) + { + throw new JsonException(ErrorMessage, new FormatException("Duplicate pack aliases defined in 'contributions'")); + } + return packs; + } + + private static ChordPack GetChordPackValue(ref Utf8JsonReader reader, JsonSerializerOptions options) + { + if (reader.TokenType != JsonTokenType.StartObject) + { + throw new JsonException(ErrorMessage, new FormatException("expected StartObject token for author")); + } + + string? auxilaryFile = null; + while (reader.Read()) + { + if (reader.TokenType == JsonTokenType.EndObject) + { + break; + } + if (reader.TokenType == JsonTokenType.PropertyName) + { + var propertyName = reader.GetString(); + reader.Read(); + switch (propertyName) + { + case "auxiliaryFile": + auxilaryFile = reader.GetNonNullOrWhiteSpaceString(); + break; + default: + throw new JsonException(ErrorMessage, new FormatException($"Property '{propertyName}' is not expected.")); + } + } + } + if (auxilaryFile is null) + { + throw new JsonException(ErrorMessage, new FormatException("No 'auxiliaryFile' defined in 'pack'")); + } + if (!auxilaryFile.IsLegalFilePath(out var index)) + { + throw new JsonException(ErrorMessage, new FormatException($"Auxiliary file path is invalid at index {index}")); + } + return new ChordPack(auxilaryFile); + } + + private static ChordDependency[] GetChordDependencies(ref Utf8JsonReader reader, JsonSerializerOptions options) + { + if (reader.TokenType != JsonTokenType.StartObject) + { + throw new JsonException(ErrorMessage, new FormatException("expected StartObject token for dependencies")); + } + + var dependencies = new List(); + while (reader.Read()) + { + if (reader.TokenType == JsonTokenType.EndObject) + { + break; + } + if (reader.TokenType == JsonTokenType.PropertyName) + { + var dependencyName = ValidateChordName(reader.GetString()); + reader.Read(); + var dependencyVersion = ValidateVersionRange(reader.GetString()); + dependencies.Add(new ChordDependency(dependencyName, dependencyVersion)); + } + } + return [.. dependencies]; + } + + + private static ChordContribution GetChordContribution(ref Utf8JsonReader reader, JsonSerializerOptions options) + { + if (reader.TokenType != JsonTokenType.StartObject) + { + throw new JsonException(ErrorMessage, new FormatException("expected StartObject token for contributions")); + } + ChordDecorator[]? decorators = null; + ChordGenerator? generator = null; + string[]? extendedAliases = null; + while (reader.Read()) + { + if (reader.TokenType == JsonTokenType.EndObject) + { + break; + } + if (reader.TokenType == JsonTokenType.PropertyName) + { + var propertyName = reader.GetString(); + reader.Read(); + switch (propertyName) + { + case "decorators": + decorators = GetChordDecorators(ref reader, options).ToArray(); + break; + case "generator": + generator = GetChordGenerator(ref reader, options); + break; + case "extends": + extendedAliases = JsonSerializer.Deserialize(ref reader, JsonContext.Default.StringArray); + break; + default: + throw new JsonException(ErrorMessage, new FormatException($"Property '{propertyName}' is not expected.")); + } + } + } + if (generator is not null && extendedAliases is not null) + { + throw new JsonException(ErrorMessage, new FormatException("Cannot define both 'generator' and 'extends' in 'contributions'")); + } + if (generator is null && extendedAliases is null) + { + throw new JsonException(ErrorMessage, new FormatException("No 'generator' or 'extends' defined in 'contributions'")); + } + + if (generator is not null) + { + return generator with { Decorators = decorators }; + } + if (extendedAliases is not null) + { + var aliases = extendedAliases.Select(ValidateGeneratorAlias).ToList(); + if (aliases.Count == 0) + { + throw new JsonException(ErrorMessage, new FormatException("Empty 'extends' collection defined in 'contributions'")); + } + if (aliases.Count != aliases.Distinct().Count()) + { + throw new JsonException(ErrorMessage, new FormatException("Duplicate generator aliases defined in 'contributions'")); + } + return new ChordExtender(aliases.ToArray(), decorators); + } + throw new JsonException(ErrorMessage, new FormatException("No 'generator' or 'extends' defined in 'contributions'")); + } + + private static List GetChordDecorators(ref Utf8JsonReader reader, JsonSerializerOptions options) + { + if (reader.TokenType != JsonTokenType.StartObject) + { + throw new JsonException(ErrorMessage, new FormatException("expected StartObject token for decorators")); + } + + var decorators = new List(); + while (reader.Read()) + { + if (reader.TokenType == JsonTokenType.EndObject) + { + break; + } + + if (reader.TokenType == JsonTokenType.PropertyName) + { + var identifier = ValidateIdentifier(reader.GetString()); + reader.Read(); // Move to the value + + // Ensure the value is an object + if (reader.TokenType != JsonTokenType.StartObject) + { + throw new JsonException(ErrorMessage, new FormatException("expected StartObject token for decorator")); + } + decorators.Add(GetChordDecorator(ref reader, options, identifier)); + } + } + if (decorators.Count == 0) + { + throw new JsonException(ErrorMessage, new FormatException("Empty 'decorators' collection defined in 'contributions'")); + } + if (decorators.Count != decorators.Select(x => x.Identifier).Distinct().Count()) + { + throw new JsonException(ErrorMessage, new FormatException("Duplicate decorator identifiers defined in 'contributions'")); + } + return decorators; + } + + private static ChordDecorator GetChordDecorator(ref Utf8JsonReader reader, JsonSerializerOptions options, string decoratorIdentifier) + { + string? description = null; + ChordDecoratorTargets targets = ChordDecoratorTargets.All; + bool allowMultiple = false; + List? parameters = null; + while (reader.Read()) + { + if (reader.TokenType == JsonTokenType.EndObject) + { + break; + } + + if (reader.TokenType == JsonTokenType.PropertyName) + { + var propertyName = reader.GetString(); + reader.Read(); // Move to the value + + switch (propertyName) + { + case "description": + description = ValidateDescription(reader.GetString()); + break; + case "targets": + targets = ValidateDecoratorTargets(reader.GetString()); + break; + case "allowMultiple": + allowMultiple = reader.GetBoolean(); + break; + case "parameters": + parameters = GetDecoratorParameters(ref reader, options); + break; + default: + throw new JsonException(ErrorMessage, new FormatException($"Property '{propertyName}' is not expected.")); + } + } + } + if (string.IsNullOrWhiteSpace(description)) + { + throw new JsonException(ErrorMessage, new FormatException($"No 'description' defined in decorator[{decoratorIdentifier}]")); + } + if (targets is ChordDecoratorTargets.None) + { + throw new JsonException(ErrorMessage, new FormatException($"No 'targets' defined in decorator[{decoratorIdentifier}]")); + } + if (parameters is not null) + { + if (parameters.Count == 0) + { + throw new JsonException(ErrorMessage, new FormatException($"Empty 'parameters' collection defined in decorator[{decoratorIdentifier}]")); + } + if (parameters.Count != parameters.Select(x => x.Identifier).Distinct().Count()) + { + throw new JsonException(ErrorMessage, new FormatException($"Duplicate parameter identifiers defined in decorator[{decoratorIdentifier}]")); + } + bool nonRequiredParameterEncountered = false; + foreach (var parameter in parameters) + { + if (!parameter.Required) + { + nonRequiredParameterEncountered = true; + } + else if (nonRequiredParameterEncountered) + { + // A required parameter is found after a non-required parameter + throw new JsonException(ErrorMessage, new FormatException($"Required parameter[{parameter.Identifier}] defined after non-required parameter in decorator[{decoratorIdentifier}]")); + } + } + } + return new ChordDecorator(decoratorIdentifier, description, targets, allowMultiple, parameters?.ToArray()); + } + + private static List GetDecoratorParameters(ref Utf8JsonReader reader, JsonSerializerOptions options) + { + if (reader.TokenType != JsonTokenType.StartObject) + { + throw new JsonException(ErrorMessage, new FormatException("expected StartObject token for parameters")); + } + var parameters = new List(); + while (reader.Read()) + { + if (reader.TokenType == JsonTokenType.EndObject) + { + break; + } + + if (reader.TokenType == JsonTokenType.PropertyName) + { + var parameterName = ValidateIdentifier(reader.GetString()); + reader.Read(); // Move to the value + + string? description = null; + bool required = false; + JsonElement? defaultRaw = null; + DefaultValueContainer? defaultValue = null; + string? type = null; + Regex? validator = null; + string? validatorErrorReason = null; + while (reader.Read()) + { + if (reader.TokenType == JsonTokenType.EndObject) + { + break; + } + if (reader.TokenType == JsonTokenType.PropertyName) + { + var propertyName = reader.GetString(); + reader.Read(); // Move to the value + switch (propertyName) + { + case "description": + description = ValidateDescription(reader.GetString()); + break; + case "required": + required = reader.GetBoolean(); + break; + case "default": + defaultRaw = JsonDocument.ParseValue(ref reader).RootElement; + break; + case "type": + type = ValidateType(reader.GetString()); + break; + case "validator": + validator = ValidateRegex(reader.GetString()); + break; + case "validatorErrorReason": + validatorErrorReason = reader.GetNonNullOrWhiteSpaceString(); + break; + default: + throw new JsonException($"Property '{propertyName}' is not expected."); + } + } + } + + if (string.IsNullOrWhiteSpace(description)) + { + throw new JsonException(ErrorMessage, new FormatException($"No 'description' defined in parameter[{parameterName}]")); + } + if (string.IsNullOrWhiteSpace(type)) + { + throw new JsonException(ErrorMessage, new FormatException($"No 'type' defined in parameter[{parameterName}]")); + } + if (defaultRaw is not null && required) + { + throw new JsonException(ErrorMessage, new FormatException($"Parameter '{parameterName}' is required but has a default value.")); + } + if (defaultRaw is null && !required) + { + throw new JsonException(ErrorMessage, new FormatException($"Parameter '{parameterName}' is not required but has no default value.")); + } + if (defaultRaw is not null) + { + defaultValue = type switch + { + "bool" => new DefaultValueContainer(defaultRaw.Value.GetBoolean()), + "byte" => new DefaultValueContainer(defaultRaw.Value.GetByte()), + "uint8" => new DefaultValueContainer(defaultRaw.Value.GetByte()), + "uint16" => new DefaultValueContainer(defaultRaw.Value.GetUInt16()), + "int16" => new DefaultValueContainer(defaultRaw.Value.GetInt16()), + "uint32" => new DefaultValueContainer(defaultRaw.Value.GetUInt32()), + "int32" => new DefaultValueContainer(defaultRaw.Value.GetInt32()), + "uint64" => new DefaultValueContainer(defaultRaw.Value.GetUInt64()), + "int64" => new DefaultValueContainer(defaultRaw.Value.GetInt64()), + "float32" => new DefaultValueContainer(defaultRaw.Value.GetSingle()), + "float64" => new DefaultValueContainer(defaultRaw.Value.GetDouble()), + "string" => new DefaultValueContainer(defaultRaw.Value.GetString() ?? throw new JsonException(ErrorMessage, new FormatException($"Parameter '{parameterName}' has a null default value."))), + _ => throw new JsonException(ErrorMessage, new FormatException($"Parameter '{parameterName}' has an invalid type.")) + }; + } + if (validator is not null && validatorErrorReason is null) + { + throw new JsonException(ErrorMessage, new FormatException($"Parameter '{parameterName}' has a validator but no validator error reason.")); + } + parameters.Add(new DecoratorParameter(parameterName, description, type, required, defaultValue, validator, validatorErrorReason)); + } + } + return parameters; + } + + + private static ChordGenerator GetChordGenerator(ref Utf8JsonReader reader, JsonSerializerOptions options) + { + if (reader.TokenType != JsonTokenType.StartObject) + { + throw new JsonException(ErrorMessage, new FormatException("expected StartObject token for generator")); + } + string? name = null; + string? alias = null; + while (reader.Read()) + { + if (reader.TokenType == JsonTokenType.EndObject) + { + break; + } + if (reader.TokenType == JsonTokenType.PropertyName) + { + var propertyName = reader.GetString(); + reader.Read(); + switch (propertyName) + { + case "name": + name = reader.GetNonNullOrWhiteSpaceString(); + break; + case "alias": + alias = ValidateContributedAlias(reader.GetString()); + break; + default: + throw new JsonException(ErrorMessage, new FormatException($"Property '{propertyName}' is not expected.")); + } + } + } + if (string.IsNullOrWhiteSpace(name)) + { + throw new JsonException(ErrorMessage, new FormatException("No 'name' defined in 'generator'")); + } + if (!AlphabeticalRegex().IsMatch(name)) + { + throw new JsonException(ErrorMessage, new FormatException($"Generator name '{name}' is not alphabetical.")); + } + if (string.IsNullOrWhiteSpace(alias)) + { + throw new JsonException(ErrorMessage, new FormatException("No 'alias' defined in 'generator'")); + } + return new ChordGenerator(name, alias); + } + + public override void Write(Utf8JsonWriter writer, ChordManifest value, JsonSerializerOptions options) + { + writer.WriteStartObject(); + writer.WriteString("name", value.Name); + writer.WriteBoolean("private", value.IsPrivate); + writer.WriteString("description", value.Description); + writer.WriteString("version", value.Version.ToString()); + writer.WriteString("license", value.License); + writer.WriteString("bin", value.Bin); + writer.WriteStartObject("build"); + writer.WriteString("script", value.Build.Script); + writer.WriteString("compiler", value.Build.Compiler.ToCompilerString()); + writer.WriteString("shell", value.Build.Shell.ToString().ToLowerInvariant()); + if (value.Build.Env is not null) + { + writer.WriteStartObject("env"); + foreach (var (key, val) in value.Build.Env) + { + writer.WriteString(key, val); + } + writer.WriteEndObject(); + } + writer.WriteEndObject(); + + if (value.Author is not null) + { + writer.WriteStartObject("author"); + writer.WriteString("name", value.Author.Name); + if (value.Author.Url is not null) + { + writer.WriteString("url", value.Author.Url.ToString()); + } + if (value.Author.Email is not null) + { + writer.WriteString("email", value.Author.Email); + } + writer.WriteEndObject(); + } + if (value.BugTracker is not null) + { + writer.WriteStartObject("bugs"); + writer.WriteString("url", value.BugTracker.Url.ToString()); + if (value.BugTracker.Email is not null) + { + writer.WriteString("email", value.BugTracker.Email); + } + writer.WriteEndObject(); + } + if (value.Repository is not null) + { + writer.WriteString("repository", value.Repository.ToString()); + } + if (value.Homepage is not null) + { + writer.WriteString("homepage", value.Homepage.ToString()); + } + if (value.Pack is not null) + { + writer.WriteStartObject("pack"); + foreach (var (key, val) in value.Pack) + { + writer.WriteStartObject(key); + writer.WriteString("auxiliaryFile", val.AuxilaryFile); + writer.WriteEndObject(); + } + writer.WriteEndObject(); + } + + writer.WriteStartObject("engine"); + writer.WriteString("bebopc", value.Engine.Bebopc.ToString()); + writer.WriteEndObject(); + + writer.WriteStartObject("contributes"); + if (value.Contributions is ChordGenerator generator) + { + writer.WriteStartObject("generator"); + writer.WriteString("name", generator.Name); + writer.WriteString("alias", generator.Alias); + writer.WriteEndObject(); + } + else if (value.Contributions is ChordExtender extender) + { + writer.WriteStartArray("extends"); + foreach (var alias in extender.Aliases) + { + writer.WriteStringValue(alias); + } + writer.WriteEndArray(); + } + if (value.Contributions.Decorators is not null) + { + writer.WriteStartObject("decorators"); + foreach (var decorator in value.Contributions.Decorators) + { + writer.WriteStartObject(decorator.Identifier); + writer.WriteString("description", decorator.Description); + writer.WriteString("targets", decorator.Targets.ToString()); + writer.WriteBoolean("allowMultiple", decorator.AllowMultiple); + if (decorator.Parameters is not null) + { + writer.WriteStartObject("parameters"); + foreach (var parameter in decorator.Parameters) + { + writer.WriteStartObject(parameter.Identifier); + writer.WriteString("description", parameter.Description); + writer.WriteString("type", parameter.Type); + writer.WriteBoolean("required", parameter.Required); + if (parameter.DefaultValue is not null) + { + writer.WritePropertyName("default"); + switch (parameter.DefaultValue.DefaultValue) + { + case bool boolValue: + writer.WriteBooleanValue(boolValue); + break; + case byte byteValue: + writer.WriteNumberValue(byteValue); + break; + case ushort ushortValue: + writer.WriteNumberValue(ushortValue); + break; + case short shortValue: + writer.WriteNumberValue(shortValue); + break; + case uint uintValue: + writer.WriteNumberValue(uintValue); + break; + case int intValue: + writer.WriteNumberValue(intValue); + break; + case ulong ulongValue: + writer.WriteNumberValue(ulongValue); + break; + case long longValue: + writer.WriteNumberValue(longValue); + break; + case float floatValue: + writer.WriteNumberValue(floatValue); + break; + case double doubleValue: + writer.WriteNumberValue(doubleValue); + break; + case string stringValue: + writer.WriteStringValue(stringValue); + break; + default: + throw new ArgumentOutOfRangeException(nameof(parameter.DefaultValue)); + } + } + if (parameter.Validator is not null) + { + writer.WriteString("validator", parameter.Validator.ToString()); + } + if (parameter.ValidationErrorReason is not null) + { + writer.WriteString("validatorErrorReason", parameter.ValidationErrorReason); + } + writer.WriteEndObject(); + } + writer.WriteEndObject(); + } + writer.WriteEndObject(); + } + writer.WriteEndObject(); + } + + writer.WriteEndObject(); + + writer.WriteEndObject(); + } +} diff --git a/extensions/chord.common/ContributionType.cs b/extensions/chord.common/ContributionType.cs new file mode 100644 index 00000000..7c02fa77 --- /dev/null +++ b/extensions/chord.common/ContributionType.cs @@ -0,0 +1,7 @@ +namespace Chord.Common; + +public enum ContributionType +{ + Generator, + Extender +} \ No newline at end of file diff --git a/extensions/chord.common/Extensions/BinaryReaderExtensions.cs b/extensions/chord.common/Extensions/BinaryReaderExtensions.cs new file mode 100644 index 00000000..dc291729 --- /dev/null +++ b/extensions/chord.common/Extensions/BinaryReaderExtensions.cs @@ -0,0 +1,71 @@ +namespace Chord.Common.Extensions; + +internal static class BinaryExtensions +{ + + public static int ReadVarInt32(this Stream stream) + { + int result = 0; + int shift = 0; + byte byteVal; + do + { + byteVal = (byte)stream.ReadByte(); + result |= (byteVal & 0x7F) << shift; + shift += 7; + } while ((byteVal & 0x80) != 0); + return result; + } + + public static sbyte ReadVarInt7(this BinaryReader reader) => (sbyte)(reader.ReadVarInt32() & 0b11111111); + public static int ReadVarInt32(this BinaryReader reader) + { + int result = 0; + int shift = 0; + byte byteVal; + do + { + byteVal = reader.ReadByte(); + result |= (byteVal & 0x7F) << shift; + shift += 7; + } while ((byteVal & 0x80) != 0); + return result; + } + + + public static byte ReadVarUInt7(this BinaryReader reader) => (byte)(reader.ReadVarUInt32() & 0b1111111); + + public static uint ReadVarUInt32(this BinaryReader reader) + { + var result = 0u; + var shift = 0; + while (true) + { + uint value = reader.ReadByte(); + result |= ((value & 0x7F) << shift); + if ((value & 0x80) == 0) + break; + shift += 7; + } + + return result; + } + + + + public static void WriteVarInt32(this BinaryWriter writer, int value) + { + bool more = true; + while (more) + { + byte byteVal = (byte)(value & 0x7F); + value >>= 7; + more = value != 0; + if (more) + { + byteVal |= 0x80; + } + writer.Write(byteVal); + } + } +} \ No newline at end of file diff --git a/extensions/chord.common/Extensions/JsonExtensions.cs b/extensions/chord.common/Extensions/JsonExtensions.cs new file mode 100644 index 00000000..4a22dcbf --- /dev/null +++ b/extensions/chord.common/Extensions/JsonExtensions.cs @@ -0,0 +1,71 @@ +using System.Text.Json; + +namespace Chord.Common.Extensions; + + +internal static class JsonExtensions +{ + public static Dictionary ReadDictionary(this ref Utf8JsonReader reader) + where TValue : notnull + where TKey : notnull + { + if (reader.TokenType != JsonTokenType.StartObject) + { + throw new JsonException("Expected StartObject token for options."); + } + + var dict = new Dictionary(); + + while (reader.Read()) + { + if (reader.TokenType == JsonTokenType.EndObject) + { + break; + } + + if (reader.TokenType == JsonTokenType.PropertyName) + { + var key = reader.ReadValue(); + reader.Read(); // Move to the value + var value = reader.ReadValue(); + dict[key] = value; + } + } + return dict; + } + + public static TValue ReadValue(this ref Utf8JsonReader reader) where TValue : notnull + { + var boxed = (object)(typeof(TValue) switch + { + Type t when t == typeof(bool) => reader.TokenType is not JsonTokenType.True or JsonTokenType.False + ? throw new JsonException("Expected boolean") + : reader.GetBoolean(), + + Type t when t == typeof(int) => reader.TokenType != JsonTokenType.Number + ? throw new JsonException("Expected number") + : reader.GetInt32(), + + Type t when t == typeof(string) => reader.GetNonNullOrWhiteSpaceString(), + + _ => throw new JsonException($"Unexpected type {typeof(TValue)}") + }); + + if (boxed is TValue value) + return value; + + throw new JsonException($"Unexpected type {typeof(TValue)}"); + } + + public static string GetNonNullOrWhiteSpaceString(this ref Utf8JsonReader reader) + { + if (reader.TokenType != JsonTokenType.String && reader.TokenType != JsonTokenType.PropertyName) + throw new JsonException($"Expected string: {reader.TokenType}"); + + var value = reader.GetString(); + if (string.IsNullOrWhiteSpace(value)) + throw new JsonException("Expected non-null, non-whitespace string"); + + return value; + } +} \ No newline at end of file diff --git a/extensions/chord.common/Extensions/StringExtensions.cs b/extensions/chord.common/Extensions/StringExtensions.cs new file mode 100644 index 00000000..1c7aca60 --- /dev/null +++ b/extensions/chord.common/Extensions/StringExtensions.cs @@ -0,0 +1,62 @@ +namespace Chord.Common; + +internal static class StringExtensions +{ + + internal static bool IsLegalPath(this string path, out int index) + { + index = -1; + if (string.IsNullOrWhiteSpace(path)) + { + return false; + } + // Check for invalid path characters + var invalidPathChars = Path.GetInvalidPathChars(); + var invalidPathCharIndex = path.IndexOfAny(invalidPathChars); + if (invalidPathCharIndex >= 0) + { + index = invalidPathCharIndex; + return false; + } + return true; + } + + internal static bool IsLegalFilePath(this string filePath, out int index) + { + index = -1; + if (string.IsNullOrWhiteSpace(filePath)) + { + return false; + } + + // Check for invalid path characters in the entire filePath + if (!IsLegalPath(filePath, out index)) + { + return false; + } + + // Extract the file name from the path and check for invalid file name characters + var fileName = Path.GetFileName(filePath); + var invalidFileNameChars = Path.GetInvalidFileNameChars(); + var invalidFileNameCharIndex = fileName.IndexOfAny(invalidFileNameChars); + if (invalidFileNameCharIndex >= 0) + { + // Adjust the index to be in the context of the full filePath, not just fileName + index = filePath.LastIndexOf(fileName) + invalidFileNameCharIndex; + return false; + } + return true; + } + + public static void Deconstruct(this IList list, out T first, out IList rest) { + + first = list.Count > 0 ? list[0] : default(T); // or throw + rest = list.Skip(1).ToList(); + } + + public static void Deconstruct(this IList list, out T first, out T second, out IList rest) { + first = list.Count > 0 ? list[0] : default(T); // or throw + second = list.Count > 1 ? list[1] : default(T); // or throw + rest = list.Skip(2).ToList(); + } +} \ No newline at end of file diff --git a/extensions/chord.common/Internal/RegeneratingWeakReference.cs b/extensions/chord.common/Internal/RegeneratingWeakReference.cs new file mode 100644 index 00000000..16593aa0 --- /dev/null +++ b/extensions/chord.common/Internal/RegeneratingWeakReference.cs @@ -0,0 +1,20 @@ +namespace Chord.Common.Internal; +internal readonly struct RegeneratingWeakReference(Func regenerator) + where T : class +{ + private readonly WeakReference reference = new WeakReference(regenerator(), false); + private readonly Func regenerator = regenerator; + + public T Reference + { + get + { + if (!reference.TryGetTarget(out var value)) + reference.SetTarget(value = regenerator()); + + return value; + } + } + + public static implicit operator T(RegeneratingWeakReference reference) => reference.Reference; +} \ No newline at end of file diff --git a/extensions/chord.common/JsonContext.cs b/extensions/chord.common/JsonContext.cs new file mode 100644 index 00000000..324e039f --- /dev/null +++ b/extensions/chord.common/JsonContext.cs @@ -0,0 +1,43 @@ +using System.Text.Json; +using System.Text.Json.Serialization; + +namespace Chord.Common; + + +[JsonSourceGenerationOptions( + JsonSerializerDefaults.Web, + AllowTrailingCommas = true, + DefaultBufferSize = 10, + WriteIndented = true, + DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull, + PropertyNamingPolicy = JsonKnownNamingPolicy.CamelCase, + UseStringEnumConverter = true, + Converters = [typeof(ChordManifestConverter)])] +[JsonSerializable(typeof(ChordManifest))] +[JsonSerializable(typeof(ChordAuthor))] +[JsonSerializable(typeof(ChordBugTracker))] +[JsonSerializable(typeof(ChordContribution))] +[JsonSerializable(typeof(ChordDependency))] +[JsonSerializable(typeof(ChordDependency[]))] +[JsonSerializable(typeof(ChordDecorator))] +[JsonSerializable(typeof(ChordDecorator[]))] +[JsonSerializable(typeof(BuildCommand))] +[JsonSerializable(typeof(ScriptShell))] +[JsonSerializable(typeof(ChordDecoratorTargets))] +[JsonSerializable(typeof(Wasm.Types.Section))] +[JsonSerializable(typeof(Wasm.Types.Section[]))] +[JsonSerializable(typeof(Wasm.Types.ImportSection))] +[JsonSerializable(typeof(Wasm.Types.ExportSection))] +[JsonSerializable(typeof(Wasm.Types.Import))] +[JsonSerializable(typeof(Wasm.Types.Import[]))] +[JsonSerializable(typeof(Wasm.Types.Export))] +[JsonSerializable(typeof(Wasm.Types.Export[]))] +[JsonSerializable(typeof(Wasm.Types.ImportKind))] +[JsonSerializable(typeof(Wasm.Types.SectionId))] +[JsonSerializable(typeof(Wasm.Types.CustomSection))] +[JsonSerializable(typeof(Wasm.Types.CustomSection[]))] +[JsonSerializable(typeof(Wasm.Types.ExportKind))] +[JsonSerializable(typeof(Wasm.WasmModule))] +[JsonSerializable(typeof(string[]))] +[JsonSerializable(typeof(PackedFile))] +internal partial class JsonContext : JsonSerializerContext { } \ No newline at end of file diff --git a/extensions/chord.common/PackedFile.cs b/extensions/chord.common/PackedFile.cs new file mode 100644 index 00000000..4a7eb1f2 --- /dev/null +++ b/extensions/chord.common/PackedFile.cs @@ -0,0 +1,10 @@ +using System.Text.Json; + +namespace Chord.Common; + +public sealed record PackedFile(string Name, byte[] Data, string Alias) +{ + public byte[] ToArray() => JsonSerializer.SerializeToUtf8Bytes(this, JsonContext.Default.PackedFile); + public static PackedFile FromBytes(byte[] bytes) => JsonSerializer.Deserialize(bytes, JsonContext.Default.PackedFile) ?? throw new JsonException(); + public override string ToString() => JsonSerializer.Serialize(this, JsonContext.Default.PackedFile); +} \ No newline at end of file diff --git a/extensions/chord.common/Properties/AssemblyInfo.cs b/extensions/chord.common/Properties/AssemblyInfo.cs new file mode 100644 index 00000000..53616c22 --- /dev/null +++ b/extensions/chord.common/Properties/AssemblyInfo.cs @@ -0,0 +1,2 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. diff --git a/extensions/chord.common/Wasm/Types/BlockType.cs b/extensions/chord.common/Wasm/Types/BlockType.cs new file mode 100644 index 00000000..f2535809 --- /dev/null +++ b/extensions/chord.common/Wasm/Types/BlockType.cs @@ -0,0 +1,67 @@ +using Chord.Common.Wasm.Types; + +namespace Chord.Common.Wasm; +/// +/// Types for use as block signatures. +/// +public enum BlockType : sbyte +{ + /// + /// 32-bit integer value-type, equivalent to .NET's and . + /// + Int32 = -0x01, + /// + /// 64-bit integer value-type, equivalent to .NET's and . + /// + Int64 = -0x02, + /// + /// 32-bit floating point value-type, equivalent to .NET's . + /// + Float32 = -0x03, + /// + /// 64-bit floating point value-type, equivalent to .NET's . + /// + Float64 = -0x04, + /// + /// Pseudo type for representing an empty block type. + /// + Empty = -0x40, +} + +static class BlockTypeExtensions +{ + public static bool TryToValueType(this BlockType blockType, out WebAssemblyValueType valueType) + { + switch (blockType) + { + default: + case BlockType.Empty: + valueType = default; + return false; + case BlockType.Int32: + valueType = WebAssemblyValueType.Int32; + break; + case BlockType.Int64: + valueType = WebAssemblyValueType.Int64; + break; + case BlockType.Float32: + valueType = WebAssemblyValueType.Float32; + break; + case BlockType.Float64: + valueType = WebAssemblyValueType.Float64; + break; + } + + return true; + } + + public static string ToTypeString(this BlockType blockType) => blockType switch + { + BlockType.Int32 => "i32", + BlockType.Int64 => "i64", + BlockType.Float32 => "f32", + BlockType.Float64 => "f64", + BlockType.Empty => "", + _ => "?", + }; +} \ No newline at end of file diff --git a/extensions/chord.common/Wasm/Types/CustomSection.cs b/extensions/chord.common/Wasm/Types/CustomSection.cs new file mode 100644 index 00000000..44ffdb85 --- /dev/null +++ b/extensions/chord.common/Wasm/Types/CustomSection.cs @@ -0,0 +1,8 @@ +namespace Chord.Common.Wasm.Types; + +/// +/// A custom section in the wasm binary. Contains arbitrary bytes that may have meaning in certain environments. +/// +/// The name of the custom section +/// The content of the custom section +public record CustomSection(string Name, byte[] Content) : Section(SectionId.Custom, Content.Length + 1); \ No newline at end of file diff --git a/extensions/chord.common/Wasm/Types/Export.cs b/extensions/chord.common/Wasm/Types/Export.cs new file mode 100644 index 00000000..d052c3c6 --- /dev/null +++ b/extensions/chord.common/Wasm/Types/Export.cs @@ -0,0 +1,16 @@ +namespace Chord.Common.Wasm.Types; + +public sealed record Export(string Name, ExportKind Kind, uint Index) +{ + public override string ToString() + { + return $"export{Kind switch + { + ExportKind.Function => $"(func {Name} {Index})", + ExportKind.Table => $"(table {Name} {Index})", + ExportKind.Memory => $"(memory {Name} {Index})", + ExportKind.Global => $"(global {Name} {Index})", + _ => throw new ArgumentOutOfRangeException() + }}"; + } +} \ No newline at end of file diff --git a/extensions/chord.common/Wasm/Types/ExportKind.cs b/extensions/chord.common/Wasm/Types/ExportKind.cs new file mode 100644 index 00000000..2f1f4d3c --- /dev/null +++ b/extensions/chord.common/Wasm/Types/ExportKind.cs @@ -0,0 +1,24 @@ +namespace Chord.Common.Wasm.Types; + +/// +/// The kind of export +/// +public enum ExportKind : byte +{ + /// + /// A function export + /// + Function = 0, + /// + /// A table export + /// + Table = 1, + /// + /// A memory export + /// + Memory = 2, + /// + /// A global export + /// + Global = 3 +} \ No newline at end of file diff --git a/extensions/chord.common/Wasm/Types/ExportSection.cs b/extensions/chord.common/Wasm/Types/ExportSection.cs new file mode 100644 index 00000000..baad9161 --- /dev/null +++ b/extensions/chord.common/Wasm/Types/ExportSection.cs @@ -0,0 +1,3 @@ +namespace Chord.Common.Wasm.Types; + +public sealed record ExportSection(Export[] Exports) : Section(SectionId.Export, Exports.Length); \ No newline at end of file diff --git a/extensions/chord.common/Wasm/Types/FunctionSection.cs b/extensions/chord.common/Wasm/Types/FunctionSection.cs new file mode 100644 index 00000000..21f51c7d --- /dev/null +++ b/extensions/chord.common/Wasm/Types/FunctionSection.cs @@ -0,0 +1,3 @@ +namespace Chord.Common.Wasm.Types; + +public sealed record FunctionSection(uint[] TypeIndices) : Section(SectionId.Function, TypeIndices.Length); \ No newline at end of file diff --git a/extensions/chord.common/Wasm/Types/FunctionType.cs b/extensions/chord.common/Wasm/Types/FunctionType.cs new file mode 100644 index 00000000..197bb717 --- /dev/null +++ b/extensions/chord.common/Wasm/Types/FunctionType.cs @@ -0,0 +1,39 @@ +using System.Text; + +namespace Chord.Common.Wasm.Types; + +public sealed record FunctionType(FunctionKind Form, WebAssemblyValueType[] Parameters, WebAssemblyValueType[] Returns) +{ + public override string ToString() + { + var builder = new StringBuilder(); + builder.Append('('); + var nParams = Parameters.Length; + for (var i = 0; i < nParams; i++) + { + builder.Append(Parameters[i].ToTypeString()); + if (i < nParams - 1) + { + builder.Append(','); + } + } + builder.Append(") -> "); + var nReturns = Returns.Length; + if (nReturns == 0) + { + builder.Append("nil"); + } + else + { + for (var i = 0; i < nReturns; i++) + { + builder.Append(Returns[i].ToTypeString()); + if (i < nReturns - 1) + { + builder.Append(','); + } + } + } + return builder.ToString(); + } +} \ No newline at end of file diff --git a/extensions/chord.common/Wasm/Types/Import.cs b/extensions/chord.common/Wasm/Types/Import.cs new file mode 100644 index 00000000..5d0ab6df --- /dev/null +++ b/extensions/chord.common/Wasm/Types/Import.cs @@ -0,0 +1,16 @@ +namespace Chord.Common.Wasm.Types; + +public sealed record Import(string Module, string Field, ImportKind Kind, uint Index) +{ + public override string ToString() + { + return $"import{Kind switch + { + ImportKind.Function => $"(func {Module}.{Field} {Index})", + ImportKind.Table => $"(table {Module}.{Field} {Index})", + ImportKind.Memory => $"(memory {Module}.{Field} {Index})", + ImportKind.Global => $"(global {Module}.{Field} {Index})", + _ => throw new ArgumentOutOfRangeException() + }}"; + } +} \ No newline at end of file diff --git a/extensions/chord.common/Wasm/Types/ImportKind.cs b/extensions/chord.common/Wasm/Types/ImportKind.cs new file mode 100644 index 00000000..fc53d827 --- /dev/null +++ b/extensions/chord.common/Wasm/Types/ImportKind.cs @@ -0,0 +1,24 @@ +namespace Chord.Common.Wasm.Types; + +/// +/// The kind of import +/// +public enum ImportKind +{ + /// + /// A function import + /// + Function = 0, + /// + /// A table import + /// + Table = 1, + /// + /// A memory import + /// + Memory = 2, + /// + /// A global import + /// + Global = 3 +} \ No newline at end of file diff --git a/extensions/chord.common/Wasm/Types/ImportSection.cs b/extensions/chord.common/Wasm/Types/ImportSection.cs new file mode 100644 index 00000000..f8ec566e --- /dev/null +++ b/extensions/chord.common/Wasm/Types/ImportSection.cs @@ -0,0 +1,3 @@ +namespace Chord.Common.Wasm.Types; + +public sealed record ImportSection(Import[] Imports) : Section(SectionId.Import, Imports.Length); \ No newline at end of file diff --git a/extensions/chord.common/Wasm/Types/Section.cs b/extensions/chord.common/Wasm/Types/Section.cs new file mode 100644 index 00000000..0bd673dc --- /dev/null +++ b/extensions/chord.common/Wasm/Types/Section.cs @@ -0,0 +1,7 @@ +namespace Chord.Common.Wasm.Types +{ + /// + /// A section in the wasm binary + /// + public abstract record Section(SectionId Id, int Size); +} \ No newline at end of file diff --git a/extensions/chord.common/Wasm/Types/SectionId.cs b/extensions/chord.common/Wasm/Types/SectionId.cs new file mode 100644 index 00000000..15b805ad --- /dev/null +++ b/extensions/chord.common/Wasm/Types/SectionId.cs @@ -0,0 +1,65 @@ +namespace Chord.Common.Wasm.Types; + +/// +/// The standard section identifiers. +/// +public enum SectionId : byte +{ + /// + /// Custom sections have the id 0. They are intended to be used for debugging information or third-party extensions, and are ignored by the WebAssembly semantics. Their contents consist of a name further identifying the custom section, followed by an uninterpreted sequence of bytes for custom use. + /// + Custom, + /// + /// The type section has the id 1. It decodes into a vector of function types that represent the component of a module. + /// + Type, + /// + /// The import section has the id 2. It decodes into a vector of imports that represent the component of a module. + /// + Import, + /// + /// The function section has the id 3. It decodes into a vector of type indices that represent the fields of the functions in the component of a module. The and fields of the respective functions are encoded separately in the code section. + /// + Function, + /// + /// The table section has the id 4. It decodes into a vector of tables that represent the component of a module. + /// + Table, + /// + /// The memory section has the id 5. It decodes into a vector of memories that represent the component of a module. + /// + Memory, + /// + /// The global section has the id 6. It decodes into a vector of globals that represent the component of a module. + /// + Global, + /// + /// The export section has the id 7. It decodes into a vector of exports that represent the component of a module. + /// + Export, + /// + /// The start section has the id 8. It decodes into an optional start function that represents the component of a module. + /// + Start, + /// + /// The element section has the id 9. It decodes into a vector of element segments that represent the component of a module. + /// + Element, + /// + ///The code section has the id 10. It decodes into a vector of code entries that are pairs of value type vectors and expressions. They represent the and field of the functions in the component of a module. The fields of the respective functions are encoded separately in the function section. + /// + Code, + /// + /// The data section has the id 11. It decodes into a vector of data segments that represent the component of a module. + /// + Data, + /// + /// The data count section has the id 12. It decodes into an optional u32 that represents the number of data segments in the data section. If this count does not match the length of the data segment vector, the module is malformed. + /// + DataCount +} + +static class SectionExtensions +{ + public static bool IsValid(this SectionId section) => section >= SectionId.Custom && section <= SectionId.DataCount; +} \ No newline at end of file diff --git a/extensions/chord.common/Wasm/Types/TypeSection.cs b/extensions/chord.common/Wasm/Types/TypeSection.cs new file mode 100644 index 00000000..145259b0 --- /dev/null +++ b/extensions/chord.common/Wasm/Types/TypeSection.cs @@ -0,0 +1,3 @@ +namespace Chord.Common.Wasm.Types; + +public sealed record TypeSection(FunctionType[] Types) : Section(SectionId.Type, Types.Length); \ No newline at end of file diff --git a/extensions/chord.common/Wasm/Types/WebAssemblyValueType.cs b/extensions/chord.common/Wasm/Types/WebAssemblyValueType.cs new file mode 100644 index 00000000..be518f08 --- /dev/null +++ b/extensions/chord.common/Wasm/Types/WebAssemblyValueType.cs @@ -0,0 +1,69 @@ +using Chord.Common.Internal; + +namespace Chord.Common.Wasm.Types; + + +public enum FunctionKind : sbyte +{ + /// + /// A function. + /// + Function = 0x60, +} + +/// +/// Types suitable when a value is expected. +/// +public enum WebAssemblyValueType : sbyte +{ + /// + /// 32-bit integer value-type, equivalent to .NET's and . + /// + Int32 = 0x7f, + /// + /// 64-bit integer value-type, equivalent to .NET's and . + /// + Int64 = 0x7e, + /// + /// 32-bit floating point value-type, equivalent to .NET's . + /// + Float32 = 0x7d, + /// + /// 64-bit floating point value-type, equivalent to .NET's . + /// + Float64 = 0x7c, +} + +public static class ValueTypeExtensions +{ + public static System.Type ToSystemType(this WebAssemblyValueType valueType) => valueType switch + { + WebAssemblyValueType.Int32 => typeof(int), + WebAssemblyValueType.Int64 => typeof(long), + WebAssemblyValueType.Float32 => typeof(float), + WebAssemblyValueType.Float64 => typeof(double), + _ => throw new System.ArgumentOutOfRangeException(nameof(valueType), $"{nameof(WebAssemblyValueType)} {valueType} not recognized."), + }; + + public static string ToTypeString(this WebAssemblyValueType valueType) => valueType switch + { + WebAssemblyValueType.Int32 => "i32", + WebAssemblyValueType.Int64 => "i64", + WebAssemblyValueType.Float32 => "f32", + WebAssemblyValueType.Float64 => "f64", + _ => throw new System.ArgumentOutOfRangeException(nameof(valueType), $"{nameof(WebAssemblyValueType)} {valueType} not recognized."), + }; + + private static readonly RegeneratingWeakReference> systemTypeToValueType + = new(() => new Dictionary + { + { typeof(int), WebAssemblyValueType.Int32 }, + { typeof(long), WebAssemblyValueType.Int64 }, + { typeof(float), WebAssemblyValueType.Float32 }, + { typeof(double), WebAssemblyValueType.Float64 }, + }); + + public static bool TryConvertToValueType(this System.Type type, out WebAssemblyValueType value) => systemTypeToValueType.Reference.TryGetValue(type, out value); + + public static bool IsSupported(this System.Type type) => systemTypeToValueType.Reference.ContainsKey(type); +} \ No newline at end of file diff --git a/extensions/chord.common/Wasm/WasmCompiler.cs b/extensions/chord.common/Wasm/WasmCompiler.cs new file mode 100644 index 00000000..8a51bf38 --- /dev/null +++ b/extensions/chord.common/Wasm/WasmCompiler.cs @@ -0,0 +1,24 @@ +namespace Chord.Common.Wasm; + +public enum WasmCompiler +{ + None, + AssemblyScript, + TinyGo, + Javy +} + +public static class WasmCompilerExtensions +{ + public static string ToCompilerString(this WasmCompiler compiler) + { + return compiler switch + { + WasmCompiler.None => "none", + WasmCompiler.AssemblyScript => "as", + WasmCompiler.TinyGo => "tinygo", + WasmCompiler.Javy => "javy", + _ => throw new ArgumentOutOfRangeException(nameof(compiler), compiler, null) + }; + } +} \ No newline at end of file diff --git a/extensions/chord.common/Wasm/WasmModule.cs b/extensions/chord.common/Wasm/WasmModule.cs new file mode 100644 index 00000000..03610f42 --- /dev/null +++ b/extensions/chord.common/Wasm/WasmModule.cs @@ -0,0 +1,193 @@ +using System.Text.Json; +using Chord.Common.Wasm.Types; + +namespace Chord.Common.Wasm; + +/// +/// Represents a WebAssembly (WASM) module, providing functionality to manipulate custom sections. +/// +public sealed class WasmModule : IDisposable +{ + private readonly Stream _stream; + private readonly List _customSections; + private readonly ImportSection? _importSection; + private readonly ExportSection? _exportSection; + + private readonly TypeSection? _typeSection; + private readonly FunctionSection? _functionSection; + + private WasmModule(Stream stream) + { + _stream = stream ?? throw new ArgumentNullException(nameof(stream)); + using var parser = new WasmParser(stream); + _customSections = []; + foreach (var section in parser.ParseSections()) + { + if (section is CustomSection customSection) + { + _customSections.Add(customSection); + } + else if (section is ImportSection importSection) + { + _importSection = importSection; + } + else if (section is ExportSection exportSection) + { + _exportSection = exportSection; + } + else if (section is TypeSection typeSection) + { + _typeSection = typeSection; + } + else if (section is FunctionSection functionSection) + { + _functionSection = functionSection; + } + } + _stream.Seek(0, SeekOrigin.Begin); + } + + /// + /// Gets a read-only list of custom sections in the WASM module. + /// + public IReadOnlyList CustomSections => _customSections; + + /// + /// Gets the import section of the WASM module, if any. + /// + public ImportSection? ImportSection => _importSection; + + /// + /// Gets the export section of the WASM module, if any. + /// + public ExportSection? ExportSection => _exportSection; + + /// + /// Gets the type section of the WASM module, if any. + /// + public TypeSection? TypeSection => _typeSection; + + /// + /// Gets the function section of the WASM module, if any. + /// + public FunctionSection? FunctionSection => _functionSection; + + /// + /// Adds a custom section to the module. + /// + /// The custom section to add. + public void AddCustomSection(CustomSection section) + { + if (section == null) + throw new ArgumentNullException(nameof(section)); + + _customSections.Add(section); + } + + /// + /// Creates a WasmModule from a file path. + /// + /// Path to the WASM file. + /// A new WasmModule instance. + public static WasmModule FromFile(string filePath) + { + if (string.IsNullOrEmpty(filePath)) + throw new ArgumentException("File path cannot be null or empty.", nameof(filePath)); + + return new WasmModule(new FileStream(filePath, FileMode.Open, FileAccess.ReadWrite)); + } + + /// + /// Creates a WasmModule from a stream. + /// + /// Stream containing the WASM data. + /// A new WasmModule instance. + public static WasmModule FromStream(Stream stream) + { + if (stream == null) + throw new ArgumentNullException(nameof(stream)); + + return new WasmModule(stream); + } + + /// + /// Serializes the current state of the WASM module back to a byte array. + /// + /// The serialized WASM module as a byte array. + public byte[] Serialize() + { + using var writer = new WasmWriter(this, _stream); + writer.Write(); + return writer.GetBytes(); + } + + public void SerializeTo(Stream outputStream) + { + using var writer = new WasmWriter(this, _stream, outputStream); + writer.Write(); + outputStream.Flush(); + } + + /// + /// Disposes the underlying stream. + /// + public void Dispose() + { + _stream?.Dispose(); + } + + public override string ToString() + { + return JsonSerializer.Serialize(this, JsonContext.Default.WasmModule); + } + + public FunctionType? GetExportedFunctionType(Export export) + { + if (export.Kind != ExportKind.Function) + { + return null; + } + + int functionIndex = (int)export.Index; + int totalImports = ImportSection?.Imports.Count(func => func.Kind == ImportKind.Function) ?? 0; + + if (functionIndex < totalImports) + { + // Refers to an imported function + int? typeIndex = (int?)ImportSection?.Imports[functionIndex].Index; + return typeIndex.HasValue ? TypeSection?.Types[typeIndex.Value] : null; + } + else + { + // Refers to a locally defined function + int localIndex = functionIndex - totalImports; + return localIndex >= 0 && localIndex < FunctionSection?.TypeIndices.Length + ? TypeSection?.Types[(int)FunctionSection.TypeIndices[localIndex]] + : null; + } + } + + public FunctionType? GetImportedFunctionType(Import import) + { + if (import.Kind != ImportKind.Function) + { + return null; + } + + // Directly use the Index from the import entry for a function + int? typeIndex = (int?)import.Index; + + if (!typeIndex.HasValue || TypeSection == null) + { + return null; + } + + // Ensure the typeIndex is within the bounds of the TypeSection.Types array + if (typeIndex.Value >= 0 && typeIndex.Value < TypeSection.Types.Length) + { + return TypeSection.Types[typeIndex.Value]; + } + + return null; + } +} diff --git a/extensions/chord.common/Wasm/WasmParser.cs b/extensions/chord.common/Wasm/WasmParser.cs new file mode 100644 index 00000000..fa2f5c1a --- /dev/null +++ b/extensions/chord.common/Wasm/WasmParser.cs @@ -0,0 +1,150 @@ +using System.Text; +using Chord.Common.Extensions; +using Chord.Common.Wasm.Types; + +namespace Chord.Common.Wasm; + +/// +/// Parses WebAssembly (WASM) binary files and extracts custom sections. +/// +internal sealed class WasmParser : IDisposable +{ + private readonly BinaryReader _reader; + + /// + /// Initializes a new instance of the WasmParser class. + /// + /// The input stream containing the WASM binary data. + public WasmParser(Stream stream) + { + _reader = new BinaryReader(stream ?? throw new ArgumentNullException(nameof(stream)), Encoding.UTF8, true); + } + + /// + /// Parses and returns sections from the WASM file. + /// + /// An enumerable of sections in the WASM file. + public IEnumerable
ParseSections() + { + ValidateWasmHeader(); + while (_reader.BaseStream.Position != _reader.BaseStream.Length) + { + var sectionId = (SectionId)_reader.ReadVarInt32(); + if (!sectionId.IsValid()) + { + throw new InvalidDataException($"Invalid section id: {sectionId}"); + } + + int sectionSize = _reader.ReadVarInt32(); + long sectionStart = _reader.BaseStream.Position; + + if (sectionId is SectionId.Custom) + { + string name = ReadString(); + byte[] content = _reader.ReadBytes((int)(sectionSize - (_reader.BaseStream.Position - sectionStart))); + yield return new CustomSection(name, content); + } + else if (sectionId is SectionId.Import) + { + var importCount = _reader.ReadVarUInt32(); + var imports = new Import[importCount]; + for (int i = 0; i < importCount; i++) + { + var module = ReadString(); + var field = ReadString(); + var kind = (ImportKind)_reader.ReadVarUInt32(); + var index = _reader.ReadVarUInt32(); + imports[i] = new Import(module, field, kind, index); + } + yield return new ImportSection(imports); + } + else if (sectionId is SectionId.Export) + { + var exportCount = _reader.ReadVarUInt32(); + var exports = new Export[exportCount]; + for (int i = 0; i < exportCount; i++) + { + var field = ReadString(); + var kind = (ExportKind)_reader.ReadVarUInt32(); + var index = _reader.ReadVarUInt32(); + + exports[i] = new Export(field, kind, index); + } + yield return new ExportSection(exports); + } + else if (sectionId is SectionId.Type) + { + var typeCount = _reader.ReadVarUInt32(); + var types = new FunctionType[typeCount]; + for (int i = 0; i < typeCount; i++) + { + var form = (FunctionKind)_reader.ReadVarInt7(); + if (form != FunctionKind.Function) + { + throw new InvalidDataException($"Invalid function type form: {form} vs {0x60}"); + } + var paramCount = checked((int)_reader.ReadVarUInt32()); + var parameters = new WebAssemblyValueType[paramCount]; + + for (int j = 0; j < paramCount; j++) + { + var paramType = (WebAssemblyValueType)_reader.ReadVarInt7(); + parameters[j] = paramType; + } + var returnCount = checked((int)_reader.ReadVarUInt32()); + var returns = new WebAssemblyValueType[returnCount]; + for (int j = 0; j < returnCount; j++) + { + var returnType = (WebAssemblyValueType)_reader.ReadVarInt7(); + returns[j] = returnType; + } + types[i] = new FunctionType(form, parameters, returns); + } + yield return new TypeSection(types); + } + else if (sectionId is SectionId.Function) + { + var count = _reader.ReadVarUInt32(); + var TypeIndices = new uint[count]; + for (int i = 0; i < count; i++) + { + TypeIndices[i] = _reader.ReadVarUInt32(); + } + yield return new FunctionSection(TypeIndices); + } + else + { + _reader.BaseStream.Seek(sectionSize, SeekOrigin.Current); // Skip unknown sections + } + } + } + + private void ValidateWasmHeader() + { + int magicNumber = _reader.ReadInt32(); + if (magicNumber != 0x6D736100) // Equivalent to \0asm + { + throw new InvalidDataException("Invalid WASM file: Incorrect magic number."); + } + + int version = _reader.ReadInt32(); + if (version != 1) + { + throw new InvalidDataException($"Unsupported WASM version: {version}"); + } + } + + private string ReadString() + { + int length = _reader.ReadVarInt32(); + return Encoding.UTF8.GetString(_reader.ReadBytes(length)); + } + + /// + /// Disposes the underlying BinaryReader and associated resources. + /// + public void Dispose() + { + _reader?.Dispose(); + } +} diff --git a/extensions/chord.common/Wasm/WasmWriter.cs b/extensions/chord.common/Wasm/WasmWriter.cs new file mode 100644 index 00000000..9881344f --- /dev/null +++ b/extensions/chord.common/Wasm/WasmWriter.cs @@ -0,0 +1,135 @@ +using System.Text; +using Chord.Common.Extensions; +using Chord.Common.Wasm.Types; + +namespace Chord.Common.Wasm; + +/// +/// Writes WebAssembly (WASM) module data, allowing for the modification and addition of custom sections. +/// +internal sealed class WasmWriter : IDisposable +{ + private readonly Stream _inputStream; + private readonly Stream _outputStream; + private readonly BinaryWriter _writer; + private readonly WasmModule _module; + + /// + /// Initializes a new instance of the WasmWriter class. + /// + /// The WASM module to be written. + /// The input stream containing the original WASM data. + public WasmWriter(WasmModule module, Stream inputStream, Stream? outputStream = null) + { + _module = module ?? throw new ArgumentNullException(nameof(module)); + _inputStream = inputStream ?? throw new ArgumentNullException(nameof(inputStream)); + _inputStream.Seek(0, SeekOrigin.Begin); + + _outputStream = outputStream ?? new MemoryStream(); + _writer = new BinaryWriter(_outputStream, Encoding.UTF8, true); + + // Copy the first 8 bytes (magic number and version) from the input stream + byte[] header = new byte[8]; + inputStream.Read(header, 0, 8); + _writer.Write(header); + } + + /// + /// Processes sections from the input stream and writes them to the output stream, + /// including modified custom sections from the module. + /// + public void Write() + { + ProcessSections(); + } + + private void ProcessSections() + { + while (_inputStream.Position < _inputStream.Length) + { + SectionId sectionId = (SectionId)_inputStream.ReadByte(); + int sectionSize = _inputStream.ReadVarInt32(); + + if (sectionId != SectionId.Custom) // Not a custom section + { + // Write section id and size + _writer.Write((byte)sectionId); + _writer.WriteVarInt32(sectionSize); + + byte[] sectionData = new byte[sectionSize]; + _inputStream.Read(sectionData, 0, sectionSize); + _writer.Write(sectionData); + } + else + { + // Skip existing custom sections + _inputStream.Seek(sectionSize, SeekOrigin.Current); + } + } + + // Append new custom sections from the module + foreach (var customSection in _module.CustomSections) + { + WriteCustomSection(customSection); + } + } + + private void WriteCustomSection(CustomSection customSection) + { + _writer.Write((byte)SectionId.Custom); // Custom section id + + var nameLength = Encoding.UTF8.GetByteCount(customSection.Name); + int sectionSize = CalculateVarInt32Size(nameLength) + nameLength + customSection.Content.Length; + + _writer.WriteVarInt32(sectionSize); // Section size + WriteString(customSection.Name); // Section name + _writer.Write(customSection.Content); // Section content + } + + private void WriteString(string str) + { + byte[] bytes = Encoding.UTF8.GetBytes(str); + _writer.WriteVarInt32(bytes.Length); + _writer.Write(bytes); + } + + private static int CalculateVarInt32Size(int value) + { + int size = 0; + do + { + value >>= 7; + size++; + } while (value != 0); + return size; + } + + /// + /// Gets the serialized bytes from the output stream. + /// + /// The byte array containing the serialized WASM data. + public byte[] GetBytes() + { + _writer.Flush(); + if (_outputStream is MemoryStream memoryStream) + { + // If the stream is already a MemoryStream, we can just get its buffer + return memoryStream.ToArray(); + } + else + { + // Otherwise, we read the stream into a MemoryStream first + using var ms = new MemoryStream(); + _outputStream.CopyTo(ms); + return ms.ToArray(); + } + } + + /// + /// Disposes the underlying BinaryWriter and associated resources. + /// + public void Dispose() + { + _writer?.Dispose(); + } +} diff --git a/extensions/chord.common/chord.common.csproj b/extensions/chord.common/chord.common.csproj new file mode 100644 index 00000000..3264e010 --- /dev/null +++ b/extensions/chord.common/chord.common.csproj @@ -0,0 +1,19 @@ + + + + net8.0 + enable + enable + true + Chord.Common + true + preview + true + true + + + + + + + diff --git a/extensions/chord.runtime/Extension.cs b/extensions/chord.runtime/Extension.cs new file mode 100644 index 00000000..83648d44 --- /dev/null +++ b/extensions/chord.runtime/Extension.cs @@ -0,0 +1,190 @@ +using System.Collections.Frozen; +using System.Collections.ObjectModel; +using System.Text; +using Chord.Common; +using Chord.Runtime.Internal.Callers; +using Chord.Runtime.Internal.Linkers; +using Wasmtime; + +namespace Chord.Runtime; + +/// +/// Represents a loaded extension within the Chord runtime. +/// +public sealed class Extension : IDisposable +{ + private readonly Module _module; + private readonly WasmLinker _linker; + private readonly Store _store; + private readonly WasmCaller _caller; + private readonly ChordManifest _manifest; + private readonly List _packedFiles; + + private readonly FileStream _standardInput; + private readonly FileStream _standardOutput; + private readonly FileStream _standardError; + + /// + /// Initializes a new instance of the class. + /// + /// The WebAssembly module. + /// The linker for the WebAssembly module. + /// The store containing module data. + /// The caller for the WebAssembly module. + /// The manifest of the extension. + /// A list of packed files associated with the extension. + internal Extension(Module module, WasmLinker linker, Store store, WasmCaller caller, ChordManifest manifest, List packedFiles, FileStream standardInput, FileStream standardOutput, FileStream standardError) + { + _module = module; + _linker = linker; + _store = store; + _caller = caller; + _manifest = manifest; + _packedFiles = packedFiles; + _standardInput = standardInput; + _standardOutput = standardOutput; + _standardError = standardError; + // update the store caller + _store.SetData(this); + } + + /// + /// Gets the manifest of the extension. + /// + public ChordManifest Manifest => _manifest; + + /// + /// Gets the packed files associated with the extension. + /// + public ReadOnlyCollection PackedFiles => new(_packedFiles); + + /// + /// Gets the name of the extension. + /// + public string Name => _manifest.Name; + + /// + /// Gets the version of the extension. + /// + public string Version => _manifest.Version.ToString(); + + /// + /// Gets the description of the extension. + /// + public string Description => _manifest.Description; + + /// + /// Gets the type of contribution made by the extension. + /// + public ContributionType Type => _manifest.Contributions.Type; + + /// + /// Gets the decorators defined in the extension. + /// + public ReadOnlyCollection Decorators + { + get + { + if (_manifest.Contributions.Decorators is null) + { + return new([]); + } + return new(_manifest.Contributions.Decorators); + } + } + + public async ValueTask WriteStandardInput(string input, CancellationToken cancellationToken, Encoding? encoding = default) + { + encoding ??= Encoding.UTF8; + if (_standardInput.CanWrite) + { + using var streamWriter = new StreamWriter(_standardInput, encoding, leaveOpen: true); + var memory = new ReadOnlyMemory(input.ToCharArray()); + await streamWriter.WriteAsync(memory, cancellationToken: cancellationToken); + await streamWriter.FlushAsync(); + } + } + + public async ValueTask ClearStandardInput(CancellationToken cancellationToken) + { + if (_standardInput.CanWrite) + { + await _standardInput.FlushAsync(cancellationToken); + // truncate + _standardInput.SetLength(0); + } + } + + public async ValueTask ReadStandardOutput(CancellationToken cancellationToken, Encoding? encoding = default) + { + encoding ??= Encoding.UTF8; + if (_standardOutput.CanRead) + { + using var streamReader = new StreamReader(_standardOutput, encoding, leaveOpen: true); + return await streamReader.ReadToEndAsync(cancellationToken); + } + return null; + } + + public async ValueTask ClearStandardOutput(CancellationToken cancellationToken) + { + if (_standardOutput.CanWrite) + { + await _standardOutput.FlushAsync(cancellationToken); + // truncate + _standardOutput.SetLength(0); + } + } + + public async ValueTask ReadStandardError(CancellationToken cancellationToken, Encoding? encoding = default) + { + encoding ??= Encoding.UTF8; + if (_standardError.CanRead) + { + using var streamReader = new StreamReader(_standardError, encoding, leaveOpen: true); + return await streamReader.ReadToEndAsync(cancellationToken); + } + return null; + } + + public async ValueTask ClearStandardError(CancellationToken cancellationToken) + { + if (_standardError.CanWrite) + { + await _standardError.FlushAsync(cancellationToken); + // truncate + _standardError.SetLength(0); + } + } + + /// + /// Gets a simplified view of the contributions made by the extension, excluding decorators. + /// + public ChordContribution Contributions => _manifest.Contributions with { Decorators = null }; + + /// + /// Compiles a given context using the extension, if it is a generator type. + /// + /// The context to compile. + /// The result of the compilation. + /// Thrown if the extension is not a generator type. + public async ValueTask ChordCompileAsync(string context, CancellationToken cancellationToken = default) + { + if (Type is not ContributionType.Generator) + { + throw new ExtensionRuntimeException("Attempted to call chord_compile on a non-generator extension."); + } + return await _caller.ChordCompileAsync(context, cancellationToken); + } + + public void Dispose() + { + _module.Dispose(); + _linker.Dispose(); + _store.Dispose(); + _standardInput.Dispose(); + _standardOutput.Dispose(); + _standardError.Dispose(); + _packedFiles.Clear(); + } +} \ No newline at end of file diff --git a/extensions/chord.runtime/ExtensionException.cs b/extensions/chord.runtime/ExtensionException.cs new file mode 100644 index 00000000..833a818f --- /dev/null +++ b/extensions/chord.runtime/ExtensionException.cs @@ -0,0 +1,24 @@ +namespace Chord.Runtime; + +/// +/// Represents errors that occur during extension processing in the Chord runtime. +/// +public sealed class ExtensionException : Exception +{ + /// + /// Initializes a new instance of the class with a specified error message. + /// + /// The message that describes the error. + internal ExtensionException(string message) : base(message) + { + } + + /// + /// Initializes a new instance of the class with a specified error message and a reference to the inner exception that is the cause of this exception. + /// + /// The error message that explains the reason for the exception. + /// The exception that is the cause of the current exception, or a null reference if no inner exception is specified. + internal ExtensionException(string message, Exception innerException) : base(message, innerException) + { + } +} \ No newline at end of file diff --git a/extensions/chord.runtime/ExtensionRuntime.cs b/extensions/chord.runtime/ExtensionRuntime.cs new file mode 100644 index 00000000..44247afa --- /dev/null +++ b/extensions/chord.runtime/ExtensionRuntime.cs @@ -0,0 +1,242 @@ +using System.Collections.ObjectModel; +using Chord.Common; +using Chord.Common.Wasm; +using Chord.Runtime.Internal.Callers; +using Chord.Runtime.Internal.Linkers; +using Chord.Runtime.Internal.Strings; +using Semver; +using Spectre.Console; +using Wasmtime; + +namespace Chord.Runtime +{ + /// + /// Represents a runtime for managing and executing Chord/WASM extensions. + /// + public sealed class ExtensionRuntime : IDisposable + { + private static readonly string[] _initMethods = ["_initialize", "_start"]; + private readonly Engine _engine; + private readonly List _extensions; + private readonly SemVersion _engineVersion; + + /// + /// Initializes a new instance of the class. + /// + /// The version of the engine (bebopc) that is hosting the runtime. + /// Standard output console. + /// Standard error console. + public ExtensionRuntime(string engineVersion, IAnsiConsole standardOut, IAnsiConsole standardError) + { + _engine = new Engine(new Config() + .WithReferenceTypes(false) + .WithDebugInfo(false) + .WithCraneliftDebugVerifier(false) + .WithCompilerStrategy(CompilerStrategy.Auto) + .WithBulkMemory(true) + .WithMultiMemory(true) + .WithSIMD(true) + .WithWasmThreads(true) + .WithOptimizationLevel(OptimizationLevel.None) + .WithEpochInterruption(false) + + ); + _extensions = []; + _engineVersion = SemVersion.Parse(engineVersion, SemVersionStyles.Strict); + StandardOut = standardOut; + StandardError = standardError; + } + + /// + /// Loads and initializes an extension by its name and version. + /// + /// + /// The extension must be installed in the default extension storage path. + /// + /// The name of the extension. + /// The version of the extension. + /// The loaded extension. + /// Thrown when the extension cannot be loaded or initialized. + /// Thrown when wasmtime fails. + public Extension LoadExtension(string extensionName, string extensionVersion) + { + var modulePath = Path.Combine(StoragePath.BebopcData, extensionName, extensionVersion, "chord.wasm"); + if (!File.Exists(modulePath)) + { + throw new ExtensionException($"Could not find extension {extensionName}@{extensionVersion} - is it installed?"); + } + try + { + return LoadExtension(modulePath); + } + catch (WasmtimeException e) + { + throw new ExtensionException($"Could not load extension {extensionName}@{extensionVersion}", e); + } + } + + + /// + /// Loads and initializes an extension by its path. + /// + /// The path to the extension. + /// The loaded extension. + /// Thrown when the extension cannot be loaded or initialized. + /// Thrown when wasmtime fails. + public Extension LoadExtension(string modulePath) + { + try + { + using var chordModule = WasmModule.FromFile(modulePath); + var manifestSection = chordModule.CustomSections.FirstOrDefault(x => x.Name == "chord_manifest") + ?? throw new ExtensionException("Could not find 'chord_manifest' section in extension"); + + var manifest = ChordManifest.FromBytes(manifestSection.Content); + + if (!_engineVersion.Satisfies(manifest.Engine.Bebopc)) + { + throw new ExtensionException($"Extension requires bebopc version '{manifest.Engine.Bebopc}' but runtime is '{_engineVersion}'"); + } + + var packedFiles = GetPackedFiles(manifest, chordModule); + + StringMarshaler marshaler = CreateMarshaler(manifest.Build.Compiler); + + var store = new Store(_engine); + + var standardOut = new FileStream(Path.Combine(Path.GetTempPath(), Path.GetRandomFileName()), FileMode.Create, FileAccess.ReadWrite, FileShare.ReadWrite | FileShare.Delete, 4096, FileOptions.DeleteOnClose); + var standardIn = new FileStream(Path.Combine(Path.GetTempPath(), Path.GetRandomFileName()), FileMode.Create, FileAccess.ReadWrite, FileShare.ReadWrite | FileShare.Delete, 4096, FileOptions.DeleteOnClose); + var standardErr = new FileStream(Path.Combine(Path.GetTempPath(), Path.GetRandomFileName()), FileMode.Create, FileAccess.ReadWrite, FileShare.ReadWrite | FileShare.Delete, 4096, FileOptions.DeleteOnClose); + + + store.SetWasiConfiguration(new WasiConfiguration().WithStandardInput(standardIn.Name).WithStandardOutput(standardOut.Name).WithStandardError(standardErr.Name)); + + WasmLinker linker = CreateLinker(manifest.Build.Compiler, store, marshaler, manifest.Contributions.Type); + linker.DefineKernel(); + var module = Module.FromFile(_engine, modulePath); + + var wasmInstance = InitializeWasmInstance(linker, store, module, marshaler, manifest.Build.Compiler); + + var wasmCaller = CreateCaller(manifest.Build.Compiler, wasmInstance, marshaler); + var extension = new Extension(module, linker, store, wasmCaller, manifest, packedFiles, standardIn, standardOut, standardErr); + if (manifest.Build.Compiler is WasmCompiler.Javy) + { + wasmCaller.SetExtension(extension); + } + _extensions.Add(extension); + return extension; + } + catch (WasmtimeException e) + { + throw new ExtensionRuntimeException($"Could not load extension @ {modulePath}", e); + } + } + + private static Instance InitializeWasmInstance(WasmLinker linker, Store store, Module module, StringMarshaler marshaler, WasmCompiler compiler) + { + var wasmInstance = linker.Instantiate(store, module); + marshaler.Bind(wasmInstance); + // if the compiler is javy, calling _start breaks other methods + if (compiler is WasmCompiler.Javy) + { + return wasmInstance; + } + bool methodInvoked = false; + foreach (var methodName in _initMethods) + { + var method = wasmInstance.GetFunction(methodName); + if (method != null) + { + method.Invoke(); + methodInvoked = true; + break; + } + } + + if (!methodInvoked) + { + throw new ExtensionException("No _initialize or _start method found"); + } + return wasmInstance; + } + + private static List GetPackedFiles(ChordManifest manifest, WasmModule chordModule) + { + var packedFiles = new List(); + if (manifest.Pack != null) + { + foreach (var (alias, pack) in manifest.Pack) + { + var packedFileSection = chordModule.CustomSections.FirstOrDefault(x => x.Name == $"chord_{alias}") + ?? throw new ExtensionException($"Could not find expected packed file '{alias}' in extension"); + packedFiles.Add(PackedFile.FromBytes(packedFileSection.Content)); + } + } + + return packedFiles; + } + + private static StringMarshaler CreateMarshaler(WasmCompiler compiler) + { + return compiler switch + { + WasmCompiler.TinyGo => new TinyGoMarshaler(), + WasmCompiler.AssemblyScript => new AssemblyScriptMarshaler(), + WasmCompiler.Javy => new JavyMarshaler(), + _ => throw new ExtensionException("Unable to determine marshaler.") + }; + } + + private WasmLinker CreateLinker(WasmCompiler compiler, Store store, StringMarshaler marshaler, ContributionType contributionType) + { + return compiler switch + { + WasmCompiler.TinyGo => new TinyGoLinker(_engine, store, marshaler, this, contributionType), + WasmCompiler.AssemblyScript => new AssemblyScriptLinker(_engine, store, marshaler, this, contributionType), + WasmCompiler.Javy => new JavyLinker(_engine, store, marshaler, this, contributionType), + _ => throw new ExtensionException("Unable to determine linker.") + }; + } + + private WasmCaller CreateCaller(WasmCompiler compiler, Instance wasmInstance, StringMarshaler marshaler) + { + return compiler switch + { + WasmCompiler.TinyGo => new TinyGoCaller(wasmInstance, marshaler, this), + WasmCompiler.AssemblyScript => new AssemblyScriptCaller(wasmInstance, marshaler, this), + WasmCompiler.Javy => new JavyCaller(wasmInstance, marshaler, this), + _ => throw new ExtensionException("Unable to determine caller.") + }; + } + + /// + /// Gets the version of the engine (bebopc) hosting this runtime. + /// + public string EngineVersion => _engineVersion.ToString(); + /// + /// Gets the standard output console. + /// + public IAnsiConsole StandardOut { get; } + /// + /// Gets the standard error console. + /// + public IAnsiConsole StandardError { get; } + /// + /// Gets the loaded extensions. + /// + public ReadOnlyCollection Extensions => _extensions.AsReadOnly(); + + /// + /// Disposes of the runtime and all loaded extensions. + /// + public void Dispose() + { + _engine.Dispose(); + foreach (var extension in _extensions) + { + extension.Dispose(); + } + _extensions.Clear(); + } + } +} diff --git a/extensions/chord.runtime/ExtensionRuntimeException.cs b/extensions/chord.runtime/ExtensionRuntimeException.cs new file mode 100644 index 00000000..04d2ffc4 --- /dev/null +++ b/extensions/chord.runtime/ExtensionRuntimeException.cs @@ -0,0 +1,24 @@ +namespace Chord.Runtime; + +/// +/// Represents runtime errors that occur during the execution of an extension in the Chord runtime. +/// +public sealed class ExtensionRuntimeException : Exception +{ + /// + /// Initializes a new instance of the class with a specified error message. + /// + /// The message that describes the error. + internal ExtensionRuntimeException(string message) : base(message) + { + } + + /// + /// Initializes a new instance of the class with a specified error message and a reference to the inner exception that is the cause of this exception. + /// + /// The error message that explains the reason for the exception. + /// The exception that is the cause of the current exception, or a null reference if no inner exception is specified. + internal ExtensionRuntimeException(string message, Exception innerException) : base(message, innerException) + { + } +} \ No newline at end of file diff --git a/extensions/chord.runtime/Internal/Callers/AssemblyScriptCaller.cs b/extensions/chord.runtime/Internal/Callers/AssemblyScriptCaller.cs new file mode 100644 index 00000000..d10b0382 --- /dev/null +++ b/extensions/chord.runtime/Internal/Callers/AssemblyScriptCaller.cs @@ -0,0 +1,29 @@ +using Chord.Runtime.Internal.Strings; +using Wasmtime; + +namespace Chord.Runtime.Internal.Callers; + +internal sealed class AssemblyScriptCaller : WasmCaller +{ + internal AssemblyScriptCaller(Instance wasmInstance, StringMarshaler stringMarshaler, ExtensionRuntime runtime) : base(wasmInstance, stringMarshaler, runtime) + { + } + + public override ValueTask ChordCompileAsync(string context, CancellationToken cancellationToken = default) + { + ArgumentException.ThrowIfNullOrWhiteSpace(context, nameof(context)); + var compile = _instance.GetFunction("chord_compile"); + if (compile == null) + { + throw new ExtensionRuntimeException("chord_compile function not found"); + } + using var contextString = _stringMarshaler.CreateString(context); + var returnAddress = compile.Invoke(contextString.Address); + return ValueTask.FromResult(_stringMarshaler.ReadString(returnAddress)); + } + + public override void SetExtension(Extension extension) + { + throw new NotImplementedException(); + } +} \ No newline at end of file diff --git a/extensions/chord.runtime/Internal/Callers/JavyCaller.cs b/extensions/chord.runtime/Internal/Callers/JavyCaller.cs new file mode 100644 index 00000000..da3357bb --- /dev/null +++ b/extensions/chord.runtime/Internal/Callers/JavyCaller.cs @@ -0,0 +1,54 @@ +using Chord.Runtime.Internal.Strings; +using Spectre.Console; +using Wasmtime; + +namespace Chord.Runtime.Internal.Callers; + +internal sealed class JavyCaller : WasmCaller +{ + private Extension? _extension; + + internal JavyCaller(Instance wasmInstance, StringMarshaler stringMarshaler, ExtensionRuntime runtime) : base(wasmInstance, stringMarshaler, runtime) + { + + } + + public override void SetExtension(Extension extension) + { + _extension = extension; + } + + public override async ValueTask ChordCompileAsync(string context, CancellationToken cancellationToken) + { + ArgumentException.ThrowIfNullOrWhiteSpace(context, nameof(context)); + + var compile = _instance.GetAction("chord-compile"); + if (compile == null) + { + throw new ExtensionRuntimeException("chord-compile function not found"); + } + if (_extension == null) + { + throw new ExtensionRuntimeException("Extension not set"); + } + await _extension.ClearStandardOutput(cancellationToken); + await _extension.ClearStandardError(cancellationToken); + await _extension.ClearStandardInput(cancellationToken); + + await _extension.WriteStandardInput(context, cancellationToken); + + compile(); + + var result = await _extension.ReadStandardOutput(cancellationToken); + var standardError = await _extension.ReadStandardError(cancellationToken); + if (!string.IsNullOrWhiteSpace(standardError)) + { + _runtime.StandardError.MarkupLineInterpolated($"{standardError}"); + } + if (string.IsNullOrWhiteSpace(result)) + { + throw new ExtensionRuntimeException("chord-compile function did not return a result"); + } + return result; + } +} \ No newline at end of file diff --git a/extensions/chord.runtime/Internal/Callers/TinyGoCaller.cs b/extensions/chord.runtime/Internal/Callers/TinyGoCaller.cs new file mode 100644 index 00000000..e61b2bb3 --- /dev/null +++ b/extensions/chord.runtime/Internal/Callers/TinyGoCaller.cs @@ -0,0 +1,31 @@ +using Chord.Runtime.Internal.Strings; +using Wasmtime; + +namespace Chord.Runtime.Internal.Callers; + +internal sealed class TinyGoCaller : WasmCaller +{ + internal TinyGoCaller(Instance wasmInstance, StringMarshaler stringMarshaler, ExtensionRuntime runtime) : base(wasmInstance, stringMarshaler, runtime) + { + } + + public override ValueTask ChordCompileAsync(string context, CancellationToken cancellationToken = default) + { + ArgumentException.ThrowIfNullOrWhiteSpace(context, nameof(context)); + var compile = _instance.GetAction("chord_compile"); + if (compile == null) + { + throw new ExtensionRuntimeException("chord_compile function not found"); + } + + var contextString = _stringMarshaler.CreateString(context); + var returnString = _stringMarshaler.CreateString(); + compile(returnString.Address, contextString.Address, contextString.Length); + return ValueTask.FromResult(returnString.Value); + } + + public override void SetExtension(Extension extension) + { + throw new NotImplementedException(); + } +} \ No newline at end of file diff --git a/extensions/chord.runtime/Internal/Callers/WasmCaller.cs b/extensions/chord.runtime/Internal/Callers/WasmCaller.cs new file mode 100644 index 00000000..5d82010c --- /dev/null +++ b/extensions/chord.runtime/Internal/Callers/WasmCaller.cs @@ -0,0 +1,43 @@ +using Chord.Common; +using Chord.Runtime.Internal.Strings; +using Wasmtime; + +namespace Chord.Runtime.Internal.Callers; + +/// +/// Represents an abstract base class for handling calls to WebAssembly modules within the Chord runtime. +/// This class provides common functionalities and serves as a base for specific WebAssembly calling implementations. +/// +internal abstract class WasmCaller +{ + protected readonly StringMarshaler _stringMarshaler; + protected readonly Instance _instance; + protected readonly ExtensionRuntime _runtime; + + /// + /// Initializes a new instance of the class. + /// + /// The WebAssembly instance associated with this caller. + /// The string marshaler for managing string operations in WebAssembly memory. + /// The runtime context in which the caller operates. + public WasmCaller(Instance wasmInstance, StringMarshaler stringMarshaler, ExtensionRuntime runtime) + { + _stringMarshaler = stringMarshaler; + _instance = wasmInstance; + _runtime = runtime; + } + + /// + /// Abstract method to compile a given context using the WebAssembly module. + /// Implementing classes should define the specific logic for compilation based on the module and runtime context. + /// + /// The context to be compiled by the WebAssembly module. + /// The result of the compilation as a string. + public abstract ValueTask ChordCompileAsync(string context, CancellationToken cancellationToken = default); + + /// + /// Sets the extension associated with this caller. + /// + /// the extension + public abstract void SetExtension(Extension extension); +} \ No newline at end of file diff --git a/extensions/chord.runtime/Internal/Linkers/AssemblyScriptLinker.cs b/extensions/chord.runtime/Internal/Linkers/AssemblyScriptLinker.cs new file mode 100644 index 00000000..51042b1b --- /dev/null +++ b/extensions/chord.runtime/Internal/Linkers/AssemblyScriptLinker.cs @@ -0,0 +1,34 @@ +using Chord.Common; +using Chord.Runtime.Internal.Strings; +using Spectre.Console; +using Wasmtime; + +namespace Chord.Runtime.Internal.Linkers; + +/// +/// A specialized linker for AssemblyScript WebAssembly modules used in the Chord runtime. +/// It defines kernel functions specific to AssemblyScript and links them to the corresponding WebAssembly module. +/// +internal sealed class AssemblyScriptLinker(Engine engine, Store store, StringMarshaler stringMarshaler, ExtensionRuntime runtime, ContributionType contributionType) : WasmLinker(engine, store, stringMarshaler, runtime, contributionType) +{ + private const string moduleName = "chord_as"; + public override void DefineKernel() + { + Define(moduleName, "write_line", Function.FromCallback(_store, (int address) => + { + var line = _stringMarshaler.ReadString(address); + _runtime.StandardOut.MarkupLine(line); + })); + Define(moduleName, "write_error", Function.FromCallback(_store, (int address) => + { + var line = _stringMarshaler.ReadString(address); + _runtime.StandardError.MarkupLine(line); + })); + Define(moduleName, "get_bebopc_version", Function.FromCallback(_store, () => + { + // don't dispose or assemblyscript will read garbage + var wasmString = _stringMarshaler.CreateString(_runtime.EngineVersion); + return wasmString.Address; + })); + } +} \ No newline at end of file diff --git a/extensions/chord.runtime/Internal/Linkers/JavyLinker.cs b/extensions/chord.runtime/Internal/Linkers/JavyLinker.cs new file mode 100644 index 00000000..9c2b2f7f --- /dev/null +++ b/extensions/chord.runtime/Internal/Linkers/JavyLinker.cs @@ -0,0 +1,19 @@ +using Chord.Common; +using Chord.Runtime.Internal.Strings; +using Spectre.Console; +using Wasmtime; + +namespace Chord.Runtime.Internal.Linkers; + +/// +/// A specialized linker for Javy WebAssembly modules used in the Chord runtime. +/// It defines kernel functions specific to Javy and links them to the corresponding WebAssembly module. +/// +internal sealed class JavyLinker(Engine engine, Store store, StringMarshaler stringMarshaler, ExtensionRuntime runtime, ContributionType contributionType) : WasmLinker(engine, store, stringMarshaler, runtime, contributionType) +{ + private const string moduleName = "chord_javy"; + public override void DefineKernel() + { + + } +} \ No newline at end of file diff --git a/extensions/chord.runtime/Internal/Linkers/TinyGoLinker.cs b/extensions/chord.runtime/Internal/Linkers/TinyGoLinker.cs new file mode 100644 index 00000000..a4bb916d --- /dev/null +++ b/extensions/chord.runtime/Internal/Linkers/TinyGoLinker.cs @@ -0,0 +1,35 @@ +using Chord.Common; +using Chord.Runtime.Internal.Strings; +using Spectre.Console; +using Wasmtime; + +namespace Chord.Runtime.Internal.Linkers; + +/// +/// A specialized linker for TinyGo WebAssembly modules used in the Chord runtime. +/// It defines kernel functions specific to TinyGo and links them to the corresponding WebAssembly module. +/// +internal sealed class TinyGoLinker(Engine engine, Store store, StringMarshaler stringMarshaler, ExtensionRuntime runtime, ContributionType contributionType) : WasmLinker(engine, store, stringMarshaler, runtime, contributionType) +{ + /// + /// Defines kernel functions specific to TinyGo, linking them to the WebAssembly module. + /// This includes functions for standard output and error, as well as getting version information. + /// + public override void DefineKernel() + { + Define("chord_tinygo", "write_line", Function.FromCallback(_store, (int address, int length) => + { + var line = _stringMarshaler.ReadString(address, length); + _runtime.StandardOut.MarkupLine(line); + })); + Define("chord_tinygo", "write_error", Function.FromCallback(_store, (int address, int length) => + { + var line = _stringMarshaler.ReadString(address, length); + _runtime.StandardError.MarkupLine(line); + })); + Define("chord_tinygo", "get_bebopc_version", Function.FromCallback(_store, (int address) => + { + _stringMarshaler.WriteString(address, _runtime.EngineVersion); + })); + } +} \ No newline at end of file diff --git a/extensions/chord.runtime/Internal/Linkers/WasmLinker.cs b/extensions/chord.runtime/Internal/Linkers/WasmLinker.cs new file mode 100644 index 00000000..40881278 --- /dev/null +++ b/extensions/chord.runtime/Internal/Linkers/WasmLinker.cs @@ -0,0 +1,41 @@ +using Chord.Common; +using Chord.Runtime.Internal.Strings; +using Wasmtime; + +namespace Chord.Runtime.Internal.Linkers; + +/// +/// Represents an abstract base class for WebAssembly linkers used in the Chord runtime. +/// This class provides common functionalities for different types of Wasm linkers. +/// +internal abstract class WasmLinker : Linker +{ + private protected Store _store; + protected readonly StringMarshaler _stringMarshaler; + protected readonly ExtensionRuntime _runtime; + protected readonly ContributionType _contributionType; + + /// + /// Initializes a new instance of the class. + /// + /// The WebAssembly engine used for module instantiation. + /// The store representing the WebAssembly runtime state. + /// The string marshaler for managing string operations in WebAssembly memory. + /// The runtime context in which the linker operates. + /// The type of contribution the linker is intended to support. + protected WasmLinker(Engine engine, Store store, StringMarshaler stringMarshaler, ExtensionRuntime runtime, ContributionType contributionType) : base(engine) + { + _store = store; + _stringMarshaler = stringMarshaler; + _runtime = runtime; + _contributionType = contributionType; + DefineWasi(); + } + + /// + /// Defines the kernel functions and environment for the WebAssembly modules. + /// Each derived linker class should implement this method to set up the specific + /// functions and environment needed for the module it supports. + /// + public abstract void DefineKernel(); +} \ No newline at end of file diff --git a/extensions/chord.runtime/Internal/Strings/AssemblyScriptMarshaler.cs b/extensions/chord.runtime/Internal/Strings/AssemblyScriptMarshaler.cs new file mode 100644 index 00000000..2ee6b767 --- /dev/null +++ b/extensions/chord.runtime/Internal/Strings/AssemblyScriptMarshaler.cs @@ -0,0 +1,111 @@ +using System.Text; +using Chord.Common.Wasm; +using Wasmtime; + +namespace Chord.Runtime.Internal.Strings; + +internal sealed class AssemblyScriptMarshaler : StringMarshaler +{ + private Memory? _memory; + private Func? _new; + private Func? _pin; + private Action? _unpin; + private Action? _collect; + + public AssemblyScriptMarshaler() : base(WasmCompiler.AssemblyScript) + { + + } + + public override void Bind(Instance wasmInstance) + { + _wasmInstance = wasmInstance; + _memory = wasmInstance.GetMemory("memory") ?? throw new ExtensionRuntimeException("Could not find the 'memory' export. Was the marshaler bound?"); + _new = wasmInstance.GetFunction("__new") ?? throw new ExtensionRuntimeException("Could not find __new function"); + _pin = wasmInstance.GetFunction("__pin") ?? throw new ExtensionRuntimeException("Could not find __pin function"); + _unpin = wasmInstance.GetAction("__unpin") ?? throw new ExtensionRuntimeException("Could not find __unpin function"); + _collect = wasmInstance.GetAction("__collect") ?? throw new ExtensionRuntimeException("Could not find __collect function"); + } + + /// + /// Creates a string in the WebAssembly memory context and returns a WasmString representing it. + /// + /// The string value to be stored in WebAssembly memory. + /// The encoding to use. If null, Unicode (UTF-16) encoding will be used. + /// A WasmString instance representing the newly created string in WebAssembly memory. + /// Thrown if the provided string value is null or whitespace. + /// Thrown if the marshaler has not been properly bound with a WebAssembly instance. + public override WasmString CreateString(string value, Encoding? encoding = null) + { + //https://www.assemblyscript.org/runtime.html#memory-layout + ArgumentException.ThrowIfNullOrWhiteSpace(value, nameof(value)); + if (_new is null || _pin is null || _unpin is null || _collect is null || _memory is null) + throw new ExtensionRuntimeException("Marshaler not bound"); + + encoding ??= Encoding.Unicode; + const int OBJECT_ID_STRING = 2; + var stringLength = encoding.GetByteCount(value); + var stringAddress = _new.Invoke(stringLength, OBJECT_ID_STRING); + var pinned = _pin.Invoke(stringAddress); + encoding.GetBytes(value, _memory.GetSpan(pinned, stringLength)); + _memory.WriteInt32(pinned - 4, stringLength); + return new WasmString(this, pinned, stringLength, value); + } + + public override WasmString CreateString() + { + throw new NotImplementedException(); + } + + public override void FreeString(WasmString value) + { + if (_unpin is null || _collect is null) + throw new ExtensionRuntimeException("Marshaler not bound"); + _unpin.Invoke(value.Address); + _collect.Invoke(); + } + + /// + /// Not implemented. AssemblyScript strings never provide a length when marshaled. + /// + /// + /// + /// + /// + /// + public override string ReadString(int address, int size, Encoding? encoding = null) + { + throw new NotImplementedException(); + } + + /// + /// Reads a string from the specified address in WebAssembly memory using the AssemblyScript string layout. + /// + /// + /// In the AssemblyScript memory layout for strings, the string length is stored at an offset of -4 bytes + /// from the start address of the string. This method first reads the length of the string from this offset + /// and then retrieves the string data starting from the provided address. + /// + /// It's important to note that AssemblyScript and other WebAssembly languages may have different + /// memory layouts for strings, so the correct handling of these layouts is crucial for accurately + /// reading string data from memory. + /// + /// The memory address where the string starts in WebAssembly memory. + /// The encoding used for the string data. Defaults to Unicode (UTF-16) if null. + /// The string read from memory. + /// Thrown if the marshaler is not properly bound to a WebAssembly instance. + public override string ReadString(int address, Encoding? encoding = null) + { + if (_memory is null) + throw new ExtensionRuntimeException("Marshaler not bound"); + encoding ??= Encoding.Unicode; + // The byte length of the string is at offset -4 in AssemblyScript string layout. + var length = _memory.ReadInt32(address - 4); + return encoding.GetString(_memory.GetSpan(address, length)); + } + + public override void WriteString(int address, string value, Encoding? encoding = null) + { + throw new NotImplementedException(); + } +} \ No newline at end of file diff --git a/extensions/chord.runtime/Internal/Strings/JavyMarshaler.cs b/extensions/chord.runtime/Internal/Strings/JavyMarshaler.cs new file mode 100644 index 00000000..e8730a8d --- /dev/null +++ b/extensions/chord.runtime/Internal/Strings/JavyMarshaler.cs @@ -0,0 +1,47 @@ +using System.Text; +using Chord.Common.Wasm; +using Wasmtime; + +namespace Chord.Runtime.Internal.Strings; + +internal sealed class JavyMarshaler : StringMarshaler +{ + public JavyMarshaler() : base(WasmCompiler.Javy) + { + } + + public override void Bind(Instance wasmInstance) + { + //NOOP + } + + public override WasmString CreateString(string value, Encoding? encoding = null) + { + throw new NotImplementedException(); + } + + public override WasmString CreateString() + { + throw new NotImplementedException(); + } + + public override void FreeString(WasmString value) + { + throw new NotImplementedException(); + } + + public override string ReadString(int address, Encoding? encoding = null) + { + throw new NotImplementedException(); + } + + public override string ReadString(int address, int size, Encoding? encoding = null) + { + throw new NotImplementedException(); + } + + public override void WriteString(int address, string value, Encoding? encoding = null) + { + throw new NotImplementedException(); + } +} \ No newline at end of file diff --git a/extensions/chord.runtime/Internal/Strings/StringMarshaler.cs b/extensions/chord.runtime/Internal/Strings/StringMarshaler.cs new file mode 100644 index 00000000..1cc51df6 --- /dev/null +++ b/extensions/chord.runtime/Internal/Strings/StringMarshaler.cs @@ -0,0 +1,29 @@ +using System.Text; +using Chord.Common.Wasm; +using Wasmtime; + +namespace Chord.Runtime.Internal.Strings; + +internal abstract class StringMarshaler +{ + protected Instance? _wasmInstance; + protected readonly WasmCompiler _compiler; + public bool IsBound => _wasmInstance != null; + + public StringMarshaler(WasmCompiler compiler) + { + _compiler = compiler; + } + + public abstract void Bind(Instance wasmInstance); + + public abstract WasmString CreateString(string value, Encoding? encoding = null); + + public abstract WasmString CreateString(); + + public abstract void FreeString(WasmString value); + public abstract string ReadString(int address, Encoding? encoding = null); + public abstract string ReadString(int address, int size, Encoding? encoding = null); + public abstract void WriteString(int address, string value, Encoding? encoding = null); +} + diff --git a/extensions/chord.runtime/Internal/Strings/TinyGoMarshaler.cs b/extensions/chord.runtime/Internal/Strings/TinyGoMarshaler.cs new file mode 100644 index 00000000..c095b11e --- /dev/null +++ b/extensions/chord.runtime/Internal/Strings/TinyGoMarshaler.cs @@ -0,0 +1,211 @@ +using System.Text; +using Chord.Common.Wasm; +using Wasmtime; + +namespace Chord.Runtime.Internal.Strings; + +internal sealed class TinyGoMarshaler : StringMarshaler +{ + private Memory? _memory; + private Func? _malloc; + private Action? _free; + + public TinyGoMarshaler() : base(WasmCompiler.TinyGo) { } + + public override void Bind(Instance wasmInstance) + { + _wasmInstance = wasmInstance; + _memory = wasmInstance.GetMemory("memory") ?? throw new ExtensionRuntimeException("Could not find the 'memory' export. Was the marshaler bound?"); + _malloc = wasmInstance.GetFunction("malloc") ?? throw new ExtensionRuntimeException("Could not find malloc function"); + _free = wasmInstance.GetAction("free") ?? throw new ExtensionRuntimeException("Could not find free function"); + } + + + public override WasmString CreateString(string value, Encoding? encoding) + { + if (_malloc is null) + { + throw new ExtensionRuntimeException("Could not find malloc function. Was the marshaler bound?"); + } + if (_memory is null) + { + throw new ExtensionRuntimeException("Could not find the 'memory' export. Was the marshaler bound?"); + } + encoding ??= Encoding.UTF8; + int stringLength = encoding.GetByteCount(value); + int stringAddress = _malloc.Invoke(stringLength); + + // Write the string bytes to the reserved memory. + encoding.GetBytes(value, _memory.GetSpan(stringAddress, stringLength)); + + return new WasmString(this, stringAddress, stringLength, value); + } + + public override WasmString CreateString() + { + if (_malloc is null) + { + throw new ExtensionRuntimeException("Could not find malloc function. Was the marshaler bound?"); + } + if (_memory is null) + { + throw new ExtensionRuntimeException("Could not find the 'memory' export. Was the marshaler bound?"); + } + // Allocate memory for the StringHeader + int stringHeaderSize = sizeof(int) + sizeof(int); // Size of StringHeader (Data + Len) + int stringHeaderAddress = _malloc.Invoke(stringHeaderSize); + + // Initially, the StringHeader's Data and Len fields are not set + _memory.WriteInt32(stringHeaderAddress, 0); // Data pointer, initially 0 + _memory.WriteInt32(stringHeaderAddress + sizeof(int), 0); // Len, initially 0 + return new WasmString(this, stringHeaderAddress, 0); + } + + /// + /// Frees the memory occupied by a string in WebAssembly memory. + /// + /// + /// This method handles the memory layout specific to Go's StringSlice. It first reads the address of the string data + /// from the StringHeader and then frees the string data if it was allocated. Finally, it frees the StringHeader itself. + /// + /// The WasmString object representing the string to be freed. + /// Thrown if the marshaler is not properly bound or if memory deallocation functions are not found. + public override void FreeString(WasmString value) + { + if (_free is null) + { + throw new ExtensionRuntimeException("Could not find free function. Was the marshaler bound?"); + } + if (_memory is null) + { + throw new ExtensionRuntimeException("Could not find the 'memory' export. Was the marshaler bound?"); + } + // Read the address of the string data from the StringHeader + int stringDataAddress = _memory.ReadInt32(value.Address); + + // Free the string data if it was allocated + if (stringDataAddress != 0) + { + // doesn't work at all, probably an incorrect assumption of how + // Go strings are laid out in memory + // try { _free.Invoke(stringDataAddress); } catch { } + } + // Free the StringHeader + _free.Invoke(value.Address); + } + + /// + /// Reads a string from the specified address and size in WebAssembly memory. + /// + /// + /// This method directly reads the string data from the given memory address, assuming the string is stored + /// in a contiguous block of memory of the specified size. This is aligned with Go's memory layout for strings. + /// + /// The memory address where the string data starts. + /// The size (in bytes) of the string data. + /// The encoding used for the string data. Defaults to UTF-8 if null. + /// The string read from memory. + /// Thrown if the marshaler is not properly bound. + public override string ReadString(int address, int size, Encoding? encoding = null) + { + if (_memory is null) + { + throw new ExtensionRuntimeException("Could not find the 'memory' export. Was the marshaler bound?"); + } + encoding ??= Encoding.UTF8; + + return encoding.GetString(_memory.GetSpan(address, size)); + } + + /// + /// Reads a string from WebAssembly memory using a StringHeader at the specified address. + /// + /// + /// In Go's `StringSlice` layout, a StringHeader is used, which consists of a pointer to the string data and the length of the string. + /// This method reads the StringHeader from the given address to obtain the memory address and length of the string, + /// and then reads the string data from that memory address. + /// It's important to handle this correctly to avoid reading incorrect memory regions, especially in the context of a managed + /// environment like WebAssembly where memory access patterns are critical for system stability and security. + /// + /// The memory address where the StringHeader is located. + /// The encoding used for the string data. Defaults to UTF-8 if null. + /// The string read from memory. + public override string ReadString(int address, Encoding? encoding = null) + { + if (_memory is null) + { + throw new ExtensionRuntimeException("Could not find the 'memory' export. Was the marshaler bound?"); + } + // Use UTF-8 encoding by default if none is provided + encoding ??= Encoding.UTF8; + + // Read the StringHeader from the given address + // The StringHeader consists of two parts: + // 1. The address of the string data (pointer) + // 2. The length of the string + var stringDataAddress = _memory.ReadInt32(address); // Read the data address + var length = _memory.ReadInt32(address + sizeof(int)); // Read the length of the string + + // If the string data address is 0, return an empty string + // This is to handle cases where the string might not be initialized + if (stringDataAddress == 0 || length == 0) + { + return string.Empty; + } + + // Read and return the string from the memory + // The string is encoded in the specified encoding (UTF-8 by default) + return encoding.GetString(_memory.GetSpan(stringDataAddress, length)); + } + + /// + /// Writes a string to a specified address in WebAssembly memory. + /// + /// + /// In Go's memory layout for strings, the StringHeader consists of a pointer to the string data and its length. + /// This method writes the string data to a newly allocated memory block and updates the StringHeader accordingly. + /// + /// The memory address to write the string data to. + /// The string to write into memory. + /// The encoding to use for the string. Defaults to UTF-8 if null. + /// Thrown if the marshaler is not properly bound or necessary memory functions are not found. + public override void WriteString(int address, string value, Encoding? encoding = null) + { + if (_memory is null) + { + throw new ExtensionRuntimeException("Could not find the 'memory' export. Was the marshaler bound?"); + } + if (_malloc is null) + { + throw new ExtensionRuntimeException("Could not find malloc function. Was the marshaler bound?"); + } + if (_free is null) + { + throw new ExtensionRuntimeException("Could not find free function. Was the marshaler bound?"); + } + // Use UTF-8 encoding by default if none is provided + encoding ??= Encoding.UTF8; + + // Calculate the byte count of the string in the specified encoding + var encodedLength = encoding.GetByteCount(value); + + // Read the address of the existing string data from the StringHeader + var stringDataAddress = _memory.ReadInt32(address); + + // If there is existing string data, free it before writing new data + if (stringDataAddress != 0) + { + // _free.Invoke(stringDataAddress); + } + + // Allocate new memory for the string data + stringDataAddress = _malloc.Invoke(encodedLength); + _memory.WriteInt32(address, stringDataAddress); // Update the StringHeader with the new data address + + // Write the string data to the allocated memory + encoding.GetBytes(value, _memory.GetSpan(stringDataAddress, encodedLength)); + + // Update the length in the StringHeader + _memory.WriteInt32(address + sizeof(int), encodedLength); + } +} \ No newline at end of file diff --git a/extensions/chord.runtime/Internal/Strings/WasmString.cs b/extensions/chord.runtime/Internal/Strings/WasmString.cs new file mode 100644 index 00000000..792ef07f --- /dev/null +++ b/extensions/chord.runtime/Internal/Strings/WasmString.cs @@ -0,0 +1,50 @@ +namespace Chord.Runtime.Internal.Strings; + +/// +/// Represents a string stored in WebAssembly memory, managed by a specific StringMarshaler. +/// +internal sealed class WasmString : IDisposable +{ + private readonly StringMarshaler _marshaler; + private readonly int _address; + private readonly string? _value; + private readonly int _length; + + /// + /// Initializes a new instance of the WasmString class. + /// + /// The StringMarshaler used to read and free the string from memory. + /// The address of the string in WebAssembly memory. + /// The length of the string in bytes. + /// The optional string value. If not provided, it will be lazy-loaded from memory. + internal WasmString(StringMarshaler marshaler, int address, int length, string? value = null) + { + _marshaler = marshaler; + _address = address; + _value = value; + _length = length; + } + + /// + /// Gets the memory address of the string in WebAssembly memory. + /// + public int Address => _address; + + /// + /// Gets the length of the string in bytes. + /// + public int Length => _length; + + /// + /// Gets the string value. If the value is not already set, it reads the string from WebAssembly memory. + /// + public string Value => _value is null ? _marshaler.ReadString(_address) : _value; + + /// + /// Releases the resources used by the WasmString, specifically freeing the associated memory in WebAssembly. + /// + public void Dispose() + { + _marshaler.FreeString(this); + } +} \ No newline at end of file diff --git a/extensions/chord.runtime/chord.runtime.csproj b/extensions/chord.runtime/chord.runtime.csproj new file mode 100644 index 00000000..ca8056d7 --- /dev/null +++ b/extensions/chord.runtime/chord.runtime.csproj @@ -0,0 +1,34 @@ + + + + net8.0 + preview + enable + enable + Chord.Runtime + true + true + true + true + + + + + + + + + + + + + + <_Parameter1>BlueNote.Tests + + + + + DEBUG;TRACE + + + diff --git a/extensions/chordc/Extensions/ExceptionExtensions.cs b/extensions/chordc/Extensions/ExceptionExtensions.cs new file mode 100644 index 00000000..1fda3ba8 --- /dev/null +++ b/extensions/chordc/Extensions/ExceptionExtensions.cs @@ -0,0 +1,27 @@ +using System.Text.Json; +using Chord.Compiler.Internal; +using Errata; +using Spectre.Console; + +namespace Chord.Compiler.Extensions; + +internal static class ExceptionExtensions +{ + + internal static void Render(this JsonException jsonException, ISourceRepository repository, string sourcePath) + { + if (jsonException.LineNumber is null || jsonException.BytePositionInLine is null) + { + Logger.Error.WriteException(jsonException); + return; + } + var location = new Location((int)jsonException.LineNumber.Value + 1, (int)jsonException.BytePositionInLine.Value); + var report = new Report(repository); + var labelMessage = jsonException.InnerException?.Message ?? string.Empty; + report.AddDiagnostic( + Diagnostic.Error(jsonException.Message) + .WithCode("CHORD0001") + .WithLabel(new Label(sourcePath, location, labelMessage))); + report.Render(Logger.Error); + } +} \ No newline at end of file diff --git a/extensions/chordc/Helpers.cs b/extensions/chordc/Helpers.cs new file mode 100644 index 00000000..63c0e3b9 --- /dev/null +++ b/extensions/chordc/Helpers.cs @@ -0,0 +1,54 @@ +using System.Diagnostics.CodeAnalysis; +using System.Text; + +namespace Chord.Compiler; + +internal static class Helpers +{ + + public const string ManifestFileName = "chord.json"; + + internal static bool TryReadFile(string path, [NotNullWhen(true)] out string? content) + { + try + { + content = File.ReadAllText(path); + return true; + } + catch + { + content = null; + return false; + } + } + + /// + /// Searches for a chord.json file in the current directory or any parent directories. + /// + /// The fully qualified path to the manifest, or null if none was found. + internal static string? LocateChordManifest() + { + try + { + var workingDirectory = Directory.GetCurrentDirectory(); + var configFile = Directory.GetFiles(workingDirectory, ManifestFileName).FirstOrDefault(); + while (string.IsNullOrWhiteSpace(configFile)) + { + if (Directory.GetParent(workingDirectory) is not { Exists: true } parent) + { + break; + } + workingDirectory = parent.FullName; + if (parent.GetFiles(ManifestFileName)?.FirstOrDefault() is { Exists: true } file) + { + configFile = file.FullName; + } + } + return configFile; + } + catch + { + return null; + } + } +} \ No newline at end of file diff --git a/extensions/chordc/Internal/API/ProgressableStreamContent.cs b/extensions/chordc/Internal/API/ProgressableStreamContent.cs new file mode 100644 index 00000000..a4edce91 --- /dev/null +++ b/extensions/chordc/Internal/API/ProgressableStreamContent.cs @@ -0,0 +1,38 @@ +using System.Net; +namespace Chord.Compiler.Internal.API; + +internal sealed class ProgressableStreamContent : HttpContent +{ + private readonly Stream _stream; + private readonly Action _progressAction; + private readonly CancellationToken _cancellationToken; + + public ProgressableStreamContent(Stream stream, Action progressAction, CancellationToken cancellationToken) + { + _stream = stream ?? throw new ArgumentNullException(nameof(stream)); + _progressAction = progressAction ?? throw new ArgumentNullException(nameof(progressAction)); + _cancellationToken = cancellationToken; + } + + protected override async Task SerializeToStreamAsync(Stream stream, TransportContext? context) + { + const int bufferSize = 4096; + var buffer = new byte[bufferSize]; + long uploadedBytes = 0; + int bytesRead; + + while ((bytesRead = await _stream.ReadAsync(buffer, 0, buffer.Length, _cancellationToken)) != 0) + { + await stream.WriteAsync(buffer.AsMemory(0, bytesRead), _cancellationToken); + uploadedBytes += bytesRead; + _progressAction(uploadedBytes, _stream.Length); + _cancellationToken.ThrowIfCancellationRequested(); + } + } + + protected override bool TryComputeLength(out long length) + { + length = _stream.Length; + return true; + } +} \ No newline at end of file diff --git a/extensions/chordc/Internal/API/RegistryClient.Downloader.cs b/extensions/chordc/Internal/API/RegistryClient.Downloader.cs new file mode 100644 index 00000000..4224ba00 --- /dev/null +++ b/extensions/chordc/Internal/API/RegistryClient.Downloader.cs @@ -0,0 +1,91 @@ +using Chord.Common; +using Chord.Compiler.Internal.API.Responses; +using Spectre.Console; + +namespace Chord.Compiler.Internal.API; + +internal sealed partial class RegistryClient +{ + private const string ContentUrl = "https://betwixtusercontent.com/chord"; + + private static void ValidateChord(string chord, out string requestedChord, out string requestedVersion) + { + if (string.IsNullOrWhiteSpace(chord)) + { + throw new ArgumentException("Value cannot be null or whitespace.", nameof(chord)); + } + + var parts = chord.Split('@'); + requestedChord = parts[0]; + requestedVersion = parts.Length > 1 ? parts[1] : "latest"; + + if (string.IsNullOrWhiteSpace(requestedChord)) + { + throw new InvalidDataException("Chord ID cannot be null or whitespace."); + } + } + + private static RegistryVersion? FindRegistryVersion(RegistryCatalog versions, string requestedVersion) + { + return requestedVersion == "latest" + ? versions.Versions.FirstOrDefault(v => v.Version == versions.Latest) + : versions.Versions.FirstOrDefault(v => v.Version == requestedVersion); + } + + private static string GetDestinationPath(string chord, string version) + { + var destinationPath = Path.Combine(StoragePath.BebopcData, chord, version, "chord.wasm"); + var destinationDirectory = Path.GetDirectoryName(destinationPath); + + if (string.IsNullOrEmpty(destinationDirectory)) + { + throw new InvalidOperationException("Destination directory cannot be null or empty."); + } + if (!Directory.Exists(destinationDirectory)) + { + Directory.CreateDirectory(destinationDirectory); + } + return destinationPath; + } + + private async Task DownloadFileAsync(string url, string destinationPath, ProgressTask progressTask, CancellationToken cancellationToken) + { + using var response = await _httpClient.GetAsync(url, HttpCompletionOption.ResponseHeadersRead, cancellationToken); + response.EnsureSuccessStatusCode(); + + var totalBytes = response.Content.Headers.ContentLength ?? -1L; + var buffer = new byte[8192]; + + using var fileStream = new FileStream(destinationPath, FileMode.Create, FileAccess.Write, FileShare.None, 8192, true); + using var contentStream = await response.Content.ReadAsStreamAsync(cancellationToken); + + await CopyContentAsync(contentStream, fileStream, buffer, progressTask, totalBytes, cancellationToken); + } + + private static async Task CopyContentAsync(Stream source, Stream destination, byte[] buffer, ProgressTask progressTask, long totalBytes, CancellationToken cancellationToken) + { + var totalBytesRead = 0L; + int bytesRead; + while ((bytesRead = await source.ReadAsync(buffer, cancellationToken)) != 0) + { + await destination.WriteAsync(buffer.AsMemory(0, bytesRead), cancellationToken); + totalBytesRead += bytesRead; + progressTask.Value = (double)totalBytesRead / totalBytes * 100; + } + } + + private async Task DownloadChordAsync(string chord, RegistryVersion version, string destinationPath, ProgressTask progressTask, CancellationToken cancellationToken) + { + var chordUrl = $"{ContentUrl}/{chord}/{version.Version}/chord.wasm"; + try + { + await DownloadFileAsync(chordUrl, destinationPath, progressTask, cancellationToken); + return true; + } + catch (HttpRequestException ex) + { + HandleHttpRequestException(ex, $"fetching chord [bold]{chord}[/] version [bold]{version.Version}[/]"); + return false; + } + } +} \ No newline at end of file diff --git a/extensions/chordc/Internal/API/RegistryClient.cs b/extensions/chordc/Internal/API/RegistryClient.cs new file mode 100644 index 00000000..2ed813ab --- /dev/null +++ b/extensions/chordc/Internal/API/RegistryClient.cs @@ -0,0 +1,153 @@ +using System.Net.Http.Json; +using System.Security.Authentication; +using Chord.Common; +using Chord.Compiler.Internal.API.Responses; +using Spectre.Console; + +namespace Chord.Compiler.Internal.API; + +internal sealed partial class RegistryClient : IDisposable +{ + private const string RegistryUrl = "https://chord-publish.andrew-03e.workers.dev"; + + private readonly HttpClientHandler _httpClientHandler; + private readonly HttpClient _httpClient; + + public RegistryClient(string? authToken = null) + { + _httpClientHandler = new HttpClientHandler + { + UseCookies = false, + SslProtocols = SslProtocols.Tls13 | SslProtocols.Tls12 + }; + + _httpClient = new HttpClient(_httpClientHandler); + _httpClient.DefaultRequestHeaders.Add("User-Agent", "chordc/0.1.0"); + if (!string.IsNullOrEmpty(authToken)) + { + _httpClient.DefaultRequestHeaders.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("Bearer", authToken); + } + } + + public async Task PublishAsync(string archivePath, CancellationToken cancellationToken = default) + { + using var fileStream = new FileStream(archivePath, FileMode.Open, FileAccess.Read); + // Create a linked token source for combined cancellation + using var linkedCts = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken); + var linkedToken = linkedCts.Token; + + var response = await AnsiConsole.Progress() + .StartAsync(async ctx => + { + var uploadTask = ctx.AddTask("[green]Publishing extension[/]", maxValue: fileStream.Length); + + using var contentStream = new ProgressableStreamContent(fileStream, (uploaded, total) => + { + uploadTask.Value = uploaded; + }, linkedToken); + + + contentStream.Headers.ContentType = new System.Net.Http.Headers.MediaTypeHeaderValue("application/charm-package"); + + var requestMessage = new HttpRequestMessage(HttpMethod.Post, $"{RegistryUrl}/publish") { Content = contentStream }; + var response = await _httpClient.SendAsync(requestMessage, linkedToken); + var publishResponse = await response.Content.ReadFromJsonAsync( + JsonContext.Default.PublishResponse, cancellationToken: linkedToken); + return publishResponse; + }); + return response; + } + + + + public async Task DownloadChordAsync(string chord, ProgressTask progressTask, bool forceDownload = false, CancellationToken cancellationToken = default) + { + try + { + ValidateChord(chord, out string requestedChord, out string requestedVersion); + + var versions = await FetchRegistryCatalog(requestedChord, progressTask, cancellationToken); + if (versions is null) + { + return false; + } + + var registryVersion = FindRegistryVersion(versions, requestedVersion); + if (registryVersion is null) + { + progressTask.Description = $"[maroon]{requestedChord}@{requestedVersion} is not published[/]"; + return false; + } + + var destinationPath = GetDestinationPath(requestedChord, registryVersion.Version); + if (File.Exists(destinationPath) && !forceDownload) + { + progressTask.Description = $"[yellow]Skipping [bold]{requestedChord}@{registryVersion.Version}[/][/] "; + progressTask.Value = progressTask.MaxValue; + return true; + } + var result = await DownloadChordAsync(requestedChord, registryVersion, destinationPath, progressTask, cancellationToken); + if (result) + { + progressTask.Description = $":check_mark_button: [green][bold]{requestedChord}@{registryVersion.Version}[/] installed successfully.[/] "; + } + return result; + } + catch (Exception ex) + { + progressTask.Description = $":cross_mark: [maroon]unknown error when downloading chord.[/]"; + if (Logger.IsVerbose) + { + Logger.Error.WriteException(ex); + } + return false; + } + } + + public async Task FetchRegistryCatalog(string chord, ProgressTask progressTask, CancellationToken cancellationToken) + { + var versionsUrl = $"{ContentUrl}/{chord}/versions.json"; + try + { + return await _httpClient.GetFromJsonAsync(versionsUrl, JsonContext.Default.RegistryCatalog, cancellationToken); + } + catch (HttpRequestException ex) + { + HandleHttpRequestException(ex, $"fetching version info for [bold]{chord}[/] ({ex.StatusCode})", progressTask); + return null; + } + } + + private static void HandleHttpRequestException(HttpRequestException ex, string contextMessage, ProgressTask? progressTask = null) + { + if (ex.StatusCode == System.Net.HttpStatusCode.NotFound) + { + if (progressTask is not null) + { + progressTask.Description = $":cross_mark: [maroon]{contextMessage}[/]"; + } + else + { + Logger.Error.MarkupLine($":cross_mark: [maroon]{contextMessage}[/]"); + } + } + else + { + if (progressTask is not null) + { + progressTask.Description = $":cross_mark: [maroon]unknown error when {contextMessage}.[/]"; + } + else + { + Logger.Error.MarkupLine($":cross_mark: [maroon]unknown error when {contextMessage}. [/]"); + } + Logger.Error.WriteException(ex); + } + } + + public void Dispose() + { + _httpClientHandler.Dispose(); + _httpClient.Dispose(); + } +} \ No newline at end of file diff --git a/extensions/chordc/Internal/API/Responses/PublishResponse.cs b/extensions/chordc/Internal/API/Responses/PublishResponse.cs new file mode 100644 index 00000000..9463cd40 --- /dev/null +++ b/extensions/chordc/Internal/API/Responses/PublishResponse.cs @@ -0,0 +1,3 @@ +namespace Chord.Compiler.Internal.API.Responses; + +internal sealed record PublishResponse(bool Success, string? Message); \ No newline at end of file diff --git a/extensions/chordc/Internal/API/Responses/RegistryVersions.cs b/extensions/chordc/Internal/API/Responses/RegistryVersions.cs new file mode 100644 index 00000000..8b4a98c5 --- /dev/null +++ b/extensions/chordc/Internal/API/Responses/RegistryVersions.cs @@ -0,0 +1,118 @@ +using System.Text.Json; +using System.Text.Json.Serialization; + +namespace Chord.Compiler.Internal.API.Responses; + +internal sealed record RegistryCatalog(string Name, string Description, string Latest, RegistryVersion[] Versions); +internal sealed record RegistryVersion(string Version, ulong PublishedAt, bool IsPreRelease); + +internal sealed class RegistryCatalogConverter : JsonConverter +{ + public override RegistryCatalog Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + { + if (reader.TokenType != JsonTokenType.StartObject) + { + throw new JsonException(); + } + + string? name = null; + string? description = null; + string? latest = null; + var versions = new List(); + + while (reader.Read()) + { + if (reader.TokenType == JsonTokenType.EndObject) + { + if (string.IsNullOrEmpty(name)) + { + throw new JsonException("Name cannot be empty."); + } + if (string.IsNullOrEmpty(description)) + { + throw new JsonException("Description cannot be empty."); + } + if (string.IsNullOrEmpty(latest)) + { + throw new JsonException("Latest version cannot be empty."); + } + return new RegistryCatalog(name, description, latest, [.. versions]); + } + if (reader.TokenType == JsonTokenType.PropertyName) + { + var propertyName = reader.GetString(); + reader.Read(); + + switch (propertyName) + { + case "name": + name = reader.GetString(); + break; + case "description": + description = reader.GetString(); + break; + case "latest": + latest = reader.GetString(); + break; + case "versions": + if (reader.TokenType == JsonTokenType.StartObject) + { + while (reader.Read() && reader.TokenType != JsonTokenType.EndObject) + { + if (reader.TokenType == JsonTokenType.PropertyName) + { + var versionNumber = reader.GetString(); + if (string.IsNullOrEmpty(versionNumber)) + { + throw new JsonException("Version number cannot be empty."); + } + var version = ReadRegistryVersion(ref reader); + versions.Add(version with { Version = versionNumber }); + } + } + } + break; + } + } + } + + throw new JsonException("JSON format is invalid."); + } + + private RegistryVersion ReadRegistryVersion(ref Utf8JsonReader reader) + { + ulong publishedAt = 0; + bool isPreRelease = false; + + reader.Read(); // Move to StartObject + if (reader.TokenType != JsonTokenType.StartObject) + { + throw new JsonException(); + } + + while (reader.Read() && reader.TokenType != JsonTokenType.EndObject) + { + if (reader.TokenType == JsonTokenType.PropertyName) + { + var propertyName = reader.GetString(); + reader.Read(); + + switch (propertyName) + { + case "publishedAt": + publishedAt = reader.GetUInt64(); + break; + case "isPreRelease": + isPreRelease = reader.GetBoolean(); + break; + } + } + } + return new RegistryVersion(string.Empty, publishedAt, isPreRelease); // Version will be filled later + } + + public override void Write(Utf8JsonWriter writer, RegistryCatalog value, JsonSerializerOptions options) + { + throw new NotImplementedException(); + } +} \ No newline at end of file diff --git a/extensions/chordc/Internal/Commands/CommandParser.cs b/extensions/chordc/Internal/Commands/CommandParser.cs new file mode 100644 index 00000000..3e98bdc8 --- /dev/null +++ b/extensions/chordc/Internal/Commands/CommandParser.cs @@ -0,0 +1,97 @@ +using System; +using System.Collections.Generic; +using System.Text; +namespace Chord.Compiler.Internal.Commands; + +internal static class CommandParser +{ + + private static readonly HashSet _validCommands = ["build", "pack", "test", "publish", "install", "help"]; + + internal static (string Command, Dictionary Flags)? GetCommand(string[] args) + { + if (args.Length == 0 || !_validCommands.Contains(args[0].ToLowerInvariant())) + { + return null; + } + + var command = args[0].Trim().ToLowerInvariant(); + var flags = new Dictionary(); + + if (command == "install") + { + // Handle multiple extensions arguments for 'install' command + var extensions = new List(); + for (int i = 1; i < args.Length; i++) + { + if (!args[i].StartsWith('-')) + { + extensions.Add(args[i]); + } + else + { + ParseFlag(args, ref i, flags); + } + } + if (extensions.Count > 0) + { + flags["extensions"] = extensions; + } + } + else + { + // Handle flags for other commands + for (int i = 1; i < args.Length; i++) + { + ParseFlag(args, ref i, flags); + } + } + + return (command, flags); + } + + private static void ParseFlag(string[] args, ref int i, Dictionary flags) + { + var arg = args[i]; + if (arg.StartsWith('-')) + { + var flag = arg.TrimStart('-'); + string? value = null; + + // Check if the next argument is a value (not another flag) + if (i + 1 < args.Length && !args[i + 1].StartsWith('-')) + { + value = args[i + 1]; + i++; // Skip the next argument since it's the value for this flag + } + + // If the flag has a value, store it; otherwise, store the flag as a boolean true + if (string.IsNullOrEmpty(value)) + { + flags[flag] = true; + } + else + { + flags[flag] = value; + } + } + } + + internal static string GetHelpText() + { + var helpText = new StringBuilder(); + helpText.AppendLine("Usage: chordc [command] [options]"); + helpText.AppendLine(); + helpText.AppendLine("Commands:"); + helpText.AppendLine(" build Build the chord extension"); + helpText.AppendLine(" pack Package the chord extension"); + helpText.AppendLine(" test Test the chord extension"); + helpText.AppendLine(" install Install the chord extension"); + helpText.AppendLine(" help Display help"); + helpText.AppendLine(); + //helpText.AppendLine("Options:"); + //helpText.AppendLine(" -h|--help Show help information"); + return helpText.ToString(); + } + +} \ No newline at end of file diff --git a/extensions/chordc/Internal/FunctionSignature.cs b/extensions/chordc/Internal/FunctionSignature.cs new file mode 100644 index 00000000..59b3697b --- /dev/null +++ b/extensions/chordc/Internal/FunctionSignature.cs @@ -0,0 +1,40 @@ +using System.Text; +using Chord.Common.Wasm.Types; + +namespace Chord.Compiler.Internal; + +public sealed record FunctionSignature(string Name, WebAssemblyValueType[] Parameters, WebAssemblyValueType[] Returns) +{ + public override string ToString() + { + var builder = new StringBuilder(); + builder.Append('('); + var nParams = Parameters.Length; + for (var i = 0; i < nParams; i++) + { + builder.Append(Parameters[i].ToTypeString()); + if (i < nParams - 1) + { + builder.Append(','); + } + } + builder.Append(") -> "); + var nReturns = Returns.Length; + if (nReturns == 0) + { + builder.Append("nil"); + } + else + { + for (var i = 0; i < nReturns; i++) + { + builder.Append(Returns[i].ToTypeString()); + if (i < nReturns - 1) + { + builder.Append(','); + } + } + } + return builder.ToString(); + } +} \ No newline at end of file diff --git a/extensions/chordc/Internal/JsonContext.cs b/extensions/chordc/Internal/JsonContext.cs new file mode 100644 index 00000000..e468554e --- /dev/null +++ b/extensions/chordc/Internal/JsonContext.cs @@ -0,0 +1,21 @@ +using System.Text.Json; +using System.Text.Json.Serialization; +using Chord.Compiler.Internal.API.Responses; + +namespace Chord.Compiler.Internal; + + +[JsonSourceGenerationOptions( + JsonSerializerDefaults.Web, + AllowTrailingCommas = true, + DefaultBufferSize = 10, + WriteIndented = true, + PropertyNamingPolicy = JsonKnownNamingPolicy.CamelCase, + UseStringEnumConverter = true, + Converters = [typeof(RegistryCatalogConverter)])] +[JsonSerializable(typeof(PublishResponse))] +[JsonSerializable(typeof(RegistryCatalog))] +[JsonSerializable(typeof(RegistryVersion))] +internal partial class JsonContext : JsonSerializerContext +{ +} diff --git a/extensions/chordc/Internal/Logger.cs b/extensions/chordc/Internal/Logger.cs new file mode 100644 index 00000000..0683dffa --- /dev/null +++ b/extensions/chordc/Internal/Logger.cs @@ -0,0 +1,24 @@ +using Spectre.Console; + +namespace Chord.Compiler.Internal; + +internal static class Logger +{ + public static readonly IAnsiConsole Out; + public static readonly IAnsiConsole Error; + public static bool IsVerbose { get; set; } + static Logger() + { + Out = AnsiConsole.Create(new AnsiConsoleSettings + { + Ansi = AnsiSupport.Yes, + Out = new AnsiConsoleOutput(Console.Out), + }); + Error = AnsiConsole.Create(new AnsiConsoleSettings + { + Ansi = AnsiSupport.Yes, + Out = new AnsiConsoleOutput(Console.Error), + }); + IsVerbose = false; + } +} \ No newline at end of file diff --git a/extensions/chordc/Internal/ProcessTerminationHandler.cs b/extensions/chordc/Internal/ProcessTerminationHandler.cs new file mode 100644 index 00000000..46240d66 --- /dev/null +++ b/extensions/chordc/Internal/ProcessTerminationHandler.cs @@ -0,0 +1,100 @@ +using System.Runtime.InteropServices; + +namespace Chord.Compiler.Internal; + + +internal sealed class ProcessTerminationHandler : IDisposable +{ + private const int SIGINT_EXIT_CODE = 130; + private const int SIGTERM_EXIT_CODE = 143; + + internal readonly TaskCompletionSource ProcessTerminationCompletionSource; + private readonly CancellationTokenSource _handlerCancellationTokenSource; + private readonly Task _startedHandler; + private readonly TimeSpan _processTerminationTimeout; +#if NET7_0_OR_GREATER + private readonly IDisposable? _sigIntRegistration, _sigTermRegistration; +#endif + + internal ProcessTerminationHandler( + CancellationTokenSource handlerCancellationTokenSource, + Task startedHandler, + TimeSpan processTerminationTimeout) + { + ProcessTerminationCompletionSource = new(); + _handlerCancellationTokenSource = handlerCancellationTokenSource; + _startedHandler = startedHandler; + _processTerminationTimeout = processTerminationTimeout; + +#if NET7_0_OR_GREATER // we prefer the new API as they allow for cancelling SIGTERM + if (!OperatingSystem.IsAndroid() + && !OperatingSystem.IsIOS() + && !OperatingSystem.IsTvOS() + && !OperatingSystem.IsBrowser() + && !OperatingSystem.IsWasi()) + { + _sigIntRegistration = PosixSignalRegistration.Create(PosixSignal.SIGINT, OnPosixSignal); + _sigTermRegistration = PosixSignalRegistration.Create(PosixSignal.SIGTERM, OnPosixSignal); + return; + } +#endif + + Console.CancelKeyPress += OnCancelKeyPress; + AppDomain.CurrentDomain.ProcessExit += OnProcessExit; + } + + public void Dispose() + { +#if NET7_0_OR_GREATER + if (_sigIntRegistration is not null) + { + _sigIntRegistration.Dispose(); + _sigTermRegistration!.Dispose(); + return; + } +#endif + + Console.CancelKeyPress -= OnCancelKeyPress; + AppDomain.CurrentDomain.ProcessExit -= OnProcessExit; + } + +#if NET7_0_OR_GREATER + void OnPosixSignal(PosixSignalContext context) + { + context.Cancel = true; + + Cancel(context.Signal == PosixSignal.SIGINT ? SIGINT_EXIT_CODE : SIGTERM_EXIT_CODE); + } +#endif + + void OnCancelKeyPress(object? sender, ConsoleCancelEventArgs e) + { + e.Cancel = true; + + Cancel(SIGINT_EXIT_CODE); + } + + void OnProcessExit(object? sender, EventArgs e) => Cancel(SIGTERM_EXIT_CODE); + + void Cancel(int forcedTerminationExitCode) + { + // request cancellation + _handlerCancellationTokenSource.Cancel(); + + + try + { + // wait for the configured interval + if (!_startedHandler.Wait(_processTerminationTimeout)) + { + // if the handler does not finish within configured time, + // use the completion source to signal forced completion (preserving native exit code) + ProcessTerminationCompletionSource.SetResult(forcedTerminationExitCode); + } + } + catch (AggregateException) + { + // The task was cancelled or an exception was thrown during the task execution. + } + } +} \ No newline at end of file diff --git a/extensions/chordc/Internal/SignatureValidator.cs b/extensions/chordc/Internal/SignatureValidator.cs new file mode 100644 index 00000000..51671b2f --- /dev/null +++ b/extensions/chordc/Internal/SignatureValidator.cs @@ -0,0 +1,179 @@ +using Chord.Common.Wasm.Types; + +namespace Chord.Compiler.Internal; + +public static class SignatureValidator +{ + private static readonly Dictionary> _signatureLookup; + + /// + /// DONT TOUCH THIS CODE + /// + static SignatureValidator() + { + // Begin generated code + _signatureLookup = new Dictionary>() + { + { + "as", + new () + { + new ("write_line", + [ + WebAssemblyValueType.Int32 + ], + [ + + ] + ), + + new ("write_error", + [ + WebAssemblyValueType.Int32 + ], + [ + + ] + ), + + new ("get_bebopc_version", + [ + + ], + [ + WebAssemblyValueType.Int32 + ] + ), + + new ("chord_compile", + [ + WebAssemblyValueType.Int32 + ], + [ + WebAssemblyValueType.Int32 + ] + ) + } + }, + { + "javy", + new () + { + new ("chord-compile", + [ + + ], + [ + + ] + ) + } + }, + { + "tinygo", + new () + { + new ("write_line", + [ + WebAssemblyValueType.Int32, + WebAssemblyValueType.Int32 + ], + [ + + ] + ), + + new ("write_error", + [ + WebAssemblyValueType.Int32, + WebAssemblyValueType.Int32 + ], + [ + + ] + ), + + new ("get_bebopc_version", + [ + WebAssemblyValueType.Int32 + ], + [ + + ] + ), + + new ("chord_compile", + [ + WebAssemblyValueType.Int32, + WebAssemblyValueType.Int32, + WebAssemblyValueType.Int32 + ], + [ + + ] + ) + } + }, + }; + // End generated code + } + + public static FunctionSignature? GetFunctionSignature(string compiler, string functionName) + { + if (!_signatureLookup.TryGetValue(compiler, out var signatures)) + { + // Compiler not found + return null; + } + + return signatures.FirstOrDefault(sig => sig.Name == functionName); + } + + public static bool ValidateSignature(FunctionType? functionType, FunctionSignature? expectedSignature) + { + if (functionType == null || expectedSignature == null) + { + // Function type or expected signature not found + return false; + } + + // Compare the parameters and returns of functionType with expectedSignature + return CompareSignatures(functionType, expectedSignature); + } + + private static bool CompareSignatures(FunctionType actual, FunctionSignature expected) + { + // Compare the number of parameters + if (actual.Parameters.Length != expected.Parameters.Length) + { + return false; + } + + // Compare each parameter type + for (int i = 0; i < actual.Parameters.Length; i++) + { + if (actual.Parameters[i] != expected.Parameters[i]) + { + return false; + } + } + + // Compare the number of return types + if (actual.Returns.Length != expected.Returns.Length) + { + return false; + } + + // Compare each return type + for (int i = 0; i < actual.Returns.Length; i++) + { + if (actual.Returns[i] != expected.Returns[i]) + { + return false; + } + } + + // All checks passed, signatures match + return true; + } +} \ No newline at end of file diff --git a/extensions/chordc/Internal/Utils/IOUtils.cs b/extensions/chordc/Internal/Utils/IOUtils.cs new file mode 100644 index 00000000..f4c6bbe9 --- /dev/null +++ b/extensions/chordc/Internal/Utils/IOUtils.cs @@ -0,0 +1,152 @@ +namespace Chord.Compiler.Internal.Utils; + +internal static class IOUtil +{ + + public static string ExeExtension + { + get + { +#if OS_WINDOWS + return ".exe"; +#else + return string.Empty; +#endif + } + } + + public static StringComparison FilePathStringComparison + { + get + { +#if OS_LINUX + return StringComparison.Ordinal; +#else + return StringComparison.OrdinalIgnoreCase; +#endif + } + } + + /// + /// Given a path and directory, return the path relative to the directory. If the path is not + /// under the directory the path is returned un modified. Examples: + /// MakeRelative(@"d:\src\project\foo.cpp", @"d:\src") -> @"project\foo.cpp" + /// MakeRelative(@"d:\src\project\foo.cpp", @"d:\specs") -> @"d:\src\project\foo.cpp" + /// MakeRelative(@"d:\src\project\foo.cpp", @"d:\src\proj") -> @"d:\src\project\foo.cpp" + /// + /// Safe for remote paths. Does not access the local disk. + /// Path to make relative. + /// Folder to make it relative to. + /// Relative path. + public static string MakeRelative(string path, string folder) + { + ArgumentNullException.ThrowIfNullOrEmpty(path, nameof(path)); + ArgumentNullException.ThrowIfNull(folder, nameof(folder)); + + // Replace all Path.AltDirectorySeparatorChar with Path.DirectorySeparatorChar from both inputs + path = path.Replace(Path.AltDirectorySeparatorChar, Path.DirectorySeparatorChar); + folder = folder.Replace(Path.AltDirectorySeparatorChar, Path.DirectorySeparatorChar); + + // Check if the dir is a prefix of the path (if not, it isn't relative at all). + if (!path.StartsWith(folder, FilePathStringComparison)) + { + return path; + } + + // Dir is a prefix of the path, if they are the same length then the relative path is empty. + if (path.Length == folder.Length) + { + return string.Empty; + } + + // If the dir ended in a '\\' (like d:\) or '/' (like user/bin/) then we have a relative path. + if (folder.Length > 0 && folder[folder.Length - 1] == Path.DirectorySeparatorChar) + { + return path.Substring(folder.Length); + } + // The next character needs to be a '\\' or they aren't really relative. + else if (path[folder.Length] == Path.DirectorySeparatorChar) + { + return path.Substring(folder.Length + 1); + } + else + { + return path; + } + } + + public static string ResolvePath(string rootPath, string relativePath) + { + ArgumentException.ThrowIfNullOrEmpty(rootPath, nameof(rootPath)); + ArgumentException.ThrowIfNullOrEmpty(relativePath, nameof(relativePath)); + + if (!Path.IsPathRooted(rootPath)) + { + throw new ArgumentException($"{rootPath} should be a rooted path."); + } + + if (relativePath.IndexOfAny(Path.GetInvalidPathChars()) > -1) + { + throw new InvalidOperationException($"{relativePath} contains invalid path characters."); + } + else if (Path.GetFileName(relativePath).IndexOfAny(Path.GetInvalidFileNameChars()) > -1) + { + throw new InvalidOperationException($"{relativePath} contains invalid folder name characters."); + } + else if (Path.IsPathRooted(relativePath)) + { + throw new InvalidOperationException($"{relativePath} can not be a rooted path."); + } + else + { + rootPath = rootPath.Replace(Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar); + relativePath = relativePath.Replace(Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar); + + // Root the path + relativePath = string.Concat(rootPath, Path.AltDirectorySeparatorChar, relativePath); + + // Collapse ".." directories with their parent, and skip "." directories. + string[] split = relativePath.Split(new[] { Path.AltDirectorySeparatorChar }, StringSplitOptions.RemoveEmptyEntries); + var segments = new Stack(split.Length); + int skip = 0; + for (int i = split.Length - 1; i >= 0; i--) + { + string segment = split[i]; + if (string.Equals(segment, ".", StringComparison.Ordinal)) + { + continue; + } + else if (string.Equals(segment, "..", StringComparison.Ordinal)) + { + skip++; + } + else if (skip > 0) + { + skip--; + } + else + { + segments.Push(segment); + } + } + + if (skip > 0) + { + throw new InvalidOperationException($"The file path {relativePath} is invalid"); + } + +#if OS_WINDOWS + if (segments.Count > 1) + { + return String.Join(Path.DirectorySeparatorChar, segments); + } + else + { + return segments.Pop() + Path.DirectorySeparatorChar; + } +#else + return Path.DirectorySeparatorChar + string.Join(Path.DirectorySeparatorChar, segments); +#endif + } + } +} \ No newline at end of file diff --git a/extensions/chordc/Internal/Utils/PathUtil.cs b/extensions/chordc/Internal/Utils/PathUtil.cs new file mode 100644 index 00000000..d398f625 --- /dev/null +++ b/extensions/chordc/Internal/Utils/PathUtil.cs @@ -0,0 +1,65 @@ +using System.Runtime.InteropServices; + +namespace Chord.Compiler.Internal.Utils; + +internal static class PathUtil +{ +#if OS_WINDOWS + public static readonly string PathVariable = "Path"; +#else + public static readonly string PathVariable = "PATH"; +#endif + + public static string PrependPath(string path, string currentPath) + { + ArgumentNullException.ThrowIfNullOrEmpty(path, nameof(path)); + if (string.IsNullOrEmpty(currentPath)) + { + // Careful not to add a trailing separator if the PATH is empty. + // On OSX/Linux, a trailing separator indicates that "current directory" + // is added to the PATH, which is considered a security risk. + return path; + } + + // Not prepend path if it is already the first path in %PATH% + if (currentPath.StartsWith(path + Path.PathSeparator, IOUtil.FilePathStringComparison)) + { + return currentPath; + } + else + { + return path + Path.PathSeparator + currentPath; + } + } + + /// + /// Determines if the given program is on the PATH. + /// + /// The name of the program + /// true if the program is on the PATH; otherwise false. + public static bool IsProgramOnPath(string program) + { + if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) + { + program += IOUtil.ExeExtension; + } + + var path = Environment.GetEnvironmentVariable(PathVariable); + if (path is null) + { + return false; + } + + var paths = path.Split(Path.PathSeparator); + foreach (var pathEntry in paths) + { + var fullPath = Path.Combine(pathEntry, program); + if (File.Exists(fullPath)) + { + return true; + } + } + + return false; + } +} \ No newline at end of file diff --git a/extensions/chordc/Program.cs b/extensions/chordc/Program.cs new file mode 100644 index 00000000..549c2465 --- /dev/null +++ b/extensions/chordc/Program.cs @@ -0,0 +1,478 @@ +// See https://aka.ms/new-console-template for more information +using System.IO.Compression; +using System.Text; +using System.Text.Json; +using Chord.Common; +using Chord.Common.Wasm; +using Chord.Common.Wasm.Types; +using Chord.Compiler; +using Chord.Compiler.Extensions; +using Chord.Compiler.Internal; +using Chord.Compiler.Internal.API; +using Chord.Compiler.Internal.Commands; +using Chord.Compiler.Internal.Utils; +using Chord.Compiler.Shells; +using Errata; +using Spectre.Console; + +string? manifestPath = null; +string? manifestContent = null; +ChordManifest? manifest = null; +string? authToken = Environment.GetEnvironmentVariable("CHORD_AUTH"); + +var parsed = CommandParser.GetCommand(args); +if (!parsed.HasValue || parsed.Value.Command is "help") +{ + Logger.Out.WriteLine(CommandParser.GetHelpText()); + return !parsed.HasValue ? 1 : 0; +} +var command = parsed.Value.Command; +var flags = parsed.Value.Flags; + +try +{ + using var cts = new CancellationTokenSource(); + var runHandler = Run(cts); + using var terminationHandler = new ProcessTerminationHandler(cts, runHandler, TimeSpan.FromSeconds(5)); + Task firstCompletedTask = await Task.WhenAny(runHandler, terminationHandler.ProcessTerminationCompletionSource.Task); + return await firstCompletedTask; // return the result or propagate the exception +} +catch (Exception ex) +{ + switch (ex) + { + case AggregateException agg: + Logger.Error.WriteException(agg); + return 1; + default: + Logger.Error.WriteException(ex); + return 1; + } +} + +async Task Run(CancellationTokenSource cts) +{ + switch (command) + { + case "install": + return await InstallAsync(flags, cts.Token); + } + var (Manifest, ManifestPath, ManifestContent) = LoadManifest(); + manifest = Manifest; + manifestPath = ManifestPath; + manifestContent = ManifestContent; + var workingDirectory = Path.GetDirectoryName(manifestPath); + return command switch + { + "build" => await CommandShell.RunScriptAsync(manifest.Build, workingDirectory!, cts.Token), + "test" => Test(workingDirectory!), + "pack" => Pack(workingDirectory!), + "publish" => await PublishAsync(workingDirectory!, cts.Token), + _ => throw new NotImplementedException(command), + }; +} + + + +async Task InstallAsync(Dictionary flags, CancellationToken token) +{ + if (!flags.TryGetValue("extensions", out var extensions)) + { + Logger.Error.MarkupLine("[maroon]No extensions specified to install.[/]"); + return 1; + } + if (extensions is not List extensionList || extensionList.Count == 0) + { + Logger.Error.MarkupLine("[maroon]Invalid or no extensions specified to install.[/]"); + return 1; + } + + var forceDownload = false; + if (flags.TryGetValue("force", out var forceFlag) && forceFlag is bool force) + { + forceDownload = force; + } + + + var distinctExtensions = extensionList.Select(e => e.Trim()).Distinct().ToList(); + + using var client = new RegistryClient(); + return await AnsiConsole.Progress() + .AutoClear(false) + .StartAsync(async ctx => + { + var progressTasks = new List(); + + // Create a progress task for each extension + foreach (var extension in distinctExtensions) + { + var task = ctx.AddTask($"[green]Installing {extension}...[/]", maxValue: 100); + progressTasks.Add(task); + } + + // Download each extension asynchronously + var downloadTasks = distinctExtensions.Select((extension, index) => + { + var progressTask = progressTasks[index]; + return client.DownloadChordAsync(extension, progressTask, forceDownload, token); + }).ToList(); + + // Wait for all downloads to complete + var results = await Task.WhenAll(downloadTasks); + + if (results.Any(r => r is false)) + { + return 1; + } + return 0; + }); +} + +int Test(string workingDirectory) +{ + return AnsiConsole.Status() + .Start("Testing...", ctx => + { + ctx.Spinner(Spinner.Known.Aesthetic); + Logger.Out.MarkupLine(":magnifying_glass_tilted_right: [white]Testing extension binary[/]"); + var chordBinary = IOUtil.ResolvePath(workingDirectory, manifest.Bin); + if (!File.Exists(chordBinary)) + { + Logger.Error.MarkupLine($":cross_mark: [maroon]Unable to find extension binary: {chordBinary}[/]"); + return 1; + } + using var module = WasmModule.FromFile(chordBinary); + ctx.Status("Checking extension imports..."); + if (module.ImportSection is null || module.ImportSection is { Size: 0 }) + { + Logger.Error.MarkupLine($":cross_mark: [maroon]Extension binary {chordBinary} does not import any functions[/]"); + return 1; + } + var wasiImports = module.ImportSection.Imports.Where(i => i.Module.Equals("wasi_snapshot_preview1")); + if (!wasiImports.Any()) + { + Logger.Error.MarkupLine($":cross_mark: [maroon]Extension binary {chordBinary} does not import any WASI functions[/]"); + return 1; + } + Logger.Out.MarkupLine("[green]WASI Imports:[/]"); + foreach (var import in wasiImports) + { + Logger.Out.MarkupLine($"[green]├──[/] [yellow]{import}[/]"); + } + + var compiler = manifest.Build.Compiler.ToCompilerString(); + var chordModule = $"chord_{compiler}"; + + var chordImports = module.ImportSection.Imports.Where(i => i.Module.Equals(chordModule)); + if (chordImports.Any()) + { + Logger.Out.MarkupLine("[green]Chord Imports:[/]"); + foreach (var import in chordImports) + { + Logger.Out.MarkupLine($"[green]├──[/] [yellow]{import}[/]"); + var functionType = module.GetImportedFunctionType(import); + if (functionType is null) + { + Logger.Error.MarkupLine($":cross_mark: [maroon]Extension binary {chordBinary} imports {import.Field} but the function type is not found.[/]"); + return 1; + } + Logger.Out.MarkupLine($"[green]│ ├──[/] [yellow]signature:[/] [blue]{module.GetImportedFunctionType(import)}[/]"); + var expectedSignature = SignatureValidator.GetFunctionSignature(compiler, import.Field); + if (expectedSignature is null) + { + Logger.Error.MarkupLine($":cross_mark: [maroon]Extension imports {import.Field} but no expected function signature was found.[/]"); + return 1; + } + if (!SignatureValidator.ValidateSignature(functionType, expectedSignature)) + { + Logger.Error.MarkupLine($":cross_mark: [maroon]Extension binary imports {import.Field} with an invalid signature[/]"); + Logger.Error.MarkupLine($"[maroon]Found: {functionType}[/]"); + Logger.Error.MarkupLine($"[maroon]Expected: {expectedSignature}[/]"); + return 1; + } + } + } + else + { + Logger.Error.MarkupLine($"[green]├──[/] :flashlight: [yellow]Extension binary does not import any Chord functions: {chordModule} [/]"); + } + ctx.Status("Checking extension exports..."); + if (module.ExportSection is null || module.ExportSection is { Size: 0 }) + { + Logger.Error.MarkupLine($":cross_mark: [maroon]Extension binary {chordBinary} does not export any functions[/]"); + return 1; + } + var hasStartOrInitialize = module.ExportSection.Exports.Any(e => (e.Name.Equals("_start") || e.Name.Equals("_initialize")) && e.Kind == ExportKind.Function); + if (!hasStartOrInitialize) + { + Logger.Error.MarkupLine($":cross_mark: [maroon]Extension binary {chordBinary} does not export a _start or _initialize function[/]"); + return 1; + } + var chordExports = module.ExportSection.Exports.Where(e => + { + if (e.Name.StartsWith("chord-") || e.Name.StartsWith("chord_")) + { + return true; + } + return false; + }); + if (!chordExports.Any()) + { + Logger.Error.MarkupLine($":cross_mark: [maroon]Extension binary {chordBinary} does not export any Chord functions[/]"); + return 1; + } + Logger.Out.MarkupLine("[green]Chord Exports:[/]"); + foreach (var export in chordExports) + { + Logger.Out.MarkupLine($"[green]├──[/] [yellow]{export}[/]"); + var functionType = module.GetExportedFunctionType(export); + if (functionType is null) + { + Logger.Error.MarkupLine($"[maroon]Extension binary {chordBinary} exports {export.Name} but the function type is not found.[/]"); + return 1; + } + Logger.Out.MarkupLine($"[green]│ ├──[/] [yellow]signature:[/] [blue]{module.GetExportedFunctionType(export)}[/]"); + var expectedSignature = SignatureValidator.GetFunctionSignature(compiler, export.Name); + if (expectedSignature is null) + { + Logger.Error.MarkupLine($":cross_mark: [maroon]Extension exports {export.Name} but no expected function signature was found.[/]"); + return 1; + } + if (!SignatureValidator.ValidateSignature(functionType, expectedSignature)) + { + Logger.Error.MarkupLine($":cross_mark: [maroon]Extension binary {chordBinary} exports {export.Name} with an invalid signature[/]"); + Logger.Error.MarkupLine($"[maroon]Found: {functionType}[/]"); + Logger.Error.MarkupLine($"[maroon]Expected: {expectedSignature}[/]"); + return 1; + } + + } + ctx.Status("Checking extension contributions..."); + // basic checks to ensure the extension binary is valid + if (manifest.Contributions.Type is ContributionType.Generator) + { + // the only export a generator should have is chord_compile + var chordCompile = chordExports.FirstOrDefault(e => e.Name.Equals("chord_compile") || e.Name.Equals("chord-compile")); + if (chordCompile is null) + { + Logger.Error.MarkupLine($":cross_mark: [maroon]Extension binary {chordBinary} does not export a chord_compile function[/]"); + return 1; + } + if (chordCompile.Kind != ExportKind.Function) + { + Logger.Error.MarkupLine($":cross_mark: [maroon]Extension binary {chordBinary} exports chord_compile as a {chordCompile.Kind} instead of a function[/]"); + return 1; + } + var functionType = module.GetExportedFunctionType(chordCompile); + if (functionType is null) + { + Logger.Error.MarkupLine($"[maroon]Extension binary {chordBinary} exports chord_compile but the function type is not found.[/]"); + return 1; + } + } + + return 0; + }); +} + +async Task PublishAsync(string workingDirectory, CancellationToken cancellationToken = default) +{ + if (manifest.IsPrivate) + { + Logger.Error.MarkupLine($":stop_sign: [maroon]Cannot publish private extension {manifest.Name}[/]"); + return 1; + } + if (string.IsNullOrWhiteSpace(authToken)) + { + Logger.Error.MarkupLine($":stop_sign: [maroon]No authentication token found.[/] Please set the CHORD_AUTH environment variable to your authentication token."); + return 1; + } + var buildResult = await CommandShell.RunScriptAsync(manifest.Build, workingDirectory, cancellationToken); + if (buildResult != 0) + { + return buildResult; + } + var testResults = Test(workingDirectory); + if (testResults != 0) + { + return testResults; + } + var packResults = Pack(workingDirectory); + if (packResults != 0) + { + return packResults; + } + using var client = new RegistryClient(authToken); + var archiveName = $"{manifest.Name}-{manifest.Version}.chord"; + var zipFilePath = Path.Combine(workingDirectory, archiveName); + try + { + var publishResponse = await client.PublishAsync(zipFilePath, cancellationToken); + if (publishResponse is null) + { + Logger.Error.MarkupLine($":stop_sign: [maroon]Error publishing extension:[/] no response from registry."); + return 1; + } + if (publishResponse.Success) + { + Logger.Out.MarkupLine($":party_popper: [green]Extension published successfully![/]"); + return 0; + } + else + { + Logger.Error.MarkupLine($":stop_sign: [maroon]Error publishing extension:[/] {publishResponse.Message}"); + return 1; + } + } + catch (Exception e) + { + Logger.Error.MarkupLine($":stop_sign: [maroon]Fatal error while publishing extension:[/] {e.Message}"); + Logger.Error.WriteException(e); + return 1; + } +} + + +int Pack(string workingDirectory) +{ + var testResult = Test(workingDirectory); + if (testResult != 0) + { + return testResult; + } + var binaryName = $"{manifest.Name}-{manifest.Version}.wasm"; + var archiveName = $"{manifest.Name}-{manifest.Version}.chord"; + var zipFilePath = Path.Combine(workingDirectory, archiveName); + var tempZipPath = Path.Combine(Path.GetTempPath(), Path.GetRandomFileName()); + + Logger.Out.MarkupLine(":package: [white]{0}[/]", archiveName); + + var encoding = new UTF8Encoding(false); + var chordBinary = IOUtil.ResolvePath(workingDirectory, manifest.Bin); + using var module = WasmModule.FromFile(chordBinary); + + // Function to log file details in a tree-like structure + void LogFileDetails(string path, long size, int level = 0) + { + var indent = new string(' ', level * 4); + var sizeInKb = size / 1024f; + Logger.Out.MarkupLine($"{indent}[green]├──[/] [blue]{Path.GetFileName(path)}[/] ([yellow]{sizeInKb:F2} KB[/])"); + } + + // Log the .wasm file being the main container + // Logger.Out.MarkupLine("[green]Main .wasm Container:[/]"); + LogFileDetails(chordBinary, new FileInfo(chordBinary).Length); + + long totalSize = 0L; // Keep track of the total size of packed files + var encodedManifest = new UTF8Encoding(false).GetBytes(manifestContent); + totalSize += encodedManifest.Length; + module.AddCustomSection(new("chord_manifest", encodedManifest)); + LogFileDetails(manifestPath, encodedManifest.Length, 1); + + // Adding custom sections to .wasm file + if (manifest.Pack is { Count: > 0 }) + { + foreach (var (alias, pack) in manifest.Pack) + { + var fullPath = IOUtil.ResolvePath(workingDirectory, pack.AuxilaryFile); + if (!File.Exists(fullPath)) + { + Logger.Error.MarkupLine($":cross_mark: [maroon]Unable to find auxiliary file {fullPath}[/]"); + return 1; + } + + var content = File.ReadAllBytes(fullPath); + var fileInfo = new FileInfo(fullPath); + totalSize += fileInfo.Length; + + // Log each file being packed into the .wasm + LogFileDetails(fullPath, fileInfo.Length, 1); + + var packedFile = new PackedFile(Path.GetFileName(fullPath), content, alias); + module.AddCustomSection(new($"chord_{alias}", packedFile.ToArray())); + } + } + + // Serialize .wasm file to a MemoryStream to get its new size + using var memoryStream = new MemoryStream(); + module.SerializeTo(memoryStream); + memoryStream.Seek(0, SeekOrigin.Begin); + + if (File.Exists(zipFilePath)) + { + File.Delete(zipFilePath); + } + if (flags.ContainsKey("nozip")) + { + using var s = File.Create(Path.Combine(workingDirectory, "chord.wasm")); + memoryStream.Seek(0, SeekOrigin.Begin); + memoryStream.CopyTo(s); + return 0; + } + + using (var zipArchive = ZipFile.Open(tempZipPath, ZipArchiveMode.Create)) + { + var zipEntry = zipArchive.CreateEntry(binaryName); + using (var entryStream = zipEntry.Open()) + { + memoryStream.CopyTo(entryStream); + } + + zipArchive.CreateEntryFromFile(manifestPath, Helpers.ManifestFileName); + + if (manifest.ReadMe is not null) + { + var readmePath = IOUtil.ResolvePath(workingDirectory, manifest.ReadMe); + if (!File.Exists(readmePath)) + { + Logger.Error.MarkupLine($":cross_mark: [maroon]Unable to find README file {readmePath}[/]"); + return 1; + } + zipArchive.CreateEntryFromFile(readmePath, "README.md"); + LogFileDetails(readmePath, new FileInfo(readmePath).Length, 0); + } + } + + File.Move(tempZipPath, zipFilePath); + + // Report the final size of the .wasm and the ZIP archive + Logger.Out.MarkupLine($"[green]Final Extension Size:[/] [yellow]{memoryStream.Length / 1024f:F2} KB[/]"); + Logger.Out.MarkupLine($"[green]Extension Archive Created at:[/] [blue]{zipFilePath}[/]"); + Logger.Out.MarkupLine($"[green]Extension Archive Size:[/] [yellow]{new FileInfo(zipFilePath).Length / 1024f:F2} KB[/]"); + return 0; +} + +(ChordManifest Manifest, string ManifestPath, string ManifestContent) LoadManifest() +{ + //preamble nonsense + var manifestPath = Helpers.LocateChordManifest(); + if (string.IsNullOrWhiteSpace(manifestPath)) + { + Logger.Error.MarkupLine("[maroon]No chord.json file found.[/]"); + return default; + } + if (Helpers.TryReadFile(manifestPath, out var manifestContent) is false) + { + Logger.Error.MarkupLine($"[maroon]Unable to read contents of chord.json file at {manifestPath}[/]"); + return default; + } + var source = new Source(manifestPath, manifestContent); + var sourceRepo = new InMemorySourceRepository(); + sourceRepo.Register(source); + + var workingDirectory = Path.GetDirectoryName(manifestPath); + if (workingDirectory is null) + { + Logger.Error.MarkupLine($"[maroon]Unable to determine working directory from {manifestPath}[/]"); + return default; + } + try + { + return (ChordManifest.FromJson(manifestContent), manifestPath, manifestContent); + } + catch (JsonException ex) + { + ex.Render(sourceRepo, manifestPath); + return default; + } +} \ No newline at end of file diff --git a/extensions/chordc/Shells/CommandShell.cs b/extensions/chordc/Shells/CommandShell.cs new file mode 100644 index 00000000..4f1e594c --- /dev/null +++ b/extensions/chordc/Shells/CommandShell.cs @@ -0,0 +1,134 @@ +using System.Collections; +using System.Diagnostics; +using System.Runtime.InteropServices; +using System.Text; +using Chord.Common; +using Chord.Compiler.Internal; +using Spectre.Console; +namespace Chord.Compiler.Shells; + +internal static class CommandShell +{ + public static async Task RunScriptAsync(BuildCommand buildCommand, string workingDirectory, CancellationToken cancellationToken) + { + + if (buildCommand.Shell is not ScriptShell.None && ShellHelpers.IsShellOnHost(buildCommand.Shell) is not true) + { + throw new Exception($"The {buildCommand.Shell} shell is not available on this host."); + } + var shell = buildCommand.Shell is not ScriptShell.None ? buildCommand.Shell : ShellHelpers.GetDefaultShell(); + + var contents = buildCommand.Script; + var multiLines = contents.Replace("\r\n", "\n").TrimEnd('\n').Split('\n'); + Logger.Out.WriteLine($"Executing Script:"); + foreach (var line in multiLines) + { + // Bright Cyan color + Logger.Out.MarkupLine("[cyan]{0}[/]", line); + } + + string argFormat = ShellHelpers.GetScriptArgumentsFormat(shell); + + var tempDir = System.IO.Path.GetTempPath(); + var scriptFilePath = Path.Combine(tempDir, $"{Guid.NewGuid()}{ShellHelpers.GetScriptFileExtension(shell)}"); + // Format arg string with script path + var arguments = string.Format(argFormat, scriptFilePath); + contents = ShellHelpers.FixUpScriptContents(shell, contents); + if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) + { + // Normalize Windows line endings + contents = contents.Replace("\r\n", "\n").Replace("\n", "\r\n"); + } + File.WriteAllText(scriptFilePath, contents, new UTF8Encoding(false)); + + var startInfo = new ProcessStartInfo + { + FileName = ShellHelpers.GetShellExecutable(shell), + Arguments = arguments, + UseShellExecute = false, + WorkingDirectory = workingDirectory, + RedirectStandardOutput = true, + RedirectStandardError = true, + WindowStyle = ProcessWindowStyle.Hidden + }; + // Inheriting all current environment variables + foreach (var envVar in Environment.GetEnvironmentVariables().Cast()) + { + if (envVar.Key is string key && envVar.Value is string value) + { + startInfo.EnvironmentVariables[key] = value; + } + } + if (buildCommand.Env is { Count: > 0 }) + { + foreach (var (key, value) in buildCommand.Env) + { + startInfo.EnvironmentVariables[key] = value; + } + } + + using var process = new Process + { + StartInfo = startInfo + }; + + process.Start(); + try + { + await Task.WhenAll( + process.WaitForExitAsync(cancellationToken), + ReadStreamAsync(process.StandardOutput, true, cancellationToken), + ReadStreamAsync(process.StandardError, false, cancellationToken) + ); + } + catch (Exception ex) when (ex is OperationCanceledException or TaskCanceledException) + { + // The task was cancelled, so we don't need to do anything here. + } + finally + { + File.Delete(scriptFilePath); + } + + if (cancellationToken.IsCancellationRequested) + { + Logger.Error.MarkupLine("[red]Script execution cancelled[/]"); + } + if (process.ExitCode != 0) + { + Logger.Error.MarkupLine("[red]Script exited with non-zero exit code {0}[/]", process.ExitCode); + } + return process.ExitCode; + } + + private static async Task ReadStreamAsync(StreamReader streamReader, bool isStandardOutput, CancellationToken cancellationToken) + { + try + { + while (!cancellationToken.IsCancellationRequested) + { + var line = await streamReader.ReadLineAsync(cancellationToken); + if (line == null) + { + break; // End of stream + } + if (isStandardOutput) + { + // Bright Green color + var escaped = Markup.Escape(line); + Logger.Out.MarkupLine("[white]{0}[/]", escaped); + } + else + { + // Bright Red color + var escaped = Markup.Escape(line); + Logger.Error.MarkupLine("[red]{0}[/]", escaped); + } + } + } + catch (OperationCanceledException) + { + // Ignore + } + } +} \ No newline at end of file diff --git a/extensions/chordc/Shells/ShellHelpers.cs b/extensions/chordc/Shells/ShellHelpers.cs new file mode 100644 index 00000000..a6feb206 --- /dev/null +++ b/extensions/chordc/Shells/ShellHelpers.cs @@ -0,0 +1,139 @@ +using System.Runtime.InteropServices; +using Chord.Common; +using Chord.Compiler.Internal.Utils; + +namespace Chord.Compiler.Shells; + +internal static class ShellHelpers +{ + private static readonly Dictionary _defaultArguments = new() + { + [ScriptShell.Cmd] = "/D /E:ON /V:OFF /S /C \"CALL \"{0}\"\"", + [ScriptShell.Pwsh] = "-command \". '{0}'\"", + [ScriptShell.Powershell] = "-command \". '{0}'\"", + [ScriptShell.Bash] = "--noprofile --norc -e -o pipefail {0}", + [ScriptShell.Sh] = "-e {0}", + [ScriptShell.Python] = "{0}" + }; + + private static readonly Dictionary _extensions = new() + { + [ScriptShell.Cmd] = ".cmd", + [ScriptShell.Pwsh] = ".ps1", + [ScriptShell.Powershell] = ".ps1", + [ScriptShell.Bash] = ".sh", + [ScriptShell.Sh] = ".sh", + [ScriptShell.Python] = ".py" + }; + + internal static string GetShellExecutable(ScriptShell shell) + { + var executable = shell switch + { + ScriptShell.Cmd => "cmd", + ScriptShell.Pwsh => "pwsh", + ScriptShell.Powershell => "powershell", + ScriptShell.Bash => "bash", + ScriptShell.Sh => "sh", + ScriptShell.Python => "python3", + _ => throw new ArgumentException($"Unknown shell {shell}") + }; + if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) + { + executable += IOUtil.ExeExtension; + } + return executable; + } + + internal static string GetScriptArgumentsFormat(ScriptShell shell) + { + if (_defaultArguments.TryGetValue(shell, out var argFormat)) + { + return argFormat; + } + return ""; + } + + internal static string GetScriptFileExtension(ScriptShell shell) + { + if (_extensions.TryGetValue(shell, out var extension)) + { + return extension; + } + return ""; + } + + internal static string FixUpScriptContents(ScriptShell shell, string contents) + { + switch (shell) + { + case ScriptShell.Cmd: + // Note, use @echo off instead of using the /Q command line switch. + // When /Q is used, echo can't be turned on. + contents = $"@echo off{Environment.NewLine}{contents}"; + break; + case ScriptShell.Pwsh: + case ScriptShell.Powershell: + var prepend = "$ErrorActionPreference = 'stop'"; + var append = @"if ((Test-Path -LiteralPath variable:\LASTEXITCODE)) { exit $LASTEXITCODE }"; + contents = $"{prepend}{Environment.NewLine}{contents}{Environment.NewLine}{append}"; + break; + } + return contents; + } + + internal static (string shellCommand, string shellArgs) ParseShellOptionString(string shellOption) + { + var shellStringParts = shellOption.Split(" ", 2); + if (shellStringParts.Length == 2) + { + return (shellCommand: shellStringParts[0], shellArgs: shellStringParts[1]); + } + else if (shellStringParts.Length == 1) + { + return (shellCommand: shellStringParts[0], shellArgs: ""); + } + else + { + throw new ArgumentException($"Failed to parse COMMAND [..ARGS] from {shellOption}"); + } + } + + internal static bool IsShellOnHost(ScriptShell shell) => shell switch + { + ScriptShell.Cmd => PathUtil.IsProgramOnPath("cmd"), + ScriptShell.Pwsh => PathUtil.IsProgramOnPath("pwsh"), + ScriptShell.Powershell => PathUtil.IsProgramOnPath("powershell"), + ScriptShell.Bash => PathUtil.IsProgramOnPath("bash"), + ScriptShell.Sh => PathUtil.IsProgramOnPath("sh"), + ScriptShell.Python => PathUtil.IsProgramOnPath("python"), + _ => throw new ArgumentException($"Unknown shell {shell}") + }; + + internal static ScriptShell GetDefaultShell() + { + if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) + { + if (PathUtil.IsProgramOnPath("pwsh")) + { + return ScriptShell.Pwsh; + } + else if (PathUtil.IsProgramOnPath("powershell")) + { + return ScriptShell.Powershell; + } + } + else + { + if (PathUtil.IsProgramOnPath("bash")) + { + return ScriptShell.Bash; + } + else if (PathUtil.IsProgramOnPath("sh")) + { + return ScriptShell.Sh; + } + } + throw new PlatformNotSupportedException("Unable to find a supported shell."); + } +} \ No newline at end of file diff --git a/extensions/chordc/chordc.csproj b/extensions/chordc/chordc.csproj new file mode 100644 index 00000000..6e914c4c --- /dev/null +++ b/extensions/chordc/chordc.csproj @@ -0,0 +1,64 @@ + + + + Exe + net8.0 + enable + enable + true + true + Size + full + true + true + false + 0.0.0.1 + 0.0.1-alpha + + + + + + + + + + + + + + + + Windows + OSX + Linux + + + + $(DefineConstants);RELEASE + true + false + false + false + false + false + true + false + false + true + true + + + + + + $(DefineConstants);OS_WINDOWS + + + $(DefineConstants);OS_OSX + + + $(DefineConstants);OS_LINUX + + + diff --git a/extensions/chordc/gen.sh b/extensions/chordc/gen.sh new file mode 100755 index 00000000..100d7323 --- /dev/null +++ b/extensions/chordc/gen.sh @@ -0,0 +1,67 @@ +#!/bin/bash + +# Check if a JSON file and a C# file are provided +if [ "$#" -ne 2 ]; then + echo "Usage: $0 " + exit 1 +fi + +json_file="$1" +cs_file="$2" + +# Generate the C# code from the JSON file and save it to a temp file +temp_code_file="temp_generated_code.cs" +{ + echo ' _signatureLookup = new Dictionary>()' + echo ' {' + jq -r '. as $in | keys[] | . as $compiler | + "\t\t\t\t{\n\t\t\t\t\t\""+$compiler+"\",\n\t\t\t\t\tnew ()\n\t\t\t\t\t{\n" + + ($in[$compiler] | to_entries | map( + "\t\t\t\t\t\tnew (\""+.key+"\",\n\t\t\t\t\t\t\t[\n\t\t\t\t\t\t\t\t" + + ( [.value.parameters[] | + if . == "i32" then "WebAssemblyValueType.Int32" + elif . == "i64" then "WebAssemblyValueType.Int64" + elif . == "f32" then "WebAssemblyValueType.Float32" + elif . == "f64" then "WebAssemblyValueType.Float64" + else . end + ] | join(",\n\t\t\t\t\t\t\t\t") ) + + "\n\t\t\t\t\t\t\t],\n\t\t\t\t\t\t\t[\n\t\t\t\t\t\t\t\t" + + (if .value.returns then + ([.value.returns[] | + if . == "i32" then "WebAssemblyValueType.Int32" + elif . == "i64" then "WebAssemblyValueType.Int64" + elif . == "f32" then "WebAssemblyValueType.Float32" + elif . == "f64" then "WebAssemblyValueType.Float64" + else . end + ] | join(",\n\t\t\t\t\t\t\t\t")) + else "" end) + + "\n\t\t\t\t\t\t\t]\n\t\t\t\t\t\t)" + ) | join(",\n\n") ) + + "\n\t\t\t\t\t}\n\t\t\t\t},"' + echo ' };' +} <"$json_file" >"$temp_code_file" + +# Use awk to insert the generated code into the static constructor +awk -v temp_file="$temp_code_file" ' + /\/\/ Begin generated code/ { + print + while((getline line < temp_file) > 0) { + print line + } + close(temp_file) + skip = 1 + next + } + /\/\/ End generated code/ { + print + skip = 0 + next + } + skip {next} + {print} +' "$cs_file" >tmp_file && mv tmp_file "$cs_file" + +# Clean up temp file +rm "$temp_code_file" +# Format the C# code +dotnet format --include "$cs_file" diff --git a/extensions/chordc/signatures.json b/extensions/chordc/signatures.json new file mode 100644 index 00000000..cae2120b --- /dev/null +++ b/extensions/chordc/signatures.json @@ -0,0 +1,44 @@ +{ + "tinygo": { + "write_line": { + "parameters": ["i32", "i32"], + "returns": [] + }, + "write_error": { + "parameters": ["i32", "i32"], + "returns": [] + }, + "get_bebopc_version": { + "parameters": ["i32"], + "returns": [] + }, + "chord_compile": { + "parameters": ["i32", "i32", "i32"], + "returns": [] + } + }, + "as": { + "write_line": { + "parameters": ["i32"], + "returns": [] + }, + "write_error": { + "parameters": ["i32"], + "returns": [] + }, + "get_bebopc_version": { + "parameters": [], + "returns": ["i32"] + }, + "chord_compile": { + "parameters": ["i32"], + "returns": ["i32"] + } + }, + "javy": { + "chord-compile": { + "parameters": [], + "returns": [] + } + } +} diff --git a/extensions/edks/assemblyscript/.prettierrc b/extensions/edks/assemblyscript/.prettierrc new file mode 100644 index 00000000..ca96619b --- /dev/null +++ b/extensions/edks/assemblyscript/.prettierrc @@ -0,0 +1,4 @@ +{ + "pluginSearchDirs": ["node_modules"], + "plugins": ["assemblyscript-prettier"] +} diff --git a/extensions/edks/assemblyscript/asconfig.json b/extensions/edks/assemblyscript/asconfig.json new file mode 100644 index 00000000..02085fd5 --- /dev/null +++ b/extensions/edks/assemblyscript/asconfig.json @@ -0,0 +1,26 @@ +{ + "targets": { + "debug": { + "outFile": "build/debug.wasm", + "textFile": "build/debug.wat", + "sourceMap": true, + "debug": true + }, + "release": { + "outFile": "build/release.wasm", + "textFile": "build/release.wat", + "sourceMap": true, + "optimizeLevel": 3, + "shrinkLevel": 0, + "converge": false, + "noAssert": true, + "optimize": true + } + }, + "extends": "./node_modules/@assemblyscript/wasi-shim/asconfig.json", + "options": { + "transform": ["visitor-as/dist/examples/exportAs"], + "exportStart": "_initialize", + "exportRuntime": true + } +} diff --git a/extensions/edks/assemblyscript/assembly/index.ts b/extensions/edks/assemblyscript/assembly/index.ts new file mode 100644 index 00000000..b9299820 --- /dev/null +++ b/extensions/edks/assemblyscript/assembly/index.ts @@ -0,0 +1,16 @@ +import { + deserializeGeneratorContext, + getBebopcVersion, + writeError, + writeLine, +} from "./std"; + +// @ts-ignore: decorator +@exportAs("chord_compile") +export function compile(context: string): string { + const generatorContext = deserializeGeneratorContext(context); + writeLine("Hello from assemblyscript!"); + writeError("This is an error from assemblyscript!"); + writeLine("BebopC version: " + getBebopcVersion()); + return context; +} diff --git a/extensions/edks/assemblyscript/assembly/std.ts b/extensions/edks/assemblyscript/assembly/std.ts new file mode 100644 index 00000000..69af44f6 --- /dev/null +++ b/extensions/edks/assemblyscript/assembly/std.ts @@ -0,0 +1,700 @@ +import { JSON } from "assemblyscript-json/assembly"; +import { Value } from "assemblyscript-json/assembly/JSON"; + +// @ts-ignore: decorator +@external("chord_as", "write_line") +export declare function writeLine(s: string): void; +// @ts-ignore: decorator +@external("chord_as", "write_error") +export declare function writeError(s: string): void; +// @ts-ignore: decorator +@external("chord_as", "get_bebopc_version") +export declare function getBebopcVersion(): string; + +export function deserializeGeneratorContext(context: string): GeneratorContext { + // Parse an object using the JSON object + const json: JSON.Obj = JSON.parse(context); + const definitionsObj = json.getObj("definitions"); + if (definitionsObj === null) { + throw new Error("Missing definitions"); + } + const definitions = new Map(); + definitionsObj.keys.forEach((key) => { + const defObj = definitionsObj.getObj(key); + if (defObj === null) { + throw new Error(`Missing definition for ${key}`); + } + const def = deserializeDefinition(defObj); + definitions.set(key, def); + }); + const services = new Map(); + const servicesObj = json.getObj("services"); + if (servicesObj !== null) { + servicesObj.keys.forEach((key) => { + const serviceObj = servicesObj.getObj(key); + if (serviceObj === null) { + throw new Error(`Missing service for ${key}`); + } + const service = deserializeService(serviceObj); + services.set(key, service); + }); + } + + const constants = new Map(); + const constantsObj = json.getObj("constants"); + if (constantsObj !== null) { + constantsObj.keys.forEach((key) => { + const constantObj = constantsObj.getObj(key); + if (constantObj === null) { + throw new Error(`Missing constant for ${key}`); + } + const constant = deserializeConstant(constantObj); + constants.set(key, constant); + }); + } + + const config = new GeneratorConfig(); + const configObj = json.getObj("config"); + if (configObj === null) { + throw new Error("Missing config"); + } + const aliasRaw = configObj.getString("alias"); + if (aliasRaw !== null) { + config.alias = aliasRaw.valueOf(); + } + const outFileRaw = configObj.getString("outFile"); + if (outFileRaw !== null) { + config.outFile = outFileRaw.valueOf(); + } + const namespaceRaw = configObj.getString("namespace"); + if (namespaceRaw !== null) { + config.namespace = namespaceRaw.valueOf(); + } + const emitNoticeRaw = configObj.getBool("emitNotice"); + if (emitNoticeRaw !== null) { + config.emitNotice = emitNoticeRaw.valueOf(); + } + const emitBinarySchemaRaw = configObj.getBool("emitBinarySchema"); + if (emitBinarySchemaRaw !== null) { + config.emitBinarySchema = emitBinarySchemaRaw.valueOf(); + } + const servicesRaw = configObj.getString("services"); + if (servicesRaw !== null) { + config.services = deserializeServicesType(servicesRaw.valueOf()); + } + + config.options = new Map(); + const optionsObj = configObj.getObj("options"); + if (optionsObj !== null) { + optionsObj.keys.forEach((key) => { + const option = optionsObj.getString(key); + if (option === null) { + throw new Error(`Missing option for ${key}`); + } + config.options.set(key, option.valueOf()); + }); + } + const generatorContext = new GeneratorContext(); + generatorContext.definitions = definitions; + generatorContext.services = services; + generatorContext.constants = constants; + generatorContext.config = config; + + return generatorContext; +} + +function deserializeServicesType(str: string): TempoServices { + switch (str) { + case "none": + return TempoServices.None; + case "both": + return TempoServices.Both; + case "client": + return TempoServices.Client; + case "server": + return TempoServices.Server; + default: + throw new Error(`Unknown services type: ${str}`); + } +} +function deserializeDefinition(obj: JSON.Obj): Definition { + const kind = obj.getString("kind"); + if (kind === null) { + throw new Error("Missing kind"); + } + + switch (kind.toString()) { + case "struct": + return deserializeStruct(obj); + case "message": + return deserializeMessage(obj); + case "union": + return deserializeUnion(obj); + case "enum": + return deserializeEnum(obj); + case "service": + return deserializeService(obj); + case "constant": + return deserializeConstant(obj); + default: + throw new Error(`Unknown kind: ${kind}`); + } +} + +function deserializeConstant(obj: JSON.Obj): ConstantDefinition { + const constant = new ConstantDefinition(); + constant.kind = "constant"; + const docsRaw = obj.getString("documentation"); + if (docsRaw !== null) { + constant.documentation = docsRaw.valueOf(); + } + const typeRaw = obj.getString("type"); + if (typeRaw !== null) { + constant.type = new BaseType(typeRaw.valueOf()); + } + + if (constant.type.name === "bool") { + const valueRaw = obj.getBool("value"); + if (valueRaw !== null) { + constant.value = new ValueContainer( + valueRaw.valueOf().toString(), + constant.type, + ); + } + } else { + const valueRaw = obj.getString("value"); + if (valueRaw !== null) { + constant.value = new ValueContainer(valueRaw.valueOf(), constant.type); + } + } + return constant; +} + +function deserializeService(obj: JSON.Obj): ServiceDefinition { + const service = new ServiceDefinition(); + service.kind = "service"; + const docsRaw = obj.getString("documentation"); + if (docsRaw !== null) { + service.documentation = docsRaw.valueOf(); + } + service.decorators = deserializeDecorators(obj); + service.methods = deserializeMethods(obj); + return service; +} + +function deserializeMethods(obj: JSON.Obj): Map { + const methods = new Map(); + const methodsObj = obj.getObj("methods"); + if (methodsObj !== null) { + methodsObj.keys.forEach((key) => { + const methodObj = methodsObj.getObj(key); + if (methodObj === null) { + throw new Error(`Missing method for ${key}`); + } + const method = deserializeMethod(methodObj); + methods.set(key, method); + }); + } + return methods; +} + +function deserializeMethod(obj: JSON.Obj): Method { + const method = new Method(); + const docsRaw = obj.getString("documentation"); + if (docsRaw !== null) { + method.documentation = docsRaw.valueOf(); + } + method.decorators = deserializeDecorators(obj); + method.type = deserializeMethodType(obj); + const requestTypeRaw = obj.getString("requestType"); + if (requestTypeRaw !== null) { + method.requestType = requestTypeRaw.valueOf(); + } + const responseTypeRaw = obj.getString("responseType"); + if (responseTypeRaw !== null) { + method.responseType = responseTypeRaw.valueOf(); + } + const idRaw = obj.getInteger("id"); + if (idRaw !== null) { + method.id = idRaw.valueOf(); + } + return method; +} + +function deserializeMethodType(obj: JSON.Obj): MethodType { + const type = obj.getString("type"); + if (type === null) { + throw new Error("Missing method type"); + } + switch (type.toString().toLowerCase()) { + case "unary": + return MethodType.Unary; + case "serverstream": + return MethodType.ServerStream; + case "clientstream": + return MethodType.ClientStream; + case "duplexstream": + return MethodType.DuplexStream; + default: + throw new Error(`Unknown method type: ${type}`); + } +} + +function deserializeEnum(obj: JSON.Obj): EnumDefinition { + const enumDef = new EnumDefinition(); + enumDef.kind = "enum"; + const docsRaw = obj.getString("documentation"); + if (docsRaw !== null) { + enumDef.documentation = docsRaw.valueOf(); + } + enumDef.decorators = deserializeDecorators(obj); + const baseTypeRaw = obj.getString("baseType"); + if (baseTypeRaw !== null) { + enumDef.baseType = new BaseType(baseTypeRaw.valueOf()); + } + const isBitFlagsRaw = obj.getBool("isBitFlags"); + if (isBitFlagsRaw !== null) { + enumDef.isBitFlags = isBitFlagsRaw.valueOf(); + } + const minimalEncodedSizeRaw = obj.getInteger("minimalEncodedSize"); + if (minimalEncodedSizeRaw !== null) { + enumDef.minimalEncodedSize = minimalEncodedSizeRaw.valueOf(); + } + + enumDef.members = deserializeEnumMembers(obj, enumDef.baseType); + return enumDef; +} + +function deserializeEnumMembers( + obj: JSON.Obj, + baseType: BaseType, +): Map { + const members = new Map(); + const membersObj = obj.getObj("members"); + if (membersObj !== null) { + membersObj.keys.forEach((key) => { + const memberObj = membersObj.getObj(key); + if (memberObj === null) { + throw new Error(`Missing member for ${key}`); + } + const member = deserializeEnumMember(memberObj, baseType); + members.set(key, member); + }); + } + return members; +} + +function deserializeEnumMember(obj: JSON.Obj, baseType: BaseType): EnumMember { + const member = new EnumMember(); + const docsRaw = obj.getString("documentation"); + if (docsRaw !== null) { + member.documentation = docsRaw.valueOf(); + } + const constantRaw = obj.getInteger("constant"); + if (constantRaw !== null) { + member.constant = constantRaw.valueOf(); + } + member.decorators = deserializeDecorators(obj); + return member; +} + +function deserializeUnion(obj: JSON.Obj): UnionDefinition { + const union = new UnionDefinition(); + union.kind = "union"; + const docsRaw = obj.getString("documentation"); + if (docsRaw !== null) { + union.documentation = docsRaw.valueOf(); + } + union.decorators = deserializeDecorators(obj); + const minimalEncodedSizeRaw = obj.getInteger("minimalEncodedSize"); + if (minimalEncodedSizeRaw !== null) { + union.minimalEncodedSize = minimalEncodedSizeRaw.valueOf(); + } + const discriminatorInParentRaw = obj.getInteger("discriminatorInParent"); + if (discriminatorInParentRaw !== null) { + union.discriminatorInParent = discriminatorInParentRaw.valueOf(); + } + union.branches = deserializeBranches(obj); + return union; +} + +function deserializeBranches(obj: JSON.Obj): Map { + const branches = new Map(); + const branchesObj = obj.getObj("branches"); + if (branchesObj !== null) { + branchesObj.keys.forEach((key) => { + const branchObj = branchesObj.getObj(key); + if (branchObj === null) { + throw new Error(`Missing branch for ${key}`); + } + const discriminatorRaw = branchObj.getInteger(key); + if (discriminatorRaw === null) { + throw new Error(`Missing discriminator for ${key}`); + } + + branches.set(discriminatorRaw.valueOf(), key); + }); + } + return branches; +} + +function deserializeStruct(obj: JSON.Obj): StructDefinition { + const struct = new StructDefinition(); + struct.kind = "struct"; + const docsRaw = obj.getString("documentation"); + if (docsRaw !== null) { + struct.documentation = docsRaw.valueOf(); + } + + struct.decorators = deserializeDecorators(obj); + const isReadOnlyRaw = obj.getBool("isReadOnly"); + if (isReadOnlyRaw !== null) { + struct.isReadOnly = isReadOnlyRaw.valueOf(); + } + const isFixedSizeRaw = obj.getBool("isFixedSize"); + if (isFixedSizeRaw !== null) { + struct.isFixedSize = isFixedSizeRaw.valueOf(); + } + + const minimalEncodedSizeRaw = obj.getInteger("minimalEncodedSize"); + if (minimalEncodedSizeRaw !== null) { + struct.minimalEncodedSize = minimalEncodedSizeRaw.valueOf(); + } + const discriminatorInParentRaw = obj.getInteger("discriminatorInParent"); + if (discriminatorInParentRaw !== null) { + struct.discriminatorInParent = discriminatorInParentRaw.valueOf(); + } + struct.fields = deserializeFields(obj); + return struct; +} + +function deserializeMessage(obj: JSON.Obj): MessageDefinition { + const message = new MessageDefinition(); + message.kind = "message"; + const docsRaw = obj.getString("documentation"); + if (docsRaw !== null) { + message.documentation = docsRaw.valueOf(); + } + message.decorators = deserializeDecorators(obj); + const minimalEncodedSizeRaw = obj.getInteger("minimalEncodedSize"); + if (minimalEncodedSizeRaw !== null) { + message.minimalEncodedSize = minimalEncodedSizeRaw.valueOf(); + } + const discriminatorInParentRaw = obj.getInteger("discriminatorInParent"); + if (discriminatorInParentRaw !== null) { + message.discriminatorInParent = discriminatorInParentRaw.valueOf(); + } + + message.fields = deserializeFields(obj); + return message; +} + +function deserializeFields(obj: JSON.Obj): Map { + const fields = new Map(); + const fieldsObj = obj.getObj("fields"); + if (fieldsObj !== null) { + fieldsObj.keys.forEach((key) => { + const fieldObj = fieldsObj.getObj(key); + if (fieldObj === null) { + throw new Error(`Missing field for ${key}`); + } + const field = deserializeField(fieldObj); + fields.set(key, field); + }); + } + return fields; +} + +function deserializeField(obj: JSON.Obj): Field { + const field = new Field(); + const docsRaw = obj.getString("documentation"); + if (docsRaw !== null) { + field.documentation = docsRaw.valueOf(); + } + field.decorators = deserializeDecorators(obj); + field.type = deserializeFieldType(obj); + const indexRaw = obj.getInteger("index"); + if (indexRaw !== null) { + field.index = indexRaw.valueOf(); + } + return field; +} + +function deserializeFieldType(obj: JSON.Obj): FieldType { + const type = obj.getString("type"); + if (type === null) { + throw new Error("Missing field type"); + } + switch (type.toString()) { + case "array": + const arrayObj = obj.getObj("array"); + if (arrayObj === null) { + throw new Error("Missing array type"); + } + return deserializeArrayType(arrayObj); + case "map": + const mapObj = obj.getObj("map"); + if (mapObj === null) { + throw new Error("Missing map type"); + } + return deserializeMapType(mapObj); + default: + return new BaseType(type.valueOf()); + } +} + +function deserializeArrayType(obj: JSON.Obj): ArrayType { + const depthObj = obj.getNum("depth"); + if (depthObj === null) { + throw new Error("Missing array depth"); + } + const depth = depthObj.valueOf(); + const memberTypeObj = obj.getString("memberType"); + if (memberTypeObj === null) { + throw new Error("Missing array member type"); + } + return new ArrayType(depth, new BaseType(memberTypeObj.valueOf())); +} + +function deserializeMapType(obj: JSON.Obj): MapType { + const keyTypeStr = obj.getString("keyType"); + if (keyTypeStr === null) { + throw new Error("Missing map key type"); + } + const valueTypeObj = obj.getString("valueType"); + if (valueTypeObj === null) { + throw new Error("Missing map value type"); + } + const keyType = keyTypeStr.valueOf(); + const valueType = valueTypeObj.valueOf(); + if (valueType === "array") { + const arrayTypeObj = obj.getObj("array"); + if (arrayTypeObj === null) { + throw new Error("Missing array type"); + } + return new MapType( + new BaseType(keyType), + deserializeArrayType(arrayTypeObj), + ); + } else if (valueType === "map") { + const mapTypeObj = obj.getObj("map"); + if (mapTypeObj === null) { + throw new Error("Missing map type"); + } + return new MapType(new BaseType(keyType), deserializeMapType(mapTypeObj)); + } + return new MapType(new BaseType(keyType), new BaseType(valueType)); +} + +function deserializeDecorators(obj: JSON.Obj): Map { + const decorators = new Map(); + const decoratorsObj = obj.getObj("decorators"); + if (decoratorsObj !== null) { + decoratorsObj.keys.forEach((key) => { + const decoratorObj = decoratorsObj.getObj(key); + if (decoratorObj === null) { + throw new Error(`Missing decorator for ${key}`); + } + const decorator = deserializeDecorator(decoratorObj); + decorators.set(key, decorator); + }); + } + return decorators; +} + +function deserializeDecorator(obj: JSON.Obj): Decorator { + const decorator = new Decorator(); + const argsObj = obj.getObj("arguments"); + if (argsObj !== null) { + const args = new Map(); + argsObj.keys.forEach((key) => { + const argObj = argsObj.getObj(key); + if (argObj === null) { + throw new Error(`Missing decorator argument for ${key}`); + } + const arg = deserializeDecoratorArgument(argObj); + args.set(key, arg); + }); + decorator.arguments = args; + } + return decorator; +} + +function deserializeDecoratorArgument(obj: JSON.Obj): DecoratorArgument { + const arg = new DecoratorArgument(); + const type = obj.getString("type"); + if (type === null) { + throw new Error("Missing decorator argument type"); + } + arg.type = new BaseType(type.valueOf()); + const value = obj.getString("value"); + if (value === null) { + throw new Error("Missing decorator argument value"); + } + arg.value = new ValueContainer(value.valueOf(), arg.type); + return arg; +} + +class GeneratorContext { + definitions!: Map; + services!: Map; + constants!: Map; + config!: GeneratorConfig; +} + +const baseTypes = [ + "byte", + "uint8", + "int16", + "uint16", + "int32", + "uint32", + "int64", + "uint64", + "float32", + "float64", + "bool", + "string", + "guid", + "date", +]; + +class BaseType { + name: string; + constructor(name: string) { + if (!baseTypes.includes(name)) { + throw new Error(`Invalid base type: ${name}`); + } + this.name = name; + } +} + +class ValueContainer { + value: string; + type: BaseType; + constructor(value: string, type: BaseType) { + this.value = value; + this.type = type; + } +} + +class ArrayType { + depth: number; + memberType: FieldType; + constructor(depth: number, memberType: FieldType) { + this.depth = depth; + this.memberType = memberType; + } +} + +class MapType { + keyType: BaseType; + valueType: FieldType; + + constructor(keyType: BaseType, valueType: FieldType) { + this.keyType = keyType; + this.valueType = valueType; + } +} + +type FieldType = BaseType | ArrayType | MapType; + +class Decorator { + arguments!: Map; +} + +class DecoratorArgument { + type!: BaseType; + value!: ValueContainer; +} + +class Definition { + kind!: string; + documentation!: string; + decorators!: Map; + parent!: string; +} + +class RecordDefinition extends Definition { + minimalEncodedSize!: number; + discriminatorInParent!: number; +} + +class FieldsDefinition extends RecordDefinition { + fields!: Map; +} + +class StructDefinition extends FieldsDefinition { + isReadOnly!: bool; + isFixedSize!: bool; +} + +class MessageDefinition extends FieldsDefinition { + +} + +class Field { + documentation!: string; + decorators!: Map; + type!: FieldType; + index!: number; +} + +class UnionDefinition extends RecordDefinition { + branches!: Map; +} + +class EnumDefinition extends Definition { + isBitFlags!: bool; + minimalEncodedSize!: number; + baseType!: BaseType; + members!: Map; +} + +class EnumMember { + constant!: number; + documentation!: string; + decorators!: Map; +} + +class ServiceDefinition extends Definition { + methods!: Map; +} + +class Method { + documentation!: string; + decorators!: Map; + type!: MethodType; + requestType!: string; + responseType!: string; + id!: number; +} + +enum MethodType { + Unary, + ServerStream, + ClientStream, + DuplexStream, +} + +class ConstantDefinition extends Definition { + type!: BaseType; + value!: ValueContainer; +} + +class GeneratorConfig { + alias!: string; + outFile!: string; + namespace!: string; + emitNotice!: bool; + emitBinarySchema!: bool; + services!: TempoServices; + options!: Map; +} + +enum TempoServices { + None, + Both, + Client, + Server, +} diff --git a/extensions/edks/assemblyscript/assembly/tsconfig.json b/extensions/edks/assemblyscript/assembly/tsconfig.json new file mode 100644 index 00000000..a0726b16 --- /dev/null +++ b/extensions/edks/assemblyscript/assembly/tsconfig.json @@ -0,0 +1,7 @@ +{ + "extends": "assemblyscript/std/assembly.json", + "include": ["./**/*.ts"], + "compilerOptions": { + "strictBindCallApply": false + } +} diff --git a/extensions/edks/assemblyscript/chord.json b/extensions/edks/assemblyscript/chord.json new file mode 100644 index 00000000..0754e80b --- /dev/null +++ b/extensions/edks/assemblyscript/chord.json @@ -0,0 +1,48 @@ +{ + "$schema": "../../src/jsonschema.json", + "name": "acme", + "description": "It is a long established fact that a reader will be distracted by the readable content of a page when looking at its layout.", + "version": "1.0.0", + "repository": "https://github.com/betwixt-labs/bebopc", + "bin": "./build/release.wasm", + "license": "Apache-2.0", + "author": { + "name": "Betwixt Labs", + "email": "hello@invalid.com" + }, + "build": { + "script": "yarn asbuild:release", + "compiler": "as", + "env": { + "FOO": "bar" + } + }, + "contributes": { + "generator": { + "alias": "acme", + "name": "ACME Generator" + }, + "decorators": { + "min": { + "description": "Sets the min of something idk", + "parameters": { + "floor": { + "description": "Specifies the floor of something idk", + "type": "int32", + "required": true + }, + "ceiling": { + "description": "Specifies the ceiling of something idk", + "type": "int32", + "required": false, + "default": 100 + } + }, + "targets": "all" + } + } + }, + "engine": { + "bebopc": "^3.0.0" + } +} diff --git a/extensions/edks/assemblyscript/package.json b/extensions/edks/assemblyscript/package.json new file mode 100644 index 00000000..dedaba15 --- /dev/null +++ b/extensions/edks/assemblyscript/package.json @@ -0,0 +1,26 @@ +{ + "name": "@betwixt/bebopc-sdk", + "version": "1.0.0", + "main": "index.js", + "license": "MIT", + "private": true, + "devDependencies": { + "@assemblyscript/wasi-shim": "^0.1.0", + "assemblyscript": "^0.27.9", + "assemblyscript-prettier": "^3.0.1", + "prettier": "3.0.0", + "serve": "^14.2.0", + "visitor-as": "^0.11.4" + }, + "type": "module", + "scripts": { + "asbuild:debug": "asc assembly/index.ts --target debug", + "asbuild:release": "asc assembly/index.ts --target release", + "asbuild": "yarn asbuild:debug && yarn asbuild:release", + "test": "node tests", + "start": "yarn serve ." + }, + "dependencies": { + "assemblyscript-json": "^1.1.0" + } +} diff --git a/extensions/edks/assemblyscript/yarn.lock b/extensions/edks/assemblyscript/yarn.lock new file mode 100644 index 00000000..8465284b --- /dev/null +++ b/extensions/edks/assemblyscript/yarn.lock @@ -0,0 +1,664 @@ +# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. +# yarn lockfile v1 + + +"@assemblyscript/wasi-shim@^0.1.0": + version "0.1.0" + resolved "https://registry.yarnpkg.com/@assemblyscript/wasi-shim/-/wasi-shim-0.1.0.tgz#967108ee5e2a21dea1378af6313ff2a0d7a5c8dd" + integrity sha512-fSLH7MdJHf2uDW5llA5VCF/CG62Jp2WkYGui9/3vIWs3jDhViGeQF7nMYLUjpigluM5fnq61I6obtCETy39FZw== + +"@zeit/schemas@2.29.0": + version "2.29.0" + resolved "https://registry.yarnpkg.com/@zeit/schemas/-/schemas-2.29.0.tgz#a59ae6ebfdf4ddc66a876872dd736baa58b6696c" + integrity sha512-g5QiLIfbg3pLuYUJPlisNKY+epQJTcMDsOnVNkscrDP1oi7vmJnzOANYJI/1pZcVJ6umUkBv3aFtlg1UvUHGzA== + +accepts@~1.3.5: + version "1.3.8" + resolved "https://registry.yarnpkg.com/accepts/-/accepts-1.3.8.tgz#0bf0be125b67014adcb0b0921e62db7bffe16b2e" + integrity sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw== + dependencies: + mime-types "~2.1.34" + negotiator "0.6.3" + +ajv@8.11.0: + version "8.11.0" + resolved "https://registry.yarnpkg.com/ajv/-/ajv-8.11.0.tgz#977e91dd96ca669f54a11e23e378e33b884a565f" + integrity sha512-wGgprdCvMalC0BztXvitD2hC04YffAvtsUn93JbGXYLAtCUO4xd17mCCZQxUOItiBwZvJScWo8NIvQMQ71rdpg== + dependencies: + fast-deep-equal "^3.1.1" + json-schema-traverse "^1.0.0" + require-from-string "^2.0.2" + uri-js "^4.2.2" + +ansi-align@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/ansi-align/-/ansi-align-3.0.1.tgz#0cdf12e111ace773a86e9a1fad1225c43cb19a59" + integrity sha512-IOfwwBF5iczOjp/WeY4YxyjqAFMQoZufdQWDd19SEExbVLNXqvpzSJ/M7Za4/sCPmQ0+GRquoA7bGcINcxew6w== + dependencies: + string-width "^4.1.0" + +ansi-regex@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-5.0.1.tgz#082cb2c89c9fe8659a311a53bd6a4dc5301db304" + integrity sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ== + +ansi-regex@^6.0.1: + version "6.0.1" + resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-6.0.1.tgz#3183e38fae9a65d7cb5e53945cd5897d0260a06a" + integrity sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA== + +ansi-styles@^4.1.0: + version "4.3.0" + resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-4.3.0.tgz#edd803628ae71c04c85ae7a0906edad34b648937" + integrity sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg== + dependencies: + color-convert "^2.0.1" + +ansi-styles@^6.1.0: + version "6.2.1" + resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-6.2.1.tgz#0e62320cf99c21afff3b3012192546aacbfb05c5" + integrity sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug== + +arch@^2.2.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/arch/-/arch-2.2.0.tgz#1bc47818f305764f23ab3306b0bfc086c5a29d11" + integrity sha512-Of/R0wqp83cgHozfIYLbBMnej79U/SVGOOyuB3VVFv1NRM/PSFMK12x9KVtiYzJqmnU5WR2qp0Z5rHb7sWGnFQ== + +arg@5.0.2: + version "5.0.2" + resolved "https://registry.yarnpkg.com/arg/-/arg-5.0.2.tgz#c81433cc427c92c4dcf4865142dbca6f15acd59c" + integrity sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg== + +assemblyscript-json@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/assemblyscript-json/-/assemblyscript-json-1.1.0.tgz#49d38bc21f1ac36f2887528a35de6cf7d59c17be" + integrity sha512-UbE8ts8csTWQgd5TnSPN7MRV9NveuHv1bVnKmDLoo/tzjqxkmsZb3lu59Uk8H7SGoqdkDSEE049alx/nHnSdFw== + +assemblyscript-prettier@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/assemblyscript-prettier/-/assemblyscript-prettier-3.0.1.tgz#e0322f680655b96330c1252a966dc368ad9364d9" + integrity sha512-q5D9rJOr1xG8p2MdcAF3wvIBPrmHaKgenJbmYrwM1T96sHKFeTOfCtgHHmrDnrqoly7wSGfWtbE84omC1IUJEA== + dependencies: + assemblyscript "~0.27.0" + +assemblyscript@^0.27.9: + version "0.27.9" + resolved "https://registry.yarnpkg.com/assemblyscript/-/assemblyscript-0.27.9.tgz#f520ccb4ea2b9c83262fcd20d0573b3677ba19cb" + integrity sha512-cFE/AMjVtBQ2iKFZPu/a3Z/KJzGex61C+X+y3GuLJ8Hr9Zdf46sy/JlIl6ikothQd2umM9CQDAD/uAPAEHL+oA== + dependencies: + binaryen "112.0.0-nightly.20230411" + long "^5.2.1" + +assemblyscript@~0.27.0: + version "0.27.22" + resolved "https://registry.yarnpkg.com/assemblyscript/-/assemblyscript-0.27.22.tgz#5825828602119c90173e974a4b55ac88769598d3" + integrity sha512-6ClobsR4Hxn6K0daYp/+n9qWTqVbpdVeSGSVDqRvUEz66vvFb8atS6nLm+fnQ54JXuXmzLQy0uWYYgB8G59btQ== + dependencies: + binaryen "116.0.0-nightly.20231102" + long "^5.2.1" + +balanced-match@^1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.2.tgz#e83e3a7e3f300b34cb9d87f615fa0cbf357690ee" + integrity sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw== + +binaryen@112.0.0-nightly.20230411: + version "112.0.0-nightly.20230411" + resolved "https://registry.yarnpkg.com/binaryen/-/binaryen-112.0.0-nightly.20230411.tgz#2f85cff68c0fe3e89014d2dd4e933c4e7e313ef1" + integrity sha512-4V9r9x9fjAVFZdR2yvBFc3BEJJIBYvd2X8X8k0zAuJsao2gl9wNHDmpQ30QsLo6hgkRfRImkCbCjhXW3RDOYXQ== + +binaryen@116.0.0-nightly.20231102: + version "116.0.0-nightly.20231102" + resolved "https://registry.yarnpkg.com/binaryen/-/binaryen-116.0.0-nightly.20231102.tgz#0bbf0181ef8a4d963859b1bf68c9bd2d24911dc7" + integrity sha512-aPU9tlKdw/gcXx6u4PxtDgOtGjg/ZKnYdk23ctYb70GxZgPhWnGWmnBt01aV5dt5yFFo2V4rbB7SzpSFhViFQA== + +boxen@7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/boxen/-/boxen-7.0.0.tgz#9e5f8c26e716793fc96edcf7cf754cdf5e3fbf32" + integrity sha512-j//dBVuyacJbvW+tvZ9HuH03fZ46QcaKvvhZickZqtB271DxJ7SNRSNxrV/dZX0085m7hISRZWbzWlJvx/rHSg== + dependencies: + ansi-align "^3.0.1" + camelcase "^7.0.0" + chalk "^5.0.1" + cli-boxes "^3.0.0" + string-width "^5.1.2" + type-fest "^2.13.0" + widest-line "^4.0.1" + wrap-ansi "^8.0.1" + +brace-expansion@^1.1.7: + version "1.1.11" + resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.11.tgz#3c7fcbf529d87226f3d2f52b966ff5271eb441dd" + integrity sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA== + dependencies: + balanced-match "^1.0.0" + concat-map "0.0.1" + +bytes@3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/bytes/-/bytes-3.0.0.tgz#d32815404d689699f85a4ea4fa8755dd13a96048" + integrity sha512-pMhOfFDPiv9t5jjIXkHosWmkSyQbvsgEVNkz0ERHbuLh2T/7j4Mqqpz523Fe8MVY89KC6Sh/QfS2sM+SjgFDcw== + +camelcase@^7.0.0: + version "7.0.1" + resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-7.0.1.tgz#f02e50af9fd7782bc8b88a3558c32fd3a388f048" + integrity sha512-xlx1yCK2Oc1APsPXDL2LdlNP6+uu8OCDdhOBSVT279M/S+y75O30C2VuD8T2ogdePBBl7PfPF4504tnLgX3zfw== + +chalk-template@0.4.0: + version "0.4.0" + resolved "https://registry.yarnpkg.com/chalk-template/-/chalk-template-0.4.0.tgz#692c034d0ed62436b9062c1707fadcd0f753204b" + integrity sha512-/ghrgmhfY8RaSdeo43hNXxpoHAtxdbskUHjPpfqUWGttFgycUhYPGx3YZBCnUCvOa7Doivn1IZec3DEGFoMgLg== + dependencies: + chalk "^4.1.2" + +chalk@5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/chalk/-/chalk-5.0.1.tgz#ca57d71e82bb534a296df63bbacc4a1c22b2a4b6" + integrity sha512-Fo07WOYGqMfCWHOzSXOt2CxDbC6skS/jO9ynEcmpANMoPrD+W1r1K6Vx7iNm+AQmETU1Xr2t+n8nzkV9t6xh3w== + +chalk@^4.1.2: + version "4.1.2" + resolved "https://registry.yarnpkg.com/chalk/-/chalk-4.1.2.tgz#aac4e2b7734a740867aeb16bf02aad556a1e7a01" + integrity sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA== + dependencies: + ansi-styles "^4.1.0" + supports-color "^7.1.0" + +chalk@^5.0.1: + version "5.3.0" + resolved "https://registry.yarnpkg.com/chalk/-/chalk-5.3.0.tgz#67c20a7ebef70e7f3970a01f90fa210cb6860385" + integrity sha512-dLitG79d+GV1Nb/VYcCDFivJeK1hiukt9QjRNVOsUtTy1rR1YJsmpGGTZ3qJos+uw7WmWF4wUwBd9jxjocFC2w== + +cli-boxes@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/cli-boxes/-/cli-boxes-3.0.0.tgz#71a10c716feeba005e4504f36329ef0b17cf3145" + integrity sha512-/lzGpEWL/8PfI0BmBOPRwp0c/wFNX1RdUML3jK/RcSBA9T8mZDdQpqYBKtCFTOfQbwPqWEOpjqW+Fnayc0969g== + +clipboardy@3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/clipboardy/-/clipboardy-3.0.0.tgz#f3876247404d334c9ed01b6f269c11d09a5e3092" + integrity sha512-Su+uU5sr1jkUy1sGRpLKjKrvEOVXgSgiSInwa/qeID6aJ07yh+5NWc3h2QfjHjBnfX4LhtFcuAWKUsJ3r+fjbg== + dependencies: + arch "^2.2.0" + execa "^5.1.1" + is-wsl "^2.2.0" + +color-convert@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-2.0.1.tgz#72d3a68d598c9bdb3af2ad1e84f21d896abd4de3" + integrity sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ== + dependencies: + color-name "~1.1.4" + +color-name@~1.1.4: + version "1.1.4" + resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.4.tgz#c2a09a87acbde69543de6f63fa3995c826c536a2" + integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA== + +compressible@~2.0.16: + version "2.0.18" + resolved "https://registry.yarnpkg.com/compressible/-/compressible-2.0.18.tgz#af53cca6b070d4c3c0750fbd77286a6d7cc46fba" + integrity sha512-AF3r7P5dWxL8MxyITRMlORQNaOA2IkAFaTr4k7BUumjPtRpGDTZpl0Pb1XCO6JeDCBdp126Cgs9sMxqSjgYyRg== + dependencies: + mime-db ">= 1.43.0 < 2" + +compression@1.7.4: + version "1.7.4" + resolved "https://registry.yarnpkg.com/compression/-/compression-1.7.4.tgz#95523eff170ca57c29a0ca41e6fe131f41e5bb8f" + integrity sha512-jaSIDzP9pZVS4ZfQ+TzvtiWhdpFhE2RDHz8QJkpX9SIpLq88VueF5jJw6t+6CUQcAoA6t+x89MLrWAqpfDE8iQ== + dependencies: + accepts "~1.3.5" + bytes "3.0.0" + compressible "~2.0.16" + debug "2.6.9" + on-headers "~1.0.2" + safe-buffer "5.1.2" + vary "~1.1.2" + +concat-map@0.0.1: + version "0.0.1" + resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" + integrity sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg== + +content-disposition@0.5.2: + version "0.5.2" + resolved "https://registry.yarnpkg.com/content-disposition/-/content-disposition-0.5.2.tgz#0cf68bb9ddf5f2be7961c3a85178cb85dba78cb4" + integrity sha512-kRGRZw3bLlFISDBgwTSA1TMBFN6J6GWDeubmDE3AF+3+yXL8hTWv8r5rkLbqYXY4RjPk/EzHnClI3zQf1cFmHA== + +cross-spawn@^7.0.3: + version "7.0.3" + resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.3.tgz#f73a85b9d5d41d045551c177e2882d4ac85728a6" + integrity sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w== + dependencies: + path-key "^3.1.0" + shebang-command "^2.0.0" + which "^2.0.1" + +debug@2.6.9: + version "2.6.9" + resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f" + integrity sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA== + dependencies: + ms "2.0.0" + +deep-extend@^0.6.0: + version "0.6.0" + resolved "https://registry.yarnpkg.com/deep-extend/-/deep-extend-0.6.0.tgz#c4fa7c95404a17a9c3e8ca7e1537312b736330ac" + integrity sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA== + +eastasianwidth@^0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/eastasianwidth/-/eastasianwidth-0.2.0.tgz#696ce2ec0aa0e6ea93a397ffcf24aa7840c827cb" + integrity sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA== + +emoji-regex@^8.0.0: + version "8.0.0" + resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-8.0.0.tgz#e818fd69ce5ccfcb404594f842963bf53164cc37" + integrity sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A== + +emoji-regex@^9.2.2: + version "9.2.2" + resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-9.2.2.tgz#840c8803b0d8047f4ff0cf963176b32d4ef3ed72" + integrity sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg== + +execa@^5.1.1: + version "5.1.1" + resolved "https://registry.yarnpkg.com/execa/-/execa-5.1.1.tgz#f80ad9cbf4298f7bd1d4c9555c21e93741c411dd" + integrity sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg== + dependencies: + cross-spawn "^7.0.3" + get-stream "^6.0.0" + human-signals "^2.1.0" + is-stream "^2.0.0" + merge-stream "^2.0.0" + npm-run-path "^4.0.1" + onetime "^5.1.2" + signal-exit "^3.0.3" + strip-final-newline "^2.0.0" + +fast-deep-equal@^3.1.1: + version "3.1.3" + resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz#3a7d56b559d6cbc3eb512325244e619a65c6c525" + integrity sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q== + +fast-url-parser@1.1.3: + version "1.1.3" + resolved "https://registry.yarnpkg.com/fast-url-parser/-/fast-url-parser-1.1.3.tgz#f4af3ea9f34d8a271cf58ad2b3759f431f0b318d" + integrity sha512-5jOCVXADYNuRkKFzNJ0dCCewsZiYo0dz8QNYljkOpFC6r2U4OBmKtvm/Tsuh4w1YYdDqDb31a8TVhBJ2OJKdqQ== + dependencies: + punycode "^1.3.2" + +get-stream@^6.0.0: + version "6.0.1" + resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-6.0.1.tgz#a262d8eef67aced57c2852ad6167526a43cbf7b7" + integrity sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg== + +has-flag@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-4.0.0.tgz#944771fd9c81c81265c4d6941860da06bb59479b" + integrity sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ== + +human-signals@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/human-signals/-/human-signals-2.1.0.tgz#dc91fcba42e4d06e4abaed33b3e7a3c02f514ea0" + integrity sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw== + +ini@~1.3.0: + version "1.3.8" + resolved "https://registry.yarnpkg.com/ini/-/ini-1.3.8.tgz#a29da425b48806f34767a4efce397269af28432c" + integrity sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew== + +is-docker@^2.0.0: + version "2.2.1" + resolved "https://registry.yarnpkg.com/is-docker/-/is-docker-2.2.1.tgz#33eeabe23cfe86f14bde4408a02c0cfb853acdaa" + integrity sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ== + +is-fullwidth-code-point@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz#f116f8064fe90b3f7844a38997c0b75051269f1d" + integrity sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg== + +is-port-reachable@4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/is-port-reachable/-/is-port-reachable-4.0.0.tgz#dac044091ef15319c8ab2f34604d8794181f8c2d" + integrity sha512-9UoipoxYmSk6Xy7QFgRv2HDyaysmgSG75TFQs6S+3pDM7ZhKTF/bskZV+0UlABHzKjNVhPjYCLfeZUEg1wXxig== + +is-stream@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-2.0.1.tgz#fac1e3d53b97ad5a9d0ae9cef2389f5810a5c077" + integrity sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg== + +is-wsl@^2.2.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/is-wsl/-/is-wsl-2.2.0.tgz#74a4c76e77ca9fd3f932f290c17ea326cd157271" + integrity sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww== + dependencies: + is-docker "^2.0.0" + +isexe@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10" + integrity sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw== + +json-schema-traverse@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz#ae7bcb3656ab77a73ba5c49bf654f38e6b6860e2" + integrity sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug== + +lodash.clonedeep@^4.5.0: + version "4.5.0" + resolved "https://registry.yarnpkg.com/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz#e23f3f9c4f8fbdde872529c1071857a086e5ccef" + integrity sha512-H5ZhCF25riFd9uB5UCkVKo61m3S/xZk1x4wA6yp/L3RFP6Z/eHH1ymQcGLo7J3GMPfm0V/7m1tryHuGVxpqEBQ== + +long@^5.2.1: + version "5.2.3" + resolved "https://registry.yarnpkg.com/long/-/long-5.2.3.tgz#a3ba97f3877cf1d778eccbcb048525ebb77499e1" + integrity sha512-lcHwpNoggQTObv5apGNCTdJrO69eHOZMi4BNC+rTLER8iHAqGrUVeLh/irVIM7zTw2bOXA8T6uNPeujwOLg/2Q== + +merge-stream@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/merge-stream/-/merge-stream-2.0.0.tgz#52823629a14dd00c9770fb6ad47dc6310f2c1f60" + integrity sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w== + +mime-db@1.52.0, "mime-db@>= 1.43.0 < 2": + version "1.52.0" + resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.52.0.tgz#bbabcdc02859f4987301c856e3387ce5ec43bf70" + integrity sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg== + +mime-db@~1.33.0: + version "1.33.0" + resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.33.0.tgz#a3492050a5cb9b63450541e39d9788d2272783db" + integrity sha512-BHJ/EKruNIqJf/QahvxwQZXKygOQ256myeN/Ew+THcAa5q+PjyTTMMeNQC4DZw5AwfvelsUrA6B67NKMqXDbzQ== + +mime-types@2.1.18: + version "2.1.18" + resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.18.tgz#6f323f60a83d11146f831ff11fd66e2fe5503bb8" + integrity sha512-lc/aahn+t4/SWV/qcmumYjymLsWfN3ELhpmVuUFjgsORruuZPVSwAQryq+HHGvO/SI2KVX26bx+En+zhM8g8hQ== + dependencies: + mime-db "~1.33.0" + +mime-types@~2.1.34: + version "2.1.35" + resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.35.tgz#381a871b62a734450660ae3deee44813f70d959a" + integrity sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw== + dependencies: + mime-db "1.52.0" + +mimic-fn@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-2.1.0.tgz#7ed2c2ccccaf84d3ffcb7a69b57711fc2083401b" + integrity sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg== + +minimatch@3.1.2: + version "3.1.2" + resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.1.2.tgz#19cd194bfd3e428f049a70817c038d89ab4be35b" + integrity sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw== + dependencies: + brace-expansion "^1.1.7" + +minimist@^1.2.0: + version "1.2.8" + resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.8.tgz#c1a464e7693302e082a075cee0c057741ac4772c" + integrity sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA== + +ms@2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8" + integrity sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A== + +negotiator@0.6.3: + version "0.6.3" + resolved "https://registry.yarnpkg.com/negotiator/-/negotiator-0.6.3.tgz#58e323a72fedc0d6f9cd4d31fe49f51479590ccd" + integrity sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg== + +npm-run-path@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/npm-run-path/-/npm-run-path-4.0.1.tgz#b7ecd1e5ed53da8e37a55e1c2269e0b97ed748ea" + integrity sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw== + dependencies: + path-key "^3.0.0" + +on-headers@~1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/on-headers/-/on-headers-1.0.2.tgz#772b0ae6aaa525c399e489adfad90c403eb3c28f" + integrity sha512-pZAE+FJLoyITytdqK0U5s+FIpjN0JP3OzFi/u8Rx+EV5/W+JTWGXG8xFzevE7AjBfDqHv/8vL8qQsIhHnqRkrA== + +onetime@^5.1.2: + version "5.1.2" + resolved "https://registry.yarnpkg.com/onetime/-/onetime-5.1.2.tgz#d0e96ebb56b07476df1dd9c4806e5237985ca45e" + integrity sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg== + dependencies: + mimic-fn "^2.1.0" + +path-is-inside@1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/path-is-inside/-/path-is-inside-1.0.2.tgz#365417dede44430d1c11af61027facf074bdfc53" + integrity sha512-DUWJr3+ULp4zXmol/SZkFf3JGsS9/SIv+Y3Rt93/UjPpDpklB5f1er4O3POIbUuUJ3FXgqte2Q7SrU6zAqwk8w== + +path-key@^3.0.0, path-key@^3.1.0: + version "3.1.1" + resolved "https://registry.yarnpkg.com/path-key/-/path-key-3.1.1.tgz#581f6ade658cbba65a0d3380de7753295054f375" + integrity sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q== + +path-to-regexp@2.2.1: + version "2.2.1" + resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-2.2.1.tgz#90b617025a16381a879bc82a38d4e8bdeb2bcf45" + integrity sha512-gu9bD6Ta5bwGrrU8muHzVOBFFREpp2iRkVfhBJahwJ6p6Xw20SjT0MxLnwkjOibQmGSYhiUnf2FLe7k+jcFmGQ== + +prettier@3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/prettier/-/prettier-3.0.0.tgz#e7b19f691245a21d618c68bc54dc06122f6105ae" + integrity sha512-zBf5eHpwHOGPC47h0zrPyNn+eAEIdEzfywMoYn2XPi0P44Zp0tSq64rq0xAREh4auw2cJZHo9QUob+NqCQky4g== + +punycode@^1.3.2: + version "1.4.1" + resolved "https://registry.yarnpkg.com/punycode/-/punycode-1.4.1.tgz#c0d5a63b2718800ad8e1eb0fa5269c84dd41845e" + integrity sha512-jmYNElW7yvO7TV33CjSmvSiE2yco3bV2czu/OzDKdMNVZQWfxCblURLhf+47syQRBntjfLdd/H0egrzIG+oaFQ== + +punycode@^2.1.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.3.0.tgz#f67fa67c94da8f4d0cfff981aee4118064199b8f" + integrity sha512-rRV+zQD8tVFys26lAGR9WUuS4iUAngJScM+ZRSKtvl5tKeZ2t5bvdNFdNHBW9FWR4guGHlgmsZ1G7BSm2wTbuA== + +range-parser@1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/range-parser/-/range-parser-1.2.0.tgz#f49be6b487894ddc40dcc94a322f611092e00d5e" + integrity sha512-kA5WQoNVo4t9lNx2kQNFCxKeBl5IbbSNBl1M/tLkw9WCn+hxNBAW5Qh8gdhs63CJnhjJ2zQWFoqPJP2sK1AV5A== + +rc@^1.0.1, rc@^1.1.6: + version "1.2.8" + resolved "https://registry.yarnpkg.com/rc/-/rc-1.2.8.tgz#cd924bf5200a075b83c188cd6b9e211b7fc0d3ed" + integrity sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw== + dependencies: + deep-extend "^0.6.0" + ini "~1.3.0" + minimist "^1.2.0" + strip-json-comments "~2.0.1" + +registry-auth-token@3.3.2: + version "3.3.2" + resolved "https://registry.yarnpkg.com/registry-auth-token/-/registry-auth-token-3.3.2.tgz#851fd49038eecb586911115af845260eec983f20" + integrity sha512-JL39c60XlzCVgNrO+qq68FoNb56w/m7JYvGR2jT5iR1xBrUA3Mfx5Twk5rqTThPmQKMWydGmq8oFtDlxfrmxnQ== + dependencies: + rc "^1.1.6" + safe-buffer "^5.0.1" + +registry-url@3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/registry-url/-/registry-url-3.1.0.tgz#3d4ef870f73dde1d77f0cf9a381432444e174942" + integrity sha512-ZbgR5aZEdf4UKZVBPYIgaglBmSF2Hi94s2PcIHhRGFjKYu+chjJdYfHn4rt3hB6eCKLJ8giVIIfgMa1ehDfZKA== + dependencies: + rc "^1.0.1" + +require-from-string@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/require-from-string/-/require-from-string-2.0.2.tgz#89a7fdd938261267318eafe14f9c32e598c36909" + integrity sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw== + +safe-buffer@5.1.2: + version "5.1.2" + resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d" + integrity sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g== + +safe-buffer@^5.0.1: + version "5.2.1" + resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.1.tgz#1eaf9fa9bdb1fdd4ec75f58f9cdb4e6b7827eec6" + integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ== + +serve-handler@6.1.5: + version "6.1.5" + resolved "https://registry.yarnpkg.com/serve-handler/-/serve-handler-6.1.5.tgz#a4a0964f5c55c7e37a02a633232b6f0d6f068375" + integrity sha512-ijPFle6Hwe8zfmBxJdE+5fta53fdIY0lHISJvuikXB3VYFafRjMRpOffSPvCYsbKyBA7pvy9oYr/BT1O3EArlg== + dependencies: + bytes "3.0.0" + content-disposition "0.5.2" + fast-url-parser "1.1.3" + mime-types "2.1.18" + minimatch "3.1.2" + path-is-inside "1.0.2" + path-to-regexp "2.2.1" + range-parser "1.2.0" + +serve@^14.2.0: + version "14.2.1" + resolved "https://registry.yarnpkg.com/serve/-/serve-14.2.1.tgz#3f078d292ed5e7b2c5a64f957af2765b0459798b" + integrity sha512-48er5fzHh7GCShLnNyPBRPEjs2I6QBozeGr02gaacROiyS/8ARADlj595j39iZXAqBbJHH/ivJJyPRWY9sQWZA== + dependencies: + "@zeit/schemas" "2.29.0" + ajv "8.11.0" + arg "5.0.2" + boxen "7.0.0" + chalk "5.0.1" + chalk-template "0.4.0" + clipboardy "3.0.0" + compression "1.7.4" + is-port-reachable "4.0.0" + serve-handler "6.1.5" + update-check "1.5.4" + +shebang-command@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/shebang-command/-/shebang-command-2.0.0.tgz#ccd0af4f8835fbdc265b82461aaf0c36663f34ea" + integrity sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA== + dependencies: + shebang-regex "^3.0.0" + +shebang-regex@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/shebang-regex/-/shebang-regex-3.0.0.tgz#ae16f1644d873ecad843b0307b143362d4c42172" + integrity sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A== + +signal-exit@^3.0.3: + version "3.0.7" + resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.7.tgz#a9a1767f8af84155114eaabd73f99273c8f59ad9" + integrity sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ== + +string-width@^4.1.0: + version "4.2.3" + resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" + integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== + dependencies: + emoji-regex "^8.0.0" + is-fullwidth-code-point "^3.0.0" + strip-ansi "^6.0.1" + +string-width@^5.0.1, string-width@^5.1.2: + version "5.1.2" + resolved "https://registry.yarnpkg.com/string-width/-/string-width-5.1.2.tgz#14f8daec6d81e7221d2a357e668cab73bdbca794" + integrity sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA== + dependencies: + eastasianwidth "^0.2.0" + emoji-regex "^9.2.2" + strip-ansi "^7.0.1" + +strip-ansi@^6.0.1: + version "6.0.1" + resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" + integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== + dependencies: + ansi-regex "^5.0.1" + +strip-ansi@^7.0.1: + version "7.1.0" + resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-7.1.0.tgz#d5b6568ca689d8561370b0707685d22434faff45" + integrity sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ== + dependencies: + ansi-regex "^6.0.1" + +strip-final-newline@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/strip-final-newline/-/strip-final-newline-2.0.0.tgz#89b852fb2fcbe936f6f4b3187afb0a12c1ab58ad" + integrity sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA== + +strip-json-comments@~2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-2.0.1.tgz#3c531942e908c2697c0ec344858c286c7ca0a60a" + integrity sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ== + +supports-color@^7.1.0: + version "7.2.0" + resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-7.2.0.tgz#1b7dcdcb32b8138801b3e478ba6a51caa89648da" + integrity sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw== + dependencies: + has-flag "^4.0.0" + +ts-mixer@^6.0.2: + version "6.0.3" + resolved "https://registry.yarnpkg.com/ts-mixer/-/ts-mixer-6.0.3.tgz#69bd50f406ff39daa369885b16c77a6194c7cae6" + integrity sha512-k43M7uCG1AkTyxgnmI5MPwKoUvS/bRvLvUb7+Pgpdlmok8AoqmUaZxUUw8zKM5B1lqZrt41GjYgnvAi0fppqgQ== + +type-fest@^2.13.0: + version "2.19.0" + resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-2.19.0.tgz#88068015bb33036a598b952e55e9311a60fd3a9b" + integrity sha512-RAH822pAdBgcNMAfWnCBU3CFZcfZ/i1eZjwFU/dsLKumyuuP3niueg2UAukXYF0E2AAoc82ZSSf9J0WQBinzHA== + +update-check@1.5.4: + version "1.5.4" + resolved "https://registry.yarnpkg.com/update-check/-/update-check-1.5.4.tgz#5b508e259558f1ad7dbc8b4b0457d4c9d28c8743" + integrity sha512-5YHsflzHP4t1G+8WGPlvKbJEbAJGCgw+Em+dGR1KmBUbr1J36SJBqlHLjR7oob7sco5hWHGQVcr9B2poIVDDTQ== + dependencies: + registry-auth-token "3.3.2" + registry-url "3.1.0" + +uri-js@^4.2.2: + version "4.4.1" + resolved "https://registry.yarnpkg.com/uri-js/-/uri-js-4.4.1.tgz#9b1a52595225859e55f669d928f88c6c57f2a77e" + integrity sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg== + dependencies: + punycode "^2.1.0" + +vary@~1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/vary/-/vary-1.1.2.tgz#2299f02c6ded30d4a5961b0b9f74524a18f634fc" + integrity sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg== + +visitor-as@^0.11.4: + version "0.11.4" + resolved "https://registry.yarnpkg.com/visitor-as/-/visitor-as-0.11.4.tgz#7b2d7b7ac8be86d4741796aa0630af5e471a4744" + integrity sha512-uih7AooY2V3LhzobjLqyEQzhYYBGeq0y/rZk295foM1Ko498f24NNXBDyM1SgzcLMFCAT/fpmSRco1BpTIdKNQ== + dependencies: + lodash.clonedeep "^4.5.0" + ts-mixer "^6.0.2" + +which@^2.0.1: + version "2.0.2" + resolved "https://registry.yarnpkg.com/which/-/which-2.0.2.tgz#7c6a8dd0a636a0327e10b59c9286eee93f3f51b1" + integrity sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA== + dependencies: + isexe "^2.0.0" + +widest-line@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/widest-line/-/widest-line-4.0.1.tgz#a0fc673aaba1ea6f0a0d35b3c2795c9a9cc2ebf2" + integrity sha512-o0cyEG0e8GPzT4iGHphIOh0cJOV8fivsXxddQasHPHfoZf1ZexrfeA21w2NaEN1RHE+fXlfISmOE8R9N3u3Qig== + dependencies: + string-width "^5.0.1" + +wrap-ansi@^8.0.1: + version "8.1.0" + resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-8.1.0.tgz#56dc22368ee570face1b49819975d9b9a5ead214" + integrity sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ== + dependencies: + ansi-styles "^6.1.0" + string-width "^5.0.1" + strip-ansi "^7.0.1" diff --git a/extensions/edks/tinygo/.gitignore b/extensions/edks/tinygo/.gitignore new file mode 100644 index 00000000..40062814 --- /dev/null +++ b/extensions/edks/tinygo/.gitignore @@ -0,0 +1,30 @@ +# Created by https://www.toptal.com/developers/gitignore/api/go +# Edit at https://www.toptal.com/developers/gitignore?templates=go + +### Go ### +# If you prefer the allow list template instead of the deny list, see community template: +# https://github.com/github/gitignore/blob/main/community/Golang/Go.AllowList.gitignore +# +# Binaries for programs and plugins +*.exe +*.exe~ +*.dll +*.so +*.wasm +*.wat +*.chord +*.dylib + +# Test binary, built with `go test -c` +*.test + +# Output of the go coverage tool, specifically when used with LiteIDE +*.out + +# Dependency directories (remove the comment below to include it) +# vendor/ + +# Go workspace file +go.work + +# End of https://www.toptal.com/developers/gitignore/api/go diff --git a/extensions/edks/tinygo/README.md b/extensions/edks/tinygo/README.md new file mode 100644 index 00000000..b15f4ee5 --- /dev/null +++ b/extensions/edks/tinygo/README.md @@ -0,0 +1,225 @@ +--- +Wow! +--- + +# h1 Heading 8-) + +## h2 Heading + +### h3 Heading + +#### h4 Heading + +##### h5 Heading + +###### h6 Heading + +## Horizontal Rules + +--- + +--- + +--- + +## Typographic replacements + +Enable typographer option to see result. + +(c) (C) (r) (R) (tm) (TM) (p) (P) +- + +test.. test... test..... test?..... test!.... + +!!!!!! ???? ,, -- --- + +"Smartypants, double quotes" and 'single quotes' + +## Emphasis + +**This is bold text** + +**This is bold text** + +_This is italic text_ + +_This is italic text_ + +~~Strikethrough~~ + +## Blockquotes + +> Blockquotes can also be nested... +> +> > ...by using additional greater-than signs right next to each other... +> > +> > > ...or with spaces between arrows. + +## Lists + +Unordered + +- Create a list by starting a line with `+`, `-`, or `*` +- Sub-lists are made by indenting 2 spaces: + - Marker character change forces new list start: + - Ac tristique libero volutpat at + * Facilisis in pretium nisl aliquet + - Nulla volutpat aliquam velit +- Very easy! + +Ordered + +1. Lorem ipsum dolor sit amet +2. Consectetur adipiscing elit +3. Integer molestie lorem at massa + +4. You can use sequential numbers... +5. ...or keep all the numbers as `1.` + +Start numbering with offset: + +57. foo +1. bar + +## Code + +Inline `code` + +Indented code + + // Some comments + line 1 of code + line 2 of code + line 3 of code + +Block code "fences" + +``` +Sample text here... +``` + +Syntax highlighting + +```js +var foo = function (bar) { + return bar++; +}; + +console.log(foo(5)); +``` + +## Tables + +| Option | Description | +| ------ | ------------------------------------------------------------------------- | +| data | path to data files to supply the data that will be passed into templates. | +| engine | engine to be used for processing templates. Handlebars is the default. | +| ext | extension to be used for dest files. | + +Right aligned columns + +| Option | Description | +| -----: | ------------------------------------------------------------------------: | +| data | path to data files to supply the data that will be passed into templates. | +| engine | engine to be used for processing templates. Handlebars is the default. | +| ext | extension to be used for dest files. | + +## Links + +[link text](http://dev.nodeca.com) + +[link with title](http://nodeca.github.io/pica/demo/ "title text!") + +Autoconverted link https://github.com/nodeca/pica (enable linkify to see) + +## Images + +![Minion](https://octodex.github.com/images/minion.png) +![Stormtroopocat](https://octodex.github.com/images/stormtroopocat.jpg "The Stormtroopocat") + +Like links, Images also have a footnote style syntax + +![Alt text][id] + +With a reference later in the document defining the URL location: + +[id]: https://octodex.github.com/images/dojocat.jpg "The Dojocat" + +## Plugins + +The killer feature of `markdown-it` is very effective support of +[syntax plugins](https://www.npmjs.org/browse/keyword/markdown-it-plugin). + +### [Emojies](https://github.com/markdown-it/markdown-it-emoji) + +> Classic markup: :wink: :cry: :laughing: :yum: +> +> Shortcuts (emoticons): :-) :-( 8-) ;) + +see [how to change output](https://github.com/markdown-it/markdown-it-emoji#change-output) with twemoji. + +### [Subscript](https://github.com/markdown-it/markdown-it-sub) / [Superscript](https://github.com/markdown-it/markdown-it-sup) + +- 19^th^ +- H~2~O + +### [\](https://github.com/markdown-it/markdown-it-ins) + +++Inserted text++ + +### [\](https://github.com/markdown-it/markdown-it-mark) + +==Marked text== + +### [Footnotes](https://github.com/markdown-it/markdown-it-footnote) + +Footnote 1 link[^first]. + +Footnote 2 link[^second]. + +Inline footnote^[Text of inline footnote] definition. + +Duplicated footnote reference[^second]. + +[^first]: Footnote **can have markup** + + and multiple paragraphs. + +[^second]: Footnote text. + +### [Definition lists](https://github.com/markdown-it/markdown-it-deflist) + +Term 1 + +: Definition 1 +with lazy continuation. + +Term 2 with _inline markup_ + +: Definition 2 + + { some code, part of Definition 2 } + + Third paragraph of definition 2. + +_Compact style:_ + +Term 1 +~ Definition 1 + +Term 2 +~ Definition 2a +~ Definition 2b + +### [Abbreviations](https://github.com/markdown-it/markdown-it-abbr) + +This is HTML abbreviation example. + +It converts "HTML", but keep intact partial entries like "xxxHTMLyyy" and so on. + +\*[HTML]: Hyper Text Markup Language + +### [Custom containers](https://github.com/markdown-it/markdown-it-container) + +::: warning +_here be dragons_ +::: diff --git a/extensions/edks/tinygo/auxiliary.txt b/extensions/edks/tinygo/auxiliary.txt new file mode 100644 index 00000000..59c4d063 --- /dev/null +++ b/extensions/edks/tinygo/auxiliary.txt @@ -0,0 +1,3 @@ +This is just an example file. + +It shows that we can pack a file into the extension. \ No newline at end of file diff --git a/extensions/edks/tinygo/build.sh b/extensions/edks/tinygo/build.sh new file mode 100755 index 00000000..64a7e4a3 --- /dev/null +++ b/extensions/edks/tinygo/build.sh @@ -0,0 +1,9 @@ +#!/bin/sh + +outDir="build" +outFile="${outDir}/main.wasm" +inputFile="chord/main.go" + +mkdir -p $outDir +tinygo build -o $outFile -gc=leaking -scheduler=none -target=wasi $inputFile +wasm2wat $outFile >"${outDir}/main.wat" diff --git a/extensions/edks/tinygo/build/.keep b/extensions/edks/tinygo/build/.keep new file mode 100644 index 00000000..e69de29b diff --git a/extensions/edks/tinygo/chord.json b/extensions/edks/tinygo/chord.json new file mode 100644 index 00000000..cf67f8d6 --- /dev/null +++ b/extensions/edks/tinygo/chord.json @@ -0,0 +1,55 @@ +{ + "$schema": "../../src/jsonschema.json", + "name": "tinygo-template", + "private": false, + "description": "A template for chords built with tinygo", + "version": "1.0.0", + "repository": "https://github.com/betwixt-labs/bebopc", + "license": "Apache-2.0", + "author": { + "name": "Betwixt Labs", + "email": "code@betwixtlabs.com" + }, + "bin": "build/main.wasm", + "build": { + "script": "./build.sh", + "compiler": "tinygo", + "env": { + "FOO": "bar" + } + }, + "pack": { + "acme": { + "auxiliaryFile": "./auxiliary.txt" + } + }, + "contributes": { + "generator": { + "alias": "acme", + "name": "ACME Generator" + }, + "decorators": { + "min": { + "description": "Sets the min of something idk", + "parameters": { + "floor": { + "description": "Specifies the floor of something idk", + "type": "int32", + "required": true + }, + "ceiling": { + "description": "Specifies the ceiling of something idk", + "type": "int32", + "required": false, + "default": 100 + } + }, + "targets": "all" + } + } + }, + "engine": { + "bebopc": "^3.0.0" + }, + "readme": "README.md" +} diff --git a/extensions/edks/tinygo/chord/main.go b/extensions/edks/tinygo/chord/main.go new file mode 100644 index 00000000..89b68289 --- /dev/null +++ b/extensions/edks/tinygo/chord/main.go @@ -0,0 +1,28 @@ +package main + +import ( + "bebopc/kernel" +) + +//go:wasm-module chord_tinygo +//export chord_compile +func compile(context string) string { + + generatorContext := kernel.DeserializeContext(context) + builder := kernel.NewIndentedStringBuilder(0) + + builder.AppendLine("This was the raw context:").AppendLine(context) + builder.AppendLine() + builder.AppendLine("This is some data from the deserialized context:") + builder.AppendLine("Config:") + builder.AppendLine(" Namespace:" + generatorContext.Config.Namespace) + builder.AppendLine(" OutFile:" + generatorContext.Config.OutFile) + builder.AppendLine(" Alias:" + generatorContext.Config.Alias) + builder.AppendLine("This shows we can call the kernel:") + builder.AppendLine(kernel.GetBebopcVersion()) + return builder.String() +} + +// main is required for the `wasi` target, even if it isn't used. +func main() { +} diff --git a/extensions/edks/tinygo/go.mod b/extensions/edks/tinygo/go.mod new file mode 100644 index 00000000..f6e5e1c6 --- /dev/null +++ b/extensions/edks/tinygo/go.mod @@ -0,0 +1,10 @@ +module bebopc + +go 1.21.5 + +require github.com/tidwall/gjson v1.17.0 + +require ( + github.com/tidwall/match v1.1.1 // indirect + github.com/tidwall/pretty v1.2.1 // indirect +) diff --git a/extensions/edks/tinygo/go.sum b/extensions/edks/tinygo/go.sum new file mode 100644 index 00000000..952d3eef --- /dev/null +++ b/extensions/edks/tinygo/go.sum @@ -0,0 +1,7 @@ +github.com/tidwall/gjson v1.17.0 h1:/Jocvlh98kcTfpN2+JzGQWQcqrPQwDrVEMApx/M5ZwM= +github.com/tidwall/gjson v1.17.0/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk= +github.com/tidwall/match v1.1.1 h1:+Ho715JplO36QYgwN9PGYNhgZvoUSc9X2c80KVTi+GA= +github.com/tidwall/match v1.1.1/go.mod h1:eRSPERbgtNPcGhD8UCthc6PmLEQXEWd3PRB5JTxsfmM= +github.com/tidwall/pretty v1.2.0/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU= +github.com/tidwall/pretty v1.2.1 h1:qjsOFOWWQl+N3RsoF5/ssm1pHmJJwhjlSbZ51I6wMl4= +github.com/tidwall/pretty v1.2.1/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU= diff --git a/extensions/edks/tinygo/kernel/kernel.go b/extensions/edks/tinygo/kernel/kernel.go new file mode 100644 index 00000000..322809bb --- /dev/null +++ b/extensions/edks/tinygo/kernel/kernel.go @@ -0,0 +1,597 @@ +package kernel + +import ( + "bytes" + "fmt" + "strings" + + "github.com/tidwall/gjson" +) + +//go:wasm-module chord_tinygo +//export write_line +func kernelWriteLine(s string) + +//go:wasm-module chord_tinygo +//export write_error +func kernelWriteError(s string) + +//go:wasm-module chord_tinygo +//export get_bebopc_version +func kernelGetBebopcVersion() string + +func WriteLine(a interface{}) { + kernelWriteLine(fmt.Sprintf("%v", a)) +} + +func WriteError(a interface{}) { + kernelWriteError(fmt.Sprintf("%v", a)) +} + +func GetBebopcVersion() string { + return kernelGetBebopcVersion() +} + +func DeserializeContext(context string) GeneratorContext { + var gc GeneratorContext + json := gjson.Parse(context) + + gc.Definitions = deserializeDefinitions(json.Get("definitions")) + gc.Services = deserializeServices(json.Get("services")) + gc.Constants = deserializeConstants(json.Get("constants")) + gc.Config = deserializeConfig(json.Get("config")) + return gc +} + +func deserializeConfig(json gjson.Result) GeneratorConfig { + return GeneratorConfig{ + Alias: json.Get("alias").String(), + OutFile: json.Get("outFile").String(), + Namespace: json.Get("namespace").String(), + EmitNotice: json.Get("emitNotice").Bool(), + EmitBinarySchema: json.Get("emitBinarySchema").Bool(), + Services: json.Get("services").String(), + Options: deserializeOptions(json.Get("options")), + } +} + +func deserializeOptions(json gjson.Result) map[string]string { + options := make(map[string]string) + json.ForEach(func(key, value gjson.Result) bool { + options[key.String()] = value.String() + return true + }) + return options +} + +func deserializeConstants(json gjson.Result) map[string]ConstantDefinition { + constants := make(map[string]ConstantDefinition) + json.ForEach(func(key, value gjson.Result) bool { + constants[key.String()] = deserializeConstant(value) + return true + }) + return constants +} + +func deserializeConstant(json gjson.Result) ConstantDefinition { + return ConstantDefinition{ + Type: NewBaseType(json.Get("type").String()), + Value: json.Get("value").String(), + } +} + +func deserializeServices(json gjson.Result) map[string]ServiceDefinition { + services := make(map[string]ServiceDefinition) + json.ForEach(func(key, value gjson.Result) bool { + services[key.String()] = deserializeService(value) + return true + }) + return services +} + +func deserializeService(json gjson.Result) ServiceDefinition { + methods := make(map[string]MethodDefinition) + json.Get("methods").ForEach(func(key, value gjson.Result) bool { + methods[key.String()] = deserializeMethod(value) + return true + }) + return ServiceDefinition{ + Kind: json.Get("kind").String(), + Methods: methods, + } +} + +func deserializeMethod(json gjson.Result) MethodDefinition { + return MethodDefinition{ + Documentation: json.Get("documentation").String(), + Decorators: deserializeDecorators(json.Get("decorators")), + Type: json.Get("type").String(), + RequestType: json.Get("requestType").String(), + ResponseType: json.Get("responseType").String(), + Id: int(json.Get("id").Int()), + } +} + +func deserializeDefinitions(json gjson.Result) map[string]Definition { + definitions := make(map[string]Definition) + json.ForEach(func(key, value gjson.Result) bool { + + kind := value.Get("kind").String() + switch kind { + case "enum": + definitions[key.String()] = deserializeEnum(value) + case "struct": + definitions[key.String()] = deserializeStruct(value) + case "message": + definitions[key.String()] = deserializeMessage(value) + case "union": + definitions[key.String()] = deserializeUnion(value) + } + return true + }) + return definitions +} + +func deserializeFields(json gjson.Result) map[string]Field { + fields := make(map[string]Field) + json.ForEach(func(key, value gjson.Result) bool { + fields[key.String()] = deserializeField(value) + return true + }) + return fields +} + +func deserializeField(json gjson.Result) Field { + return Field{ + Documentation: json.Get("documentation").String(), + Decorators: deserializeDecorators(json.Get("decorators")), + Type: deserializeFieldType(json.Get("type")), + Index: int(json.Get("index").Int()), + } +} + +func deserializeStruct(json gjson.Result) StructDefinition { + return StructDefinition{ + FieldsDefinition: FieldsDefinition{ + RecordDefinition: RecordDefinition{ + BaseDefinition: BaseDefinition{ + Kind: json.Get("kind").String(), + Documentation: json.Get("documentation").String(), + Decorators: deserializeDecorators(json.Get("decorators")), + Parent: json.Get("parent").String(), + }, + minimalEncodedSize: int(json.Get("minimalEncodedSize").Int()), + discriminatorInParent: int(json.Get("discriminatorInParent").Int()), + }, + Fields: deserializeFields(json.Get("fields")), + }, + IsReadOnly: json.Get("readonly").Bool(), + IsFixedSize: json.Get("isFixedSize").Bool(), + } +} + +func deserializeMessage(json gjson.Result) MessageDefinition { + return MessageDefinition{ + FieldsDefinition: FieldsDefinition{ + RecordDefinition: RecordDefinition{ + BaseDefinition: BaseDefinition{ + Kind: json.Get("kind").String(), + Documentation: json.Get("documentation").String(), + Decorators: deserializeDecorators(json.Get("decorators")), + Parent: json.Get("parent").String(), + }, + minimalEncodedSize: int(json.Get("minimalEncodedSize").Int()), + discriminatorInParent: int(json.Get("discriminatorInParent").Int()), + }, + Fields: deserializeFields(json.Get("fields")), + }, + } +} + +func deserializeUnion(json gjson.Result) UnionDefinition { + return UnionDefinition{ + RecordDefinition: RecordDefinition{ + BaseDefinition: BaseDefinition{ + Kind: json.Get("kind").String(), + Documentation: json.Get("documentation").String(), + Decorators: deserializeDecorators(json.Get("decorators")), + Parent: json.Get("parent").String(), + }, + minimalEncodedSize: int(json.Get("minimalEncodedSize").Int()), + discriminatorInParent: int(json.Get("discriminatorInParent").Int()), + }, + Branches: deserializeUnionBranches(json.Get("branches")), + } +} + +func deserializeUnionBranches(json gjson.Result) map[int]string { + branches := make(map[int]string) + json.ForEach(func(key, value gjson.Result) bool { + branches[int(value.Int())] = key.String() + return true + }) + return branches +} + +func deserializeFieldType(json gjson.Result) FieldType { + typeId := json.Get("type").String() + if typeId == "array" { + return deserializeArrayType(json.Get("array")) + } else if typeId == "map" { + return deserializeMapType(json.Get("map")) + } else { + return NewBaseType(typeId) + } +} + +func deserializeArrayType(json gjson.Result) ArrayType { + return ArrayType{ + Depth: int(json.Get("depth").Int()), + MemberType: NewBaseType(json.Get("memberType").String()), + } +} + +func deserializeMapType(json gjson.Result) MapType { + keyType := json.Get("keyType").String() + valueType := json.Get("valueType").String() + if valueType == "array" { + return MapType{ + KeyType: NewBaseType(keyType), + ValueType: deserializeArrayType(json.Get("array")), + } + } else if valueType == "map" { + return MapType{ + KeyType: NewBaseType(keyType), + ValueType: deserializeMapType(json.Get("map")), + } + } + return MapType{ + KeyType: NewBaseType(keyType), + ValueType: NewBaseType(valueType), + } +} + +func deserializeEnum(json gjson.Result) EnumDefinition { + return EnumDefinition{ + BaseDefinition: BaseDefinition{ + Kind: json.Get("kind").String(), + Documentation: json.Get("documentation").String(), + Decorators: deserializeDecorators(json.Get("decorators")), + Parent: json.Get("parent").String(), + }, + IsBitFlags: json.Get("isBitFlags").Bool(), + MinimalEncodedSize: int(json.Get("minimalEncodedSize").Int()), + BaseType: json.Get("baseType").String(), + Members: deserializeEnumMembers(json.Get("members")), + } +} + +func deserializeEnumMembers(json gjson.Result) map[string]EnumMember { + members := make(map[string]EnumMember) + json.ForEach(func(key, value gjson.Result) bool { + members[key.String()] = deserializeEnumMember(value) + return true + }) + return members +} + +func deserializeEnumMember(json gjson.Result) EnumMember { + return EnumMember{ + Documentation: json.Get("documentation").String(), + Decorators: deserializeDecorators(json.Get("decorators")), + Value: json.Get("value").String(), + } +} + +func deserializeDecorators(json gjson.Result) map[string]Decorator { + decorators := make(map[string]Decorator) + json.ForEach(func(key, value gjson.Result) bool { + decorators[key.String()] = deserializeDecorator(value) + return true + }) + return decorators +} + +func deserializeDecorator(json gjson.Result) Decorator { + decorator := Decorator{ + Arguments: make(map[string]DecoratorArgument), + } + arguments := json.Get("arguments") + if !arguments.Exists() { + return decorator + } + arguments.ForEach(func(key, value gjson.Result) bool { + decorator.Arguments[key.String()] = deserializeDecoratorArgument(value) + return true + }) + return decorator +} + +func deserializeDecoratorArgument(json gjson.Result) DecoratorArgument { + return DecoratorArgument{ + Type: NewBaseType(json.Get("type").String()), + Value: json.Get("value").String(), + } +} + +type GeneratorContext struct { + Definitions map[string]Definition + Services map[string]ServiceDefinition + Constants map[string]ConstantDefinition + Config GeneratorConfig +} + +func (gc GeneratorContext) GetDefinition(name string) Definition { + definition, exists := gc.Definitions[name] + if !exists { + return nil + } + return definition +} + +type GeneratorConfig struct { + Alias string + OutFile string + Namespace string + EmitNotice bool + EmitBinarySchema bool + Services string + Options map[string]string +} + +var baseTypes = map[string]bool{ + "byte": true, "uint8": true, "int16": true, "uint16": true, "int32": true, + "uint32": true, "int64": true, "uint64": true, "float32": true, "float64": true, + "bool": true, "string": true, "guid": true, "date": true, +} + +type BaseType struct { + Name string +} + +func (b BaseType) IsBaseType() bool { + return baseTypes[b.Name] +} + +func (b BaseType) IsFieldType() bool { + return true +} + +func (b BaseType) IsArray() bool { + return false +} + +func (b BaseType) IsMap() bool { + return false +} + +func NewBaseType(name string) BaseType { + return BaseType{Name: name} +} + +type FieldType interface { + IsFieldType() bool + IsArray() bool + IsMap() bool + IsBaseType() bool +} + +type ArrayType struct { + Depth int + MemberType FieldType +} + +func (a ArrayType) IsMap() bool { + return false +} + +func (a ArrayType) IsBaseType() bool { + return false +} + +func (a ArrayType) IsFieldType() bool { + return true +} +func (a ArrayType) IsArray() bool { + return true +} + +type MapType struct { + KeyType BaseType + ValueType FieldType +} + +func (m MapType) IsFieldType() bool { + return true +} +func (m MapType) IsMap() bool { + return true +} + +func (m MapType) IsArray() bool { + return false +} + +func (m MapType) IsBaseType() bool { + return false +} + +type Decorator struct { + Arguments map[string]DecoratorArgument +} + +type DecoratorArgument struct { + Type BaseType + Value string +} + +type Definition interface { + Kind() string +} + +type BaseDefinition struct { + Kind string + Documentation string + Decorators map[string]Decorator + Parent string +} + +type RecordDefinition struct { + BaseDefinition + minimalEncodedSize int + discriminatorInParent int +} + +type FieldsDefinition struct { + RecordDefinition + Fields map[string]Field +} + +type StructDefinition struct { + FieldsDefinition + IsReadOnly bool + IsFixedSize bool +} + +func (sd StructDefinition) Kind() string { return "struct" } + +type MessageDefinition struct { + FieldsDefinition +} + +func (md MessageDefinition) Kind() string { return "message" } + +type EnumDefinition struct { + BaseDefinition + IsBitFlags bool + MinimalEncodedSize int + BaseType string + Members map[string]EnumMember +} + +func (ed EnumDefinition) Kind() string { return "enum" } + +type EnumMember struct { + Documentation string + Value string + Decorators map[string]Decorator +} + +type UnionDefinition struct { + RecordDefinition + Branches map[int]string +} + +func (ud UnionDefinition) Kind() string { return "union" } + +type ServiceDefinition struct { + Kind string + Methods map[string]MethodDefinition +} + +type MethodDefinition struct { + Documentation string + Decorators map[string]Decorator + Type string + RequestType string + ResponseType string + Id int +} + +type ConstantDefinition struct { + Type BaseType + Value string +} + +type Field struct { + Documentation string + Decorators map[string]Decorator + Type FieldType + Index int +} + +type IndentedStringBuilder struct { + spaces int + buffer bytes.Buffer +} + +func NewIndentedStringBuilder(spaces int) *IndentedStringBuilder { + return &IndentedStringBuilder{ + spaces: spaces, + } +} + +func (b *IndentedStringBuilder) AppendLine(texts ...string) *IndentedStringBuilder { + text := "" + if len(texts) > 0 { + text = texts[0] + } + lines := getLines(text) + for _, line := range lines { + b.buffer.WriteString(strings.Repeat(" ", b.spaces) + strings.TrimRight(line, " \t") + "\n") + } + return b +} + +func (b *IndentedStringBuilder) Append(text string) *IndentedStringBuilder { + lines := getLines(text) + for _, line := range lines { + b.buffer.WriteString(strings.Repeat(" ", b.spaces) + strings.TrimRight(line, " \t")) + } + return b +} + +func (b *IndentedStringBuilder) AppendMid(text string) *IndentedStringBuilder { + if len(getLines(text)) > 1 { + panic("AppendMid must not contain multiple lines") + } + b.buffer.WriteString(text) + return b +} + +func (b *IndentedStringBuilder) AppendEnd(text string) *IndentedStringBuilder { + if len(getLines(text)) > 1 { + panic("AppendEnd must not contain multiple lines") + } + b.buffer.WriteString(strings.TrimRight(text, " \t") + "\n") + return b +} + +func (b *IndentedStringBuilder) Indent(addSpaces int) *IndentedStringBuilder { + b.spaces = max(0, b.spaces+addSpaces) + return b +} + +func (b *IndentedStringBuilder) Dedent(removeSpaces int) *IndentedStringBuilder { + b.spaces = max(0, b.spaces-removeSpaces) + return b +} + +func (b *IndentedStringBuilder) CodeBlock(openingLine string, spaces int, fn func(b *IndentedStringBuilder), open string, close string) *IndentedStringBuilder { + if openingLine != "" { + b.Append(openingLine) + b.AppendEnd(" " + open) + } else { + b.AppendLine(open) + } + b.Indent(spaces) + fn(b) + b.Dedent(spaces) + b.AppendLine(close) + return b +} + +func (b *IndentedStringBuilder) String() string { + return b.buffer.String() +} + +func getLines(text string) []string { + return strings.Split(text, "\n") +} + +func max(a, b int) int { + if a > b { + return a + } + return b +} diff --git a/extensions/edks/typescript/README.md b/extensions/edks/typescript/README.md new file mode 100644 index 00000000..29658341 --- /dev/null +++ b/extensions/edks/typescript/README.md @@ -0,0 +1 @@ +# Hello World diff --git a/extensions/edks/typescript/chord.json b/extensions/edks/typescript/chord.json new file mode 100644 index 00000000..427ae7aa --- /dev/null +++ b/extensions/edks/typescript/chord.json @@ -0,0 +1,46 @@ +{ + "name": "javascript-template", + "private": true, + "description": "A template for chords built with javascript", + "version": "1.0.0", + "repository": "https://github.com/betwixt-labs/bebopc", + "license": "Apache-2.0", + "author": { + "name": "Betwixt Labs", + "email": "code@betwixtlabs.com" + }, + "bin": "dist/index.wasm", + "build": { + "script": "yarn build", + "compiler": "javy" + }, + "contributes": { + "generator": { + "alias": "acme", + "name": "ACME Generator" + }, + "decorators": { + "min": { + "description": "Sets the min of something idk", + "parameters": { + "floor": { + "description": "Specifies the floor of something idk", + "type": "int32", + "required": true + }, + "ceiling": { + "description": "Specifies the ceiling of something idk", + "type": "int32", + "required": false, + "default": 100 + } + }, + "targets": "all" + } + } + }, + "engine": { + "bebopc": "^3.0.0" + }, + "readme": "README.md" +} \ No newline at end of file diff --git a/extensions/edks/typescript/package.json b/extensions/edks/typescript/package.json new file mode 100644 index 00000000..143b49c2 --- /dev/null +++ b/extensions/edks/typescript/package.json @@ -0,0 +1,17 @@ +{ + "name": "javascript-example", + "version": "1.0.0", + "private": true, + "type": "module", + "main": "index.js", + "license": "MIT", + "scripts": { + "build": "esbuild src/index.ts --bundle --outfile=dist/index.js --platform=node --target=es2022 --format=esm && npx javy-cli compile ./dist/index.js --wit ./src/index.wit -n chord -o ./dist/index.wasm", + "build-wat": "yarn build && --wit ./src/index.wit -n chord -o ./dist/index.wasm" + }, + "devDependencies": { + "esbuild": "^0.19.11", + "javy-cli": "^0.2.0", + "typescript": "^5.3.3" + } +} diff --git a/extensions/edks/typescript/src/index.ts b/extensions/edks/typescript/src/index.ts new file mode 100644 index 00000000..e9b5fb02 --- /dev/null +++ b/extensions/edks/typescript/src/index.ts @@ -0,0 +1,82 @@ +import { CompilerContext, IndentedStringBuilder, commitResult, readContext } from "./kernel"; + +export function chordCompile() { + const input = readContext(); + + commitResult(generateDocumentation(input)); +} + +function generateDocumentation(context: CompilerContext): string { + const builder = new IndentedStringBuilder(); + + builder.appendLine(`Compiler Context Documentation`); + builder.appendLine(`===============================`); + builder.appendLine(); + + // Generate constants table + builder.appendLine(`Constants:`); + builder.appendLine(`+--------+--------+-------+`); + builder.appendLine(`| Name | Type | Value |`); + builder.appendLine(`+--------+--------+-------+`); + for (const [name, constant] of Object.entries(context.constants)) { + builder.appendLine(`| ${pad(name, 6)} | ${pad(constant.type, 6)} | ${pad(constant.value, 5)} |`); + } + builder.appendLine(`+--------+--------+-------+`); + builder.appendLine(); + + // Generate definitions table + builder.appendLine(`Definitions:`); + builder.appendLine(`+-------------+----------+`); + builder.appendLine(`| Name | Kind |`); + builder.appendLine(`+-------------+----------+`); + for (const [name, definition] of Object.entries(context.definitions)) { + builder.appendLine(`| ${pad(name, 11)} | ${pad(definition.kind, 8)} |`); + } + builder.appendLine(`+-------------+----------+`); + builder.appendLine(); + + // Generate fields table for each struct definition + for (const [name, definition] of Object.entries(context.definitions)) { + if (definition.kind === "struct") { + builder.appendLine(`Fields for struct "${name}":`); + builder.appendLine(`+-------------+----------+`); + builder.appendLine(`| Name | Type |`); + builder.appendLine(`+-------------+----------+`); + for (const [fieldName, field] of Object.entries(definition.fields)) { + builder.appendLine(`| ${pad(fieldName, 11)} | ${pad(field.type, 8)} |`); + } + builder.appendLine(`+-------------+----------+`); + builder.appendLine(); + } + } + + // Generate services table + builder.appendLine(`Services:`); + builder.appendLine(`+-------------+`); + builder.appendLine(`| Name |`); + builder.appendLine(`+-------------+`); + for (const [name, service] of Object.entries(context.services)) { + builder.appendLine(`| ${pad(name, 11)} |`); + } + builder.appendLine(`+-------------+`); + builder.appendLine(); + + // Generate methods table for each service + for (const [serviceName, service] of Object.entries(context.services)) { + builder.appendLine(`Methods for service "${serviceName}":`); + builder.appendLine(`+----------------+-------------+-------------+`); + builder.appendLine(`| Name | Request | Response |`); + builder.appendLine(`+----------------+-------------+-------------+`); + for (const [methodName, method] of Object.entries(service.methods)) { + builder.appendLine(`| ${pad(methodName, 14)} | ${pad(method.requestType, 11)} | ${pad(method.responseType, 11)} |`); + } + builder.appendLine(`+----------------+-------------+-------------+`); + builder.appendLine(); + } + + return builder.toString(); +} + +function pad(str: string, length: number): string { + return str.padEnd(length, " "); +} \ No newline at end of file diff --git a/extensions/edks/typescript/src/index.wit b/extensions/edks/typescript/src/index.wit new file mode 100644 index 00000000..6bb574ff --- /dev/null +++ b/extensions/edks/typescript/src/index.wit @@ -0,0 +1,5 @@ +package chord:kernel; + +world chord { + export chord-compile: func(); +} \ No newline at end of file diff --git a/extensions/edks/typescript/src/kernel.ts b/extensions/edks/typescript/src/kernel.ts new file mode 100644 index 00000000..837ea319 --- /dev/null +++ b/extensions/edks/typescript/src/kernel.ts @@ -0,0 +1,305 @@ +interface JavyBuiltins { + IO: { + // readSync: Similar to `write` in POSIX + // + // Params: + // - fd: File Descriptor (0 = stdin, 1 = stdout, 2 = stderr, >2 = custom) + // - buffer: Buffer to read into + // + // Return: + // - > 0: Number of bytes read + // - = 0: EOF reached + // - < 0: Error occured + readSync(fd: number, buffer: Uint8Array): number; + // writeSync: Similar to `write` in POSIX + // + // Params: + // - fd: File Descriptor (0 = stdin, 1 = stdout, 2 = stderr, >2 = custom) + // - buffer: Buffer to write + // + // Return: + // - >= 0: Number of bytes written + // - < 0: Error occured + writeSync(fd: number, buffer: Uint8Array): number; + }; +} + +declare global { + const Javy: JavyBuiltins; +} + +const enum STDIO { + StdIn, + StdOut, + StdErr, +} + +const decoder = new TextDecoder(); +const encoder = new TextEncoder(); + +function readFileSync(fd: number): Uint8Array { + let buffer = new Uint8Array(1024); + let bytesUsed = 0; + while (true) { + const bytesRead = Javy.IO.readSync(fd, buffer.subarray(bytesUsed)); + // A negative number of bytes read indicates an error. + if (bytesRead < 0) { + // FIXME: Figure out the specific error that occured. + throw Error("Error while reading from file descriptor"); + } + // 0 bytes read means we have reached EOF. + if (bytesRead === 0) { + return buffer.subarray(0, bytesUsed + bytesRead); + } + + bytesUsed += bytesRead; + // If we have filled the buffer, but have not reached EOF yet, + // double the buffers capacity and continue. + if (bytesUsed === buffer.length) { + const nextBuffer = new Uint8Array(buffer.length * 2); + nextBuffer.set(buffer); + buffer = nextBuffer; + } + } +} + +function writeFileSync(fd: number, buffer: Uint8Array) { + while (buffer.length > 0) { + // Try to write the entire buffer. + const bytesWritten = Javy.IO.writeSync(fd, buffer); + // A negative number of bytes written indicates an error. + if (bytesWritten < 0) { + throw Error("Error while writing to file descriptor"); + } + // 0 bytes means that the destination cannot accept additional bytes. + if (bytesWritten === 0) { + throw Error("Could not write all contents in buffer to file descriptor"); + } + // Otherwise cut off the bytes from the buffer that + // were successfully written. + buffer = buffer.subarray(bytesWritten); + } +} + +export const readContext = () => { + const input = decoder.decode(readFileSync(STDIO.StdIn)); + return JSON.parse(input) as CompilerContext; +}; + +export const commitResult = (result: string) => { + const data = encoder.encode(result); + writeFileSync(STDIO.StdOut, data); +}; + +export const writeStandardError = (error: string) => { + const data = encoder.encode(error); + writeFileSync(STDIO.StdErr, data); +}; + +export class IndentedStringBuilder { + private spaces: number; + private builder: string[]; + + constructor(spaces: number = 0) { + this.spaces = spaces; + this.builder = []; + } + + append(text: string): IndentedStringBuilder { + const indent = " ".repeat(this.spaces); + const lines = text.split("\n"); + const indentedLines = lines.map((x) => indent + x.trimEnd()); + const indentedText = indentedLines.join("\n").trimEnd(); + this.builder.push(indentedText); + return this; + } + + appendMid(text: string): IndentedStringBuilder { + if (text.split("\n").length > 1) { + throw new Error("AppendMid must not contain multiple lines"); + } + + this.builder.push(text); + return this; + } + + appendEnd(text: string): IndentedStringBuilder { + if (text.split("\n").length > 1) { + throw new Error("AppendEnd must not contain multiple lines"); + } + + this.builder.push(text.trimEnd() + "\n"); + return this; + } + + appendLine(text: string = "\n"): IndentedStringBuilder { + const indent = " ".repeat(this.spaces); + const lines = text.split("\n"); + const indentedLines = lines.map((x) => indent + x.trimEnd()); + const indentedText = indentedLines.join("\n").trimEnd(); + this.builder.push(`${indentedText}\n`); + return this; + } + + indent(addSpaces: number = 0): IndentedStringBuilder { + this.spaces = Math.max(0, this.spaces + addSpaces); + return this; + } + + dedent(removeSpaces: number = 0): IndentedStringBuilder { + this.spaces = Math.max(0, this.spaces - removeSpaces); + return this; + } + + codeBlock( + openingLine: string, + spaces: number, + fn: () => void, + open: string = "{", + close: string = "}" + ): IndentedStringBuilder { + if (openingLine) { + this.append(openingLine); + this.appendEnd(` ${open}`); + } else { + this.appendLine(open); + } + + this.indent(spaces); + fn(); + this.dedent(spaces); + this.appendLine(close); + return this; + } + + toString(): string { + return this.builder.join(""); + } +} + + +export type BaseType = "uint8" | "uint16" | "uint32" | "uint64" | "int8" | "int16" | "int32" | "int64" | "float32" | "float64" | "bool" | "string" | "guid" | "date"; + +export type Kind = "enum" | "struct" | "message" | "union" | "service" | "const"; + + +export type Decorator = { + [key: string]: { + arguments?: { + [key: string]: { + type: BaseType; + value: string; + }; + }; + }; +}; + +export type ArrayType = { + depth: number; + memberType: BaseType | "map"; + map?: MapType; +}; + +export type MapType = { + keyType: BaseType; + valueType: BaseType | "map" | "array"; + array?: ArrayType; + map?: MapType; +}; + +export type Field = { + documentation?: string; + decorators?: Decorator; + type: T; + index?: number; + array?: T extends "array" ? ArrayType : never; + map?: T extends "map" ? MapType : never; +}; + +export type EnumMember = { + documentation?: string; + decorators?: Decorator; + value: string; +}; + +export type BaseDefinition = { + kind: K; + documentation?: string; + decorators?: Decorator; + minimalEncodedSize: number; + discriminatorInParent?: number; + parent?: string; +}; + +export type EnumDefinition = BaseDefinition<"enum"> & { + isBitFlags?: boolean; + baseType?: BaseType; + members: { + [key: string]: EnumMember; + }; +}; + +export type StructDefinition = BaseDefinition<"struct"> & { + mutable: boolean; + isFixedSize: boolean; + fields: { + [key: string]: Field; + }; +}; + +export type MessageDefinition = BaseDefinition<"message"> & { + fields: { + [key: string]: Field; + }; +}; + +export type UnionDefinition = BaseDefinition<"union"> & { + branches: { + [key: string]: number; + }; +}; + +export type Method = { + decorators?: Decorator; + documentation?: string; + type: "Unary" | "DuplexStream" | "ClientStream" | "ServerStream"; + requestType: string; + responseType: string; + id: number; +}; + +export type ServiceDefinition = BaseDefinition<"service"> & { + methods: { + [key: string]: Method; + }; +}; + +export type ConstDefinition = BaseDefinition<"const"> & { + type: BaseType; + value: string; +}; + +export type Config = { + alias: string; + outFile: string; + namespace: string; + emitNotice: boolean; + emitBinarySchema: boolean; + services: "both" | "server" | "client"; + options: { + [key: string]: string; + }; +}; + +export type CompilerContext = { + definitions: { + [key: string]: EnumDefinition | StructDefinition | MessageDefinition | UnionDefinition; + }; + services: { + [key: string]: ServiceDefinition; + }; + constants: { + [key: string]: ConstDefinition; + }; + config: Config; +}; diff --git a/extensions/edks/typescript/ts-edk.code-workspace b/extensions/edks/typescript/ts-edk.code-workspace new file mode 100644 index 00000000..c7223d8e --- /dev/null +++ b/extensions/edks/typescript/ts-edk.code-workspace @@ -0,0 +1,7 @@ +{ + "folders": [ + { + "path": "." + } + ] +} diff --git a/extensions/edks/typescript/tsconfig.json b/extensions/edks/typescript/tsconfig.json new file mode 100644 index 00000000..d25f7e69 --- /dev/null +++ b/extensions/edks/typescript/tsconfig.json @@ -0,0 +1,15 @@ +{ + "compilerOptions": { + "module": "ES2022", + "esModuleInterop": true, + "allowSyntheticDefaultImports": true, + "target": "ES2022", + "noImplicitAny": true, + "moduleResolution": "node", + "sourceMap": true, + "outDir": "dist", + "rootDir": "src" + }, + "include": ["src/**/*"], + "exclude": ["node_modules", "dist"] +} diff --git a/extensions/edks/typescript/yarn.lock b/extensions/edks/typescript/yarn.lock new file mode 100644 index 00000000..0045487d --- /dev/null +++ b/extensions/edks/typescript/yarn.lock @@ -0,0 +1,204 @@ +# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. +# yarn lockfile v1 + + +"@esbuild/aix-ppc64@0.19.11": + version "0.19.11" + resolved "https://registry.yarnpkg.com/@esbuild/aix-ppc64/-/aix-ppc64-0.19.11.tgz#2acd20be6d4f0458bc8c784103495ff24f13b1d3" + integrity sha512-FnzU0LyE3ySQk7UntJO4+qIiQgI7KoODnZg5xzXIrFJlKd2P2gwHsHY4927xj9y5PJmJSzULiUCWmv7iWnNa7g== + +"@esbuild/android-arm64@0.19.11": + version "0.19.11" + resolved "https://registry.yarnpkg.com/@esbuild/android-arm64/-/android-arm64-0.19.11.tgz#b45d000017385c9051a4f03e17078abb935be220" + integrity sha512-aiu7K/5JnLj//KOnOfEZ0D90obUkRzDMyqd/wNAUQ34m4YUPVhRZpnqKV9uqDGxT7cToSDnIHsGooyIczu9T+Q== + +"@esbuild/android-arm@0.19.11": + version "0.19.11" + resolved "https://registry.yarnpkg.com/@esbuild/android-arm/-/android-arm-0.19.11.tgz#f46f55414e1c3614ac682b29977792131238164c" + integrity sha512-5OVapq0ClabvKvQ58Bws8+wkLCV+Rxg7tUVbo9xu034Nm536QTII4YzhaFriQ7rMrorfnFKUsArD2lqKbFY4vw== + +"@esbuild/android-x64@0.19.11": + version "0.19.11" + resolved "https://registry.yarnpkg.com/@esbuild/android-x64/-/android-x64-0.19.11.tgz#bfc01e91740b82011ef503c48f548950824922b2" + integrity sha512-eccxjlfGw43WYoY9QgB82SgGgDbibcqyDTlk3l3C0jOVHKxrjdc9CTwDUQd0vkvYg5um0OH+GpxYvp39r+IPOg== + +"@esbuild/darwin-arm64@0.19.11": + version "0.19.11" + resolved "https://registry.yarnpkg.com/@esbuild/darwin-arm64/-/darwin-arm64-0.19.11.tgz#533fb7f5a08c37121d82c66198263dcc1bed29bf" + integrity sha512-ETp87DRWuSt9KdDVkqSoKoLFHYTrkyz2+65fj9nfXsaV3bMhTCjtQfw3y+um88vGRKRiF7erPrh/ZuIdLUIVxQ== + +"@esbuild/darwin-x64@0.19.11": + version "0.19.11" + resolved "https://registry.yarnpkg.com/@esbuild/darwin-x64/-/darwin-x64-0.19.11.tgz#62f3819eff7e4ddc656b7c6815a31cf9a1e7d98e" + integrity sha512-fkFUiS6IUK9WYUO/+22omwetaSNl5/A8giXvQlcinLIjVkxwTLSktbF5f/kJMftM2MJp9+fXqZ5ezS7+SALp4g== + +"@esbuild/freebsd-arm64@0.19.11": + version "0.19.11" + resolved "https://registry.yarnpkg.com/@esbuild/freebsd-arm64/-/freebsd-arm64-0.19.11.tgz#d478b4195aa3ca44160272dab85ef8baf4175b4a" + integrity sha512-lhoSp5K6bxKRNdXUtHoNc5HhbXVCS8V0iZmDvyWvYq9S5WSfTIHU2UGjcGt7UeS6iEYp9eeymIl5mJBn0yiuxA== + +"@esbuild/freebsd-x64@0.19.11": + version "0.19.11" + resolved "https://registry.yarnpkg.com/@esbuild/freebsd-x64/-/freebsd-x64-0.19.11.tgz#7bdcc1917409178257ca6a1a27fe06e797ec18a2" + integrity sha512-JkUqn44AffGXitVI6/AbQdoYAq0TEullFdqcMY/PCUZ36xJ9ZJRtQabzMA+Vi7r78+25ZIBosLTOKnUXBSi1Kw== + +"@esbuild/linux-arm64@0.19.11": + version "0.19.11" + resolved "https://registry.yarnpkg.com/@esbuild/linux-arm64/-/linux-arm64-0.19.11.tgz#58ad4ff11685fcc735d7ff4ca759ab18fcfe4545" + integrity sha512-LneLg3ypEeveBSMuoa0kwMpCGmpu8XQUh+mL8XXwoYZ6Be2qBnVtcDI5azSvh7vioMDhoJFZzp9GWp9IWpYoUg== + +"@esbuild/linux-arm@0.19.11": + version "0.19.11" + resolved "https://registry.yarnpkg.com/@esbuild/linux-arm/-/linux-arm-0.19.11.tgz#ce82246d873b5534d34de1e5c1b33026f35e60e3" + integrity sha512-3CRkr9+vCV2XJbjwgzjPtO8T0SZUmRZla+UL1jw+XqHZPkPgZiyWvbDvl9rqAN8Zl7qJF0O/9ycMtjU67HN9/Q== + +"@esbuild/linux-ia32@0.19.11": + version "0.19.11" + resolved "https://registry.yarnpkg.com/@esbuild/linux-ia32/-/linux-ia32-0.19.11.tgz#cbae1f313209affc74b80f4390c4c35c6ab83fa4" + integrity sha512-caHy++CsD8Bgq2V5CodbJjFPEiDPq8JJmBdeyZ8GWVQMjRD0sU548nNdwPNvKjVpamYYVL40AORekgfIubwHoA== + +"@esbuild/linux-loong64@0.19.11": + version "0.19.11" + resolved "https://registry.yarnpkg.com/@esbuild/linux-loong64/-/linux-loong64-0.19.11.tgz#5f32aead1c3ec8f4cccdb7ed08b166224d4e9121" + integrity sha512-ppZSSLVpPrwHccvC6nQVZaSHlFsvCQyjnvirnVjbKSHuE5N24Yl8F3UwYUUR1UEPaFObGD2tSvVKbvR+uT1Nrg== + +"@esbuild/linux-mips64el@0.19.11": + version "0.19.11" + resolved "https://registry.yarnpkg.com/@esbuild/linux-mips64el/-/linux-mips64el-0.19.11.tgz#38eecf1cbb8c36a616261de858b3c10d03419af9" + integrity sha512-B5x9j0OgjG+v1dF2DkH34lr+7Gmv0kzX6/V0afF41FkPMMqaQ77pH7CrhWeR22aEeHKaeZVtZ6yFwlxOKPVFyg== + +"@esbuild/linux-ppc64@0.19.11": + version "0.19.11" + resolved "https://registry.yarnpkg.com/@esbuild/linux-ppc64/-/linux-ppc64-0.19.11.tgz#9c5725a94e6ec15b93195e5a6afb821628afd912" + integrity sha512-MHrZYLeCG8vXblMetWyttkdVRjQlQUb/oMgBNurVEnhj4YWOr4G5lmBfZjHYQHHN0g6yDmCAQRR8MUHldvvRDA== + +"@esbuild/linux-riscv64@0.19.11": + version "0.19.11" + resolved "https://registry.yarnpkg.com/@esbuild/linux-riscv64/-/linux-riscv64-0.19.11.tgz#2dc4486d474a2a62bbe5870522a9a600e2acb916" + integrity sha512-f3DY++t94uVg141dozDu4CCUkYW+09rWtaWfnb3bqe4w5NqmZd6nPVBm+qbz7WaHZCoqXqHz5p6CM6qv3qnSSQ== + +"@esbuild/linux-s390x@0.19.11": + version "0.19.11" + resolved "https://registry.yarnpkg.com/@esbuild/linux-s390x/-/linux-s390x-0.19.11.tgz#4ad8567df48f7dd4c71ec5b1753b6f37561a65a8" + integrity sha512-A5xdUoyWJHMMlcSMcPGVLzYzpcY8QP1RtYzX5/bS4dvjBGVxdhuiYyFwp7z74ocV7WDc0n1harxmpq2ePOjI0Q== + +"@esbuild/linux-x64@0.19.11": + version "0.19.11" + resolved "https://registry.yarnpkg.com/@esbuild/linux-x64/-/linux-x64-0.19.11.tgz#b7390c4d5184f203ebe7ddaedf073df82a658766" + integrity sha512-grbyMlVCvJSfxFQUndw5mCtWs5LO1gUlwP4CDi4iJBbVpZcqLVT29FxgGuBJGSzyOxotFG4LoO5X+M1350zmPA== + +"@esbuild/netbsd-x64@0.19.11": + version "0.19.11" + resolved "https://registry.yarnpkg.com/@esbuild/netbsd-x64/-/netbsd-x64-0.19.11.tgz#d633c09492a1721377f3bccedb2d821b911e813d" + integrity sha512-13jvrQZJc3P230OhU8xgwUnDeuC/9egsjTkXN49b3GcS5BKvJqZn86aGM8W9pd14Kd+u7HuFBMVtrNGhh6fHEQ== + +"@esbuild/openbsd-x64@0.19.11": + version "0.19.11" + resolved "https://registry.yarnpkg.com/@esbuild/openbsd-x64/-/openbsd-x64-0.19.11.tgz#17388c76e2f01125bf831a68c03a7ffccb65d1a2" + integrity sha512-ysyOGZuTp6SNKPE11INDUeFVVQFrhcNDVUgSQVDzqsqX38DjhPEPATpid04LCoUr2WXhQTEZ8ct/EgJCUDpyNw== + +"@esbuild/sunos-x64@0.19.11": + version "0.19.11" + resolved "https://registry.yarnpkg.com/@esbuild/sunos-x64/-/sunos-x64-0.19.11.tgz#e320636f00bb9f4fdf3a80e548cb743370d41767" + integrity sha512-Hf+Sad9nVwvtxy4DXCZQqLpgmRTQqyFyhT3bZ4F2XlJCjxGmRFF0Shwn9rzhOYRB61w9VMXUkxlBy56dk9JJiQ== + +"@esbuild/win32-arm64@0.19.11": + version "0.19.11" + resolved "https://registry.yarnpkg.com/@esbuild/win32-arm64/-/win32-arm64-0.19.11.tgz#c778b45a496e90b6fc373e2a2bb072f1441fe0ee" + integrity sha512-0P58Sbi0LctOMOQbpEOvOL44Ne0sqbS0XWHMvvrg6NE5jQ1xguCSSw9jQeUk2lfrXYsKDdOe6K+oZiwKPilYPQ== + +"@esbuild/win32-ia32@0.19.11": + version "0.19.11" + resolved "https://registry.yarnpkg.com/@esbuild/win32-ia32/-/win32-ia32-0.19.11.tgz#481a65fee2e5cce74ec44823e6b09ecedcc5194c" + integrity sha512-6YOrWS+sDJDmshdBIQU+Uoyh7pQKrdykdefC1avn76ss5c+RN6gut3LZA4E2cH5xUEp5/cA0+YxRaVtRAb0xBg== + +"@esbuild/win32-x64@0.19.11": + version "0.19.11" + resolved "https://registry.yarnpkg.com/@esbuild/win32-x64/-/win32-x64-0.19.11.tgz#a5d300008960bb39677c46bf16f53ec70d8dee04" + integrity sha512-vfkhltrjCAb603XaFhqhAF4LGDi2M4OrCRrFusyQ+iTLQ/o60QQXxc9cZC/FFpihBI9N1Grn6SMKVJ4KP7Fuiw== + +cachedir@^2.3.0: + version "2.4.0" + resolved "https://registry.yarnpkg.com/cachedir/-/cachedir-2.4.0.tgz#7fef9cf7367233d7c88068fe6e34ed0d355a610d" + integrity sha512-9EtFOZR8g22CL7BWjJ9BUx1+A/djkofnyW3aOXZORNW2kxoUpx2h+uN2cOqwPmFhnpVmxg+KW2OjOSgChTEvsQ== + +data-uri-to-buffer@^4.0.0: + version "4.0.1" + resolved "https://registry.yarnpkg.com/data-uri-to-buffer/-/data-uri-to-buffer-4.0.1.tgz#d8feb2b2881e6a4f58c2e08acfd0e2834e26222e" + integrity sha512-0R9ikRb668HB7QDxT1vkpuUBtqc53YyAwMwGeUFKRojY/NWKvdZ+9UYtRfGmhqNbRkTSVpMbmyhXipFFv2cb/A== + +esbuild@^0.19.11: + version "0.19.11" + resolved "https://registry.yarnpkg.com/esbuild/-/esbuild-0.19.11.tgz#4a02dca031e768b5556606e1b468fe72e3325d96" + integrity sha512-HJ96Hev2hX/6i5cDVwcqiJBBtuo9+FeIJOtZ9W1kA5M6AMJRHUZlpYZ1/SbEwtO0ioNAW8rUooVpC/WehY2SfA== + optionalDependencies: + "@esbuild/aix-ppc64" "0.19.11" + "@esbuild/android-arm" "0.19.11" + "@esbuild/android-arm64" "0.19.11" + "@esbuild/android-x64" "0.19.11" + "@esbuild/darwin-arm64" "0.19.11" + "@esbuild/darwin-x64" "0.19.11" + "@esbuild/freebsd-arm64" "0.19.11" + "@esbuild/freebsd-x64" "0.19.11" + "@esbuild/linux-arm" "0.19.11" + "@esbuild/linux-arm64" "0.19.11" + "@esbuild/linux-ia32" "0.19.11" + "@esbuild/linux-loong64" "0.19.11" + "@esbuild/linux-mips64el" "0.19.11" + "@esbuild/linux-ppc64" "0.19.11" + "@esbuild/linux-riscv64" "0.19.11" + "@esbuild/linux-s390x" "0.19.11" + "@esbuild/linux-x64" "0.19.11" + "@esbuild/netbsd-x64" "0.19.11" + "@esbuild/openbsd-x64" "0.19.11" + "@esbuild/sunos-x64" "0.19.11" + "@esbuild/win32-arm64" "0.19.11" + "@esbuild/win32-ia32" "0.19.11" + "@esbuild/win32-x64" "0.19.11" + +fetch-blob@^3.1.2, fetch-blob@^3.1.4: + version "3.2.0" + resolved "https://registry.yarnpkg.com/fetch-blob/-/fetch-blob-3.2.0.tgz#f09b8d4bbd45adc6f0c20b7e787e793e309dcce9" + integrity sha512-7yAQpD2UMJzLi1Dqv7qFYnPbaPx7ZfFK6PiIxQ4PfkGPyNyl2Ugx+a/umUonmKqjhM4DnfbMvdX6otXq83soQQ== + dependencies: + node-domexception "^1.0.0" + web-streams-polyfill "^3.0.3" + +formdata-polyfill@^4.0.10: + version "4.0.10" + resolved "https://registry.yarnpkg.com/formdata-polyfill/-/formdata-polyfill-4.0.10.tgz#24807c31c9d402e002ab3d8c720144ceb8848423" + integrity sha512-buewHzMvYL29jdeQTVILecSaZKnt/RJWjoZCF5OW60Z67/GmSLBkOFM7qh1PI3zFNtJbaZL5eQu1vLfazOwj4g== + dependencies: + fetch-blob "^3.1.2" + +javy-cli@^0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/javy-cli/-/javy-cli-0.2.0.tgz#12a8219227f7a7f64a234e139a81eca69bb3ea36" + integrity sha512-kxY6+q+1bhXjfFb5o8ETlfKtxEMajSqNB/0G1l9tVfFU0RMYiDHHRQgs2r8cSAZhkEgs2gSKlpxgopQ4NuL6Ig== + dependencies: + cachedir "^2.3.0" + node-fetch "^3.2.10" + +node-domexception@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/node-domexception/-/node-domexception-1.0.0.tgz#6888db46a1f71c0b76b3f7555016b63fe64766e5" + integrity sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ== + +node-fetch@^3.2.10: + version "3.3.2" + resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-3.3.2.tgz#d1e889bacdf733b4ff3b2b243eb7a12866a0b78b" + integrity sha512-dRB78srN/l6gqWulah9SrxeYnxeddIG30+GOqK/9OlLVyLg3HPnr6SqOWTWOXKRwC2eGYCkZ59NNuSgvSrpgOA== + dependencies: + data-uri-to-buffer "^4.0.0" + fetch-blob "^3.1.4" + formdata-polyfill "^4.0.10" + +typescript@^5.3.3: + version "5.3.3" + resolved "https://registry.yarnpkg.com/typescript/-/typescript-5.3.3.tgz#b3ce6ba258e72e6305ba66f5c9b452aaee3ffe37" + integrity sha512-pXWcraxM0uxAS+tN0AG/BF2TyqmHO014Z070UsJ+pFvYuRSq8KH8DmWpnbXe0pEPDHXZV3FcAbJkijJ5oNEnWw== + +web-streams-polyfill@^3.0.3: + version "3.3.2" + resolved "https://registry.yarnpkg.com/web-streams-polyfill/-/web-streams-polyfill-3.3.2.tgz#32e26522e05128203a7de59519be3c648004343b" + integrity sha512-3pRGuxRF5gpuZc0W+EpwQRmCD7gRqcDOMt688KmdlDAgAyaB1XlN0zq2njfDNm44XVdIouE7pZ6GzbdyH47uIQ== diff --git a/extensions/extensions.sln b/extensions/extensions.sln new file mode 100644 index 00000000..131c88c5 --- /dev/null +++ b/extensions/extensions.sln @@ -0,0 +1,37 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 17 +VisualStudioVersion = 17.5.002.0 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "chordc", "chordc\chordc.csproj", "{617AED90-2146-4DA0-875F-B5BEA087B8C7}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "chord.runtime", "chord.runtime\chord.runtime.csproj", "{C84E23BE-8971-409E-8057-6D84DB81E9EE}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "chord.common", "chord.common\chord.common.csproj", "{AC1D6FCE-00DA-4228-B663-F6FF6DBA08F7}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {617AED90-2146-4DA0-875F-B5BEA087B8C7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {617AED90-2146-4DA0-875F-B5BEA087B8C7}.Debug|Any CPU.Build.0 = Debug|Any CPU + {617AED90-2146-4DA0-875F-B5BEA087B8C7}.Release|Any CPU.ActiveCfg = Release|Any CPU + {617AED90-2146-4DA0-875F-B5BEA087B8C7}.Release|Any CPU.Build.0 = Release|Any CPU + {C84E23BE-8971-409E-8057-6D84DB81E9EE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {C84E23BE-8971-409E-8057-6D84DB81E9EE}.Debug|Any CPU.Build.0 = Debug|Any CPU + {C84E23BE-8971-409E-8057-6D84DB81E9EE}.Release|Any CPU.ActiveCfg = Release|Any CPU + {C84E23BE-8971-409E-8057-6D84DB81E9EE}.Release|Any CPU.Build.0 = Release|Any CPU + {AC1D6FCE-00DA-4228-B663-F6FF6DBA08F7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {AC1D6FCE-00DA-4228-B663-F6FF6DBA08F7}.Debug|Any CPU.Build.0 = Debug|Any CPU + {AC1D6FCE-00DA-4228-B663-F6FF6DBA08F7}.Release|Any CPU.ActiveCfg = Release|Any CPU + {AC1D6FCE-00DA-4228-B663-F6FF6DBA08F7}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {EA962056-F10C-4DE2-8E73-4CCDC244F10A} + EndGlobalSection +EndGlobal diff --git a/homepage/index.html b/homepage/index.html index 95f0dd51..0e55cec9 100644 --- a/homepage/index.html +++ b/homepage/index.html @@ -1,5 +1,6 @@ + @@ -8,23 +9,23 @@ - - - + + + - - - - - + + + + + - - - - - + + + + + - + @@ -32,6 +33,7 @@ +
@@ -116,4 +116,5 @@

🎷No ceremony, just code. Blazing fast, typesafe binary serialization.

+ \ No newline at end of file diff --git a/playground/.eslintrc.json b/playground/.eslintrc.json new file mode 100644 index 00000000..6933b6c9 --- /dev/null +++ b/playground/.eslintrc.json @@ -0,0 +1,106 @@ +{ + "extends": [ + "eslint:recommended", + "plugin:@typescript-eslint/recommended", + "plugin:@typescript-eslint/recommended-requiring-type-checking", + "plugin:@typescript-eslint/strict", + "plugin:unicorn/recommended" + ], + "parser": "@typescript-eslint/parser", + "parserOptions": { + "warnOnUnsupportedTypeScriptVersion": false, + "sourceType": "module", + "project": true + }, + "plugins": ["@typescript-eslint", "simple-import-sort", "unicorn"], + "root": true, + "rules": { + "eqeqeq": "error", + "no-constant-condition": 0, + "no-inner-declarations": 0, + "no-undef": 0, + "no-unused-vars": 0, + "no-restricted-globals": ["error", "console", "process"], + "simple-import-sort/imports": "error", + "simple-import-sort/exports": "error", + // In theory good, but less good when declaring a new interface and + // stopping to think about its contents. + "@typescript-eslint/no-empty-interface": 0, + "@typescript-eslint/no-namespace": 0, + "@typescript-eslint/no-unused-vars": ["warn", { "argsIgnorePattern": "^_", "varsIgnorePattern": "^_" }], + "@typescript-eslint/no-use-before-define": 0, + "@typescript-eslint/no-explicit-any": 0, + "@typescript-eslint/no-unsafe-argument": 0, + "@typescript-eslint/no-unsafe-assignment": 0, + "@typescript-eslint/no-unsafe-call": 0, + "@typescript-eslint/no-unsafe-member-access": 0, + "@typescript-eslint/no-unsafe-return": 0, + "@typescript-eslint/ban-types": 0, + "@typescript-eslint/require-await": 0, + "@typescript-eslint/restrict-template-expressions": 0, + "@typescript-eslint/naming-convention": [ + "error", + { + "selector": [ + "classProperty", + "typeProperty", + "parameterProperty", + "classMethod", + "typeMethod", + "accessor" + ], + "modifiers": ["private"], + "leadingUnderscore": "require", + "format": ["camelCase"], + "filter": { + "regex": "^(test_| )", + "match": false + } + }, + { + "selector": [ + "classProperty", + "typeProperty", + "parameterProperty", + "classMethod", + "typeMethod", + "accessor" + ], + "modifiers": ["protected"], + "leadingUnderscore": "allow", + "format": ["camelCase"], + "filter": { + "regex": "^(test_| )", + "match": false + } + }, + { + "selector": [ + "classProperty", + "typeProperty", + "parameterProperty", + "classMethod", + "typeMethod", + "accessor" + ], + "modifiers": ["public"], + "leadingUnderscore": "forbid", + "format": ["camelCase"], + "filter": { + "regex": "^(test_| )", + "match": false + } + } + ], + "unicorn/no-negated-condition": 0, + "unicorn/catch-error-name": 0, + "unicorn/filename-case": 0, + "unicorn/no-array-callback-reference": 0, + "unicorn/no-await-expression-member": 0, + "unicorn/no-useless-undefined": 0, + "unicorn/prevent-abbreviations": 0, + "unicorn/switch-case-braces": 0, + "unicorn/prefer-string-replace-all": 0 // Bad suggestion for old targets + }, + "ignorePatterns": ["**/dist/**", "**/node_modules/**", "vite.config.ts"] +} \ No newline at end of file diff --git a/playground/.gitignore b/playground/.gitignore new file mode 100644 index 00000000..bc286cdc --- /dev/null +++ b/playground/.gitignore @@ -0,0 +1,26 @@ +# Logs +logs +*.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* +pnpm-debug.log* +lerna-debug.log* + +node_modules +dist +dist-ssr +*.local +.tsup +*.wasm + +# Editor directories and files +.vscode/* +!.vscode/extensions.json +.idea +.DS_Store +*.suo +*.ntvs* +*.njsproj +*.sln +*.sw? diff --git a/playground/LICENSE b/playground/LICENSE new file mode 100644 index 00000000..261eeb9e --- /dev/null +++ b/playground/LICENSE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/playground/README.md b/playground/README.md new file mode 100644 index 00000000..6b0e36ae --- /dev/null +++ b/playground/README.md @@ -0,0 +1,2 @@ +an experimental package for running [bebopc](https://github.com/betwixt-labs/bebopc) in the browser as a JavaScript library. + diff --git a/playground/assets/favicon-16.png b/playground/assets/favicon-16.png new file mode 100644 index 00000000..89e85c29 Binary files /dev/null and b/playground/assets/favicon-16.png differ diff --git a/playground/assets/favicon-32.png b/playground/assets/favicon-32.png new file mode 100644 index 00000000..d2bfff29 Binary files /dev/null and b/playground/assets/favicon-32.png differ diff --git a/playground/assets/index.css b/playground/assets/index.css new file mode 100644 index 00000000..b5c61c95 --- /dev/null +++ b/playground/assets/index.css @@ -0,0 +1,3 @@ +@tailwind base; +@tailwind components; +@tailwind utilities; diff --git a/playground/assets/logo.svg b/playground/assets/logo.svg new file mode 100644 index 00000000..68776c8b --- /dev/null +++ b/playground/assets/logo.svg @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/playground/index.html b/playground/index.html new file mode 100644 index 00000000..e4d61b56 --- /dev/null +++ b/playground/index.html @@ -0,0 +1,208 @@ + + + + + + + + + Bebop Playground - An online editor for exploring the Bebop schema language + + + + + + + + + + + + + +
+
+ Logo +
+ +
+ + +
+ +
+
+
0%
+
+
+ +
+ +
+
+ schema.bop +
+
+
+ +
+
+ bebop.json +
+
+
+ +
+
+ .dev.vars +
+
+
+ + +
+ + +
+ + +
+ Version info +
+ + + + + + \ No newline at end of file diff --git a/playground/package.json b/playground/package.json new file mode 100644 index 00000000..4ea4edda --- /dev/null +++ b/playground/package.json @@ -0,0 +1,65 @@ +{ + "name": "bebopc-playground", + "description": "a playground for the Bebop compiler", + "author": "", + "license": "MIT", + "homepage": "", + "repository": { + "type": "git", + "url": "https://github.com//my-ts-lib.git" + }, + "bugs": { + "url": "https://github.com//my-ts-lib/issues" + }, + "keywords": [ + "some", + "keywords", + "to", + "describe", + "the", + "package" + ], + "private": true, + "version": "0.0.0", + "type": "module", + "files": [ + "dist" + ], + "scripts": { + "build:compiler": "../scripts/build-wasi.sh && cp -f ../bin/compiler/Release/artifacts/wasi-wasm/AppBundle/bebopc.wasm ./src/bebopc/bebopc.wasm", + "dev": "vite", + "build:site": "NODE_ENV=production yarn build:compiler && vite build", + "preview:site": "vite preview", + "test": "vitest" + }, + "dependencies": { + "coi-serviceworker": "^0.1.7", + "comlink": "^4.4.1", + "lz-string": "^1.5.0", + "monaco-editor": "^0.45.0", + "wasi-js": "^1.7.3" + }, + "devDependencies": { + "@types/node": "^20.10.3", + "@typescript-eslint/eslint-plugin": "^6.13.2", + "@typescript-eslint/parser": "^6.13.2", + "autoprefixer": "^10.4.17", + "cssnano": "^6.0.3", + "eslint": "^8.56.0", + "eslint-plugin-simple-import-sort": "^10.0.0", + "eslint-plugin-unicorn": "^49.0.0", + "postcss": "^8.4.33", + "rollup-plugin-terser": "^7.0.2", + "tailwindcss": "^3.4.1", + "typescript": "^5.2.2", + "vite": "^5.0.8", + "vite-plugin-comlink": "^3.0.5", + "vite-plugin-dts": "^1.7.3", + "vite-plugin-html": "^3.2.2", + "vite-plugin-inspect": "^0.8.1", + "vite-plugin-monaco-editor": "^1.1.0", + "vite-plugin-node-polyfills": "^0.17.0", + "vite-plugin-static-copy": "^1.0.0", + "vitest": "^1.0.4" + } +} diff --git a/playground/postcss.config.js b/playground/postcss.config.js new file mode 100644 index 00000000..3fc77ed7 --- /dev/null +++ b/playground/postcss.config.js @@ -0,0 +1,7 @@ +export default { + plugins: { + tailwindcss: {}, + autoprefixer: {}, + ...(process.env.NODE_ENV === "production" ? { cssnano: {} } : {}), + }, +}; diff --git a/playground/src/bebopc/index.ts b/playground/src/bebopc/index.ts new file mode 100644 index 00000000..2d7877ff --- /dev/null +++ b/playground/src/bebopc/index.ts @@ -0,0 +1,621 @@ +/* eslint-disable no-control-regex */ +/* eslint-disable unicorn/no-hex-escape */ +/* eslint-disable unicorn/escape-case */ + +import { posix as path } from "node:path"; + +import * as Comlink from "comlink"; + +import { memoize, stripJsonComments } from "../internal/helpers"; +import { + BebopConfig, + BuildOptions, + CommandBuilder, + CompilerError, + CompilerException, + CompilerOutput, + Diagnostic, + DiagnosticError, + Flag, + FlagValue, + GeneratedFile, + GeneratorConfig, + RootOptions, + SubCommand, + WorkerResponse, +} from "./types"; + +function stripAnsiCodes(message: string): string { + const regex = /\x1b\[(\d+);5;(\d+)m|\x1b\[0m/g; + return message.replace(regex, ""); +} +// Function to convert ANSI codes to CSS styles and print in browser console +function printAnsiLogToBrowser(message: string): void { + const regex = /\x1b\[(\d+);5;(\d+)m|\x1b\[0m/g; + + let formattedMessage = message; + let match: RegExpExecArray | null; + const cssStyles: string[] = []; + + // Reset the lastIndex to ensure the regex works correctly for global search + regex.lastIndex = 0; + + while ((match = regex.exec(message)) !== null) { + if (match[0] === "\x1b[0m") { + // Handle ANSI reset sequence + cssStyles.push("color: initial; background-color: initial;"); + formattedMessage = formattedMessage.replace(match[0], "%c"); + } else { + let style = ""; + const colorType = Number.parseInt(match[1], 10); + const colorCode = Number.parseInt(match[2], 10); + + let cssColor = ""; + switch (colorCode) { + case 11: + cssColor = "yellow"; + break; + case 12: + cssColor = "lightblue"; + break; + case 15: + cssColor = "white"; + break; + case 2: + cssColor = "lightgreen"; + break; + default: + cssColor = "initial"; + } + + style = (colorType === 38 ? "color: " : "background-color: ") + cssColor; + + cssStyles.push(style); + formattedMessage = formattedMessage.replace(match[0], "%c"); + } + } + // eslint-disable-next-line no-restricted-globals + console.log(formattedMessage, ...cssStyles); +} + +const spanExitCode = 74; +const ok = 0; +const compilerErrorExitCode = 1; +const fileNotFoundExitCode = 66; + +const generatorConfigToString = (config: GeneratorConfig) => { + const [alias] = Object.keys(config); + const [body] = Object.values(config); + const options = Object.entries(body.options || {}) + .map(([key, value]) => `${key}=${value}`) + .join(","); + + let result = `${alias}:${body.outFile}`; + if (body.services) { + result += `,${body.services}`; + } + if (body.emitNotice) { + result += ",emitNotice=true"; + } + if (body.emitBinarySchema) { + result += ",emitBinarySchema=true"; + } + if (body.namespace) { + result += `,namespace=${body.namespace}`; + } + if (options) { + result += `,${options}`; + } + return result; +}; + +/** + * Creates a command builder for bebopc. + * @param parentArgs + * @returns + */ +const createCommandBuilder = (parentArgs?: string[]) => { + const args: string[] = parentArgs || ["bebopc"]; + const addFlag = (flag: Flag, value?: FlagValue) => { + args.push(flag); + if (value !== undefined) { + if (Array.isArray(value)) { + args.push(...value); + } else { + args.push(value); + } + } + return { addFlag, addSubCommand, build }; + }; + + const addSubCommand = (subCommand: SubCommand) => { + args.push(subCommand); + return createCommandBuilder(args); + }; + + const build = () => { + return args; + }; + + return { addFlag, addSubCommand, build }; +}; + +/* + * Creates the worker and memoizing it so that it is only created once. + */ +const getWorker = memoize( + () => + new ComlinkWorker( + new URL("worker", import.meta.url), + { type: "classic" } + ) +); + +const addRootOptions = (options: RootOptions, builder: CommandBuilder) => { + if (options.config) { + builder.addFlag("--config", options.config); + } + if (options.trace) { + builder.addFlag("--trace"); + } + if (options.include) { + builder.addFlag("--include", options.include); + } + if (options.exclude) { + builder.addFlag("--exclude", options.exclude); + } + if (options.locale) { + builder.addFlag("--locale", options.locale); + } + if (options.diagnosticFormat) { + builder.addFlag("--diagnostic-format", options.diagnosticFormat); + } else { + builder.addFlag("--diagnostic-format", "json"); + } +}; + +const fileNameRegexp = /^\s*\/\/\s*@filename:\s*(.+)$/gim; + +export const createFileMap = (input: string): Map => { + fileNameRegexp.lastIndex = 0; + if (!fileNameRegexp.test(input)) { + throw new CompilerError("error", "no input files found", 1); + } + const lines = input.split(/\r?\n/g); + let currentFilename: string | undefined; + let currentLines: string[] = []; + + const files = new Map(); + function finalizeFile() { + if (currentFilename) { + files.set(currentFilename, currentLines.join("\n")); + } + } + for (const line of lines) { + fileNameRegexp.lastIndex = 0; + const match = fileNameRegexp.exec(line); + if (match) { + finalizeFile(); + currentFilename = path.resolve("/", match[1]); + currentLines = []; + continue; + } + + if (currentFilename) { + currentLines.push(line); + } + } + finalizeFile(); + return files; +}; + +const extLookupTable: Record< + string, + { + ext: string; + auxiliaryExt?: string; + } +> = { + cpp: { ext: "cpp", auxiliaryExt: "hpp" }, + cs: { ext: "cs" }, + dart: { ext: "dart" }, + py: { ext: "py" }, + rust: { ext: "rs" }, + ts: { ext: "ts" }, +}; + +const createCompilerOutput = ( + files: Map, + configs: GeneratorConfig[], + stdError: string +): CompilerOutput => { + if (configs.length === 0) { + throw new CompilerError("error", "no generators specified", 1); + } + + let warnings: Diagnostic[] = []; + let errors: Diagnostic[] = []; + if (stdError) { + try { + const diagnostics = JSON.parse(stdError) as CompilerOutput; + warnings = diagnostics.warnings; + errors = diagnostics.errors; + } catch (e) { + if (e instanceof Error) { + throw new CompilerError( + "error", + "error while parsing standard error", + 1, + undefined, + e + ); + } + + throw e; + } + } + + const results: GeneratedFile[] = []; + for (const config of configs) { + const [alias, { outFile }] = Object.entries(config)[0]; + const resolvedOutFile = path.resolve("/", outFile); + const extMatch = resolvedOutFile.match(/\.([^.]+)$/); + if (!extMatch) { + throw new CompilerError("error", "unable to determine extension", 1); + } + + const extInfo = extLookupTable[alias]; + if (!extInfo) { + throw new CompilerError( + "error", + "unable to lookup extension", + 1, + undefined, + { alias } + ); + } + + const outFileMatch = [...files.keys()].find((f) => + f.endsWith(resolvedOutFile) + ); + if (!outFileMatch) { + throw new CompilerError( + "error", + "unable to find output file", + 1, + undefined, + { resolvedOutFile } + ); + } + + const generatedFile: GeneratedFile = { + name: outFileMatch, + content: files.get(outFileMatch) ?? "", + generator: alias, + }; + + if (extInfo.auxiliaryExt) { + const outFileDir = path.dirname(resolvedOutFile); + const auxiliaryFileMatch = [...files.keys()].find((f) => { + const fileDir = path.dirname(f); + return f.endsWith(`.${extInfo.auxiliaryExt}`) && fileDir === outFileDir; + }); + + if (auxiliaryFileMatch) { + generatedFile.auxiliaryFile = { + name: auxiliaryFileMatch, + content: files.get(auxiliaryFileMatch) ?? "", + }; + } else { + throw new CompilerError( + "error", + "unable to find auxiliary file", + 1, + undefined, + { resolvedOutFile } + ); + } + } + + results.push(generatedFile); + } + + return { warnings, errors, results }; +}; +function smoothUpdate(element: HTMLDivElement, start: number, end: number) { + const duration = 300; // duration of the animation in milliseconds + const startTime = performance.now(); + + function update() { + const currentTime = performance.now(); + const timeFraction = Math.min((currentTime - startTime) / duration, 1); + const currentProgress = start + (end - start) * timeFraction; + + element.style.width = `${currentProgress}%`; + element.textContent = `${Math.round(currentProgress)}%`; + + if (timeFraction < 1) { + requestAnimationFrame(update); + } + } + + requestAnimationFrame(update); +} + +async function runBebopc( + files: Map, + args: string[] +): Promise { + const worker = getWorker(); + const updateProgress = Comlink.proxy((loaded: number, total: number) => { + const progressElement = document.querySelector( + "#progress-bar" + ) as HTMLDivElement; + const currentPercent = Number.parseFloat(progressElement.style.width) || 0; + const newPercent = (loaded / total) * 100; + + smoothUpdate(progressElement, currentPercent, newPercent); + }); + return worker.runBebopc(files, args, updateProgress); +} + +function mapToGeneratorConfigArray(config?: BebopConfig): GeneratorConfig[] { + if (!config?.generators) { + return []; + } + return Object.entries(config.generators).map(([key, value]) => { + return { [key]: value } as GeneratorConfig; + }); +} +/** + * Tries to find any JSON objects in the stdError output and returns them as an array. + */ +const findErrorEntries = (stdError: string): object[] => { + const validObjects: object[] = []; + let currentObjectString = ""; + + // Split the input string into lines + const lines = stdError.split("\n"); + + for (const line of lines) { + if (line.trim() === "" && currentObjectString.trim() !== "") { + // Try to parse the current object string as JSON + try { + validObjects.push(JSON.parse(currentObjectString)); + } catch { + // Ignore parsing errors + } + // Reset the current object string for the next JSON object + currentObjectString = ""; + } else { + // Add the current line to the current object string + currentObjectString += line; + } + } + + // Check for any remaining JSON object after the last line + if (currentObjectString.trim() !== "") { + try { + validObjects.push(JSON.parse(currentObjectString)); + } catch { + // Ignore parsing errors + } + } + + return validObjects; +}; +const isCompilerOutput = (obj: unknown): obj is CompilerOutput => { + if (!obj || typeof obj !== "object") { + return false; + } + return "warnings" in obj && "errors" in obj; +}; + +const isCompilerException = (obj: unknown): obj is CompilerException => { + if (!obj || typeof obj !== "object") { + return false; + } + return "severity" in obj && "message" in obj && "errorCode" in obj; +}; + +const throwCompilerError = (stdError: string, exitCode: number): void => { + const errorEntries = findErrorEntries(stdError); + if (errorEntries && errorEntries.length > 0 && exitCode >= 400) { + const compilerException = errorEntries.at(-1); + if (isCompilerException(compilerException)) { + throw new CompilerError( + compilerException.severity, + compilerException.message, + compilerException.errorCode, + compilerException.span + ); + } + } + throw new CompilerError("error", stdError, exitCode); +}; + +const parseStandardError = ( + stdError: string, + exitCode: number +): CompilerOutput => { + // we know in our heart of hearts this is a well-formed compiler output + if (exitCode === 0) { + return JSON.parse(stdError); + } + + if (exitCode === 1) { + const errorEntries = findErrorEntries(stdError); + if (errorEntries && errorEntries.length > 1) { + const aggregateErrors: Error[] = []; + for (const error of errorEntries) { + if (isCompilerException(error)) { + aggregateErrors.push( + new CompilerError( + error.severity, + error.message, + error.errorCode, + error.span + ) + ); + } else if (isCompilerOutput(error)) { + aggregateErrors.push( + new DiagnosticError([...error.errors, ...error.warnings]) + ); + } + } + throw new AggregateError(aggregateErrors, "One or more errors occurred"); + } + const error = JSON.parse(stdError); + if (isCompilerException(error)) { + throw new CompilerError( + error.severity, + error.message, + error.errorCode, + error.span + ); + } + return error as CompilerOutput; + } + throw new CompilerError("error", stdError, exitCode); +}; + +export const BebopCompiler = ( + files?: Map, + options?: RootOptions +) => { + const fileMap = files ?? new Map(); + const builder = createCommandBuilder(); + let bebopConfig: BebopConfig | undefined = undefined; + if (options) { + addRootOptions(options, builder); + if (options.config) { + const configContent = fileMap.get(options.config); + if (!configContent) { + throw new CompilerError("error", "bebop.json not found", 1, undefined, { + config: options.config, + }); + } + + bebopConfig = JSON.parse(stripJsonComments(configContent)); + } + } + return { + getHelp: async (): Promise => { + builder.addFlag("--help"); + const response = await runBebopc(fileMap, builder.build()); + if (response.exitCode !== 0) { + throwCompilerError(response.stdErr, response.exitCode); + } + if (!response.stdOut) { + throwCompilerError(response.stdErr, response.exitCode); + } + return response.stdOut.trim(); + }, + getVersion: async (): Promise => { + builder.addFlag("--version"); + const response = await runBebopc(fileMap, builder.build()); + if (response.exitCode !== 0) { + throwCompilerError(response.stdErr, response.exitCode); + } + if (!response.stdOut) { + throwCompilerError(response.stdErr, response.exitCode); + } + return response.stdOut.trim(); + }, + init: async (): Promise => { + builder.addFlag("--init"); + const response = await runBebopc(fileMap, builder.build()); + if (response.exitCode !== 0) { + throwCompilerError(response.stdErr, response.exitCode); + } + if (response.newFiles === undefined) { + throwCompilerError(response.stdErr, response.exitCode); + throw new Error("response.newFiles is undefined"); + } + const emittedFiles = createFileMap(response.newFiles); + for (const [key, value] of emittedFiles.entries()) { + if (key.endsWith("bebop.json")) { + return JSON.parse(value); + } + } + throw new CompilerError("error", "bebop.json not found", 1); + }, + showConfig: async (): Promise => { + builder.addFlag("--show-config"); + const response = await runBebopc(fileMap, builder.build()); + if (response.exitCode !== 0) { + throwCompilerError(response.stdErr, response.exitCode); + } + if (!response.stdOut) { + throwCompilerError(response.stdErr, response.exitCode); + } + return JSON.parse(stripAnsiCodes(response.stdOut)); + }, + listSchemas: async (): Promise => { + builder.addFlag("--list-schemas-only"); + const response = await runBebopc(fileMap, builder.build()); + if (response.exitCode !== 0) { + throwCompilerError(response.stdErr, response.exitCode); + } + if (!response.stdOut) { + return []; + } + return response.stdOut.trim().split(/\r?\n/); + }, + langServer: async (): Promise => { + throw new Error("not implemented"); + }, + watch: async (): Promise => { + throw new Error("not implemented"); + }, + build: async ( + generators?: GeneratorConfig[], + options?: BuildOptions + ): Promise => { + const buildCommand = builder.addSubCommand("build"); + if (generators) { + for (const generator of generators) { + buildCommand.addFlag( + "--generator", + generatorConfigToString(generator) + ); + } + } + if (options) { + if (options.noEmit) { + buildCommand.addFlag("--no-emit"); + } + if (options.noWarn) { + buildCommand.addFlag("--no-warn", options.noWarn.map(String)); + } + if (options.writeToStdOut) { + buildCommand.addFlag("--stdout"); + } + } + const response = await runBebopc(fileMap, buildCommand.build()); + if (response.exitCode !== 0) { + return parseStandardError(response.stdErr, response.exitCode); + } + if (options?.noEmit) { + // if empty likely no errors or warnings + if (!response.stdErr) { + return { warnings: [], errors: [] }; + } + return parseStandardError(response.stdErr, response.exitCode); + } + if (!response.newFiles) { + throwCompilerError(response.stdErr, response.exitCode); + throw new Error("response.newFiles is undefined"); + } + if (response.stdOut) { + printAnsiLogToBrowser(response.stdOut); + } + const emittedFiles = createFileMap(response.newFiles); + return createCompilerOutput( + emittedFiles, + generators ?? mapToGeneratorConfigArray(bebopConfig), + response.stdErr + ); + }, + }; +}; diff --git a/playground/src/bebopc/types.ts b/playground/src/bebopc/types.ts new file mode 100644 index 00000000..5d758811 --- /dev/null +++ b/playground/src/bebopc/types.ts @@ -0,0 +1,184 @@ +export type TempoService = "none" | "client" | "server" | "both"; +export type GeneratorAlias = "cs" | "ts" | "rust" | "py" | "dart" | "cpp"; +export type DiagnosticFormat = "json" | "enhanced" | "structured" | "msbuild"; + +interface GeneratorConfigBody { + /** + * Specify a file that bundles all generated code into one file. + */ + outFile: string; + /** + * By default, bebopc generates a concrete client and a service base class. This property can be used to limit bebopc asset generation. + */ + services?: TempoService; + /** + * Specify if the code generator should produce a notice stating code was auto-generated. + */ + emitNotice?: boolean; + /** + * Specify if the code generator should emit a binary schema in the output file that can be used for dynamic serialization. + */ + emitBinarySchema?: boolean; + /** + * Specify a namespace for the generated code. + */ + namespace?: string; + + /** + * Specify custom options for the code generator. + */ + options?: { + [k: string]: string; + }; +} + +export type GeneratorConfig = { + [K in GeneratorAlias]: { [P in K]: GeneratorConfigBody }; +}[GeneratorAlias]; + +export interface BebopConfig { + /** + * Specifies code generators to use for compilation. + */ + generators?: { + [K in GeneratorAlias]?: GeneratorConfigBody; + }; + /** + * Specifies an array of filenames or patterns to include in the compiler. These filenames are resolved relative to the directory containing the bebop.json file. + */ + include?: string[]; + /** + * Specifies an array of filenames or patterns that should be skipped when resolving include. The 'exclude' property only affects the files included via the 'include' property. + */ + exclude?: string[]; + + /** + * Settings for the watch mode in bebopc. + */ + watchOptions?: { + /** + * Remove a list of files from the watch mode's processing. + */ + excludeFiles?: string[]; + /** + * Remove a list of directories from the watch process. + */ + excludeDirectories?: string[]; + }; + /** + * Specifies an array of warning codes to silence + */ + noWarn?: number[]; + + /** + * Disable emitting files from a compilation. + */ + noEmit?: boolean; + + /** + * Specify extensions to load. + */ + extensions?: { + [k: string]: string; + }; +} + +export interface Span { + fileNames: string; + startLine: number; + endLine: number; + startColumn: number; + endColumn: number; + lines: number; +} + +export interface Diagnostic { + message: string; + errorCode: number; + severity: "error" | "warning"; + span: Span; +} + +export interface AuxiliaryFile { + name: string; + content: string; +} + +export interface GeneratedFile { + name: string; + content: string; + generator: string; + auxiliaryFile?: AuxiliaryFile; +} + +export interface CompilerOutput { + warnings: Diagnostic[]; + errors: Diagnostic[]; + results?: GeneratedFile[]; +} +export type Severity = "error" | "warning"; +export interface CompilerException { + severity: Severity; + message: string; + errorCode: number; + span?: Span; +} +export class AggregateError extends Error { + constructor(public errors: Error[]) { + super("One or more errors occurred"); + } +} +export class DiagnosticError extends Error { + constructor(public diagnostics: Diagnostic[]) { + super("One or more diagnostics occurred"); + } +} + +export class CompilerError extends Error { + constructor( + public severity: Severity, + public message: string, + public errorCode: number, + public span?: Span, + public cause?: Error | Record + ) { + super(message, cause); + } + + static emptyStandardOutput(exitCode: number): CompilerError { + return new CompilerError("error", "Standard output is empty", exitCode); + } +} + +export type Flag = string; +export type FlagValue = string | string[]; +export type SubCommand = "build" | "watch" | "langserver"; + +export interface CommandBuilder { + addFlag: (flag: Flag, value?: FlagValue) => CommandBuilder; + addSubCommand: (subCommand: SubCommand) => CommandBuilder; + build: () => string[]; +} + +export interface BuildOptions { + noEmit?: boolean; + noWarn?: number[]; + readFromStdIn?: boolean; + writeToStdOut?: boolean; +} + +export interface RootOptions { + config?: string; + trace?: boolean; + include?: string[]; + exclude?: string[]; + locale?: string; + diagnosticFormat?: DiagnosticFormat; +} + +export interface WorkerResponse { + exitCode: number; + stdErr: string; + newFiles?: string; + stdOut: string; +} diff --git a/playground/src/bebopc/worker.ts b/playground/src/bebopc/worker.ts new file mode 100644 index 00000000..0deb9d01 --- /dev/null +++ b/playground/src/bebopc/worker.ts @@ -0,0 +1,136 @@ +import { posix as path } from "node:path"; + +import WASI, { createFileSystem } from "wasi-js"; +import browserBindings from "wasi-js/dist/bindings/browser"; +import { WASIExitError, WASIFileSystem } from "wasi-js/dist/types"; + +import bebopcWasmUrl from "./bebopc.wasm?url"; +import { WorkerResponse } from "./types"; + +const decoder = new TextDecoder(); +let bebopcModule: WebAssembly.Module | undefined; + +const getModule = async (callback: (loaded: number, total: number) => void) => { + if (bebopcModule) return bebopcModule; + const response = await fetch(bebopcWasmUrl); + if (!response.ok) { + throw new Error(`Failed to fetch bebopc.wasm: ${response.statusText}`); + } + const contentLength = response.headers.get("content-length"); + if (!contentLength) { + throw new Error("Missing Content-Length header"); + } + const total = Number.parseInt(contentLength, 10); + let loaded = 0; + + const reader = response.body?.getReader(); + const chunks = new Uint8Array(total); + let receivedLength = 0; + + if (reader) { + while (true) { + const { done, value } = await reader.read(); + if (done) break; + + if (value) { + chunks.set(value, receivedLength); + receivedLength += value.length; + loaded += value.length; + callback(loaded, total); + } + } + } + return (bebopcModule = WebAssembly.compile(chunks)); +}; + +export async function runBebopc( + files: Map, + args: string[], + downloadProgressCallback: (loaded: number, total: number) => void +): Promise { + const fs = createFileSystem([ + { + type: "mem", + contents: Object.fromEntries(files), + }, + ]); + + let stdout = ""; + let stderr = ""; + let sab: Int32Array | undefined; + const wasi = new WASI({ + args, + env: { + RUST_BACKTRACE: "1", + BEBOPC_LOG_FORMAT: "JSON", + }, + // Workaround for bug in wasi-js; browser-hrtime incorrectly returns a number. + bindings: { + ...browserBindings, + fs, + hrtime: (...args): bigint => BigInt(browserBindings.hrtime(...args)), + }, + preopens: { + "/": "/", + }, + + sendStdout: (data: Uint8Array): void => { + stdout += decoder.decode(data); + }, + sendStderr: (data: Uint8Array) => { + stderr += decoder.decode(data); + }, + sleep: (ms: number) => { + sab ??= new Int32Array(new SharedArrayBuffer(4)); + Atomics.wait(sab, 0, 0, Math.max(ms, 1)); + }, + }); + const module = await getModule(downloadProgressCallback); + let imports = wasi.getImports(module); + imports = { + wasi_snapshot_preview1: { + ...imports.wasi_snapshot_preview1, + sock_accept: () => -1, + }, + }; + const instance = await WebAssembly.instantiate(module, imports); + let exitCode: number; + try { + wasi.start(instance); + exitCode = 0; + } catch (e) { + if (e instanceof WASIExitError) { + exitCode = e.code ?? 127; + } else { + return (e as any).toString(); + } + } + + let output = ""; + + for (const p of walk(fs, "/")) { + if (files.has(p)) continue; + output += `// @filename: ${p}\n`; + output += fs.readFileSync(p, { encoding: "utf8" }); + output += "\n\n"; + } + + return { + exitCode, + stdErr: stderr, + newFiles: output.trim(), + stdOut: stdout, + }; +} + +function* walk(fs: WASIFileSystem, dir: string): Generator { + for (const p of fs.readdirSync(dir)) { + const entry = path.join(dir, p); + const stat = fs.statSync(entry); + if (stat.isDirectory()) { + yield* walk(fs, entry); + } else if (stat.isFile()) { + yield entry; + } + } +} diff --git a/playground/src/examples.json b/playground/src/examples.json new file mode 100644 index 00000000..d17b2f79 --- /dev/null +++ b/playground/src/examples.json @@ -0,0 +1,26 @@ +{ + "examples": { + "v3": { + "helloworld": { + "name": "Hello World", + "data": "PTAEAEDMEsBsFMB2BDAtvAXKAzgYwBbyrIB0ARgPYAOAUCKAOryy4XqgAuFnhoAQvEpVQVWMgCeAcwBOFAK6IAJiVAAVfNGw5oHeKE2hkoWBQ50wsaAGs90hYmiJJh-oOojZAK3i4O+xNjQinpGAO6CoGSyodjw0iQ05mq8ohIy8kqgxDZaOqDwyNjioJAU0qDi8pzc2MiQzMXwAB5UcdDoiGb0oTr4+sGF-q5CkcXZjs55HNLQAG7QyLDVOPjI0vBJHISxHhTevtgq6noAqgBKADIlZTwGVMiSegbws3HiWxNJ6wCOctDrimWJmQgK2eioXh8flK5WwbHgFEQemYsQSNGw0zkvlAAAlmCZQABvGigUmODgAZgATKAmgBuElkzrUioMgC+iXoAE0qrhkIgcPA9IieLZoJJ8H4waB1tg5LA-BRIKLhtQkqxUFQ4HEsHlHkjpMhdIDVOJWgBlXAzKh+UIaAigPkCuwCxFJKIUGJxbAAGkF0le0i0N354jtcVsBVgsHEaNYAT8GJmTlAoTKsEBAF5QAAiBjpxQ5hlJHlyR38wV6fCe-R+Ig6LQcM3wWr1R0UYI8I0y+CQBAHJJ27sGYIwJGAxwVeSwghEUjJAwGRHC5XS4iIcSbDROLQCEaKCgt5bZJ5+KgUbCBMgIZaxTKKI3Id3wDjhJC7GRoVATYz8yRyB4jx6LZ5D8dZQhmDgf1YYI0XoAA5Gstm7Soy1WV5DAFIIClAJVQGrUIVVSKRZAUQE02kKxfU2ZAbBcEwKCsQwpV4WY1mgeQtGaNBRCPGFSVQMo9EgBQVCSAQcDkdZlnWep1kQXA9GlA9cGwDBOTASUOCoNSQDINwqBIbB8GAFTsGAJIoG1FB0CwEhglmEg2KDDSIBgBAbMwSIDJITw4UQGhiVJUkc0cXBYDkYIcywABtRlguCnMACokuAJLyGoHN4tAABdH1spzfU4iNMpsGiolsoS7leQreNpgoJYYKU1Y-AMIrDWNUZHVWJwfwAcg4bA+sq4L6C4FVFgWYNlX5UxCHKdqSthLE+kKdSEqqsBQAAWkdbARtJegdplOQMQO0Ajt2qhNw20att23AqFoW7Dvu0AH2kMwXtzQbyqC77EtAgAxbVypzRRNA4YAhBIX7zo5BKOQ5IA" + }, + "enum": { + "name": "Enum", + "data": "PTAEAEDMEsBsFMB2BDAtvAXKAzgYwBbyrIB0ARgPYAOAUCKAIKKhICuqoAJvDIvNqGSgALgE8q8EfmTDBuYQOSLQAdwBOyKhLWC1FVok6Dm0RMPgBzeDrES6YACqEuPZK1iyD3NbFGmLIuKS0AKspsIAzABMoAAUFDoALAC0ZKLm2ACUNGwcDvyyAN40oKWgACJq0ABukgC8oACMANw0AL72oA4UOMi1ONAAXpIUzMLOKtBqkqL6oLjIzLijwnqwUpJe1r7+gRIYOYjsoADKxLAIagCiRxxYYWYAHKDFZaAAssgAHqANUQCs-1ab3oDjUfkQAWEPWw8FkQnQqDI1lA+GgFkINmkY2cxB+1WQsFY8BKZXoFEgG1AWx8EKhQVUcHW02w7lkpmMLDUejUBxBwAA8rU1JBYBQVL9QACAGytDqdABCohckDcHgANIJQAr4JQqCxbntggJEBRZKytBRYUZoaBplQWUhhJ1FsqaTtIaACUT+KAAPr6BTQbh+0AUqmjX2wELmTgkToASUps1YqkW8NAqASkgAwslowBreCasjQYSi5AWbD56BF0DI6TVaAJTXIThGZCdKCwSsCbjLDTQnTIyDZqm5A7d3uHY4ABWsqBC2GbiAE93Cz1eZQASvA25KAAxfA8tUmlADqVXMh+PUWBZRzFFQ6DMN4PiTlnXKPFM8BtaIEFRkFEdVOgAA3nNRF2wZdRmwMCvUJYkBELSQwIPBDYlNUBtGg2DV0yUAXUMUAwIiTDd33AAfUBLzLeBCOQaZEOjG0KAoeNOhOCRkALXZw3QsDFV1ahM1431xk2I5WUJeZ8AoaBcBGSlkX4qhTBXGQ-yI+hkEoVhZBUaRZEk0iMMzIhkR0cNFgNY4QnmQkEDjGcOAAVVhNQHAZLdSgYThF2YBoD2aUp6FYfBkgofAzw+Vhl1waBbIaU83gAGRjJAUT+T96CgOAkDQTBQBIbhqhIAk1GwGguxgBAUHQLBkT1EgACtsFGGhfNAAAiUxcCJbgeqwABtWK3h6gAqSbgEm8hqB62KAF1QImqw+EHBJsGGl5xrKHqFB27q3gmwMADECp2nrOBjYAWsOvbSg6N4Og6IA" + }, + "const": { + "name": "Constants", + "data": "PTAEAEDMEsBsFMB2BDAtvAXKAzgYwBbyrIB0ARgPYAOAUCKAELyVU4CuVVFATgC7ahcFVFTjxQvaOkEVE2XskT8ANHTDY2BUMgHzu0RAHNsGGkLm8cvfUdABZAJoB9AMoAVAEoBJAHIBxUABeUAAiAAl4WFgKUAB1HlgAEwBCEIBuNVAfNlQyeG4BRBy8gtNzeVA2A14ARgA2ewBBAA0nAFUXAFEPFyDQACYABgAWAA4M+koKBEUTM1kKqdhQABFOhja-JzsAeTW+6zZ4CbBGxETQeAA3JFBUHnEhEQQADwkATyp4AVhoAGtxIYqokygtLEDoBcugBhDydNx9EIATgAzPBIABWSDDADsAFoMcNIHU8bjEki8cgMai8bhEsgyDUcZjRsNcGR0jRMl5LNABMhYNgYlxsNhoGQEBIYol0QZxMgrDZDDILIpLJBuMJMopLogrtBNYh0EpQFdkPoGZK2GLbBoyPJoLw2JJZPMLIqDMrOj4AGp9AAkAG9vT6AL4nUA7bigd4UNiCHVkKpJO4PGTPeBvPSe7BuirZ2wbLwAGRWTl8ADEdojOnqsEGQ6HQKdnfgePXA402m4wjsPE2wNCdnY7F43B2hyOx6HOfQoGIUOgsCQZVcSGaCjQAMSrNhK0CryLUY2WDfQS3fBOIUB5bSi6CGRDwC4GCT4PmgGAIGghwJUTWJJoLqIDQXY9n2gR5LwADu0AvLwNCTqObiBCiGJkHUkD9DimTzggi6YDezDUCQABWQogYGNCgDRoQGLgsBsDKIRYAA2tRtGcSEABU3HANx5DUCEHE0QAuqoXGGEg+TILwPDYCxoBUZxXH8IpykqZpIRxrwFZiIpISJHyvDACwJBqSJKmhpZ1nWUAA" + }, + "import": { + "name": "Imports", + "data": "PTAEAEDMEsBsFMB2BDAtvAXKAzgYwBbyrIB0ARgPYAOAUCKAII4FHKjECeo0iusArgBN4oZLFigALoVDCYiaJOgVE2UBUjrp8AE7NCxbABpQdMGS6xo2JYgDm3VFQo7JOScklEkktZ6kyktTqmtqgMAgkNNBOLm4ARPAAHmhUkZRU8WagAJoU-KC4yIgBSKA68JC6SLgixVxyPIrKquE6FKiljs6u8IL6rDQ2Ovy4bgBK8ACO-PA2oADeNKArAKIpTgigyakIANw0AL7ZUHBIaJigJMIAbiQ3yDrYNCcR5+hYZPAZJABW2CoaEsVit4jw+EJ4PEsABtZYghHxPAGUgZLIIgC6RnhoLsSF0nhc2GhixxCNA8V8JOB5Np8XykgAYmcSfFBNZJMAflSyQjjnyjjQgA" + }, + "struct": { + "name": "Structs", + "data": "PTAEAEDMEsBsFMB2BDAtvAXKAzgYwBbyrIB0ARgPYAOAUCKAII4AuATgK67OgAm8MieNlDJEIgObjW8ccmbQKYipFAAiGPFg9sqgDSg6YXIubJoic+NDMAnlXg9QAN2Sx2Q0OZGgYADwegFKx8rCQG9AywsM6u7sLI0iKwAO7INsJU0thIzGEAktzQwuzZjqic+KCw0ADW8N7YbJyFYgDCJDSNHFygAErwXKLiCKAA3jSgk56IzADMAEygvgDcE1Pmc4s2q1PTm6DJ0DzM+DvrMwughNDi+MyrAL40hqAAKoSgZOYJNqDSmUIcnIFEoVMgWN1CsJsNBUFRYL8TnJAmCoj5oJpHC43B4vEEQmEXu8iqB0KJhNBmAByYSoIL1Yxw5A9USOfgwXAYmbWfCiUlCbDIcTwQkvADqhDENgo7F4-HM9XBXWa+ml7CpiUyFFQRUs1gooEETngrBEPEcQT+RAoxvRmOEkFY2s8uSJvO4ZMQ2H00BUatNyp6ggc8UN8GSdq0qplOHwMq0coEirDEeNrBhigwnSaPX6g0Qw3gTkW412G0uKzWk3LWzO1Yui0Ox1OVb2l2ut3urcoFGiRVa0FYuAQj2e9AAQr8+JBkOxYMx9Eqc1DPKhyqYyAgwgxuBwZrD6pSaZHtKBcKJEBRuGR6nSeL6MTwXshIMwTXLjPeC2FXgbiHUGmXUl2A3EY1VAAE-BdA5KUqE56gAA3XBDSQoe8NFYLN1whZo+gGUwCwQJxZjGVsayWOs21rMiGxg5tKPIjs7lHF4ADkKAjYVBFYOQAk-epDjRVxYA4nxLT3eR0FQ9DoHPeRFGEZQeXqQNmGEbE4g6egoDgJA0EwUASD4JwSBcdMxzAHSEBQdAsBvSgqBIAArbBFBoUsplUcxh3YPhVCwABtVtdlUAAqULgFC8hqFUVsAF1dFbVQuJNOQgh0LAPN2SZVDU-zSOywq1BlZgADFdPy1R70aYAHJIPLgqmJ5dieJ4gA" + } + } + } +} diff --git a/playground/src/index.ts b/playground/src/index.ts new file mode 100644 index 00000000..ef77bc95 --- /dev/null +++ b/playground/src/index.ts @@ -0,0 +1,565 @@ +import * as lzString from "lz-string"; +import editorWorker from "monaco-editor/esm/vs/editor/editor.worker?worker"; +import jsonWorker from "monaco-editor/esm/vs/language/json/json.worker?worker"; +import jsWorker from "monaco-editor/esm/vs/language/typescript/ts.worker?worker"; + +import { BebopCompiler, createFileMap } from "./bebopc"; +import { CompilerError, Diagnostic, DiagnosticError } from "./bebopc/types"; +import exampleStore from "./examples.json" with { type: "json" }; +import { monaco } from "./internal/customMonaco.ts"; + +const configName = "bebop.json"; +const envName = ".dev.vars"; +const schemaName = "schema.bop"; +const v3Prefix = "#v3="; + +type EditorFile = { + name: string; + content: string; +}; + +const configureMonaco = () => { + monaco.languages.register({ + id: "bebop", + extensions: [".bop"], + aliases: ["Bebop"], + }); + + monaco.languages.setMonarchTokensProvider("bebop", { + keywords: [ + "import", + "const", + "message", + "union", + "struct", + "enum", + "true", + "false", + "inf", + "-inf", + "nan", + "readonly", + "mut", + "service", + "stream", + "deprecated", + "flags", + "opcode", + "true", + "false", + ], + + typeKeywords: [ + "bool", + "byte", + "uint8", + "uint16", + "int16", + "uint32", + "int32", + "uint64", + "int64", + "float32", + "float64", + "string", + "guid", + "date" + ], + + operators: ["=", "->"], + + // we include these common regular expressions + symbols: /[!%&*+/:<=>?^|~-]+/, + + // C# style strings + escapes: + /\\(?:["'\\abfnrtv]|x[\dA-Fa-f]{1,4}|u[\dA-Fa-f]{4}|U[\dA-Fa-f]{8})/, + + // The main tokenizer for our languages + tokenizer: { + root: [ + // identifiers and keywords + [ + /[$_a-z][\w$]*/, + { + cases: { + "@typeKeywords": "keyword", + "@keywords": "keyword", + "@default": "identifier", + }, + }, + ], + [/[A-Z][\w$]*/, "type.identifier"], // to show class names nicely + + // whitespace + { include: "@whitespace" }, + + // delimiters and operators + [/[()[\]{}]/, "@brackets"], + [/[<>](?!@symbols)/, "@brackets"], + [/@symbols/, { cases: { "@operators": "operator", "@default": "" } }], + + // @ annotations. + [ + /@\s*[$A-Z_a-z][\w$]*/, + { token: "annotation" }, + ], + + // numbers + [/\d*\.\d+([Ee][+-]?\d+)?/, "number.float"], + [/0[Xx][\dA-Fa-f]+/, "number.hex"], + [/\d+/, "number"], + + // delimiter: after number because of .\d floats + [/[,.;]/, "delimiter"], + + // strings + [/"([^"\\]|\\.)*$/, "string.invalid"], // non-teminated string + [/"/, { token: "string.quote", bracket: "@open", next: "@string" }], + + // characters + [/'[^'\\]'/, "string"], + [/(')(@escapes)(')/, ["string", "string.escape", "string"]], + [/'/, "string.invalid"], + ], + + comment: [ + [/[^*/]+/, "comment"], + [/\/\*/, "comment", "@push"], // nested comment + ["\\*/", "comment", "@pop"], + [/[*/]/, "comment"], + ], + + string: [ + [/[^"\\]+/, "string"], + [/'+/, "string"], + [/@escapes/, "string.escape"], + [/\\./, "string.escape.invalid"], + [/"/, { token: "string.quote", bracket: "@close", next: "@pop" }], + ], + + whitespace: [ + [/[\t\n\r ]+/, "white"], + [/\/\*/, "comment", "@comment"], + [/\/\/.*$/, "comment"], + ], + }, + }); + + monaco.languages.json.jsonDefaults.setDiagnosticsOptions({ + validate: true, + allowComments: true, + trailingCommas: "ignore", + enableSchemaRequest: true, + schemas: [ + { + uri: `${document.location.protocol}//${document.location.host}/assets/bebop-schema.json`, + fileMatch: ["bebop.json"], + }, + ], + }); +}; + +const createEditor = ( + container: string, + language: string, + label: string, + content: string = "" +) => { + const editorContainer = document.querySelector( + `#${container}` + ) as HTMLElement; + if (editorContainer === null) { + throw new Error(`Could not find editor container ${container}`); + } + const editorElement = editorContainer.querySelector(`.editor`) as HTMLElement; + + if (editorElement === null) { + throw new Error(`Could not find editor element within ${container}`); + } + + const labelElement = editorContainer.querySelector( + `.editor-label` + ) as HTMLElement; + if (labelElement === null) { + throw new Error(`Could not find editor label within ${container}`); + } + + //labelElement.textContent = label; + + const model = monaco.editor.createModel( + content, + language, + monaco.Uri.parse(`file:///${label}`) + ); + + const editor = monaco.editor.create(editorElement, { + model, + minimap: { + enabled: false, + }, + readOnly: label === "preview", + domReadOnly: label === "preview", + //readOnly: false, + theme: "vs-dark", + formatOnPaste: true, + formatOnType: true, + }); + + return { + editor, + updateLanguage: (language: string) => { + const model = editor.getModel(); + if (model) { + monaco.editor.setModelLanguage(model, language); + } + }, + setMarkers: (markers: monaco.editor.IMarkerData[]) => { + const model = editor.getModel(); + if (model) { + monaco.editor.setModelMarkers(model, language, markers); + } + }, + disableErrors: () => { + const model = editor.getModel(); + if (model) { + monaco.editor.setModelMarkers(model, "error", []); + } + }, + clearMarkers: () => { + monaco.editor.removeAllMarkers(language); + }, + setValue: (content: string) => { + editor.setValue(content.trim()); + editor.setScrollPosition({scrollTop: 0}); + }, + getValue: () => { + return editor.getValue().trim(); + }, + hasErrors: () => { + const model = editor.getModel(); + if (model) { + const markers = monaco.editor.getModelMarkers({ + resource: model.uri, + }); + return markers.some((f) => f.severity === monaco.MarkerSeverity.Error); + } + return false; + }, + hasWarnings: () => { + const model = editor.getModel(); + if (model) { + const markers = monaco.editor.getModelMarkers({ + resource: model.uri, + }); + return markers.some( + (f) => f.severity === monaco.MarkerSeverity.Warning + ); + } + return false; + }, + setLabel: (label: string) => { + labelElement.textContent = label; + }, + getFile: (): EditorFile => { + const model = editor.getModel(); + if (model) { + return { + name: model.uri.fsPath.slice(1), + content: model.getValue().trim(), + }; + } + throw new Error(`Could not find model`); + }, + }; +}; + +const importSchema = ` +/** This record was imported by another schema */ +struct Example { + string data; + guid id; + date createdAt; +} +const string greetings = "I am a const from an imported schema"; +` + +const createBebopCompiler = (files?: EditorFile[]) => { + let initialContent = ""; + if (files !== undefined) { + for (const file of files) { + initialContent += `// @filename: ${file.name}`; + initialContent += "\n"; + initialContent += file.content; + initialContent += "\n"; + if (file.name === schemaName && file.content.includes("example.bop")) { + initialContent += "// @filename: example.bop"; + initialContent += "\n"; + initialContent += importSchema; + initialContent += "\n"; + } + } + const fileMap = createFileMap(initialContent); + return BebopCompiler(fileMap, { + config: "/bebop.json", + trace: true, + }); + } + + return BebopCompiler(); +}; + +export const populateExamples = () => { + for (const [k, v] of Object.entries(exampleStore.examples.v3)) { + const examples = document.querySelector( + "body > header > nav > ul > li.relative.group > div" + ); + if (examples === null) { + throw new Error(`Could not find examples element`); + } + const warningText = `'${v.name}' example will be loaded to the playground. Your current changes will be lost.`; + const exampleElement = document.createElement("a"); + exampleElement.dataset.modalText = warningText; + exampleElement.className = "confirm-link block px-4 py-1 hover:bg-gray-600"; + exampleElement.href = `${v3Prefix}${v.data}`; + exampleElement.textContent = v.name; + examples.append(exampleElement); + } +}; + +export const init = async () => { + + self.MonacoEnvironment = { + getWorker: function (_, label) { + switch (label) { + case "json": + return new jsonWorker(); + case "typescript": + case "javascript": + return new jsWorker(); + default: + return new editorWorker(); + } + }, + }; + configureMonaco(); + const playground = { + schema: createEditor("schema-container", "bebop", schemaName), + env: createEditor("vars-container", "ini", envName), + config: createEditor("config-container", "json", configName), + preview: createEditor("preview-container", "typescript", "preview"), + layout: () => { + playground.schema.editor.layout(); + playground.env.editor.layout(); + playground.config.editor.layout(); + playground.preview.editor.layout(); + }, + getFiles: (): EditorFile[] => { + return [ + playground.schema.getFile(), + playground.env.getFile(), + playground.config.getFile(), + ]; + }, + saveState: () => { + let files = ""; + for (const file of playground.getFiles()) { + files += `// @filename: ${file.name}`; + files += "\n"; + files += file.content.trim(); + files += "\n"; + } + + const value = v3Prefix + lzString.compressToEncodedURIComponent(files); + const valueWithHash = value.startsWith("#") ? value : `#${value}`; + window.location.hash = valueWithHash; + }, + loadState: (state?: string) => { + if (state === undefined) { + state = window.location.hash; + } + + if (!state.startsWith(v3Prefix) || window.location.hash === "") { + state = `${v3Prefix}${exampleStore.examples.v3.helloworld.data}`; + } + + state = state.slice(v3Prefix.length); + try { + const decompressedState = + lzString.decompressFromEncodedURIComponent(state); + const parsed = createFileMap(decompressedState); + let schemaContent = ""; + let envContent = ""; + let configContent = ""; + for (const [file, content] of parsed.entries()) { + if (typeof file === "string" && typeof content === "string") { + switch (file.slice(1)) { + case schemaName: + schemaContent = content; + break; + case envName: + envContent = content; + break; + case configName: + configContent = content; + break; + } + } + } + // avoid triggering change events until all files are loaded + playground.env.setValue(envContent); + playground.config.setValue(configContent); + playground.schema.setValue(schemaContent); + + } catch (e) { + // eslint-disable-next-line no-restricted-globals + console.error(e); + playground.loadState( + `${v3Prefix}${exampleStore.examples.v3.helloworld.data}` + ); + } + }, + updatePreview: async (content: string, languageId: string) => { + playground.preview.updateLanguage(languageId); + playground.preview.editor.updateOptions({ + readOnly: false, + }); + playground.preview.setValue(content); + playground.preview.clearMarkers(); + await playground.preview.editor + .getAction("editor.action.formatDocument") + ?.run(); + playground.preview.editor.updateOptions({ + readOnly: true, + }); + playground.preview.editor.setScrollPosition({scrollTop: 0}); + }, + }; + playground.loadState(); + + const versionLabel = document.querySelector("body > footer"); + if (versionLabel) { + versionLabel.textContent = `Version ${await createBebopCompiler().getVersion()}`; + } + const setMarkers = async (diagnostics: Diagnostic[]) => { + playground.schema.clearMarkers(); + const markers = diagonsticsToMarkers(diagnostics); + if (markers.length > 0) { + playground.schema.setMarkers(markers); + } + }; + const tryBuild = async () => { + if (playground.config.hasErrors() || playground.config.hasWarnings()) { + return; + } + const compiler = createBebopCompiler(playground.getFiles()); + try { + const output = await compiler.build(); + await setMarkers([...output.warnings, ...output.errors]); + if (playground.schema.hasErrors()) { + await playground.updatePreview("", "text"); + return; + } + if (output.results !== null && output.results !== undefined) { + for (const result of output.results) { + switch (result.generator) { + case "ts": + await playground.updatePreview(result.content, "typescript"); + break; + case "py": + await playground.updatePreview(result.content, "python"); + break; + case "rust": + await playground.updatePreview(result.content, "rust"); + break; + case "cs": + await playground.updatePreview(result.content, "csharp"); + break; + case "dart": + await playground.updatePreview(result.content, "dart"); + break; + case "cpp": + await playground.updatePreview(result.content, "cpp"); + break; + } + } + } + } catch (e) { + if (e instanceof AggregateError) { + for (const error of e.errors) { + if (error instanceof DiagnosticError) { + await setMarkers(error.diagnostics); + } else if (error instanceof CompilerError) { + await playground.updatePreview(error.message, "text"); + } + } + } else if (e instanceof CompilerError) { + await playground.updatePreview(e.message, "text"); + } else if (e instanceof Error) { + await playground.updatePreview(e.toString(), "text"); + } + } + }; + playground.config.editor.onDidChangeModelContent(() => { + playground.saveState(); + }); + playground.env.editor.onDidChangeModelContent(() => { + playground.saveState(); + }); + playground.schema.editor.onDidChangeModelContent(async () => { + playground.saveState(); + await tryBuild(); + }); + + //populateLanguages(playground); + await tryBuild(); + + const overlay = document.querySelector("#main-overlay"); + if (overlay) { + overlay.classList.add("hidden"); + } + + window.addEventListener("resize", () => { + playground.layout(); + }); + populateExamples(); + + return playground; +}; + +function diagonsticsToMarkers(diagnostics: Diagnostic[]) { + const markers = []; + for (const diagnostic of diagnostics) { + const range = new monaco.Range( + diagnostic.span.startLine + 1, + diagnostic.span.startColumn + 1, + diagnostic.span.endLine + 1, + diagnostic.span.endColumn + 1 + ); + markers.push({ + severity: + diagnostic.severity === "error" + ? monaco.MarkerSeverity.Error + : monaco.MarkerSeverity.Warning, + message: diagnostic.message, + startLineNumber: range.startLineNumber, + startColumn: range.startColumn, + endLineNumber: range.endLineNumber, + endColumn: range.endColumn, + }); + } + return markers; +} + +export const debounce = ) => ReturnType>( + fn: F, + delay: number +) => { + let timeout: ReturnType; + return function (...args: Parameters) { + clearTimeout(timeout); + timeout = setTimeout(() => { + fn.apply(args); + }, delay); + }; +}; diff --git a/playground/src/internal/customMonaco.ts b/playground/src/internal/customMonaco.ts new file mode 100644 index 00000000..666e0266 --- /dev/null +++ b/playground/src/internal/customMonaco.ts @@ -0,0 +1,15 @@ +import "monaco-editor/esm/vs/editor/editor.all.js"; +import "monaco-editor/esm/vs/basic-languages/ini/ini.contribution"; +import "monaco-editor/esm/vs/language/json/monaco.contribution"; +import "monaco-editor/esm/vs/language/typescript/monaco.contribution"; +import "monaco-editor/esm/vs/basic-languages/rust/rust.contribution"; +import "monaco-editor/esm/vs/basic-languages/csharp/csharp.contribution"; +import "monaco-editor/esm/vs/basic-languages/python/python.contribution"; +import "monaco-editor/esm/vs/basic-languages/dart/dart.contribution"; +import "monaco-editor/esm/vs/basic-languages/cpp/cpp.contribution"; +import "monaco-editor/esm/vs/basic-languages/typescript/typescript.contribution"; + +import * as monaco from "monaco-editor/esm/vs/editor/editor.api"; + +// eslint-disable-next-line unicorn/prefer-export-from +export { monaco }; diff --git a/playground/src/internal/helpers.ts b/playground/src/internal/helpers.ts new file mode 100644 index 00000000..0b7ddfdc --- /dev/null +++ b/playground/src/internal/helpers.ts @@ -0,0 +1,128 @@ +export function memoize(fn: () => T): () => T { + let value: T | undefined; + return () => (value ??= fn()); +} + +const singleComment = Symbol("singleComment"); +const multiComment = Symbol("multiComment"); + +const stripWithoutWhitespace = (): string => ""; +const stripWithWhitespace = ( + string: string, + start: number, + end: number +): string => string.slice(start, end).replace(/\S/g, " "); + +const isEscaped = (jsonString: string, quotePosition: number): boolean => { + let index = quotePosition - 1; + let backslashCount = 0; + + while (jsonString[index] === "\\") { + index -= 1; + backslashCount += 1; + } + + return Boolean(backslashCount % 2); +}; + +export function stripJsonComments( + jsonString: string, + { whitespace = true, trailingCommas = false } = {} +): string { + if (typeof jsonString !== "string") { + throw new TypeError( + `Expected argument \`jsonString\` to be a \`string\`, got \`${typeof jsonString}\`` + ); + } + + const strip = whitespace ? stripWithWhitespace : stripWithoutWhitespace; + + let isInsideString = false; + let isInsideComment: symbol | false = false; + let offset = 0; + let buffer = ""; + let result = ""; + let commaIndex = -1; + + for (let index = 0; index < jsonString.length; index++) { + const currentCharacter = jsonString[index]; + const nextCharacter = jsonString[index + 1]; + + if (!isInsideComment && currentCharacter === '"') { + const escaped = isEscaped(jsonString, index); + if (!escaped) { + isInsideString = !isInsideString; + } + } + + if (isInsideString) { + continue; + } + + if (!isInsideComment && currentCharacter + nextCharacter === "//") { + buffer += jsonString.slice(offset, index); + offset = index; + isInsideComment = singleComment; + index++; + } else if ( + isInsideComment === singleComment && + currentCharacter + nextCharacter === "\r\n" + ) { + index++; + isInsideComment = false; + buffer += strip(jsonString, offset, index); + offset = index; + continue; + } else if (isInsideComment === singleComment && currentCharacter === "\n") { + isInsideComment = false; + buffer += strip(jsonString, offset, index); + offset = index; + } else if (!isInsideComment && currentCharacter + nextCharacter === "/*") { + buffer += jsonString.slice(offset, index); + offset = index; + isInsideComment = multiComment; + index++; + continue; + } else if ( + isInsideComment === multiComment && + currentCharacter + nextCharacter === "*/" + ) { + index++; + isInsideComment = false; + buffer += strip(jsonString, offset, index + 1); + offset = index + 1; + continue; + } else if (trailingCommas && !isInsideComment) { + if (commaIndex !== -1) { + if (currentCharacter === "}" || currentCharacter === "]") { + buffer += jsonString.slice(offset, index); + result += strip(buffer, 0, 1) + buffer.slice(1); + buffer = ""; + offset = index; + commaIndex = -1; + } else if ( + currentCharacter !== " " && + currentCharacter !== "\t" && + currentCharacter !== "\r" && + currentCharacter !== "\n" + ) { + buffer += jsonString.slice(offset, index); + offset = index; + commaIndex = -1; + } + } else if (currentCharacter === ",") { + result += buffer + jsonString.slice(offset, index); + buffer = ""; + offset = index; + commaIndex = index; + } + } + } + return ( + result + + buffer + + (isInsideComment + ? strip(jsonString, offset, jsonString.length) + : jsonString.slice(offset)) + ); +} diff --git a/playground/src/vite-env.d.ts b/playground/src/vite-env.d.ts new file mode 100644 index 00000000..b9804307 --- /dev/null +++ b/playground/src/vite-env.d.ts @@ -0,0 +1,2 @@ +/// +/// \ No newline at end of file diff --git a/playground/tailwind.config.js b/playground/tailwind.config.js new file mode 100644 index 00000000..614c86b4 --- /dev/null +++ b/playground/tailwind.config.js @@ -0,0 +1,8 @@ +/** @type {import('tailwindcss').Config} */ +export default { + content: ["./index.html", "./src/**/*.{js,ts,jsx,tsx}"], + theme: { + extend: {}, + }, + plugins: [], +}; diff --git a/playground/tsconfig.json b/playground/tsconfig.json new file mode 100644 index 00000000..73b9435e --- /dev/null +++ b/playground/tsconfig.json @@ -0,0 +1,34 @@ +{ + "compilerOptions": { + "target": "ESNext", + "useDefineForClassFields": true, + "module": "ESNext", + "lib": [ + "ESNext", + "DOM", + "DOM.Iterable", + "WebWorker", + "WebWorker.ImportScripts", + "Webworker.Iterable" + ], + "skipLibCheck": true, + + /* Bundler mode */ + "moduleResolution": "bundler", + "allowImportingTsExtensions": true, + "allowSyntheticDefaultImports": true, + "resolveJsonModule": true, + "isolatedModules": true, + + "noEmit": true, + + /* Linting */ + "strict": true, + "alwaysStrict": true, + "noUnusedLocals": true, + "noUnusedParameters": true, + "noFallthroughCasesInSwitch": true, + "forceConsistentCasingInFileNames": true + }, + "include": ["src"] +} diff --git a/playground/vite.config.ts b/playground/vite.config.ts new file mode 100644 index 00000000..02a1677e --- /dev/null +++ b/playground/vite.config.ts @@ -0,0 +1,70 @@ +/// +import { defineConfig, splitVendorChunkPlugin } from "vite"; +import { comlink } from "vite-plugin-comlink"; +import Inspect from "vite-plugin-inspect"; +import { nodePolyfills } from "vite-plugin-node-polyfills"; +import { viteStaticCopy } from "vite-plugin-static-copy"; +import { resolve } from "path"; +import monacoEditorPlugin from "vite-plugin-monaco-editor"; +import { createHtmlPlugin } from "vite-plugin-html"; +import { terser } from "rollup-plugin-terser"; + +// https://vitejs.dev/config/ +export default defineConfig({ + test: { + include: ["src/**/*.{test,spec}.{js,ts}"], + }, + base: "./", + plugins: [ + monacoEditorPlugin.default({ + languageWorkers: ["json", "typescript"], + }), + Inspect(), + comlink(), + splitVendorChunkPlugin(), + nodePolyfills(), + createHtmlPlugin({ + minify: true, + }), + viteStaticCopy({ + targets: [ + { + src: "../vscode-bebop/schemas/bebop-schema.json", + dest: "./assets/", + }, + /* { + src: "node_modules/coi-serviceworker/coi-serviceworker.min.js", + dest: ".", + },*/ + /*{ + src: "./bin/bebopc.wasm", + dest: ".", + }, + */ + ], + }), + ], + worker: { + plugins: () => [comlink()], + }, + build: { + minify: "terser", + sourcemap: false, + chunkSizeWarningLimit: Infinity, + target: "ES2022", + rollupOptions: { + plugins: [ + terser({ + format: { + comments: false, + }, + + mangle: { + keep_classnames: false, + reserved: [], + }, + }), + ], + }, + }, +}); diff --git a/playground/yarn.lock b/playground/yarn.lock new file mode 100644 index 00000000..d05bc4f1 --- /dev/null +++ b/playground/yarn.lock @@ -0,0 +1,4333 @@ +# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. +# yarn lockfile v1 + + +"@aashutoshrathi/word-wrap@^1.2.3": + version "1.2.6" + resolved "https://registry.yarnpkg.com/@aashutoshrathi/word-wrap/-/word-wrap-1.2.6.tgz#bd9154aec9983f77b3a034ecaa015c2e4201f6cf" + integrity sha512-1Yjs2SvM8TflER/OD3cOjhWWOZb58A2t7wpE2S9XfBYTiIl+XFhQG2bjy4Pu1I+EAlCNUzRDYDdFwFYUKvXcIA== + +"@alloc/quick-lru@^5.2.0": + version "5.2.0" + resolved "https://registry.yarnpkg.com/@alloc/quick-lru/-/quick-lru-5.2.0.tgz#7bf68b20c0a350f936915fcae06f58e32007ce30" + integrity sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw== + +"@antfu/utils@^0.7.7": + version "0.7.7" + resolved "https://registry.yarnpkg.com/@antfu/utils/-/utils-0.7.7.tgz#26ea493a831b4f3a85475e7157be02fb4eab51fb" + integrity sha512-gFPqTG7otEJ8uP6wrhDv6mqwGWYZKNvAcCq6u9hOj0c+IKCEsY4L1oC9trPq2SaWIzAfHvqfBDxF591JkMf+kg== + +"@babel/code-frame@^7.0.0", "@babel/code-frame@^7.10.4": + version "7.23.5" + resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.23.5.tgz#9009b69a8c602293476ad598ff53e4562e15c244" + integrity sha512-CgH3s1a96LipHCmSUmYFPwY7MNx8C3avkq7i4Wl3cfa662ldtUe4VM1TPXX70pfmrlWTb6jLqTYrZyT2ZTJBgA== + dependencies: + "@babel/highlight" "^7.23.4" + chalk "^2.4.2" + +"@babel/helper-validator-identifier@^7.22.20": + version "7.22.20" + resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.20.tgz#c4ae002c61d2879e724581d96665583dbc1dc0e0" + integrity sha512-Y4OZ+ytlatR8AI+8KZfKuL5urKp7qey08ha31L8b3BwewJAoJamTzyvxPR/5D+KkdJCGPq/+8TukHBlY10FX9A== + +"@babel/highlight@^7.23.4": + version "7.23.4" + resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.23.4.tgz#edaadf4d8232e1a961432db785091207ead0621b" + integrity sha512-acGdbYSfp2WheJoJm/EBBBLh/ID8KDc64ISZ9DYtBmC8/Q204PZJLHyzeB5qMzJ5trcOkybd78M4x2KWsUq++A== + dependencies: + "@babel/helper-validator-identifier" "^7.22.20" + chalk "^2.4.2" + js-tokens "^4.0.0" + +"@cowasm/memfs@^3.5.1": + version "3.5.1" + resolved "https://registry.yarnpkg.com/@cowasm/memfs/-/memfs-3.5.1.tgz#8846077117ff300fe3edc41e8657184c01437c62" + integrity sha512-TSz00K+BdLxAYFQvwHmKgM/eAK6h9OYSQhPTcv/9XSyOF0Q90EtMkJvtwirwAXunfTsZw8R9YMu1UU213ooVKw== + dependencies: + fs-monkey "^1.0.3" + +"@esbuild/aix-ppc64@0.19.12": + version "0.19.12" + resolved "https://registry.yarnpkg.com/@esbuild/aix-ppc64/-/aix-ppc64-0.19.12.tgz#d1bc06aedb6936b3b6d313bf809a5a40387d2b7f" + integrity sha512-bmoCYyWdEL3wDQIVbcyzRyeKLgk2WtWLTWz1ZIAZF/EGbNOwSA6ew3PftJ1PqMiOOGu0OyFMzG53L0zqIpPeNA== + +"@esbuild/android-arm64@0.19.12": + version "0.19.12" + resolved "https://registry.yarnpkg.com/@esbuild/android-arm64/-/android-arm64-0.19.12.tgz#7ad65a36cfdb7e0d429c353e00f680d737c2aed4" + integrity sha512-P0UVNGIienjZv3f5zq0DP3Nt2IE/3plFzuaS96vihvD0Hd6H/q4WXUGpCxD/E8YrSXfNyRPbpTq+T8ZQioSuPA== + +"@esbuild/android-arm@0.19.12": + version "0.19.12" + resolved "https://registry.yarnpkg.com/@esbuild/android-arm/-/android-arm-0.19.12.tgz#b0c26536f37776162ca8bde25e42040c203f2824" + integrity sha512-qg/Lj1mu3CdQlDEEiWrlC4eaPZ1KztwGJ9B6J+/6G+/4ewxJg7gqj8eVYWvao1bXrqGiW2rsBZFSX3q2lcW05w== + +"@esbuild/android-x64@0.19.12": + version "0.19.12" + resolved "https://registry.yarnpkg.com/@esbuild/android-x64/-/android-x64-0.19.12.tgz#cb13e2211282012194d89bf3bfe7721273473b3d" + integrity sha512-3k7ZoUW6Q6YqhdhIaq/WZ7HwBpnFBlW905Fa4s4qWJyiNOgT1dOqDiVAQFwBH7gBRZr17gLrlFCRzF6jFh7Kew== + +"@esbuild/darwin-arm64@0.19.12": + version "0.19.12" + resolved "https://registry.yarnpkg.com/@esbuild/darwin-arm64/-/darwin-arm64-0.19.12.tgz#cbee41e988020d4b516e9d9e44dd29200996275e" + integrity sha512-B6IeSgZgtEzGC42jsI+YYu9Z3HKRxp8ZT3cqhvliEHovq8HSX2YX8lNocDn79gCKJXOSaEot9MVYky7AKjCs8g== + +"@esbuild/darwin-x64@0.19.12": + version "0.19.12" + resolved "https://registry.yarnpkg.com/@esbuild/darwin-x64/-/darwin-x64-0.19.12.tgz#e37d9633246d52aecf491ee916ece709f9d5f4cd" + integrity sha512-hKoVkKzFiToTgn+41qGhsUJXFlIjxI/jSYeZf3ugemDYZldIXIxhvwN6erJGlX4t5h417iFuheZ7l+YVn05N3A== + +"@esbuild/freebsd-arm64@0.19.12": + version "0.19.12" + resolved "https://registry.yarnpkg.com/@esbuild/freebsd-arm64/-/freebsd-arm64-0.19.12.tgz#1ee4d8b682ed363b08af74d1ea2b2b4dbba76487" + integrity sha512-4aRvFIXmwAcDBw9AueDQ2YnGmz5L6obe5kmPT8Vd+/+x/JMVKCgdcRwH6APrbpNXsPz+K653Qg8HB/oXvXVukA== + +"@esbuild/freebsd-x64@0.19.12": + version "0.19.12" + resolved "https://registry.yarnpkg.com/@esbuild/freebsd-x64/-/freebsd-x64-0.19.12.tgz#37a693553d42ff77cd7126764b535fb6cc28a11c" + integrity sha512-EYoXZ4d8xtBoVN7CEwWY2IN4ho76xjYXqSXMNccFSx2lgqOG/1TBPW0yPx1bJZk94qu3tX0fycJeeQsKovA8gg== + +"@esbuild/linux-arm64@0.19.12": + version "0.19.12" + resolved "https://registry.yarnpkg.com/@esbuild/linux-arm64/-/linux-arm64-0.19.12.tgz#be9b145985ec6c57470e0e051d887b09dddb2d4b" + integrity sha512-EoTjyYyLuVPfdPLsGVVVC8a0p1BFFvtpQDB/YLEhaXyf/5bczaGeN15QkR+O4S5LeJ92Tqotve7i1jn35qwvdA== + +"@esbuild/linux-arm@0.19.12": + version "0.19.12" + resolved "https://registry.yarnpkg.com/@esbuild/linux-arm/-/linux-arm-0.19.12.tgz#207ecd982a8db95f7b5279207d0ff2331acf5eef" + integrity sha512-J5jPms//KhSNv+LO1S1TX1UWp1ucM6N6XuL6ITdKWElCu8wXP72l9MM0zDTzzeikVyqFE6U8YAV9/tFyj0ti+w== + +"@esbuild/linux-ia32@0.19.12": + version "0.19.12" + resolved "https://registry.yarnpkg.com/@esbuild/linux-ia32/-/linux-ia32-0.19.12.tgz#d0d86b5ca1562523dc284a6723293a52d5860601" + integrity sha512-Thsa42rrP1+UIGaWz47uydHSBOgTUnwBwNq59khgIwktK6x60Hivfbux9iNR0eHCHzOLjLMLfUMLCypBkZXMHA== + +"@esbuild/linux-loong64@0.19.12": + version "0.19.12" + resolved "https://registry.yarnpkg.com/@esbuild/linux-loong64/-/linux-loong64-0.19.12.tgz#9a37f87fec4b8408e682b528391fa22afd952299" + integrity sha512-LiXdXA0s3IqRRjm6rV6XaWATScKAXjI4R4LoDlvO7+yQqFdlr1Bax62sRwkVvRIrwXxvtYEHHI4dm50jAXkuAA== + +"@esbuild/linux-mips64el@0.19.12": + version "0.19.12" + resolved "https://registry.yarnpkg.com/@esbuild/linux-mips64el/-/linux-mips64el-0.19.12.tgz#4ddebd4e6eeba20b509d8e74c8e30d8ace0b89ec" + integrity sha512-fEnAuj5VGTanfJ07ff0gOA6IPsvrVHLVb6Lyd1g2/ed67oU1eFzL0r9WL7ZzscD+/N6i3dWumGE1Un4f7Amf+w== + +"@esbuild/linux-ppc64@0.19.12": + version "0.19.12" + resolved "https://registry.yarnpkg.com/@esbuild/linux-ppc64/-/linux-ppc64-0.19.12.tgz#adb67dadb73656849f63cd522f5ecb351dd8dee8" + integrity sha512-nYJA2/QPimDQOh1rKWedNOe3Gfc8PabU7HT3iXWtNUbRzXS9+vgB0Fjaqr//XNbd82mCxHzik2qotuI89cfixg== + +"@esbuild/linux-riscv64@0.19.12": + version "0.19.12" + resolved "https://registry.yarnpkg.com/@esbuild/linux-riscv64/-/linux-riscv64-0.19.12.tgz#11bc0698bf0a2abf8727f1c7ace2112612c15adf" + integrity sha512-2MueBrlPQCw5dVJJpQdUYgeqIzDQgw3QtiAHUC4RBz9FXPrskyyU3VI1hw7C0BSKB9OduwSJ79FTCqtGMWqJHg== + +"@esbuild/linux-s390x@0.19.12": + version "0.19.12" + resolved "https://registry.yarnpkg.com/@esbuild/linux-s390x/-/linux-s390x-0.19.12.tgz#e86fb8ffba7c5c92ba91fc3b27ed5a70196c3cc8" + integrity sha512-+Pil1Nv3Umes4m3AZKqA2anfhJiVmNCYkPchwFJNEJN5QxmTs1uzyy4TvmDrCRNT2ApwSari7ZIgrPeUx4UZDg== + +"@esbuild/linux-x64@0.19.12": + version "0.19.12" + resolved "https://registry.yarnpkg.com/@esbuild/linux-x64/-/linux-x64-0.19.12.tgz#5f37cfdc705aea687dfe5dfbec086a05acfe9c78" + integrity sha512-B71g1QpxfwBvNrfyJdVDexenDIt1CiDN1TIXLbhOw0KhJzE78KIFGX6OJ9MrtC0oOqMWf+0xop4qEU8JrJTwCg== + +"@esbuild/netbsd-x64@0.19.12": + version "0.19.12" + resolved "https://registry.yarnpkg.com/@esbuild/netbsd-x64/-/netbsd-x64-0.19.12.tgz#29da566a75324e0d0dd7e47519ba2f7ef168657b" + integrity sha512-3ltjQ7n1owJgFbuC61Oj++XhtzmymoCihNFgT84UAmJnxJfm4sYCiSLTXZtE00VWYpPMYc+ZQmB6xbSdVh0JWA== + +"@esbuild/openbsd-x64@0.19.12": + version "0.19.12" + resolved "https://registry.yarnpkg.com/@esbuild/openbsd-x64/-/openbsd-x64-0.19.12.tgz#306c0acbdb5a99c95be98bdd1d47c916e7dc3ff0" + integrity sha512-RbrfTB9SWsr0kWmb9srfF+L933uMDdu9BIzdA7os2t0TXhCRjrQyCeOt6wVxr79CKD4c+p+YhCj31HBkYcXebw== + +"@esbuild/sunos-x64@0.19.12": + version "0.19.12" + resolved "https://registry.yarnpkg.com/@esbuild/sunos-x64/-/sunos-x64-0.19.12.tgz#0933eaab9af8b9b2c930236f62aae3fc593faf30" + integrity sha512-HKjJwRrW8uWtCQnQOz9qcU3mUZhTUQvi56Q8DPTLLB+DawoiQdjsYq+j+D3s9I8VFtDr+F9CjgXKKC4ss89IeA== + +"@esbuild/win32-arm64@0.19.12": + version "0.19.12" + resolved "https://registry.yarnpkg.com/@esbuild/win32-arm64/-/win32-arm64-0.19.12.tgz#773bdbaa1971b36db2f6560088639ccd1e6773ae" + integrity sha512-URgtR1dJnmGvX864pn1B2YUYNzjmXkuJOIqG2HdU62MVS4EHpU2946OZoTMnRUHklGtJdJZ33QfzdjGACXhn1A== + +"@esbuild/win32-ia32@0.19.12": + version "0.19.12" + resolved "https://registry.yarnpkg.com/@esbuild/win32-ia32/-/win32-ia32-0.19.12.tgz#000516cad06354cc84a73f0943a4aa690ef6fd67" + integrity sha512-+ZOE6pUkMOJfmxmBZElNOx72NKpIa/HFOMGzu8fqzQJ5kgf6aTGrcJaFsNiVMH4JKpMipyK+7k0n2UXN7a8YKQ== + +"@esbuild/win32-x64@0.19.12": + version "0.19.12" + resolved "https://registry.yarnpkg.com/@esbuild/win32-x64/-/win32-x64-0.19.12.tgz#c57c8afbb4054a3ab8317591a0b7320360b444ae" + integrity sha512-T1QyPSDCyMXaO3pzBkF96E8xMkiRYbUEZADd29SyPGabqxMViNoii+NcK7eWJAEoU6RZyEm5lVSIjTmcdoB9HA== + +"@eslint-community/eslint-utils@^4.2.0", "@eslint-community/eslint-utils@^4.4.0": + version "4.4.0" + resolved "https://registry.yarnpkg.com/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz#a23514e8fb9af1269d5f7788aa556798d61c6b59" + integrity sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA== + dependencies: + eslint-visitor-keys "^3.3.0" + +"@eslint-community/regexpp@^4.5.1", "@eslint-community/regexpp@^4.6.1": + version "4.10.0" + resolved "https://registry.yarnpkg.com/@eslint-community/regexpp/-/regexpp-4.10.0.tgz#548f6de556857c8bb73bbee70c35dc82a2e74d63" + integrity sha512-Cu96Sd2By9mCNTx2iyKOmq10v22jUVQv0lQnlGNy16oE9589yE+QADPbrMGCkA51cKZSg3Pu/aTJVTGfL/qjUA== + +"@eslint/eslintrc@^2.1.4": + version "2.1.4" + resolved "https://registry.yarnpkg.com/@eslint/eslintrc/-/eslintrc-2.1.4.tgz#388a269f0f25c1b6adc317b5a2c55714894c70ad" + integrity sha512-269Z39MS6wVJtsoUl10L60WdkhJVdPG24Q4eZTH3nnF6lpvSShEK3wQjDX9JRWAUPvPh7COouPpU9IrqaZFvtQ== + dependencies: + ajv "^6.12.4" + debug "^4.3.2" + espree "^9.6.0" + globals "^13.19.0" + ignore "^5.2.0" + import-fresh "^3.2.1" + js-yaml "^4.1.0" + minimatch "^3.1.2" + strip-json-comments "^3.1.1" + +"@eslint/js@8.56.0": + version "8.56.0" + resolved "https://registry.yarnpkg.com/@eslint/js/-/js-8.56.0.tgz#ef20350fec605a7f7035a01764731b2de0f3782b" + integrity sha512-gMsVel9D7f2HLkBma9VbtzZRehRogVRfbr++f06nL2vnCGCNlzOD+/MUov/F4p8myyAHspEhVobgjpX64q5m6A== + +"@humanwhocodes/config-array@^0.11.13": + version "0.11.14" + resolved "https://registry.yarnpkg.com/@humanwhocodes/config-array/-/config-array-0.11.14.tgz#d78e481a039f7566ecc9660b4ea7fe6b1fec442b" + integrity sha512-3T8LkOmg45BV5FICb15QQMsyUSWrQ8AygVfC7ZG32zOalnqrilm018ZVCw0eapXux8FtA33q8PSRSstjee3jSg== + dependencies: + "@humanwhocodes/object-schema" "^2.0.2" + debug "^4.3.1" + minimatch "^3.0.5" + +"@humanwhocodes/module-importer@^1.0.1": + version "1.0.1" + resolved "https://registry.yarnpkg.com/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz#af5b2691a22b44be847b0ca81641c5fb6ad0172c" + integrity sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA== + +"@humanwhocodes/object-schema@^2.0.2": + version "2.0.2" + resolved "https://registry.yarnpkg.com/@humanwhocodes/object-schema/-/object-schema-2.0.2.tgz#d9fae00a2d5cb40f92cfe64b47ad749fbc38f917" + integrity sha512-6EwiSjwWYP7pTckG6I5eyFANjPhmPjUX9JRLUSfNPC7FX7zK9gyZAfUEaECL6ALTpGX5AjnBq3C9XmVWPitNpw== + +"@isaacs/cliui@^8.0.2": + version "8.0.2" + resolved "https://registry.yarnpkg.com/@isaacs/cliui/-/cliui-8.0.2.tgz#b37667b7bc181c168782259bab42474fbf52b550" + integrity sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA== + dependencies: + string-width "^5.1.2" + string-width-cjs "npm:string-width@^4.2.0" + strip-ansi "^7.0.1" + strip-ansi-cjs "npm:strip-ansi@^6.0.1" + wrap-ansi "^8.1.0" + wrap-ansi-cjs "npm:wrap-ansi@^7.0.0" + +"@jest/schemas@^29.6.3": + version "29.6.3" + resolved "https://registry.yarnpkg.com/@jest/schemas/-/schemas-29.6.3.tgz#430b5ce8a4e0044a7e3819663305a7b3091c8e03" + integrity sha512-mo5j5X+jIZmJQveBKeS/clAueipV7KgiX1vMgCxam1RNYiqE1w62n0/tJJnHtjW8ZHcQco5gY85jA3mi0L+nSA== + dependencies: + "@sinclair/typebox" "^0.27.8" + +"@jridgewell/gen-mapping@^0.3.0", "@jridgewell/gen-mapping@^0.3.2": + version "0.3.3" + resolved "https://registry.yarnpkg.com/@jridgewell/gen-mapping/-/gen-mapping-0.3.3.tgz#7e02e6eb5df901aaedb08514203b096614024098" + integrity sha512-HLhSWOLRi875zjjMG/r+Nv0oCW8umGb0BgEhyX3dDX3egwZtB8PqLnjz3yedt8R5StBrzcg4aBpnh8UA9D1BoQ== + dependencies: + "@jridgewell/set-array" "^1.0.1" + "@jridgewell/sourcemap-codec" "^1.4.10" + "@jridgewell/trace-mapping" "^0.3.9" + +"@jridgewell/resolve-uri@^3.1.0": + version "3.1.1" + resolved "https://registry.yarnpkg.com/@jridgewell/resolve-uri/-/resolve-uri-3.1.1.tgz#c08679063f279615a3326583ba3a90d1d82cc721" + integrity sha512-dSYZh7HhCDtCKm4QakX0xFpsRDqjjtZf/kjI/v3T3Nwt5r8/qz/M19F9ySyOqU94SXBmeG9ttTul+YnR4LOxFA== + +"@jridgewell/set-array@^1.0.1": + version "1.1.2" + resolved "https://registry.yarnpkg.com/@jridgewell/set-array/-/set-array-1.1.2.tgz#7c6cf998d6d20b914c0a55a91ae928ff25965e72" + integrity sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw== + +"@jridgewell/source-map@^0.3.3": + version "0.3.5" + resolved "https://registry.yarnpkg.com/@jridgewell/source-map/-/source-map-0.3.5.tgz#a3bb4d5c6825aab0d281268f47f6ad5853431e91" + integrity sha512-UTYAUj/wviwdsMfzoSJspJxbkH5o1snzwX0//0ENX1u/55kkZZkcTZP6u9bwKGkv+dkk9at4m1Cpt0uY80kcpQ== + dependencies: + "@jridgewell/gen-mapping" "^0.3.0" + "@jridgewell/trace-mapping" "^0.3.9" + +"@jridgewell/sourcemap-codec@^1.4.10", "@jridgewell/sourcemap-codec@^1.4.14", "@jridgewell/sourcemap-codec@^1.4.15": + version "1.4.15" + resolved "https://registry.yarnpkg.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz#d7c6e6755c78567a951e04ab52ef0fd26de59f32" + integrity sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg== + +"@jridgewell/trace-mapping@^0.3.9": + version "0.3.22" + resolved "https://registry.yarnpkg.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.22.tgz#72a621e5de59f5f1ef792d0793a82ee20f645e4c" + integrity sha512-Wf963MzWtA2sjrNt+g18IAln9lKnlRp+K2eH4jjIoF1wYeq3aMREpG09xhlhdzS0EjwU7qmUJYangWa+151vZw== + dependencies: + "@jridgewell/resolve-uri" "^3.1.0" + "@jridgewell/sourcemap-codec" "^1.4.14" + +"@microsoft/api-extractor-model@7.28.7": + version "7.28.7" + resolved "https://registry.yarnpkg.com/@microsoft/api-extractor-model/-/api-extractor-model-7.28.7.tgz#efb36902a9d360b3044409c9404dc3e877afd0b9" + integrity sha512-4gCGGEQGHmbQmarnDcEWS2cjj0LtNuD3D6rh3ZcAyAYTkceAugAk2eyQHGdTcGX8w3qMjWCTU1TPb8xHnMM+Kg== + dependencies: + "@microsoft/tsdoc" "0.14.2" + "@microsoft/tsdoc-config" "~0.16.1" + "@rushstack/node-core-library" "3.64.2" + +"@microsoft/api-extractor@^7.33.5": + version "7.39.4" + resolved "https://registry.yarnpkg.com/@microsoft/api-extractor/-/api-extractor-7.39.4.tgz#15732630670df295c1b45fbd3bfd12494e2176cc" + integrity sha512-6YvfkpbEqRQ0UPdVBc+lOiq7VlXi9kw8U3w+RcXCFDVc/UljlXU5l9fHEyuBAW1GGO2opUe+yf9OscWhoHANhg== + dependencies: + "@microsoft/api-extractor-model" "7.28.7" + "@microsoft/tsdoc" "0.14.2" + "@microsoft/tsdoc-config" "~0.16.1" + "@rushstack/node-core-library" "3.64.2" + "@rushstack/rig-package" "0.5.1" + "@rushstack/ts-command-line" "4.17.1" + colors "~1.2.1" + lodash "~4.17.15" + resolve "~1.22.1" + semver "~7.5.4" + source-map "~0.6.1" + typescript "5.3.3" + +"@microsoft/tsdoc-config@~0.16.1": + version "0.16.2" + resolved "https://registry.yarnpkg.com/@microsoft/tsdoc-config/-/tsdoc-config-0.16.2.tgz#b786bb4ead00d54f53839a458ce626c8548d3adf" + integrity sha512-OGiIzzoBLgWWR0UdRJX98oYO+XKGf7tiK4Zk6tQ/E4IJqGCe7dvkTvgDZV5cFJUzLGDOjeAXrnZoA6QkVySuxw== + dependencies: + "@microsoft/tsdoc" "0.14.2" + ajv "~6.12.6" + jju "~1.4.0" + resolve "~1.19.0" + +"@microsoft/tsdoc@0.14.2": + version "0.14.2" + resolved "https://registry.yarnpkg.com/@microsoft/tsdoc/-/tsdoc-0.14.2.tgz#c3ec604a0b54b9a9b87e9735dfc59e1a5da6a5fb" + integrity sha512-9b8mPpKrfeGRuhFH5iO1iwCLeIIsV6+H1sRfxbkoGXIyQE2BTsPd9zqSqQJ+pv5sJ/hT5M1zvOFL02MnEezFug== + +"@nodelib/fs.scandir@2.1.5": + version "2.1.5" + resolved "https://registry.yarnpkg.com/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz#7619c2eb21b25483f6d167548b4cfd5a7488c3d5" + integrity sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g== + dependencies: + "@nodelib/fs.stat" "2.0.5" + run-parallel "^1.1.9" + +"@nodelib/fs.stat@2.0.5", "@nodelib/fs.stat@^2.0.2": + version "2.0.5" + resolved "https://registry.yarnpkg.com/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz#5bd262af94e9d25bd1e71b05deed44876a222e8b" + integrity sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A== + +"@nodelib/fs.walk@^1.2.3", "@nodelib/fs.walk@^1.2.8": + version "1.2.8" + resolved "https://registry.yarnpkg.com/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz#e95737e8bb6746ddedf69c556953494f196fe69a" + integrity sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg== + dependencies: + "@nodelib/fs.scandir" "2.1.5" + fastq "^1.6.0" + +"@pkgjs/parseargs@^0.11.0": + version "0.11.0" + resolved "https://registry.yarnpkg.com/@pkgjs/parseargs/-/parseargs-0.11.0.tgz#a77ea742fab25775145434eb1d2328cf5013ac33" + integrity sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg== + +"@polka/url@^1.0.0-next.24": + version "1.0.0-next.24" + resolved "https://registry.yarnpkg.com/@polka/url/-/url-1.0.0-next.24.tgz#58601079e11784d20f82d0585865bb42305c4df3" + integrity sha512-2LuNTFBIO0m7kKIQvvPHN6UE63VjpmL9rnEEaOOaiSPbZK+zUOYIzBAWcED+3XYzhYsd/0mD57VdxAEqqV52CQ== + +"@rollup/plugin-inject@^5.0.5": + version "5.0.5" + resolved "https://registry.yarnpkg.com/@rollup/plugin-inject/-/plugin-inject-5.0.5.tgz#616f3a73fe075765f91c5bec90176608bed277a3" + integrity sha512-2+DEJbNBoPROPkgTDNe8/1YXWcqxbN5DTjASVIOx8HS+pITXushyNiBV56RB08zuptzz8gT3YfkqriTBVycepg== + dependencies: + "@rollup/pluginutils" "^5.0.1" + estree-walker "^2.0.2" + magic-string "^0.30.3" + +"@rollup/pluginutils@^4.2.0": + version "4.2.1" + resolved "https://registry.yarnpkg.com/@rollup/pluginutils/-/pluginutils-4.2.1.tgz#e6c6c3aba0744edce3fb2074922d3776c0af2a6d" + integrity sha512-iKnFXr7NkdZAIHiIWE+BX5ULi/ucVFYWD6TbAV+rZctiRTY2PL6tsIKhoIOaoskiWAkgu+VsbXgUVDNLHf+InQ== + dependencies: + estree-walker "^2.0.1" + picomatch "^2.2.2" + +"@rollup/pluginutils@^5.0.1", "@rollup/pluginutils@^5.0.2", "@rollup/pluginutils@^5.1.0": + version "5.1.0" + resolved "https://registry.yarnpkg.com/@rollup/pluginutils/-/pluginutils-5.1.0.tgz#7e53eddc8c7f483a4ad0b94afb1f7f5fd3c771e0" + integrity sha512-XTIWOPPcpvyKI6L1NHo0lFlCyznUEyPmPY1mc3KpPVDYulHSTvyeLNVW00QTLIAFNhR3kYnJTQHeGqU4M3n09g== + dependencies: + "@types/estree" "^1.0.0" + estree-walker "^2.0.2" + picomatch "^2.3.1" + +"@rollup/rollup-android-arm-eabi@4.9.6": + version "4.9.6" + resolved "https://registry.yarnpkg.com/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.9.6.tgz#66b8d9cb2b3a474d115500f9ebaf43e2126fe496" + integrity sha512-MVNXSSYN6QXOulbHpLMKYi60ppyO13W9my1qogeiAqtjb2yR4LSmfU2+POvDkLzhjYLXz9Rf9+9a3zFHW1Lecg== + +"@rollup/rollup-android-arm64@4.9.6": + version "4.9.6" + resolved "https://registry.yarnpkg.com/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.9.6.tgz#46327d5b86420d2307946bec1535fdf00356e47d" + integrity sha512-T14aNLpqJ5wzKNf5jEDpv5zgyIqcpn1MlwCrUXLrwoADr2RkWA0vOWP4XxbO9aiO3dvMCQICZdKeDrFl7UMClw== + +"@rollup/rollup-darwin-arm64@4.9.6": + version "4.9.6" + resolved "https://registry.yarnpkg.com/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.9.6.tgz#166987224d2f8b1e2fd28ee90c447d52271d5e90" + integrity sha512-CqNNAyhRkTbo8VVZ5R85X73H3R5NX9ONnKbXuHisGWC0qRbTTxnF1U4V9NafzJbgGM0sHZpdO83pLPzq8uOZFw== + +"@rollup/rollup-darwin-x64@4.9.6": + version "4.9.6" + resolved "https://registry.yarnpkg.com/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.9.6.tgz#a2e6e096f74ccea6e2f174454c26aef6bcdd1274" + integrity sha512-zRDtdJuRvA1dc9Mp6BWYqAsU5oeLixdfUvkTHuiYOHwqYuQ4YgSmi6+/lPvSsqc/I0Omw3DdICx4Tfacdzmhog== + +"@rollup/rollup-linux-arm-gnueabihf@4.9.6": + version "4.9.6" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.9.6.tgz#09fcd4c55a2d6160c5865fec708a8e5287f30515" + integrity sha512-oNk8YXDDnNyG4qlNb6is1ojTOGL/tRhbbKeE/YuccItzerEZT68Z9gHrY3ROh7axDc974+zYAPxK5SH0j/G+QQ== + +"@rollup/rollup-linux-arm64-gnu@4.9.6": + version "4.9.6" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.9.6.tgz#19a3c0b6315c747ca9acf86e9b710cc2440f83c9" + integrity sha512-Z3O60yxPtuCYobrtzjo0wlmvDdx2qZfeAWTyfOjEDqd08kthDKexLpV97KfAeUXPosENKd8uyJMRDfFMxcYkDQ== + +"@rollup/rollup-linux-arm64-musl@4.9.6": + version "4.9.6" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.9.6.tgz#94aaf95fdaf2ad9335983a4552759f98e6b2e850" + integrity sha512-gpiG0qQJNdYEVad+1iAsGAbgAnZ8j07FapmnIAQgODKcOTjLEWM9sRb+MbQyVsYCnA0Im6M6QIq6ax7liws6eQ== + +"@rollup/rollup-linux-riscv64-gnu@4.9.6": + version "4.9.6" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.9.6.tgz#160510e63f4b12618af4013bddf1761cf9fc9880" + integrity sha512-+uCOcvVmFUYvVDr27aiyun9WgZk0tXe7ThuzoUTAukZJOwS5MrGbmSlNOhx1j80GdpqbOty05XqSl5w4dQvcOA== + +"@rollup/rollup-linux-x64-gnu@4.9.6": + version "4.9.6" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.9.6.tgz#5ac5d068ce0726bd0a96ca260d5bd93721c0cb98" + integrity sha512-HUNqM32dGzfBKuaDUBqFB7tP6VMN74eLZ33Q9Y1TBqRDn+qDonkAUyKWwF9BR9unV7QUzffLnz9GrnKvMqC/fw== + +"@rollup/rollup-linux-x64-musl@4.9.6": + version "4.9.6" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.9.6.tgz#bafa759ab43e8eab9edf242a8259ffb4f2a57a5d" + integrity sha512-ch7M+9Tr5R4FK40FHQk8VnML0Szi2KRujUgHXd/HjuH9ifH72GUmw6lStZBo3c3GB82vHa0ZoUfjfcM7JiiMrQ== + +"@rollup/rollup-win32-arm64-msvc@4.9.6": + version "4.9.6" + resolved "https://registry.yarnpkg.com/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.9.6.tgz#1cc3416682e5a20d8f088f26657e6e47f8db468e" + integrity sha512-VD6qnR99dhmTQ1mJhIzXsRcTBvTjbfbGGwKAHcu+52cVl15AC/kplkhxzW/uT0Xl62Y/meBKDZvoJSJN+vTeGA== + +"@rollup/rollup-win32-ia32-msvc@4.9.6": + version "4.9.6" + resolved "https://registry.yarnpkg.com/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.9.6.tgz#7d2251e1aa5e8a1e47c86891fe4547a939503461" + integrity sha512-J9AFDq/xiRI58eR2NIDfyVmTYGyIZmRcvcAoJ48oDld/NTR8wyiPUu2X/v1navJ+N/FGg68LEbX3Ejd6l8B7MQ== + +"@rollup/rollup-win32-x64-msvc@4.9.6": + version "4.9.6" + resolved "https://registry.yarnpkg.com/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.9.6.tgz#2c1fb69e02a3f1506f52698cfdc3a8b6386df9a6" + integrity sha512-jqzNLhNDvIZOrt69Ce4UjGRpXJBzhUBzawMwnaDAwyHriki3XollsewxWzOzz+4yOFDkuJHtTsZFwMxhYJWmLQ== + +"@rushstack/node-core-library@3.64.2", "@rushstack/node-core-library@^3.53.2": + version "3.64.2" + resolved "https://registry.yarnpkg.com/@rushstack/node-core-library/-/node-core-library-3.64.2.tgz#348df161d2cc4ebcd08ddb77ab6e63aec82f610a" + integrity sha512-n1S2VYEklONiwKpUyBq/Fym6yAsfsCXrqFabuOMcCuj4C+zW+HyaspSHXJCKqkMxfjviwe/c9+DUqvRWIvSN9Q== + dependencies: + colors "~1.2.1" + fs-extra "~7.0.1" + import-lazy "~4.0.0" + jju "~1.4.0" + resolve "~1.22.1" + semver "~7.5.4" + z-schema "~5.0.2" + +"@rushstack/rig-package@0.5.1": + version "0.5.1" + resolved "https://registry.yarnpkg.com/@rushstack/rig-package/-/rig-package-0.5.1.tgz#6c9c283cc96b5bb1eae9875946d974ac5429bb21" + integrity sha512-pXRYSe29TjRw7rqxD4WS3HN/sRSbfr+tJs4a9uuaSIBAITbUggygdhuG0VrO0EO+QqH91GhYMN4S6KRtOEmGVA== + dependencies: + resolve "~1.22.1" + strip-json-comments "~3.1.1" + +"@rushstack/ts-command-line@4.17.1": + version "4.17.1" + resolved "https://registry.yarnpkg.com/@rushstack/ts-command-line/-/ts-command-line-4.17.1.tgz#c78db928ce5b93f2e98fd9e14c24f3f3876e57f1" + integrity sha512-2jweO1O57BYP5qdBGl6apJLB+aRIn5ccIRTPDyULh0KMwVzFqWtw6IZWt1qtUoZD/pD2RNkIOosH6Cq45rIYeg== + dependencies: + "@types/argparse" "1.0.38" + argparse "~1.0.9" + colors "~1.2.1" + string-argv "~0.3.1" + +"@sinclair/typebox@^0.27.8": + version "0.27.8" + resolved "https://registry.yarnpkg.com/@sinclair/typebox/-/typebox-0.27.8.tgz#6667fac16c436b5434a387a34dedb013198f6e6e" + integrity sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA== + +"@trysound/sax@0.2.0": + version "0.2.0" + resolved "https://registry.yarnpkg.com/@trysound/sax/-/sax-0.2.0.tgz#cccaab758af56761eb7bf37af6f03f326dd798ad" + integrity sha512-L7z9BgrNEcYyUYtF+HaEfiS5ebkh9jXqbszz7pC0hRBPaatV0XjSD3+eHrpqFemQfgwiFF0QPIarnIihIDn7OA== + +"@ts-morph/common@~0.18.0": + version "0.18.1" + resolved "https://registry.yarnpkg.com/@ts-morph/common/-/common-0.18.1.tgz#ca40c3a62c3f9e17142e0af42633ad63efbae0ec" + integrity sha512-RVE+zSRICWRsfrkAw5qCAK+4ZH9kwEFv5h0+/YeHTLieWP7F4wWq4JsKFuNWG+fYh/KF+8rAtgdj5zb2mm+DVA== + dependencies: + fast-glob "^3.2.12" + minimatch "^5.1.0" + mkdirp "^1.0.4" + path-browserify "^1.0.1" + +"@types/argparse@1.0.38": + version "1.0.38" + resolved "https://registry.yarnpkg.com/@types/argparse/-/argparse-1.0.38.tgz#a81fd8606d481f873a3800c6ebae4f1d768a56a9" + integrity sha512-ebDJ9b0e702Yr7pWgB0jzm+CX4Srzz8RcXtLJDJB+BSccqMa36uyH/zUsSYao5+BD1ytv3k3rPYCq4mAE1hsXA== + +"@types/estree@1.0.5", "@types/estree@^1.0.0": + version "1.0.5" + resolved "https://registry.yarnpkg.com/@types/estree/-/estree-1.0.5.tgz#a6ce3e556e00fd9895dd872dd172ad0d4bd687f4" + integrity sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw== + +"@types/json-schema@^7.0.12": + version "7.0.15" + resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.15.tgz#596a1747233694d50f6ad8a7869fcb6f56cf5841" + integrity sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA== + +"@types/node@*": + version "20.11.16" + resolved "https://registry.yarnpkg.com/@types/node/-/node-20.11.16.tgz#4411f79411514eb8e2926f036c86c9f0e4ec6708" + integrity sha512-gKb0enTmRCzXSSUJDq6/sPcqrfCv2mkkG6Jt/clpn5eiCbKTY+SgZUxo+p8ZKMof5dCp9vHQUAB7wOUTod22wQ== + dependencies: + undici-types "~5.26.4" + +"@types/node@^20.10.3": + version "20.11.10" + resolved "https://registry.yarnpkg.com/@types/node/-/node-20.11.10.tgz#6c3de8974d65c362f82ee29db6b5adf4205462f9" + integrity sha512-rZEfe/hJSGYmdfX9tvcPMYeYPW2sNl50nsw4jZmRcaG0HIAb0WYEpsB05GOb53vjqpyE9GUhlDQ4jLSoB5q9kg== + dependencies: + undici-types "~5.26.4" + +"@types/normalize-package-data@^2.4.0": + version "2.4.4" + resolved "https://registry.yarnpkg.com/@types/normalize-package-data/-/normalize-package-data-2.4.4.tgz#56e2cc26c397c038fab0e3a917a12d5c5909e901" + integrity sha512-37i+OaWTh9qeK4LSHPsyRC7NahnGotNuZvjLSgcPzblpHB3rrCJxAOgI5gCdKm7coonsaX1Of0ILiTcnZjbfxA== + +"@types/semver@^7.5.0": + version "7.5.6" + resolved "https://registry.yarnpkg.com/@types/semver/-/semver-7.5.6.tgz#c65b2bfce1bec346582c07724e3f8c1017a20339" + integrity sha512-dn1l8LaMea/IjDoHNd9J52uBbInB796CDffS6VdIxvqYCPSG0V0DzHp76GpaWnlhg88uYyPbXCDIowa86ybd5A== + +"@typescript-eslint/eslint-plugin@^6.13.2": + version "6.20.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-6.20.0.tgz#9cf31546d2d5e884602626d89b0e0d2168ac25ed" + integrity sha512-fTwGQUnjhoYHeSF6m5pWNkzmDDdsKELYrOBxhjMrofPqCkoC2k3B2wvGHFxa1CTIqkEn88nlW1HVMztjo2K8Hg== + dependencies: + "@eslint-community/regexpp" "^4.5.1" + "@typescript-eslint/scope-manager" "6.20.0" + "@typescript-eslint/type-utils" "6.20.0" + "@typescript-eslint/utils" "6.20.0" + "@typescript-eslint/visitor-keys" "6.20.0" + debug "^4.3.4" + graphemer "^1.4.0" + ignore "^5.2.4" + natural-compare "^1.4.0" + semver "^7.5.4" + ts-api-utils "^1.0.1" + +"@typescript-eslint/parser@^6.13.2": + version "6.20.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-6.20.0.tgz#17e314177304bdf498527e3c4b112e41287b7416" + integrity sha512-bYerPDF/H5v6V76MdMYhjwmwgMA+jlPVqjSDq2cRqMi8bP5sR3Z+RLOiOMad3nsnmDVmn2gAFCyNgh/dIrfP/w== + dependencies: + "@typescript-eslint/scope-manager" "6.20.0" + "@typescript-eslint/types" "6.20.0" + "@typescript-eslint/typescript-estree" "6.20.0" + "@typescript-eslint/visitor-keys" "6.20.0" + debug "^4.3.4" + +"@typescript-eslint/scope-manager@6.20.0": + version "6.20.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-6.20.0.tgz#8a926e60f6c47feb5bab878246dc2ae465730151" + integrity sha512-p4rvHQRDTI1tGGMDFQm+GtxP1ZHyAh64WANVoyEcNMpaTFn3ox/3CcgtIlELnRfKzSs/DwYlDccJEtr3O6qBvA== + dependencies: + "@typescript-eslint/types" "6.20.0" + "@typescript-eslint/visitor-keys" "6.20.0" + +"@typescript-eslint/type-utils@6.20.0": + version "6.20.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/type-utils/-/type-utils-6.20.0.tgz#d395475cd0f3610dd80c7d8716fa0db767da3831" + integrity sha512-qnSobiJQb1F5JjN0YDRPHruQTrX7ICsmltXhkV536mp4idGAYrIyr47zF/JmkJtEcAVnIz4gUYJ7gOZa6SmN4g== + dependencies: + "@typescript-eslint/typescript-estree" "6.20.0" + "@typescript-eslint/utils" "6.20.0" + debug "^4.3.4" + ts-api-utils "^1.0.1" + +"@typescript-eslint/types@6.20.0": + version "6.20.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-6.20.0.tgz#5ccd74c29011ae7714ae6973e4ec0c634708b448" + integrity sha512-MM9mfZMAhiN4cOEcUOEx+0HmuaW3WBfukBZPCfwSqFnQy0grXYtngKCqpQN339X3RrwtzspWJrpbrupKYUSBXQ== + +"@typescript-eslint/typescript-estree@6.20.0": + version "6.20.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-6.20.0.tgz#5b2d0975949e6bdd8d45ee1471461ef5fadc5542" + integrity sha512-RnRya9q5m6YYSpBN7IzKu9FmLcYtErkDkc8/dKv81I9QiLLtVBHrjz+Ev/crAqgMNW2FCsoZF4g2QUylMnJz+g== + dependencies: + "@typescript-eslint/types" "6.20.0" + "@typescript-eslint/visitor-keys" "6.20.0" + debug "^4.3.4" + globby "^11.1.0" + is-glob "^4.0.3" + minimatch "9.0.3" + semver "^7.5.4" + ts-api-utils "^1.0.1" + +"@typescript-eslint/utils@6.20.0": + version "6.20.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/utils/-/utils-6.20.0.tgz#0e52afcfaa51af5656490ba4b7437cc3aa28633d" + integrity sha512-/EKuw+kRu2vAqCoDwDCBtDRU6CTKbUmwwI7SH7AashZ+W+7o8eiyy6V2cdOqN49KsTcASWsC5QeghYuRDTyOOg== + dependencies: + "@eslint-community/eslint-utils" "^4.4.0" + "@types/json-schema" "^7.0.12" + "@types/semver" "^7.5.0" + "@typescript-eslint/scope-manager" "6.20.0" + "@typescript-eslint/types" "6.20.0" + "@typescript-eslint/typescript-estree" "6.20.0" + semver "^7.5.4" + +"@typescript-eslint/visitor-keys@6.20.0": + version "6.20.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-6.20.0.tgz#f7ada27f2803de89df0edd9fd7be22c05ce6a498" + integrity sha512-E8Cp98kRe4gKHjJD4NExXKz/zOJ1A2hhZc+IMVD6i7w4yjIvh6VyuRI0gRtxAsXtoC35uGMaQ9rjI2zJaXDEAw== + dependencies: + "@typescript-eslint/types" "6.20.0" + eslint-visitor-keys "^3.4.1" + +"@ungap/structured-clone@^1.2.0": + version "1.2.0" + resolved "https://registry.yarnpkg.com/@ungap/structured-clone/-/structured-clone-1.2.0.tgz#756641adb587851b5ccb3e095daf27ae581c8406" + integrity sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ== + +"@vitest/expect@1.2.2": + version "1.2.2" + resolved "https://registry.yarnpkg.com/@vitest/expect/-/expect-1.2.2.tgz#39ea22e849bbf404b7e5272786551aa99e2663d0" + integrity sha512-3jpcdPAD7LwHUUiT2pZTj2U82I2Tcgg2oVPvKxhn6mDI2On6tfvPQTjAI4628GUGDZrCm4Zna9iQHm5cEexOAg== + dependencies: + "@vitest/spy" "1.2.2" + "@vitest/utils" "1.2.2" + chai "^4.3.10" + +"@vitest/runner@1.2.2": + version "1.2.2" + resolved "https://registry.yarnpkg.com/@vitest/runner/-/runner-1.2.2.tgz#8b060a56ecf8b3d607b044d79f5f50d3cd9fee2f" + integrity sha512-JctG7QZ4LSDXr5CsUweFgcpEvrcxOV1Gft7uHrvkQ+fsAVylmWQvnaAr/HDp3LAH1fztGMQZugIheTWjaGzYIg== + dependencies: + "@vitest/utils" "1.2.2" + p-limit "^5.0.0" + pathe "^1.1.1" + +"@vitest/snapshot@1.2.2": + version "1.2.2" + resolved "https://registry.yarnpkg.com/@vitest/snapshot/-/snapshot-1.2.2.tgz#f56fd575569774968f3eeba9382a166c26201042" + integrity sha512-SmGY4saEw1+bwE1th6S/cZmPxz/Q4JWsl7LvbQIky2tKE35US4gd0Mjzqfr84/4OD0tikGWaWdMja/nWL5NIPA== + dependencies: + magic-string "^0.30.5" + pathe "^1.1.1" + pretty-format "^29.7.0" + +"@vitest/spy@1.2.2": + version "1.2.2" + resolved "https://registry.yarnpkg.com/@vitest/spy/-/spy-1.2.2.tgz#8fc2aeccb96cecbbdd192c643729bd5f97a01c86" + integrity sha512-k9Gcahssw8d7X3pSLq3e3XEu/0L78mUkCjivUqCQeXJm9clfXR/Td8+AP+VC1O6fKPIDLcHDTAmBOINVuv6+7g== + dependencies: + tinyspy "^2.2.0" + +"@vitest/utils@1.2.2": + version "1.2.2" + resolved "https://registry.yarnpkg.com/@vitest/utils/-/utils-1.2.2.tgz#94b5a1bd8745ac28cf220a99a8719efea1bcfc83" + integrity sha512-WKITBHLsBHlpjnDQahr+XK6RE7MiAsgrIkr0pGhQ9ygoxBfUeG0lUG5iLlzqjmKSlBv3+j5EGsriBzh+C3Tq9g== + dependencies: + diff-sequences "^29.6.3" + estree-walker "^3.0.3" + loupe "^2.3.7" + pretty-format "^29.7.0" + +"@wapython/unionfs@^4.5.7": + version "4.5.7" + resolved "https://registry.yarnpkg.com/@wapython/unionfs/-/unionfs-4.5.7.tgz#bdc35e34534709702cf125ac3e96d7a67a761af3" + integrity sha512-7809nAVelP9TqXCq5c1yCwymPreTXrK4n4q/zxOlQOIIz4PNmoQmIKiLnwkKk39GDnLmP1MvP9ZkBDyXCCmTfg== + dependencies: + fs-monkey "^1.0.0" + +acorn-jsx@^5.3.2: + version "5.3.2" + resolved "https://registry.yarnpkg.com/acorn-jsx/-/acorn-jsx-5.3.2.tgz#7ed5bb55908b3b2f1bc55c6af1653bada7f07937" + integrity sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ== + +acorn-walk@^8.3.2: + version "8.3.2" + resolved "https://registry.yarnpkg.com/acorn-walk/-/acorn-walk-8.3.2.tgz#7703af9415f1b6db9315d6895503862e231d34aa" + integrity sha512-cjkyv4OtNCIeqhHrfS81QWXoCBPExR/J62oyEqepVw8WaQeSqpW2uhuLPh1m9eWhDuOo/jUXVTlifvesOWp/4A== + +acorn@^8.10.0, acorn@^8.11.3, acorn@^8.8.2, acorn@^8.9.0: + version "8.11.3" + resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.11.3.tgz#71e0b14e13a4ec160724b38fb7b0f233b1b81d7a" + integrity sha512-Y9rRfJG5jcKOE0CLisYbojUjIrIEE7AGMzA/Sm4BslANhbS+cDMpgBdcPT91oJ7OuJ9hYJBx59RjbhxVnrF8Xg== + +ajv@^6.12.4, ajv@~6.12.6: + version "6.12.6" + resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.12.6.tgz#baf5a62e802b07d977034586f8c3baf5adf26df4" + integrity sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g== + dependencies: + fast-deep-equal "^3.1.1" + fast-json-stable-stringify "^2.0.0" + json-schema-traverse "^0.4.1" + uri-js "^4.2.2" + +ansi-regex@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-5.0.1.tgz#082cb2c89c9fe8659a311a53bd6a4dc5301db304" + integrity sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ== + +ansi-regex@^6.0.1: + version "6.0.1" + resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-6.0.1.tgz#3183e38fae9a65d7cb5e53945cd5897d0260a06a" + integrity sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA== + +ansi-styles@^3.2.1: + version "3.2.1" + resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-3.2.1.tgz#41fbb20243e50b12be0f04b8dedbf07520ce841d" + integrity sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA== + dependencies: + color-convert "^1.9.0" + +ansi-styles@^4.0.0, ansi-styles@^4.1.0: + version "4.3.0" + resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-4.3.0.tgz#edd803628ae71c04c85ae7a0906edad34b648937" + integrity sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg== + dependencies: + color-convert "^2.0.1" + +ansi-styles@^5.0.0: + version "5.2.0" + resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-5.2.0.tgz#07449690ad45777d1924ac2abb2fc8895dba836b" + integrity sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA== + +ansi-styles@^6.1.0: + version "6.2.1" + resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-6.2.1.tgz#0e62320cf99c21afff3b3012192546aacbfb05c5" + integrity sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug== + +any-promise@^1.0.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/any-promise/-/any-promise-1.3.0.tgz#abc6afeedcea52e809cdc0376aed3ce39635d17f" + integrity sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A== + +anymatch@~3.1.2: + version "3.1.3" + resolved "https://registry.yarnpkg.com/anymatch/-/anymatch-3.1.3.tgz#790c58b19ba1720a84205b57c618d5ad8524973e" + integrity sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw== + dependencies: + normalize-path "^3.0.0" + picomatch "^2.0.4" + +arg@^5.0.2: + version "5.0.2" + resolved "https://registry.yarnpkg.com/arg/-/arg-5.0.2.tgz#c81433cc427c92c4dcf4865142dbca6f15acd59c" + integrity sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg== + +argparse@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/argparse/-/argparse-2.0.1.tgz#246f50f3ca78a3240f6c997e8a9bd1eac49e4b38" + integrity sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q== + +argparse@~1.0.9: + version "1.0.10" + resolved "https://registry.yarnpkg.com/argparse/-/argparse-1.0.10.tgz#bcd6791ea5ae09725e17e5ad988134cd40b3d911" + integrity sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg== + dependencies: + sprintf-js "~1.0.2" + +array-union@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/array-union/-/array-union-2.1.0.tgz#b798420adbeb1de828d84acd8a2e23d3efe85e8d" + integrity sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw== + +asn1.js@^5.2.0: + version "5.4.1" + resolved "https://registry.yarnpkg.com/asn1.js/-/asn1.js-5.4.1.tgz#11a980b84ebb91781ce35b0fdc2ee294e3783f07" + integrity sha512-+I//4cYPccV8LdmBLiX8CYvf9Sp3vQsrqu2QNXRcrbiWvcx/UdlFiqUJJzxRQxgsZmvhXhn4cSKeSmoFjVdupA== + dependencies: + bn.js "^4.0.0" + inherits "^2.0.1" + minimalistic-assert "^1.0.0" + safer-buffer "^2.1.0" + +assert@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/assert/-/assert-2.1.0.tgz#6d92a238d05dc02e7427c881fb8be81c8448b2dd" + integrity sha512-eLHpSK/Y4nhMJ07gDaAzoX/XAKS8PSaojml3M0DM4JpV1LAi5JOJ/p6H/XWrl8L+DzVEvVCW1z3vWAaB9oTsQw== + dependencies: + call-bind "^1.0.2" + is-nan "^1.3.2" + object-is "^1.1.5" + object.assign "^4.1.4" + util "^0.12.5" + +assertion-error@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/assertion-error/-/assertion-error-1.1.0.tgz#e60b6b0e8f301bd97e5375215bda406c85118c0b" + integrity sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw== + +async@^3.2.3: + version "3.2.5" + resolved "https://registry.yarnpkg.com/async/-/async-3.2.5.tgz#ebd52a8fdaf7a2289a24df399f8d8485c8a46b66" + integrity sha512-baNZyqaaLhyLVKm/DlvdW051MSgO6b8eVfIezl9E5PqWxFgzLm/wQntEW4zOytVburDEr0JlALEpdOFwvErLsg== + +autoprefixer@^10.4.17: + version "10.4.17" + resolved "https://registry.yarnpkg.com/autoprefixer/-/autoprefixer-10.4.17.tgz#35cd5695cbbe82f536a50fa025d561b01fdec8be" + integrity sha512-/cpVNRLSfhOtcGflT13P2794gVSgmPgTR+erw5ifnMLZb0UnSlkK4tquLmkd3BhA+nLo5tX8Cu0upUsGKvKbmg== + dependencies: + browserslist "^4.22.2" + caniuse-lite "^1.0.30001578" + fraction.js "^4.3.7" + normalize-range "^0.1.2" + picocolors "^1.0.0" + postcss-value-parser "^4.2.0" + +available-typed-arrays@^1.0.5: + version "1.0.5" + resolved "https://registry.yarnpkg.com/available-typed-arrays/-/available-typed-arrays-1.0.5.tgz#92f95616501069d07d10edb2fc37d3e1c65123b7" + integrity sha512-DMD0KiN46eipeziST1LPP/STfDU0sufISXmjSgvVsoU2tqxctQeASejWcfNtxYKqETM1UxQ8sp2OrSBWpHY6sw== + +balanced-match@^1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.2.tgz#e83e3a7e3f300b34cb9d87f615fa0cbf357690ee" + integrity sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw== + +base64-js@^1.3.1: + version "1.5.1" + resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.5.1.tgz#1b1b440160a5bf7ad40b650f095963481903930a" + integrity sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA== + +binary-extensions@^2.0.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-2.2.0.tgz#75f502eeaf9ffde42fc98829645be4ea76bd9e2d" + integrity sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA== + +bn.js@^4.0.0, bn.js@^4.1.0, bn.js@^4.11.9: + version "4.12.0" + resolved "https://registry.yarnpkg.com/bn.js/-/bn.js-4.12.0.tgz#775b3f278efbb9718eec7361f483fb36fbbfea88" + integrity sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA== + +bn.js@^5.0.0, bn.js@^5.2.1: + version "5.2.1" + resolved "https://registry.yarnpkg.com/bn.js/-/bn.js-5.2.1.tgz#0bc527a6a0d18d0aa8d5b0538ce4a77dccfa7b70" + integrity sha512-eXRvHzWyYPBuB4NBy0cmYQjGitUrtqwbvlzP3G6VFnNRbsZQIxQ10PbKKHt8gZ/HW/D/747aDl+QkDqg3KQLMQ== + +boolbase@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/boolbase/-/boolbase-1.0.0.tgz#68dff5fbe60c51eb37725ea9e3ed310dcc1e776e" + integrity sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww== + +brace-expansion@^1.1.7: + version "1.1.11" + resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.11.tgz#3c7fcbf529d87226f3d2f52b966ff5271eb441dd" + integrity sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA== + dependencies: + balanced-match "^1.0.0" + concat-map "0.0.1" + +brace-expansion@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-2.0.1.tgz#1edc459e0f0c548486ecf9fc99f2221364b9a0ae" + integrity sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA== + dependencies: + balanced-match "^1.0.0" + +braces@^3.0.2, braces@~3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/braces/-/braces-3.0.2.tgz#3454e1a462ee8d599e236df336cd9ea4f8afe107" + integrity sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A== + dependencies: + fill-range "^7.0.1" + +brorand@^1.0.1, brorand@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/brorand/-/brorand-1.1.0.tgz#12c25efe40a45e3c323eb8675a0a0ce57b22371f" + integrity sha512-cKV8tMCEpQs4hK/ik71d6LrPOnpkpGBR0wzxqr68g2m/LB2GxVYQroAjMJZRVM1Y4BCjCKc3vAamxSzOY2RP+w== + +browser-resolve@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/browser-resolve/-/browser-resolve-2.0.0.tgz#99b7304cb392f8d73dba741bb2d7da28c6d7842b" + integrity sha512-7sWsQlYL2rGLy2IWm8WL8DCTJvYLc/qlOnsakDac87SOoCd16WLsaAMdCiAqsTNHIe+SXfaqyxyo6THoWqs8WQ== + dependencies: + resolve "^1.17.0" + +browserify-aes@^1.0.0, browserify-aes@^1.0.4: + version "1.2.0" + resolved "https://registry.yarnpkg.com/browserify-aes/-/browserify-aes-1.2.0.tgz#326734642f403dabc3003209853bb70ad428ef48" + integrity sha512-+7CHXqGuspUn/Sl5aO7Ea0xWGAtETPXNSAjHo48JfLdPWcMng33Xe4znFvQweqc/uzk5zSOI3H52CYnjCfb5hA== + dependencies: + buffer-xor "^1.0.3" + cipher-base "^1.0.0" + create-hash "^1.1.0" + evp_bytestokey "^1.0.3" + inherits "^2.0.1" + safe-buffer "^5.0.1" + +browserify-cipher@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/browserify-cipher/-/browserify-cipher-1.0.1.tgz#8d6474c1b870bfdabcd3bcfcc1934a10e94f15f0" + integrity sha512-sPhkz0ARKbf4rRQt2hTpAHqn47X3llLkUGn+xEJzLjwY8LRs2p0v7ljvI5EyoRO/mexrNunNECisZs+gw2zz1w== + dependencies: + browserify-aes "^1.0.4" + browserify-des "^1.0.0" + evp_bytestokey "^1.0.0" + +browserify-des@^1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/browserify-des/-/browserify-des-1.0.2.tgz#3af4f1f59839403572f1c66204375f7a7f703e9c" + integrity sha512-BioO1xf3hFwz4kc6iBhI3ieDFompMhrMlnDFC4/0/vd5MokpuAc3R+LYbwTA9A5Yc9pq9UYPqffKpW2ObuwX5A== + dependencies: + cipher-base "^1.0.1" + des.js "^1.0.0" + inherits "^2.0.1" + safe-buffer "^5.1.2" + +browserify-rsa@^4.0.0, browserify-rsa@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/browserify-rsa/-/browserify-rsa-4.1.0.tgz#b2fd06b5b75ae297f7ce2dc651f918f5be158c8d" + integrity sha512-AdEER0Hkspgno2aR97SAf6vi0y0k8NuOpGnVH3O99rcA5Q6sh8QxcngtHuJ6uXwnfAXNM4Gn1Gb7/MV1+Ymbog== + dependencies: + bn.js "^5.0.0" + randombytes "^2.0.1" + +browserify-sign@^4.0.0: + version "4.2.2" + resolved "https://registry.yarnpkg.com/browserify-sign/-/browserify-sign-4.2.2.tgz#e78d4b69816d6e3dd1c747e64e9947f9ad79bc7e" + integrity sha512-1rudGyeYY42Dk6texmv7c4VcQ0EsvVbLwZkA+AQB7SxvXxmcD93jcHie8bzecJ+ChDlmAm2Qyu0+Ccg5uhZXCg== + dependencies: + bn.js "^5.2.1" + browserify-rsa "^4.1.0" + create-hash "^1.2.0" + create-hmac "^1.1.7" + elliptic "^6.5.4" + inherits "^2.0.4" + parse-asn1 "^5.1.6" + readable-stream "^3.6.2" + safe-buffer "^5.2.1" + +browserify-zlib@^0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/browserify-zlib/-/browserify-zlib-0.2.0.tgz#2869459d9aa3be245fe8fe2ca1f46e2e7f54d73f" + integrity sha512-Z942RysHXmJrhqk88FmKBVq/v5tqmSkDz7p54G/MGyjMnCFFnC79XWNbg+Vta8W6Wb2qtSZTSxIGkJrRpCFEiA== + dependencies: + pako "~1.0.5" + +browserslist@^4.0.0, browserslist@^4.22.2: + version "4.22.3" + resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.22.3.tgz#299d11b7e947a6b843981392721169e27d60c5a6" + integrity sha512-UAp55yfwNv0klWNapjs/ktHoguxuQNGnOzxYmfnXIS+8AsRDZkSDxg7R1AX3GKzn078SBI5dzwzj/Yx0Or0e3A== + dependencies: + caniuse-lite "^1.0.30001580" + electron-to-chromium "^1.4.648" + node-releases "^2.0.14" + update-browserslist-db "^1.0.13" + +buffer-from@^1.0.0: + version "1.1.2" + resolved "https://registry.yarnpkg.com/buffer-from/-/buffer-from-1.1.2.tgz#2b146a6fd72e80b4f55d255f35ed59a3a9a41bd5" + integrity sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ== + +"buffer-polyfill@npm:buffer@^6.0.3": + version "6.0.3" + resolved "https://registry.yarnpkg.com/buffer/-/buffer-6.0.3.tgz#2ace578459cc8fbe2a70aaa8f52ee63b6a74c6c6" + integrity sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA== + dependencies: + base64-js "^1.3.1" + ieee754 "^1.2.1" + +buffer-xor@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/buffer-xor/-/buffer-xor-1.0.3.tgz#26e61ed1422fb70dd42e6e36729ed51d855fe8d9" + integrity sha512-571s0T7nZWK6vB67HI5dyUF7wXiNcfaPPPTl6zYCNApANjIvYJTg7hlud/+cJpdAhS7dVzqMLmfhfHR3rAcOjQ== + +buffer@^5.7.1: + version "5.7.1" + resolved "https://registry.yarnpkg.com/buffer/-/buffer-5.7.1.tgz#ba62e7c13133053582197160851a8f648e99eed0" + integrity sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ== + dependencies: + base64-js "^1.3.1" + ieee754 "^1.1.13" + +builtin-modules@^3.3.0: + version "3.3.0" + resolved "https://registry.yarnpkg.com/builtin-modules/-/builtin-modules-3.3.0.tgz#cae62812b89801e9656336e46223e030386be7b6" + integrity sha512-zhaCDicdLuWN5UbN5IMnFqNMhNfo919sH85y2/ea+5Yg9TsTkeZxpL+JLbp6cgYFS4sRLp3YV4S6yDuqVWHYOw== + +builtin-status-codes@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/builtin-status-codes/-/builtin-status-codes-3.0.0.tgz#85982878e21b98e1c66425e03d0174788f569ee8" + integrity sha512-HpGFw18DgFWlncDfjTa2rcQ4W88O1mC8e8yZ2AvQY5KDaktSTwo+KRf6nHK6FRI5FyRyb/5T6+TSxfP7QyGsmQ== + +bundle-name@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/bundle-name/-/bundle-name-4.1.0.tgz#f3b96b34160d6431a19d7688135af7cfb8797889" + integrity sha512-tjwM5exMg6BGRI+kNmTntNsvdZS1X8BFYS6tnJ2hdH0kVxM6/eVZ2xy+FqStSWvYmtfFMDLIxurorHwDKfDz5Q== + dependencies: + run-applescript "^7.0.0" + +cac@^6.7.14: + version "6.7.14" + resolved "https://registry.yarnpkg.com/cac/-/cac-6.7.14.tgz#804e1e6f506ee363cb0e3ccbb09cad5dd9870959" + integrity sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ== + +call-bind@^1.0.0, call-bind@^1.0.2, call-bind@^1.0.4, call-bind@^1.0.5: + version "1.0.5" + resolved "https://registry.yarnpkg.com/call-bind/-/call-bind-1.0.5.tgz#6fa2b7845ce0ea49bf4d8b9ef64727a2c2e2e513" + integrity sha512-C3nQxfFZxFRVoJoGKKI8y3MOEo129NQ+FgQ08iye+Mk4zNZZGdjfs06bVTr+DBSlA66Q2VEcMki/cUCP4SercQ== + dependencies: + function-bind "^1.1.2" + get-intrinsic "^1.2.1" + set-function-length "^1.1.1" + +callsites@^3.0.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/callsites/-/callsites-3.1.0.tgz#b3630abd8943432f54b3f0519238e33cd7df2f73" + integrity sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ== + +camel-case@^4.1.2: + version "4.1.2" + resolved "https://registry.yarnpkg.com/camel-case/-/camel-case-4.1.2.tgz#9728072a954f805228225a6deea6b38461e1bd5a" + integrity sha512-gxGWBrTT1JuMx6R+o5PTXMmUnhnVzLQ9SNutD4YqKtI6ap897t3tKECYla6gCWEkplXnlNybEkZg9GEGxKFCgw== + dependencies: + pascal-case "^3.1.2" + tslib "^2.0.3" + +camelcase-css@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/camelcase-css/-/camelcase-css-2.0.1.tgz#ee978f6947914cc30c6b44741b6ed1df7f043fd5" + integrity sha512-QOSvevhslijgYwRx6Rv7zKdMF8lbRmx+uQGx2+vDc+KI/eBnsy9kit5aj23AgGu3pa4t9AgwbnXWqS+iOY+2aA== + +caniuse-api@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/caniuse-api/-/caniuse-api-3.0.0.tgz#5e4d90e2274961d46291997df599e3ed008ee4c0" + integrity sha512-bsTwuIg/BZZK/vreVTYYbSWoe2F+71P7K5QGEX+pT250DZbfU1MQ5prOKpPR+LL6uWKK3KMwMCAS74QB3Um1uw== + dependencies: + browserslist "^4.0.0" + caniuse-lite "^1.0.0" + lodash.memoize "^4.1.2" + lodash.uniq "^4.5.0" + +caniuse-lite@^1.0.0: + version "1.0.30001583" + resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001583.tgz#abb2970cc370801dc7e27bf290509dc132cfa390" + integrity sha512-acWTYaha8xfhA/Du/z4sNZjHUWjkiuoAi2LM+T/aL+kemKQgPT1xBb/YKjlQ0Qo8gvbHsGNplrEJ+9G3gL7i4Q== + +caniuse-lite@^1.0.30001578, caniuse-lite@^1.0.30001580: + version "1.0.30001582" + resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001582.tgz#db3070547ce0b48d9f44a509b86c4a02ba5d9055" + integrity sha512-vsJG3V5vgfduaQGVxL53uSX/HUzxyr2eA8xCo36OLal7sRcSZbibJtLeh0qja4sFOr/QQGt4opB4tOy+eOgAxg== + +chai@^4.3.10: + version "4.4.1" + resolved "https://registry.yarnpkg.com/chai/-/chai-4.4.1.tgz#3603fa6eba35425b0f2ac91a009fe924106e50d1" + integrity sha512-13sOfMv2+DWduEU+/xbun3LScLoqN17nBeTLUsmDfKdoiC1fr0n9PU4guu4AhRcOVFk/sW8LyZWHuhWtQZiF+g== + dependencies: + assertion-error "^1.1.0" + check-error "^1.0.3" + deep-eql "^4.1.3" + get-func-name "^2.0.2" + loupe "^2.3.6" + pathval "^1.1.1" + type-detect "^4.0.8" + +chalk@^2.4.2: + version "2.4.2" + resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.4.2.tgz#cd42541677a54333cf541a49108c1432b44c9424" + integrity sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ== + dependencies: + ansi-styles "^3.2.1" + escape-string-regexp "^1.0.5" + supports-color "^5.3.0" + +chalk@^4.0.0, chalk@^4.0.2: + version "4.1.2" + resolved "https://registry.yarnpkg.com/chalk/-/chalk-4.1.2.tgz#aac4e2b7734a740867aeb16bf02aad556a1e7a01" + integrity sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA== + dependencies: + ansi-styles "^4.1.0" + supports-color "^7.1.0" + +check-error@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/check-error/-/check-error-1.0.3.tgz#a6502e4312a7ee969f646e83bb3ddd56281bd694" + integrity sha512-iKEoDYaRmd1mxM90a2OEfWhjsjPpYPuQ+lMYsoxB126+t8fw7ySEO48nmDg5COTjxDI65/Y2OWpeEHk3ZOe8zg== + dependencies: + get-func-name "^2.0.2" + +chokidar@^3.5.3: + version "3.5.3" + resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-3.5.3.tgz#1cf37c8707b932bd1af1ae22c0432e2acd1903bd" + integrity sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw== + dependencies: + anymatch "~3.1.2" + braces "~3.0.2" + glob-parent "~5.1.2" + is-binary-path "~2.1.0" + is-glob "~4.0.1" + normalize-path "~3.0.0" + readdirp "~3.6.0" + optionalDependencies: + fsevents "~2.3.2" + +ci-info@^3.8.0: + version "3.9.0" + resolved "https://registry.yarnpkg.com/ci-info/-/ci-info-3.9.0.tgz#4279a62028a7b1f262f3473fc9605f5e218c59b4" + integrity sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ== + +cipher-base@^1.0.0, cipher-base@^1.0.1, cipher-base@^1.0.3: + version "1.0.4" + resolved "https://registry.yarnpkg.com/cipher-base/-/cipher-base-1.0.4.tgz#8760e4ecc272f4c363532f926d874aae2c1397de" + integrity sha512-Kkht5ye6ZGmwv40uUDZztayT2ThLQGfnj/T71N/XzeZeo3nf8foyW7zGTsPYkEya3m5f3cAypH+qe7YOrM1U2Q== + dependencies: + inherits "^2.0.1" + safe-buffer "^5.0.1" + +clean-css@^5.2.2: + version "5.3.3" + resolved "https://registry.yarnpkg.com/clean-css/-/clean-css-5.3.3.tgz#b330653cd3bd6b75009cc25c714cae7b93351ccd" + integrity sha512-D5J+kHaVb/wKSFcyyV75uCn8fiY4sV38XJoe4CUyGQ+mOU/fMVYUdH1hJC+CJQ5uY3EnW27SbJYS4X8BiLrAFg== + dependencies: + source-map "~0.6.0" + +clean-regexp@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/clean-regexp/-/clean-regexp-1.0.0.tgz#8df7c7aae51fd36874e8f8d05b9180bc11a3fed7" + integrity sha512-GfisEZEJvzKrmGWkvfhgzcz/BllN1USeqD2V6tg14OAOgaCD2Z/PUEuxnAZ/nPvmaHRG7a8y77p1T/IRQ4D1Hw== + dependencies: + escape-string-regexp "^1.0.5" + +code-block-writer@^11.0.3: + version "11.0.3" + resolved "https://registry.yarnpkg.com/code-block-writer/-/code-block-writer-11.0.3.tgz#9eec2993edfb79bfae845fbc093758c0a0b73b76" + integrity sha512-NiujjUFB4SwScJq2bwbYUtXbZhBSlY6vYzm++3Q6oC+U+injTqfPYFK8wS9COOmb2lueqp0ZRB4nK1VYeHgNyw== + +coi-serviceworker@^0.1.7: + version "0.1.7" + resolved "https://registry.yarnpkg.com/coi-serviceworker/-/coi-serviceworker-0.1.7.tgz#a105739386bf7101cdbaa32a167c576ca3376200" + integrity sha512-bjSUqEngCPOkErY2vbyWsaIGCNRODYzlNycaREVw5s12/C8SM+RnRUUeX6pZbTtov6C52ZLY/+tvHK+BDxuUuA== + +color-convert@^1.9.0: + version "1.9.3" + resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-1.9.3.tgz#bb71850690e1f136567de629d2d5471deda4c1e8" + integrity sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg== + dependencies: + color-name "1.1.3" + +color-convert@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-2.0.1.tgz#72d3a68d598c9bdb3af2ad1e84f21d896abd4de3" + integrity sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ== + dependencies: + color-name "~1.1.4" + +color-name@1.1.3: + version "1.1.3" + resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.3.tgz#a7d0558bd89c42f795dd42328f740831ca53bc25" + integrity sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw== + +color-name@~1.1.4: + version "1.1.4" + resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.4.tgz#c2a09a87acbde69543de6f63fa3995c826c536a2" + integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA== + +colord@^2.9.1: + version "2.9.3" + resolved "https://registry.yarnpkg.com/colord/-/colord-2.9.3.tgz#4f8ce919de456f1d5c1c368c307fe20f3e59fb43" + integrity sha512-jeC1axXpnb0/2nn/Y1LPuLdgXBLH7aDcHu4KEKfqw3CUhX7ZpfBSlPKyqXE6btIgEzfWtrX3/tyBCaCvXvMkOw== + +colorette@^2.0.16: + version "2.0.20" + resolved "https://registry.yarnpkg.com/colorette/-/colorette-2.0.20.tgz#9eb793e6833067f7235902fcd3b09917a000a95a" + integrity sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w== + +colors@~1.2.1: + version "1.2.5" + resolved "https://registry.yarnpkg.com/colors/-/colors-1.2.5.tgz#89c7ad9a374bc030df8013241f68136ed8835afc" + integrity sha512-erNRLao/Y3Fv54qUa0LBB+//Uf3YwMUmdJinN20yMXm9zdKKqH9wt7R9IIVZ+K7ShzfpLV/Zg8+VyrBJYB4lpg== + +comlink@^4.4.1: + version "4.4.1" + resolved "https://registry.yarnpkg.com/comlink/-/comlink-4.4.1.tgz#e568b8e86410b809e8600eb2cf40c189371ef981" + integrity sha512-+1dlx0aY5Jo1vHy/tSsIGpSkN4tS9rZSW8FIhG0JH/crs9wwweswIo/POr451r7bZww3hFbPAKnTpimzL/mm4Q== + +commander@^10.0.0: + version "10.0.1" + resolved "https://registry.yarnpkg.com/commander/-/commander-10.0.1.tgz#881ee46b4f77d1c1dccc5823433aa39b022cbe06" + integrity sha512-y4Mg2tXshplEbSGzx7amzPwKKOCGuoSRP/CjEdwwk0FOGlUbq6lKuoyDZTNZkmxHdJtp54hdfY/JUrdL7Xfdug== + +commander@^2.20.0: + version "2.20.3" + resolved "https://registry.yarnpkg.com/commander/-/commander-2.20.3.tgz#fd485e84c03eb4881c20722ba48035e8531aeb33" + integrity sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ== + +commander@^4.0.0: + version "4.1.1" + resolved "https://registry.yarnpkg.com/commander/-/commander-4.1.1.tgz#9fd602bd936294e9e9ef46a3f4d6964044b18068" + integrity sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA== + +commander@^7.2.0: + version "7.2.0" + resolved "https://registry.yarnpkg.com/commander/-/commander-7.2.0.tgz#a36cb57d0b501ce108e4d20559a150a391d97ab7" + integrity sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw== + +commander@^8.3.0: + version "8.3.0" + resolved "https://registry.yarnpkg.com/commander/-/commander-8.3.0.tgz#4837ea1b2da67b9c616a67afbb0fafee567bca66" + integrity sha512-OkTL9umf+He2DZkUq8f8J9of7yL6RJKI24dVITBmNfZBmri9zYZQrKkuXiKhyfPSu8tUhnVBB1iKXevvnlR4Ww== + +concat-map@0.0.1: + version "0.0.1" + resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" + integrity sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg== + +connect-history-api-fallback@^1.6.0: + version "1.6.0" + resolved "https://registry.yarnpkg.com/connect-history-api-fallback/-/connect-history-api-fallback-1.6.0.tgz#8b32089359308d111115d81cad3fceab888f97bc" + integrity sha512-e54B99q/OUoH64zYYRf3HBP5z24G38h5D3qXu23JGRoigpX5Ss4r9ZnDk3g0Z8uQC2x2lPaJ+UlWBc1ZWBWdLg== + +consola@^2.15.3: + version "2.15.3" + resolved "https://registry.yarnpkg.com/consola/-/consola-2.15.3.tgz#2e11f98d6a4be71ff72e0bdf07bd23e12cb61550" + integrity sha512-9vAdYbHj6x2fLKC4+oPH0kFzY/orMZyG2Aj+kNylHxKGJ/Ed4dpNyAQYwJOdqO4zdM7XpVHmyejQDcQHrnuXbw== + +console-browserify@^1.1.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/console-browserify/-/console-browserify-1.2.0.tgz#67063cef57ceb6cf4993a2ab3a55840ae8c49336" + integrity sha512-ZMkYO/LkF17QvCPqM0gxw8yUzigAOZOSWSHg91FH6orS7vcEj5dVZTidN2fQ14yBSdg97RqhSNwLUXInd52OTA== + +constants-browserify@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/constants-browserify/-/constants-browserify-1.0.0.tgz#c20b96d8c617748aaf1c16021760cd27fcb8cb75" + integrity sha512-xFxOwqIzR/e1k1gLiWEophSCMqXcwVHIH7akf7b/vxcUeGunlj3hvZaaqxwHsTgn+IndtkQJgSztIDWeumWJDQ== + +create-ecdh@^4.0.0: + version "4.0.4" + resolved "https://registry.yarnpkg.com/create-ecdh/-/create-ecdh-4.0.4.tgz#d6e7f4bffa66736085a0762fd3a632684dabcc4e" + integrity sha512-mf+TCx8wWc9VpuxfP2ht0iSISLZnt0JgWlrOKZiNqyUZWnjIaCIVNQArMHnCZKfEYRg6IM7A+NeJoN8gf/Ws0A== + dependencies: + bn.js "^4.1.0" + elliptic "^6.5.3" + +create-hash@^1.1.0, create-hash@^1.1.2, create-hash@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/create-hash/-/create-hash-1.2.0.tgz#889078af11a63756bcfb59bd221996be3a9ef196" + integrity sha512-z00bCGNHDG8mHAkP7CtT1qVu+bFQUPjYq/4Iv3C3kWjTFV10zIjfSoeqXo9Asws8gwSHDGj/hl2u4OGIjapeCg== + dependencies: + cipher-base "^1.0.1" + inherits "^2.0.1" + md5.js "^1.3.4" + ripemd160 "^2.0.1" + sha.js "^2.4.0" + +create-hmac@^1.1.0, create-hmac@^1.1.4, create-hmac@^1.1.7: + version "1.1.7" + resolved "https://registry.yarnpkg.com/create-hmac/-/create-hmac-1.1.7.tgz#69170c78b3ab957147b2b8b04572e47ead2243ff" + integrity sha512-MJG9liiZ+ogc4TzUwuvbER1JRdgvUFSB5+VR/g5h82fGaIRWMWddtKBHi7/sVhfjQZ6SehlyhvQYrcYkaUIpLg== + dependencies: + cipher-base "^1.0.3" + create-hash "^1.1.0" + inherits "^2.0.1" + ripemd160 "^2.0.0" + safe-buffer "^5.0.1" + sha.js "^2.4.8" + +create-require@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/create-require/-/create-require-1.1.1.tgz#c1d7e8f1e5f6cfc9ff65f9cd352d37348756c333" + integrity sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ== + +cross-spawn@^7.0.0, cross-spawn@^7.0.2, cross-spawn@^7.0.3: + version "7.0.3" + resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.3.tgz#f73a85b9d5d41d045551c177e2882d4ac85728a6" + integrity sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w== + dependencies: + path-key "^3.1.0" + shebang-command "^2.0.0" + which "^2.0.1" + +crypto-browserify@^3.11.0: + version "3.12.0" + resolved "https://registry.yarnpkg.com/crypto-browserify/-/crypto-browserify-3.12.0.tgz#396cf9f3137f03e4b8e532c58f698254e00f80ec" + integrity sha512-fz4spIh+znjO2VjL+IdhEpRJ3YN6sMzITSBijk6FK2UvTqruSQW+/cCZTSNsMiZNvUeq0CqurF+dAbyiGOY6Wg== + dependencies: + browserify-cipher "^1.0.0" + browserify-sign "^4.0.0" + create-ecdh "^4.0.0" + create-hash "^1.1.0" + create-hmac "^1.1.0" + diffie-hellman "^5.0.0" + inherits "^2.0.1" + pbkdf2 "^3.0.3" + public-encrypt "^4.0.0" + randombytes "^2.0.0" + randomfill "^1.0.3" + +css-declaration-sorter@^7.1.1: + version "7.1.1" + resolved "https://registry.yarnpkg.com/css-declaration-sorter/-/css-declaration-sorter-7.1.1.tgz#9796bcc257b4647c39993bda8d431ce32b666f80" + integrity sha512-dZ3bVTEEc1vxr3Bek9vGwfB5Z6ESPULhcRvO472mfjVnj8jRcTnKO8/JTczlvxM10Myb+wBM++1MtdO76eWcaQ== + +css-select@^4.2.1: + version "4.3.0" + resolved "https://registry.yarnpkg.com/css-select/-/css-select-4.3.0.tgz#db7129b2846662fd8628cfc496abb2b59e41529b" + integrity sha512-wPpOYtnsVontu2mODhA19JrqWxNsfdatRKd64kmpRbQgh1KtItko5sTnEpPdpSaJszTOhEMlF/RPz28qj4HqhQ== + dependencies: + boolbase "^1.0.0" + css-what "^6.0.1" + domhandler "^4.3.1" + domutils "^2.8.0" + nth-check "^2.0.1" + +css-select@^5.1.0: + version "5.1.0" + resolved "https://registry.yarnpkg.com/css-select/-/css-select-5.1.0.tgz#b8ebd6554c3637ccc76688804ad3f6a6fdaea8a6" + integrity sha512-nwoRF1rvRRnnCqqY7updORDsuqKzqYJ28+oSMaJMMgOauh3fvwHqMS7EZpIPqK8GL+g9mKxF1vP/ZjSeNjEVHg== + dependencies: + boolbase "^1.0.0" + css-what "^6.1.0" + domhandler "^5.0.2" + domutils "^3.0.1" + nth-check "^2.0.1" + +css-tree@^2.3.1: + version "2.3.1" + resolved "https://registry.yarnpkg.com/css-tree/-/css-tree-2.3.1.tgz#10264ce1e5442e8572fc82fbe490644ff54b5c20" + integrity sha512-6Fv1DV/TYw//QF5IzQdqsNDjx/wc8TrMBZsqjL9eW01tWb7R7k/mq+/VXfJCl7SoD5emsJop9cOByJZfs8hYIw== + dependencies: + mdn-data "2.0.30" + source-map-js "^1.0.1" + +css-tree@~2.2.0: + version "2.2.1" + resolved "https://registry.yarnpkg.com/css-tree/-/css-tree-2.2.1.tgz#36115d382d60afd271e377f9c5f67d02bd48c032" + integrity sha512-OA0mILzGc1kCOCSJerOeqDxDQ4HOh+G8NbOJFOTgOCzpw7fCBubk0fEyxp8AgOL/jvLgYA/uV0cMbe43ElF1JA== + dependencies: + mdn-data "2.0.28" + source-map-js "^1.0.1" + +css-what@^6.0.1, css-what@^6.1.0: + version "6.1.0" + resolved "https://registry.yarnpkg.com/css-what/-/css-what-6.1.0.tgz#fb5effcf76f1ddea2c81bdfaa4de44e79bac70f4" + integrity sha512-HTUrgRJ7r4dsZKU6GjmpfRK1O76h97Z8MfS1G0FozR+oF2kG6Vfe8JE6zwrkbxigziPHinCJ+gCPjA9EaBDtRw== + +cssesc@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/cssesc/-/cssesc-3.0.0.tgz#37741919903b868565e1c09ea747445cd18983ee" + integrity sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg== + +cssnano-preset-default@^6.0.3: + version "6.0.3" + resolved "https://registry.yarnpkg.com/cssnano-preset-default/-/cssnano-preset-default-6.0.3.tgz#b4ce755974f4dc8d3d09ac13bb6281cce3ced45e" + integrity sha512-4y3H370aZCkT9Ev8P4SO4bZbt+AExeKhh8wTbms/X7OLDo5E7AYUUy6YPxa/uF5Grf+AJwNcCnxKhZynJ6luBA== + dependencies: + css-declaration-sorter "^7.1.1" + cssnano-utils "^4.0.1" + postcss-calc "^9.0.1" + postcss-colormin "^6.0.2" + postcss-convert-values "^6.0.2" + postcss-discard-comments "^6.0.1" + postcss-discard-duplicates "^6.0.1" + postcss-discard-empty "^6.0.1" + postcss-discard-overridden "^6.0.1" + postcss-merge-longhand "^6.0.2" + postcss-merge-rules "^6.0.3" + postcss-minify-font-values "^6.0.1" + postcss-minify-gradients "^6.0.1" + postcss-minify-params "^6.0.2" + postcss-minify-selectors "^6.0.2" + postcss-normalize-charset "^6.0.1" + postcss-normalize-display-values "^6.0.1" + postcss-normalize-positions "^6.0.1" + postcss-normalize-repeat-style "^6.0.1" + postcss-normalize-string "^6.0.1" + postcss-normalize-timing-functions "^6.0.1" + postcss-normalize-unicode "^6.0.2" + postcss-normalize-url "^6.0.1" + postcss-normalize-whitespace "^6.0.1" + postcss-ordered-values "^6.0.1" + postcss-reduce-initial "^6.0.2" + postcss-reduce-transforms "^6.0.1" + postcss-svgo "^6.0.2" + postcss-unique-selectors "^6.0.2" + +cssnano-utils@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/cssnano-utils/-/cssnano-utils-4.0.1.tgz#fd18b42f95938bf55ab47967705355d6047bf1da" + integrity sha512-6qQuYDqsGoiXssZ3zct6dcMxiqfT6epy7x4R0TQJadd4LWO3sPR6JH6ZByOvVLoZ6EdwPGgd7+DR1EmX3tiXQQ== + +cssnano@^6.0.3: + version "6.0.3" + resolved "https://registry.yarnpkg.com/cssnano/-/cssnano-6.0.3.tgz#46db972da71aa159437287fb4c6bc9c5d3cc5d93" + integrity sha512-MRq4CIj8pnyZpcI2qs6wswoYoDD1t0aL28n+41c1Ukcpm56m1h6mCexIHBGjfZfnTqtGSSCP4/fB1ovxgjBOiw== + dependencies: + cssnano-preset-default "^6.0.3" + lilconfig "^3.0.0" + +csso@^5.0.5: + version "5.0.5" + resolved "https://registry.yarnpkg.com/csso/-/csso-5.0.5.tgz#f9b7fe6cc6ac0b7d90781bb16d5e9874303e2ca6" + integrity sha512-0LrrStPOdJj+SPCCrGhzryycLjwcgUSHBtxNA8aIDxf0GLsRh1cKYhB00Gd1lDOS4yGH69+SNn13+TWbVHETFQ== + dependencies: + css-tree "~2.2.0" + +debug@^4.3.1, debug@^4.3.2, debug@^4.3.4: + version "4.3.4" + resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.4.tgz#1319f6579357f2338d3337d2cdd4914bb5dcc865" + integrity sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ== + dependencies: + ms "2.1.2" + +deep-eql@^4.1.3: + version "4.1.3" + resolved "https://registry.yarnpkg.com/deep-eql/-/deep-eql-4.1.3.tgz#7c7775513092f7df98d8df9996dd085eb668cc6d" + integrity sha512-WaEtAOpRA1MQ0eohqZjpGD8zdI0Ovsm8mmFhaDN8dvDZzyoUMcYDnf5Y6iu7HTXxf8JDS23qWa4a+hKCDyOPzw== + dependencies: + type-detect "^4.0.0" + +deep-is@^0.1.3: + version "0.1.4" + resolved "https://registry.yarnpkg.com/deep-is/-/deep-is-0.1.4.tgz#a6f2dce612fadd2ef1f519b73551f17e85199831" + integrity sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ== + +default-browser-id@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/default-browser-id/-/default-browser-id-5.0.0.tgz#a1d98bf960c15082d8a3fa69e83150ccccc3af26" + integrity sha512-A6p/pu/6fyBcA1TRz/GqWYPViplrftcW2gZC9q79ngNCKAeR/X3gcEdXQHl4KNXV+3wgIJ1CPkJQ3IHM6lcsyA== + +default-browser@^5.2.1: + version "5.2.1" + resolved "https://registry.yarnpkg.com/default-browser/-/default-browser-5.2.1.tgz#7b7ba61204ff3e425b556869ae6d3e9d9f1712cf" + integrity sha512-WY/3TUME0x3KPYdRRxEJJvXRHV4PyPoUsxtZa78lwItwRQRHhd2U9xOscaT/YTf8uCXIAjeJOFBVEh/7FtD8Xg== + dependencies: + bundle-name "^4.1.0" + default-browser-id "^5.0.0" + +define-data-property@^1.0.1, define-data-property@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/define-data-property/-/define-data-property-1.1.1.tgz#c35f7cd0ab09883480d12ac5cb213715587800b3" + integrity sha512-E7uGkTzkk1d0ByLeSc6ZsFS79Axg+m1P/VsgYsxHgiuc3tFSj+MjMIwe90FC4lOAZzNBdY7kkO2P2wKdsQ1vgQ== + dependencies: + get-intrinsic "^1.2.1" + gopd "^1.0.1" + has-property-descriptors "^1.0.0" + +define-lazy-prop@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/define-lazy-prop/-/define-lazy-prop-3.0.0.tgz#dbb19adfb746d7fc6d734a06b72f4a00d021255f" + integrity sha512-N+MeXYoqr3pOgn8xfyRPREN7gHakLYjhsHhWGT3fWAiL4IkAt0iDw14QiiEm2bE30c5XX5q0FtAA3CK5f9/BUg== + +define-properties@^1.1.3, define-properties@^1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/define-properties/-/define-properties-1.2.1.tgz#10781cc616eb951a80a034bafcaa7377f6af2b6c" + integrity sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg== + dependencies: + define-data-property "^1.0.1" + has-property-descriptors "^1.0.0" + object-keys "^1.1.1" + +des.js@^1.0.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/des.js/-/des.js-1.1.0.tgz#1d37f5766f3bbff4ee9638e871a8768c173b81da" + integrity sha512-r17GxjhUCjSRy8aiJpr8/UadFIzMzJGexI3Nmz4ADi9LYSFx4gTBp80+NaX/YsXWWLhpZ7v/v/ubEc/bCNfKwg== + dependencies: + inherits "^2.0.1" + minimalistic-assert "^1.0.0" + +didyoumean@^1.2.2: + version "1.2.2" + resolved "https://registry.yarnpkg.com/didyoumean/-/didyoumean-1.2.2.tgz#989346ffe9e839b4555ecf5666edea0d3e8ad037" + integrity sha512-gxtyfqMg7GKyhQmb056K7M3xszy/myH8w+B4RT+QXBQsvAOdc3XymqDDPHx1BgPgsdAA5SIifona89YtRATDzw== + +diff-sequences@^29.6.3: + version "29.6.3" + resolved "https://registry.yarnpkg.com/diff-sequences/-/diff-sequences-29.6.3.tgz#4deaf894d11407c51efc8418012f9e70b84ea921" + integrity sha512-EjePK1srD3P08o2j4f0ExnylqRs5B9tJjcp9t1krH2qRi8CCdsYfwe9JgSLurFBWwq4uOlipzfk5fHNvwFKr8Q== + +diffie-hellman@^5.0.0: + version "5.0.3" + resolved "https://registry.yarnpkg.com/diffie-hellman/-/diffie-hellman-5.0.3.tgz#40e8ee98f55a2149607146921c63e1ae5f3d2875" + integrity sha512-kqag/Nl+f3GwyK25fhUMYj81BUOrZ9IuJsjIcDE5icNM9FJHAVm3VcUDxdLPoQtTuUylWm6ZIknYJwwaPxsUzg== + dependencies: + bn.js "^4.1.0" + miller-rabin "^4.0.0" + randombytes "^2.0.0" + +dir-glob@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/dir-glob/-/dir-glob-3.0.1.tgz#56dbf73d992a4a93ba1584f4534063fd2e41717f" + integrity sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA== + dependencies: + path-type "^4.0.0" + +dlv@^1.1.3: + version "1.1.3" + resolved "https://registry.yarnpkg.com/dlv/-/dlv-1.1.3.tgz#5c198a8a11453596e751494d49874bc7732f2e79" + integrity sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA== + +doctrine@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/doctrine/-/doctrine-3.0.0.tgz#addebead72a6574db783639dc87a121773973961" + integrity sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w== + dependencies: + esutils "^2.0.2" + +dom-serializer@^1.0.1: + version "1.4.1" + resolved "https://registry.yarnpkg.com/dom-serializer/-/dom-serializer-1.4.1.tgz#de5d41b1aea290215dc45a6dae8adcf1d32e2d30" + integrity sha512-VHwB3KfrcOOkelEG2ZOfxqLZdfkil8PtJi4P8N2MMXucZq2yLp75ClViUlOVwyoHEDjYU433Aq+5zWP61+RGag== + dependencies: + domelementtype "^2.0.1" + domhandler "^4.2.0" + entities "^2.0.0" + +dom-serializer@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/dom-serializer/-/dom-serializer-2.0.0.tgz#e41b802e1eedf9f6cae183ce5e622d789d7d8e53" + integrity sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg== + dependencies: + domelementtype "^2.3.0" + domhandler "^5.0.2" + entities "^4.2.0" + +domain-browser@^4.22.0: + version "4.23.0" + resolved "https://registry.yarnpkg.com/domain-browser/-/domain-browser-4.23.0.tgz#427ebb91efcb070f05cffdfb8a4e9a6c25f8c94b" + integrity sha512-ArzcM/II1wCCujdCNyQjXrAFwS4mrLh4C7DZWlaI8mdh7h3BfKdNd3bKXITfl2PT9FtfQqaGvhi1vPRQPimjGA== + +domelementtype@^2.0.1, domelementtype@^2.2.0, domelementtype@^2.3.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/domelementtype/-/domelementtype-2.3.0.tgz#5c45e8e869952626331d7aab326d01daf65d589d" + integrity sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw== + +domhandler@^4.2.0, domhandler@^4.3.1: + version "4.3.1" + resolved "https://registry.yarnpkg.com/domhandler/-/domhandler-4.3.1.tgz#8d792033416f59d68bc03a5aa7b018c1ca89279c" + integrity sha512-GrwoxYN+uWlzO8uhUXRl0P+kHE4GtVPfYzVLcUxPL7KNdHKj66vvlhiweIHqYYXWlw+T8iLMp42Lm67ghw4WMQ== + dependencies: + domelementtype "^2.2.0" + +domhandler@^5.0.2, domhandler@^5.0.3: + version "5.0.3" + resolved "https://registry.yarnpkg.com/domhandler/-/domhandler-5.0.3.tgz#cc385f7f751f1d1fc650c21374804254538c7d31" + integrity sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w== + dependencies: + domelementtype "^2.3.0" + +domutils@^2.8.0: + version "2.8.0" + resolved "https://registry.yarnpkg.com/domutils/-/domutils-2.8.0.tgz#4437def5db6e2d1f5d6ee859bd95ca7d02048135" + integrity sha512-w96Cjofp72M5IIhpjgobBimYEfoPjx1Vx0BSX9P30WBdZW2WIKU0T1Bd0kz2eNZ9ikjKgHbEyKx8BB6H1L3h3A== + dependencies: + dom-serializer "^1.0.1" + domelementtype "^2.2.0" + domhandler "^4.2.0" + +domutils@^3.0.1: + version "3.1.0" + resolved "https://registry.yarnpkg.com/domutils/-/domutils-3.1.0.tgz#c47f551278d3dc4b0b1ab8cbb42d751a6f0d824e" + integrity sha512-H78uMmQtI2AhgDJjWeQmHwJJ2bLPD3GMmO7Zja/ZZh84wkm+4ut+IUnUdRa8uCGX88DiVx1j6FRe1XfxEgjEZA== + dependencies: + dom-serializer "^2.0.0" + domelementtype "^2.3.0" + domhandler "^5.0.3" + +dot-case@^3.0.4: + version "3.0.4" + resolved "https://registry.yarnpkg.com/dot-case/-/dot-case-3.0.4.tgz#9b2b670d00a431667a8a75ba29cd1b98809ce751" + integrity sha512-Kv5nKlh6yRrdrGvxeJ2e5y2eRUpkUosIW4A2AS38zwSz27zu7ufDwQPi5Jhs3XAlGNetl3bmnGhQsMtkKJnj3w== + dependencies: + no-case "^3.0.4" + tslib "^2.0.3" + +dotenv-expand@^8.0.2: + version "8.0.3" + resolved "https://registry.yarnpkg.com/dotenv-expand/-/dotenv-expand-8.0.3.tgz#29016757455bcc748469c83a19b36aaf2b83dd6e" + integrity sha512-SErOMvge0ZUyWd5B0NXMQlDkN+8r+HhVUsxgOO7IoPDOdDRD2JjExpN6y3KnFR66jsJMwSn1pqIivhU5rcJiNg== + +dotenv@^16.0.0: + version "16.4.1" + resolved "https://registry.yarnpkg.com/dotenv/-/dotenv-16.4.1.tgz#1d9931f1d3e5d2959350d1250efab299561f7f11" + integrity sha512-CjA3y+Dr3FyFDOAMnxZEGtnW9KBR2M0JvvUtXNW+dYJL5ROWxP9DUHCwgFqpMk0OXCc0ljhaNTr2w/kutYIcHQ== + +eastasianwidth@^0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/eastasianwidth/-/eastasianwidth-0.2.0.tgz#696ce2ec0aa0e6ea93a397ffcf24aa7840c827cb" + integrity sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA== + +ejs@^3.1.6: + version "3.1.9" + resolved "https://registry.yarnpkg.com/ejs/-/ejs-3.1.9.tgz#03c9e8777fe12686a9effcef22303ca3d8eeb361" + integrity sha512-rC+QVNMJWv+MtPgkt0y+0rVEIdbtxVADApW9JXrUVlzHetgcyczP/E7DJmWJ4fJCZF2cPcBk0laWO9ZHMG3DmQ== + dependencies: + jake "^10.8.5" + +electron-to-chromium@^1.4.648: + version "1.4.655" + resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.4.655.tgz#112410db0d7f9c2b4ed8baa3b1b548522a6f89d4" + integrity sha512-2yszojF7vIZ68adIOvzV4bku8OZad9w5H9xF3ZAMZjPuOjBarlflUkjN6DggdV+L71WZuKUfKUhov/34+G5QHg== + +elliptic@^6.5.3, elliptic@^6.5.4: + version "6.5.4" + resolved "https://registry.yarnpkg.com/elliptic/-/elliptic-6.5.4.tgz#da37cebd31e79a1367e941b592ed1fbebd58abbb" + integrity sha512-iLhC6ULemrljPZb+QutR5TQGB+pdW6KGD5RSegS+8sorOZT+rdQFbsQFJgvN3eRqNALqJer4oQ16YvJHlU8hzQ== + dependencies: + bn.js "^4.11.9" + brorand "^1.1.0" + hash.js "^1.0.0" + hmac-drbg "^1.0.1" + inherits "^2.0.4" + minimalistic-assert "^1.0.1" + minimalistic-crypto-utils "^1.0.1" + +emoji-regex@^8.0.0: + version "8.0.0" + resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-8.0.0.tgz#e818fd69ce5ccfcb404594f842963bf53164cc37" + integrity sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A== + +emoji-regex@^9.2.2: + version "9.2.2" + resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-9.2.2.tgz#840c8803b0d8047f4ff0cf963176b32d4ef3ed72" + integrity sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg== + +entities@^2.0.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/entities/-/entities-2.2.0.tgz#098dc90ebb83d8dffa089d55256b351d34c4da55" + integrity sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A== + +entities@^4.2.0: + version "4.5.0" + resolved "https://registry.yarnpkg.com/entities/-/entities-4.5.0.tgz#5d268ea5e7113ec74c4d033b79ea5a35a488fb48" + integrity sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw== + +error-ex@^1.3.1: + version "1.3.2" + resolved "https://registry.yarnpkg.com/error-ex/-/error-ex-1.3.2.tgz#b4ac40648107fdcdcfae242f428bea8a14d4f1bf" + integrity sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g== + dependencies: + is-arrayish "^0.2.1" + +error-stack-parser-es@^0.1.1: + version "0.1.1" + resolved "https://registry.yarnpkg.com/error-stack-parser-es/-/error-stack-parser-es-0.1.1.tgz#9c1d2bbfbba8b51670062e7fbf43c6bcfb6eb4da" + integrity sha512-g/9rfnvnagiNf+DRMHEVGuGuIBlCIMDFoTA616HaP2l9PlCjGjVhD98PNbVSJvmK4TttqT5mV5tInMhoFgi+aA== + +esbuild@^0.19.3: + version "0.19.12" + resolved "https://registry.yarnpkg.com/esbuild/-/esbuild-0.19.12.tgz#dc82ee5dc79e82f5a5c3b4323a2a641827db3e04" + integrity sha512-aARqgq8roFBj054KvQr5f1sFu0D65G+miZRCuJyJ0G13Zwx7vRar5Zhn2tkQNzIXcBrNVsv/8stehpj+GAjgbg== + optionalDependencies: + "@esbuild/aix-ppc64" "0.19.12" + "@esbuild/android-arm" "0.19.12" + "@esbuild/android-arm64" "0.19.12" + "@esbuild/android-x64" "0.19.12" + "@esbuild/darwin-arm64" "0.19.12" + "@esbuild/darwin-x64" "0.19.12" + "@esbuild/freebsd-arm64" "0.19.12" + "@esbuild/freebsd-x64" "0.19.12" + "@esbuild/linux-arm" "0.19.12" + "@esbuild/linux-arm64" "0.19.12" + "@esbuild/linux-ia32" "0.19.12" + "@esbuild/linux-loong64" "0.19.12" + "@esbuild/linux-mips64el" "0.19.12" + "@esbuild/linux-ppc64" "0.19.12" + "@esbuild/linux-riscv64" "0.19.12" + "@esbuild/linux-s390x" "0.19.12" + "@esbuild/linux-x64" "0.19.12" + "@esbuild/netbsd-x64" "0.19.12" + "@esbuild/openbsd-x64" "0.19.12" + "@esbuild/sunos-x64" "0.19.12" + "@esbuild/win32-arm64" "0.19.12" + "@esbuild/win32-ia32" "0.19.12" + "@esbuild/win32-x64" "0.19.12" + +escalade@^3.1.1: + version "3.1.1" + resolved "https://registry.yarnpkg.com/escalade/-/escalade-3.1.1.tgz#d8cfdc7000965c5a0174b4a82eaa5c0552742e40" + integrity sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw== + +escape-string-regexp@^1.0.5: + version "1.0.5" + resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4" + integrity sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg== + +escape-string-regexp@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz#14ba83a5d373e3d311e5afca29cf5bfad965bf34" + integrity sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA== + +eslint-plugin-simple-import-sort@^10.0.0: + version "10.0.0" + resolved "https://registry.yarnpkg.com/eslint-plugin-simple-import-sort/-/eslint-plugin-simple-import-sort-10.0.0.tgz#cc4ceaa81ba73252427062705b64321946f61351" + integrity sha512-AeTvO9UCMSNzIHRkg8S6c3RPy5YEwKWSQPx3DYghLedo2ZQxowPFLGDN1AZ2evfg6r6mjBSZSLxLFsWSu3acsw== + +eslint-plugin-unicorn@^49.0.0: + version "49.0.0" + resolved "https://registry.yarnpkg.com/eslint-plugin-unicorn/-/eslint-plugin-unicorn-49.0.0.tgz#4449ea954d7e1455eec8518f9417d7021b245fa8" + integrity sha512-0fHEa/8Pih5cmzFW5L7xMEfUTvI9WKeQtjmKpTUmY+BiFCDxkxrTdnURJOHKykhtwIeyYsxnecbGvDCml++z4Q== + dependencies: + "@babel/helper-validator-identifier" "^7.22.20" + "@eslint-community/eslint-utils" "^4.4.0" + ci-info "^3.8.0" + clean-regexp "^1.0.0" + esquery "^1.5.0" + indent-string "^4.0.0" + is-builtin-module "^3.2.1" + jsesc "^3.0.2" + pluralize "^8.0.0" + read-pkg-up "^7.0.1" + regexp-tree "^0.1.27" + regjsparser "^0.10.0" + semver "^7.5.4" + strip-indent "^3.0.0" + +eslint-scope@^7.2.2: + version "7.2.2" + resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-7.2.2.tgz#deb4f92563390f32006894af62a22dba1c46423f" + integrity sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg== + dependencies: + esrecurse "^4.3.0" + estraverse "^5.2.0" + +eslint-visitor-keys@^3.3.0, eslint-visitor-keys@^3.4.1, eslint-visitor-keys@^3.4.3: + version "3.4.3" + resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz#0cd72fe8550e3c2eae156a96a4dddcd1c8ac5800" + integrity sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag== + +eslint@^8.56.0: + version "8.56.0" + resolved "https://registry.yarnpkg.com/eslint/-/eslint-8.56.0.tgz#4957ce8da409dc0809f99ab07a1b94832ab74b15" + integrity sha512-Go19xM6T9puCOWntie1/P997aXxFsOi37JIHRWI514Hc6ZnaHGKY9xFhrU65RT6CcBEzZoGG1e6Nq+DT04ZtZQ== + dependencies: + "@eslint-community/eslint-utils" "^4.2.0" + "@eslint-community/regexpp" "^4.6.1" + "@eslint/eslintrc" "^2.1.4" + "@eslint/js" "8.56.0" + "@humanwhocodes/config-array" "^0.11.13" + "@humanwhocodes/module-importer" "^1.0.1" + "@nodelib/fs.walk" "^1.2.8" + "@ungap/structured-clone" "^1.2.0" + ajv "^6.12.4" + chalk "^4.0.0" + cross-spawn "^7.0.2" + debug "^4.3.2" + doctrine "^3.0.0" + escape-string-regexp "^4.0.0" + eslint-scope "^7.2.2" + eslint-visitor-keys "^3.4.3" + espree "^9.6.1" + esquery "^1.4.2" + esutils "^2.0.2" + fast-deep-equal "^3.1.3" + file-entry-cache "^6.0.1" + find-up "^5.0.0" + glob-parent "^6.0.2" + globals "^13.19.0" + graphemer "^1.4.0" + ignore "^5.2.0" + imurmurhash "^0.1.4" + is-glob "^4.0.0" + is-path-inside "^3.0.3" + js-yaml "^4.1.0" + json-stable-stringify-without-jsonify "^1.0.1" + levn "^0.4.1" + lodash.merge "^4.6.2" + minimatch "^3.1.2" + natural-compare "^1.4.0" + optionator "^0.9.3" + strip-ansi "^6.0.1" + text-table "^0.2.0" + +espree@^9.6.0, espree@^9.6.1: + version "9.6.1" + resolved "https://registry.yarnpkg.com/espree/-/espree-9.6.1.tgz#a2a17b8e434690a5432f2f8018ce71d331a48c6f" + integrity sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ== + dependencies: + acorn "^8.9.0" + acorn-jsx "^5.3.2" + eslint-visitor-keys "^3.4.1" + +esquery@^1.4.2, esquery@^1.5.0: + version "1.5.0" + resolved "https://registry.yarnpkg.com/esquery/-/esquery-1.5.0.tgz#6ce17738de8577694edd7361c57182ac8cb0db0b" + integrity sha512-YQLXUplAwJgCydQ78IMJywZCceoqk1oH01OERdSAJc/7U2AylwjhSCLDEtqwg811idIS/9fIU5GjG73IgjKMVg== + dependencies: + estraverse "^5.1.0" + +esrecurse@^4.3.0: + version "4.3.0" + resolved "https://registry.yarnpkg.com/esrecurse/-/esrecurse-4.3.0.tgz#7ad7964d679abb28bee72cec63758b1c5d2c9921" + integrity sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag== + dependencies: + estraverse "^5.2.0" + +estraverse@^5.1.0, estraverse@^5.2.0: + version "5.3.0" + resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-5.3.0.tgz#2eea5290702f26ab8fe5370370ff86c965d21123" + integrity sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA== + +estree-walker@^2.0.1, estree-walker@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/estree-walker/-/estree-walker-2.0.2.tgz#52f010178c2a4c117a7757cfe942adb7d2da4cac" + integrity sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w== + +estree-walker@^3.0.3: + version "3.0.3" + resolved "https://registry.yarnpkg.com/estree-walker/-/estree-walker-3.0.3.tgz#67c3e549ec402a487b4fc193d1953a524752340d" + integrity sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g== + dependencies: + "@types/estree" "^1.0.0" + +esutils@^2.0.2: + version "2.0.3" + resolved "https://registry.yarnpkg.com/esutils/-/esutils-2.0.3.tgz#74d2eb4de0b8da1293711910d50775b9b710ef64" + integrity sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g== + +events@^3.0.0: + version "3.3.0" + resolved "https://registry.yarnpkg.com/events/-/events-3.3.0.tgz#31a95ad0a924e2d2c419a813aeb2c4e878ea7400" + integrity sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q== + +evp_bytestokey@^1.0.0, evp_bytestokey@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/evp_bytestokey/-/evp_bytestokey-1.0.3.tgz#7fcbdb198dc71959432efe13842684e0525acb02" + integrity sha512-/f2Go4TognH/KvCISP7OUsHn85hT9nUkxxA9BEWxFn+Oj9o8ZNLm/40hdlgSLyuOimsrTKLUMEorQexp/aPQeA== + dependencies: + md5.js "^1.3.4" + safe-buffer "^5.1.1" + +execa@^8.0.1: + version "8.0.1" + resolved "https://registry.yarnpkg.com/execa/-/execa-8.0.1.tgz#51f6a5943b580f963c3ca9c6321796db8cc39b8c" + integrity sha512-VyhnebXciFV2DESc+p6B+y0LjSm0krU4OgJN44qFAhBY0TJ+1V61tYD2+wHusZ6F9n5K+vl8k0sTy7PEfV4qpg== + dependencies: + cross-spawn "^7.0.3" + get-stream "^8.0.1" + human-signals "^5.0.0" + is-stream "^3.0.0" + merge-stream "^2.0.0" + npm-run-path "^5.1.0" + onetime "^6.0.0" + signal-exit "^4.1.0" + strip-final-newline "^3.0.0" + +fast-deep-equal@^3.1.1, fast-deep-equal@^3.1.3: + version "3.1.3" + resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz#3a7d56b559d6cbc3eb512325244e619a65c6c525" + integrity sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q== + +fast-glob@^3.2.11, fast-glob@^3.2.12, fast-glob@^3.2.9, fast-glob@^3.3.0: + version "3.3.2" + resolved "https://registry.yarnpkg.com/fast-glob/-/fast-glob-3.3.2.tgz#a904501e57cfdd2ffcded45e99a54fef55e46129" + integrity sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow== + dependencies: + "@nodelib/fs.stat" "^2.0.2" + "@nodelib/fs.walk" "^1.2.3" + glob-parent "^5.1.2" + merge2 "^1.3.0" + micromatch "^4.0.4" + +fast-json-stable-stringify@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz#874bf69c6f404c2b5d99c481341399fd55892633" + integrity sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw== + +fast-levenshtein@^2.0.6: + version "2.0.6" + resolved "https://registry.yarnpkg.com/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz#3d8a5c66883a16a30ca8643e851f19baa7797917" + integrity sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw== + +fastq@^1.6.0: + version "1.17.0" + resolved "https://registry.yarnpkg.com/fastq/-/fastq-1.17.0.tgz#ca5e1a90b5e68f97fc8b61330d5819b82f5fab03" + integrity sha512-zGygtijUMT7jnk3h26kUms3BkSDp4IfIKjmnqI2tvx6nuBfiF1UqOxbnLfzdv+apBy+53oaImsKtMw/xYbW+1w== + dependencies: + reusify "^1.0.4" + +fflate@^0.7.3: + version "0.7.4" + resolved "https://registry.yarnpkg.com/fflate/-/fflate-0.7.4.tgz#61587e5d958fdabb5a9368a302c25363f4f69f50" + integrity sha512-5u2V/CDW15QM1XbbgS+0DfPxVB+jUKhWEKuuFuHncbk3tEEqzmoXL+2KyOFuKGqOnmdIy0/davWF1CkuwtibCw== + +file-entry-cache@^6.0.1: + version "6.0.1" + resolved "https://registry.yarnpkg.com/file-entry-cache/-/file-entry-cache-6.0.1.tgz#211b2dd9659cb0394b073e7323ac3c933d522027" + integrity sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg== + dependencies: + flat-cache "^3.0.4" + +filelist@^1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/filelist/-/filelist-1.0.4.tgz#f78978a1e944775ff9e62e744424f215e58352b5" + integrity sha512-w1cEuf3S+DrLCQL7ET6kz+gmlJdbq9J7yXCSjK/OZCPA+qEN1WyF4ZAf0YYJa4/shHJra2t/d/r8SV4Ji+x+8Q== + dependencies: + minimatch "^5.0.1" + +fill-range@^7.0.1: + version "7.0.1" + resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-7.0.1.tgz#1919a6a7c75fe38b2c7c77e5198535da9acdda40" + integrity sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ== + dependencies: + to-regex-range "^5.0.1" + +find-up@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/find-up/-/find-up-4.1.0.tgz#97afe7d6cdc0bc5928584b7c8d7b16e8a9aa5d19" + integrity sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw== + dependencies: + locate-path "^5.0.0" + path-exists "^4.0.0" + +find-up@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/find-up/-/find-up-5.0.0.tgz#4c92819ecb7083561e4f4a240a86be5198f536fc" + integrity sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng== + dependencies: + locate-path "^6.0.0" + path-exists "^4.0.0" + +flat-cache@^3.0.4: + version "3.2.0" + resolved "https://registry.yarnpkg.com/flat-cache/-/flat-cache-3.2.0.tgz#2c0c2d5040c99b1632771a9d105725c0115363ee" + integrity sha512-CYcENa+FtcUKLmhhqyctpclsq7QF38pKjZHsGNiSQF5r4FtoKDWabFDl3hzaEQMvT1LHEysw5twgLvpYYb4vbw== + dependencies: + flatted "^3.2.9" + keyv "^4.5.3" + rimraf "^3.0.2" + +flatted@^3.2.9: + version "3.2.9" + resolved "https://registry.yarnpkg.com/flatted/-/flatted-3.2.9.tgz#7eb4c67ca1ba34232ca9d2d93e9886e611ad7daf" + integrity sha512-36yxDn5H7OFZQla0/jFJmbIKTdZAQHngCedGxiMmpNfEZM0sdEeT+WczLQrjK6D7o2aiyLYDnkw0R3JK0Qv1RQ== + +for-each@^0.3.3: + version "0.3.3" + resolved "https://registry.yarnpkg.com/for-each/-/for-each-0.3.3.tgz#69b447e88a0a5d32c3e7084f3f1710034b21376e" + integrity sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw== + dependencies: + is-callable "^1.1.3" + +foreground-child@^3.1.0: + version "3.1.1" + resolved "https://registry.yarnpkg.com/foreground-child/-/foreground-child-3.1.1.tgz#1d173e776d75d2772fed08efe4a0de1ea1b12d0d" + integrity sha512-TMKDUnIte6bfb5nWv7V/caI169OHgvwjb7V4WkeUvbQQdjr5rWKqHFiKWb/fcOwB+CzBT+qbWjvj+DVwRskpIg== + dependencies: + cross-spawn "^7.0.0" + signal-exit "^4.0.1" + +fraction.js@^4.3.7: + version "4.3.7" + resolved "https://registry.yarnpkg.com/fraction.js/-/fraction.js-4.3.7.tgz#06ca0085157e42fda7f9e726e79fefc4068840f7" + integrity sha512-ZsDfxO51wGAXREY55a7la9LScWpwv9RxIrYABrlvOFBlH/ShPnrtsXeuUIfXKKOVicNxQ+o8JTbJvjS4M89yew== + +fs-extra@^10.0.1, fs-extra@^10.1.0: + version "10.1.0" + resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-10.1.0.tgz#02873cfbc4084dde127eaa5f9905eef2325d1abf" + integrity sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ== + dependencies: + graceful-fs "^4.2.0" + jsonfile "^6.0.1" + universalify "^2.0.0" + +fs-extra@^11.1.0, fs-extra@^11.2.0: + version "11.2.0" + resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-11.2.0.tgz#e70e17dfad64232287d01929399e0ea7c86b0e5b" + integrity sha512-PmDi3uwK5nFuXh7XDTlVnS17xJS7vW36is2+w3xcv8SVxiB4NyATf4ctkVY5bkSjX0Y4nbvZCq1/EjtEyr9ktw== + dependencies: + graceful-fs "^4.2.0" + jsonfile "^6.0.1" + universalify "^2.0.0" + +fs-extra@~7.0.1: + version "7.0.1" + resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-7.0.1.tgz#4f189c44aa123b895f722804f55ea23eadc348e9" + integrity sha512-YJDaCJZEnBmcbw13fvdAM9AwNOJwOzrE4pqMqBq5nFiEqXUqHwlK4B+3pUw6JNvfSPtX05xFHtYy/1ni01eGCw== + dependencies: + graceful-fs "^4.1.2" + jsonfile "^4.0.0" + universalify "^0.1.0" + +fs-monkey@^1.0.0, fs-monkey@^1.0.3: + version "1.0.5" + resolved "https://registry.yarnpkg.com/fs-monkey/-/fs-monkey-1.0.5.tgz#fe450175f0db0d7ea758102e1d84096acb925788" + integrity sha512-8uMbBjrhzW76TYgEV27Y5E//W2f/lTFmx78P2w19FZSxarhI/798APGQyuGCwmkNxgwGRhrLfvWyLBvNtuOmew== + +fs.realpath@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f" + integrity sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw== + +fsevents@~2.3.2, fsevents@~2.3.3: + version "2.3.3" + resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.3.3.tgz#cac6407785d03675a2a5e1a5305c697b347d90d6" + integrity sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw== + +function-bind@^1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.2.tgz#2c02d864d97f3ea6c8830c464cbd11ab6eab7a1c" + integrity sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA== + +get-func-name@^2.0.1, get-func-name@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/get-func-name/-/get-func-name-2.0.2.tgz#0d7cf20cd13fda808669ffa88f4ffc7a3943fc41" + integrity sha512-8vXOvuE167CtIc3OyItco7N/dpRtBbYOsPsXCz7X/PMnlGjYjSGuZJgM1Y7mmew7BKf9BqvLX2tnOVy1BBUsxQ== + +get-intrinsic@^1.0.2, get-intrinsic@^1.1.3, get-intrinsic@^1.2.1, get-intrinsic@^1.2.2: + version "1.2.2" + resolved "https://registry.yarnpkg.com/get-intrinsic/-/get-intrinsic-1.2.2.tgz#281b7622971123e1ef4b3c90fd7539306da93f3b" + integrity sha512-0gSo4ml/0j98Y3lngkFEot/zhiCeWsbYIlZ+uZOVgzLyLaUw7wxUL+nCTP0XJvJg1AXulJRI3UJi8GsbDuxdGA== + dependencies: + function-bind "^1.1.2" + has-proto "^1.0.1" + has-symbols "^1.0.3" + hasown "^2.0.0" + +get-stream@^8.0.1: + version "8.0.1" + resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-8.0.1.tgz#def9dfd71742cd7754a7761ed43749a27d02eca2" + integrity sha512-VaUJspBffn/LMCJVoMvSAdmscJyS1auj5Zulnn5UoYcY531UWmdwhRWkcGKnGU93m5HSXP9LP2usOryrBtQowA== + +glob-parent@^5.1.2, glob-parent@~5.1.2: + version "5.1.2" + resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-5.1.2.tgz#869832c58034fe68a4093c17dc15e8340d8401c4" + integrity sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow== + dependencies: + is-glob "^4.0.1" + +glob-parent@^6.0.2: + version "6.0.2" + resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-6.0.2.tgz#6d237d99083950c79290f24c7642a3de9a28f9e3" + integrity sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A== + dependencies: + is-glob "^4.0.3" + +glob@^10.3.10: + version "10.3.10" + resolved "https://registry.yarnpkg.com/glob/-/glob-10.3.10.tgz#0351ebb809fd187fe421ab96af83d3a70715df4b" + integrity sha512-fa46+tv1Ak0UPK1TOy/pZrIybNNt4HCv7SDzwyfiOZkvZLEbjsZkJBPtDHVshZjbecAoAGSC20MjLDG/qr679g== + dependencies: + foreground-child "^3.1.0" + jackspeak "^2.3.5" + minimatch "^9.0.1" + minipass "^5.0.0 || ^6.0.2 || ^7.0.0" + path-scurry "^1.10.1" + +glob@^7.1.3: + version "7.2.3" + resolved "https://registry.yarnpkg.com/glob/-/glob-7.2.3.tgz#b8df0fb802bbfa8e89bd1d938b4e16578ed44f2b" + integrity sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q== + 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" + +globals@^13.19.0: + version "13.24.0" + resolved "https://registry.yarnpkg.com/globals/-/globals-13.24.0.tgz#8432a19d78ce0c1e833949c36adb345400bb1171" + integrity sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ== + dependencies: + type-fest "^0.20.2" + +globby@^11.1.0: + version "11.1.0" + resolved "https://registry.yarnpkg.com/globby/-/globby-11.1.0.tgz#bd4be98bb042f83d796f7e3811991fbe82a0d34b" + integrity sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g== + dependencies: + array-union "^2.1.0" + dir-glob "^3.0.1" + fast-glob "^3.2.9" + ignore "^5.2.0" + merge2 "^1.4.1" + slash "^3.0.0" + +gopd@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/gopd/-/gopd-1.0.1.tgz#29ff76de69dac7489b7c0918a5788e56477c332c" + integrity sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA== + dependencies: + get-intrinsic "^1.1.3" + +graceful-fs@^4.1.2, graceful-fs@^4.1.6, graceful-fs@^4.2.0: + version "4.2.11" + resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.11.tgz#4183e4e8bf08bb6e05bbb2f7d2e0c8f712ca40e3" + integrity sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ== + +graphemer@^1.4.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/graphemer/-/graphemer-1.4.0.tgz#fb2f1d55e0e3a1849aeffc90c4fa0dd53a0e66c6" + integrity sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag== + +has-flag@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-3.0.0.tgz#b5d454dc2199ae225699f3467e5a07f3b955bafd" + integrity sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw== + +has-flag@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-4.0.0.tgz#944771fd9c81c81265c4d6941860da06bb59479b" + integrity sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ== + +has-property-descriptors@^1.0.0, has-property-descriptors@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/has-property-descriptors/-/has-property-descriptors-1.0.1.tgz#52ba30b6c5ec87fd89fa574bc1c39125c6f65340" + integrity sha512-VsX8eaIewvas0xnvinAe9bw4WfIeODpGYikiWYLH+dma0Jw6KHYqWiWfhQlgOVK8D6PvjubK5Uc4P0iIhIcNVg== + dependencies: + get-intrinsic "^1.2.2" + +has-proto@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/has-proto/-/has-proto-1.0.1.tgz#1885c1305538958aff469fef37937c22795408e0" + integrity sha512-7qE+iP+O+bgF9clE5+UoBFzE65mlBiVj3tKCrlNQ0Ogwm0BjpT/gK4SlLYDMybDh5I3TCTKnPPa0oMG7JDYrhg== + +has-symbols@^1.0.2, has-symbols@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/has-symbols/-/has-symbols-1.0.3.tgz#bb7b2c4349251dce87b125f7bdf874aa7c8b39f8" + integrity sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A== + +has-tostringtag@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/has-tostringtag/-/has-tostringtag-1.0.0.tgz#7e133818a7d394734f941e73c3d3f9291e658b25" + integrity sha512-kFjcSNhnlGV1kyoGk7OXKSawH5JOb/LzUc5w9B02hOTO0dfFRjbHQKvg1d6cf3HbeUmtU9VbbV3qzZ2Teh97WQ== + dependencies: + has-symbols "^1.0.2" + +hash-base@^3.0.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/hash-base/-/hash-base-3.1.0.tgz#55c381d9e06e1d2997a883b4a3fddfe7f0d3af33" + integrity sha512-1nmYp/rhMDiE7AYkDw+lLwlAzz0AntGIe51F3RfFfEqyQ3feY2eI/NcwC6umIQVOASPMsWJLJScWKSSvzL9IVA== + dependencies: + inherits "^2.0.4" + readable-stream "^3.6.0" + safe-buffer "^5.2.0" + +hash.js@^1.0.0, hash.js@^1.0.3: + version "1.1.7" + resolved "https://registry.yarnpkg.com/hash.js/-/hash.js-1.1.7.tgz#0babca538e8d4ee4a0f8988d68866537a003cf42" + integrity sha512-taOaskGt4z4SOANNseOviYDvjEJinIkRgmp7LbKP2YTTmVxWBl87s/uzK9r+44BclBSp2X7K1hqeNfz9JbBeXA== + dependencies: + inherits "^2.0.3" + minimalistic-assert "^1.0.1" + +hasown@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/hasown/-/hasown-2.0.0.tgz#f4c513d454a57b7c7e1650778de226b11700546c" + integrity sha512-vUptKVTpIJhcczKBbgnS+RtcuYMB8+oNzPK2/Hp3hanz8JmpATdmmgLgSaadVREkDm+e2giHwY3ZRkyjSIDDFA== + dependencies: + function-bind "^1.1.2" + +he@1.2.0, he@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/he/-/he-1.2.0.tgz#84ae65fa7eafb165fddb61566ae14baf05664f0f" + integrity sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw== + +hmac-drbg@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/hmac-drbg/-/hmac-drbg-1.0.1.tgz#d2745701025a6c775a6c545793ed502fc0c649a1" + integrity sha512-Tti3gMqLdZfhOQY1Mzf/AanLiqh1WTiJgEj26ZuYQ9fbkLomzGchCws4FyrSd4VkpBfiNhaE1On+lOz894jvXg== + dependencies: + hash.js "^1.0.3" + minimalistic-assert "^1.0.0" + minimalistic-crypto-utils "^1.0.1" + +hosted-git-info@^2.1.4: + version "2.8.9" + resolved "https://registry.yarnpkg.com/hosted-git-info/-/hosted-git-info-2.8.9.tgz#dffc0bf9a21c02209090f2aa69429e1414daf3f9" + integrity sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw== + +html-minifier-terser@^6.1.0: + version "6.1.0" + resolved "https://registry.yarnpkg.com/html-minifier-terser/-/html-minifier-terser-6.1.0.tgz#bfc818934cc07918f6b3669f5774ecdfd48f32ab" + integrity sha512-YXxSlJBZTP7RS3tWnQw74ooKa6L9b9i9QYXY21eUEvhZ3u9XLfv6OnFsQq6RxkhHygsaUMvYsZRV5rU/OVNZxw== + dependencies: + camel-case "^4.1.2" + clean-css "^5.2.2" + commander "^8.3.0" + he "^1.2.0" + param-case "^3.0.4" + relateurl "^0.2.7" + terser "^5.10.0" + +https-browserify@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/https-browserify/-/https-browserify-1.0.0.tgz#ec06c10e0a34c0f2faf199f7fd7fc78fffd03c73" + integrity sha512-J+FkSdyD+0mA0N+81tMotaRMfSL9SGi+xpD3T6YApKsc3bGSXJlfXri3VyFOeYkfLRQisDk1W+jIFFKBeUBbBg== + +human-signals@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/human-signals/-/human-signals-5.0.0.tgz#42665a284f9ae0dade3ba41ebc37eb4b852f3a28" + integrity sha512-AXcZb6vzzrFAUE61HnN4mpLqd/cSIwNQjtNWR0euPm6y0iqx3G4gOXaIDdtdDwZmhwe82LA6+zinmW4UBWVePQ== + +ieee754@^1.1.13, ieee754@^1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/ieee754/-/ieee754-1.2.1.tgz#8eb7a10a63fff25d15a57b001586d177d1b0d352" + integrity sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA== + +ignore@^5.2.0, ignore@^5.2.4: + version "5.3.0" + resolved "https://registry.yarnpkg.com/ignore/-/ignore-5.3.0.tgz#67418ae40d34d6999c95ff56016759c718c82f78" + integrity sha512-g7dmpshy+gD7mh88OC9NwSGTKoc3kyLAZQRU1mt53Aw/vnvfXnbC+F/7F7QoYVKbV+KNvJx8wArewKy1vXMtlg== + +import-fresh@^3.2.1: + version "3.3.0" + resolved "https://registry.yarnpkg.com/import-fresh/-/import-fresh-3.3.0.tgz#37162c25fcb9ebaa2e6e53d5b4d88ce17d9e0c2b" + integrity sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw== + dependencies: + parent-module "^1.0.0" + resolve-from "^4.0.0" + +import-lazy@~4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/import-lazy/-/import-lazy-4.0.0.tgz#e8eb627483a0a43da3c03f3e35548be5cb0cc153" + integrity sha512-rKtvo6a868b5Hu3heneU+L4yEQ4jYKLtjpnPeUdK7h0yzXGmyBTypknlkCvHFBqfX9YlorEiMM6Dnq/5atfHkw== + +imurmurhash@^0.1.4: + version "0.1.4" + resolved "https://registry.yarnpkg.com/imurmurhash/-/imurmurhash-0.1.4.tgz#9218b9b2b928a238b13dc4fb6b6d576f231453ea" + integrity sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA== + +indent-string@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/indent-string/-/indent-string-4.0.0.tgz#624f8f4497d619b2d9768531d58f4122854d7251" + integrity sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg== + +inflight@^1.0.4: + version "1.0.6" + resolved "https://registry.yarnpkg.com/inflight/-/inflight-1.0.6.tgz#49bd6331d7d02d0c09bc910a1075ba8165b56df9" + integrity sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA== + dependencies: + once "^1.3.0" + wrappy "1" + +inherits@2, inherits@^2.0.1, inherits@^2.0.3, inherits@^2.0.4, inherits@~2.0.4: + version "2.0.4" + resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c" + integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== + +is-arguments@^1.0.4: + version "1.1.1" + resolved "https://registry.yarnpkg.com/is-arguments/-/is-arguments-1.1.1.tgz#15b3f88fda01f2a97fec84ca761a560f123efa9b" + integrity sha512-8Q7EARjzEnKpt/PCD7e1cgUS0a6X8u5tdSiMqXhojOdoV9TsMsiO+9VLC5vAmO8N7/GmXn7yjR8qnA6bVAEzfA== + dependencies: + call-bind "^1.0.2" + has-tostringtag "^1.0.0" + +is-arrayish@^0.2.1: + version "0.2.1" + resolved "https://registry.yarnpkg.com/is-arrayish/-/is-arrayish-0.2.1.tgz#77c99840527aa8ecb1a8ba697b80645a7a926a9d" + integrity sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg== + +is-binary-path@~2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/is-binary-path/-/is-binary-path-2.1.0.tgz#ea1f7f3b80f064236e83470f86c09c254fb45b09" + integrity sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw== + dependencies: + binary-extensions "^2.0.0" + +is-builtin-module@^3.2.1: + version "3.2.1" + resolved "https://registry.yarnpkg.com/is-builtin-module/-/is-builtin-module-3.2.1.tgz#f03271717d8654cfcaf07ab0463faa3571581169" + integrity sha512-BSLE3HnV2syZ0FK0iMA/yUGplUeMmNz4AW5fnTunbCIqZi4vG3WjJT9FHMy5D69xmAYBHXQhJdALdpwVxV501A== + dependencies: + builtin-modules "^3.3.0" + +is-callable@^1.1.3: + version "1.2.7" + resolved "https://registry.yarnpkg.com/is-callable/-/is-callable-1.2.7.tgz#3bc2a85ea742d9e36205dcacdd72ca1fdc51b055" + integrity sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA== + +is-core-module@^2.1.0, is-core-module@^2.13.0: + version "2.13.1" + resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.13.1.tgz#ad0d7532c6fea9da1ebdc82742d74525c6273384" + integrity sha512-hHrIjvZsftOsvKSn2TRYl63zvxsgE0K+0mYMoH6gD4omR5IWB2KynivBQczo3+wF1cCkjzvptnI9Q0sPU66ilw== + dependencies: + hasown "^2.0.0" + +is-docker@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/is-docker/-/is-docker-3.0.0.tgz#90093aa3106277d8a77a5910dbae71747e15a200" + integrity sha512-eljcgEDlEns/7AXFosB5K/2nCM4P7FQPkGc/DWLy5rmFEWvZayGrik1d9/QIY5nJ4f9YsVvBkA6kJpHn9rISdQ== + +is-extglob@^2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/is-extglob/-/is-extglob-2.1.1.tgz#a88c02535791f02ed37c76a1b9ea9773c833f8c2" + integrity sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ== + +is-fullwidth-code-point@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz#f116f8064fe90b3f7844a38997c0b75051269f1d" + integrity sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg== + +is-generator-function@^1.0.7: + version "1.0.10" + resolved "https://registry.yarnpkg.com/is-generator-function/-/is-generator-function-1.0.10.tgz#f1558baf1ac17e0deea7c0415c438351ff2b3c72" + integrity sha512-jsEjy9l3yiXEQ+PsXdmBwEPcOxaXWLspKdplFUVI9vq1iZgIekeC0L167qeu86czQaxed3q/Uzuw0swL0irL8A== + dependencies: + has-tostringtag "^1.0.0" + +is-glob@^4.0.0, is-glob@^4.0.1, is-glob@^4.0.3, is-glob@~4.0.1: + version "4.0.3" + resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-4.0.3.tgz#64f61e42cbbb2eec2071a9dac0b28ba1e65d5084" + integrity sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg== + dependencies: + is-extglob "^2.1.1" + +is-inside-container@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/is-inside-container/-/is-inside-container-1.0.0.tgz#e81fba699662eb31dbdaf26766a61d4814717ea4" + integrity sha512-KIYLCCJghfHZxqjYBE7rEy0OBuTd5xCHS7tHVgvCLkx7StIoaxwNW3hCALgEUjFfeRk+MG/Qxmp/vtETEF3tRA== + dependencies: + is-docker "^3.0.0" + +is-nan@^1.3.2: + version "1.3.2" + resolved "https://registry.yarnpkg.com/is-nan/-/is-nan-1.3.2.tgz#043a54adea31748b55b6cd4e09aadafa69bd9e1d" + integrity sha512-E+zBKpQ2t6MEo1VsonYmluk9NxGrbzpeeLC2xIViuO2EjU2xsXsBPwTr3Ykv9l08UYEVEdWeRZNouaZqF6RN0w== + dependencies: + call-bind "^1.0.0" + define-properties "^1.1.3" + +is-number@^7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/is-number/-/is-number-7.0.0.tgz#7535345b896734d5f80c4d06c50955527a14f12b" + integrity sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng== + +is-path-inside@^3.0.3: + version "3.0.3" + resolved "https://registry.yarnpkg.com/is-path-inside/-/is-path-inside-3.0.3.tgz#d231362e53a07ff2b0e0ea7fed049161ffd16283" + integrity sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ== + +is-stream@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-3.0.0.tgz#e6bfd7aa6bef69f4f472ce9bb681e3e57b4319ac" + integrity sha512-LnQR4bZ9IADDRSkvpqMGvt/tEJWclzklNgSw48V5EAaAeDd6qGvN8ei6k5p0tvxSR171VmGyHuTiAOfxAbr8kA== + +is-typed-array@^1.1.3: + version "1.1.12" + resolved "https://registry.yarnpkg.com/is-typed-array/-/is-typed-array-1.1.12.tgz#d0bab5686ef4a76f7a73097b95470ab199c57d4a" + integrity sha512-Z14TF2JNG8Lss5/HMqt0//T9JeHXttXy5pH/DBU4vi98ozO2btxzq9MwYDZYnKwU8nRsz/+GVFVRDq3DkVuSPg== + dependencies: + which-typed-array "^1.1.11" + +is-wsl@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/is-wsl/-/is-wsl-3.1.0.tgz#e1c657e39c10090afcbedec61720f6b924c3cbd2" + integrity sha512-UcVfVfaK4Sc4m7X3dUSoHoozQGBEFeDC+zVo06t98xe8CzHSZZBekNXH+tu0NalHolcJ/QAGqS46Hef7QXBIMw== + dependencies: + is-inside-container "^1.0.0" + +isexe@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10" + integrity sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw== + +isomorphic-timers-promises@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/isomorphic-timers-promises/-/isomorphic-timers-promises-1.0.1.tgz#e4137c24dbc54892de8abae3a4b5c1ffff381598" + integrity sha512-u4sej9B1LPSxTGKB/HiuzvEQnXH0ECYkSVQU39koSwmFAxhlEAFl9RdTvLv4TOTQUgBS5O3O5fwUxk6byBZ+IQ== + +jackspeak@^2.3.5: + version "2.3.6" + resolved "https://registry.yarnpkg.com/jackspeak/-/jackspeak-2.3.6.tgz#647ecc472238aee4b06ac0e461acc21a8c505ca8" + integrity sha512-N3yCS/NegsOBokc8GAdM8UcmfsKiSS8cipheD/nivzr700H+nsMOxJjQnvwOcRYVuFkdH0wGUvW2WbXGmrZGbQ== + dependencies: + "@isaacs/cliui" "^8.0.2" + optionalDependencies: + "@pkgjs/parseargs" "^0.11.0" + +jake@^10.8.5: + version "10.8.7" + resolved "https://registry.yarnpkg.com/jake/-/jake-10.8.7.tgz#63a32821177940c33f356e0ba44ff9d34e1c7d8f" + integrity sha512-ZDi3aP+fG/LchyBzUM804VjddnwfSfsdeYkwt8NcbKRvo4rFkjhs456iLFn3k2ZUWvNe4i48WACDbza8fhq2+w== + dependencies: + async "^3.2.3" + chalk "^4.0.2" + filelist "^1.0.4" + minimatch "^3.1.2" + +jest-worker@^26.2.1: + version "26.6.2" + resolved "https://registry.yarnpkg.com/jest-worker/-/jest-worker-26.6.2.tgz#7f72cbc4d643c365e27b9fd775f9d0eaa9c7a8ed" + integrity sha512-KWYVV1c4i+jbMpaBC+U++4Va0cp8OisU185o73T1vo99hqi7w8tSJfUXYswwqqrjzwxa6KpRK54WhPvwf5w6PQ== + dependencies: + "@types/node" "*" + merge-stream "^2.0.0" + supports-color "^7.0.0" + +jiti@^1.19.1: + version "1.21.0" + resolved "https://registry.yarnpkg.com/jiti/-/jiti-1.21.0.tgz#7c97f8fe045724e136a397f7340475244156105d" + integrity sha512-gFqAIbuKyyso/3G2qhiO2OM6shY6EPP/R0+mkDbyspxKazh8BXDC5FiFsUjlczgdNz/vfra0da2y+aHrusLG/Q== + +jju@~1.4.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/jju/-/jju-1.4.0.tgz#a3abe2718af241a2b2904f84a625970f389ae32a" + integrity sha512-8wb9Yw966OSxApiCt0K3yNJL8pnNeIv+OEq2YMidz4FKP6nonSRoOXc80iXY4JaN2FC11B9qsNmDsm+ZOfMROA== + +js-tokens@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499" + integrity sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ== + +js-yaml@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-4.1.0.tgz#c1fb65f8f5017901cdd2c951864ba18458a10602" + integrity sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA== + dependencies: + argparse "^2.0.1" + +jsesc@^3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/jsesc/-/jsesc-3.0.2.tgz#bb8b09a6597ba426425f2e4a07245c3d00b9343e" + integrity sha512-xKqzzWXDttJuOcawBt4KnKHHIf5oQ/Cxax+0PWFG+DFDgHNAdi+TXECADI+RYiFUMmx8792xsMbbgXj4CwnP4g== + +jsesc@~0.5.0: + version "0.5.0" + resolved "https://registry.yarnpkg.com/jsesc/-/jsesc-0.5.0.tgz#e7dee66e35d6fc16f710fe91d5cf69f70f08911d" + integrity sha512-uZz5UnB7u4T9LvwmFqXii7pZSouaRPorGs5who1Ip7VO0wxanFvBL7GkM6dTHlgX+jhBApRetaWpnDabOeTcnA== + +json-buffer@3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/json-buffer/-/json-buffer-3.0.1.tgz#9338802a30d3b6605fbe0613e094008ca8c05a13" + integrity sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ== + +json-parse-even-better-errors@^2.3.0: + version "2.3.1" + resolved "https://registry.yarnpkg.com/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz#7c47805a94319928e05777405dc12e1f7a4ee02d" + integrity sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w== + +json-schema-traverse@^0.4.1: + version "0.4.1" + resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz#69f6a87d9513ab8bb8fe63bdb0979c448e684660" + integrity sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg== + +json-stable-stringify-without-jsonify@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz#9db7b59496ad3f3cfef30a75142d2d930ad72651" + integrity sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw== + +json5@2.2.1: + version "2.2.1" + resolved "https://registry.yarnpkg.com/json5/-/json5-2.2.1.tgz#655d50ed1e6f95ad1a3caababd2b0efda10b395c" + integrity sha512-1hqLFMSrGHRHxav9q9gNjJ5EXznIxGVO09xQRrwplcS8qs28pZ8s8hupZAmqDwZUmVZ2Qb2jnyPOWcDH8m8dlA== + +jsonc-parser@^3.2.0: + version "3.2.1" + resolved "https://registry.yarnpkg.com/jsonc-parser/-/jsonc-parser-3.2.1.tgz#031904571ccf929d7670ee8c547545081cb37f1a" + integrity sha512-AilxAyFOAcK5wA1+LeaySVBrHsGQvUFCDWXKpZjzaL0PqW+xfBOttn8GNtWKFWqneyMZj41MWF9Kl6iPWLwgOA== + +jsonfile@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/jsonfile/-/jsonfile-4.0.0.tgz#8771aae0799b64076b76640fca058f9c10e33ecb" + integrity sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg== + optionalDependencies: + graceful-fs "^4.1.6" + +jsonfile@^6.0.1: + version "6.1.0" + resolved "https://registry.yarnpkg.com/jsonfile/-/jsonfile-6.1.0.tgz#bc55b2634793c679ec6403094eb13698a6ec0aae" + integrity sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ== + dependencies: + universalify "^2.0.0" + optionalDependencies: + graceful-fs "^4.1.6" + +keyv@^4.5.3: + version "4.5.4" + resolved "https://registry.yarnpkg.com/keyv/-/keyv-4.5.4.tgz#a879a99e29452f942439f2a405e3af8b31d4de93" + integrity sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw== + dependencies: + json-buffer "3.0.1" + +kolorist@^1.6.0: + version "1.8.0" + resolved "https://registry.yarnpkg.com/kolorist/-/kolorist-1.8.0.tgz#edddbbbc7894bc13302cdf740af6374d4a04743c" + integrity sha512-Y+60/zizpJ3HRH8DCss+q95yr6145JXZo46OTpFvDZWLfRCE4qChOyk1b26nMaNpfHHgxagk9dXT5OP0Tfe+dQ== + +levn@^0.4.1: + version "0.4.1" + resolved "https://registry.yarnpkg.com/levn/-/levn-0.4.1.tgz#ae4562c007473b932a6200d403268dd2fffc6ade" + integrity sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ== + dependencies: + prelude-ls "^1.2.1" + type-check "~0.4.0" + +lilconfig@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/lilconfig/-/lilconfig-2.1.0.tgz#78e23ac89ebb7e1bfbf25b18043de756548e7f52" + integrity sha512-utWOt/GHzuUxnLKxB6dk81RoOeoNeHgbrXiuGk4yyF5qlRz+iIVWu56E2fqGHFrXz0QNUhLB/8nKqvRH66JKGQ== + +lilconfig@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/lilconfig/-/lilconfig-3.0.0.tgz#f8067feb033b5b74dab4602a5f5029420be749bc" + integrity sha512-K2U4W2Ff5ibV7j7ydLr+zLAkIg5JJ4lPn1Ltsdt+Tz/IjQ8buJ55pZAxoP34lqIiwtF9iAvtLv3JGv7CAyAg+g== + +lines-and-columns@^1.1.6: + version "1.2.4" + resolved "https://registry.yarnpkg.com/lines-and-columns/-/lines-and-columns-1.2.4.tgz#eca284f75d2965079309dc0ad9255abb2ebc1632" + integrity sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg== + +local-pkg@^0.5.0: + version "0.5.0" + resolved "https://registry.yarnpkg.com/local-pkg/-/local-pkg-0.5.0.tgz#093d25a346bae59a99f80e75f6e9d36d7e8c925c" + integrity sha512-ok6z3qlYyCDS4ZEU27HaU6x/xZa9Whf8jD4ptH5UZTQYZVYeb9bnZ3ojVhiJNLiXK1Hfc0GNbLXcmZ5plLDDBg== + dependencies: + mlly "^1.4.2" + pkg-types "^1.0.3" + +locate-path@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-5.0.0.tgz#1afba396afd676a6d42504d0a67a3a7eb9f62aa0" + integrity sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g== + dependencies: + p-locate "^4.1.0" + +locate-path@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-6.0.0.tgz#55321eb309febbc59c4801d931a72452a681d286" + integrity sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw== + dependencies: + p-locate "^5.0.0" + +lodash.get@^4.4.2: + version "4.4.2" + resolved "https://registry.yarnpkg.com/lodash.get/-/lodash.get-4.4.2.tgz#2d177f652fa31e939b4438d5341499dfa3825e99" + integrity sha512-z+Uw/vLuy6gQe8cfaFWD7p0wVv8fJl3mbzXh33RS+0oW2wvUqiRXiQ69gLWSLpgB5/6sU+r6BlQR0MBILadqTQ== + +lodash.isequal@^4.5.0: + version "4.5.0" + resolved "https://registry.yarnpkg.com/lodash.isequal/-/lodash.isequal-4.5.0.tgz#415c4478f2bcc30120c22ce10ed3226f7d3e18e0" + integrity sha512-pDo3lu8Jhfjqls6GkMgpahsF9kCyayhgykjyLMNFTKWrpVdAQtYyB4muAMWozBB4ig/dtWAmsMxLEI8wuz+DYQ== + +lodash.memoize@^4.1.2: + version "4.1.2" + resolved "https://registry.yarnpkg.com/lodash.memoize/-/lodash.memoize-4.1.2.tgz#bcc6c49a42a2840ed997f323eada5ecd182e0bfe" + integrity sha512-t7j+NzmgnQzTAYXcsHYLgimltOV1MXHtlOWf6GjL9Kj8GK5FInw5JotxvbOs+IvV1/Dzo04/fCGfLVs7aXb4Ag== + +lodash.merge@^4.6.2: + version "4.6.2" + resolved "https://registry.yarnpkg.com/lodash.merge/-/lodash.merge-4.6.2.tgz#558aa53b43b661e1925a0afdfa36a9a1085fe57a" + integrity sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ== + +lodash.uniq@^4.5.0: + version "4.5.0" + resolved "https://registry.yarnpkg.com/lodash.uniq/-/lodash.uniq-4.5.0.tgz#d0225373aeb652adc1bc82e4945339a842754773" + integrity sha512-xfBaXQd9ryd9dlSDvnvI0lvxfLJlYAZzXomUYzLKtUeOQvOP5piqAWuGtrhWeqaXK9hhoM/iyJc5AV+XfsX3HQ== + +lodash@~4.17.15: + version "4.17.21" + resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c" + integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg== + +loupe@^2.3.6, loupe@^2.3.7: + version "2.3.7" + resolved "https://registry.yarnpkg.com/loupe/-/loupe-2.3.7.tgz#6e69b7d4db7d3ab436328013d37d1c8c3540c697" + integrity sha512-zSMINGVYkdpYSOBmLi0D1Uo7JU9nVdQKrHxC8eYlV+9YKK9WePqAlL7lSlorG/U2Fw1w0hTBmaa/jrQ3UbPHtA== + dependencies: + get-func-name "^2.0.1" + +lower-case@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/lower-case/-/lower-case-2.0.2.tgz#6fa237c63dbdc4a82ca0fd882e4722dc5e634e28" + integrity sha512-7fm3l3NAF9WfN6W3JOmf5drwpVqX78JtoGJ3A6W0a6ZnldM41w2fV5D490psKFTpMds8TJse/eHLFFsNHHjHgg== + dependencies: + tslib "^2.0.3" + +lru-cache@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-6.0.0.tgz#6d6fe6570ebd96aaf90fcad1dafa3b2566db3a94" + integrity sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA== + dependencies: + yallist "^4.0.0" + +"lru-cache@^9.1.1 || ^10.0.0": + version "10.2.0" + resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-10.2.0.tgz#0bd445ca57363465900f4d1f9bd8db343a4d95c3" + integrity sha512-2bIM8x+VAf6JT4bKAljS1qUWgMsqZRPGJS6FSahIMPVvctcNhyVp7AJu7quxOW9jwkryBReKZY5tY5JYv2n/7Q== + +lz-string@^1.5.0: + version "1.5.0" + resolved "https://registry.yarnpkg.com/lz-string/-/lz-string-1.5.0.tgz#c1ab50f77887b712621201ba9fd4e3a6ed099941" + integrity sha512-h5bgJWpxJNswbU7qCrV0tIKQCaS3blPDrqKWx+QxzuzL1zGUzij9XCWLrSLsJPu5t+eWA/ycetzYAO5IOMcWAQ== + +magic-string@0.26.7: + version "0.26.7" + resolved "https://registry.yarnpkg.com/magic-string/-/magic-string-0.26.7.tgz#caf7daf61b34e9982f8228c4527474dac8981d6f" + integrity sha512-hX9XH3ziStPoPhJxLq1syWuZMxbDvGNbVchfrdCtanC7D13888bMFow61x8axrx+GfHLtVeAx2kxL7tTGRl+Ow== + dependencies: + sourcemap-codec "^1.4.8" + +magic-string@^0.30.3, magic-string@^0.30.5: + version "0.30.5" + resolved "https://registry.yarnpkg.com/magic-string/-/magic-string-0.30.5.tgz#1994d980bd1c8835dc6e78db7cbd4ae4f24746f9" + integrity sha512-7xlpfBaQaP/T6Vh8MO/EqXSW5En6INHEvEXQiuff7Gku0PWjU3uf6w/j9o7O+SpB5fOAkrI5HeoNgwjEO0pFsA== + dependencies: + "@jridgewell/sourcemap-codec" "^1.4.15" + +md5.js@^1.3.4: + version "1.3.5" + resolved "https://registry.yarnpkg.com/md5.js/-/md5.js-1.3.5.tgz#b5d07b8e3216e3e27cd728d72f70d1e6a342005f" + integrity sha512-xitP+WxNPcTTOgnTJcrhM0xvdPepipPSf3I8EIpGKeFLjt3PlJLIDG3u8EX53ZIubkb+5U2+3rELYpEhHhzdkg== + dependencies: + hash-base "^3.0.0" + inherits "^2.0.1" + safe-buffer "^5.1.2" + +mdn-data@2.0.28: + version "2.0.28" + resolved "https://registry.yarnpkg.com/mdn-data/-/mdn-data-2.0.28.tgz#5ec48e7bef120654539069e1ae4ddc81ca490eba" + integrity sha512-aylIc7Z9y4yzHYAJNuESG3hfhC+0Ibp/MAMiaOZgNv4pmEdFyfZhhhny4MNiAfWdBQ1RQ2mfDWmM1x8SvGyp8g== + +mdn-data@2.0.30: + version "2.0.30" + resolved "https://registry.yarnpkg.com/mdn-data/-/mdn-data-2.0.30.tgz#ce4df6f80af6cfbe218ecd5c552ba13c4dfa08cc" + integrity sha512-GaqWWShW4kv/G9IEucWScBx9G1/vsFZZJUO+tD26M8J8z3Kw5RDQjaoZe03YAClgeS/SWPOcb4nkFBTEi5DUEA== + +merge-stream@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/merge-stream/-/merge-stream-2.0.0.tgz#52823629a14dd00c9770fb6ad47dc6310f2c1f60" + integrity sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w== + +merge2@^1.3.0, merge2@^1.4.1: + version "1.4.1" + resolved "https://registry.yarnpkg.com/merge2/-/merge2-1.4.1.tgz#4368892f885e907455a6fd7dc55c0c9d404990ae" + integrity sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg== + +micromatch@^4.0.4, micromatch@^4.0.5: + version "4.0.5" + resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-4.0.5.tgz#bc8999a7cbbf77cdc89f132f6e467051b49090c6" + integrity sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA== + dependencies: + braces "^3.0.2" + picomatch "^2.3.1" + +miller-rabin@^4.0.0: + version "4.0.1" + resolved "https://registry.yarnpkg.com/miller-rabin/-/miller-rabin-4.0.1.tgz#f080351c865b0dc562a8462966daa53543c78a4d" + integrity sha512-115fLhvZVqWwHPbClyntxEVfVDfl9DLLTuJvq3g2O/Oxi8AiNouAHvDSzHS0viUJc+V5vm3eq91Xwqn9dp4jRA== + dependencies: + bn.js "^4.0.0" + brorand "^1.0.1" + +mimic-fn@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-4.0.0.tgz#60a90550d5cb0b239cca65d893b1a53b29871ecc" + integrity sha512-vqiC06CuhBTUdZH+RYl8sFrL096vA45Ok5ISO6sE/Mr1jRbGH4Csnhi8f3wKVl7x8mO4Au7Ir9D3Oyv1VYMFJw== + +min-indent@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/min-indent/-/min-indent-1.0.1.tgz#a63f681673b30571fbe8bc25686ae746eefa9869" + integrity sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg== + +minimalistic-assert@^1.0.0, minimalistic-assert@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz#2e194de044626d4a10e7f7fbc00ce73e83e4d5c7" + integrity sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A== + +minimalistic-crypto-utils@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/minimalistic-crypto-utils/-/minimalistic-crypto-utils-1.0.1.tgz#f6c00c1c0b082246e5c4d99dfb8c7c083b2b582a" + integrity sha512-JIYlbt6g8i5jKfJ3xz7rF0LXmv2TkDxBLUkiBeZ7bAx4GnnNMr8xFpGnOxn6GhTEHx3SjRrZEoU+j04prX1ktg== + +minimatch@9.0.3, minimatch@^9.0.1: + version "9.0.3" + resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-9.0.3.tgz#a6e00c3de44c3a542bfaae70abfc22420a6da825" + integrity sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg== + dependencies: + brace-expansion "^2.0.1" + +minimatch@^3.0.5, minimatch@^3.1.1, minimatch@^3.1.2: + version "3.1.2" + resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.1.2.tgz#19cd194bfd3e428f049a70817c038d89ab4be35b" + integrity sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw== + dependencies: + brace-expansion "^1.1.7" + +minimatch@^5.0.1, minimatch@^5.1.0: + version "5.1.6" + resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-5.1.6.tgz#1cfcb8cf5522ea69952cd2af95ae09477f122a96" + integrity sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g== + dependencies: + brace-expansion "^2.0.1" + +"minipass@^5.0.0 || ^6.0.2 || ^7.0.0": + version "7.0.4" + resolved "https://registry.yarnpkg.com/minipass/-/minipass-7.0.4.tgz#dbce03740f50a4786ba994c1fb908844d27b038c" + integrity sha512-jYofLM5Dam9279rdkWzqHozUo4ybjdZmCsDHePy5V/PbBcVMiSZR97gmAy45aqi8CK1lG2ECd356FU86avfwUQ== + +mkdirp@^1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-1.0.4.tgz#3eb5ed62622756d79a5f0e2a221dfebad75c2f7e" + integrity sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw== + +mlly@^1.2.0, mlly@^1.4.2: + version "1.5.0" + resolved "https://registry.yarnpkg.com/mlly/-/mlly-1.5.0.tgz#8428a4617d54cc083d3009030ac79739a0e5447a" + integrity sha512-NPVQvAY1xr1QoVeG0cy8yUYC7FQcOx6evl/RjT1wL5FvzPnzOysoqB/jmx/DhssT2dYa8nxECLAaFI/+gVLhDQ== + dependencies: + acorn "^8.11.3" + pathe "^1.1.2" + pkg-types "^1.0.3" + ufo "^1.3.2" + +monaco-editor@^0.45.0: + version "0.45.0" + resolved "https://registry.yarnpkg.com/monaco-editor/-/monaco-editor-0.45.0.tgz#6939123a6254aea9fea2d647697f846306dd4448" + integrity sha512-mjv1G1ZzfEE3k9HZN0dQ2olMdwIfaeAAjFiwNprLfYNRSz7ctv9XuCT7gPtBGrMUeV1/iZzYKj17Khu1hxoHOA== + +mrmime@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/mrmime/-/mrmime-2.0.0.tgz#151082a6e06e59a9a39b46b3e14d5cfe92b3abb4" + integrity sha512-eu38+hdgojoyq63s+yTpN4XMBdt5l8HhMhc4VKLO9KM5caLIBvUm4thi7fFaxyTmCKeNnXZ5pAlBwCUnhA09uw== + +ms@2.1.2: + version "2.1.2" + resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009" + integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w== + +mz@^2.7.0: + version "2.7.0" + resolved "https://registry.yarnpkg.com/mz/-/mz-2.7.0.tgz#95008057a56cafadc2bc63dde7f9ff6955948e32" + integrity sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q== + dependencies: + any-promise "^1.0.0" + object-assign "^4.0.1" + thenify-all "^1.0.0" + +nanoid@^3.3.7: + version "3.3.7" + resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.3.7.tgz#d0c301a691bc8d54efa0a2226ccf3fe2fd656bd8" + integrity sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g== + +natural-compare@^1.4.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/natural-compare/-/natural-compare-1.4.0.tgz#4abebfeed7541f2c27acfb29bdbbd15c8d5ba4f7" + integrity sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw== + +no-case@^3.0.4: + version "3.0.4" + resolved "https://registry.yarnpkg.com/no-case/-/no-case-3.0.4.tgz#d361fd5c9800f558551a8369fc0dcd4662b6124d" + integrity sha512-fgAN3jGAh+RoxUGZHTSOLJIqUc2wmoBwGR4tbpNAKmmovFoWq0OdRkb0VkldReO2a2iBT/OEulG9XSUc10r3zg== + dependencies: + lower-case "^2.0.2" + tslib "^2.0.3" + +node-html-parser@^5.3.3: + version "5.4.2" + resolved "https://registry.yarnpkg.com/node-html-parser/-/node-html-parser-5.4.2.tgz#93e004038c17af80226c942336990a0eaed8136a" + integrity sha512-RaBPP3+51hPne/OolXxcz89iYvQvKOydaqoePpOgXcrOKZhjVIzmpKZz+Hd/RBO2/zN2q6CNJhQzucVz+u3Jyw== + dependencies: + css-select "^4.2.1" + he "1.2.0" + +node-releases@^2.0.14: + version "2.0.14" + resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-2.0.14.tgz#2ffb053bceb8b2be8495ece1ab6ce600c4461b0b" + integrity sha512-y10wOWt8yZpqXmOgRo77WaHEmhYQYGNA6y421PKsKYWEK8aW+cqAphborZDhqfyKrbZEN92CN1X2KbafY2s7Yw== + +node-stdlib-browser@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/node-stdlib-browser/-/node-stdlib-browser-1.2.0.tgz#5ddcfdf4063b88fb282979a1aa6ddab9728d5e4c" + integrity sha512-VSjFxUhRhkyed8AtLwSCkMrJRfQ3e2lGtG3sP6FEgaLKBBbxM/dLfjRe1+iLhjvyLFW3tBQ8+c0pcOtXGbAZJg== + dependencies: + assert "^2.0.0" + browser-resolve "^2.0.0" + browserify-zlib "^0.2.0" + buffer "^5.7.1" + console-browserify "^1.1.0" + constants-browserify "^1.0.0" + create-require "^1.1.1" + crypto-browserify "^3.11.0" + domain-browser "^4.22.0" + events "^3.0.0" + https-browserify "^1.0.0" + isomorphic-timers-promises "^1.0.1" + os-browserify "^0.3.0" + path-browserify "^1.0.1" + pkg-dir "^5.0.0" + process "^0.11.10" + punycode "^1.4.1" + querystring-es3 "^0.2.1" + readable-stream "^3.6.0" + stream-browserify "^3.0.0" + stream-http "^3.2.0" + string_decoder "^1.0.0" + timers-browserify "^2.0.4" + tty-browserify "0.0.1" + url "^0.11.0" + util "^0.12.4" + vm-browserify "^1.0.1" + +normalize-package-data@^2.5.0: + version "2.5.0" + resolved "https://registry.yarnpkg.com/normalize-package-data/-/normalize-package-data-2.5.0.tgz#e66db1838b200c1dfc233225d12cb36520e234a8" + integrity sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA== + dependencies: + hosted-git-info "^2.1.4" + resolve "^1.10.0" + semver "2 || 3 || 4 || 5" + validate-npm-package-license "^3.0.1" + +normalize-path@^3.0.0, normalize-path@~3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-3.0.0.tgz#0dcd69ff23a1c9b11fd0978316644a0388216a65" + integrity sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA== + +normalize-range@^0.1.2: + version "0.1.2" + resolved "https://registry.yarnpkg.com/normalize-range/-/normalize-range-0.1.2.tgz#2d10c06bdfd312ea9777695a4d28439456b75942" + integrity sha512-bdok/XvKII3nUpklnV6P2hxtMNrCboOjAcyBuQnWEhO665FwrSNRxU+AqpsyvO6LgGYPspN+lu5CLtw4jPRKNA== + +npm-run-path@^5.1.0: + version "5.2.0" + resolved "https://registry.yarnpkg.com/npm-run-path/-/npm-run-path-5.2.0.tgz#224cdd22c755560253dd71b83a1ef2f758b2e955" + integrity sha512-W4/tgAXFqFA0iL7fk0+uQ3g7wkL8xJmx3XdK0VGb4cHW//eZTtKGvFBBoRKVTpY7n6ze4NL9ly7rgXcHufqXKg== + dependencies: + path-key "^4.0.0" + +nth-check@^2.0.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/nth-check/-/nth-check-2.1.1.tgz#c9eab428effce36cd6b92c924bdb000ef1f1ed1d" + integrity sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w== + dependencies: + boolbase "^1.0.0" + +object-assign@^4.0.1: + version "4.1.1" + resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863" + integrity sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg== + +object-hash@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/object-hash/-/object-hash-3.0.0.tgz#73f97f753e7baffc0e2cc9d6e079079744ac82e9" + integrity sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw== + +object-inspect@^1.9.0: + version "1.13.1" + resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.13.1.tgz#b96c6109324ccfef6b12216a956ca4dc2ff94bc2" + integrity sha512-5qoj1RUiKOMsCCNLV1CBiPYE10sziTsnmNxkAI/rZhiD63CF7IqdFGC/XzjWjpSgLf0LxXX3bDFIh0E18f6UhQ== + +object-is@^1.1.5: + version "1.1.5" + resolved "https://registry.yarnpkg.com/object-is/-/object-is-1.1.5.tgz#b9deeaa5fc7f1846a0faecdceec138e5778f53ac" + integrity sha512-3cyDsyHgtmi7I7DfSSI2LDp6SK2lwvtbg0p0R1e0RvTqF5ceGx+K2dfSjm1bKDMVCFEDAQvy+o8c6a7VujOddw== + dependencies: + call-bind "^1.0.2" + define-properties "^1.1.3" + +object-keys@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/object-keys/-/object-keys-1.1.1.tgz#1c47f272df277f3b1daf061677d9c82e2322c60e" + integrity sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA== + +object.assign@^4.1.4: + version "4.1.5" + resolved "https://registry.yarnpkg.com/object.assign/-/object.assign-4.1.5.tgz#3a833f9ab7fdb80fc9e8d2300c803d216d8fdbb0" + integrity sha512-byy+U7gp+FVwmyzKPYhW2h5l3crpmGsxl7X2s8y43IgxvG4g3QZ6CffDtsNQy1WsmZpQbO+ybo0AlW7TY6DcBQ== + dependencies: + call-bind "^1.0.5" + define-properties "^1.2.1" + has-symbols "^1.0.3" + object-keys "^1.1.1" + +once@^1.3.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1" + integrity sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w== + dependencies: + wrappy "1" + +onetime@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/onetime/-/onetime-6.0.0.tgz#7c24c18ed1fd2e9bca4bd26806a33613c77d34b4" + integrity sha512-1FlR+gjXK7X+AsAHso35MnyN5KqGwJRi/31ft6x0M194ht7S+rWAvd7PHss9xSKMzE0asv1pyIHaJYq+BbacAQ== + dependencies: + mimic-fn "^4.0.0" + +open@^10.0.3: + version "10.0.3" + resolved "https://registry.yarnpkg.com/open/-/open-10.0.3.tgz#f60d8db49fa126c50aec751957fb5d7de3308d4f" + integrity sha512-dtbI5oW7987hwC9qjJTyABldTaa19SuyJse1QboWv3b0qCcrrLNVDqBx1XgELAjh9QTVQaP/C5b1nhQebd1H2A== + dependencies: + default-browser "^5.2.1" + define-lazy-prop "^3.0.0" + is-inside-container "^1.0.0" + is-wsl "^3.1.0" + +optionator@^0.9.3: + version "0.9.3" + resolved "https://registry.yarnpkg.com/optionator/-/optionator-0.9.3.tgz#007397d44ed1872fdc6ed31360190f81814e2c64" + integrity sha512-JjCoypp+jKn1ttEFExxhetCKeJt9zhAgAve5FXHixTvFDW/5aEktX9bufBKLRRMdU7bNtpLfcGu94B3cdEJgjg== + dependencies: + "@aashutoshrathi/word-wrap" "^1.2.3" + deep-is "^0.1.3" + fast-levenshtein "^2.0.6" + levn "^0.4.1" + prelude-ls "^1.2.1" + type-check "^0.4.0" + +os-browserify@^0.3.0: + version "0.3.0" + resolved "https://registry.yarnpkg.com/os-browserify/-/os-browserify-0.3.0.tgz#854373c7f5c2315914fc9bfc6bd8238fdda1ec27" + integrity sha512-gjcpUc3clBf9+210TRaDWbf+rZZZEshZ+DlXMRCeAjp0xhTrnQsKHypIy1J3d5hKdUzj69t708EHtU8P6bUn0A== + +p-limit@^2.2.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-2.3.0.tgz#3dd33c647a214fdfffd835933eb086da0dc21db1" + integrity sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w== + dependencies: + p-try "^2.0.0" + +p-limit@^3.0.2: + version "3.1.0" + resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-3.1.0.tgz#e1daccbe78d0d1388ca18c64fea38e3e57e3706b" + integrity sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ== + dependencies: + yocto-queue "^0.1.0" + +p-limit@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-5.0.0.tgz#6946d5b7140b649b7a33a027d89b4c625b3a5985" + integrity sha512-/Eaoq+QyLSiXQ4lyYV23f14mZRQcXnxfHrN0vCai+ak9G0pp9iEQukIIZq5NccEvwRB8PUnZT0KsOoDCINS1qQ== + dependencies: + yocto-queue "^1.0.0" + +p-locate@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-4.1.0.tgz#a3428bb7088b3a60292f66919278b7c297ad4f07" + integrity sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A== + dependencies: + p-limit "^2.2.0" + +p-locate@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-5.0.0.tgz#83c8315c6785005e3bd021839411c9e110e6d834" + integrity sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw== + dependencies: + p-limit "^3.0.2" + +p-try@^2.0.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/p-try/-/p-try-2.2.0.tgz#cb2868540e313d61de58fafbe35ce9004d5540e6" + integrity sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ== + +pako@~1.0.5: + version "1.0.11" + resolved "https://registry.yarnpkg.com/pako/-/pako-1.0.11.tgz#6c9599d340d54dfd3946380252a35705a6b992bf" + integrity sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw== + +param-case@^3.0.4: + version "3.0.4" + resolved "https://registry.yarnpkg.com/param-case/-/param-case-3.0.4.tgz#7d17fe4aa12bde34d4a77d91acfb6219caad01c5" + integrity sha512-RXlj7zCYokReqWpOPH9oYivUzLYZ5vAPIfEmCTNViosC78F8F0H9y7T7gG2M39ymgutxF5gcFEsyZQSph9Bp3A== + dependencies: + dot-case "^3.0.4" + tslib "^2.0.3" + +parent-module@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/parent-module/-/parent-module-1.0.1.tgz#691d2709e78c79fae3a156622452d00762caaaa2" + integrity sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g== + dependencies: + callsites "^3.0.0" + +parse-asn1@^5.0.0, parse-asn1@^5.1.6: + version "5.1.6" + resolved "https://registry.yarnpkg.com/parse-asn1/-/parse-asn1-5.1.6.tgz#385080a3ec13cb62a62d39409cb3e88844cdaed4" + integrity sha512-RnZRo1EPU6JBnra2vGHj0yhp6ebyjBZpmUCLHWiFhxlzvBCCpAuZ7elsBp1PVAbQN0/04VD/19rfzlBSwLstMw== + dependencies: + asn1.js "^5.2.0" + browserify-aes "^1.0.0" + evp_bytestokey "^1.0.0" + pbkdf2 "^3.0.3" + safe-buffer "^5.1.1" + +parse-json@^5.0.0: + version "5.2.0" + resolved "https://registry.yarnpkg.com/parse-json/-/parse-json-5.2.0.tgz#c76fc66dee54231c962b22bcc8a72cf2f99753cd" + integrity sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg== + dependencies: + "@babel/code-frame" "^7.0.0" + error-ex "^1.3.1" + json-parse-even-better-errors "^2.3.0" + lines-and-columns "^1.1.6" + +pascal-case@^3.1.2: + version "3.1.2" + resolved "https://registry.yarnpkg.com/pascal-case/-/pascal-case-3.1.2.tgz#b48e0ef2b98e205e7c1dae747d0b1508237660eb" + integrity sha512-uWlGT3YSnK9x3BQJaOdcZwrnV6hPpd8jFH1/ucpiLRPh/2zCVJKS19E4GvYHvaCcACn3foXZ0cLB9Wrx1KGe5g== + dependencies: + no-case "^3.0.4" + tslib "^2.0.3" + +path-browserify@^1.0.0, path-browserify@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/path-browserify/-/path-browserify-1.0.1.tgz#d98454a9c3753d5790860f16f68867b9e46be1fd" + integrity sha512-b7uo2UCUOYZcnF/3ID0lulOJi/bafxa1xPe7ZPsammBSpjSWQkjNxlt635YGS2MiR9GjvuXCtz2emr3jbsz98g== + +path-exists@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-4.0.0.tgz#513bdbe2d3b95d7762e8c1137efa195c6c61b5b3" + integrity sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w== + +path-is-absolute@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f" + integrity sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg== + +path-key@^3.1.0: + version "3.1.1" + resolved "https://registry.yarnpkg.com/path-key/-/path-key-3.1.1.tgz#581f6ade658cbba65a0d3380de7753295054f375" + integrity sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q== + +path-key@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/path-key/-/path-key-4.0.0.tgz#295588dc3aee64154f877adb9d780b81c554bf18" + integrity sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ== + +path-parse@^1.0.6, path-parse@^1.0.7: + version "1.0.7" + resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.7.tgz#fbc114b60ca42b30d9daf5858e4bd68bbedb6735" + integrity sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw== + +path-scurry@^1.10.1: + version "1.10.1" + resolved "https://registry.yarnpkg.com/path-scurry/-/path-scurry-1.10.1.tgz#9ba6bf5aa8500fe9fd67df4f0d9483b2b0bfc698" + integrity sha512-MkhCqzzBEpPvxxQ71Md0b1Kk51W01lrYvlMzSUaIzNsODdd7mqhiimSZlr+VegAz5Z6Vzt9Xg2ttE//XBhH3EQ== + dependencies: + lru-cache "^9.1.1 || ^10.0.0" + minipass "^5.0.0 || ^6.0.2 || ^7.0.0" + +path-type@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/path-type/-/path-type-4.0.0.tgz#84ed01c0a7ba380afe09d90a8c180dcd9d03043b" + integrity sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw== + +pathe@^0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/pathe/-/pathe-0.2.0.tgz#30fd7bbe0a0d91f0e60bae621f5d19e9e225c339" + integrity sha512-sTitTPYnn23esFR3RlqYBWn4c45WGeLcsKzQiUpXJAyfcWkolvlYpV8FLo7JishK946oQwMFUCHXQ9AjGPKExw== + +pathe@^1.1.0, pathe@^1.1.1, pathe@^1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/pathe/-/pathe-1.1.2.tgz#6c4cb47a945692e48a1ddd6e4094d170516437ec" + integrity sha512-whLdWMYL2TwI08hn8/ZqAbrVemu0LNaNNJZX73O6qaIdCTfXutsLhMkjdENX0qhsQ9uIimo4/aQOmXkoon2nDQ== + +pathval@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/pathval/-/pathval-1.1.1.tgz#8534e77a77ce7ac5a2512ea21e0fdb8fcf6c3d8d" + integrity sha512-Dp6zGqpTdETdR63lehJYPeIOqpiNBNtc7BpWSLrOje7UaIsE5aY92r/AunQA7rsXvet3lrJ3JnZX29UPTKXyKQ== + +pbkdf2@^3.0.3: + version "3.1.2" + resolved "https://registry.yarnpkg.com/pbkdf2/-/pbkdf2-3.1.2.tgz#dd822aa0887580e52f1a039dc3eda108efae3075" + integrity sha512-iuh7L6jA7JEGu2WxDwtQP1ddOpaJNC4KlDEFfdQajSGgGPNi4OyDc2R7QnbY2bR9QjBVGwgvTdNJZoE7RaxUMA== + dependencies: + create-hash "^1.1.2" + create-hmac "^1.1.4" + ripemd160 "^2.0.1" + safe-buffer "^5.0.1" + sha.js "^2.4.8" + +perfect-debounce@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/perfect-debounce/-/perfect-debounce-1.0.0.tgz#9c2e8bc30b169cc984a58b7d5b28049839591d2a" + integrity sha512-xCy9V055GLEqoFaHoC1SoLIaLmWctgCUaBaWxDZ7/Zx4CTyX7cJQLJOok/orfjZAh9kEYpjJa4d0KcJmCbctZA== + +picocolors@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/picocolors/-/picocolors-1.0.0.tgz#cb5bdc74ff3f51892236eaf79d68bc44564ab81c" + integrity sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ== + +picomatch@^2.0.4, picomatch@^2.2.1, picomatch@^2.2.2, picomatch@^2.3.1: + version "2.3.1" + resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.3.1.tgz#3ba3833733646d9d3e4995946c1365a67fb07a42" + integrity sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA== + +pify@^2.3.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/pify/-/pify-2.3.0.tgz#ed141a6ac043a849ea588498e7dca8b15330e90c" + integrity sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog== + +pirates@^4.0.1: + version "4.0.6" + resolved "https://registry.yarnpkg.com/pirates/-/pirates-4.0.6.tgz#3018ae32ecfcff6c29ba2267cbf21166ac1f36b9" + integrity sha512-saLsH7WeYYPiD25LDuLRRY/i+6HaPYr6G1OUlN39otzkSTxKnubR9RTxS3/Kk50s1g2JTgFwWQDQyplC5/SHZg== + +pkg-dir@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/pkg-dir/-/pkg-dir-5.0.0.tgz#a02d6aebe6ba133a928f74aec20bafdfe6b8e760" + integrity sha512-NPE8TDbzl/3YQYY7CSS228s3g2ollTFnc+Qi3tqmqJp9Vg2ovUpixcJEo2HJScN2Ez+kEaal6y70c0ehqJBJeA== + dependencies: + find-up "^5.0.0" + +pkg-types@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/pkg-types/-/pkg-types-1.0.3.tgz#988b42ab19254c01614d13f4f65a2cfc7880f868" + integrity sha512-nN7pYi0AQqJnoLPC9eHFQ8AcyaixBUOwvqc5TDnIKCMEE6I0y8P7OKA7fPexsXGCGxQDl/cmrLAp26LhcwxZ4A== + dependencies: + jsonc-parser "^3.2.0" + mlly "^1.2.0" + pathe "^1.1.0" + +pluralize@^8.0.0: + version "8.0.0" + resolved "https://registry.yarnpkg.com/pluralize/-/pluralize-8.0.0.tgz#1a6fa16a38d12a1901e0320fa017051c539ce3b1" + integrity sha512-Nc3IT5yHzflTfbjgqWcCPpo7DaKy4FnpB0l/zCAW0Tc7jxAiuqSxHasntB3D7887LSrA93kDJ9IXovxJYxyLCA== + +postcss-calc@^9.0.1: + version "9.0.1" + resolved "https://registry.yarnpkg.com/postcss-calc/-/postcss-calc-9.0.1.tgz#a744fd592438a93d6de0f1434c572670361eb6c6" + integrity sha512-TipgjGyzP5QzEhsOZUaIkeO5mKeMFpebWzRogWG/ysonUlnHcq5aJe0jOjpfzUU8PeSaBQnrE8ehR0QA5vs8PQ== + dependencies: + postcss-selector-parser "^6.0.11" + postcss-value-parser "^4.2.0" + +postcss-colormin@^6.0.2: + version "6.0.2" + resolved "https://registry.yarnpkg.com/postcss-colormin/-/postcss-colormin-6.0.2.tgz#2af9ce753937b08e058dbc6879e4aedfab42806b" + integrity sha512-TXKOxs9LWcdYo5cgmcSHPkyrLAh86hX1ijmyy6J8SbOhyv6ua053M3ZAM/0j44UsnQNIWdl8gb5L7xX2htKeLw== + dependencies: + browserslist "^4.22.2" + caniuse-api "^3.0.0" + colord "^2.9.1" + postcss-value-parser "^4.2.0" + +postcss-convert-values@^6.0.2: + version "6.0.2" + resolved "https://registry.yarnpkg.com/postcss-convert-values/-/postcss-convert-values-6.0.2.tgz#c4a7509aeb1cc7ac3f6948fcbffc2bf8cac7c56a" + integrity sha512-aeBmaTnGQ+NUSVQT8aY0sKyAD/BaLJenEKZ03YK0JnDE1w1Rr8XShoxdal2V2H26xTJKr3v5haByOhJuyT4UYw== + dependencies: + browserslist "^4.22.2" + postcss-value-parser "^4.2.0" + +postcss-discard-comments@^6.0.1: + version "6.0.1" + resolved "https://registry.yarnpkg.com/postcss-discard-comments/-/postcss-discard-comments-6.0.1.tgz#46176212bd9c3e5f48aa4b8b4868786726c41d36" + integrity sha512-f1KYNPtqYLUeZGCHQPKzzFtsHaRuECe6jLakf/RjSRqvF5XHLZnM2+fXLhb8Qh/HBFHs3M4cSLb1k3B899RYIg== + +postcss-discard-duplicates@^6.0.1: + version "6.0.1" + resolved "https://registry.yarnpkg.com/postcss-discard-duplicates/-/postcss-discard-duplicates-6.0.1.tgz#112b1a95948e69b3484fdd43584dda6930977939" + integrity sha512-1hvUs76HLYR8zkScbwyJ8oJEugfPV+WchpnA+26fpJ7Smzs51CzGBHC32RS03psuX/2l0l0UKh2StzNxOrKCYg== + +postcss-discard-empty@^6.0.1: + version "6.0.1" + resolved "https://registry.yarnpkg.com/postcss-discard-empty/-/postcss-discard-empty-6.0.1.tgz#b34cb45ec891246da4506b53e352390fdef126c4" + integrity sha512-yitcmKwmVWtNsrrRqGJ7/C0YRy53i0mjexBDQ9zYxDwTWVBgbU4+C9jIZLmQlTDT9zhml+u0OMFJh8+31krmOg== + +postcss-discard-overridden@^6.0.1: + version "6.0.1" + resolved "https://registry.yarnpkg.com/postcss-discard-overridden/-/postcss-discard-overridden-6.0.1.tgz#c63c559237758d74bc505452393a64dda9b19ef4" + integrity sha512-qs0ehZMMZpSESbRkw1+inkf51kak6OOzNRaoLd/U7Fatp0aN2HQ1rxGOrJvYcRAN9VpX8kUF13R2ofn8OlvFVA== + +postcss-import@^15.1.0: + version "15.1.0" + resolved "https://registry.yarnpkg.com/postcss-import/-/postcss-import-15.1.0.tgz#41c64ed8cc0e23735a9698b3249ffdbf704adc70" + integrity sha512-hpr+J05B2FVYUAXHeK1YyI267J/dDDhMU6B6civm8hSY1jYJnBXxzKDKDswzJmtLHryrjhnDjqqp/49t8FALew== + dependencies: + postcss-value-parser "^4.0.0" + read-cache "^1.0.0" + resolve "^1.1.7" + +postcss-js@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/postcss-js/-/postcss-js-4.0.1.tgz#61598186f3703bab052f1c4f7d805f3991bee9d2" + integrity sha512-dDLF8pEO191hJMtlHFPRa8xsizHaM82MLfNkUHdUtVEV3tgTp5oj+8qbEqYM57SLfc74KSbw//4SeJma2LRVIw== + dependencies: + camelcase-css "^2.0.1" + +postcss-load-config@^4.0.1: + version "4.0.2" + resolved "https://registry.yarnpkg.com/postcss-load-config/-/postcss-load-config-4.0.2.tgz#7159dcf626118d33e299f485d6afe4aff7c4a3e3" + integrity sha512-bSVhyJGL00wMVoPUzAVAnbEoWyqRxkjv64tUl427SKnPrENtq6hJwUojroMz2VB+Q1edmi4IfrAPpami5VVgMQ== + dependencies: + lilconfig "^3.0.0" + yaml "^2.3.4" + +postcss-merge-longhand@^6.0.2: + version "6.0.2" + resolved "https://registry.yarnpkg.com/postcss-merge-longhand/-/postcss-merge-longhand-6.0.2.tgz#cd4e83014851da59545e9a906b245615550f4064" + integrity sha512-+yfVB7gEM8SrCo9w2lCApKIEzrTKl5yS1F4yGhV3kSim6JzbfLGJyhR1B6X+6vOT0U33Mgx7iv4X9MVWuaSAfw== + dependencies: + postcss-value-parser "^4.2.0" + stylehacks "^6.0.2" + +postcss-merge-rules@^6.0.3: + version "6.0.3" + resolved "https://registry.yarnpkg.com/postcss-merge-rules/-/postcss-merge-rules-6.0.3.tgz#08fcf714faaad75b1980ecd961b080ae2f8ddeb3" + integrity sha512-yfkDqSHGohy8sGYIJwBmIGDv4K4/WrJPX355XrxQb/CSsT4Kc/RxDi6akqn5s9bap85AWgv21ArcUWwWdGNSHA== + dependencies: + browserslist "^4.22.2" + caniuse-api "^3.0.0" + cssnano-utils "^4.0.1" + postcss-selector-parser "^6.0.15" + +postcss-minify-font-values@^6.0.1: + version "6.0.1" + resolved "https://registry.yarnpkg.com/postcss-minify-font-values/-/postcss-minify-font-values-6.0.1.tgz#788eb930168be90225f3937f0b70aa19d8b532b2" + integrity sha512-tIwmF1zUPoN6xOtA/2FgVk1ZKrLcCvE0dpZLtzyyte0j9zUeB8RTbCqrHZGjJlxOvNWKMYtunLrrl7HPOiR46w== + dependencies: + postcss-value-parser "^4.2.0" + +postcss-minify-gradients@^6.0.1: + version "6.0.1" + resolved "https://registry.yarnpkg.com/postcss-minify-gradients/-/postcss-minify-gradients-6.0.1.tgz#4faf1880b483dc37016658aa186b42194ff9b5bc" + integrity sha512-M1RJWVjd6IOLPl1hYiOd5HQHgpp6cvJVLrieQYS9y07Yo8itAr6jaekzJphaJFR0tcg4kRewCk3kna9uHBxn/w== + dependencies: + colord "^2.9.1" + cssnano-utils "^4.0.1" + postcss-value-parser "^4.2.0" + +postcss-minify-params@^6.0.2: + version "6.0.2" + resolved "https://registry.yarnpkg.com/postcss-minify-params/-/postcss-minify-params-6.0.2.tgz#bd64af642fa5610281b8a9461598bbb91f92ae05" + integrity sha512-zwQtbrPEBDj+ApELZ6QylLf2/c5zmASoOuA4DzolyVGdV38iR2I5QRMsZcHkcdkZzxpN8RS4cN7LPskOkTwTZw== + dependencies: + browserslist "^4.22.2" + cssnano-utils "^4.0.1" + postcss-value-parser "^4.2.0" + +postcss-minify-selectors@^6.0.2: + version "6.0.2" + resolved "https://registry.yarnpkg.com/postcss-minify-selectors/-/postcss-minify-selectors-6.0.2.tgz#62065b38d3453ddc6627ba50e4f4a2154b031aa0" + integrity sha512-0b+m+w7OAvZejPQdN2GjsXLv5o0jqYHX3aoV0e7RBKPCsB7TYG5KKWBFhGnB/iP3213Ts8c5H4wLPLMm7z28Sg== + dependencies: + postcss-selector-parser "^6.0.15" + +postcss-nested@^6.0.1: + version "6.0.1" + resolved "https://registry.yarnpkg.com/postcss-nested/-/postcss-nested-6.0.1.tgz#f83dc9846ca16d2f4fa864f16e9d9f7d0961662c" + integrity sha512-mEp4xPMi5bSWiMbsgoPfcP74lsWLHkQbZc3sY+jWYd65CUwXrUaTp0fmNpa01ZcETKlIgUdFN/MpS2xZtqL9dQ== + dependencies: + postcss-selector-parser "^6.0.11" + +postcss-normalize-charset@^6.0.1: + version "6.0.1" + resolved "https://registry.yarnpkg.com/postcss-normalize-charset/-/postcss-normalize-charset-6.0.1.tgz#5f70e1eb8bbdbcfcbed060ef70f179e8fef57d0c" + integrity sha512-aW5LbMNRZ+oDV57PF9K+WI1Z8MPnF+A8qbajg/T8PP126YrGX1f9IQx21GI2OlGz7XFJi/fNi0GTbY948XJtXg== + +postcss-normalize-display-values@^6.0.1: + version "6.0.1" + resolved "https://registry.yarnpkg.com/postcss-normalize-display-values/-/postcss-normalize-display-values-6.0.1.tgz#ff9aa30bbf1283294bfd9cc8b6fb81ff060a7f2d" + integrity sha512-mc3vxp2bEuCb4LgCcmG1y6lKJu1Co8T+rKHrcbShJwUmKJiEl761qb/QQCfFwlrvSeET3jksolCR/RZuMURudw== + dependencies: + postcss-value-parser "^4.2.0" + +postcss-normalize-positions@^6.0.1: + version "6.0.1" + resolved "https://registry.yarnpkg.com/postcss-normalize-positions/-/postcss-normalize-positions-6.0.1.tgz#41ffdc72994f024c6cd6e91dbfb40ab9abe6fe90" + integrity sha512-HRsq8u/0unKNvm0cvwxcOUEcakFXqZ41fv3FOdPn916XFUrympjr+03oaLkuZENz3HE9RrQE9yU0Xv43ThWjQg== + dependencies: + postcss-value-parser "^4.2.0" + +postcss-normalize-repeat-style@^6.0.1: + version "6.0.1" + resolved "https://registry.yarnpkg.com/postcss-normalize-repeat-style/-/postcss-normalize-repeat-style-6.0.1.tgz#55dc54b6f80305b280a379899a6626e0a07b04a8" + integrity sha512-Gbb2nmCy6tTiA7Sh2MBs3fj9W8swonk6lw+dFFeQT68B0Pzwp1kvisJQkdV6rbbMSd9brMlS8I8ts52tAGWmGQ== + dependencies: + postcss-value-parser "^4.2.0" + +postcss-normalize-string@^6.0.1: + version "6.0.1" + resolved "https://registry.yarnpkg.com/postcss-normalize-string/-/postcss-normalize-string-6.0.1.tgz#7605e0fb4ec7bf2709709991d13a949e4419db1d" + integrity sha512-5Fhx/+xzALJD9EI26Aq23hXwmv97Zfy2VFrt5PLT8lAhnBIZvmaT5pQk+NuJ/GWj/QWaKSKbnoKDGLbV6qnhXg== + dependencies: + postcss-value-parser "^4.2.0" + +postcss-normalize-timing-functions@^6.0.1: + version "6.0.1" + resolved "https://registry.yarnpkg.com/postcss-normalize-timing-functions/-/postcss-normalize-timing-functions-6.0.1.tgz#ef937b7ca2fd62ed0b46645ea5728b842a3600db" + integrity sha512-4zcczzHqmCU7L5dqTB9rzeqPWRMc0K2HoR+Bfl+FSMbqGBUcP5LRfgcH4BdRtLuzVQK1/FHdFoGT3F7rkEnY+g== + dependencies: + postcss-value-parser "^4.2.0" + +postcss-normalize-unicode@^6.0.2: + version "6.0.2" + resolved "https://registry.yarnpkg.com/postcss-normalize-unicode/-/postcss-normalize-unicode-6.0.2.tgz#361026744ff11baebaec771b60c2a5f36f274fd0" + integrity sha512-Ff2VdAYCTGyMUwpevTZPZ4w0+mPjbZzLLyoLh/RMpqUqeQKZ+xMm31hkxBavDcGKcxm6ACzGk0nBfZ8LZkStKA== + dependencies: + browserslist "^4.22.2" + postcss-value-parser "^4.2.0" + +postcss-normalize-url@^6.0.1: + version "6.0.1" + resolved "https://registry.yarnpkg.com/postcss-normalize-url/-/postcss-normalize-url-6.0.1.tgz#eae58cb4f5f9a4fa5bbbf6d4222dff534ad46186" + integrity sha512-jEXL15tXSvbjm0yzUV7FBiEXwhIa9H88JOXDGQzmcWoB4mSjZIsmtto066s2iW9FYuIrIF4k04HA2BKAOpbsaQ== + dependencies: + postcss-value-parser "^4.2.0" + +postcss-normalize-whitespace@^6.0.1: + version "6.0.1" + resolved "https://registry.yarnpkg.com/postcss-normalize-whitespace/-/postcss-normalize-whitespace-6.0.1.tgz#b5933750b938814c028d3d2b2e5c0199e0037b53" + integrity sha512-76i3NpWf6bB8UHlVuLRxG4zW2YykF9CTEcq/9LGAiz2qBuX5cBStadkk0jSkg9a9TCIXbMQz7yzrygKoCW9JuA== + dependencies: + postcss-value-parser "^4.2.0" + +postcss-ordered-values@^6.0.1: + version "6.0.1" + resolved "https://registry.yarnpkg.com/postcss-ordered-values/-/postcss-ordered-values-6.0.1.tgz#553e735d009065b362da93340e57f43d5f2d0fbc" + integrity sha512-XXbb1O/MW9HdEhnBxitZpPFbIvDgbo9NK4c/5bOfiKpnIGZDoL2xd7/e6jW5DYLsWxBbs+1nZEnVgnjnlFViaA== + dependencies: + cssnano-utils "^4.0.1" + postcss-value-parser "^4.2.0" + +postcss-reduce-initial@^6.0.2: + version "6.0.2" + resolved "https://registry.yarnpkg.com/postcss-reduce-initial/-/postcss-reduce-initial-6.0.2.tgz#763d25902406c872264041df69f182eb15a5d9be" + integrity sha512-YGKalhNlCLcjcLvjU5nF8FyeCTkCO5UtvJEt0hrPZVCTtRLSOH4z00T1UntQPj4dUmIYZgMj8qK77JbSX95hSw== + dependencies: + browserslist "^4.22.2" + caniuse-api "^3.0.0" + +postcss-reduce-transforms@^6.0.1: + version "6.0.1" + resolved "https://registry.yarnpkg.com/postcss-reduce-transforms/-/postcss-reduce-transforms-6.0.1.tgz#7bf59d7c6e7066e3b18ef17237d2344bd3da6d75" + integrity sha512-fUbV81OkUe75JM+VYO1gr/IoA2b/dRiH6HvMwhrIBSUrxq3jNZQZitSnugcTLDi1KkQh1eR/zi+iyxviUNBkcQ== + dependencies: + postcss-value-parser "^4.2.0" + +postcss-selector-parser@^6.0.11, postcss-selector-parser@^6.0.15: + version "6.0.15" + resolved "https://registry.yarnpkg.com/postcss-selector-parser/-/postcss-selector-parser-6.0.15.tgz#11cc2b21eebc0b99ea374ffb9887174855a01535" + integrity sha512-rEYkQOMUCEMhsKbK66tbEU9QVIxbhN18YiniAwA7XQYTVBqrBy+P2p5JcdqsHgKM2zWylp8d7J6eszocfds5Sw== + dependencies: + cssesc "^3.0.0" + util-deprecate "^1.0.2" + +postcss-svgo@^6.0.2: + version "6.0.2" + resolved "https://registry.yarnpkg.com/postcss-svgo/-/postcss-svgo-6.0.2.tgz#dbc9d03e7f346bc0d82443078602a951e0214836" + integrity sha512-IH5R9SjkTkh0kfFOQDImyy1+mTCb+E830+9SV1O+AaDcoHTvfsvt6WwJeo7KwcHbFnevZVCsXhDmjFiGVuwqFQ== + dependencies: + postcss-value-parser "^4.2.0" + svgo "^3.2.0" + +postcss-unique-selectors@^6.0.2: + version "6.0.2" + resolved "https://registry.yarnpkg.com/postcss-unique-selectors/-/postcss-unique-selectors-6.0.2.tgz#09a34a5a31a649d3e9bca5962af0616f39d071d2" + integrity sha512-8IZGQ94nechdG7Y9Sh9FlIY2b4uS8/k8kdKRX040XHsS3B6d1HrJAkXrBSsSu4SuARruSsUjW3nlSw8BHkaAYQ== + dependencies: + postcss-selector-parser "^6.0.15" + +postcss-value-parser@^4.0.0, postcss-value-parser@^4.2.0: + version "4.2.0" + resolved "https://registry.yarnpkg.com/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz#723c09920836ba6d3e5af019f92bc0971c02e514" + integrity sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ== + +postcss@^8.4.23, postcss@^8.4.32, postcss@^8.4.33: + version "8.4.33" + resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.4.33.tgz#1378e859c9f69bf6f638b990a0212f43e2aaa742" + integrity sha512-Kkpbhhdjw2qQs2O2DGX+8m5OVqEcbB9HRBvuYM9pgrjEFUg30A9LmXNlTAUj4S9kgtGyrMbTzVjH7E+s5Re2yg== + dependencies: + nanoid "^3.3.7" + picocolors "^1.0.0" + source-map-js "^1.0.2" + +prelude-ls@^1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.2.1.tgz#debc6489d7a6e6b0e7611888cec880337d316396" + integrity sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g== + +pretty-format@^29.7.0: + version "29.7.0" + resolved "https://registry.yarnpkg.com/pretty-format/-/pretty-format-29.7.0.tgz#ca42c758310f365bfa71a0bda0a807160b776812" + integrity sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ== + dependencies: + "@jest/schemas" "^29.6.3" + ansi-styles "^5.0.0" + react-is "^18.0.0" + +process@^0.11.10: + version "0.11.10" + resolved "https://registry.yarnpkg.com/process/-/process-0.11.10.tgz#7332300e840161bda3e69a1d1d91a7d4bc16f182" + integrity sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A== + +public-encrypt@^4.0.0: + version "4.0.3" + resolved "https://registry.yarnpkg.com/public-encrypt/-/public-encrypt-4.0.3.tgz#4fcc9d77a07e48ba7527e7cbe0de33d0701331e0" + integrity sha512-zVpa8oKZSz5bTMTFClc1fQOnyyEzpl5ozpi1B5YcvBrdohMjH2rfsBtyXcuNuwjsDIXmBYlF2N5FlJYhR29t8Q== + dependencies: + bn.js "^4.1.0" + browserify-rsa "^4.0.0" + create-hash "^1.1.0" + parse-asn1 "^5.0.0" + randombytes "^2.0.1" + safe-buffer "^5.1.2" + +punycode@^1.4.1: + version "1.4.1" + resolved "https://registry.yarnpkg.com/punycode/-/punycode-1.4.1.tgz#c0d5a63b2718800ad8e1eb0fa5269c84dd41845e" + integrity sha512-jmYNElW7yvO7TV33CjSmvSiE2yco3bV2czu/OzDKdMNVZQWfxCblURLhf+47syQRBntjfLdd/H0egrzIG+oaFQ== + +punycode@^2.1.0: + version "2.3.1" + resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.3.1.tgz#027422e2faec0b25e1549c3e1bd8309b9133b6e5" + integrity sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg== + +qs@^6.11.2: + version "6.11.2" + resolved "https://registry.yarnpkg.com/qs/-/qs-6.11.2.tgz#64bea51f12c1f5da1bc01496f48ffcff7c69d7d9" + integrity sha512-tDNIz22aBzCDxLtVH++VnTfzxlfeK5CbqohpSqpJgj1Wg/cQbStNAz3NuqCs5vV+pjBsK4x4pN9HlVh7rcYRiA== + dependencies: + side-channel "^1.0.4" + +querystring-es3@^0.2.1: + version "0.2.1" + resolved "https://registry.yarnpkg.com/querystring-es3/-/querystring-es3-0.2.1.tgz#9ec61f79049875707d69414596fd907a4d711e73" + integrity sha512-773xhDQnZBMFobEiztv8LIl70ch5MSF/jUQVlhwFyBILqq96anmoctVIYz+ZRp0qbCKATTn6ev02M3r7Ga5vqA== + +queue-microtask@^1.2.2: + version "1.2.3" + resolved "https://registry.yarnpkg.com/queue-microtask/-/queue-microtask-1.2.3.tgz#4929228bbc724dfac43e0efb058caf7b6cfb6243" + integrity sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A== + +randombytes@^2.0.0, randombytes@^2.0.1, randombytes@^2.0.5, randombytes@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/randombytes/-/randombytes-2.1.0.tgz#df6f84372f0270dc65cdf6291349ab7a473d4f2a" + integrity sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ== + dependencies: + safe-buffer "^5.1.0" + +randomfill@^1.0.3, randomfill@^1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/randomfill/-/randomfill-1.0.4.tgz#c92196fc86ab42be983f1bf31778224931d61458" + integrity sha512-87lcbR8+MhcWcUiQ+9e+Rwx8MyR2P7qnt15ynUlbm3TU/fjbgz4GsvfSUDTemtCCtVCqb4ZcEFlyPNTh9bBTLw== + dependencies: + randombytes "^2.0.5" + safe-buffer "^5.1.0" + +react-is@^18.0.0: + version "18.2.0" + resolved "https://registry.yarnpkg.com/react-is/-/react-is-18.2.0.tgz#199431eeaaa2e09f86427efbb4f1473edb47609b" + integrity sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w== + +read-cache@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/read-cache/-/read-cache-1.0.0.tgz#e664ef31161166c9751cdbe8dbcf86b5fb58f774" + integrity sha512-Owdv/Ft7IjOgm/i0xvNDZ1LrRANRfew4b2prF3OWMQLxLfu3bS8FVhCsrSCMK4lR56Y9ya+AThoTpDCTxCmpRA== + dependencies: + pify "^2.3.0" + +read-pkg-up@^7.0.1: + version "7.0.1" + resolved "https://registry.yarnpkg.com/read-pkg-up/-/read-pkg-up-7.0.1.tgz#f3a6135758459733ae2b95638056e1854e7ef507" + integrity sha512-zK0TB7Xd6JpCLmlLmufqykGE+/TlOePD6qKClNW7hHDKFh/J7/7gCWGR7joEQEW1bKq3a3yUZSObOoWLFQ4ohg== + dependencies: + find-up "^4.1.0" + read-pkg "^5.2.0" + type-fest "^0.8.1" + +read-pkg@^5.2.0: + version "5.2.0" + resolved "https://registry.yarnpkg.com/read-pkg/-/read-pkg-5.2.0.tgz#7bf295438ca5a33e56cd30e053b34ee7250c93cc" + integrity sha512-Ug69mNOpfvKDAc2Q8DRpMjjzdtrnv9HcSMX+4VsZxD1aZ6ZzrIE7rlzXBtWTyhULSMKg076AW6WR5iZpD0JiOg== + dependencies: + "@types/normalize-package-data" "^2.4.0" + normalize-package-data "^2.5.0" + parse-json "^5.0.0" + type-fest "^0.6.0" + +readable-stream@^3.5.0, readable-stream@^3.6.0, readable-stream@^3.6.2: + version "3.6.2" + resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-3.6.2.tgz#56a9b36ea965c00c5a93ef31eb111a0f11056967" + integrity sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA== + dependencies: + inherits "^2.0.3" + string_decoder "^1.1.1" + util-deprecate "^1.0.1" + +readdirp@~3.6.0: + version "3.6.0" + resolved "https://registry.yarnpkg.com/readdirp/-/readdirp-3.6.0.tgz#74a370bd857116e245b29cc97340cd431a02a6c7" + integrity sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA== + dependencies: + picomatch "^2.2.1" + +regexp-tree@^0.1.27: + version "0.1.27" + resolved "https://registry.yarnpkg.com/regexp-tree/-/regexp-tree-0.1.27.tgz#2198f0ef54518ffa743fe74d983b56ffd631b6cd" + integrity sha512-iETxpjK6YoRWJG5o6hXLwvjYAoW+FEZn9os0PD/b6AP6xQwsa/Y7lCVgIixBbUPMfhu+i2LtdeAqVTgGlQarfA== + +regjsparser@^0.10.0: + version "0.10.0" + resolved "https://registry.yarnpkg.com/regjsparser/-/regjsparser-0.10.0.tgz#b1ed26051736b436f22fdec1c8f72635f9f44892" + integrity sha512-qx+xQGZVsy55CH0a1hiVwHmqjLryfh7wQyF5HO07XJ9f7dQMY/gPQHhlyDkIzJKC+x2fUCpCcUODUUUFrm7SHA== + dependencies: + jsesc "~0.5.0" + +relateurl@^0.2.7: + version "0.2.7" + resolved "https://registry.yarnpkg.com/relateurl/-/relateurl-0.2.7.tgz#54dbf377e51440aca90a4cd274600d3ff2d888a9" + integrity sha512-G08Dxvm4iDN3MLM0EsP62EDV9IuhXPR6blNz6Utcp7zyV3tr4HVNINt6MpaRWbxoOHT3Q7YN2P+jaHX8vUbgog== + +resolve-from@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-4.0.0.tgz#4abcd852ad32dd7baabfe9b40e00a36db5f392e6" + integrity sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g== + +resolve@^1.1.7, resolve@^1.10.0, resolve@^1.17.0, resolve@^1.22.2, resolve@~1.22.1: + version "1.22.8" + resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.22.8.tgz#b6c87a9f2aa06dfab52e3d70ac8cde321fa5a48d" + integrity sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw== + dependencies: + is-core-module "^2.13.0" + path-parse "^1.0.7" + supports-preserve-symlinks-flag "^1.0.0" + +resolve@~1.19.0: + version "1.19.0" + resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.19.0.tgz#1af5bf630409734a067cae29318aac7fa29a267c" + integrity sha512-rArEXAgsBG4UgRGcynxWIWKFvh/XZCcS8UJdHhwy91zwAvCZIbcs+vAbflgBnNjYMs/i/i+/Ux6IZhML1yPvxg== + dependencies: + is-core-module "^2.1.0" + path-parse "^1.0.6" + +reusify@^1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/reusify/-/reusify-1.0.4.tgz#90da382b1e126efc02146e90845a88db12925d76" + integrity sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw== + +rimraf@^3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-3.0.2.tgz#f1a5402ba6220ad52cc1282bac1ae3aa49fd061a" + integrity sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA== + dependencies: + glob "^7.1.3" + +ripemd160@^2.0.0, ripemd160@^2.0.1: + version "2.0.2" + resolved "https://registry.yarnpkg.com/ripemd160/-/ripemd160-2.0.2.tgz#a1c1a6f624751577ba5d07914cbc92850585890c" + integrity sha512-ii4iagi25WusVoiC4B4lq7pbXfAp3D9v5CwfkY33vffw2+pkDjY1D8GaN7spsxvCSx8dkPqOZCEZyfxcmJG2IA== + dependencies: + hash-base "^3.0.0" + inherits "^2.0.1" + +rollup-plugin-terser@^7.0.2: + version "7.0.2" + resolved "https://registry.yarnpkg.com/rollup-plugin-terser/-/rollup-plugin-terser-7.0.2.tgz#e8fbba4869981b2dc35ae7e8a502d5c6c04d324d" + integrity sha512-w3iIaU4OxcF52UUXiZNsNeuXIMDvFrr+ZXK6bFZ0Q60qyVfq4uLptoS4bbq3paG3x216eQllFZX7zt6TIImguQ== + dependencies: + "@babel/code-frame" "^7.10.4" + jest-worker "^26.2.1" + serialize-javascript "^4.0.0" + terser "^5.0.0" + +rollup@^4.2.0: + version "4.9.6" + resolved "https://registry.yarnpkg.com/rollup/-/rollup-4.9.6.tgz#4515facb0318ecca254a2ee1315e22e09efc50a0" + integrity sha512-05lzkCS2uASX0CiLFybYfVkwNbKZG5NFQ6Go0VWyogFTXXbR039UVsegViTntkk4OglHBdF54ccApXRRuXRbsg== + dependencies: + "@types/estree" "1.0.5" + optionalDependencies: + "@rollup/rollup-android-arm-eabi" "4.9.6" + "@rollup/rollup-android-arm64" "4.9.6" + "@rollup/rollup-darwin-arm64" "4.9.6" + "@rollup/rollup-darwin-x64" "4.9.6" + "@rollup/rollup-linux-arm-gnueabihf" "4.9.6" + "@rollup/rollup-linux-arm64-gnu" "4.9.6" + "@rollup/rollup-linux-arm64-musl" "4.9.6" + "@rollup/rollup-linux-riscv64-gnu" "4.9.6" + "@rollup/rollup-linux-x64-gnu" "4.9.6" + "@rollup/rollup-linux-x64-musl" "4.9.6" + "@rollup/rollup-win32-arm64-msvc" "4.9.6" + "@rollup/rollup-win32-ia32-msvc" "4.9.6" + "@rollup/rollup-win32-x64-msvc" "4.9.6" + fsevents "~2.3.2" + +run-applescript@^7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/run-applescript/-/run-applescript-7.0.0.tgz#e5a553c2bffd620e169d276c1cd8f1b64778fbeb" + integrity sha512-9by4Ij99JUr/MCFBUkDKLWK3G9HVXmabKz9U5MlIAIuvuzkiOicRYs8XJLxX+xahD+mLiiCYDqF9dKAgtzKP1A== + +run-parallel@^1.1.9: + version "1.2.0" + resolved "https://registry.yarnpkg.com/run-parallel/-/run-parallel-1.2.0.tgz#66d1368da7bdf921eb9d95bd1a9229e7f21a43ee" + integrity sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA== + dependencies: + queue-microtask "^1.2.2" + +safe-buffer@^5.0.1, safe-buffer@^5.1.0, safe-buffer@^5.1.1, safe-buffer@^5.1.2, safe-buffer@^5.2.0, safe-buffer@^5.2.1, safe-buffer@~5.2.0: + version "5.2.1" + resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.1.tgz#1eaf9fa9bdb1fdd4ec75f58f9cdb4e6b7827eec6" + integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ== + +safer-buffer@^2.1.0: + version "2.1.2" + resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a" + integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg== + +"semver@2 || 3 || 4 || 5": + version "5.7.2" + resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.2.tgz#48d55db737c3287cd4835e17fa13feace1c41ef8" + integrity sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g== + +semver@^7.5.4, semver@~7.5.4: + version "7.5.4" + resolved "https://registry.yarnpkg.com/semver/-/semver-7.5.4.tgz#483986ec4ed38e1c6c48c34894a9182dbff68a6e" + integrity sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA== + dependencies: + lru-cache "^6.0.0" + +serialize-javascript@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/serialize-javascript/-/serialize-javascript-4.0.0.tgz#b525e1238489a5ecfc42afacc3fe99e666f4b1aa" + integrity sha512-GaNA54380uFefWghODBWEGisLZFj00nS5ACs6yHa9nLqlLpVLO8ChDGeKRjZnV4Nh4n0Qi7nhYZD/9fCPzEqkw== + dependencies: + randombytes "^2.1.0" + +set-function-length@^1.1.1: + version "1.2.0" + resolved "https://registry.yarnpkg.com/set-function-length/-/set-function-length-1.2.0.tgz#2f81dc6c16c7059bda5ab7c82c11f03a515ed8e1" + integrity sha512-4DBHDoyHlM1IRPGYcoxexgh67y4ueR53FKV1yyxwFMY7aCqcN/38M1+SwZ/qJQ8iLv7+ck385ot4CcisOAPT9w== + dependencies: + define-data-property "^1.1.1" + function-bind "^1.1.2" + get-intrinsic "^1.2.2" + gopd "^1.0.1" + has-property-descriptors "^1.0.1" + +setimmediate@^1.0.4: + version "1.0.5" + resolved "https://registry.yarnpkg.com/setimmediate/-/setimmediate-1.0.5.tgz#290cbb232e306942d7d7ea9b83732ab7856f8285" + integrity sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA== + +sha.js@^2.4.0, sha.js@^2.4.8: + version "2.4.11" + resolved "https://registry.yarnpkg.com/sha.js/-/sha.js-2.4.11.tgz#37a5cf0b81ecbc6943de109ba2960d1b26584ae7" + integrity sha512-QMEp5B7cftE7APOjk5Y6xgrbWu+WkLVQwk8JNjZ8nKRciZaByEW6MubieAiToS7+dwvrjGhH8jRXz3MVd0AYqQ== + dependencies: + inherits "^2.0.1" + safe-buffer "^5.0.1" + +shebang-command@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/shebang-command/-/shebang-command-2.0.0.tgz#ccd0af4f8835fbdc265b82461aaf0c36663f34ea" + integrity sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA== + dependencies: + shebang-regex "^3.0.0" + +shebang-regex@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/shebang-regex/-/shebang-regex-3.0.0.tgz#ae16f1644d873ecad843b0307b143362d4c42172" + integrity sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A== + +side-channel@^1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/side-channel/-/side-channel-1.0.4.tgz#efce5c8fdc104ee751b25c58d4290011fa5ea2cf" + integrity sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw== + dependencies: + call-bind "^1.0.0" + get-intrinsic "^1.0.2" + object-inspect "^1.9.0" + +siginfo@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/siginfo/-/siginfo-2.0.0.tgz#32e76c70b79724e3bb567cb9d543eb858ccfaf30" + integrity sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g== + +signal-exit@^4.0.1, signal-exit@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-4.1.0.tgz#952188c1cbd546070e2dd20d0f41c0ae0530cb04" + integrity sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw== + +sirv@^2.0.4: + version "2.0.4" + resolved "https://registry.yarnpkg.com/sirv/-/sirv-2.0.4.tgz#5dd9a725c578e34e449f332703eb2a74e46a29b0" + integrity sha512-94Bdh3cC2PKrbgSOUqTiGPWVZeSiXfKOVZNJniWoqrWrRkB1CJzBU3NEbiTsPcYy1lDsANA/THzS+9WBiy5nfQ== + dependencies: + "@polka/url" "^1.0.0-next.24" + mrmime "^2.0.0" + totalist "^3.0.0" + +slash@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/slash/-/slash-3.0.0.tgz#6539be870c165adbd5240220dbe361f1bc4d4634" + integrity sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q== + +source-map-js@^1.0.1, source-map-js@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/source-map-js/-/source-map-js-1.0.2.tgz#adbc361d9c62df380125e7f161f71c826f1e490c" + integrity sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw== + +source-map-support@~0.5.20: + version "0.5.21" + resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.21.tgz#04fe7c7f9e1ed2d662233c28cb2b35b9f63f6e4f" + integrity sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w== + dependencies: + buffer-from "^1.0.0" + source-map "^0.6.0" + +source-map@^0.6.0, source-map@~0.6.0, source-map@~0.6.1: + version "0.6.1" + resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263" + integrity sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g== + +sourcemap-codec@^1.4.8: + version "1.4.8" + resolved "https://registry.yarnpkg.com/sourcemap-codec/-/sourcemap-codec-1.4.8.tgz#ea804bd94857402e6992d05a38ef1ae35a9ab4c4" + integrity sha512-9NykojV5Uih4lgo5So5dtw+f0JgJX30KCNI8gwhz2J9A15wD0Ml6tjHKwf6fTSa6fAdVBdZeNOs9eJ71qCk8vA== + +spdx-correct@^3.0.0: + version "3.2.0" + resolved "https://registry.yarnpkg.com/spdx-correct/-/spdx-correct-3.2.0.tgz#4f5ab0668f0059e34f9c00dce331784a12de4e9c" + integrity sha512-kN9dJbvnySHULIluDHy32WHRUu3Og7B9sbY7tsFLctQkIqnMh3hErYgdMjTYuqmcXX+lK5T1lnUt3G7zNswmZA== + dependencies: + spdx-expression-parse "^3.0.0" + spdx-license-ids "^3.0.0" + +spdx-exceptions@^2.1.0: + version "2.4.0" + resolved "https://registry.yarnpkg.com/spdx-exceptions/-/spdx-exceptions-2.4.0.tgz#c07a4ede25b16e4f78e6707bbd84b15a45c19c1b" + integrity sha512-hcjppoJ68fhxA/cjbN4T8N6uCUejN8yFw69ttpqtBeCbF3u13n7mb31NB9jKwGTTWWnt9IbRA/mf1FprYS8wfw== + +spdx-expression-parse@^3.0.0: + version "3.0.1" + resolved "https://registry.yarnpkg.com/spdx-expression-parse/-/spdx-expression-parse-3.0.1.tgz#cf70f50482eefdc98e3ce0a6833e4a53ceeba679" + integrity sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q== + dependencies: + spdx-exceptions "^2.1.0" + spdx-license-ids "^3.0.0" + +spdx-license-ids@^3.0.0: + version "3.0.16" + resolved "https://registry.yarnpkg.com/spdx-license-ids/-/spdx-license-ids-3.0.16.tgz#a14f64e0954f6e25cc6587bd4f392522db0d998f" + integrity sha512-eWN+LnM3GR6gPu35WxNgbGl8rmY1AEmoMDvL/QD6zYmPWgywxWqJWNdLGT+ke8dKNWrcYgYjPpG5gbTfghP8rw== + +sprintf-js@~1.0.2: + version "1.0.3" + resolved "https://registry.yarnpkg.com/sprintf-js/-/sprintf-js-1.0.3.tgz#04e6926f662895354f3dd015203633b857297e2c" + integrity sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g== + +stackback@0.0.2: + version "0.0.2" + resolved "https://registry.yarnpkg.com/stackback/-/stackback-0.0.2.tgz#1ac8a0d9483848d1695e418b6d031a3c3ce68e3b" + integrity sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw== + +std-env@^3.5.0: + version "3.7.0" + resolved "https://registry.yarnpkg.com/std-env/-/std-env-3.7.0.tgz#c9f7386ced6ecf13360b6c6c55b8aaa4ef7481d2" + integrity sha512-JPbdCEQLj1w5GilpiHAx3qJvFndqybBysA3qUOnznweH4QbNYUsW/ea8QzSrnh0vNsezMMw5bcVool8lM0gwzg== + +stream-browserify@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/stream-browserify/-/stream-browserify-3.0.0.tgz#22b0a2850cdf6503e73085da1fc7b7d0c2122f2f" + integrity sha512-H73RAHsVBapbim0tU2JwwOiXUj+fikfiaoYAKHF3VJfA0pe2BCzkhAHBlLG6REzE+2WNZcxOXjK7lkso+9euLA== + dependencies: + inherits "~2.0.4" + readable-stream "^3.5.0" + +stream-http@^3.2.0: + version "3.2.0" + resolved "https://registry.yarnpkg.com/stream-http/-/stream-http-3.2.0.tgz#1872dfcf24cb15752677e40e5c3f9cc1926028b5" + integrity sha512-Oq1bLqisTyK3TSCXpPbT4sdeYNdmyZJv1LxpEm2vu1ZhK89kSE5YXwZc3cWk0MagGaKriBh9mCFbVGtO+vY29A== + dependencies: + builtin-status-codes "^3.0.0" + inherits "^2.0.4" + readable-stream "^3.6.0" + xtend "^4.0.2" + +string-argv@~0.3.1: + version "0.3.2" + resolved "https://registry.yarnpkg.com/string-argv/-/string-argv-0.3.2.tgz#2b6d0ef24b656274d957d54e0a4bbf6153dc02b6" + integrity sha512-aqD2Q0144Z+/RqG52NeHEkZauTAUWJO8c6yTftGJKO3Tja5tUgIfmIl6kExvhtxSDP7fXB6DvzkfMpCd/F3G+Q== + +"string-width-cjs@npm:string-width@^4.2.0", string-width@^4.1.0: + name string-width-cjs + version "4.2.3" + resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" + integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== + dependencies: + emoji-regex "^8.0.0" + is-fullwidth-code-point "^3.0.0" + strip-ansi "^6.0.1" + +string-width@^5.0.1, string-width@^5.1.2: + version "5.1.2" + resolved "https://registry.yarnpkg.com/string-width/-/string-width-5.1.2.tgz#14f8daec6d81e7221d2a357e668cab73bdbca794" + integrity sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA== + dependencies: + eastasianwidth "^0.2.0" + emoji-regex "^9.2.2" + strip-ansi "^7.0.1" + +string_decoder@^1.0.0, string_decoder@^1.1.1: + version "1.3.0" + resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.3.0.tgz#42f114594a46cf1a8e30b0a84f56c78c3edac21e" + integrity sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA== + dependencies: + safe-buffer "~5.2.0" + +"strip-ansi-cjs@npm:strip-ansi@^6.0.1", strip-ansi@^6.0.0, strip-ansi@^6.0.1: + version "6.0.1" + resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" + integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== + dependencies: + ansi-regex "^5.0.1" + +strip-ansi@^7.0.1: + version "7.1.0" + resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-7.1.0.tgz#d5b6568ca689d8561370b0707685d22434faff45" + integrity sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ== + dependencies: + ansi-regex "^6.0.1" + +strip-final-newline@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/strip-final-newline/-/strip-final-newline-3.0.0.tgz#52894c313fbff318835280aed60ff71ebf12b8fd" + integrity sha512-dOESqjYr96iWYylGObzd39EuNTa5VJxyvVAEm5Jnh7KGo75V43Hk1odPQkNDyXNmUR6k+gEiDVXnjB8HJ3crXw== + +strip-indent@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/strip-indent/-/strip-indent-3.0.0.tgz#c32e1cee940b6b3432c771bc2c54bcce73cd3001" + integrity sha512-laJTa3Jb+VQpaC6DseHhF7dXVqHTfJPCRDaEbid/drOhgitgYku/letMUqOXFoWV0zIIUbjpdH2t+tYj4bQMRQ== + dependencies: + min-indent "^1.0.0" + +strip-json-comments@^3.1.1, strip-json-comments@~3.1.1: + version "3.1.1" + resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-3.1.1.tgz#31f1281b3832630434831c310c01cccda8cbe006" + integrity sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig== + +strip-literal@^1.3.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/strip-literal/-/strip-literal-1.3.0.tgz#db3942c2ec1699e6836ad230090b84bb458e3a07" + integrity sha512-PugKzOsyXpArk0yWmUwqOZecSO0GH0bPoctLcqNDH9J04pVW3lflYE0ujElBGTloevcxF5MofAOZ7C5l2b+wLg== + dependencies: + acorn "^8.10.0" + +stylehacks@^6.0.2: + version "6.0.2" + resolved "https://registry.yarnpkg.com/stylehacks/-/stylehacks-6.0.2.tgz#5bf2654561752547d4548765f35c9a49659b3742" + integrity sha512-00zvJGnCu64EpMjX8b5iCZ3us2Ptyw8+toEkb92VdmkEaRaSGBNKAoK6aWZckhXxmQP8zWiTaFaiMGIU8Ve8sg== + dependencies: + browserslist "^4.22.2" + postcss-selector-parser "^6.0.15" + +sucrase@^3.32.0: + version "3.35.0" + resolved "https://registry.yarnpkg.com/sucrase/-/sucrase-3.35.0.tgz#57f17a3d7e19b36d8995f06679d121be914ae263" + integrity sha512-8EbVDiu9iN/nESwxeSxDKe0dunta1GOlHufmSSXxMD2z2/tMZpDMpvXQGsc+ajGo8y2uYUmixaSRUc/QPoQ0GA== + dependencies: + "@jridgewell/gen-mapping" "^0.3.2" + commander "^4.0.0" + glob "^10.3.10" + lines-and-columns "^1.1.6" + mz "^2.7.0" + pirates "^4.0.1" + ts-interface-checker "^0.1.9" + +supports-color@^5.3.0: + version "5.5.0" + resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-5.5.0.tgz#e2e69a44ac8772f78a1ec0b35b689df6530efc8f" + integrity sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow== + dependencies: + has-flag "^3.0.0" + +supports-color@^7.0.0, supports-color@^7.1.0: + version "7.2.0" + resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-7.2.0.tgz#1b7dcdcb32b8138801b3e478ba6a51caa89648da" + integrity sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw== + dependencies: + has-flag "^4.0.0" + +supports-preserve-symlinks-flag@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz#6eda4bd344a3c94aea376d4cc31bc77311039e09" + integrity sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w== + +svgo@^3.2.0: + version "3.2.0" + resolved "https://registry.yarnpkg.com/svgo/-/svgo-3.2.0.tgz#7a5dff2938d8c6096e00295c2390e8e652fa805d" + integrity sha512-4PP6CMW/V7l/GmKRKzsLR8xxjdHTV4IMvhTnpuHwwBazSIlw5W/5SmPjN8Dwyt7lKbSJrRDgp4t9ph0HgChFBQ== + dependencies: + "@trysound/sax" "0.2.0" + commander "^7.2.0" + css-select "^5.1.0" + css-tree "^2.3.1" + css-what "^6.1.0" + csso "^5.0.5" + picocolors "^1.0.0" + +tailwindcss@^3.4.1: + version "3.4.1" + resolved "https://registry.yarnpkg.com/tailwindcss/-/tailwindcss-3.4.1.tgz#f512ca5d1dd4c9503c7d3d28a968f1ad8f5c839d" + integrity sha512-qAYmXRfk3ENzuPBakNK0SRrUDipP8NQnEY6772uDhflcQz5EhRdD7JNZxyrFHVQNCwULPBn6FNPp9brpO7ctcA== + dependencies: + "@alloc/quick-lru" "^5.2.0" + arg "^5.0.2" + chokidar "^3.5.3" + didyoumean "^1.2.2" + dlv "^1.1.3" + fast-glob "^3.3.0" + glob-parent "^6.0.2" + is-glob "^4.0.3" + jiti "^1.19.1" + lilconfig "^2.1.0" + micromatch "^4.0.5" + normalize-path "^3.0.0" + object-hash "^3.0.0" + picocolors "^1.0.0" + postcss "^8.4.23" + postcss-import "^15.1.0" + postcss-js "^4.0.1" + postcss-load-config "^4.0.1" + postcss-nested "^6.0.1" + postcss-selector-parser "^6.0.11" + resolve "^1.22.2" + sucrase "^3.32.0" + +terser@^5.0.0, terser@^5.10.0: + version "5.27.0" + resolved "https://registry.yarnpkg.com/terser/-/terser-5.27.0.tgz#70108689d9ab25fef61c4e93e808e9fd092bf20c" + integrity sha512-bi1HRwVRskAjheeYl291n3JC4GgO/Ty4z1nVs5AAsmonJulGxpSektecnNedrwK9C7vpvVtcX3cw00VSLt7U2A== + dependencies: + "@jridgewell/source-map" "^0.3.3" + acorn "^8.8.2" + commander "^2.20.0" + source-map-support "~0.5.20" + +text-table@^0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/text-table/-/text-table-0.2.0.tgz#7f5ee823ae805207c00af2df4a84ec3fcfa570b4" + integrity sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw== + +thenify-all@^1.0.0: + version "1.6.0" + resolved "https://registry.yarnpkg.com/thenify-all/-/thenify-all-1.6.0.tgz#1a1918d402d8fc3f98fbf234db0bcc8cc10e9726" + integrity sha512-RNxQH/qI8/t3thXJDwcstUO4zeqo64+Uy/+sNVRBx4Xn2OX+OZ9oP+iJnNFqplFra2ZUVeKCSa2oVWi3T4uVmA== + dependencies: + thenify ">= 3.1.0 < 4" + +"thenify@>= 3.1.0 < 4": + version "3.3.1" + resolved "https://registry.yarnpkg.com/thenify/-/thenify-3.3.1.tgz#8932e686a4066038a016dd9e2ca46add9838a95f" + integrity sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw== + dependencies: + any-promise "^1.0.0" + +timers-browserify@^2.0.4: + version "2.0.12" + resolved "https://registry.yarnpkg.com/timers-browserify/-/timers-browserify-2.0.12.tgz#44a45c11fbf407f34f97bccd1577c652361b00ee" + integrity sha512-9phl76Cqm6FhSX9Xe1ZUAMLtm1BLkKj2Qd5ApyWkXzsMRaA7dgr81kf4wJmQf/hAvg8EEyJxDo3du/0KlhPiKQ== + dependencies: + setimmediate "^1.0.4" + +tinybench@^2.5.1: + version "2.6.0" + resolved "https://registry.yarnpkg.com/tinybench/-/tinybench-2.6.0.tgz#1423284ee22de07c91b3752c048d2764714b341b" + integrity sha512-N8hW3PG/3aOoZAN5V/NSAEDz0ZixDSSt5b/a05iqtpgfLWMSVuCo7w0k2vVvEjdrIoeGqZzweX2WlyioNIHchA== + +tinypool@^0.8.2: + version "0.8.2" + resolved "https://registry.yarnpkg.com/tinypool/-/tinypool-0.8.2.tgz#84013b03dc69dacb322563a475d4c0a9be00f82a" + integrity sha512-SUszKYe5wgsxnNOVlBYO6IC+8VGWdVGZWAqUxp3UErNBtptZvWbwyUOyzNL59zigz2rCA92QiL3wvG+JDSdJdQ== + +tinyspy@^2.2.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/tinyspy/-/tinyspy-2.2.0.tgz#9dc04b072746520b432f77ea2c2d17933de5d6ce" + integrity sha512-d2eda04AN/cPOR89F7Xv5bK/jrQEhmcLFe6HFldoeO9AJtps+fqEnh486vnT/8y4bw38pSyxDcTCAq+Ks2aJTg== + +to-regex-range@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/to-regex-range/-/to-regex-range-5.0.1.tgz#1648c44aae7c8d988a326018ed72f5b4dd0392e4" + integrity sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ== + dependencies: + is-number "^7.0.0" + +totalist@^3.0.0: + version "3.0.1" + resolved "https://registry.yarnpkg.com/totalist/-/totalist-3.0.1.tgz#ba3a3d600c915b1a97872348f79c127475f6acf8" + integrity sha512-sf4i37nQ2LBx4m3wB74y+ubopq6W/dIzXg0FDGjsYnZHVa1Da8FH853wlL2gtUhg+xJXjfk3kUZS3BRoQeoQBQ== + +ts-api-utils@^1.0.1: + version "1.0.3" + resolved "https://registry.yarnpkg.com/ts-api-utils/-/ts-api-utils-1.0.3.tgz#f12c1c781d04427313dbac808f453f050e54a331" + integrity sha512-wNMeqtMz5NtwpT/UZGY5alT+VoKdSsOOP/kqHFcUW1P/VRhH2wJ48+DN2WwUliNbQ976ETwDL0Ifd2VVvgonvg== + +ts-interface-checker@^0.1.9: + version "0.1.13" + resolved "https://registry.yarnpkg.com/ts-interface-checker/-/ts-interface-checker-0.1.13.tgz#784fd3d679722bc103b1b4b8030bcddb5db2a699" + integrity sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA== + +ts-morph@17.0.1: + version "17.0.1" + resolved "https://registry.yarnpkg.com/ts-morph/-/ts-morph-17.0.1.tgz#d85df4fcf9a1fcda1b331d52c00655f381c932d1" + integrity sha512-10PkHyXmrtsTvZSL+cqtJLTgFXkU43Gd0JCc0Rw6GchWbqKe0Rwgt1v3ouobTZwQzF1mGhDeAlWYBMGRV7y+3g== + dependencies: + "@ts-morph/common" "~0.18.0" + code-block-writer "^11.0.3" + +tslib@^2.0.3: + version "2.6.2" + resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.6.2.tgz#703ac29425e7b37cd6fd456e92404d46d1f3e4ae" + integrity sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q== + +tty-browserify@0.0.1: + version "0.0.1" + resolved "https://registry.yarnpkg.com/tty-browserify/-/tty-browserify-0.0.1.tgz#3f05251ee17904dfd0677546670db9651682b811" + integrity sha512-C3TaO7K81YvjCgQH9Q1S3R3P3BtN3RIM8n+OvX4il1K1zgE8ZhI0op7kClgkxtutIE8hQrcrHBXvIheqKUUCxw== + +type-check@^0.4.0, type-check@~0.4.0: + version "0.4.0" + resolved "https://registry.yarnpkg.com/type-check/-/type-check-0.4.0.tgz#07b8203bfa7056c0657050e3ccd2c37730bab8f1" + integrity sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew== + dependencies: + prelude-ls "^1.2.1" + +type-detect@^4.0.0, type-detect@^4.0.8: + version "4.0.8" + resolved "https://registry.yarnpkg.com/type-detect/-/type-detect-4.0.8.tgz#7646fb5f18871cfbb7749e69bd39a6388eb7450c" + integrity sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g== + +type-fest@^0.20.2: + version "0.20.2" + resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.20.2.tgz#1bf207f4b28f91583666cb5fbd327887301cd5f4" + integrity sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ== + +type-fest@^0.6.0: + version "0.6.0" + resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.6.0.tgz#8d2a2370d3df886eb5c90ada1c5bf6188acf838b" + integrity sha512-q+MB8nYR1KDLrgr4G5yemftpMC7/QLqVndBmEEdqzmNj5dcFOO4Oo8qlwZE3ULT3+Zim1F8Kq4cBnikNhlCMlg== + +type-fest@^0.8.1: + version "0.8.1" + resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.8.1.tgz#09e249ebde851d3b1e48d27c105444667f17b83d" + integrity sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA== + +typedarray-to-buffer@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/typedarray-to-buffer/-/typedarray-to-buffer-4.0.0.tgz#cdd2933c61dd3f5f02eda5d012d441f95bfeb50a" + integrity sha512-6dOYeZfS3O9RtRD1caom0sMxgK59b27+IwoNy8RDPsmslSGOyU+mpTamlaIW7aNKi90ZQZ9DFaZL3YRoiSCULQ== + +typescript@5.3.3, typescript@^5.2.2: + version "5.3.3" + resolved "https://registry.yarnpkg.com/typescript/-/typescript-5.3.3.tgz#b3ce6ba258e72e6305ba66f5c9b452aaee3ffe37" + integrity sha512-pXWcraxM0uxAS+tN0AG/BF2TyqmHO014Z070UsJ+pFvYuRSq8KH8DmWpnbXe0pEPDHXZV3FcAbJkijJ5oNEnWw== + +ufo@^1.3.2: + version "1.3.2" + resolved "https://registry.yarnpkg.com/ufo/-/ufo-1.3.2.tgz#c7d719d0628a1c80c006d2240e0d169f6e3c0496" + integrity sha512-o+ORpgGwaYQXgqGDwd+hkS4PuZ3QnmqMMxRuajK/a38L6fTpcE5GPIfrf+L/KemFzfUpeUQc1rRS1iDBozvnFA== + +undici-types@~5.26.4: + version "5.26.5" + resolved "https://registry.yarnpkg.com/undici-types/-/undici-types-5.26.5.tgz#bcd539893d00b56e964fd2657a4866b221a65617" + integrity sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA== + +universalify@^0.1.0: + version "0.1.2" + resolved "https://registry.yarnpkg.com/universalify/-/universalify-0.1.2.tgz#b646f69be3942dabcecc9d6639c80dc105efaa66" + integrity sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg== + +universalify@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/universalify/-/universalify-2.0.1.tgz#168efc2180964e6386d061e094df61afe239b18d" + integrity sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw== + +update-browserslist-db@^1.0.13: + version "1.0.13" + resolved "https://registry.yarnpkg.com/update-browserslist-db/-/update-browserslist-db-1.0.13.tgz#3c5e4f5c083661bd38ef64b6328c26ed6c8248c4" + integrity sha512-xebP81SNcPuNpPP3uzeW1NYXxI3rxyJzF3pD6sH4jE7o/IX+WtSpwnVU+qIsDPyk0d3hmFQ7mjqc6AtV604hbg== + dependencies: + escalade "^3.1.1" + picocolors "^1.0.0" + +uri-js@^4.2.2: + version "4.4.1" + resolved "https://registry.yarnpkg.com/uri-js/-/uri-js-4.4.1.tgz#9b1a52595225859e55f669d928f88c6c57f2a77e" + integrity sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg== + dependencies: + punycode "^2.1.0" + +url@^0.11.0: + version "0.11.3" + resolved "https://registry.yarnpkg.com/url/-/url-0.11.3.tgz#6f495f4b935de40ce4a0a52faee8954244f3d3ad" + integrity sha512-6hxOLGfZASQK/cijlZnZJTq8OXAkt/3YGfQX45vvMYXpZoo8NdWZcY73K108Jf759lS1Bv/8wXnHDTSz17dSRw== + dependencies: + punycode "^1.4.1" + qs "^6.11.2" + +util-deprecate@^1.0.1, util-deprecate@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf" + integrity sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw== + +util@^0.12.4, util@^0.12.5: + version "0.12.5" + resolved "https://registry.yarnpkg.com/util/-/util-0.12.5.tgz#5f17a6059b73db61a875668781a1c2b136bd6fbc" + integrity sha512-kZf/K6hEIrWHI6XqOFUiiMa+79wE/D8Q+NCNAWclkyg3b4d2k7s0QGepNjiABc+aR3N1PAyHL7p6UcLY6LmrnA== + dependencies: + inherits "^2.0.3" + is-arguments "^1.0.4" + is-generator-function "^1.0.7" + is-typed-array "^1.1.3" + which-typed-array "^1.1.2" + +validate-npm-package-license@^3.0.1: + version "3.0.4" + resolved "https://registry.yarnpkg.com/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz#fc91f6b9c7ba15c857f4cb2c5defeec39d4f410a" + integrity sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew== + dependencies: + spdx-correct "^3.0.0" + spdx-expression-parse "^3.0.0" + +validator@^13.7.0: + version "13.11.0" + resolved "https://registry.yarnpkg.com/validator/-/validator-13.11.0.tgz#23ab3fd59290c61248364eabf4067f04955fbb1b" + integrity sha512-Ii+sehpSfZy+At5nPdnyMhx78fEoPDkR2XW/zimHEL3MyGJQOCQ7WeP20jPYRz7ZCpcKLB21NxuXHF3bxjStBQ== + +vite-node@1.2.2: + version "1.2.2" + resolved "https://registry.yarnpkg.com/vite-node/-/vite-node-1.2.2.tgz#f6d329b06f9032130ae6eac1dc773f3663903c25" + integrity sha512-1as4rDTgVWJO3n1uHmUYqq7nsFgINQ9u+mRcXpjeOMJUmviqNKjcZB7UfRZrlM7MjYXMKpuWp5oGkjaFLnjawg== + dependencies: + cac "^6.7.14" + debug "^4.3.4" + pathe "^1.1.1" + picocolors "^1.0.0" + vite "^5.0.0" + +vite-plugin-comlink@^3.0.5: + version "3.0.5" + resolved "https://registry.yarnpkg.com/vite-plugin-comlink/-/vite-plugin-comlink-3.0.5.tgz#bd97207191fd9c880ae5677a1e99716d007b221a" + integrity sha512-my8BE9GFJEaLc7l3e2SfRUL8JJsN9On8PiW7q4Eyq3g6DHUsNqo5WlS7Butuzc8ngrs24Tf1RC8Xfdda+E5T9w== + dependencies: + json5 "2.2.1" + magic-string "0.26.7" + +vite-plugin-dts@^1.7.3: + version "1.7.3" + resolved "https://registry.yarnpkg.com/vite-plugin-dts/-/vite-plugin-dts-1.7.3.tgz#cf0c243fff9ae3fc1f103987b97439b3bf813f15" + integrity sha512-u3t45p6fTbzUPMkwYe0ESwuUeiRMlwdPfD3dRyDKUwLe2WmEYcFyVp2o9/ke2EMrM51lQcmNWdV9eLcgjD1/ng== + dependencies: + "@microsoft/api-extractor" "^7.33.5" + "@rollup/pluginutils" "^5.0.2" + "@rushstack/node-core-library" "^3.53.2" + debug "^4.3.4" + fast-glob "^3.2.12" + fs-extra "^10.1.0" + kolorist "^1.6.0" + ts-morph "17.0.1" + +vite-plugin-html@^3.2.2: + version "3.2.2" + resolved "https://registry.yarnpkg.com/vite-plugin-html/-/vite-plugin-html-3.2.2.tgz#661834fa09015d3fda48ba694dbaa809396f5f7a" + integrity sha512-vb9C9kcdzcIo/Oc3CLZVS03dL5pDlOFuhGlZYDCJ840BhWl/0nGeZWf3Qy7NlOayscY4Cm/QRgULCQkEZige5Q== + dependencies: + "@rollup/pluginutils" "^4.2.0" + colorette "^2.0.16" + connect-history-api-fallback "^1.6.0" + consola "^2.15.3" + dotenv "^16.0.0" + dotenv-expand "^8.0.2" + ejs "^3.1.6" + fast-glob "^3.2.11" + fs-extra "^10.0.1" + html-minifier-terser "^6.1.0" + node-html-parser "^5.3.3" + pathe "^0.2.0" + +vite-plugin-inspect@^0.8.1: + version "0.8.3" + resolved "https://registry.yarnpkg.com/vite-plugin-inspect/-/vite-plugin-inspect-0.8.3.tgz#06ff565f1df84f2ce607007493301579d288cf60" + integrity sha512-SBVzOIdP/kwe6hjkt7LSW4D0+REqqe58AumcnCfRNw4Kt3mbS9pEBkch+nupu2PBxv2tQi69EQHQ1ZA1vgB/Og== + dependencies: + "@antfu/utils" "^0.7.7" + "@rollup/pluginutils" "^5.1.0" + debug "^4.3.4" + error-stack-parser-es "^0.1.1" + fs-extra "^11.2.0" + open "^10.0.3" + perfect-debounce "^1.0.0" + picocolors "^1.0.0" + sirv "^2.0.4" + +vite-plugin-monaco-editor@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/vite-plugin-monaco-editor/-/vite-plugin-monaco-editor-1.1.0.tgz#a6238c2e13d5e98dd54a1bc51f6f189325219de3" + integrity sha512-IvtUqZotrRoVqwT0PBBDIZPNraya3BxN/bfcNfnxZ5rkJiGcNtO5eAOWWSgT7zullIAEqQwxMU83yL9J5k7gww== + +vite-plugin-node-polyfills@^0.17.0: + version "0.17.0" + resolved "https://registry.yarnpkg.com/vite-plugin-node-polyfills/-/vite-plugin-node-polyfills-0.17.0.tgz#1044a4955174ddaeedbc3679b179e1efac9da006" + integrity sha512-iPmPn7376e5u6QvoTSJa16hf5Q0DFwHFXJk2uYpsNlmI3JdPms7hWyh55o+OysJ5jo9J5XPhLC9sMOYifwFd1w== + dependencies: + "@rollup/plugin-inject" "^5.0.5" + buffer-polyfill "npm:buffer@^6.0.3" + node-stdlib-browser "^1.2.0" + process "^0.11.10" + +vite-plugin-static-copy@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/vite-plugin-static-copy/-/vite-plugin-static-copy-1.0.1.tgz#c8aa9871d920b0de9c8583caae5510669546cf8e" + integrity sha512-3eGL4mdZoPJMDBT68pv/XKIHR4MgVolStIxxv1gIBP4R8TpHn9C9EnaU0hesqlseJ4ycLGUxckFTu/jpuJXQlA== + dependencies: + chokidar "^3.5.3" + fast-glob "^3.2.11" + fs-extra "^11.1.0" + picocolors "^1.0.0" + +vite@^5.0.0, vite@^5.0.8: + version "5.0.12" + resolved "https://registry.yarnpkg.com/vite/-/vite-5.0.12.tgz#8a2ffd4da36c132aec4adafe05d7adde38333c47" + integrity sha512-4hsnEkG3q0N4Tzf1+t6NdN9dg/L3BM+q8SWgbSPnJvrgH2kgdyzfVJwbR1ic69/4uMJJ/3dqDZZE5/WwqW8U1w== + dependencies: + esbuild "^0.19.3" + postcss "^8.4.32" + rollup "^4.2.0" + optionalDependencies: + fsevents "~2.3.3" + +vitest@^1.0.4: + version "1.2.2" + resolved "https://registry.yarnpkg.com/vitest/-/vitest-1.2.2.tgz#9e29ad2a74a5df553c30c5798c57a062d58ce299" + integrity sha512-d5Ouvrnms3GD9USIK36KG8OZ5bEvKEkITFtnGv56HFaSlbItJuYr7hv2Lkn903+AvRAgSixiamozUVfORUekjw== + dependencies: + "@vitest/expect" "1.2.2" + "@vitest/runner" "1.2.2" + "@vitest/snapshot" "1.2.2" + "@vitest/spy" "1.2.2" + "@vitest/utils" "1.2.2" + acorn-walk "^8.3.2" + cac "^6.7.14" + chai "^4.3.10" + debug "^4.3.4" + execa "^8.0.1" + local-pkg "^0.5.0" + magic-string "^0.30.5" + pathe "^1.1.1" + picocolors "^1.0.0" + std-env "^3.5.0" + strip-literal "^1.3.0" + tinybench "^2.5.1" + tinypool "^0.8.2" + vite "^5.0.0" + vite-node "1.2.2" + why-is-node-running "^2.2.2" + +vm-browserify@^1.0.1: + version "1.1.2" + resolved "https://registry.yarnpkg.com/vm-browserify/-/vm-browserify-1.1.2.tgz#78641c488b8e6ca91a75f511e7a3b32a86e5dda0" + integrity sha512-2ham8XPWTONajOR0ohOKOHXkm3+gaBmGut3SRuu75xLd/RRaY6vqgh8NBYYk7+RW3u5AtzPQZG8F10LHkl0lAQ== + +wasi-js@^1.7.3: + version "1.7.3" + resolved "https://registry.yarnpkg.com/wasi-js/-/wasi-js-1.7.3.tgz#0831c5f1656de78f4c26bfdccfde45521688e6dc" + integrity sha512-4oq6iVdY4nlsqFviS98ltEdWnhYQoeZwz3/5AxQ3bs0fd/YBzBMS162p/n0DHEIJFev1Jvk3gnJwck1s4a8mVQ== + dependencies: + "@cowasm/memfs" "^3.5.1" + "@wapython/unionfs" "^4.5.7" + debug "^4.3.4" + fflate "^0.7.3" + path-browserify "^1.0.0" + randomfill "^1.0.4" + typedarray-to-buffer "^4.0.0" + +which-typed-array@^1.1.11, which-typed-array@^1.1.2: + version "1.1.13" + resolved "https://registry.yarnpkg.com/which-typed-array/-/which-typed-array-1.1.13.tgz#870cd5be06ddb616f504e7b039c4c24898184d36" + integrity sha512-P5Nra0qjSncduVPEAr7xhoF5guty49ArDTwzJ/yNuPIbZppyRxFQsRCWrocxIY+CnMVG+qfbU2FmDKyvSGClow== + dependencies: + available-typed-arrays "^1.0.5" + call-bind "^1.0.4" + for-each "^0.3.3" + gopd "^1.0.1" + has-tostringtag "^1.0.0" + +which@^2.0.1: + version "2.0.2" + resolved "https://registry.yarnpkg.com/which/-/which-2.0.2.tgz#7c6a8dd0a636a0327e10b59c9286eee93f3f51b1" + integrity sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA== + dependencies: + isexe "^2.0.0" + +why-is-node-running@^2.2.2: + version "2.2.2" + resolved "https://registry.yarnpkg.com/why-is-node-running/-/why-is-node-running-2.2.2.tgz#4185b2b4699117819e7154594271e7e344c9973e" + integrity sha512-6tSwToZxTOcotxHeA+qGCq1mVzKR3CwcJGmVcY+QE8SHy6TnpFnh8PAvPNHYr7EcuVeG0QSMxtYCuO1ta/G/oA== + dependencies: + siginfo "^2.0.0" + stackback "0.0.2" + +"wrap-ansi-cjs@npm:wrap-ansi@^7.0.0": + version "7.0.0" + resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43" + integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q== + dependencies: + ansi-styles "^4.0.0" + string-width "^4.1.0" + strip-ansi "^6.0.0" + +wrap-ansi@^8.1.0: + version "8.1.0" + resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-8.1.0.tgz#56dc22368ee570face1b49819975d9b9a5ead214" + integrity sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ== + dependencies: + ansi-styles "^6.1.0" + string-width "^5.0.1" + strip-ansi "^7.0.1" + +wrappy@1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" + integrity sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ== + +xtend@^4.0.2: + version "4.0.2" + resolved "https://registry.yarnpkg.com/xtend/-/xtend-4.0.2.tgz#bb72779f5fa465186b1f438f674fa347fdb5db54" + integrity sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ== + +yallist@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/yallist/-/yallist-4.0.0.tgz#9bb92790d9c0effec63be73519e11a35019a3a72" + integrity sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A== + +yaml@^2.3.4: + version "2.3.4" + resolved "https://registry.yarnpkg.com/yaml/-/yaml-2.3.4.tgz#53fc1d514be80aabf386dc6001eb29bf3b7523b2" + integrity sha512-8aAvwVUSHpfEqTQ4w/KMlf3HcRdt50E5ODIQJBw1fQ5RL34xabzxtUlzTXVqc4rkZsPbvrXKWnABCD7kWSmocA== + +yocto-queue@^0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/yocto-queue/-/yocto-queue-0.1.0.tgz#0294eb3dee05028d31ee1a5fa2c556a6aaf10a1b" + integrity sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q== + +yocto-queue@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/yocto-queue/-/yocto-queue-1.0.0.tgz#7f816433fb2cbc511ec8bf7d263c3b58a1a3c251" + integrity sha512-9bnSc/HEW2uRy67wc+T8UwauLuPJVn28jb+GtJY16iiKWyvmYJRXVT4UamsAEGQfPohgr2q4Tq0sQbQlxTfi1g== + +z-schema@~5.0.2: + version "5.0.6" + resolved "https://registry.yarnpkg.com/z-schema/-/z-schema-5.0.6.tgz#46d6a687b15e4a4369e18d6cb1c7b8618fc256c5" + integrity sha512-+XR1GhnWklYdfr8YaZv/iu+vY+ux7V5DS5zH1DQf6bO5ufrt/5cgNhVO5qyhsjFXvsqQb/f08DWE9b6uPscyAg== + dependencies: + lodash.get "^4.4.2" + lodash.isequal "^4.5.0" + validator "^13.7.0" + optionalDependencies: + commander "^10.0.0" diff --git a/scripts/build-wasi.sh b/scripts/build-wasi.sh new file mode 100755 index 00000000..88e58a09 --- /dev/null +++ b/scripts/build-wasi.sh @@ -0,0 +1,32 @@ +#!/bin/sh + +set -e + +dotnet --version +export WASI_VERSION=20 +export WASI_VERSION_FULL=${WASI_VERSION}.0 +compiler_dir="$(readlink -f ../Compiler)" +env_file="$(readlink -f ../.env)" +BEBOPC_VERSION=$(grep '^VERSION=' "$env_file" | cut -d '"' -f 2) +export WASI_SDK_PATH="$(readlink -f ~/.wasi-sdk/wasi-sdk-${WASI_VERSION_FULL})" + +echo "Building WASI.." +echo "WASI SDK path: $WASI_SDK_PATH" +dotnet restore "$compiler_dir" +dotnet publish "$compiler_dir" -c Release \ + /p:RuntimeIdentifier=wasi-wasm \ + /p:PublishSingleFile=false \ + /p:WasmSingleFileBundle=true \ + /p:WASI_SDK_PATH="$WASI_SDK_PATH" \ + /p:InvariantGlobalization=true \ + /p:TrimMode=full \ + /p:DebuggerSupport=false \ + /p:EventSourceSupport=false \ + /p:StackTraceSupport=false \ + /p:UseSystemResourceKeys=true \ + /p:NativeDebugSymbols=false \ + /p:WasmNativeStrip=true \ + /p:WasmEnableSIMD=false \ + /p:WasmExceptionHandling=false \ + /p:WasmRunWasmOpt=true \ + /p:ReleaseVersion="$BEBOPC_VERSION" diff --git a/scripts/install-wasi.sh b/scripts/install-wasi.sh new file mode 100755 index 00000000..80262bd3 --- /dev/null +++ b/scripts/install-wasi.sh @@ -0,0 +1,66 @@ +#!/bin/sh + +set -e + +# Define the WASI version +export WASI_VERSION=20 +export WASI_VERSION_FULL=${WASI_VERSION}.0 +# Path to the hidden WASI SDK directory +install_path="$(readlink -f ~/.wasi-sdk)" +export WASI_SDK_PATH="$install_path/wasi-sdk-${WASI_VERSION_FULL}" + +# Function to download and extract WASI SDK +download_and_extract() { + os=$1 + file_name="wasi-sdk-${WASI_VERSION_FULL}-${os}.tar.gz" + url="https://github.com/WebAssembly/wasi-sdk/releases/download/wasi-sdk-${WASI_VERSION}/$file_name" + # Download the file + wget "$url" + tar -xvf "$file_name" -C "$install_path" + # Clean up: remove the downloaded tar.gz file + rm "$file_name" +} + +cleanup() { + # Check if the tar file exists and delete it + file_name="wasi-sdk-${WASI_VERSION_FULL}-$(uname -s).tar.gz" + if [ -f "$file_name" ]; then + rm "$file_name" + fi +} + +# Check if the .wasi-sdk directory exists and delete it if it does +if [ -d "$install_path" ]; then + echo "Existing WASI SDK directory found. Deleting it..." + rm -rf "$install_path" +fi + +# Create a new hidden directory for WASI SDK +mkdir -p "$install_path" + +# Check the operating system +case "$(uname -s)" in +Darwin) + echo "MacOS detected" + download_and_extract "macos" + ;; +Linux) + echo "Linux detected" + download_and_extract "linux" + ;; +*) + echo "Unsupported operating system" + exit 1 + ;; +esac + +echo "WASI SDK installed successfully at $WASI_SDK_PATH" + +# Check if wasi-experimental workload is already installed +if dotnet workload list | grep -q 'wasi-experimental'; then + echo "WASI experimental workload is already installed." +else + echo "Installing WASI experimental workload..." + dotnet workload install wasi-experimental + echo "WASI workload installed successfully" +fi diff --git a/vscode-bebop/package.json b/vscode-bebop/package.json index 8406e59b..777056a6 100644 --- a/vscode-bebop/package.json +++ b/vscode-bebop/package.json @@ -73,6 +73,13 @@ "*bebop.json" ], "url": "./schemas/bebop-schema.json" + }, + { + "fileMatch": [ + "chord.json", + "*chord.json" + ], + "url": "./schemas/chord-schema.json" } ], "grammars": [ diff --git a/vscode-bebop/schemas/bebop-schema.json b/vscode-bebop/schemas/bebop-schema.json index 4b96cc59..e4ba1c88 100644 --- a/vscode-bebop/schemas/bebop-schema.json +++ b/vscode-bebop/schemas/bebop-schema.json @@ -1,148 +1,174 @@ { - "$schema": "http://json-schema.org/draft-04/schema#", - "id": "https://json.schemastore.org/bebop", - "title": "JSON schema for the Bebop compiler's configuration file", - "type": "object", - "definitions": { - "//": { - "explainer": "https://github.com/RainwayApp/bebop/wiki", - "reference": "https://github.com/RainwayApp/bebop/wiki/bebop.json-configuration" - }, - "excludeDefinition": { - "properties": { - "exclude": { - "description": "Specifies an array of filenames or patterns that should be skipped when resolving include. The 'exclude' property only affects the files included via the 'include' property.", - "type": "array", - "uniqueItems": true, - "items": { - "type": "string" - } - } - } - }, - "includeDefinition": { - "properties": { - "include": { - "description": "Specifies an array of filenames or patterns to compile. These filenames are resolved relative to the directory containing the bebop.json file. If no 'include' property is present in a bebop.json, the compiler defaults to including all files in the containing directory and subdirectories except those specified by 'exclude'.", - "type": "array", - "uniqueItems": true, - "items": { - "type": "string" - } - } - } - }, - "namespaceDefinition": { - "properties": { + "$schema": "http://json-schema.org/draft-04/schema#", + "id": "https://json.schemastore.org/bebop", + "title": "JSON schema for the Bebop compiler's configuration file", + "type": "object", + "properties": { + "generators": { + "$ref": "#/definitions/generatorsDefinition/properties/generators" + }, + "include": { + "$ref": "#/definitions/includeDefinition/properties/include" + }, + "exclude": { + "$ref": "#/definitions/excludeDefinition/properties/exclude" + }, + "watchOptions": { + "$ref": "#/definitions/watchOptionsDefinition/properties/watchOptions" + }, + "noWarn": { + "$ref": "#/definitions/noWarnDefinition/properties/noWarn" + }, + "noEmit": { + "$ref": "#/definitions/noEmitDefinition/properties/noEmit" + }, + "extensions": { + "$ref": "#/definitions/extensionsDefinition/properties/extensions" + } + }, + "additionalProperties": false, + "definitions": { + "//": { + "explainer": "https://github.com/betwixt-labs/bebop/wiki", + "reference": "https://github.com/betwixt-labs/bebop/wiki/bebop.json-configuration" + }, + "excludeDefinition": { + "properties": { + "exclude": { + "description": "Specifies an array of filenames or patterns that should be skipped when resolving include. The 'exclude' property only affects the files included via the 'include' property.", + "type": "array", + "uniqueItems": true, + "items": { + "type": "string" + } + } + } + }, + "includeDefinition": { + "properties": { + "include": { + "description": "Specifies an array of filenames or patterns to include in the compiler. These filenames are resolved relative to the directory containing the bebop.json file.", + "type": "array", + "uniqueItems": true, + "items": { + "type": "string" + } + } + } + }, + "generatorsDefinition": { + "properties": { + "generators": { + "description": "Specifies code generators to use for compilation.", + "type": "object", + "patternProperties": { + "^(cs|ts|cpp|dart|rust|py|[a-z]{1,7})$": { + "type": "object", + "properties": { + "outFile": { + "description": "Specify a file that bundles all generated code into one file.", + "type": "string" + }, + "services": { + "description": "By default, bebopc generates a concrete client and a service base class. This property can be used to limit bebopc asset generation.", + "type": "string", + "enum": ["none", "client", "server", "both"], + "default": "both" + }, + "emitNotice": { + "description": "Specify if the code generator should produce a notice stating code was auto-generated.", + "type": "boolean" + }, + "emitBinarySchema": { + "description": "Specify if the code generator should emit a binary schema in the output file that can be used for dynamic serialization.", + "type": "boolean", + "default": false + }, "namespace": { - "description": "Specifies a namespace that generated code will use.", - "type": "string", - "minLength": 1 - } - } - }, - "generatorsDefinition": { - "properties": { - "generators": { - "description": "Specifies a list of code generators to target during compilation.", - "type": "array", - "uniqueItems": true, - "items": { - "type": "object", - "properties": { - "alias": { - "description": "Specify the code generator schemas will be compiled to.", - "type": "string", - "enum": [ - "cs", - "ts", - "cpp", - "dart", - "rust" - ] - }, - "outFile": { - "description": "Specify a file that bundles all generated code into one file.", - "type": "string" - }, - "langVersion": { - "description": "Specify the version of the language the code generator should target.", - "type": "string" - }, - "noGenerationNotice": { - "default": false, - "description": "Specify if the code generator should produces a notice at the start of the output file stating code was auto-generated.", - "type": "boolean" - }, - "emitBinarySchema": { - "default": false, - "description": "Specify if the code generator should emit a binary schema in the output file that can be used for dynamic serialization.", - "type": "boolean" - }, - "services": { - "default": "both", - "description": "By default, bebopc generates a concrete client and a service base class. This property can be used to limit bebopc asset generation.", - "type": "string", - "enum": [ - "none", - "client", - "server", - "both" - ] - } - }, - "required": [ - "alias", - "outFile" - ], - "additionalProperties": false - } + "description": "Specify a namespace for the generated code.", + "type": "string", + "minLength": 1, + "pattern": "^[a-zA-Z]+(\\.[a-zA-Z]+)*$" + }, + "options": { + "description": "Specify custom options for the code generator.", + "type": "object", + "additionalProperties": { + "type": "string" + } } + }, + "required": ["outFile"], + "additionalProperties": false } - }, - "watchOptionsDefinition": { - "properties": { - "watchOptions": { - "type": "object", - "description": "Settings for the watch mode in bebopc.", - "properties": { - "excludeFiles": { - "description": "Remove a list of files from the watch mode's processing.", - "type": "array", - "uniqueItems": true, - "items": { - "type": "string" - } - }, - "excludeDirectories": { - "description": "Remove a list of directories from the watch process.", - "type": "array", - "uniqueItems": true, - "items": { - "type": "string" - } - } - }, - "additionalProperties": false - } + }, + "additionalProperties": false + } + } + }, + "watchOptionsDefinition": { + "properties": { + "watchOptions": { + "type": "object", + "description": "Settings for the watch mode in bebopc.", + "properties": { + "excludeFiles": { + "description": "Remove a list of files from the watch mode's processing.", + "type": "array", + "uniqueItems": true, + "items": { + "type": "string" + } + }, + "excludeDirectories": { + "description": "Remove a list of directories from the watch process.", + "type": "array", + "uniqueItems": true, + "items": { + "type": "string" + } } + }, + "additionalProperties": false + } + } + }, + "noWarnDefinition": { + "properties": { + "noWarn": { + "description": "Specifies an array of warning codes to silence", + "type": "array", + "uniqueItems": true, + "items": { + "type": "number" + } } + } }, - "allOf": [ - { - "$ref": "#/definitions/generatorsDefinition" - }, - { - "$ref": "#/definitions/namespaceDefinition" - }, - { - "$ref": "#/definitions/includeDefinition" - }, - { - "$ref": "#/definitions/excludeDefinition" - }, - { - "$ref": "#/definitions/watchOptionsDefinition" + "noEmitDefinition": { + "properties": { + "noEmit": { + "description": "Disable emitting files from a compilation.", + "type": "boolean" + } + } + }, + "extensionsDefinition": { + "properties": { + "extensions": { + "type": "object", + "description": "An object of extensions the compiler should load.", + "patternProperties": { + "^(?:(?:@(?:[a-z0-9-*~][a-z0-9-*._~]*)?/[a-z0-9-._~])|[a-z0-9-~])[a-z0-9-._~]*$": { + "type": "string", + "description": "The version of the extension, in semver format without comparison operators.", + "pattern": "^(0|[1-9]\\d*)\\.(0|[1-9]\\d*)\\.(0|[1-9]\\d*)(?:-([0-9A-Za-z-]+(\\.[0-9A-Za-z-]+)*))?$", + "patternErrorMessage": "Must be in semver format without comparison operators." + } + }, + "additionalProperties": false } - ] -} \ No newline at end of file + } + } + } +} diff --git a/vscode-bebop/schemas/chord-schema.json b/vscode-bebop/schemas/chord-schema.json new file mode 100644 index 00000000..ccdc7238 --- /dev/null +++ b/vscode-bebop/schemas/chord-schema.json @@ -0,0 +1,519 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "title": "JSON schema for chord.json files", + "$defs": { + "url": { + "type": "string", + "format": "uri", + "pattern": "^https://.+", + "patternErrorMessage": "Must be an HTTPS URL." + }, + "email": { + "type": "string", + "format": "email", + "maxLength": 254 + }, + "license": { + "anyOf": [ + { + "type": "string" + }, + { + "enum": [ + "Apache-2.0", + "MIT", + "ISC", + "BSD-3-Clause", + "BSD-2-Clause", + "CC0-1.0", + "CDDL-1.1", + "LGPL-2.1-only", + "LGPL-2.1-or-later", + "LGPL-3.0-only", + "LGPL-3.0-or-later", + "EPL-1.0", + "EPL-2.0", + "MS-PL", + "UNLICENSED" + ] + } + ] + } + }, + "type": "object", + "properties": { + "name": { + "type": "string", + "description": "The name of the extension, part of URL, command line argument, and folder name. Must be URL-safe, no uppercase, <= 214 characters.", + "minLength": 1, + "maxLength": 214, + "pattern": "^(?:(?:@(?:[a-z0-9-*~][a-z0-9-*._~]*)?/[a-z0-9-._~])|[a-z0-9-~])[a-z0-9-._~]*$", + "patternErrorMessage": "Must be URL-safe, no uppercase, <= 214 characters." + }, + "private": { + "type": "boolean", + "description": "If true, the extension will not be published to the registry." + }, + "description": { + "type": "string", + "description": "The description of the extension.", + "minLength": 1, + "maxLength": 280 + }, + "version": { + "type": "string", + "description": "The version of the extension, in semver format without comparison operators.", + "pattern": "^(0|[1-9]\\d*)\\.(0|[1-9]\\d*)\\.(0|[1-9]\\d*)(?:-([0-9A-Za-z-]+(\\.[0-9A-Za-z-]+)*))?$", + "patternErrorMessage": "Must be in semver format without comparison operators." + }, + "repository": { + "type": "string", + "description": "The HTTPS URL of the extension's repository.", + "$ref": "#/$defs/url" + }, + "author": { + "type": "object", + "description": "The author of the extension, with name, optional email and URL.", + "properties": { + "name": { + "type": "string", + "description": "The name of the author.", + "minLength": 1, + "maxLength": 50 + }, + "email": { + "$ref": "#/$defs/email", + "description": "The email of the author." + }, + "url": { + "description": "The URL of the author.", + "$ref": "#/$defs/url" + } + }, + "required": ["name"], + "additionalProperties": false + }, + "license": { + "$ref": "#/$defs/license", + "description": "You should specify a license for your package so that people know how they are permitted to use it, and any restrictions you're placing on it." + }, + "bugs": { + "type": "object", + "description": "Where to report issues about the extension.", + "properties": { + "url": { + "description": "The URL to the issue tracker.", + "$ref": "#/$defs/url" + }, + "email": { + "$ref": "#/$defs/email", + "description": "The email to the issue tracker." + } + }, + "required": ["url"], + "additionalProperties": false + }, + "homepage": { + "description": "The URL to the homepage of the extension.", + "$ref": "#/$defs/url" + }, + "dependencies": { + "type": "object", + "description": "An object of dependencies with extension name as key and semver range as value.", + "patternProperties": { + "^(?:(?:@(?:[a-z0-9-*~][a-z0-9-*._~]*)?/[a-z0-9-._~])|[a-z0-9-~])[a-z0-9-._~]*$": { + "type": "string", + "description": "Semver range for the dependency.", + "pattern": "^(\\*|[0-9]+(\\.[0-9]+)?(\\.[0-9]+)?(-[0-9A-Za-z-]+(\\.[0-9A-Za-z-]+)*)?([ \\t]*[-][ \\t]*(0|[1-9][0-9]*)\\.([0-9]+)\\.([0-9]+)(-[0-9A-Za-z-]+(\\.[0-9A-Za-z-]+)*)?)?|[0-9]+(\\.[0-9]+)?(\\.[0-9]+)?(-[0-9A-Za-z-]+(\\.[0-9A-Za-z-]+)*)?([ \\t]*[<>=]+[ \\t]*(0|[1-9][0-9]*)\\.([0-9]+)\\.([0-9]+)(-[0-9A-Za-z-]+(\\.[0-9A-Za-z-]+)*)?)*|~[0-9]+(\\.[0-9]+)?(\\.[0-9]+)?|\\^[0-9]+(\\.[0-9]+)?(\\.[0-9]+)?)$", + "patternErrorMessage": "Must be a valid semver or semver range. (e.g. ^1.0.0 or >=1.0.0 <2.0.0)" + } + }, + "additionalProperties": false + }, + "bin": { + "type": "string", + "description": "Path to the compiled extension, a valid relative file path.", + "pattern": "^((\\.?\\.?\\/?)|(\\.?\\.?\\\\?))([\\w\\-\\s\\/\\\\]+\\/)*[\\w\\-\\s]+\\.wasm$", + "patternErrorMessage": "Must be a valid relative file path to the compiled extensions .wasm file (e.g. ./bin/extension.wasm)." + }, + "build": { + "type": "object", + "description": "Defines how to build the extension, including command, args, env, and shell.", + "properties": { + "script": { + "type": "string", + "description": "The script to run to build the extension.", + "minLength": 1 + }, + "args": { + "type": "array", + "description": "Array of arguments to pass to the build command.", + "items": { + "type": "string" + } + }, + "env": { + "type": "object", + "description": "Environment variables to set before running the build command.", + "additionalProperties": { + "type": "string" + } + }, + "shell": { + "type": "string", + "description": "The shell to use when running the build command.", + "enum": ["bash", "sh", "pwsh", "cmd", "powershell", "python"] + }, + "compiler": { + "type": "string", + "description": "The compiler that is used to build the WASM binary.", + "enum": ["as", "tinygo"] + } + }, + "required": ["script", "compiler"], + "additionalProperties": false + }, + "pack": { + "type": "object", + "description": "Defines how to package the extension.", + "minProperties": 1, + "patternProperties": { + "^[a-z]{1,7}$": { + "type": "object", + "description": "Defines how to package the extension, including auxiliaryFile.", + "properties": { + "auxiliaryFile": { + "type": "string", + "description": "Path to an auxiliary file that will be packaged with the extension.", + "pattern": "^((\\.?\\.?\\/?)|(\\.?\\.?\\\\?))([\\w\\-\\/\\\\]+\\/)*[\\w\\-]+(\\.\\w+)$", + "patternErrorMessage": "Must be a valid relative file path to an auxiliary file (e.g. ./auxiliary.txt)." + } + }, + "required": ["auxiliaryFile"], + "patternErrorMessage": "Must be a valid generator alias.", + "minLength": 1, + "maxLength": 7 + } + }, + "additionalProperties": false + }, + "contributes": { + "type": "object", + "description": "Defines what the extension contributes to bebopc, including generator, decorators, and extends.", + "oneOf": [ + { + "required": ["generator"] + }, + { + "required": ["extends"] + } + ], + "properties": { + "generator": { + "type": "object", + "description": "Defines the code generator that the extension contributes to bebopc.", + "properties": { + "name": { + "type": "string", + "description": "The friendly name of the generator.", + "minLength": 1, + "maxLength": 32, + "pattern": "^[a-zA-Z ]+$", + "patternErrorMessage": "Must be a valid friendly name (e.g. CSharp)." + }, + "alias": { + "type": "string", + "description": "The alias of the generator.", + "minLength": 1, + "maxLength": 7, + "pattern": "^(?!cs$|py$|ts$|rust$|dart$|cpp$)[a-z]+$", + "patternErrorMessage": "Alias cannot be the same as a built-in generator and must be lowercase (e.g. csharp)." + } + }, + "required": ["name", "alias"], + "additionalProperties": false + }, + "decorators": { + "type": "object", + "description": "Defines the decorators that the extension contributes to bebopc.", + "minProperties": 1, + "patternProperties": { + "^[a-z]+([A-Z][a-z]+)*$": { + "type": "object", + "properties": { + "description": { + "type": "string", + "description": "The description of the decorator.", + "minLength": 1, + "maxLength": 280 + }, + "targets": { + "type": "string", + "description": "Flag of types the decorator is usable on.", + "pattern": "^(all|((enum|message|struct|union|field|service|method)(\\|(enum|message|struct|union|field|service|method))*))$", + "patternErrorMessage": "Must be a valid flag of types the decorator is valid on. (e.g. enum, message, struct, union, field, service, method) or 'all'." + }, + "allowMultiple": { + "type": "boolean", + "description": "Indicates if the decorator can be used multiple times on the same target." + }, + "parameters": { + "type": "object", + "description": "Possible parameters for the decorator.", + "minProperties": 1, + "patternProperties": { + "^[a-z]+([A-Z][a-z]+)*$": { + "type": "object", + "properties": { + "description": { + "type": "string", + "description": "The description of the parameter.", + "minLength": 1, + "maxLength": 280 + }, + "required": { + "type": "boolean", + "description": "Indicates if the parameter is required." + }, + "type": { + "type": "string", + "description": "The type of the parameter.", + "enum": [ + "bool", + "byte", + "uint8", + "uint16", + "int16", + "uint32", + "int32", + "uint64", + "int64", + "float32", + "float64", + "string" + ] + }, + "default": { + "description": "The default value of the parameter." + }, + "validator": { + "type": "string", + "description": "Regex pattern to validate value of the argument passed into the parameter.", + "pattern": "^.+$", + "patternErrorMessage": "Must be a valid regex pattern." + }, + "validationErrorReason": { + "type": "string", + "description": "The error message to display when the validator fails." + } + }, + "required": ["description", "type"], + "additionalProperties": false, + "oneOf": [ + { + "properties": { + "type": { + "const": "bool" + }, + "default": { + "type": "boolean" + } + } + }, + { + "properties": { + "type": { + "const": "string" + }, + "default": { + "type": "string", + "minLength": 1 + } + } + }, + { + "properties": { + "type": { + "const": "uint8" + }, + "default": { + "type": "number", + "minimum": 0, + "maximum": 255 + } + } + }, + { + "properties": { + "type": { + "const": "byte" + }, + "default": { + "type": "number", + "minimum": 0, + "maximum": 255 + } + } + }, + { + "properties": { + "type": { + "const": "uint16" + }, + "default": { + "type": "number", + "minimum": 0, + "maximum": 65535 + } + } + }, + { + "properties": { + "type": { + "const": "int16" + }, + "default": { + "type": "number", + "minimum": -32768, + "maximum": 32767 + } + } + }, + { + "properties": { + "type": { + "const": "uint32" + }, + "default": { + "type": "number", + "minimum": 0, + "maximum": 4294967295 + } + } + }, + { + "properties": { + "type": { + "const": "int32" + }, + "default": { + "type": "number", + "minimum": -2147483648, + "maximum": 2147483647 + } + } + }, + { + "properties": { + "type": { + "const": "uint64" + }, + "default": { + "type": "number", + "minimum": 0, + "maximum": 18446744073709551615 + } + } + }, + { + "properties": { + "type": { + "const": "int64" + }, + "default": { + "type": "number", + "minimum": -9223372036854775808, + "maximum": 9223372036854775807 + } + } + }, + { + "properties": { + "type": { + "const": "float32" + }, + "default": { + "type": "number" + } + } + }, + { + "properties": { + "type": { + "const": "float64" + }, + "default": { + "type": "number" + } + } + } + ] + } + }, + "additionalProperties": false + } + }, + "required": ["description"], + "additionalProperties": false + } + }, + "additionalProperties": false + }, + "extends": { + "type": "array", + "description": "List of generator aliases that the extension extends. Should only be present if the extension is not a generator extension.", + "items": { + "type": "string", + "description": "Generator alias that the extension extends.", + "anyOf": [ + { + "enum": ["cs", "py", "ts", "rust", "dart", "cpp"] + }, + { + "pattern": "^[a-z]{1,7}$", + "patternErrorMessage": "Must be a valid generator alias.", + "minLength": 1, + "maxLength": 7 + } + ] + }, + "minItems": 1, + "uniqueItems": true + }, + "additionalProperties": false + } + }, + "readme": { + "type": "string", + "description": "Path to the compiled extension, a valid relative file path.", + "pattern": "^((\\.?\\.?\\/?)|(\\.?\\.?\\\\?))([\\w\\-\\s\\/\\\\]+\\/)*[\\w\\-\\s]+\\.md$", + "patternErrorMessage": "Must be a valid relative file path to the README.md for the extension.", + "minLength": 1 + }, + "engine": { + "type": "object", + "description": "An object containing at least the bebopc key matching the versions of Bebopc that the extension is compatible with. Cannot be *. For example: ^3.0.5 indicates compatibility with a minimum VS Code version of 3.0.5.", + "properties": { + "bebopc": { + "type": "string", + "description": "The semver range of bebopc that the extension is compatible with.", + "pattern": "^(\\*|[0-9]+(\\.[0-9]+)?(\\.[0-9]+)?(-[0-9A-Za-z-]+(\\.[0-9A-Za-z-]+)*)?([ \\t]*[-][ \\t]*(0|[1-9][0-9]*)\\.([0-9]+)\\.([0-9]+)(-[0-9A-Za-z-]+(\\.[0-9A-Za-z-]+)*)?)?|[0-9]+(\\.[0-9]+)?(\\.[0-9]+)?(-[0-9A-Za-z-]+(\\.[0-9A-Za-z-]+)*)?([ \\t]*[<>=]+[ \\t]*(0|[1-9][0-9]*)\\.([0-9]+)\\.([0-9]+)(-[0-9A-Za-z-]+(\\.[0-9A-Za-z-]+)*)?)*|~[0-9]+(\\.[0-9]+)?(\\.[0-9]+)?|\\^[0-9]+(\\.[0-9]+)?(\\.[0-9]+)?)$", + "patternErrorMessage": "Must be a valid semver or semver range. (e.g. ^1.0.0 or >=1.0.0 <2.0.0)" + } + }, + "additionalProperties": false, + "required": ["bebopc"] + } + }, + "required": [ + "name", + "description", + "version", + "license", + "bin", + "build", + "contributes", + "engine" + ], + "additionalProperties": false +} diff --git a/vscode-bebop/src/extension.ts b/vscode-bebop/src/extension.ts index 88156a4e..fa6253bd 100644 --- a/vscode-bebop/src/extension.ts +++ b/vscode-bebop/src/extension.ts @@ -1,105 +1,175 @@ -'use strict'; +"use strict"; -import * as vscode from 'vscode'; -import { workspace, Disposable, ExtensionContext } from 'vscode'; +import * as vscode from "vscode"; +import { workspace, Disposable, ExtensionContext, Uri } from "vscode"; import * as lsp from "vscode-languageclient/node"; -import { Trace } from 'vscode-jsonrpc'; +import { Trace } from "vscode-jsonrpc"; import * as path from "path"; import { existsSync } from "fs"; -import { arch, platform } from 'os'; +import { arch, platform } from "os"; export async function activate(context: vscode.ExtensionContext) { - // Register our own little status bar - let statusBar = createStatusBar(context); - statusBar.text = '$(sync~spin) Starting Bebop...'; - - // Start the language server - let client = startLanguageServer(context); - if (client !== null) { - // Wait until the language server is ready - await client.onReady(); + // Register our own little status bar + let statusBar = createStatusBar(context); + statusBar.text = "$(sync~spin) Starting Bebop..."; + + const bebopConfigPaths = await vscode.workspace.findFiles( + "**/bebop.json", + "**/node_modules/**" + ); + let bebopConfigPath = null; + + const channel = vscode.window.createOutputChannel( + "Bebop Language Server (Extension)" + ); + + if (bebopConfigPaths.length > 1) { + const result = await vscode.window.showQuickPick( + bebopConfigPaths.map((path) => path.fsPath), + { + title: "Multiple bebop.json files found. Please select one.", + canPickMany: false, + } + ); + if (result) { + bebopConfigPath = result; + } else { + channel.appendLine( + "No bebop.json file selected. Starting language server without a config." + ); + } + } else if (bebopConfigPaths.length === 1) { + channel.appendLine( + `Found bebop.json file at ${bebopConfigPaths[0].fsPath}.` + ); + bebopConfigPath = bebopConfigPaths[0].fsPath; + } else { + channel.appendLine("No bebop.json file found."); + } + + // Start the language server + let client = startLanguageServer(context, bebopConfigPath); + if (client !== null) { + // Wait until the language server is ready + await client.onReady(); + + if (bebopConfigPath !== null) { + const watcher = vscode.workspace.createFileSystemWatcher(bebopConfigPath); + context.subscriptions.push(watcher); + watcher.onDidChange(async (e) => { + channel.appendLine( + `bebop.json file changed at ${e.fsPath}. Restarting language server.` + ); + if (client !== null) { + await client.stop(); + client = startLanguageServer(context, bebopConfigPath); + if (client !== null) { + await client.onReady(); + } + } + }); } + } - // Hide the status bar - statusBar.hide(); + // Hide the status bar + statusBar.hide(); } -function createStatusBar(context: vscode.ExtensionContext): vscode.StatusBarItem { - let statusBar = vscode.window.createStatusBarItem(vscode.StatusBarAlignment.Right, 100); - context.subscriptions.push(statusBar); - statusBar.command = 'bebop.status'; - statusBar.show(); - return statusBar; +function createStatusBar( + context: vscode.ExtensionContext +): vscode.StatusBarItem { + let statusBar = vscode.window.createStatusBarItem( + vscode.StatusBarAlignment.Right, + 100 + ); + context.subscriptions.push(statusBar); + statusBar.command = "bebop.status"; + statusBar.show(); + return statusBar; } -function startLanguageServer(context: vscode.ExtensionContext): lsp.LanguageClient | null { - // Get the Bebop compiler path - let executable = getBebopCompilerPath(context); - if (!existsSync(executable)) { - vscode.window.showErrorMessage(`Bebop compiler was not found at: ${executable}`); - return null; - } +function startLanguageServer( + context: vscode.ExtensionContext, + configPath: string | null +): lsp.LanguageClient | null { + // Get the Bebop compiler path + let executable = getBebopCompilerPath(context); + if (!existsSync(executable)) { + vscode.window.showErrorMessage( + `Bebop compiler was not found at: ${executable}` + ); + return null; + } + + let serverOptions: lsp.ServerOptions = { + run: { command: executable, args: ["langserver"] }, + // debug: { command: serverExe, args: ['--langserv', '--debug'] } + debug: { command: executable, args: ["--trace", "langserver"] }, + }; + if (configPath !== null) { + serverOptions!.run!.args!.unshift("-c", configPath); + serverOptions!.debug!.args!.unshift("-c", configPath); + } + + let clientOptions: lsp.LanguageClientOptions = { + documentSelector: [ + { + pattern: "**/*.bop", + }, + ], + synchronize: { + configurationSection: "bebopLanguageServer", + fileEvents: workspace.createFileSystemWatcher("**/*.bop"), + }, + }; + + // Create the language client and start the client. + const client = new lsp.LanguageClient( + "bebopLanguageServer", + "Bebop Language Server", + serverOptions, + clientOptions + ); + client.trace = Trace.Verbose; + let disposable = client.start(); - let serverOptions: lsp.ServerOptions = { - run: { command: executable, args: ['--langserv'] }, - // debug: { command: serverExe, args: ['--langserv', '--debug'] } - debug: { command: executable, args: ['--langserv'] } - }; - - let clientOptions: lsp.LanguageClientOptions = { - documentSelector: [ - { - pattern: '**/*.bop', - } - ], - synchronize: { - configurationSection: 'bebopLanguageServer', - fileEvents: workspace.createFileSystemWatcher('**/*.bop') - }, - }; - - // Create the language client and start the client. - const client = new lsp.LanguageClient('bebopLanguageServer', 'Bebop Language Server', serverOptions, clientOptions); - client.trace = Trace.Verbose; - let disposable = client.start(); - - // Push the disposable to the context's subscriptions so that the - // client can be deactivated on extension deactivation - context.subscriptions.push(disposable); - - return client; + // Push the disposable to the context's subscriptions so that the + // client can be deactivated on extension deactivation + context.subscriptions.push(disposable); + + return client; } function getBebopCompilerPath(context: vscode.ExtensionContext) { - // Got an environment variable? - let envPath = process.env.BEBOP_LANGUAGE_SERVER_PATH; - if (envPath !== undefined) { - return envPath; - } + // Got an environment variable? + let envPath = process.env.BEBOP_LANGUAGE_SERVER_PATH; + if (envPath !== undefined) { + return envPath; + } - // Use the packaged compiler - return context.asAbsolutePath(getCompilerPlatformPath()); + // Use the packaged compiler + return context.asAbsolutePath(getCompilerPlatformPath()); } function getCompilerPlatformPath() { - const cpu = arch(); - if (cpu !== "x64" && cpu !== "arm64") { - throw new Error(`${cpu} is not supported`); + const cpu = arch(); + if (cpu !== "x64" && cpu !== "arm64") { + throw new Error(`${cpu} is not supported`); + } + const os = platform(); + const osName = () => { + switch (os) { + case "win32": + return "windows"; + case "linux": + return "linux"; + case "darwin": + return "macos"; + default: + throw new Error(`unsupported OS: ${os}`); } - const os = platform(); - const osName = () => { - switch (os) { - case "win32": - return "windows"; - case "linux": - return "linux"; - case "darwin": - return "macos"; - default: - throw new Error(`unsupported OS: ${os}`); - } - }; - return `compiler/${osName()}/${cpu}/bebopc${os === "win32" ? ".exe" : ""}`; + }; + return `compiler/${osName()}/${cpu}/bebopc${os === "win32" ? ".exe" : ""}`; } -export function deactivate() { } +export function deactivate() {}