diff --git a/.changeset/README.md b/.changeset/README.md new file mode 100644 index 0000000..654c6d4 --- /dev/null +++ b/.changeset/README.md @@ -0,0 +1,8 @@ +# Changesets + +Hello and welcome! This folder has been automatically generated by `@changesets/cli`, a build tool that works +with multi-package repos, or single-package repos to help you version and publish your code. You can +find the full documentation for it [in our repository](https://github.com/changesets/changesets). + +We have a quick list of common questions to get you started engaging with this project in +[our documentation](https://github.com/changesets/changesets/blob/main/docs/common-questions.md). diff --git a/.changeset/config.json b/.changeset/config.json new file mode 100644 index 0000000..a5c99b8 --- /dev/null +++ b/.changeset/config.json @@ -0,0 +1,21 @@ +{ + "$schema": "https://unpkg.com/@changesets/config@3.1.4/schema.json", + "changelog": [ + "@changesets/changelog-github", + { "repo": "neolution-ch/Neolution.Extensions.Caching" } + ], + "commit": false, + "fixed": [ + [ + "@neolution-ch/neolution.extensions.caching.abstractions", + "@neolution-ch/neolution.extensions.caching.distributed", + "@neolution-ch/neolution.extensions.caching.inmemory", + "@neolution-ch/neolution.extensions.caching.redishybrid" + ] + ], + "linked": [], + "access": "public", + "baseBranch": "main", + "updateInternalDependencies": "patch", + "ignore": [] +} diff --git a/.changeset/strong-dragons-like.md b/.changeset/strong-dragons-like.md new file mode 100644 index 0000000..a845151 --- /dev/null +++ b/.changeset/strong-dragons-like.md @@ -0,0 +1,2 @@ +--- +--- diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..c5f84f8 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,29 @@ +name: Changeset Check + +on: + pull_request: + branches: + - main + - "release/**" + +jobs: + changeset-check: + name: Changeset Check + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v6 + with: + fetch-depth: 0 + + - name: Setup Node.js + uses: actions/setup-node@v6 + with: + node-version: 24 + + - name: Install dependencies + run: npm ci + + - name: Check for changesets + if: github.head_ref != 'changeset-release/main' && !startsWith(github.head_ref, 'changeset-release/release/') + run: npx changeset status --since=origin/${{ github.base_ref }} diff --git a/.github/workflows/dependabot-changeset.yml b/.github/workflows/dependabot-changeset.yml new file mode 100644 index 0000000..d7b69c2 --- /dev/null +++ b/.github/workflows/dependabot-changeset.yml @@ -0,0 +1,82 @@ +name: Dependabot Changeset + +on: + pull_request: + types: [opened, synchronize] + +permissions: + contents: write + +jobs: + generate-changeset: + name: Generate Changeset + runs-on: ubuntu-latest + if: github.actor == 'dependabot[bot]' + steps: + - name: Generate App Token + id: app-token + uses: actions/create-github-app-token@v3 + with: + app-id: ${{ secrets.CHANGSETS_BOT_APP_ID }} + private-key: ${{ secrets.CHANGSETS_BOT_PRIVATE_KEY }} + + - name: Checkout + uses: actions/checkout@v6 + with: + fetch-depth: 0 + ref: ${{ github.event.pull_request.head.ref }} + token: ${{ steps.app-token.outputs.token }} + + - name: Check for existing changeset + id: check-changeset + shell: bash + run: | + CHANGESET_FILES=$(git diff --name-only origin/${{ github.base_ref }}...HEAD -- '.changeset/*.md' | grep -v README.md || true) + if [ -n "$CHANGESET_FILES" ]; then + echo "exists=true" >> "$GITHUB_OUTPUT" + echo "Changeset already exists, skipping generation" + else + echo "exists=false" >> "$GITHUB_OUTPUT" + fi + + - name: Detect packable project changes + id: detect-changes + if: steps.check-changeset.outputs.exists == 'false' + shell: bash + run: | + # Any change to a packable .csproj propagates to consumers via NuGet + # dependencies. Changes confined to the (non-packable) UnitTests + # project under tests/ are excluded automatically and should produce + # an empty changeset. + CHANGED=$(git diff --name-only origin/${{ github.base_ref }}...HEAD -- 'src/**/*.csproj') + if [ -n "$CHANGED" ]; then + echo "changed=true" >> "$GITHUB_OUTPUT" + echo "Packable .csproj changes detected:" + echo "$CHANGED" + else + echo "changed=false" >> "$GITHUB_OUTPUT" + echo "No packable .csproj changes detected (test-only or other internal update)" + fi + + - name: Setup Node.js + if: steps.check-changeset.outputs.exists == 'false' + uses: actions/setup-node@v6 + with: + node-version: 24 + + - name: Generate changeset + if: steps.check-changeset.outputs.exists == 'false' + run: node scripts/generate-dependabot-changeset.js + env: + PR_TITLE: ${{ github.event.pull_request.title }} + PR_BODY: ${{ github.event.pull_request.body }} + PACKABLE_PROJECTS_CHANGED: ${{ steps.detect-changes.outputs.changed }} + + - name: Commit and push changeset + if: steps.check-changeset.outputs.exists == 'false' + run: | + git config user.name "github-actions[bot]" + git config user.email "41898282+github-actions[bot]@users.noreply.github.com" + git add .changeset/ + git commit -m "chore: add changeset for dependency updates" + git push diff --git a/.github/workflows/dotnet-publish.yml b/.github/workflows/dotnet-publish.yml deleted file mode 100644 index a61f01a..0000000 --- a/.github/workflows/dotnet-publish.yml +++ /dev/null @@ -1,53 +0,0 @@ -# https://docs.microsoft.com/en-us/azure/devops/artifacts/quickstarts/github-actions?view=azure-devops -name: Publish NuGet package - -on: - push: - tags: - - 'v*.*.*' - workflow_dispatch: - -env: - ARTIFACTS_FEED_URL: https://api.nuget.org/v3/index.json - BUILD_CONFIGURATION: "Release" - DOTNET_VERSION: "6.x" - -jobs: - build-and-deploy: - runs-on: ubuntu-latest - steps: - - name: Checkout - uses: actions/checkout@v2 - with: - fetch-depth: 0 # Needed for GitVersion to function correctly - - - name: Setup .NET - uses: actions/setup-dotnet@v1 - with: - dotnet-version: ${{ env.DOTNET_VERSION }} - source-url: ${{ env.ARTIFACTS_FEED_URL }} - env: - NUGET_AUTH_TOKEN: ${{ secrets.NUGET_API_KEY_NEOLUTION }} - - - name: Install GitVersion - uses: gittools/actions/gitversion/setup@v0.9.7 - with: - versionSpec: '5.x' - - - name: Determine version number - uses: gittools/actions/gitversion/execute@v0.9.7 - - - name: Build and pack - run: | - dotnet restore - dotnet build --configuration '${{ env.BUILD_CONFIGURATION }}' -p:Version=$GITVERSION_NUGETVERSION - dotnet pack Neolution.Extensions.Caching.Abstractions/Neolution.Extensions.Caching.Abstractions.csproj --configuration '${{ env.BUILD_CONFIGURATION }}' --no-build -p:PackageVersion=$GITVERSION_NUGETVERSION - dotnet pack Neolution.Extensions.Caching.InMemory/Neolution.Extensions.Caching.InMemory.csproj --configuration '${{ env.BUILD_CONFIGURATION }}' --no-build -p:PackageVersion=$GITVERSION_NUGETVERSION - dotnet pack Neolution.Extensions.Caching.RedisHybrid/Neolution.Extensions.Caching.RedisHybrid.csproj --configuration '${{ env.BUILD_CONFIGURATION }}' --no-build -p:PackageVersion=$GITVERSION_NUGETVERSION - dotnet pack Neolution.Extensions.Caching.Distributed/Neolution.Extensions.Caching.Distributed.csproj --configuration '${{ env.BUILD_CONFIGURATION }}' --no-build -p:PackageVersion=$GITVERSION_NUGETVERSION - - - - name: Push NuGet package - run: dotnet nuget push --skip-duplicate -k $NUGET_AUTH_TOKEN **/bin/Release/*.nupkg - env: - NUGET_AUTH_TOKEN: ${{ secrets.NUGET_API_KEY_NEOLUTION }} diff --git a/.github/workflows/nuget-publish.yml b/.github/workflows/nuget-publish.yml new file mode 100644 index 0000000..6cbfbd8 --- /dev/null +++ b/.github/workflows/nuget-publish.yml @@ -0,0 +1,80 @@ +name: NuGet Publish + +on: + release: + types: [published] + +jobs: + publish: + name: Pack and push to nuget.org + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v6 + with: + ref: ${{ github.event.release.tag_name }} + + - name: Setup .NET + uses: actions/setup-dotnet@v4 + with: + dotnet-version: "8.x" + + - name: Map tag to project + id: map + shell: bash + run: | + TAG="${{ github.event.release.tag_name }}" + VERSION="${TAG##*@}" + NAME="${TAG%@*}" + + PROJ_DIR=$(node -e " + const fs = require('fs'); + const path = require('path'); + for (const dir of fs.readdirSync('src')) { + const p = path.join('src', dir, 'package.json'); + if (!fs.existsSync(p)) continue; + const pkg = JSON.parse(fs.readFileSync(p, 'utf8')); + if (pkg.name === process.argv[1]) { + console.log(path.join('src', dir)); + break; + } + } + " "$NAME") + + if [ -z "$PROJ_DIR" ]; then + echo "Tag '$TAG' does not match a known package under src/. Skipping." + echo "skip=true" >> "$GITHUB_OUTPUT" + exit 0 + fi + + PROJ_NAME=$(basename "$PROJ_DIR") + echo "version=$VERSION" >> "$GITHUB_OUTPUT" + echo "project_dir=$PROJ_DIR" >> "$GITHUB_OUTPUT" + echo "project_name=$PROJ_NAME" >> "$GITHUB_OUTPUT" + + - name: Remove package.json files from pack input + if: steps.map.outputs.skip != 'true' + shell: bash + run: find . -name package.json -not -path "./node_modules/*" -delete + + - name: Restore + if: steps.map.outputs.skip != 'true' + run: dotnet restore ${{ steps.map.outputs.project_dir }}/${{ steps.map.outputs.project_name }}.csproj + + - name: Pack + if: steps.map.outputs.skip != 'true' + run: | + dotnet pack ${{ steps.map.outputs.project_dir }}/${{ steps.map.outputs.project_name }}.csproj \ + --configuration Release \ + --no-restore \ + -p:Version=${{ steps.map.outputs.version }} \ + -p:PackageVersion=${{ steps.map.outputs.version }} \ + -o ./nupkgs + + - name: Push + if: steps.map.outputs.skip != 'true' + run: | + dotnet nuget push ./nupkgs/*.nupkg \ + --source https://api.nuget.org/v3/index.json \ + --api-key ${{ secrets.NUGET_API_KEY_NEOLUTION }} \ + --skip-duplicate diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 0000000..7644bc2 --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,104 @@ +name: Release + +on: + push: + branches: + - main + - "release/**" + +concurrency: ${{ github.workflow }}-${{ github.ref }} + +permissions: + contents: write + pull-requests: write + +jobs: + release: + name: Release + runs-on: ubuntu-latest + steps: + - name: Generate App Token + id: app-token + uses: actions/create-github-app-token@v3 + with: + app-id: ${{ secrets.CHANGSETS_BOT_APP_ID }} + private-key: ${{ secrets.CHANGSETS_BOT_PRIVATE_KEY }} + + - name: Checkout + uses: actions/checkout@v6 + with: + fetch-depth: 0 + token: ${{ steps.app-token.outputs.token }} + + - name: Setup Node.js + uses: actions/setup-node@v6 + with: + node-version: 24 + + - name: Install dependencies + run: npm ci + + - name: Create Release Pull Request or Publish + id: changesets + uses: changesets/action@v1 + with: + version: node scripts/sync-versions.js + publish: node scripts/publish.js + title: "chore: version packages" + commit: "chore: version packages" + env: + GITHUB_TOKEN: ${{ steps.app-token.outputs.token }} + + - name: Create release branch on major version bump + if: steps.changesets.outputs.published == 'true' && github.ref == 'refs/heads/main' + uses: actions/github-script@v8 + with: + script: | + const published = JSON.parse('${{ steps.changesets.outputs.publishedPackages }}'); + if (!published.length) return; + + const newVersion = published[0].version; + const newMajor = parseInt(newVersion.split('.')[0], 10); + + // Only act on stable major bumps (not prereleases) + if (newVersion.includes('-') || newMajor < 1) return; + + const prevMajor = newMajor - 1; + const branchName = `release/v${prevMajor}.x`; + + try { + await github.rest.repos.getBranch({ + owner: context.repo.owner, + repo: context.repo.repo, + branch: branchName, + }); + console.log(`Branch ${branchName} already exists, skipping.`); + return; + } catch (e) { + if (e.status !== 404) throw e; + } + + const tags = await github.paginate(github.rest.repos.listTags, { + owner: context.repo.owner, + repo: context.repo.repo, + per_page: 100, + }); + + const prevTag = tags.find(t => { + const match = t.name.match(/(?:^v?|@)(\d+)\.\d+\.\d+$/); + return match && parseInt(match[1], 10) === prevMajor; + }); + + if (!prevTag) { + console.log(`No tag found for v${prevMajor}, skipping branch creation.`); + return; + } + + await github.rest.git.createRef({ + owner: context.repo.owner, + repo: context.repo.repo, + ref: `refs/heads/${branchName}`, + sha: prevTag.commit.sha, + }); + + console.log(`Created ${branchName} from tag ${prevTag.name} (${prevTag.commit.sha}).`); diff --git a/.gitignore b/.gitignore index b8656b3..55c1eb2 100644 --- a/.gitignore +++ b/.gitignore @@ -426,3 +426,9 @@ App_Data/ # Repository rules # ================ + +# Changesets internal cache +.changeset/.cache/ + +# Local NuGet pack output +nupkgs/ diff --git a/.npmrc b/.npmrc new file mode 100644 index 0000000..cffe8cd --- /dev/null +++ b/.npmrc @@ -0,0 +1 @@ +save-exact=true diff --git a/Directory.Build.props b/Directory.Build.props new file mode 100644 index 0000000..5d08263 --- /dev/null +++ b/Directory.Build.props @@ -0,0 +1,15 @@ + + + 2.1.1 + Neolution + Neolution AG + Copyright © Neolution AG + https://github.com/neolution-ch/Neolution.Extensions.Caching + https://github.com/neolution-ch/Neolution.Extensions.Caching + git + MIT + true + true + snupkg + + diff --git a/GitVersion.yml b/GitVersion.yml deleted file mode 100644 index edd2ff1..0000000 --- a/GitVersion.yml +++ /dev/null @@ -1,7 +0,0 @@ -mode: ContinuousDelivery -branches: - feature: - mode: ContinuousDeployment -ignore: - sha: [] -merge-message-formats: {} diff --git a/Neolution.Extensions.Caching.sln b/Neolution.Extensions.Caching.sln index f1eaef3..1748c56 100644 --- a/Neolution.Extensions.Caching.sln +++ b/Neolution.Extensions.Caching.sln @@ -6,19 +6,19 @@ MinimumVisualStudioVersion = 10.0.40219.1 Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{E00BB836-121A-4858-8763-4A47C8855095}" ProjectSection(SolutionItems) = preProject .gitignore = .gitignore - azure-pipelines.yml = azure-pipelines.yml + Directory.Build.props = Directory.Build.props README.md = README.md EndProjectSection EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Neolution.Extensions.Caching.UnitTests", "Neolution.Extensions.Caching.UnitTests\Neolution.Extensions.Caching.UnitTests.csproj", "{04A35DA9-EEC3-4937-8A14-39623DA66F35}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Neolution.Extensions.Caching.UnitTests", "tests\Neolution.Extensions.Caching.UnitTests\Neolution.Extensions.Caching.UnitTests.csproj", "{04A35DA9-EEC3-4937-8A14-39623DA66F35}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Neolution.Extensions.Caching.Abstractions", "Neolution.Extensions.Caching.Abstractions\Neolution.Extensions.Caching.Abstractions.csproj", "{68AB5C41-FF5D-4669-8648-0F330AF73DE9}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Neolution.Extensions.Caching.Abstractions", "src\Neolution.Extensions.Caching.Abstractions\Neolution.Extensions.Caching.Abstractions.csproj", "{68AB5C41-FF5D-4669-8648-0F330AF73DE9}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Neolution.Extensions.Caching.InMemory", "Neolution.Extensions.Caching.InMemory\Neolution.Extensions.Caching.InMemory.csproj", "{682B05A7-F3F3-4918-A5BB-8E6482E2C705}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Neolution.Extensions.Caching.InMemory", "src\Neolution.Extensions.Caching.InMemory\Neolution.Extensions.Caching.InMemory.csproj", "{682B05A7-F3F3-4918-A5BB-8E6482E2C705}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Neolution.Extensions.Caching.RedisHybrid", "Neolution.Extensions.Caching.RedisHybrid\Neolution.Extensions.Caching.RedisHybrid.csproj", "{49874974-90FA-4683-84BF-7B217D072899}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Neolution.Extensions.Caching.RedisHybrid", "src\Neolution.Extensions.Caching.RedisHybrid\Neolution.Extensions.Caching.RedisHybrid.csproj", "{49874974-90FA-4683-84BF-7B217D072899}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Neolution.Extensions.Caching.Distributed", "Neolution.Extensions.Caching.Distributed\Neolution.Extensions.Caching.Distributed.csproj", "{AFEFABB1-E20A-4274-8E2A-AC50123AF295}" +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Neolution.Extensions.Caching.Distributed", "src\Neolution.Extensions.Caching.Distributed\Neolution.Extensions.Caching.Distributed.csproj", "{AFEFABB1-E20A-4274-8E2A-AC50123AF295}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution diff --git a/azure-pipelines.yml b/azure-pipelines.yml deleted file mode 100644 index 5b89c1b..0000000 --- a/azure-pipelines.yml +++ /dev/null @@ -1,74 +0,0 @@ -# ASP.NET Core -# Build and test ASP.NET Core projects targeting .NET Core. -# Add steps that run tests, create a NuGet package, deploy, and more: -# https://docs.microsoft.com/azure/devops/pipelines/languages/dotnet-core - -name: $(BuildDefinitionName)_$(Year:yyyy).$(Month).$(DayOfMonth)$(Rev:.r) - -trigger: -- master -- release/* - -pool: - vmImage: Ubuntu-latest - -resources: - repositories: - - repository: templates - type: git - name: neolution-pipelines/pipeline-templates - -variables: - BuildConfiguration: 'Release' - -steps: - -- task: UseDotNet@2 - displayName: Install .NET Core SDK - inputs: - packageType: 'sdk' - version: '6.x' - -- task: gitversion/setup@0 - displayName: Install GitVersion - inputs: - versionSpec: '5.x' - -- task: gitversion/execute@0 - displayName: Execute GitVersion - -- task: PowerShell@2 - displayName: Update Build Number - inputs: - targetType: 'inline' - script: 'Write-Host "##vso[build.updatebuildnumber]$(NuGetVersion)"' - -- task: DotNetCoreCLI@2 - displayName: Restore - inputs: - command: restore - projects: '**/*.csproj' - vstsFeed: 'dc68f94e-847b-4595-86ba-405d5744b21d' - -- task: DotNetCoreCLI@2 - displayName: Build - inputs: - projects: '**/*.csproj' - arguments: '--configuration $(BuildConfiguration)' - -- template: testing/unittests.yml@templates - -- task: DotNetCoreCLI@2 - displayName: Pack - inputs: - command: pack - packagesToPack: '**/Neolution.Extensions.Caching.Abstractions.csproj;**/Neolution.Extensions.Caching.RedisHybrid.csproj;**/Neolution.Extensions.Caching.InMemory.csproj;**/Neolution.Extensions.Caching.Distributed.csproj' - nobuild: true - versioningScheme: byEnvVar - versionEnvVar: NuGetVersion - -- task: DotNetCoreCLI@2 - displayName: Push - inputs: - command: push - publishVstsFeed: 'dc68f94e-847b-4595-86ba-405d5744b21d' diff --git a/docs/CHANGESETS.md b/docs/CHANGESETS.md new file mode 100644 index 0000000..35249ae --- /dev/null +++ b/docs/CHANGESETS.md @@ -0,0 +1,349 @@ +# Changesets in this repo (NuGet edition) + +This guide explains how versioning and releases work in `Neolution.Extensions.Caching`. It is the .NET-flavoured counterpart of the org's canonical [Changesets Guide](https://github.com/neolution-ch/changeset-test/blob/main/docs/CHANGESETS.md). Read the canonical doc for the underlying mental model and rare scenarios (prereleases, backports, recovery). Read this doc for the specifics of working with a NuGet-only project. + +## Table of contents + +- [Overview](#overview) +- [Why `package.json` files exist in a .NET repo](#why-packagejson-files-exist-in-a-net-repo) +- [For contributors](#for-contributors) + - [Adding a changeset](#adding-a-changeset) + - [Choosing the right semver bump](#choosing-the-right-semver-bump) + - [Empty changesets](#empty-changesets) + - [What if you forget a changeset](#what-if-you-forget-a-changeset) + - [Changeset file format](#changeset-file-format) + - [Locally testing a `.nupkg` before opening a PR](#locally-testing-a-nupkg-before-opening-a-pr) +- [For maintainers](#for-maintainers) + - [Regular release flow](#regular-release-flow) + - [How the fixed group works](#how-the-fixed-group-works) + - [Prereleases / release candidates](#prereleases--release-candidates) + - [Backporting on `release/vX.x` branches](#backporting-on-releasevxx-branches) + - [Recovering from a failed publish](#recovering-from-a-failed-publish) + - [Manual publish fallback](#manual-publish-fallback) + - [Reverting a release](#reverting-a-release) + - [Dependabot automation](#dependabot-automation) +- [Reference](#reference) + - [`.changeset/config.json`](#changesetconfigjson) + - [Workflows](#workflows) + - [Useful commands](#useful-commands) + - [Where to find what](#where-to-find-what) + +## Overview + +This repository publishes four libraries to **nuget.org**, always in lockstep at the same version: + +| Library | Path | Workspace package name | +| --------------------------------------------- | ------------------------------------------------------ | ------------------------------------------------------------ | +| `Neolution.Extensions.Caching.Abstractions` | `src/Neolution.Extensions.Caching.Abstractions/` | `@neolution-ch/neolution.extensions.caching.abstractions` | +| `Neolution.Extensions.Caching.Distributed` | `src/Neolution.Extensions.Caching.Distributed/` | `@neolution-ch/neolution.extensions.caching.distributed` | +| `Neolution.Extensions.Caching.InMemory` | `src/Neolution.Extensions.Caching.InMemory/` | `@neolution-ch/neolution.extensions.caching.inmemory` | +| `Neolution.Extensions.Caching.RedisHybrid` | `src/Neolution.Extensions.Caching.RedisHybrid/` | `@neolution-ch/neolution.extensions.caching.redishybrid` | + +The test project lives at `tests/Neolution.Extensions.Caching.UnitTests/`. It is **not** a workspace package (`tests/` is outside the `src/*` workspace glob), so changes confined to it never require a changeset. + +The release flow: + +``` +PR opens + ├─ build + test (dotnet.yml) + └─ Changeset Check (ci.yml) ← blocks PR if no changeset + +PR merges to main + └─ Release workflow opens/updates a "chore: version packages" PR + +Version Packages PR merges to main + └─ Release workflow runs scripts/publish.js + → pushes 4 git tags + → creates 4 GitHub Releases + ↓ + release: published event (×4) + ↓ + nuget-publish.yml fires four times in parallel + → dotnet pack + dotnet nuget push + ↓ + Four new versions on nuget.org +``` + +## Why `package.json` files exist in a .NET repo + +Changesets is a JavaScript tool. To use it for version coordination on a .NET project, the repo grows a few npm-flavoured files. None of them are ever published to npm: + +- Every per-project `package.json` carries `"private": true`. `npm publish` refuses to run on private packages, so a fat-fingered local `npm publish` cannot leak anything. +- The `@neolution-ch/...` names are local identifiers for the changesets config. They are not reserved on npmjs.com. +- `nuget-publish.yml` deletes every `package.json` before `dotnet pack` runs, so they never end up inside the `.nupkg`. +- The only artifact pushed anywhere outside this repo is the `.nupkg`, going to nuget.org via `dotnet nuget push` using `NUGET_API_KEY_NEOLUTION`. + +Mental model: + +| Concern | Owned by | Output | +| ------------------------ | --------------------------------- | -------------------------------------------- | +| What version is next | Changesets (JS, `package.json`) | Bumped `package.json` + `Directory.Build.props` | +| What changed (notes) | Changesets | `CHANGELOG.md`, GitHub Release body | +| Building the artifact | `dotnet pack` | `.nupkg` files | +| Publishing the artifact | `dotnet nuget push` | nuget.org listing | + +Treat `package.json` like the old `GitVersion.yml`: configuration for the versioning tool, not something downstream consumers ever see. + +## For contributors + +### Adding a changeset + +After making your code changes, from the repo root: + +```bash +npx changeset +``` + +Pick **any one** of the four libraries (the other three auto-bump to match, see [How the fixed group works](#how-the-fixed-group-works)), pick a bump type, write a one-paragraph summary. The result is a new file under `.changeset/`, e.g. `.changeset/brave-dogs-dance.md`. Commit it alongside your code change. + +### Choosing the right semver bump + +| Bump | When to use | Example | +| ----- | -------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------- | +| patch | Bug fix, internal refactor that doesn't change the public API, doc update on a public type | Fix `RedisHybridCache.Set` swallowing exceptions; rename a private field | +| minor | Backwards-compatible addition | Add a new option on `MessagePackDistributedCacheOptions`; add a new public extension | +| major | Breaking change | Rename or remove a public type/method; change a method signature; drop a TFM | + +When in doubt, pick the smallest bump that's accurate. + +### Empty changesets + +Some changes don't need to ship — CI tweaks, doc-only changes under `docs/`, build script edits. The CI **still requires** a changeset to be present, so add an empty one: + +```bash +npx changeset --empty +``` + +The PR title will be reused as the changeset summary. Nothing gets bumped, no changelog entry is generated. + +### What if you forget a changeset + +The **Changeset Check** workflow fails. Fix: + +```bash +npx changeset # or --empty for non-shipping changes +git add .changeset/*.md +git commit -m "Add changeset" +git push +``` + +CI re-runs and goes green. + +### Changeset file format + +```markdown +--- +"@neolution-ch/neolution.extensions.caching.distributed": minor +--- + +Add `IDistributedCache.GetOrSetAsync` overload that accepts a `CancellationToken`. +``` + +The YAML frontmatter declares the bump. The body is Markdown and becomes the changelog entry. Because the four packages are fixed, listing just one is enough. + +### Locally testing a `.nupkg` before opening a PR + +The canonical doc covers npm-only [pkg.pr.new](https://pkg.pr.new) previews. For NuGet, do it manually: + +1. Pack the library locally: + ```bash + dotnet pack src/Neolution.Extensions.Caching.Distributed/Neolution.Extensions.Caching.Distributed.csproj \ + -c Release \ + -p:Version=2.2.0-local.1 \ + -o ./nupkgs + ``` +2. Add a `NuGet.config` to the consuming project that points at the local folder: + ```xml + + + + + + ``` +3. `dotnet add package Neolution.Extensions.Caching.Distributed --version 2.2.0-local.1` + +Use any unique prerelease suffix you want; the package never leaves your disk. + +## For maintainers + +### Regular release flow + +1. Contributor PR with a changeset merges into `main`. +2. **Release workflow** opens (or updates) a PR titled **chore: version packages**. The PR diff: + - Bumps every `package.json` from `X.Y.Z` to the new version (all four, because fixed). + - Bumps `Directory.Build.props` `` to the new version. + - Generates/updates per-project `CHANGELOG.md` files. + - Deletes the consumed `.changeset/*.md` files. +3. Review the PR, then **merge it** when you want to ship. +4. The Release workflow runs again, executes `scripts/publish.js`, the action pushes four tags and creates four GitHub Releases. +5. The `release: published` event fires the **NuGet Publish** workflow four times (once per Release), each packing and pushing one library to nuget.org. + +You can batch multiple changeset-bearing PRs before merging the Version Packages PR. All accumulated changesets fold into a single release. + +### How the fixed group works + +`fixed: [[…]]` in `.changeset/config.json` lists all four library package names as one group. The rules: + +- All four always share the same version. +- A changeset selecting any one of them bumps all four. +- The **highest requested bump wins**: if changeset A says `patch` and changeset B says `minor`, the next release is a `minor`. +- Every release ships all four packages, even if only one changed code-wise. This matches the existing pre-changesets convention on nuget.org (every version 2.0.0 through 2.1.1 is present for all four). + +Example, starting at `2.1.1`: + +| Pending changesets | Next version | +| --------------------------------------------------------- | ------------ | +| `…distributed: patch` | `2.1.2` | +| `…inmemory: minor` | `2.2.0` | +| `…redishybrid: patch` + `…abstractions: minor` | `2.2.0` | +| `…abstractions: major` | `3.0.0` | + +### Prereleases / release candidates + +Use only for major releases. See the canonical doc's [Prereleases section](https://github.com/neolution-ch/changeset-test/blob/main/docs/CHANGESETS.md#prereleases--release-candidates) for the full walkthrough. nuget.org accepts SemVer 2.0 prerelease suffixes (`3.0.0-beta.0`, `3.0.0-rc.1`) natively, so the npm-flavoured commands in the canonical doc work as-is: + +```bash +git checkout -b chore/enter-beta +npx changeset pre enter beta +git commit -am "Enter prerelease mode (beta)" +# PR → main, merge +``` + +Once on `main`, every Version Packages PR bumps the beta counter. Exit with `npx changeset pre exit` on a new PR. + +Consumers on `dotnet add package Neolution.Extensions.Caching.Abstractions` keep getting the latest stable (e.g. `2.1.1`); they have to ask for the prerelease explicitly with `--prerelease` or `--version 3.0.0-beta.0`. + +### Backporting on `release/vX.x` branches + +When `main` has moved on to a new major (e.g. v3) but a fix is needed for the old major (v2), use a `release/v2.x` branch. The Release workflow auto-creates `release/v(N-1).x` whenever a new major is published from `main` — see `scripts:` block in `release.yml`. For all other cases (manually creating it retroactively, when to use it, what a backport PR looks like), follow the [Backporting section of the canonical doc](https://github.com/neolution-ch/changeset-test/blob/main/docs/CHANGESETS.md#backporting--hotfixes-on-older-versions) — both `ci.yml` and `release.yml` already trigger on `release/**`. + +> **nuget.org has no dist-tags.** The canonical doc's `--tag release-v1` mechanism is npm-only. On nuget.org, "latest" is always the highest stable SemVer. A backport release like `2.2.4` will never be served to a `dotnet add package` that resolves to `3.x` because `3.x > 2.x`. No extra config needed. + +### Recovering from a failed publish + +`dotnet nuget push --skip-duplicate` is idempotent. If `nuget-publish.yml` fails partway: + +1. Open the failing run on the **Actions** tab. +2. Click **Re-run failed jobs**. +3. Already-pushed packages return "Conflict 409" which `--skip-duplicate` swallows; remaining packages get pushed. + +If the Release workflow created the tags + Releases but a Release fired the wrong publish: +- Inspect the Release page — confirm which package it represents (`@neolution-ch/...@version` tag). +- Re-run the corresponding NuGet Publish workflow run. + +### Manual publish fallback + +If CI is broken and a release is urgent: + +```bash +# Pull latest main (or the release branch) +git checkout main && git pull + +# If versions haven't been bumped yet, do it locally: +node scripts/sync-versions.js +git add . && git commit -m "Version packages" + +# Pack each library (any directory under src/ with a .csproj) +for dir in src/*/; do + proj=$(basename "$dir") + dotnet pack "$dir$proj.csproj" -c Release -o ./nupkgs +done + +# Push to nuget.org with a maintainer's personal API key +dotnet nuget push ./nupkgs/*.nupkg \ + --source https://api.nuget.org/v3/index.json \ + --api-key \ + --skip-duplicate + +# Push tags (one per package) +git tag "@neolution-ch/neolution.extensions.caching.abstractions@2.2.0" +git tag "@neolution-ch/neolution.extensions.caching.distributed@2.2.0" +git tag "@neolution-ch/neolution.extensions.caching.inmemory@2.2.0" +git tag "@neolution-ch/neolution.extensions.caching.redishybrid@2.2.0" +git push --tags +``` + +Then create the GitHub Releases manually from the tags, copy-pasting the changelog body. + +### Reverting a release + +nuget.org does **not allow deletion** of published packages. It does allow **unlisting** (the version stays accessible by exact version, but isn't returned for "latest" or default queries). + +Preferred path: + +1. `git revert ` +2. `npx changeset` → patch → "Revert " +3. Merge to `main`, let the regular flow ship the next patch. + +Only unlist on nuget.org if the bad version is genuinely dangerous (e.g. ships secrets). To unlist: nuget.org → package page → Manage Package → Listing → Unlist. + +### Dependabot automation + +Dependabot PRs do not include changesets, so CI would normally fail. The **Dependabot Changeset** workflow handles this automatically: + +1. Dependabot opens a PR. +2. `dependabot-changeset.yml` runs because `github.actor == 'dependabot[bot]'`. +3. The workflow checks whether any `.csproj` under `src/` changed (the test project under `tests/` is excluded automatically). + - If yes → emits a `patch` changeset for all four fixed-group packages. + - If no (test-only update) → emits an **empty** changeset. +4. `scripts/generate-dependabot-changeset.js` parses the PR body for `Bumps [X] from Y to Z` / `Updated [X] from Y to Z` lines and includes a bump table in the changeset body. +5. The bot commits the changeset to the PR branch. CI re-runs, passes. + +If parsing fails, the workflow still emits a changeset using just the PR title. It never blocks the PR. + +## Reference + +### `.changeset/config.json` + +| Option | Value | Meaning | +| ---------------------------- | -------------------------------------------------------------- | ----------------------------------------------------- | +| `changelog` | `["@changesets/changelog-github", { "repo": "neolution-ch/Neolution.Extensions.Caching" }]` | Changelogs include PR links and contributor credit | +| `commit` | `false` | `npx changeset` does not auto-commit | +| `fixed` | All four library names in one inner array | The four packages always share a version | +| `access` | `public` | Marks intent (would only matter on an npm publish) | +| `baseBranch` | `main` | Comparisons run against `origin/main` | +| `updateInternalDependencies` | `patch` | Internal dependency ranges update on every release | +| `ignore` | `[]` | No packages excluded | + +### Workflows + +| File | Trigger | Purpose | +| --------------------------------------------- | -------------------------------------- | -------------------------------------------------------- | +| `.github/workflows/dotnet.yml` | PR or push to `main` | Build + test | +| `.github/workflows/ci.yml` | PR to `main` / `release/**` | **Changeset Check** — blocks PRs without a changeset | +| `.github/workflows/release.yml` | Push to `main` / `release/**` | Open Version PR; on subsequent push, push tags + Releases | +| `.github/workflows/nuget-publish.yml` | `release: published` | `dotnet pack` + `dotnet nuget push` | +| `.github/workflows/dependabot-changeset.yml` | PR opened/synced (Dependabot only) | Auto-generate a changeset for the PR | + +The Release workflow uses the **CSAG-Changesets-Bot** GitHub App token, not `GITHUB_TOKEN`, so Version Packages PRs trigger CI checks (GITHUB_TOKEN-pushed PRs do not). + +### Useful commands + +```bash +# Add a changeset (interactive) +npx changeset + +# Add an empty changeset (no release) +npx changeset --empty + +# Check if changesets exist (what CI runs) +npx changeset status --since=origin/main + +# Preview what versions would be bumped +npx changeset status --verbose + +# Apply changesets locally (bump version + write CHANGELOGs + update .csproj via Directory.Build.props) +node scripts/sync-versions.js + +# Enter / exit prerelease mode (allowed tags: alpha, beta, rc) +npx changeset pre enter +npx changeset pre exit +``` + +### Where to find what + +- **Current version of the libraries** → `Directory.Build.props`'s `` (and any `package.json`). +- **What's been released** → GitHub Releases page + each `src/Neolution.Extensions.Caching.*/CHANGELOG.md`. +- **What's queued to ship next** → `.changeset/*.md` files. `npx changeset status --verbose` summarises. +- **Why a tag has the form `@neolution-ch/...@version`** → changesets/action [tag-name code](https://github.com/changesets/action/blob/main/src/run.ts). +- **Canonical org-wide release flow doc** → [neolution-ch/changeset-test/docs/CHANGESETS.md](https://github.com/neolution-ch/changeset-test/blob/main/docs/CHANGESETS.md). diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 0000000..894329a --- /dev/null +++ b/package-lock.json @@ -0,0 +1,1414 @@ +{ + "name": "neolution.extensions.caching", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "neolution.extensions.caching", + "workspaces": [ + "src/*" + ], + "devDependencies": { + "@changesets/changelog-github": "^0.5.0", + "@changesets/cli": "^2.27.0" + } + }, + "Neolution.Extensions.Caching.Abstractions": { + "name": "@neolution-ch/neolution.extensions.caching.abstractions", + "version": "2.1.1", + "extraneous": true + }, + "Neolution.Extensions.Caching.Distributed": { + "name": "@neolution-ch/neolution.extensions.caching.distributed", + "version": "2.1.1", + "extraneous": true + }, + "Neolution.Extensions.Caching.InMemory": { + "name": "@neolution-ch/neolution.extensions.caching.inmemory", + "version": "2.1.1", + "extraneous": true + }, + "Neolution.Extensions.Caching.RedisHybrid": { + "name": "@neolution-ch/neolution.extensions.caching.redishybrid", + "version": "2.1.1", + "extraneous": true + }, + "node_modules/@babel/runtime": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.29.7.tgz", + "integrity": "sha512-Nq8OhGWiZIZGV6hLHoyAKLLcJihP/xFeBMGJoUrxTX2psI8dCifzLhZISFb+VWS3wFMRDmCGw5R+dOySCqPLhw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@changesets/apply-release-plan": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/@changesets/apply-release-plan/-/apply-release-plan-7.1.1.tgz", + "integrity": "sha512-9qPCm/rLx/xoOFXIHGB229+4GOL76S4MC+7tyOuTsR6+1jYlfFDQORdvwR5hDA6y4FL2BPt3qpbcQIS+dW85LA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@changesets/config": "^3.1.4", + "@changesets/get-version-range-type": "^0.4.0", + "@changesets/git": "^3.0.4", + "@changesets/should-skip-package": "^0.1.2", + "@changesets/types": "^6.1.0", + "@manypkg/get-packages": "^1.1.3", + "detect-indent": "^6.0.0", + "fs-extra": "^7.0.1", + "lodash.startcase": "^4.4.0", + "outdent": "^0.5.0", + "prettier": "^2.7.1", + "resolve-from": "^5.0.0", + "semver": "^7.5.3" + } + }, + "node_modules/@changesets/assemble-release-plan": { + "version": "6.0.10", + "resolved": "https://registry.npmjs.org/@changesets/assemble-release-plan/-/assemble-release-plan-6.0.10.tgz", + "integrity": "sha512-rSDcqdJ9KbVyjpBIuCidhvZNIiVt1XaIYp73ycVQRIA5n/j6wQaEk0ChRLMUQ1vkxZe51PTQ9OIhbg6HQMW45A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@changesets/errors": "^0.2.0", + "@changesets/get-dependents-graph": "^2.1.4", + "@changesets/should-skip-package": "^0.1.2", + "@changesets/types": "^6.1.0", + "@manypkg/get-packages": "^1.1.3", + "semver": "^7.5.3" + } + }, + "node_modules/@changesets/changelog-git": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/@changesets/changelog-git/-/changelog-git-0.2.1.tgz", + "integrity": "sha512-x/xEleCFLH28c3bQeQIyeZf8lFXyDFVn1SgcBiR2Tw/r4IAWlk1fzxCEZ6NxQAjF2Nwtczoen3OA2qR+UawQ8Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@changesets/types": "^6.1.0" + } + }, + "node_modules/@changesets/changelog-github": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/@changesets/changelog-github/-/changelog-github-0.5.2.tgz", + "integrity": "sha512-HeGeDl8HaIGj9fQHo/tv5XKQ2SNEi9+9yl1Bss1jttPqeiASRXhfi0A2wv8yFKCp07kR1gpOI5ge6+CWNm1jPw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@changesets/get-github-info": "^0.7.0", + "@changesets/types": "^6.1.0", + "dotenv": "^8.1.0" + } + }, + "node_modules/@changesets/cli": { + "version": "2.31.0", + "resolved": "https://registry.npmjs.org/@changesets/cli/-/cli-2.31.0.tgz", + "integrity": "sha512-AhI4enNTgHu2IZr6K4WZyf0EPch4XVMn1yOMFmCD9gsfBGqMYaHXls5HyDv6/CL5axVQABz68eG30eCtbr2wFg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@changesets/apply-release-plan": "^7.1.1", + "@changesets/assemble-release-plan": "^6.0.10", + "@changesets/changelog-git": "^0.2.1", + "@changesets/config": "^3.1.4", + "@changesets/errors": "^0.2.0", + "@changesets/get-dependents-graph": "^2.1.4", + "@changesets/get-release-plan": "^4.0.16", + "@changesets/git": "^3.0.4", + "@changesets/logger": "^0.1.1", + "@changesets/pre": "^2.0.2", + "@changesets/read": "^0.6.7", + "@changesets/should-skip-package": "^0.1.2", + "@changesets/types": "^6.1.0", + "@changesets/write": "^0.4.0", + "@inquirer/external-editor": "^1.0.2", + "@manypkg/get-packages": "^1.1.3", + "ansi-colors": "^4.1.3", + "enquirer": "^2.4.1", + "fs-extra": "^7.0.1", + "mri": "^1.2.0", + "package-manager-detector": "^0.2.0", + "picocolors": "^1.1.0", + "resolve-from": "^5.0.0", + "semver": "^7.5.3", + "spawndamnit": "^3.0.1", + "term-size": "^2.1.0" + }, + "bin": { + "changeset": "bin.js" + } + }, + "node_modules/@changesets/config": { + "version": "3.1.4", + "resolved": "https://registry.npmjs.org/@changesets/config/-/config-3.1.4.tgz", + "integrity": "sha512-pf0bvD/v6WI2cRlZ6hzpjtZdSlXDXMAJ+Iz7xfFzV4ZxJ8OGGAON+1qYc99ZPrijnt4xp3VGG7eNvAOGS24V1Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@changesets/errors": "^0.2.0", + "@changesets/get-dependents-graph": "^2.1.4", + "@changesets/logger": "^0.1.1", + "@changesets/should-skip-package": "^0.1.2", + "@changesets/types": "^6.1.0", + "@manypkg/get-packages": "^1.1.3", + "fs-extra": "^7.0.1", + "micromatch": "^4.0.8" + } + }, + "node_modules/@changesets/errors": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/@changesets/errors/-/errors-0.2.0.tgz", + "integrity": "sha512-6BLOQUscTpZeGljvyQXlWOItQyU71kCdGz7Pi8H8zdw6BI0g3m43iL4xKUVPWtG+qrrL9DTjpdn8eYuCQSRpow==", + "dev": true, + "license": "MIT", + "dependencies": { + "extendable-error": "^0.1.5" + } + }, + "node_modules/@changesets/get-dependents-graph": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/@changesets/get-dependents-graph/-/get-dependents-graph-2.1.4.tgz", + "integrity": "sha512-ZsS00x6WvmHq3sQv8oCMwL0f/z3wbXCVuSVTJwCnnmbC/iBdNJGFx1EcbMG4PC6sXRyH69liM4A2WKXzn/kRPg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@changesets/types": "^6.1.0", + "@manypkg/get-packages": "^1.1.3", + "picocolors": "^1.1.0", + "semver": "^7.5.3" + } + }, + "node_modules/@changesets/get-github-info": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/@changesets/get-github-info/-/get-github-info-0.7.0.tgz", + "integrity": "sha512-+i67Bmhfj9V4KfDeS1+Tz3iF32btKZB2AAx+cYMqDSRFP7r3/ZdGbjCo+c6qkyViN9ygDuBjzageuPGJtKGe5A==", + "dev": true, + "license": "MIT", + "dependencies": { + "dataloader": "^1.4.0", + "node-fetch": "^2.5.0" + } + }, + "node_modules/@changesets/get-release-plan": { + "version": "4.0.16", + "resolved": "https://registry.npmjs.org/@changesets/get-release-plan/-/get-release-plan-4.0.16.tgz", + "integrity": "sha512-2K5Om6CrMPm45rtvckfzWo7e9jOVCKLCnXia5eUPaURH7/LWzri7pK1TycdzAuAtehLkW7VPbWLCSExTHmiI6g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@changesets/assemble-release-plan": "^6.0.10", + "@changesets/config": "^3.1.4", + "@changesets/pre": "^2.0.2", + "@changesets/read": "^0.6.7", + "@changesets/types": "^6.1.0", + "@manypkg/get-packages": "^1.1.3" + } + }, + "node_modules/@changesets/get-version-range-type": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/@changesets/get-version-range-type/-/get-version-range-type-0.4.0.tgz", + "integrity": "sha512-hwawtob9DryoGTpixy1D3ZXbGgJu1Rhr+ySH2PvTLHvkZuQ7sRT4oQwMh0hbqZH1weAooedEjRsbrWcGLCeyVQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/@changesets/git": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@changesets/git/-/git-3.0.4.tgz", + "integrity": "sha512-BXANzRFkX+XcC1q/d27NKvlJ1yf7PSAgi8JG6dt8EfbHFHi4neau7mufcSca5zRhwOL8j9s6EqsxmT+s+/E6Sw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@changesets/errors": "^0.2.0", + "@manypkg/get-packages": "^1.1.3", + "is-subdir": "^1.1.1", + "micromatch": "^4.0.8", + "spawndamnit": "^3.0.1" + } + }, + "node_modules/@changesets/logger": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/@changesets/logger/-/logger-0.1.1.tgz", + "integrity": "sha512-OQtR36ZlnuTxKqoW4Sv6x5YIhOmClRd5pWsjZsddYxpWs517R0HkyiefQPIytCVh4ZcC5x9XaG8KTdd5iRQUfg==", + "dev": true, + "license": "MIT", + "dependencies": { + "picocolors": "^1.1.0" + } + }, + "node_modules/@changesets/parse": { + "version": "0.4.3", + "resolved": "https://registry.npmjs.org/@changesets/parse/-/parse-0.4.3.tgz", + "integrity": "sha512-ZDmNc53+dXdWEv7fqIUSgRQOLYoUom5Z40gmLgmATmYR9NbL6FJJHwakcCpzaeCy+1D0m0n7mT4jj2B/MQPl7A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@changesets/types": "^6.1.0", + "js-yaml": "^4.1.1" + } + }, + "node_modules/@changesets/pre": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/@changesets/pre/-/pre-2.0.2.tgz", + "integrity": "sha512-HaL/gEyFVvkf9KFg6484wR9s0qjAXlZ8qWPDkTyKF6+zqjBe/I2mygg3MbpZ++hdi0ToqNUF8cjj7fBy0dg8Ug==", + "dev": true, + "license": "MIT", + "dependencies": { + "@changesets/errors": "^0.2.0", + "@changesets/types": "^6.1.0", + "@manypkg/get-packages": "^1.1.3", + "fs-extra": "^7.0.1" + } + }, + "node_modules/@changesets/read": { + "version": "0.6.7", + "resolved": "https://registry.npmjs.org/@changesets/read/-/read-0.6.7.tgz", + "integrity": "sha512-D1G4AUYGrBEk8vj8MGwf75k9GpN6XL3wg8i42P2jZZwFLXnlr2Pn7r9yuQNbaMCarP7ZQWNJbV6XLeysAIMhTA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@changesets/git": "^3.0.4", + "@changesets/logger": "^0.1.1", + "@changesets/parse": "^0.4.3", + "@changesets/types": "^6.1.0", + "fs-extra": "^7.0.1", + "p-filter": "^2.1.0", + "picocolors": "^1.1.0" + } + }, + "node_modules/@changesets/should-skip-package": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/@changesets/should-skip-package/-/should-skip-package-0.1.2.tgz", + "integrity": "sha512-qAK/WrqWLNCP22UDdBTMPH5f41elVDlsNyat180A33dWxuUDyNpg6fPi/FyTZwRriVjg0L8gnjJn2F9XAoF0qw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@changesets/types": "^6.1.0", + "@manypkg/get-packages": "^1.1.3" + } + }, + "node_modules/@changesets/types": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/@changesets/types/-/types-6.1.0.tgz", + "integrity": "sha512-rKQcJ+o1nKNgeoYRHKOS07tAMNd3YSN0uHaJOZYjBAgxfV7TUE7JE+z4BzZdQwb5hKaYbayKN5KrYV7ODb2rAA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@changesets/write": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/@changesets/write/-/write-0.4.0.tgz", + "integrity": "sha512-CdTLvIOPiCNuH71pyDu3rA+Q0n65cmAbXnwWH84rKGiFumFzkmHNT8KHTMEchcxN+Kl8I54xGUhJ7l3E7X396Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@changesets/types": "^6.1.0", + "fs-extra": "^7.0.1", + "human-id": "^4.1.1", + "prettier": "^2.7.1" + } + }, + "node_modules/@inquirer/external-editor": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@inquirer/external-editor/-/external-editor-1.0.3.tgz", + "integrity": "sha512-RWbSrDiYmO4LbejWY7ttpxczuwQyZLBUyygsA9Nsv95hpzUWwnNTVQmAq3xuh7vNwCp07UTmE5i11XAEExx4RA==", + "dev": true, + "license": "MIT", + "dependencies": { + "chardet": "^2.1.1", + "iconv-lite": "^0.7.0" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@types/node": ">=18" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } + }, + "node_modules/@manypkg/find-root": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@manypkg/find-root/-/find-root-1.1.0.tgz", + "integrity": "sha512-mki5uBvhHzO8kYYix/WRy2WX8S3B5wdVSc9D6KcU5lQNglP2yt58/VfLuAK49glRXChosY8ap2oJ1qgma3GUVA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.5.5", + "@types/node": "^12.7.1", + "find-up": "^4.1.0", + "fs-extra": "^8.1.0" + } + }, + "node_modules/@manypkg/find-root/node_modules/@types/node": { + "version": "12.20.55", + "resolved": "https://registry.npmjs.org/@types/node/-/node-12.20.55.tgz", + "integrity": "sha512-J8xLz7q2OFulZ2cyGTLE1TbbZcjpno7FaN6zdJNrgAdrJ+DZzh/uFR6YrTb4C+nXakvud8Q4+rbhoIWlYQbUFQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/@manypkg/find-root/node_modules/fs-extra": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-8.1.0.tgz", + "integrity": "sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g==", + "dev": true, + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.2.0", + "jsonfile": "^4.0.0", + "universalify": "^0.1.0" + }, + "engines": { + "node": ">=6 <7 || >=8" + } + }, + "node_modules/@manypkg/get-packages": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/@manypkg/get-packages/-/get-packages-1.1.3.tgz", + "integrity": "sha512-fo+QhuU3qE/2TQMQmbVMqaQ6EWbMhi4ABWP+O4AM1NqPBuy0OrApV5LO6BrrgnhtAHS2NH6RrVk9OL181tTi8A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.5.5", + "@changesets/types": "^4.0.1", + "@manypkg/find-root": "^1.1.0", + "fs-extra": "^8.1.0", + "globby": "^11.0.0", + "read-yaml-file": "^1.1.0" + } + }, + "node_modules/@manypkg/get-packages/node_modules/@changesets/types": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/@changesets/types/-/types-4.1.0.tgz", + "integrity": "sha512-LDQvVDv5Kb50ny2s25Fhm3d9QSZimsoUGBsUioj6MC3qbMUCuC8GPIvk/M6IvXx3lYhAs0lwWUQLb+VIEUCECw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@manypkg/get-packages/node_modules/fs-extra": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-8.1.0.tgz", + "integrity": "sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g==", + "dev": true, + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.2.0", + "jsonfile": "^4.0.0", + "universalify": "^0.1.0" + }, + "engines": { + "node": ">=6 <7 || >=8" + } + }, + "node_modules/@neolution-ch/neolution.extensions.caching.abstractions": { + "resolved": "src/Neolution.Extensions.Caching.Abstractions", + "link": true + }, + "node_modules/@neolution-ch/neolution.extensions.caching.distributed": { + "resolved": "src/Neolution.Extensions.Caching.Distributed", + "link": true + }, + "node_modules/@neolution-ch/neolution.extensions.caching.inmemory": { + "resolved": "src/Neolution.Extensions.Caching.InMemory", + "link": true + }, + "node_modules/@neolution-ch/neolution.extensions.caching.redishybrid": { + "resolved": "src/Neolution.Extensions.Caching.RedisHybrid", + "link": true + }, + "node_modules/@nodelib/fs.scandir": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", + "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@nodelib/fs.stat": "2.0.5", + "run-parallel": "^1.1.9" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.stat": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", + "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.walk": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", + "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@nodelib/fs.scandir": "2.1.5", + "fastq": "^1.6.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/ansi-colors": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.3.tgz", + "integrity": "sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "dev": true, + "license": "Python-2.0" + }, + "node_modules/array-union": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", + "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/better-path-resolve": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/better-path-resolve/-/better-path-resolve-1.0.0.tgz", + "integrity": "sha512-pbnl5XzGBdrFU/wT4jqmJVPn2B6UHPBOhzMQkY/SPUPB6QtUXtmBHBIwCbXJol93mOpGMnQyP/+BB19q04xj7g==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-windows": "^1.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/braces": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", + "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", + "dev": true, + "license": "MIT", + "dependencies": { + "fill-range": "^7.1.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/chardet": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/chardet/-/chardet-2.1.1.tgz", + "integrity": "sha512-PsezH1rqdV9VvyNhxxOW32/d75r01NY7TQCmOqomRo15ZSOKbpTFVsfjghxo6JloQUCGnH4k1LGu0R4yCLlWQQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/cross-spawn": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", + "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", + "dev": true, + "license": "MIT", + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/dataloader": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/dataloader/-/dataloader-1.4.0.tgz", + "integrity": "sha512-68s5jYdlvasItOJnCuI2Q9s4q98g0pCyL3HrcKJu8KNugUl8ahgmZYg38ysLTgQjjXX3H8CJLkAvWrclWfcalw==", + "dev": true, + "license": "BSD-3-Clause" + }, + "node_modules/detect-indent": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/detect-indent/-/detect-indent-6.1.0.tgz", + "integrity": "sha512-reYkTUJAZb9gUuZ2RvVCNhVHdg62RHnJ7WJl8ftMi4diZ6NWlciOzQN88pUhSELEwflJht4oQDv0F0BMlwaYtA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/dir-glob": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", + "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==", + "dev": true, + "license": "MIT", + "dependencies": { + "path-type": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/dotenv": { + "version": "8.6.0", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-8.6.0.tgz", + "integrity": "sha512-IrPdXQsk2BbzvCBGBOTmmSH5SodmqZNt4ERAZDmW4CT+tL8VtvinqywuANaFu4bOMWki16nqf0e4oC0QIaDr/g==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=10" + } + }, + "node_modules/enquirer": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/enquirer/-/enquirer-2.4.1.tgz", + "integrity": "sha512-rRqJg/6gd538VHvR3PSrdRBb/1Vy2YfzHqzvbhGIQpDRKIa4FgV/54b5Q1xYSxOOwKvjXweS26E0Q+nAMwp2pQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-colors": "^4.1.1", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8.6" + } + }, + "node_modules/esprima": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", + "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", + "dev": true, + "license": "BSD-2-Clause", + "bin": { + "esparse": "bin/esparse.js", + "esvalidate": "bin/esvalidate.js" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/extendable-error": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/extendable-error/-/extendable-error-0.1.7.tgz", + "integrity": "sha512-UOiS2in6/Q0FK0R0q6UY9vYpQ21mr/Qn1KOnte7vsACuNJf514WvCCUHSRCPcgjPT2bAhNIJdlE6bVap1GKmeg==", + "dev": true, + "license": "MIT" + }, + "node_modules/fast-glob": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.3.tgz", + "integrity": "sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==", + "dev": true, + "license": "MIT", + "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.8" + }, + "engines": { + "node": ">=8.6.0" + } + }, + "node_modules/fastq": { + "version": "1.20.1", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.20.1.tgz", + "integrity": "sha512-GGToxJ/w1x32s/D2EKND7kTil4n8OVk/9mycTc4VDza13lOvpUZTGX3mFSCtV9ksdGBVzvsyAVLM6mHFThxXxw==", + "dev": true, + "license": "ISC", + "dependencies": { + "reusify": "^1.0.4" + } + }, + "node_modules/fill-range": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", + "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", + "dev": true, + "license": "MIT", + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/find-up": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "dev": true, + "license": "MIT", + "dependencies": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/fs-extra": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-7.0.1.tgz", + "integrity": "sha512-YJDaCJZEnBmcbw13fvdAM9AwNOJwOzrE4pqMqBq5nFiEqXUqHwlK4B+3pUw6JNvfSPtX05xFHtYy/1ni01eGCw==", + "dev": true, + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.1.2", + "jsonfile": "^4.0.0", + "universalify": "^0.1.0" + }, + "engines": { + "node": ">=6 <7 || >=8" + } + }, + "node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/globby": { + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz", + "integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==", + "dev": true, + "license": "MIT", + "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" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/graceful-fs": { + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/human-id": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/human-id/-/human-id-4.1.3.tgz", + "integrity": "sha512-tsYlhAYpjCKa//8rXZ9DqKEawhPoSytweBC2eNvcaDK+57RZLHGqNs3PZTQO6yekLFSuvA6AlnAfrw1uBvtb+Q==", + "dev": true, + "license": "MIT", + "bin": { + "human-id": "dist/cli.js" + } + }, + "node_modules/iconv-lite": { + "version": "0.7.2", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.7.2.tgz", + "integrity": "sha512-im9DjEDQ55s9fL4EYzOAv0yMqmMBSZp6G0VvFyTMPKWxiSBHUj9NW/qqLmXUwXrrM7AvqSlTCfvqRb0cM8yYqw==", + "dev": true, + "license": "MIT", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + }, + "engines": { + "node": ">=0.10.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/ignore": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", + "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, + "node_modules/is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/is-subdir": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/is-subdir/-/is-subdir-1.2.0.tgz", + "integrity": "sha512-2AT6j+gXe/1ueqbW6fLZJiIw3F8iXGJtt0yDrZaBhAZEG1raiTxKWU+IPqMCzQAXOUCKdA4UDMgacKH25XG2Cw==", + "dev": true, + "license": "MIT", + "dependencies": { + "better-path-resolve": "1.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/is-windows": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-windows/-/is-windows-1.0.2.tgz", + "integrity": "sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "dev": true, + "license": "ISC" + }, + "node_modules/js-yaml": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.1.tgz", + "integrity": "sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==", + "dev": true, + "license": "MIT", + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/jsonfile": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz", + "integrity": "sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg==", + "dev": true, + "license": "MIT", + "optionalDependencies": { + "graceful-fs": "^4.1.6" + } + }, + "node_modules/locate-path": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-locate": "^4.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/lodash.startcase": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/lodash.startcase/-/lodash.startcase-4.4.0.tgz", + "integrity": "sha512-+WKqsK294HMSc2jEbNgpHpd0JfIBhp7rEV4aqXWqFr6AlXov+SlcgB1Fv01y2kGe3Gc8nMW7VA0SrGuSkRfIEg==", + "dev": true, + "license": "MIT" + }, + "node_modules/merge2": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", + "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 8" + } + }, + "node_modules/micromatch": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", + "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", + "dev": true, + "license": "MIT", + "dependencies": { + "braces": "^3.0.3", + "picomatch": "^2.3.1" + }, + "engines": { + "node": ">=8.6" + } + }, + "node_modules/mri": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/mri/-/mri-1.2.0.tgz", + "integrity": "sha512-tzzskb3bG8LvYGFF/mDTpq3jpI6Q9wc3LEmBaghu+DdCssd1FakN7Bc0hVNmEyGq1bq3RgfkCb3cmQLpNPOroA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/node-fetch": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz", + "integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==", + "dev": true, + "license": "MIT", + "dependencies": { + "whatwg-url": "^5.0.0" + }, + "engines": { + "node": "4.x || >=6.0.0" + }, + "peerDependencies": { + "encoding": "^0.1.0" + }, + "peerDependenciesMeta": { + "encoding": { + "optional": true + } + } + }, + "node_modules/outdent": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/outdent/-/outdent-0.5.0.tgz", + "integrity": "sha512-/jHxFIzoMXdqPzTaCpFzAAWhpkSjZPF4Vsn6jAfNpmbH/ymsmd7Qc6VE9BGn0L6YMj6uwpQLxCECpus4ukKS9Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/p-filter": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/p-filter/-/p-filter-2.1.0.tgz", + "integrity": "sha512-ZBxxZ5sL2HghephhpGAQdoskxplTwr7ICaehZwLIlfL6acuVgZPm8yBNuRAFBGEqtD/hmUeq9eqLg2ys9Xr/yw==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-map": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-try": "^2.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-locate": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-limit": "^2.2.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/p-map": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/p-map/-/p-map-2.1.0.tgz", + "integrity": "sha512-y3b8Kpd8OAN444hxfBbFfj1FY/RjtTd8tzYwhUqNYXx0fXx2iX4maP4Qr6qhIKbQXI02wTLAda4fYUbDagTUFw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/p-try": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", + "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/package-manager-detector": { + "version": "0.2.11", + "resolved": "https://registry.npmjs.org/package-manager-detector/-/package-manager-detector-0.2.11.tgz", + "integrity": "sha512-BEnLolu+yuz22S56CU1SUKq3XC3PkwD5wv4ikR4MfGvnRVcmzXR9DwSlW2fEamyTPyXHomBJRzgapeuBvRNzJQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "quansync": "^0.2.7" + } + }, + "node_modules/path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/path-type": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", + "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/picocolors": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", + "dev": true, + "license": "ISC" + }, + "node_modules/picomatch": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.2.tgz", + "integrity": "sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/pify": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz", + "integrity": "sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/prettier": { + "version": "2.8.8", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.8.8.tgz", + "integrity": "sha512-tdN8qQGvNjw4CHbY+XXk0JgCXn9QiF21a55rBe5LJAU+kDyC4WQn4+awm2Xfk2lQMk5fKup9XgzTZtGkjBdP9Q==", + "dev": true, + "license": "MIT", + "bin": { + "prettier": "bin-prettier.js" + }, + "engines": { + "node": ">=10.13.0" + }, + "funding": { + "url": "https://github.com/prettier/prettier?sponsor=1" + } + }, + "node_modules/quansync": { + "version": "0.2.11", + "resolved": "https://registry.npmjs.org/quansync/-/quansync-0.2.11.tgz", + "integrity": "sha512-AifT7QEbW9Nri4tAwR5M/uzpBuqfZf+zwaEM/QkzEjj7NBuFD2rBuy0K3dE+8wltbezDV7JMA0WfnCPYRSYbXA==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/antfu" + }, + { + "type": "individual", + "url": "https://github.com/sponsors/sxzz" + } + ], + "license": "MIT" + }, + "node_modules/queue-microtask": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", + "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/read-yaml-file": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/read-yaml-file/-/read-yaml-file-1.1.0.tgz", + "integrity": "sha512-VIMnQi/Z4HT2Fxuwg5KrY174U1VdUIASQVWXXyqtNRtxSr9IYkn1rsI6Tb6HsrHCmB7gVpNwX6JxPTHcH6IoTA==", + "dev": true, + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.1.5", + "js-yaml": "^3.6.1", + "pify": "^4.0.1", + "strip-bom": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/read-yaml-file/node_modules/argparse": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", + "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "dev": true, + "license": "MIT", + "dependencies": { + "sprintf-js": "~1.0.2" + } + }, + "node_modules/read-yaml-file/node_modules/js-yaml": { + "version": "3.14.2", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.2.tgz", + "integrity": "sha512-PMSmkqxr106Xa156c2M265Z+FTrPl+oxd/rgOQy2tijQeK5TxQ43psO1ZCwhVOSdnn+RzkzlRz/eY4BgJBYVpg==", + "dev": true, + "license": "MIT", + "dependencies": { + "argparse": "^1.0.7", + "esprima": "^4.0.0" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/resolve-from": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", + "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/reusify": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.1.0.tgz", + "integrity": "sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==", + "dev": true, + "license": "MIT", + "engines": { + "iojs": ">=1.0.0", + "node": ">=0.10.0" + } + }, + "node_modules/run-parallel": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", + "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", + "dependencies": { + "queue-microtask": "^1.2.2" + } + }, + "node_modules/safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", + "dev": true, + "license": "MIT" + }, + "node_modules/semver": { + "version": "7.8.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.8.1.tgz", + "integrity": "sha512-rkVq3IXh+4FDGch+KwzX3aV9W3kO54GyEgpvBzSyctDA6Xtd7RJQV1xmXbeQp5v7+VzLOfVqiutSE6GICgPFvg==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dev": true, + "license": "MIT", + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/signal-exit": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", + "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/slash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", + "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/spawndamnit": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/spawndamnit/-/spawndamnit-3.0.1.tgz", + "integrity": "sha512-MmnduQUuHCoFckZoWnXsTg7JaiLBJrKFj9UI2MbRPGaJeVpsLcVBu6P/IGZovziM/YBsellCmsprgNA+w0CzVg==", + "dev": true, + "license": "SEE LICENSE IN LICENSE", + "dependencies": { + "cross-spawn": "^7.0.5", + "signal-exit": "^4.0.1" + } + }, + "node_modules/sprintf-js": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", + "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==", + "dev": true, + "license": "BSD-3-Clause" + }, + "node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-bom": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", + "integrity": "sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/term-size": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/term-size/-/term-size-2.2.1.tgz", + "integrity": "sha512-wK0Ri4fOGjv/XPy8SBHZChl8CM7uMc5VML7SqiQ0zG7+J5Vr+RMQDoHa2CNT6KHUnTGIXH34UDMkPzAUyapBZg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/tr46": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", + "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==", + "dev": true, + "license": "MIT" + }, + "node_modules/undici-types": { + "version": "7.24.6", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.24.6.tgz", + "integrity": "sha512-WRNW+sJgj5OBN4/0JpHFqtqzhpbnV0GuB+OozA9gCL7a993SmU+1JBZCzLNxYsbMfIeDL+lTsphD5jN5N+n0zg==", + "dev": true, + "license": "MIT", + "optional": true + }, + "node_modules/universalify": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz", + "integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 4.0.0" + } + }, + "node_modules/webidl-conversions": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", + "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==", + "dev": true, + "license": "BSD-2-Clause" + }, + "node_modules/whatwg-url": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", + "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", + "dev": true, + "license": "MIT", + "dependencies": { + "tr46": "~0.0.3", + "webidl-conversions": "^3.0.0" + } + }, + "node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "license": "ISC", + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "src/Neolution.Extensions.Caching.Abstractions": { + "name": "@neolution-ch/neolution.extensions.caching.abstractions", + "version": "2.1.1" + }, + "src/Neolution.Extensions.Caching.Distributed": { + "name": "@neolution-ch/neolution.extensions.caching.distributed", + "version": "2.1.1" + }, + "src/Neolution.Extensions.Caching.InMemory": { + "name": "@neolution-ch/neolution.extensions.caching.inmemory", + "version": "2.1.1" + }, + "src/Neolution.Extensions.Caching.RedisHybrid": { + "name": "@neolution-ch/neolution.extensions.caching.redishybrid", + "version": "2.1.1" + } + } +} diff --git a/package.json b/package.json new file mode 100644 index 0000000..160c037 --- /dev/null +++ b/package.json @@ -0,0 +1,14 @@ +{ + "name": "neolution.extensions.caching", + "private": true, + "workspaces": ["src/*"], + "scripts": { + "changeset": "changeset", + "version-packages": "node scripts/sync-versions.js", + "release": "changeset publish" + }, + "devDependencies": { + "@changesets/cli": "^2.27.0", + "@changesets/changelog-github": "^0.5.0" + } +} diff --git a/scripts/generate-dependabot-changeset.js b/scripts/generate-dependabot-changeset.js new file mode 100644 index 0000000..5576d0c --- /dev/null +++ b/scripts/generate-dependabot-changeset.js @@ -0,0 +1,113 @@ +const fs = require("fs"); +const path = require("path"); +const crypto = require("crypto"); + +const prTitle = process.env.PR_TITLE || ""; +const prBody = process.env.PR_BODY || ""; +const packableProjectsChanged = process.env.PACKABLE_PROJECTS_CHANGED === "true"; + +/** + * Read the fixed package groups from .changeset/config.json + */ +function getFixedPackages() { + const configPath = path.resolve(__dirname, "..", ".changeset", "config.json"); + const raw = fs.readFileSync(configPath, "utf8").replace(/^/, ""); + const config = JSON.parse(raw); + return (config.fixed || []).flat(); +} + +/** + * Parse dependency updates from the Dependabot PR body. + * + * Security PRs use: "Bumps [name](url) from X to Y" + * Grouped PRs use: "Updated [name](url) from X to Y" + */ +function parseDependencyUpdates(body) { + const regex = /(?:Bumps|Updated) \[([^\]]+)\]\([^)]+\) from (\S+) to (\S+)/g; + const updates = []; + let match; + while ((match = regex.exec(body)) !== null) { + updates.push({ + name: match[1], + from: match[2].replace(/[.,;:]+$/, ""), + to: match[3].replace(/[.,;:]+$/, ""), + }); + } + return updates; +} + +/** + * Compare two version strings and return the bump type. + * Handles versions with or without 'v' prefix. + * Falls back to "patch" if versions can't be parsed. + */ +function getBumpType(from, to) { + const parse = (v) => + v + .replace(/^v/, "") + .split(".") + .map((n) => parseInt(n, 10)); + const fromParts = parse(from); + const toParts = parse(to); + + if (fromParts.some(isNaN) || toParts.some(isNaN)) { + return "patch"; + } + + if ((toParts[0] || 0) !== (fromParts[0] || 0)) return "major"; + if ((toParts[1] || 0) !== (fromParts[1] || 0)) return "minor"; + return "patch"; +} + +function bumpEmoji(bumpType) { + switch (bumpType) { + case "major": + return "🔴 major"; + case "minor": + return "🟡 minor"; + default: + return "🟢 patch"; + } +} + +function buildChangeset(fixedPackages, updates) { + const lines = []; + + lines.push("---"); + if (packableProjectsChanged) { + for (const pkg of fixedPackages) { + lines.push(`"${pkg}": patch`); + } + } + lines.push("---"); + lines.push(""); + + lines.push(prTitle); + + if (updates.length > 0) { + lines.push(""); + lines.push("| Package | From | To | Bump |"); + lines.push("|---------|------|----|------|"); + for (const dep of updates) { + const bump = getBumpType(dep.from, dep.to); + lines.push( + `| ${dep.name} | ${dep.from} | ${dep.to} | ${bumpEmoji(bump)} |`, + ); + } + } + + lines.push(""); + return lines.join("\n"); +} + +const fixedPackages = getFixedPackages(); +const updates = parseDependencyUpdates(prBody); +const content = buildChangeset(fixedPackages, updates); + +const id = crypto.randomBytes(8).toString("hex"); +const filename = `dependabot-${id}.md`; +const changesetDir = path.resolve(__dirname, "..", ".changeset"); +const filepath = path.join(changesetDir, filename); + +fs.writeFileSync(filepath, content, "utf8"); +console.log(`Created changeset: .changeset/${filename}`); diff --git a/scripts/publish.js b/scripts/publish.js new file mode 100644 index 0000000..cfccdd7 --- /dev/null +++ b/scripts/publish.js @@ -0,0 +1,54 @@ +/** + * Custom publish script for changesets/action. + * + * This repo publishes NuGet packages, not npm packages. We still use + * @changesets to manage versions and changelogs, and we still use + * changesets/action to push git tags and create GitHub Releases. The actual + * artifact build/push is handled by .github/workflows/nuget-publish.yml, + * which triggers on the Releases that this step creates. + * + * changesets/action detects "what was published" by scanning this script's + * stdout for lines matching: New tag: @ + * For each match it pushes the tag and creates the GitHub Release. See: + * https://github.com/changesets/action/blob/main/src/run.ts + * + * Workspace packages live under src/. We emit a "New tag:" line per package + * if (and only if) the tag for the current version does not already exist, + * making this script idempotent across workflow re-runs. + */ + +const fs = require("fs"); +const path = require("path"); +const { execSync } = require("child_process"); + +const repoRoot = path.join(__dirname, ".."); +const srcDir = path.join(repoRoot, "src"); + +const packages = fs + .readdirSync(srcDir) + .map((dir) => path.join(srcDir, dir, "package.json")) + .filter((p) => fs.existsSync(p)) + .map((p) => JSON.parse(fs.readFileSync(p, "utf8"))) + .map((pkg) => ({ name: pkg.name, version: pkg.version })); + +if (packages.length === 0) { + console.error("ERROR: No workspace packages found under src/"); + process.exit(1); +} + +const firstTag = `${packages[0].name}@${packages[0].version}`; +const existing = execSync(`git tag -l "${firstTag}"`, { + cwd: repoRoot, + encoding: "utf8", +}).trim(); + +if (existing) { + console.log( + `Tag ${firstTag} already exists. Nothing to release. Exiting cleanly.`, + ); + process.exit(0); +} + +for (const pkg of packages) { + console.log(`New tag: ${pkg.name}@${pkg.version}`); +} diff --git a/scripts/sync-versions.js b/scripts/sync-versions.js new file mode 100644 index 0000000..ae7231e --- /dev/null +++ b/scripts/sync-versions.js @@ -0,0 +1,36 @@ +const fs = require("fs"); +const path = require("path"); +const { execSync } = require("child_process"); + +execSync("npx changeset version", { stdio: "inherit" }); + +const repoRoot = path.join(__dirname, ".."); +const srcDir = path.join(repoRoot, "src"); +const libs = fs + .readdirSync(srcDir) + .filter((d) => fs.existsSync(path.join(srcDir, d, "package.json"))); + +if (libs.length === 0) { + console.error("ERROR: No workspace packages found under src/"); + process.exit(1); +} + +const referencePkgJson = path.join(srcDir, libs[0], "package.json"); +const version = JSON.parse(fs.readFileSync(referencePkgJson, "utf8")).version; + +const propsPath = path.join(repoRoot, "Directory.Build.props"); +let props = fs.readFileSync(propsPath, "utf8"); +const updated = props.replace( + /[^<]*<\/Version>/, + `${version}`, +); + +if (props === updated) { + console.error( + "ERROR: Could not find ... in Directory.Build.props", + ); + process.exit(1); +} + +fs.writeFileSync(propsPath, updated); +console.log(`Updated Directory.Build.props to ${version}`); diff --git a/Neolution.Extensions.Caching.Abstractions/CacheEntryOptions.cs b/src/Neolution.Extensions.Caching.Abstractions/CacheEntryOptions.cs similarity index 100% rename from Neolution.Extensions.Caching.Abstractions/CacheEntryOptions.cs rename to src/Neolution.Extensions.Caching.Abstractions/CacheEntryOptions.cs diff --git a/Neolution.Extensions.Caching.Abstractions/DistributedCache.cs b/src/Neolution.Extensions.Caching.Abstractions/DistributedCache.cs similarity index 100% rename from Neolution.Extensions.Caching.Abstractions/DistributedCache.cs rename to src/Neolution.Extensions.Caching.Abstractions/DistributedCache.cs diff --git a/Neolution.Extensions.Caching.Abstractions/IDistributedCache.cs b/src/Neolution.Extensions.Caching.Abstractions/IDistributedCache.cs similarity index 100% rename from Neolution.Extensions.Caching.Abstractions/IDistributedCache.cs rename to src/Neolution.Extensions.Caching.Abstractions/IDistributedCache.cs diff --git a/Neolution.Extensions.Caching.Abstractions/IMemoryCache.cs b/src/Neolution.Extensions.Caching.Abstractions/IMemoryCache.cs similarity index 100% rename from Neolution.Extensions.Caching.Abstractions/IMemoryCache.cs rename to src/Neolution.Extensions.Caching.Abstractions/IMemoryCache.cs diff --git a/Neolution.Extensions.Caching.Abstractions/MemoryCache.cs b/src/Neolution.Extensions.Caching.Abstractions/MemoryCache.cs similarity index 100% rename from Neolution.Extensions.Caching.Abstractions/MemoryCache.cs rename to src/Neolution.Extensions.Caching.Abstractions/MemoryCache.cs diff --git a/Neolution.Extensions.Caching.Abstractions/Neolution.Extensions.Caching.Abstractions.csproj b/src/Neolution.Extensions.Caching.Abstractions/Neolution.Extensions.Caching.Abstractions.csproj similarity index 100% rename from Neolution.Extensions.Caching.Abstractions/Neolution.Extensions.Caching.Abstractions.csproj rename to src/Neolution.Extensions.Caching.Abstractions/Neolution.Extensions.Caching.Abstractions.csproj diff --git a/src/Neolution.Extensions.Caching.Abstractions/package.json b/src/Neolution.Extensions.Caching.Abstractions/package.json new file mode 100644 index 0000000..95e9895 --- /dev/null +++ b/src/Neolution.Extensions.Caching.Abstractions/package.json @@ -0,0 +1,5 @@ +{ + "name": "@neolution-ch/neolution.extensions.caching.abstractions", + "version": "2.1.1", + "private": true +} diff --git a/Neolution.Extensions.Caching.Distributed/MessagePackDistributedCache.cs b/src/Neolution.Extensions.Caching.Distributed/MessagePackDistributedCache.cs similarity index 100% rename from Neolution.Extensions.Caching.Distributed/MessagePackDistributedCache.cs rename to src/Neolution.Extensions.Caching.Distributed/MessagePackDistributedCache.cs diff --git a/Neolution.Extensions.Caching.Distributed/MessagePackDistributedCacheOptions.cs b/src/Neolution.Extensions.Caching.Distributed/MessagePackDistributedCacheOptions.cs similarity index 100% rename from Neolution.Extensions.Caching.Distributed/MessagePackDistributedCacheOptions.cs rename to src/Neolution.Extensions.Caching.Distributed/MessagePackDistributedCacheOptions.cs diff --git a/Neolution.Extensions.Caching.Distributed/Neolution.Extensions.Caching.Distributed.csproj b/src/Neolution.Extensions.Caching.Distributed/Neolution.Extensions.Caching.Distributed.csproj similarity index 100% rename from Neolution.Extensions.Caching.Distributed/Neolution.Extensions.Caching.Distributed.csproj rename to src/Neolution.Extensions.Caching.Distributed/Neolution.Extensions.Caching.Distributed.csproj diff --git a/Neolution.Extensions.Caching.Distributed/ServiceCollectionExtensions.cs b/src/Neolution.Extensions.Caching.Distributed/ServiceCollectionExtensions.cs similarity index 100% rename from Neolution.Extensions.Caching.Distributed/ServiceCollectionExtensions.cs rename to src/Neolution.Extensions.Caching.Distributed/ServiceCollectionExtensions.cs diff --git a/src/Neolution.Extensions.Caching.Distributed/package.json b/src/Neolution.Extensions.Caching.Distributed/package.json new file mode 100644 index 0000000..dc64365 --- /dev/null +++ b/src/Neolution.Extensions.Caching.Distributed/package.json @@ -0,0 +1,5 @@ +{ + "name": "@neolution-ch/neolution.extensions.caching.distributed", + "version": "2.1.1", + "private": true +} diff --git a/Neolution.Extensions.Caching.InMemory/InMemoryCache.cs b/src/Neolution.Extensions.Caching.InMemory/InMemoryCache.cs similarity index 100% rename from Neolution.Extensions.Caching.InMemory/InMemoryCache.cs rename to src/Neolution.Extensions.Caching.InMemory/InMemoryCache.cs diff --git a/Neolution.Extensions.Caching.InMemory/Neolution.Extensions.Caching.InMemory.csproj b/src/Neolution.Extensions.Caching.InMemory/Neolution.Extensions.Caching.InMemory.csproj similarity index 100% rename from Neolution.Extensions.Caching.InMemory/Neolution.Extensions.Caching.InMemory.csproj rename to src/Neolution.Extensions.Caching.InMemory/Neolution.Extensions.Caching.InMemory.csproj diff --git a/Neolution.Extensions.Caching.InMemory/ServiceCollectionExtensions.cs b/src/Neolution.Extensions.Caching.InMemory/ServiceCollectionExtensions.cs similarity index 100% rename from Neolution.Extensions.Caching.InMemory/ServiceCollectionExtensions.cs rename to src/Neolution.Extensions.Caching.InMemory/ServiceCollectionExtensions.cs diff --git a/src/Neolution.Extensions.Caching.InMemory/package.json b/src/Neolution.Extensions.Caching.InMemory/package.json new file mode 100644 index 0000000..055ea81 --- /dev/null +++ b/src/Neolution.Extensions.Caching.InMemory/package.json @@ -0,0 +1,5 @@ +{ + "name": "@neolution-ch/neolution.extensions.caching.inmemory", + "version": "2.1.1", + "private": true +} diff --git a/Neolution.Extensions.Caching.RedisHybrid/MsgPackSerializer.cs b/src/Neolution.Extensions.Caching.RedisHybrid/MsgPackSerializer.cs similarity index 100% rename from Neolution.Extensions.Caching.RedisHybrid/MsgPackSerializer.cs rename to src/Neolution.Extensions.Caching.RedisHybrid/MsgPackSerializer.cs diff --git a/Neolution.Extensions.Caching.RedisHybrid/Neolution.Extensions.Caching.RedisHybrid.csproj b/src/Neolution.Extensions.Caching.RedisHybrid/Neolution.Extensions.Caching.RedisHybrid.csproj similarity index 100% rename from Neolution.Extensions.Caching.RedisHybrid/Neolution.Extensions.Caching.RedisHybrid.csproj rename to src/Neolution.Extensions.Caching.RedisHybrid/Neolution.Extensions.Caching.RedisHybrid.csproj diff --git a/Neolution.Extensions.Caching.RedisHybrid/RedisHybridCache.cs b/src/Neolution.Extensions.Caching.RedisHybrid/RedisHybridCache.cs similarity index 100% rename from Neolution.Extensions.Caching.RedisHybrid/RedisHybridCache.cs rename to src/Neolution.Extensions.Caching.RedisHybrid/RedisHybridCache.cs diff --git a/Neolution.Extensions.Caching.RedisHybrid/ServiceCollectionExtensions.cs b/src/Neolution.Extensions.Caching.RedisHybrid/ServiceCollectionExtensions.cs similarity index 100% rename from Neolution.Extensions.Caching.RedisHybrid/ServiceCollectionExtensions.cs rename to src/Neolution.Extensions.Caching.RedisHybrid/ServiceCollectionExtensions.cs diff --git a/src/Neolution.Extensions.Caching.RedisHybrid/package.json b/src/Neolution.Extensions.Caching.RedisHybrid/package.json new file mode 100644 index 0000000..64c4092 --- /dev/null +++ b/src/Neolution.Extensions.Caching.RedisHybrid/package.json @@ -0,0 +1,5 @@ +{ + "name": "@neolution-ch/neolution.extensions.caching.redishybrid", + "version": "2.1.1", + "private": true +} diff --git a/Neolution.Extensions.Caching.UnitTests/DistributedCacheAsyncTests.cs b/tests/Neolution.Extensions.Caching.UnitTests/DistributedCacheAsyncTests.cs similarity index 100% rename from Neolution.Extensions.Caching.UnitTests/DistributedCacheAsyncTests.cs rename to tests/Neolution.Extensions.Caching.UnitTests/DistributedCacheAsyncTests.cs diff --git a/Neolution.Extensions.Caching.UnitTests/DistributedCacheTests.cs b/tests/Neolution.Extensions.Caching.UnitTests/DistributedCacheTests.cs similarity index 100% rename from Neolution.Extensions.Caching.UnitTests/DistributedCacheTests.cs rename to tests/Neolution.Extensions.Caching.UnitTests/DistributedCacheTests.cs diff --git a/Neolution.Extensions.Caching.UnitTests/MemoryCacheTests.cs b/tests/Neolution.Extensions.Caching.UnitTests/MemoryCacheTests.cs similarity index 100% rename from Neolution.Extensions.Caching.UnitTests/MemoryCacheTests.cs rename to tests/Neolution.Extensions.Caching.UnitTests/MemoryCacheTests.cs diff --git a/Neolution.Extensions.Caching.UnitTests/Models/TestCacheId.cs b/tests/Neolution.Extensions.Caching.UnitTests/Models/TestCacheId.cs similarity index 100% rename from Neolution.Extensions.Caching.UnitTests/Models/TestCacheId.cs rename to tests/Neolution.Extensions.Caching.UnitTests/Models/TestCacheId.cs diff --git a/Neolution.Extensions.Caching.UnitTests/Neolution.Extensions.Caching.UnitTests.csproj b/tests/Neolution.Extensions.Caching.UnitTests/Neolution.Extensions.Caching.UnitTests.csproj similarity index 81% rename from Neolution.Extensions.Caching.UnitTests/Neolution.Extensions.Caching.UnitTests.csproj rename to tests/Neolution.Extensions.Caching.UnitTests/Neolution.Extensions.Caching.UnitTests.csproj index 858824e..f57e4ab 100644 --- a/Neolution.Extensions.Caching.UnitTests/Neolution.Extensions.Caching.UnitTests.csproj +++ b/tests/Neolution.Extensions.Caching.UnitTests/Neolution.Extensions.Caching.UnitTests.csproj @@ -34,9 +34,9 @@ - - - + + + diff --git a/Neolution.Extensions.Caching.UnitTests/RedisHybridCacheTests.cs b/tests/Neolution.Extensions.Caching.UnitTests/RedisHybridCacheTests.cs similarity index 100% rename from Neolution.Extensions.Caching.UnitTests/RedisHybridCacheTests.cs rename to tests/Neolution.Extensions.Caching.UnitTests/RedisHybridCacheTests.cs diff --git a/Neolution.Extensions.Caching.UnitTests/TestData/ServiceCollectionTestDataCollection.cs b/tests/Neolution.Extensions.Caching.UnitTests/TestData/ServiceCollectionTestDataCollection.cs similarity index 100% rename from Neolution.Extensions.Caching.UnitTests/TestData/ServiceCollectionTestDataCollection.cs rename to tests/Neolution.Extensions.Caching.UnitTests/TestData/ServiceCollectionTestDataCollection.cs