diff --git a/.bun-version b/.bun-version index c813fe11..d5e98f72 100644 --- a/.bun-version +++ b/.bun-version @@ -1 +1 @@ -1.2.5 +1.3.2 \ No newline at end of file diff --git a/.changeset/README.md b/.changeset/README.md new file mode 100644 index 00000000..8d31c94f --- /dev/null +++ b/.changeset/README.md @@ -0,0 +1,29 @@ +# 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) + +## Usage + +To add a changeset, run: + +```bash +bun run changeset +``` + +To version the package based on changesets, run: + +```bash +bun run changeset:version +``` + +To check the status of changesets, run: + +```bash +bun run changeset:status +``` + diff --git a/.changeset/config.json b/.changeset/config.json new file mode 100644 index 00000000..2794a175 --- /dev/null +++ b/.changeset/config.json @@ -0,0 +1,16 @@ +{ + "$schema": "https://unpkg.com/@changesets/config@3.0.0/schema.json", + "changelog": "@changesets/cli/changelog", + "commit": false, + "fixed": [], + "linked": [], + "access": "restricted", + "baseBranch": "main", + "updateInternalDependencies": "patch", + "ignore": [], + "privatePackages": { + "version": true, + "tag": false + } +} + diff --git a/.github/actions/deploy/action.yml b/.github/actions/deploy/action.yml index ab4718fb..87c8cb66 100644 --- a/.github/actions/deploy/action.yml +++ b/.github/actions/deploy/action.yml @@ -13,10 +13,6 @@ inputs: runs: using: composite steps: - - name: Validate environment - shell: bash - run: if [ "${{ inputs.environment }}" != "staging" ] && [ "${{ inputs.environment }}" != "live" ]; then echo "Invalid environment"; exit 1; fi - - name: Setup Terraform uses: hashicorp/setup-terraform@v3 with: @@ -56,26 +52,41 @@ runs: working-directory: terraform run: terraform validate - - name: Setup version information + - name: Build functions for Lambda shell: bash - env: - GITHUB_EVENT_NAME: ${{ github.event_name }} - GITHUB_REF_NAME: ${{ github.ref_name }} run: | - chmod +x scripts/version.sh - ./scripts/version.sh info + docker run --rm \ + --platform linux/amd64 \ + -v "${{ github.workspace }}:/workspace" \ + -w /workspace \ + swift:6.1-amazonlinux2 \ + bash -c "swift build -c release --static-swift-stdlib && \ + cp .build/release/lambda .build/release/bootstrap && \ + chmod 755 .build/release/bootstrap && \ + mkdir -p .build/release/authorizer-package && \ + cp .build/release/authorizer .build/release/authorizer-package/bootstrap && \ + chmod 755 .build/release/authorizer-package/bootstrap" + sudo chown -R $(id -u):$(id -g) .build - - name: Build .NET Lambda functions + - name: Get version + id: version shell: bash run: | - chmod +x scripts/build.sh - ./scripts/build.sh + # Use VERSION env var if set (for pre-releases), otherwise read from package.json + if [ -n "$VERSION" ]; then + echo "Using provided version: $VERSION" + echo "APP_VERSION=$VERSION" >> $GITHUB_ENV + else + PKG_VERSION=$(jq -r .version package.json) + echo "Using package.json version: $PKG_VERSION" + echo "APP_VERSION=$PKG_VERSION" >> $GITHUB_ENV + fi - name: Terraform plan id: plan shell: bash working-directory: terraform - run: terraform plan -no-color -out=tfplan -var="app_version=$VERSION" -var="git_sha=$GIT_SHA" + run: terraform plan -no-color -out=tfplan -var="app_version=${{ env.APP_VERSION }}" -var="git_sha=$GIT_SHA" - name: Terraform apply shell: bash diff --git a/.github/actions/install/action.yml b/.github/actions/install/action.yml index f45f037b..7a2bb373 100644 --- a/.github/actions/install/action.yml +++ b/.github/actions/install/action.yml @@ -3,38 +3,18 @@ description: Install dependencies runs: using: composite steps: - - name: Install moreutils - run: sudo apt install moreutils - shell: bash - - # https://github.com/actions/virtual-environments/issues/1187 - - name: tune linux network - run: sudo ethtool -K eth0 tx off rx off - shell: bash - - name: Ensure branch is rebased with main run: git stash && git pull --rebase shell: bash - - name: Setup .NET - uses: actions/setup-dotnet@v4 + - name: Setup Swift + uses: swift-actions/setup-swift@v3 with: - dotnet-version: "8.0.x" - - - name: Restore .NET dependencies for main lambda - shell: bash - working-directory: apps/lho-lambda/src - run: dotnet restore - - - name: Restore .NET dependencies for authorizer - shell: bash - working-directory: apps/lho-authorizer/src - run: dotnet restore + swift-version: "6.1.0" - # https://github.com/actions/virtual-environments/issues/1187 - - name: tune linux network - run: sudo ethtool -K eth0 tx off rx off + - name: Resolve Swift dependencies shell: bash + run: swift package resolve - name: 🥟 Setup Bun uses: oven-sh/setup-bun@v2 diff --git a/.github/actions/validate/action.yml b/.github/actions/validate/action.yml index 254260c1..00adbcb4 100644 --- a/.github/actions/validate/action.yml +++ b/.github/actions/validate/action.yml @@ -1,14 +1,12 @@ -name: Install +name: Validate description: Validate runs: using: composite steps: - - name: Build main lambda + - name: Run SwiftLint shell: bash - working-directory: apps/lho-lambda - run: dotnet build --configuration Release --no-restore + run: swiftlint - - name: Build authorizer lambda + - name: Build functions shell: bash - working-directory: apps/lho-authorizer - run: dotnet build --configuration Release --no-restore + run: swift build -c release diff --git a/.github/workflows/build-prerelease-version.yml b/.github/workflows/build-prerelease-version.yml index 62d6e0e9..f0bf3dbe 100644 --- a/.github/workflows/build-prerelease-version.yml +++ b/.github/workflows/build-prerelease-version.yml @@ -1,11 +1,12 @@ -name: Build pre-release version +name: Deploy Staging (Pre-release) + on: workflow_dispatch: - pull_request: - types: [opened, synchronize, edited, reopened] + concurrency: group: ${{ github.workflow }}-${{ github.ref }} cancel-in-progress: true + env: TF_VAR_env: staging TF_VAR_env_vars: ${{ secrets.LIVE_ENV_VARS }} @@ -17,42 +18,38 @@ env: TF_VAR_certificate_chain: ${{ secrets.STAGING_CERTIFICATE_CHAIN }} TF_VAR_deployed_by: ${{ github.actor }} TF_VAR_git_sha: ${{ github.sha }} - TURBO_TOKEN: ${{ secrets.TURBO_TOKEN }} - TURBO_TEAM: luke-h1-project TF_VAR_api_key: ${{ secrets.STAGING_API_KEY }} - TF_VAR_discord_webhook_url: ${{ secrets.DISCORD_ALERTS_WEBHOOK_URL}} + TF_VAR_discord_webhook_url: ${{ secrets.DISCORD_ALERTS_WEBHOOK_URL }} permissions: write-all jobs: deploy: - name: Deploy to staging + name: Deploy to Staging runs-on: ubuntu-latest - timeout-minutes: 10 + timeout-minutes: 15 steps: - name: Checkout repository uses: actions/checkout@v4 with: fetch-depth: 0 - ref: ${{ github.head_ref }} - - - name: Ensure rebased with main - run: ./scripts/ensure-rebased.sh - - - name: Install - uses: ./.github/actions/install - - name: Validate - uses: ./.github/actions/validate - - - name: Changelogs - uses: ./.github/actions/changelog + - name: Setup Bun + uses: oven-sh/setup-bun@v2 with: - prerelease: true - publish: false + bun-version-file: ".bun-version" + + - name: Install dependencies + run: bun install --frozen-lockfile - - name: fetch latest commits - run: git fetch && git pull + - name: Get pre-release version + id: version + run: | + BASE_VERSION=$(jq -r .version package.json) + SHORT_SHA=$(echo ${{ github.sha }} | cut -c1-7) + PRERELEASE_VERSION="${BASE_VERSION}-staging.${SHORT_SHA}" + echo "version=${PRERELEASE_VERSION}" >> $GITHUB_OUTPUT + echo "Pre-release version: ${PRERELEASE_VERSION}" - name: Deploy uses: ./.github/actions/deploy @@ -60,19 +57,12 @@ jobs: environment: staging aws-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }} aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }} + env: + VERSION: ${{ steps.version.outputs.version }} - - name: Get versions - id: lambda-version + - name: Summary run: | - echo "::set-output name=LAMBDA_VERSION::$(./scripts/version.sh get)" - echo "::set-output name=AUTHORIZER_VERSION::$(./scripts/version.sh get)" - - - uses: actions/github-script@v7 - with: - script: | - github.rest.issues.createComment({ - issue_number: context.issue.number, - owner: context.repo.owner, - repo: context.repo.repo, - body: '* [Lambda version](https://nowplaying-${{env.TF_VAR_env}}.lhowsam.com) - `${{ steps.lambda-version.outputs.LAMBDA_VERSION }}`\n* [Authorizer version](https://nowplaying-${{env.TF_VAR_env}}.lhowsam.com) - `${{ steps.lambda-version.outputs.AUTHORIZER_VERSION }}`' - }) + echo "## 🚀 Staging Deployment" >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + echo "- **Version**: \`${{ steps.version.outputs.version }}\`" >> $GITHUB_STEP_SUMMARY + echo "- **URL**: https://nowplaying-staging.lhowsam.com" >> $GITHUB_STEP_SUMMARY diff --git a/.github/workflows/build-release-version.yml b/.github/workflows/build-release-version.yml index 1e94efb2..099dc0fe 100644 --- a/.github/workflows/build-release-version.yml +++ b/.github/workflows/build-release-version.yml @@ -31,7 +31,7 @@ env: jobs: release: name: Release packages - runs-on: ubuntu-latest + runs-on: macos-latest timeout-minutes: 10 steps: - name: Checkout repository diff --git a/.github/workflows/deploy-test.yml b/.github/workflows/deploy-test.yml new file mode 100644 index 00000000..19b6bef6 --- /dev/null +++ b/.github/workflows/deploy-test.yml @@ -0,0 +1,103 @@ +name: Deploy Test (PR Preview) + +on: + workflow_dispatch: + pull_request: + types: [opened, synchronize, reopened] + +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + +env: + TF_VAR_env: test + TF_VAR_env_vars: ${{ secrets.LIVE_ENV_VARS }} + TF_VAR_zone_id: ${{ secrets.LIVE_ZONE_ID }} + TF_VAR_root_domain: lhowsam.com + TF_VAR_sub_domain: nowplaying.lhowsam.com + TF_VAR_private_key: ${{ secrets.STAGING_PRIVATE_KEY }} + TF_VAR_certificate_body: ${{ secrets.STAGING_CERTIFICATE_BODY }} + TF_VAR_certificate_chain: ${{ secrets.STAGING_CERTIFICATE_CHAIN }} + TF_VAR_deployed_by: ${{ github.actor }} + TF_VAR_git_sha: ${{ github.sha }} + TF_VAR_api_key: ${{ secrets.STAGING_API_KEY }} + TF_VAR_discord_webhook_url: ${{ secrets.DISCORD_ALERTS_WEBHOOK_URL }} + +permissions: write-all + +jobs: + deploy: + name: Deploy to Test + runs-on: ubuntu-latest + timeout-minutes: 15 + steps: + - name: Checkout repository + uses: actions/checkout@v4 + with: + fetch-depth: 0 + ref: ${{ github.head_ref }} + + - name: Ensure rebased with main + run: ./scripts/ensure-rebased.sh + + - name: Setup Bun + uses: oven-sh/setup-bun@v2 + with: + bun-version-file: ".bun-version" + + - name: Install dependencies + run: bun install --frozen-lockfile + + - name: Get pre-release version + id: version + run: | + BASE_VERSION=$(jq -r .version package.json) + PR_NUMBER=${{ github.event.pull_request.number }} + SHORT_SHA=$(echo ${{ github.sha }} | cut -c1-7) + PRERELEASE_VERSION="${BASE_VERSION}-pr.${PR_NUMBER}.${SHORT_SHA}" + echo "version=${PRERELEASE_VERSION}" >> $GITHUB_OUTPUT + echo "Pre-release version: ${PRERELEASE_VERSION}" + + - name: Deploy + uses: ./.github/actions/deploy + with: + environment: ${{ env.TF_VAR_env }} + aws-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }} + aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }} + env: + VERSION: ${{ steps.version.outputs.version }} + + - name: Comment on PR + uses: actions/github-script@v7 + with: + script: | + const version = '${{ steps.version.outputs.version }}'; + const marker = ''; + const body = `${marker}\n## 🚀 Test Deployment\n\n- **Version**: \`${version}\`\n- **URL**: https://nowplaying-test.lhowsam.com\n- **Health**: https://nowplaying-test.lhowsam.com/api/health\n- **Version API**: https://nowplaying-test.lhowsam.com/api/version`; + + // Find existing comment + const { data: comments } = await github.rest.issues.listComments({ + owner: context.repo.owner, + repo: context.repo.repo, + issue_number: context.issue.number, + }); + + const existingComment = comments.find(comment => comment.body.includes(marker)); + + if (existingComment) { + // Update existing comment + await github.rest.issues.updateComment({ + owner: context.repo.owner, + repo: context.repo.repo, + comment_id: existingComment.id, + body: body + }); + } else { + // Create new comment + await github.rest.issues.createComment({ + owner: context.repo.owner, + repo: context.repo.repo, + issue_number: context.issue.number, + body: body + }); + } diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index 0345454c..b18fa1eb 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -32,7 +32,7 @@ env: jobs: deploy: name: Deploy ${{ github.event.inputs.env }} - runs-on: ubuntu-latest + runs-on: macos-latest timeout-minutes: 10 steps: - name: Checkout repository @@ -47,11 +47,11 @@ jobs: - name: Install uses: ./.github/actions/install - - name: Validate - uses: ./.github/actions/validate + # - name: Validate + # uses: ./.github/actions/validate - - name: Changelogs - uses: ./.github/actions/changelog + # - name: Changelogs + # uses: ./.github/actions/changelog - name: Deploy uses: ./.github/actions/deploy diff --git a/.github/workflows/destroy.yml b/.github/workflows/destroy.yml index 0007ce5e..688eb50e 100644 --- a/.github/workflows/destroy.yml +++ b/.github/workflows/destroy.yml @@ -31,7 +31,7 @@ env: jobs: destroy: name: Destroy ${{ github.event.inputs.env }} - runs-on: ubuntu-latest + runs-on: macos-latest timeout-minutes: 10 steps: - name: Checkout repository diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 00000000..a2c6e8a9 --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,109 @@ +name: Release + +on: + push: + branches: + - main + +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + +env: + TF_VAR_env: live + TF_VAR_env_vars: ${{ secrets.LIVE_ENV_VARS }} + TF_VAR_zone_id: ${{ secrets.LIVE_ZONE_ID }} + TF_VAR_root_domain: lhowsam.com + TF_VAR_sub_domain: nowplaying.lhowsam.com + TF_VAR_private_key: ${{ secrets.LIVE_PRIVATE_KEY }} + TF_VAR_certificate_body: ${{ secrets.LIVE_CERTIFICATE_BODY }} + TF_VAR_certificate_chain: ${{ secrets.LIVE_CERTIFICATE_CHAIN }} + TF_VAR_deployed_by: ${{ github.actor }} + TF_VAR_git_sha: ${{ github.sha }} + TF_VAR_api_key: ${{ secrets.LIVE_API_KEY }} + TF_VAR_discord_webhook_url: ${{ secrets.DISCORD_ALERTS_WEBHOOK_URL }} + +permissions: + contents: write + pull-requests: write + +jobs: + release: + name: Release + runs-on: ubuntu-latest + timeout-minutes: 15 + steps: + - name: Checkout repository + uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - name: Setup Bun + uses: oven-sh/setup-bun@v2 + with: + bun-version-file: ".bun-version" + + - name: Install dependencies + run: bun install --frozen-lockfile + + - name: Create Release PR or Deploy + id: changesets + uses: changesets/action@v1 + with: + title: "chore(release): version packages" + commit: "chore(release): version packages" + version: bun run changeset:version + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + + # Deploy to production when changesets are released + - name: Deploy to Production + if: steps.changesets.outputs.hasChangesets == 'false' + uses: ./.github/actions/deploy + with: + environment: live + aws-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }} + aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }} + + - name: Create GitHub Release + if: steps.changesets.outputs.hasChangesets == 'false' + uses: actions/github-script@v7 + with: + script: | + const fs = require('fs'); + const pkg = JSON.parse(fs.readFileSync('package.json', 'utf8')); + const version = pkg.version; + const tag = `v${version}`; + + // Check if tag already exists + try { + await github.rest.git.getRef({ + owner: context.repo.owner, + repo: context.repo.repo, + ref: `tags/${tag}` + }); + console.log(`Tag ${tag} already exists, skipping release`); + return; + } catch (e) { + // Tag doesn't exist, create release + } + + // Create tag + await github.rest.git.createRef({ + owner: context.repo.owner, + repo: context.repo.repo, + ref: `refs/tags/${tag}`, + sha: context.sha + }); + + // Create release + await github.rest.repos.createRelease({ + owner: context.repo.owner, + repo: context.repo.repo, + tag_name: tag, + name: `Release ${tag}`, + body: `## What's Changed\n\nSee [CHANGELOG.md](./CHANGELOG.md) for details.`, + draft: false, + prerelease: false + }); + diff --git a/.github/workflows/version-bump.yml b/.github/workflows/version-bump.yml index 0acb914c..9a42e89b 100644 --- a/.github/workflows/version-bump.yml +++ b/.github/workflows/version-bump.yml @@ -10,7 +10,7 @@ on: jobs: version-bump: if: github.event_name == 'push' || (github.event.pull_request.merged == true && github.event.pull_request.base.ref == 'main') - runs-on: ubuntu-latest + runs-on: macos-latest permissions: contents: write pull-requests: write diff --git a/.gitignore b/.gitignore index 0c5ede9c..dc534b16 100644 --- a/.gitignore +++ b/.gitignore @@ -9,14 +9,7 @@ *.tfstate *.tfstate.* -# Crash log files -crash.log -crash.*.log - -# Exclude all .tfvars files, which are likely to contain sensitive data, such as -# password, private keys, and other secrets. These should not be part of version -# control as they are data points which are potentially sensitive and subject -# to change depending on the environment. + *.tfvars *.tfvars.json @@ -26,163 +19,10 @@ override.tf override.tf.json *_override.tf *_override.tf.json -+ .turbo - -# Include override files you do wish to add to version control using negated pattern -# !example_override.tf - -# Include tfplan files to ignore the plan output of command: terraform plan -out=tfplan -# example: *tfplan* - -# Ignore CLI configuration files .terraformrc terraform.rc -# End of https://www.toptal.com/developers/gitignore/api/terraform -# Created by https://www.toptal.com/developers/gitignore/api/node -# Edit at https://www.toptal.com/developers/gitignore?templates=node - -### Node ### -# Logs -logs -*.log -npm-debug.log* -yarn-debug.log* -yarn-error.log* -lerna-debug.log* -.pnpm-debug.log* - -# Diagnostic reports (https://nodejs.org/api/report.html) -report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json - -# Runtime data -pids -*.pid -*.seed -*.pid.lock - -# Directory for instrumented libs generated by jscoverage/JSCover -lib-cov - -# Coverage directory used by tools like istanbul -coverage -*.lcov - -# nyc test coverage -.nyc_output - -# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) -.grunt - -# Bower dependency directory (https://bower.io/) -bower_components - -# node-waf configuration -.lock-wscript - -# Compiled binary addons (https://nodejs.org/api/addons.html) -build/Release - -# Dependency directories -node_modules/ -jspm_packages/ - -# Snowpack dependency directory (https://snowpack.dev/) -web_modules/ - -# TypeScript cache -*.tsbuildinfo - -# Optional npm cache directory -.npm - -# Optional eslint cache -.eslintcache - -# Optional stylelint cache -.stylelintcache - -# Microbundle cache -.rpt2_cache/ -.rts2_cache_cjs/ -.rts2_cache_es/ -.rts2_cache_umd/ - -# Optional REPL history -.node_repl_history -# Output of 'npm pack' -*.tgz - -# Yarn Integrity file -.yarn-integrity - -# dotenv environment variable files -.env -.env.development.local -.env.test.local -.env.production.local -.env.local - -# parcel-bundler cache (https://parceljs.org/) -.cache -.parcel-cache - -# Next.js build output -.next -out - -# Nuxt.js build / generate output -.nuxt -dist - -# Gatsby files -.cache/ -# Comment in the public line in if your project uses Gatsby and not Next.js -# https://nextjs.org/blog/next-9-1#public-directory-support -# public - -# vuepress build output -.vuepress/dist - -# vuepress v2.x temp and cache directory -.temp - -# Docusaurus cache and generated files -.docusaurus - -# Serverless directories -.serverless/ - -# FuseBox cache -.fusebox/ - -# DynamoDB Local files -.dynamodb/ - -# TernJS port file -.tern-port - -# Stores VSCode versions used for testing VSCode extensions -.vscode-test - -# yarn v2 -.yarn/cache -.yarn/unplugged -.yarn/build-state.yml -.yarn/install-state.gz -.pnp.* - -### Node Patch ### -# Serverless Webpack directories -.webpack/ - -# Optional stylelint cache - -# SvelteKit build / generate output -.svelte-kit - -# End of https://www.toptal.com/developers/gitignore/api/node certs .terraform.lock.hcl .terraform @@ -194,19 +34,105 @@ lambda.zip .idea -# Created by https://www.toptal.com/developers/gitignore/api/dotnetcore -# Edit at https://www.toptal.com/developers/gitignore?templates=dotnetcore - -### DotnetCore ### -# .NET Core build folders -bin/ -obj/ -obj - +# Created by https://www.toptal.com/developers/gitignore/api/swift +# Edit at https://www.toptal.com/developers/gitignore?templates=swift + +### Swift ### +# Xcode +# +# gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore + +## User settings +xcuserdata/ + +## compatibility with Xcode 8 and earlier (ignoring not required starting Xcode 9) +*.xcscmblueprint +*.xccheckout + +## compatibility with Xcode 3 and earlier (ignoring not required starting Xcode 4) +build/ +DerivedData/ +*.moved-aside +*.pbxuser +!default.pbxuser +*.mode1v3 +!default.mode1v3 +*.mode2v3 +!default.mode2v3 +*.perspectivev3 +!default.perspectivev3 + +## Obj-C/Swift specific +*.hmap + +## App packaging +*.ipa +*.dSYM.zip +*.dSYM + +## Playgrounds +timeline.xctimeline +playground.xcworkspace + +# Swift Package Manager +# Add this line if you want to avoid checking in source code from Swift Package Manager dependencies. +# Packages/ +# Package.pins +# Package.resolved +# *.xcodeproj +# Xcode automatically generates this directory with a .xcworkspacedata file and xcuserdata +# hence it is not needed unless you have added a package configuration file to your project +# .swiftpm + +.build/ + +# CocoaPods +# We recommend against adding the Pods directory to your .gitignore. However +# you should judge for yourself, the pros and cons are mentioned at: +# https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control +# Pods/ +# Add this line if you want to avoid checking in source code from the Xcode workspace +# *.xcworkspace + +# Carthage +# Add this line if you want to avoid checking in source code from Carthage dependencies. +# Carthage/Checkouts + +Carthage/Build/ + +# Accio dependency management +Dependencies/ +.accio/ + +# fastlane +# It is recommended to not store the screenshots in the git repo. +# Instead, use fastlane to re-generate the screenshots whenever they are needed. +# For more information about the recommended setup visit: +# https://docs.fastlane.tools/best-practices/source-control/#source-control + +fastlane/report.xml +fastlane/Preview.html +fastlane/screenshots/**/*.png +fastlane/test_output + +# Code Injection +# After new code Injection tools there's a generated folder /iOSInjectionProject +# https://github.com/johnno1962/injectionforxcode + +iOSInjectionProject/ + +# End of https://www.toptal.com/developers/gitignore/api/swift +.DS_Store +/.build +/Packages +xcuserdata/ +DerivedData/ +.swiftpm/configuration/registries.json +.swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata +.netrc +.vscode -# Common node modules locations -/node_modules -/wwwroot/node_modules +*.swiftpm -# End of https://www.toptal.com/developers/gitignore/api/dotnetcore +*.resolved \ No newline at end of file diff --git a/.nvmrc b/.nvmrc index fdb2eaaf..8fdd954d 100644 --- a/.nvmrc +++ b/.nvmrc @@ -1 +1 @@ -22.11.0 \ No newline at end of file +22 \ No newline at end of file diff --git a/.swiftlint.yml b/.swiftlint.yml new file mode 100644 index 00000000..055e8fa2 --- /dev/null +++ b/.swiftlint.yml @@ -0,0 +1,43 @@ +# SwiftLint Configuration +# https://github.com/realm/SwiftLint + +# Paths to include/exclude +included: + - Sources + +excluded: + - .build + - .swiftpm + +opt_in_rules: + - empty_count + - empty_string + - first_where + - sorted_first_last + - vertical_whitespace_opening_braces + +line_length: + warning: 120 + error: 200 + ignores_function_declarations: true + ignores_comments: true + ignores_urls: true + +function_body_length: + warning: 100 + error: 200 + +function_parameter_count: + warning: 8 + error: 10 + +type_body_length: + warning: 400 + error: 600 + +file_length: + warning: 500 + error: 1000 + +# Reporter +reporter: "xcode" diff --git a/Package.swift b/Package.swift new file mode 100644 index 00000000..942b54ff --- /dev/null +++ b/Package.swift @@ -0,0 +1,37 @@ +// swift-tools-version: 6.1 +// The swift-tools-version declares the minimum version of Swift required to build this package. + +import PackageDescription + +let package = Package( + name: "lambda", + platforms: [.macOS(.v15)], + products: [ + .executable(name: "lambda", targets: ["lambda"]), + .executable(name: "authorizer", targets: ["authorizer"]), + ], + dependencies: [ + // for local dev + .package( + url: "https://github.com/swift-server/swift-aws-lambda-runtime.git", from: "2.4.0"), + .package(url: "https://github.com/awslabs/swift-aws-lambda-events.git", from: "1.0.0"), + .package(url: "https://github.com/apple/swift-log.git", "1.0.0"..<"1.8.0"), + ], + targets: [ + .executableTarget( + name: "authorizer", + dependencies: [ + .product(name: "AWSLambdaRuntime", package: "swift-aws-lambda-runtime"), + .product(name: "AWSLambdaEvents", package: "swift-aws-lambda-events"), + + ] + ), + .executableTarget( + name: "lambda", + dependencies: [ + .product(name: "AWSLambdaRuntime", package: "swift-aws-lambda-runtime"), + .product(name: "AWSLambdaEvents", package: "swift-aws-lambda-events"), + ], + ), + ] +) diff --git a/Sources/authorizer/main.swift b/Sources/authorizer/main.swift new file mode 100644 index 00000000..1183bf7f --- /dev/null +++ b/Sources/authorizer/main.swift @@ -0,0 +1,78 @@ +import AWSLambdaRuntime +import Foundation + +// MARK: - Custom Request/Response types for HTTP API v2 Authorizer + +struct AuthorizerRequest: Codable { + let version: String? + let type: String? + let routeArn: String? + let routeKey: String? + let rawPath: String? + let rawQueryString: String? + let headers: [String: String]? + let requestContext: RequestContext? + + struct RequestContext: Codable { + let http: HTTPContext? + + struct HTTPContext: Codable { + let method: String? + let path: String? + } + } +} + +struct AuthorizerSimpleResponse: Codable { + let isAuthorized: Bool +} + +// MARK: - Helper Functions + +/// Gets a header value from the request headers (case-insensitive) +func getHeaderValue(_ headers: [String: String]?, key: String) -> String? { + guard let headers = headers else { + return nil + } + + for (headerKey, value) in headers { + if headerKey.caseInsensitiveCompare(key) == .orderedSame { + return value + } + } + + return nil +} + +// MARK: - Lambda Runtime + +let runtime = LambdaRuntime { + (event: AuthorizerRequest, context: LambdaContext) -> AuthorizerSimpleResponse in + + context.logger.info("Authorizer invoked") + context.logger.info("Headers: \(String(describing: event.headers))") + context.logger.info("RouteArn: \(String(describing: event.routeArn))") + + let consumer = getHeaderValue(event.headers, key: "x-consumer") + let validConsumers = ["lhowsam-dev", "lhowsam-prod", "lhowsam-local"] + + let apiKey = getHeaderValue(event.headers, key: "x-api-key") + let validKey = ProcessInfo.processInfo.environment["API_KEY"] + + context.logger.info("API Key from header: '\(apiKey ?? "nil")'") + + if apiKey != validKey { + context.logger.info("Deny - API key mismatch") + return AuthorizerSimpleResponse(isAuthorized: false) + } + + if let consumer = consumer, !validConsumers.contains(consumer) { + context.logger.info("Deny - Invalid consumer: \(consumer)") + return AuthorizerSimpleResponse(isAuthorized: false) + } + + context.logger.info("Allow - Authorization successful") + return AuthorizerSimpleResponse(isAuthorized: true) +} + +try await runtime.run() diff --git a/Sources/lambda/Models/Spotify.swift b/Sources/lambda/Models/Spotify.swift new file mode 100644 index 00000000..311541b0 --- /dev/null +++ b/Sources/lambda/Models/Spotify.swift @@ -0,0 +1,43 @@ +import Foundation + +struct NowPlayingResponse: Codable { + let isPlaying: Bool + let maintenance: Bool? + let status: Int + let album: String + let albumImageUrl: String + let artist: String + let songUrl: String + let title: String +} + +struct SpotifyResponse: Codable { + let isPlaying: Bool + let item: SpotifyItem? +} + +struct SpotifyItem: Codable { + let name: String + let artists: [SpotifyArtist] + let album: SpotifyAlbum + let externalUrls: SpotifyExternalUrls +} + +struct SpotifyArtist: Codable { + let name: String +} + +struct SpotifyImage: Codable { + let url: String + let height: Int? + let width: Int? +} + +struct SpotifyAlbum: Codable { + let name: String + let images: [SpotifyImage] +} + +struct SpotifyExternalUrls: Codable { + let spotify: String +} diff --git a/Sources/lambda/Services/NowPlayingService.swift b/Sources/lambda/Services/NowPlayingService.swift new file mode 100644 index 00000000..400e6ab1 --- /dev/null +++ b/Sources/lambda/Services/NowPlayingService.swift @@ -0,0 +1,94 @@ +import AWSLambdaEvents +import AWSLambdaRuntime +import Foundation +import Logging + +actor NowPlayingService { + private static let nowPlayingCacheKey = "NowPlaying" + private static let revalidateSeconds = 3 + + private let cache: MemoryCache + private let spotifyApi: SpotifyApi + private let logger: Logger + + init(cache: MemoryCache, spotifyApi: SpotifyApi, logger: Logger) { + self.cache = cache + self.spotifyApi = spotifyApi + self.logger = logger + } + + func handleNowPlaying() async throws -> NowPlayingResponse { + do { + if let cachedResponse: NowPlayingResponse = await cache.get(Self.nowPlayingCacheKey) { + logger.info("Returning cached now playing response") + return cachedResponse + } + + let shouldCallSpotify = + ProcessInfo.processInfo.environment["SHOULD_CALL_SPOTIFY"] ?? "true" + + if shouldCallSpotify.lowercased() == "false" { + logger.info("Now playing responses disabled") + + return NowPlayingResponse( + isPlaying: false, maintenance: true, status: 200, album: "", + albumImageUrl: "", artist: "", songUrl: "", title: "" + ) + } + + let nowPlayingResponse = try await spotifyApi.getNowPlaying() + + if nowPlayingResponse?.item == nil { + logger.info("No song currently playing") + + return NowPlayingResponse( + isPlaying: false, + maintenance: nil, + status: 200, + album: "", + albumImageUrl: "", + artist: "", + songUrl: "", + title: "" + ) + } + + guard let item = nowPlayingResponse?.item else { + throw NowPlayingError.missingItem + } + + let response = NowPlayingResponse( + isPlaying: nowPlayingResponse?.isPlaying ?? false, + maintenance: nil, + status: 200, + album: item.album.name, + albumImageUrl: item.album.images.first?.url ?? "", + artist: item.artists.map { $0.name }.joined(separator: ", "), + songUrl: item.externalUrls.spotify, + title: item.name + ) + + await cache.set(Self.nowPlayingCacheKey, value: response, expiration: 5.0) + + return response + } catch { + logger.error( + "Error fetching nowplaying data from spotify: \(error.localizedDescription)") + + return NowPlayingResponse( + isPlaying: false, + maintenance: nil, + status: 500, + album: "", + albumImageUrl: "", + artist: "", + songUrl: "", + title: "" + ) + } + } +} + +enum NowPlayingError: Error { + case missingItem +} diff --git a/Sources/lambda/Services/VersionService.swift b/Sources/lambda/Services/VersionService.swift new file mode 100644 index 00000000..a54a5af8 --- /dev/null +++ b/Sources/lambda/Services/VersionService.swift @@ -0,0 +1,26 @@ +import AWSLambdaEvents +import AWSLambdaRuntime +import Foundation +import Logging + +struct VersionResponse: Codable { + let version: String + let deployedAt: String + let deployedBy: String + let gitSha: String +} + +actor VersionService { + func handleVersion() async throws -> APIGatewayV2Response { + let version = ProcessInfo.processInfo.environment["VERSION"] ?? "unknown" + let deployedAt = ProcessInfo.processInfo.environment["DEPLOYED_AT"] ?? "unknown" + let deployedBy = ProcessInfo.processInfo.environment["DEPLOYED_BY"] ?? "unknown" + let gitSha = ProcessInfo.processInfo.environment["GIT_SHA"] ?? "unknown" + + let response = VersionResponse( + version: version, deployedAt: deployedAt, deployedBy: deployedBy, gitSha: gitSha + ) + + return ResponseBuilder.createResponse(body: response, includeCacheControl: false) + } +} diff --git a/Sources/lambda/Utils/Cache.swift b/Sources/lambda/Utils/Cache.swift new file mode 100644 index 00000000..0f79d292 --- /dev/null +++ b/Sources/lambda/Utils/Cache.swift @@ -0,0 +1,38 @@ +import Foundation + +// MARK: - Memory Cache Actor + +actor MemoryCache { + private var cache: [String: CacheEntry] = [:] + + struct CacheEntry { + let value: Any + let expirationDate: Date + } + + func get(_ key: String) -> T? { + guard let entry = cache[key] else { + return nil + } + + if entry.expirationDate < Date() { + cache.removeValue(forKey: key) + return nil + } + + return entry.value as? T + } + + func set(_ key: String, value: T, expiration: TimeInterval) { + let expirationDate = Date().addingTimeInterval(expiration) + cache[key] = CacheEntry(value: value, expirationDate: expirationDate) + } + + func remove(_ key: String) { + cache.removeValue(forKey: key) + } + + func clear() { + cache.removeAll() + } +} diff --git a/Sources/lambda/Utils/ResponseBuilder.swift b/Sources/lambda/Utils/ResponseBuilder.swift new file mode 100644 index 00000000..06382756 --- /dev/null +++ b/Sources/lambda/Utils/ResponseBuilder.swift @@ -0,0 +1,36 @@ +import AWSLambdaEvents +import Foundation + +// MARK: - Response Builder + +enum ResponseBuilder { + static func createResponse( + body: T, + includeCacheControl: Bool, + revalidateSeconds: Int = 3 + ) -> APIGatewayV2Response { + let encoder = JSONEncoder() + + var headers: [String: String] = [ + "content-type": "application/json", + "Access-Control-Allow-Origin": "*", + "Access-Control-Allow-Methods": "GET,OPTIONS,POST,PUT,DELETE", + ] + + if includeCacheControl { + headers["Cache-Control"] = + "max-age=\(revalidateSeconds), s-maxage=\(revalidateSeconds), stale-while-revalidate=\(revalidateSeconds), stale-if-error=\(revalidateSeconds)" + } else { + headers["Cache-Control"] = "no-cache" + } + + let bodyData = try? encoder.encode(body) + let bodyString = bodyData.flatMap { String(data: $0, encoding: .utf8) } ?? "{}" + + return APIGatewayV2Response( + statusCode: .ok, + headers: headers, + body: bodyString + ) + } +} diff --git a/Sources/lambda/Utils/Router.swift b/Sources/lambda/Utils/Router.swift new file mode 100644 index 00000000..b9bcdcc4 --- /dev/null +++ b/Sources/lambda/Utils/Router.swift @@ -0,0 +1,159 @@ +import AWSLambdaEvents +import AWSLambdaRuntime +import Foundation + +// MARK: - Router + +final class Router: @unchecked Sendable { + typealias Handler = (APIGatewayV2Request, LambdaContext) throws -> APIGatewayV2Response + typealias AsyncHandler = (APIGatewayV2Request, LambdaContext) async throws -> + APIGatewayV2Response + + private var routes: [Route] = [] + private var asyncRoutes: [AsyncRoute] = [] + + func add(method: HTTPMethod, path: String, handler: @escaping Handler) { + routes.append(Route(method: method, path: path, handler: handler)) + } + + func add(method: HTTPMethod, path: String, handler: @escaping AsyncHandler) { + asyncRoutes.append(AsyncRoute(method: method, path: path, handler: handler)) + } + + func handle(event: APIGatewayV2Request, context: LambdaContext) async throws + -> APIGatewayV2Response + { + let methodString = String(describing: event.context.http.method) + let requestMethod = HTTPMethod(rawValue: methodString) ?? .GET + var requestPath = event.rawPath + + // Strip stage prefix if present (e.g., /test/api/health -> /api/health) + let knownStages = ["/test", "/staging", "/live", "/prod"] + for stage in knownStages { + if requestPath.hasPrefix(stage + "/") { + requestPath = String(requestPath.dropFirst(stage.count)) + break + } else if requestPath == stage { + requestPath = "/" + break + } + } + + // Strip /invoke prefix if present (for local testing) + if requestPath.hasPrefix("/invoke") { + requestPath = String(requestPath.dropFirst(7)) + if requestPath.isEmpty { + requestPath = "/" + } else if !requestPath.hasPrefix("/") { + requestPath = "/" + requestPath + } + } + + context.logger.info( + "Received request - Method: \(methodString), rawPath: '\(event.rawPath)', processed path: '\(requestPath)', available routes: \(routes.map { "\($0.method.rawValue) \($0.path)" }) + \(asyncRoutes.map { "\($0.method.rawValue) \($0.path)" })" + ) + + for asyncRoute in asyncRoutes { + if asyncRoute.method == requestMethod && asyncRoute.matches(path: requestPath) { + return try await asyncRoute.handler(event, context) + } + } + + for route in routes { + if route.method == requestMethod && route.matches(path: requestPath) { + return try route.handler(event, context) + } + } + + // No route found + return APIGatewayV2Response( + statusCode: .notFound, + headers: ["content-type": "application/json"], + body: #"{"error": "Not Found"}"# + ) + } +} + +struct Route { + let method: HTTPMethod + let path: String + let handler: Router.Handler + + func matches(path: String) -> Bool { + return self.path == path + } +} + +struct AsyncRoute { + let method: HTTPMethod + let path: String + let handler: Router.AsyncHandler + + func matches(path: String) -> Bool { + return self.path == path + } +} + +enum HTTPMethod: String { + case GET + case POST + case PUT + case DELETE + case PATCH + case OPTIONS +} + +// MARK: - Route Handlers + +func createRouter() -> Router { + let router = Router() + let cache = MemoryCache() + let spotifyApi = SpotifyApi() + + // GET /api/health + router.add(method: .GET, path: "/api/health") { event, context in + context.logger.info("Health check endpoint called") + return APIGatewayV2Response( + statusCode: .ok, + headers: ["content-type": "application/json"], + body: #"{"status": "OK"}"# + ) + } + + // GET /api/version + router.add(method: .GET, path: "/api/version") { event, context async throws in + context.logger.info("Version endpoint called") + let versionService = VersionService() + return try await versionService.handleVersion() + } + + // GET /api/now-playing + router.add(method: .GET, path: "/api/now-playing") { + event, context async throws -> APIGatewayV2Response in + context.logger.info("GET /api/now-playing called") + let nowplayingService = NowPlayingService( + cache: cache, + spotifyApi: spotifyApi, + logger: context.logger + ) + let response = try await nowplayingService.handleNowPlaying() + + let encoder = JSONEncoder() + let bodyData = try encoder.encode(response) + let bodyString = String(data: bodyData, encoding: .utf8) ?? "{}" + + return APIGatewayV2Response( + statusCode: .ok, + headers: [ + "content-type": "application/json", + "Access-Control-Allow-Origin": "*", + "Access-Control-Allow-Methods": "GET,OPTIONS,POST,PUT,DELETE", + "Cache-Control": + "max-age=3, s-maxage=3, stale-while-revalidate=3, stale-if-error=3", + ], + body: bodyString + ) + } + + return router +} diff --git a/Sources/lambda/http/SpotifyApi.swift b/Sources/lambda/http/SpotifyApi.swift new file mode 100644 index 00000000..289ea7e5 --- /dev/null +++ b/Sources/lambda/http/SpotifyApi.swift @@ -0,0 +1,57 @@ +import Foundation + +#if canImport(FoundationNetworking) + import FoundationNetworking +#endif + +enum SpotifyServiceError: Error { + case missingAccessToken + case invalidURL + case invalidResponse + case httpError(statusCode: Int) +} + +actor SpotifyApi { + private let baseURL: String + private let accessToken: String? + + init(baseURL: String? = nil, accessToken: String? = nil) { + self.baseURL = baseURL ?? "https://api.spotify.com/v1" + self.accessToken = + accessToken ?? ProcessInfo.processInfo.environment["SPOTIFY_ACCESS_TOKEN"] + } + + func getNowPlaying() async throws -> SpotifyResponse? { + guard let accessToken = accessToken else { + throw SpotifyServiceError.missingAccessToken + } + + guard let url = URL(string: "\(baseURL)/me/player/currently-playing") else { + throw SpotifyServiceError.missingAccessToken + } + + var request = URLRequest(url: url) + request.httpMethod = "GET" + request.setValue("Bearer \(accessToken)", forHTTPHeaderField: "Authorization") + request.setValue("application/json", forHTTPHeaderField: "Content-Type") + + let (data, response) = try await URLSession.shared.data(for: request) + + guard let httpResponse = response as? HTTPURLResponse else { + throw SpotifyServiceError.invalidResponse + } + + if httpResponse.statusCode == 204 { + return nil + } + + guard (200...299).contains(httpResponse.statusCode) else { + throw SpotifyServiceError.httpError(statusCode: httpResponse.statusCode) + } + + let decoder = JSONDecoder() + decoder.keyDecodingStrategy = .convertFromSnakeCase + return try decoder.decode(SpotifyResponse.self, from: data) + + } +} diff --git a/Sources/lambda/main.swift b/Sources/lambda/main.swift new file mode 100644 index 00000000..353c6d5b --- /dev/null +++ b/Sources/lambda/main.swift @@ -0,0 +1,15 @@ +import AWSLambdaEvents +import AWSLambdaRuntime +import Foundation + +// MARK: - Lambda Runtime + +let router = createRouter() + +let runtime = LambdaRuntime { + (event: APIGatewayV2Request, context: LambdaContext) -> APIGatewayV2Response in + + return try await router.handle(event: event, context: context) +} + +try await runtime.run() diff --git a/apps/lho-authorizer/CHANGELOG.md b/apps/lho-authorizer/CHANGELOG.md deleted file mode 100644 index 8a6bece4..00000000 --- a/apps/lho-authorizer/CHANGELOG.md +++ /dev/null @@ -1,34 +0,0 @@ -# [](https://github.com/luke-h1/lho-lambda/compare/v1.4.2...v) (2025-09-16) - - -### Features - -* **lambda:** rewrite lambdas ([2ec5bdb](https://github.com/luke-h1/lho-lambda/commit/2ec5bdbc2c801381348cfa18970e8f0f28331a22)) - - - -## [1.4.2](https://github.com/luke-h1/lho-lambda/compare/v1.4.1...v1.4.2) (2025-07-03) - - - -## [1.4.1](https://github.com/luke-h1/lho-lambda/compare/v1.4.0...v1.4.1) (2025-07-03) - - - -# [1.4.0](https://github.com/luke-h1/lho-lambda/compare/v1.3.0...v1.4.0) (2025-06-20) - - - -# [1.3.0](https://github.com/luke-h1/lho-lambda/compare/v1.2.0...v1.3.0) (2025-06-20) - - - -# [1.2.0](https://github.com/luke-h1/lho-lambda/compare/v1.1.2...v1.2.0) (2025-05-10) - - -### Features - -* **infrastructure:** convert to bun ([4825ab6](https://github.com/luke-h1/lho-lambda/commit/4825ab64231ca2b56987db87c11ba6016dc14cfc)) - - - diff --git a/apps/lho-authorizer/lho-authorizer.sln b/apps/lho-authorizer/lho-authorizer.sln deleted file mode 100644 index 343951cd..00000000 --- a/apps/lho-authorizer/lho-authorizer.sln +++ /dev/null @@ -1,16 +0,0 @@ - -Microsoft Visual Studio Solution File, Format Version 12.00 -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "lhoAuthorizer", "src\lhoAuthorizer.csproj", "{4636982B-EB03-46F5-A991-0E9F5561AA95}" -EndProject -Global - GlobalSection(SolutionConfigurationPlatforms) = preSolution - Debug|Any CPU = Debug|Any CPU - Release|Any CPU = Release|Any CPU - EndGlobalSection - GlobalSection(ProjectConfigurationPlatforms) = postSolution - {4636982B-EB03-46F5-A991-0E9F5561AA95}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {4636982B-EB03-46F5-A991-0E9F5561AA95}.Debug|Any CPU.Build.0 = Debug|Any CPU - {4636982B-EB03-46F5-A991-0E9F5561AA95}.Release|Any CPU.ActiveCfg = Release|Any CPU - {4636982B-EB03-46F5-A991-0E9F5561AA95}.Release|Any CPU.Build.0 = Release|Any CPU - EndGlobalSection -EndGlobal diff --git a/apps/lho-authorizer/src/Function.cs b/apps/lho-authorizer/src/Function.cs deleted file mode 100644 index 908347cf..00000000 --- a/apps/lho-authorizer/src/Function.cs +++ /dev/null @@ -1,85 +0,0 @@ -using Amazon.Lambda.APIGatewayEvents; -using Amazon.Lambda.Core; - -[assembly: LambdaSerializer(typeof(Amazon.Lambda.Serialization.SystemTextJson.DefaultLambdaJsonSerializer))] - -namespace lhoAuthorizer; - -public class Function -{ - public Task FunctionHandler(APIGatewayCustomAuthorizerRequest request, - ILambdaContext context) { - try - { - var consumer = GetHeaderValue(request.Headers, "x-consumer"); - string[] validConsumers = - [ - "lhowsam-dev", - "lhowsam-prod", - "lhowsam-local" - ]; - - - var referer = GetHeaderValue(request.Headers, "Referer") ?? - GetHeaderValue(request.Headers, "referer") ?? "unknown"; - - var apiKey = GetHeaderValue(request.Headers, "x-api-key"); - var validKey = Environment.GetEnvironmentVariable("API_KEY"); - - if (apiKey != validKey) - { - context.Logger.LogInformation("Deny"); - return GeneratePolicy("user", "Deny", request.MethodArn); - } - - // ReSharper disable once InvertIf - if (!validConsumers.Contains(consumer ?? "") && apiKey == validKey) - { - context.Logger.LogInformation("Deny - invalid consumer"); - return GeneratePolicy("user", "Deny", request.MethodArn); - } - - return GeneratePolicy("user", "Allow", request.MethodArn); - - } - catch (Exception e) - { - context.Logger.LogError($"Error in authorizer: {e.Message}"); - return GeneratePolicy("user", "Deny", request.MethodArn); - } - } - - private static string? GetHeaderValue(IDictionary? headers, string key) - { - // ReSharper disable once UseNullPropagation - if (headers == null) - { - return null; - } - - var kvpHeaders = headers.FirstOrDefault(h => string.Equals(h.Key, key, StringComparison.OrdinalIgnoreCase)); - - return kvpHeaders.Key != null ? kvpHeaders.Value : null; - } - - private static Task GeneratePolicy(string principalId, string effect, string resource) - { - return Task.FromResult(new APIGatewayCustomAuthorizerResponse - { - PrincipalID = principalId, - PolicyDocument = new APIGatewayCustomAuthorizerPolicy - { - Version = "2012-10-17", - Statement = - [ - new APIGatewayCustomAuthorizerPolicy.IAMPolicyStatement - { - Action = ["execute-api:Invoke"], - Effect = effect, - Resource = [resource] - } - ] - } - }); - } -} diff --git a/apps/lho-authorizer/src/appsettings.Development.json b/apps/lho-authorizer/src/appsettings.Development.json deleted file mode 100644 index 905d15a0..00000000 --- a/apps/lho-authorizer/src/appsettings.Development.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "AWS": { - "Region": "" - } -} \ No newline at end of file diff --git a/apps/lho-authorizer/src/appsettings.json b/apps/lho-authorizer/src/appsettings.json deleted file mode 100644 index 8c4d28e0..00000000 --- a/apps/lho-authorizer/src/appsettings.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "Logging": { - "LogLevel": { - "Default": "Information" - } - } -} \ No newline at end of file diff --git a/apps/lho-authorizer/src/lhoAuthorizer.csproj b/apps/lho-authorizer/src/lhoAuthorizer.csproj deleted file mode 100644 index c0b2598c..00000000 --- a/apps/lho-authorizer/src/lhoAuthorizer.csproj +++ /dev/null @@ -1,18 +0,0 @@ - - - 1.4.2-pre.112a466 - Library - net8.0 - enable - enable - true - Lambda - true - true - - - - - - - diff --git a/apps/lho-lambda/CHANGELOG.md b/apps/lho-lambda/CHANGELOG.md deleted file mode 100644 index 5ee2e8aa..00000000 --- a/apps/lho-lambda/CHANGELOG.md +++ /dev/null @@ -1,44 +0,0 @@ -# [](https://github.com/luke-h1/lho-lambda/compare/v1.4.2...v) (2025-09-16) - - -### Features - -* **lambda:** rewrite lambdas ([2ec5bdb](https://github.com/luke-h1/lho-lambda/commit/2ec5bdbc2c801381348cfa18970e8f0f28331a22)) - - - -## [1.4.2](https://github.com/luke-h1/lho-lambda/compare/v1.4.1...v1.4.2) (2025-07-03) - - - -## [1.4.1](https://github.com/luke-h1/lho-lambda/compare/v1.4.0...v1.4.1) (2025-07-03) - - - -# [1.4.0](https://github.com/luke-h1/lho-lambda/compare/v1.3.0...v1.4.0) (2025-06-20) - - -### Features - -* **api:** add streak handler ([8c1fbc1](https://github.com/luke-h1/lho-lambda/commit/8c1fbc11208ef6d68ad8b1a0600bea9650be8907)) - - - -# [1.3.0](https://github.com/luke-h1/lho-lambda/compare/v1.2.0...v1.3.0) (2025-06-20) - - -### Features - -* **api:** add streak handler ([8749386](https://github.com/luke-h1/lho-lambda/commit/8749386ac0025ec127fbc0f66e11ed2d963f38e7)) - - - -# [1.2.0](https://github.com/luke-h1/lho-lambda/compare/v1.1.2...v1.2.0) (2025-05-10) - - -### Features - -* **infrastructure:** convert to bun ([4825ab6](https://github.com/luke-h1/lho-lambda/commit/4825ab64231ca2b56987db87c11ba6016dc14cfc)) - - - diff --git a/apps/lho-lambda/LhoLambda.sln b/apps/lho-lambda/LhoLambda.sln deleted file mode 100644 index d9d992ca..00000000 --- a/apps/lho-lambda/LhoLambda.sln +++ /dev/null @@ -1,16 +0,0 @@ - -Microsoft Visual Studio Solution File, Format Version 12.00 -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "lho-lambda", "src\lho-lambda.csproj", "{2F6A7930-AD24-47BF-8883-8CDFC9874F82}" -EndProject -Global - GlobalSection(SolutionConfigurationPlatforms) = preSolution - Debug|Any CPU = Debug|Any CPU - Release|Any CPU = Release|Any CPU - EndGlobalSection - GlobalSection(ProjectConfigurationPlatforms) = postSolution - {2F6A7930-AD24-47BF-8883-8CDFC9874F82}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {2F6A7930-AD24-47BF-8883-8CDFC9874F82}.Debug|Any CPU.Build.0 = Debug|Any CPU - {2F6A7930-AD24-47BF-8883-8CDFC9874F82}.Release|Any CPU.ActiveCfg = Release|Any CPU - {2F6A7930-AD24-47BF-8883-8CDFC9874F82}.Release|Any CPU.Build.0 = Release|Any CPU - EndGlobalSection -EndGlobal diff --git a/apps/lho-lambda/src/Controllers/HealthController.cs b/apps/lho-lambda/src/Controllers/HealthController.cs deleted file mode 100644 index a3f0eba5..00000000 --- a/apps/lho-lambda/src/Controllers/HealthController.cs +++ /dev/null @@ -1,22 +0,0 @@ -using Microsoft.AspNetCore.Mvc; - -namespace LhoLambda.Controllers; - -[ApiController] -public class HealthController : ControllerBase -{ - [HttpGet("api/health")] - public IActionResult Health() - { - var response = new - { - status = "OK" - }; - - Response.Headers.Append("Access-Control-Allow-Origin", "*"); - Response.Headers.Append("Access-Control-Allow-Methods", "GET,OPTIONS,POST,PUT,DELETE"); - Response.Headers.Append("Cache-Control", "no-cache"); - - return Ok(response); - } -} diff --git a/apps/lho-lambda/src/Controllers/NowPlayingController.cs b/apps/lho-lambda/src/Controllers/NowPlayingController.cs deleted file mode 100644 index 4958c8aa..00000000 --- a/apps/lho-lambda/src/Controllers/NowPlayingController.cs +++ /dev/null @@ -1,120 +0,0 @@ -using LhoLambda.Models; -using LhoLambda.services; -using Microsoft.AspNetCore.Mvc; -using Microsoft.Extensions.Caching.Memory; - -namespace LhoLambda.Controllers; - -[ApiController] -public class NowPlayingController : ControllerBase -{ - private const string NowPlayingCacheKey = "NowPlaying"; - private const int RevalidateSeconds = 2; - private readonly IMemoryCache _cache; - private readonly IConfiguration _configuration; - private readonly ILogger _logger; - private readonly ISpotifyService _spotifyService; - - public NowPlayingController( - ISpotifyService spotifyService, - IMemoryCache cache, - IConfiguration configuration, - ILogger logger) - { - _spotifyService = spotifyService; - _cache = cache; - _configuration = configuration; - _logger = logger; - } - - [HttpGet("api/now-playing")] - public async Task> NowPlaying() - { - try - { - if (_cache.TryGetValue(NowPlayingCacheKey, out var cachedNowPlaying)) - { - AddCorsAndCacheHeaders(true); - return Ok(cachedNowPlaying); - } - - var shouldCallSpotify = _configuration.GetValue("SHOULD_CALL_SPOTIFY"); - - if (string.Equals(shouldCallSpotify, "false", StringComparison.OrdinalIgnoreCase)) - { - _logger.LogInformation("Now playing is disabled"); - var maintenanceResponse = new NowPlayingResponse - { - IsPlaying = false, - Maintenance = true, - Status = 200, - Album = string.Empty, - AlbumImageUrl = string.Empty, - Artist = string.Empty, - SongUrl = string.Empty, - Title = string.Empty - }; - - AddCorsAndCacheHeaders(true); - return Ok(maintenanceResponse); - } - - var spotifyResponse = await _spotifyService.GetNowPlayingAsync(); - - _logger.LogInformation($"Received response from spotify -> {spotifyResponse}"); - - - if (spotifyResponse?.Item == null) - { - _logger.LogInformation("No track currently playing"); - var notPlayingResponse = new NowPlayingResponse - { - IsPlaying = false - - }; - AddCorsAndCacheHeaders(true); - return Ok(notPlayingResponse); - } - - var response = new NowPlayingResponse - { - IsPlaying = spotifyResponse.IsPlaying, - Title = spotifyResponse.Item.Name, - Artist = string.Join(", ", spotifyResponse.Item.Artists.Select(a => a.Name)), - Album = spotifyResponse.Item.Album.Name, - AlbumImageUrl = spotifyResponse.Item.Album.Images.FirstOrDefault()?.Url ?? string.Empty, - SongUrl = spotifyResponse.Item.ExternalUrls.Spotify, - Status = 200 - }; - - _cache.Set(NowPlayingCacheKey, response, TimeSpan.FromSeconds(5)); - - AddCorsAndCacheHeaders(true); - return Ok(response); - } - catch (Exception ex) - { - _logger.LogError(ex, "Error fetching now playing data"); - var errorResponse = new NowPlayingResponse - { - IsPlaying = false, - Status = 500, - }; - - AddCorsAndCacheHeaders(true); - return Ok(errorResponse); - } - } - - private void AddCorsAndCacheHeaders(bool includeCacheControl) - { - Response.Headers.Append("Access-Control-Allow-Origin", "*"); - Response.Headers.Append("Access-Control-Allow-Methods", "GET,OPTIONS,POST,PUT,DELETE"); - - if (includeCacheControl) - Response.Headers.Append("Cache-Control", - $"max-age={RevalidateSeconds}, s-maxage={RevalidateSeconds}, stale-while-revalidate={RevalidateSeconds}, stale-if-error={RevalidateSeconds}"); - else - Response.Headers.Append("Cache-Control", "no-cache"); - } -} diff --git a/apps/lho-lambda/src/Controllers/StreakController.cs b/apps/lho-lambda/src/Controllers/StreakController.cs deleted file mode 100644 index c42b5122..00000000 --- a/apps/lho-lambda/src/Controllers/StreakController.cs +++ /dev/null @@ -1,109 +0,0 @@ -using Microsoft.AspNetCore.Mvc; - -namespace LhoLambda.Controllers; - -[ApiController] -public class StreakController : ControllerBase -{ - [HttpGet("api/streak")] - public IActionResult Streak() - { - var targetDates = new[] - { - new DateTime(2025, 6, 20), - new DateTime(2025, 7, 2), - new DateTime(2025, 7, 3) - }; - - var distances = targetDates.Select(date => new - { - date = date.ToString("yyyy-MM-ddTHH:mm:ss.fffZ"), - distance = GetDistanceToNow(date) - }).ToArray(); - - var response = new - { - distances - }; - - Response.Headers.Append("Access-Control-Allow-Origin", "*"); - Response.Headers.Append("Access-Control-Allow-Methods", "GET,OPTIONS,POST,PUT,DELETE"); - Response.Headers.Append("Cache-Control", "no-cache"); - - return Ok(response); - } - - private static string GetDistanceToNow(DateTime targetDate) - { - var now = DateTime.UtcNow; - var timeSpan = targetDate - now; - - if (timeSpan.TotalDays > 365) - { - var years = (int)(timeSpan.TotalDays / 365); - return $"in {years} year{(years == 1 ? "" : "s")}"; - } - - if (timeSpan.TotalDays > 30) - { - var months = (int)(timeSpan.TotalDays / 30); - return $"in {months} month{(months == 1 ? "" : "s")}"; - } - - if (timeSpan.TotalDays >= 1) - { - var days = (int)timeSpan.TotalDays; - return $"in {days} day{(days == 1 ? "" : "s")}"; - } - - if (timeSpan.TotalHours >= 1) - { - var hours = (int)timeSpan.TotalHours; - return $"in {hours} hour{(hours == 1 ? "" : "s")}"; - } - - if (timeSpan.TotalMinutes >= 1) - { - var minutes = (int)timeSpan.TotalMinutes; - return $"in {minutes} minute{(minutes == 1 ? "" : "s")}"; - } - - // If the date is in the past - if (timeSpan.TotalSeconds < 0) - { - var absTimeSpan = now - targetDate; - - if (absTimeSpan.TotalDays > 365) - { - var years = (int)(absTimeSpan.TotalDays / 365); - return $"{years} year{(years == 1 ? "" : "s")} ago"; - } - - if (absTimeSpan.TotalDays > 30) - { - var months = (int)(absTimeSpan.TotalDays / 30); - return $"{months} month{(months == 1 ? "" : "s")} ago"; - } - - if (absTimeSpan.TotalDays >= 1) - { - var days = (int)absTimeSpan.TotalDays; - return $"{days} day{(days == 1 ? "" : "s")} ago"; - } - - if (absTimeSpan.TotalHours >= 1) - { - var hours = (int)absTimeSpan.TotalHours; - return $"{hours} hour{(hours == 1 ? "" : "s")} ago"; - } - - if (absTimeSpan.TotalMinutes >= 1) - { - var minutes = (int)absTimeSpan.TotalMinutes; - return $"{minutes} minute{(minutes == 1 ? "" : "s")} ago"; - } - } - - return "now"; - } -} diff --git a/apps/lho-lambda/src/Controllers/VersionController.cs b/apps/lho-lambda/src/Controllers/VersionController.cs deleted file mode 100644 index 9af83868..00000000 --- a/apps/lho-lambda/src/Controllers/VersionController.cs +++ /dev/null @@ -1,32 +0,0 @@ -using System.Reflection; -using LhoLambda.Models; -using Microsoft.AspNetCore.Mvc; - -namespace LhoLambda.Controllers; - -[ApiController] -public class VersionController : ControllerBase -{ - [HttpGet("api/version")] - public ActionResult Version() - { - // Try to get version from environment variable first, then fall back to assembly version - var version = Environment.GetEnvironmentVariable("VERSION") - ?? Assembly.GetExecutingAssembly().GetName().Version?.ToString() - ?? "Unknown"; - - var response = new VersionResponse - { - Version = version, - DeployedAt = Environment.GetEnvironmentVariable("DEPLOYED_AT"), - DeployedBy = Environment.GetEnvironmentVariable("DEPLOYED_BY"), - GitSha = Environment.GetEnvironmentVariable("GIT_SHA") ?? "unknown" - }; - - Response.Headers.Append("Access-Control-Allow-Origin", "*"); - Response.Headers.Append("Access-Control-Allow-Methods", "GET,OPTIONS,POST,PUT,DELETE"); - Response.Headers.Append("Cache-Control", "no-cache"); - - return Ok(response); - } -} diff --git a/apps/lho-lambda/src/LambdaEntryPoint.cs b/apps/lho-lambda/src/LambdaEntryPoint.cs deleted file mode 100644 index 9dce10d1..00000000 --- a/apps/lho-lambda/src/LambdaEntryPoint.cs +++ /dev/null @@ -1,44 +0,0 @@ -using Amazon.Lambda.AspNetCoreServer; - -namespace LhoLambda; - -/// -/// This class extends from APIGatewayProxyFunction which contains the method FunctionHandlerAsync which is the -/// actual Lambda function entry point. The Lambda handler field should be set to -/// lho-lambda::lho-lambda.LambdaEntryPoint::FunctionHandlerAsync -/// -public class LambdaEntryPoint : - - // The base class must be set to match the AWS service invoking the Lambda function. If not Amazon.Lambda.AspNetCoreServer - // will fail to convert the incoming request correctly into a valid ASP.NET Core request. - // - // API Gateway REST API -> Amazon.Lambda.AspNetCoreServer.APIGatewayProxyFunction - // API Gateway HTTP API payload version 1.0 -> Amazon.Lambda.AspNetCoreServer.APIGatewayProxyFunction - // API Gateway HTTP API payload version 2.0 -> Amazon.Lambda.AspNetCoreServer.APIGatewayHttpApiV2ProxyFunction - // Application Load Balancer -> Amazon.Lambda.AspNetCoreServer.ApplicationLoadBalancerFunction - // - // Note: When using the AWS::Serverless::Function resource with an event type of "HttpApi" then payload version 2.0 - // will be the default and you must make Amazon.Lambda.AspNetCoreServer.APIGatewayHttpApiV2ProxyFunction the base class. - APIGatewayProxyFunction -{ - /// - /// The builder has configuration, logging and Amazon API Gateway already configured. The startup class - /// needs to be configured in this method using the UseStartup<>() method. - /// - /// The IWebHostBuilder to configure. - protected override void Init(IWebHostBuilder builder) - { - builder - .UseStartup(); - } - - /// - /// Use this override to customize the services registered with the IHostBuilder. - /// It is recommended not to call ConfigureWebHostDefaults to configure the IWebHostBuilder inside this method. - /// Instead, customize the IWebHostBuilder in the Init(IWebHostBuilder) overload. - /// - /// The IHostBuilder to configure. - protected override void Init(IHostBuilder builder) - { - } -} diff --git a/apps/lho-lambda/src/LocalEntryPoint.cs b/apps/lho-lambda/src/LocalEntryPoint.cs deleted file mode 100644 index 884a1cb0..00000000 --- a/apps/lho-lambda/src/LocalEntryPoint.cs +++ /dev/null @@ -1,18 +0,0 @@ -namespace LhoLambda; - -/// -/// The Main function can be used to run the ASP.NET Core application locally using the Kestrel webserver. -/// -public class LocalEntryPoint -{ - public static void Main(string[] args) - { - CreateHostBuilder(args).Build().Run(); - } - - public static IHostBuilder CreateHostBuilder(string[] args) - { - return Host.CreateDefaultBuilder(args) - .ConfigureWebHostDefaults(webBuilder => { webBuilder.UseStartup(); }); - } -} diff --git a/apps/lho-lambda/src/Startup.cs b/apps/lho-lambda/src/Startup.cs deleted file mode 100644 index 35653229..00000000 --- a/apps/lho-lambda/src/Startup.cs +++ /dev/null @@ -1,70 +0,0 @@ -using System.Text.Json; -using System.Text.Json.Serialization; -using LhoLambda.services; - -namespace LhoLambda; - -public class Startup -{ - public Startup(IConfiguration configuration) - { - Configuration = configuration; - } - - public IConfiguration Configuration { get; } - - public void ConfigureServices(IServiceCollection services) - { - services.AddControllers().AddJsonOptions(opts => - { - opts.JsonSerializerOptions.PropertyNamingPolicy = JsonNamingPolicy.CamelCase; - opts.JsonSerializerOptions.DictionaryKeyPolicy = JsonNamingPolicy.CamelCase; - opts.JsonSerializerOptions.PropertyNameCaseInsensitive = true; - opts.JsonSerializerOptions.DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull; - }); - - services.AddCors(options => - { - options.AddDefaultPolicy(builder => - { - builder - .AllowAnyOrigin() - .AllowAnyMethod() - .AllowAnyHeader(); - }); - }); - - services.AddMemoryCache(); - services.AddHttpClient(); - services.AddScoped(); - services.AddLogging(); - } - - public void Configure(IApplicationBuilder app, IWebHostEnvironment env) - { - if (env.IsDevelopment()) - app.UseDeveloperExceptionPage(); - - app.UseCors(); - app.UseHttpsRedirection(); - app.UseRouting(); - app.UseAuthorization(); - - app.UseEndpoints(endpoints => - { - endpoints.MapControllers(); - - // Handle undefined routes - return 404 with proper JSON response - endpoints.MapFallback(async context => - { - context.Response.StatusCode = 404; - context.Response.ContentType = "application/json"; - context.Response.Headers.Append("Access-Control-Allow-Origin", "*"); - context.Response.Headers.Append("Access-Control-Allow-Methods", "GET,OPTIONS,POST,PUT,DELETE"); - context.Response.Headers.Append("Cache-Control", "no-cache"); - - await context.Response.WriteAsync("{\"message\": \"route not found\"}"); - }); - }); - } -} diff --git a/apps/lho-lambda/src/appsettings.Development.json b/apps/lho-lambda/src/appsettings.Development.json deleted file mode 100644 index 905d15a0..00000000 --- a/apps/lho-lambda/src/appsettings.Development.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "AWS": { - "Region": "" - } -} \ No newline at end of file diff --git a/apps/lho-lambda/src/appsettings.json b/apps/lho-lambda/src/appsettings.json deleted file mode 100644 index 8b3c036a..00000000 --- a/apps/lho-lambda/src/appsettings.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "Logging": { - "LogLevel": { - "Default": "Error" - } - } -} diff --git a/apps/lho-lambda/src/lho-lambda.csproj b/apps/lho-lambda/src/lho-lambda.csproj deleted file mode 100644 index 0dbe7c99..00000000 --- a/apps/lho-lambda/src/lho-lambda.csproj +++ /dev/null @@ -1,19 +0,0 @@ - - - 1.4.2-pre.112a466 - Exe - net8.0 - enable - enable - true - Lambda - - true - - true - LhoLambda - - - - - \ No newline at end of file diff --git a/apps/lho-lambda/src/models/NowPlayingResponse.cs b/apps/lho-lambda/src/models/NowPlayingResponse.cs deleted file mode 100644 index bf2f9da1..00000000 --- a/apps/lho-lambda/src/models/NowPlayingResponse.cs +++ /dev/null @@ -1,20 +0,0 @@ -namespace LhoLambda.Models; - -public class NowPlayingResponse -{ - public int Status { get; set; } = 200; - - public bool? Maintenance { get; set; } = false; - - public bool IsPlaying { get; set; } = false; - - public string? Title { get; set; } = string.Empty; - - public string Artist { get; set; } = string.Empty; - - public string? Album { get; set; } = string.Empty; - - public string AlbumImageUrl { get; set; } = string.Empty; - - public string? SongUrl { get; set; } = string.Empty; -} diff --git a/apps/lho-lambda/src/models/SpotifyAlbum.cs b/apps/lho-lambda/src/models/SpotifyAlbum.cs deleted file mode 100644 index c2414ced..00000000 --- a/apps/lho-lambda/src/models/SpotifyAlbum.cs +++ /dev/null @@ -1,24 +0,0 @@ -using System.Text.Json.Serialization; - -namespace LhoLambda.Models; - -public class SpotifyAlbum -{ - [JsonPropertyName("album_type")] - public string AlbumType { get; set; } = string.Empty; - - [JsonPropertyName("artists")] - public List Artists { get; set; } = new(); - - [JsonPropertyName("external_urls")] - public SpotifyExternalUrls ExternalUrls { get; set; } = new(); - - [JsonPropertyName("images")] - public List Images { get; set; } = new(); - - [JsonPropertyName("name")] - public string Name { get; set; } = string.Empty; - - [JsonPropertyName("release_date")] - public string ReleaseDate { get; set; } = string.Empty; -} diff --git a/apps/lho-lambda/src/models/SpotifyArtist.cs b/apps/lho-lambda/src/models/SpotifyArtist.cs deleted file mode 100644 index 3f1881ad..00000000 --- a/apps/lho-lambda/src/models/SpotifyArtist.cs +++ /dev/null @@ -1,24 +0,0 @@ -using System.Text.Json.Serialization; - -namespace LhoLambda.Models; - -public class SpotifyArtist -{ - [JsonPropertyName("external_urls")] - public SpotifyExternalUrls ExternalUrls { get; set; } = new(); - - [JsonPropertyName("href")] - public string Href { get; set; } = string.Empty; - - [JsonPropertyName("id")] - public string Id { get; set; } = string.Empty; - - [JsonPropertyName("name")] - public string Name { get; set; } = string.Empty; - - [JsonPropertyName("type")] - public string Type { get; set; } = string.Empty; - - [JsonPropertyName("uri")] - public string Uri { get; set; } = string.Empty; -} diff --git a/apps/lho-lambda/src/models/SpotifyExternalUrls.cs b/apps/lho-lambda/src/models/SpotifyExternalUrls.cs deleted file mode 100644 index 1992bc5c..00000000 --- a/apps/lho-lambda/src/models/SpotifyExternalUrls.cs +++ /dev/null @@ -1,9 +0,0 @@ -using System.Text.Json.Serialization; - -namespace LhoLambda.Models; - -public class SpotifyExternalUrls -{ - [JsonPropertyName("spotify")] - public string Spotify { get; set; } = string.Empty; -} diff --git a/apps/lho-lambda/src/models/SpotifyImage.cs b/apps/lho-lambda/src/models/SpotifyImage.cs deleted file mode 100644 index f449719d..00000000 --- a/apps/lho-lambda/src/models/SpotifyImage.cs +++ /dev/null @@ -1,15 +0,0 @@ -using System.Text.Json.Serialization; - -namespace LhoLambda.Models; - -public class SpotifyImage -{ - [JsonPropertyName("height")] - public int Height { get; set; } - - [JsonPropertyName("url")] - public string Url { get; set; } = string.Empty; - - [JsonPropertyName("width")] - public int Width { get; set; } -} diff --git a/apps/lho-lambda/src/models/SpotifyNowPlayingResponse.cs b/apps/lho-lambda/src/models/SpotifyNowPlayingResponse.cs deleted file mode 100644 index 1259d697..00000000 --- a/apps/lho-lambda/src/models/SpotifyNowPlayingResponse.cs +++ /dev/null @@ -1,12 +0,0 @@ -using System.Text.Json.Serialization; - -namespace LhoLambda.Models; - -public class SpotifyNowPlayingResponse -{ - [JsonPropertyName("is_playing")] - public bool IsPlaying { get; set; } - - [JsonPropertyName("item")] - public SpotifyTrack? Item { get; set; } -} diff --git a/apps/lho-lambda/src/models/SpotifyTokenResponse.cs b/apps/lho-lambda/src/models/SpotifyTokenResponse.cs deleted file mode 100644 index 0eec9534..00000000 --- a/apps/lho-lambda/src/models/SpotifyTokenResponse.cs +++ /dev/null @@ -1,15 +0,0 @@ -using System.Text.Json.Serialization; - -namespace LhoLambda.Models; - -public class SpotifyTokenResponse -{ - [JsonPropertyName("access_token")] - public string AccessToken { get; set; } = string.Empty; - - [JsonPropertyName("token_type")] - public string TokenType { get; set; } = string.Empty; - - [JsonPropertyName("expires_in")] - public int ExpiresIn { get; set; } -} diff --git a/apps/lho-lambda/src/models/SpotifyTrack.cs b/apps/lho-lambda/src/models/SpotifyTrack.cs deleted file mode 100644 index b282434c..00000000 --- a/apps/lho-lambda/src/models/SpotifyTrack.cs +++ /dev/null @@ -1,39 +0,0 @@ -using System.Text.Json.Serialization; - -namespace LhoLambda.Models; - -public class SpotifyTrack -{ - [JsonPropertyName("album")] - public SpotifyAlbum Album { get; set; } = new(); - - [JsonPropertyName("artists")] - public List Artists { get; set; } = new(); - - [JsonPropertyName("external_urls")] - public SpotifyExternalUrls ExternalUrls { get; set; } = new(); - - [JsonPropertyName("href")] - public string Href { get; set; } = string.Empty; - - [JsonPropertyName("id")] - public string Id { get; set; } = string.Empty; - - [JsonPropertyName("name")] - public string Name { get; set; } = string.Empty; - - [JsonPropertyName("popularity")] - public int Popularity { get; set; } - - [JsonPropertyName("preview_url")] - public string? PreviewUrl { get; set; } - - [JsonPropertyName("track_number")] - public int TrackNumber { get; set; } - - [JsonPropertyName("type")] - public string Type { get; set; } = string.Empty; - - [JsonPropertyName("uri")] - public string Uri { get; set; } = string.Empty; -} diff --git a/apps/lho-lambda/src/models/VersionResponse.cs b/apps/lho-lambda/src/models/VersionResponse.cs deleted file mode 100644 index 6b3c7987..00000000 --- a/apps/lho-lambda/src/models/VersionResponse.cs +++ /dev/null @@ -1,12 +0,0 @@ -namespace LhoLambda.Models; - -public class VersionResponse -{ - public string Version { get; set; } = string.Empty; - - public string? DeployedBy { get; set; } - - public string? DeployedAt { get; set; } - - public string GitSha { get; set; } = string.Empty; -} diff --git a/apps/lho-lambda/src/services/SpotifyService.cs b/apps/lho-lambda/src/services/SpotifyService.cs deleted file mode 100644 index 10c2df6d..00000000 --- a/apps/lho-lambda/src/services/SpotifyService.cs +++ /dev/null @@ -1,171 +0,0 @@ -using System.Net; -using System.Text; -using System.Text.Json; -using LhoLambda.Models; -using Microsoft.Extensions.Caching.Memory; -using Microsoft.Extensions.Configuration; - -namespace LhoLambda.services; - -public interface ISpotifyService -{ - Task GetNowPlayingAsync(); -} - -public class SpotifyService : ISpotifyService -{ - private const string NowPlayingEndpoint = "https://api.spotify.com/v1/me/player/currently-playing"; - private const string TokenEndpoint = "https://accounts.spotify.com/api/token"; - private const string AccessTokenCacheKey = "SpotifyAccessToken"; - - private readonly IMemoryCache _cache; - private readonly IConfiguration _configuration; - private readonly HttpClient _httpClient; - private readonly ILogger _logger; - - private static readonly JsonSerializerOptions JsonOptions = new() - { - PropertyNameCaseInsensitive = true - }; - - public SpotifyService( - HttpClient httpClient, - IMemoryCache memoryCache, - IConfiguration configuration, - ILogger logger - ) - { - _httpClient = httpClient; - _cache = memoryCache; - _configuration = configuration; - _logger = logger; - } - - public async Task GetNowPlayingAsync() - { - try - { - var accessToken = await GetAccessTokenAsync(); - - if (string.IsNullOrEmpty(accessToken)) - { - _logger.LogWarning("Access token is null or empty"); - return null; - } - - // Clear any existing authorization headers - _httpClient.DefaultRequestHeaders.Authorization = null; - _httpClient.DefaultRequestHeaders.Remove("Authorization"); - - // Set Bearer token - _httpClient.DefaultRequestHeaders.Add("Authorization", $"Bearer {accessToken}"); - - var response = await _httpClient.GetAsync(NowPlayingEndpoint); - - // No song currently being played - if (response.StatusCode == HttpStatusCode.NoContent) - { - _logger.LogInformation("No content returned from Spotify - no song currently playing"); - return null; - } - - if (!response.IsSuccessStatusCode) - { - var errorContent = await response.Content.ReadAsStringAsync(); - _logger.LogWarning("Spotify now playing returned status code: {StatusCode}, Content: {Content}", - response.StatusCode, errorContent); - return null; - } - - var content = await response.Content.ReadAsStringAsync(); - _logger.LogDebug("Spotify API response content: {Content}", content); - - return JsonSerializer.Deserialize(content, JsonOptions); - } - catch (JsonException jsonEx) - { - _logger.LogError(jsonEx, "Error deserializing Spotify now playing response"); - return null; - } - catch (HttpRequestException httpEx) - { - _logger.LogError(httpEx, "HTTP error getting Spotify now playing response"); - return null; - } - catch (Exception e) - { - _logger.LogError(e, "Error getting Spotify now playing response"); - return null; - } - } - - private async Task GetAccessTokenAsync() - { - // if (_cache.TryGetValue(AccessTokenCacheKey, out string? cachedToken)) return cachedToken; - try - { - var clientId = _configuration["SPOTIFY_CLIENT_ID"]; - var clientSecret = _configuration["SPOTIFY_CLIENT_SECRET"]; - var refreshToken = _configuration["SPOTIFY_REFRESH_TOKEN"]; - - if (string.IsNullOrEmpty(clientId) || string.IsNullOrEmpty(clientSecret) || string.IsNullOrEmpty(refreshToken)) - { - _logger.LogError("Missing Spotify configuration values - ClientId: {ClientIdPresent}, ClientSecret: {ClientSecretPresent}, RefreshToken: {RefreshTokenPresent}", - !string.IsNullOrEmpty(clientId), !string.IsNullOrEmpty(clientSecret), !string.IsNullOrEmpty(refreshToken)); - return null; - } - - var basicAuth = Convert.ToBase64String(Encoding.UTF8.GetBytes($"{clientId}:{clientSecret}")); - - // Clear any existing authorization headers - _httpClient.DefaultRequestHeaders.Authorization = null; - _httpClient.DefaultRequestHeaders.Remove("Authorization"); - - // Set Basic auth for token request - _httpClient.DefaultRequestHeaders.Add("Authorization", $"Basic {basicAuth}"); - - var content = new FormUrlEncodedContent([ - new KeyValuePair("grant_type", "refresh_token"), - new KeyValuePair("refresh_token", refreshToken) - ]); - - var response = await _httpClient.PostAsync(TokenEndpoint, content); - - if (!response.IsSuccessStatusCode) - { - var errorContent = await response.Content.ReadAsStringAsync(); - _logger.LogError("Failed to retrieve access token. Status {StatusCode}, Content: {Content}", - response.StatusCode, errorContent); - return null; - } - - var contentStr = await response.Content.ReadAsStringAsync(); - _logger.LogDebug("Token response content: {Content}", contentStr); - - var tokenResponse = JsonSerializer.Deserialize(contentStr, JsonOptions); - - if (string.IsNullOrEmpty(tokenResponse?.AccessToken)) - { - _logger.LogError("Access token is null or empty in token response"); - return null; - } - - return tokenResponse.AccessToken; - } - catch (JsonException jsonEx) - { - _logger.LogError(jsonEx, "Error deserializing token response"); - return null; - } - catch (HttpRequestException httpEx) - { - _logger.LogError(httpEx, "HTTP error retrieving access token"); - return null; - } - catch (Exception ex) - { - _logger.LogError(ex, "Failed to retrieve access token"); - return null; - } - } -} diff --git a/bun.lock b/bun.lock deleted file mode 100644 index e5473ff9..00000000 --- a/bun.lock +++ /dev/null @@ -1,787 +0,0 @@ -{ - "lockfileVersion": 1, - "workspaces": { - "": { - "name": "root", - "devDependencies": { - "@commitlint/cli": "^19.6.1", - "@commitlint/config-conventional": "^19.6.0", - "commitizen": "4.3.1", - "conventional-changelog-cli": "^3.0.0", - "git-cz": "^4.9.0", - "lint-staged": "16.1.6", - }, - }, - }, - "packages": { - "@babel/code-frame": ["@babel/code-frame@7.27.1", "", { "dependencies": { "@babel/helper-validator-identifier": "^7.27.1", "js-tokens": "^4.0.0", "picocolors": "^1.1.1" } }, "sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg=="], - - "@babel/helper-validator-identifier": ["@babel/helper-validator-identifier@7.27.1", "", {}, "sha512-D2hP9eA+Sqx1kBZgzxZh0y1trbuU+JoDkiEwqhQ36nodYqJwyEIhPSdMNd7lOm/4io72luTPWH20Yda0xOuUow=="], - - "@commitlint/cli": ["@commitlint/cli@19.8.1", "", { "dependencies": { "@commitlint/format": "^19.8.1", "@commitlint/lint": "^19.8.1", "@commitlint/load": "^19.8.1", "@commitlint/read": "^19.8.1", "@commitlint/types": "^19.8.1", "tinyexec": "^1.0.0", "yargs": "^17.0.0" }, "bin": { "commitlint": "./cli.js" } }, "sha512-LXUdNIkspyxrlV6VDHWBmCZRtkEVRpBKxi2Gtw3J54cGWhLCTouVD/Q6ZSaSvd2YaDObWK8mDjrz3TIKtaQMAA=="], - - "@commitlint/config-conventional": ["@commitlint/config-conventional@19.8.1", "", { "dependencies": { "@commitlint/types": "^19.8.1", "conventional-changelog-conventionalcommits": "^7.0.2" } }, "sha512-/AZHJL6F6B/G959CsMAzrPKKZjeEiAVifRyEwXxcT6qtqbPwGw+iQxmNS+Bu+i09OCtdNRW6pNpBvgPrtMr9EQ=="], - - "@commitlint/config-validator": ["@commitlint/config-validator@19.8.1", "", { "dependencies": { "@commitlint/types": "^19.8.1", "ajv": "^8.11.0" } }, "sha512-0jvJ4u+eqGPBIzzSdqKNX1rvdbSU1lPNYlfQQRIFnBgLy26BtC0cFnr7c/AyuzExMxWsMOte6MkTi9I3SQ3iGQ=="], - - "@commitlint/ensure": ["@commitlint/ensure@19.8.1", "", { "dependencies": { "@commitlint/types": "^19.8.1", "lodash.camelcase": "^4.3.0", "lodash.kebabcase": "^4.1.1", "lodash.snakecase": "^4.1.1", "lodash.startcase": "^4.4.0", "lodash.upperfirst": "^4.3.1" } }, "sha512-mXDnlJdvDzSObafjYrOSvZBwkD01cqB4gbnnFuVyNpGUM5ijwU/r/6uqUmBXAAOKRfyEjpkGVZxaDsCVnHAgyw=="], - - "@commitlint/execute-rule": ["@commitlint/execute-rule@19.8.1", "", {}, "sha512-YfJyIqIKWI64Mgvn/sE7FXvVMQER/Cd+s3hZke6cI1xgNT/f6ZAz5heND0QtffH+KbcqAwXDEE1/5niYayYaQA=="], - - "@commitlint/format": ["@commitlint/format@19.8.1", "", { "dependencies": { "@commitlint/types": "^19.8.1", "chalk": "^5.3.0" } }, "sha512-kSJj34Rp10ItP+Eh9oCItiuN/HwGQMXBnIRk69jdOwEW9llW9FlyqcWYbHPSGofmjsqeoxa38UaEA5tsbm2JWw=="], - - "@commitlint/is-ignored": ["@commitlint/is-ignored@19.8.1", "", { "dependencies": { "@commitlint/types": "^19.8.1", "semver": "^7.6.0" } }, "sha512-AceOhEhekBUQ5dzrVhDDsbMaY5LqtN8s1mqSnT2Kz1ERvVZkNihrs3Sfk1Je/rxRNbXYFzKZSHaPsEJJDJV8dg=="], - - "@commitlint/lint": ["@commitlint/lint@19.8.1", "", { "dependencies": { "@commitlint/is-ignored": "^19.8.1", "@commitlint/parse": "^19.8.1", "@commitlint/rules": "^19.8.1", "@commitlint/types": "^19.8.1" } }, "sha512-52PFbsl+1EvMuokZXLRlOsdcLHf10isTPlWwoY1FQIidTsTvjKXVXYb7AvtpWkDzRO2ZsqIgPK7bI98x8LRUEw=="], - - "@commitlint/load": ["@commitlint/load@19.8.1", "", { "dependencies": { "@commitlint/config-validator": "^19.8.1", "@commitlint/execute-rule": "^19.8.1", "@commitlint/resolve-extends": "^19.8.1", "@commitlint/types": "^19.8.1", "chalk": "^5.3.0", "cosmiconfig": "^9.0.0", "cosmiconfig-typescript-loader": "^6.1.0", "lodash.isplainobject": "^4.0.6", "lodash.merge": "^4.6.2", "lodash.uniq": "^4.5.0" } }, "sha512-9V99EKG3u7z+FEoe4ikgq7YGRCSukAcvmKQuTtUyiYPnOd9a2/H9Ak1J9nJA1HChRQp9OA/sIKPugGS+FK/k1A=="], - - "@commitlint/message": ["@commitlint/message@19.8.1", "", {}, "sha512-+PMLQvjRXiU+Ae0Wc+p99EoGEutzSXFVwQfa3jRNUZLNW5odZAyseb92OSBTKCu+9gGZiJASt76Cj3dLTtcTdg=="], - - "@commitlint/parse": ["@commitlint/parse@19.8.1", "", { "dependencies": { "@commitlint/types": "^19.8.1", "conventional-changelog-angular": "^7.0.0", "conventional-commits-parser": "^5.0.0" } }, "sha512-mmAHYcMBmAgJDKWdkjIGq50X4yB0pSGpxyOODwYmoexxxiUCy5JJT99t1+PEMK7KtsCtzuWYIAXYAiKR+k+/Jw=="], - - "@commitlint/read": ["@commitlint/read@19.8.1", "", { "dependencies": { "@commitlint/top-level": "^19.8.1", "@commitlint/types": "^19.8.1", "git-raw-commits": "^4.0.0", "minimist": "^1.2.8", "tinyexec": "^1.0.0" } }, "sha512-03Jbjb1MqluaVXKHKRuGhcKWtSgh3Jizqy2lJCRbRrnWpcM06MYm8th59Xcns8EqBYvo0Xqb+2DoZFlga97uXQ=="], - - "@commitlint/resolve-extends": ["@commitlint/resolve-extends@19.8.1", "", { "dependencies": { "@commitlint/config-validator": "^19.8.1", "@commitlint/types": "^19.8.1", "global-directory": "^4.0.1", "import-meta-resolve": "^4.0.0", "lodash.mergewith": "^4.6.2", "resolve-from": "^5.0.0" } }, "sha512-GM0mAhFk49I+T/5UCYns5ayGStkTt4XFFrjjf0L4S26xoMTSkdCf9ZRO8en1kuopC4isDFuEm7ZOm/WRVeElVg=="], - - "@commitlint/rules": ["@commitlint/rules@19.8.1", "", { "dependencies": { "@commitlint/ensure": "^19.8.1", "@commitlint/message": "^19.8.1", "@commitlint/to-lines": "^19.8.1", "@commitlint/types": "^19.8.1" } }, "sha512-Hnlhd9DyvGiGwjfjfToMi1dsnw1EXKGJNLTcsuGORHz6SS9swRgkBsou33MQ2n51/boIDrbsg4tIBbRpEWK2kw=="], - - "@commitlint/to-lines": ["@commitlint/to-lines@19.8.1", "", {}, "sha512-98Mm5inzbWTKuZQr2aW4SReY6WUukdWXuZhrqf1QdKPZBCCsXuG87c+iP0bwtD6DBnmVVQjgp4whoHRVixyPBg=="], - - "@commitlint/top-level": ["@commitlint/top-level@19.8.1", "", { "dependencies": { "find-up": "^7.0.0" } }, "sha512-Ph8IN1IOHPSDhURCSXBz44+CIu+60duFwRsg6HqaISFHQHbmBtxVw4ZrFNIYUzEP7WwrNPxa2/5qJ//NK1FGcw=="], - - "@commitlint/types": ["@commitlint/types@19.8.1", "", { "dependencies": { "@types/conventional-commits-parser": "^5.0.0", "chalk": "^5.3.0" } }, "sha512-/yCrWGCoA1SVKOks25EGadP9Pnj0oAIHGpl2wH2M2Y46dPM2ueb8wyCVOD7O3WCTkaJ0IkKvzhl1JY7+uCT2Dw=="], - - "@hutson/parse-repository-url": ["@hutson/parse-repository-url@3.0.2", "", {}, "sha512-H9XAx3hc0BQHY6l+IFSWHDySypcXsvsuLhgYLUGywmJ5pswRVQJUHpOsobnLYp2ZUaUlKiKDrgWWhosOwAEM8Q=="], - - "@types/conventional-commits-parser": ["@types/conventional-commits-parser@5.0.1", "", { "dependencies": { "@types/node": "*" } }, "sha512-7uz5EHdzz2TqoMfV7ee61Egf5y6NkcO4FB/1iCCQnbeiI1F3xzv3vK5dBCXUCLQgGYS+mUeigK1iKQzvED+QnQ=="], - - "@types/minimist": ["@types/minimist@1.2.5", "", {}, "sha512-hov8bUuiLiyFPGyFPE1lwWhmzYbirOXQNNo40+y3zow8aFVTeyn3VWL0VFFfdNddA8S4Vf0Tc062rzyNr7Paag=="], - - "@types/node": ["@types/node@24.4.0", "", { "dependencies": { "undici-types": "~7.11.0" } }, "sha512-gUuVEAK4/u6F9wRLznPUU4WGUacSEBDPoC2TrBkw3GAnOLHBL45QdfHOXp1kJ4ypBGLxTOB+t7NJLpKoC3gznQ=="], - - "@types/normalize-package-data": ["@types/normalize-package-data@2.4.4", "", {}, "sha512-37i+OaWTh9qeK4LSHPsyRC7NahnGotNuZvjLSgcPzblpHB3rrCJxAOgI5gCdKm7coonsaX1Of0ILiTcnZjbfxA=="], - - "JSONStream": ["JSONStream@1.3.5", "", { "dependencies": { "jsonparse": "^1.2.0", "through": ">=2.2.7 <3" }, "bin": { "JSONStream": "./bin.js" } }, "sha512-E+iruNOY8VV9s4JEbe1aNEm6MiszPRr/UfcHMz0TQh1BXSxHK+ASV1R6W4HpjBhSeS+54PIsAMCBmwD06LLsqQ=="], - - "add-stream": ["add-stream@1.0.0", "", {}, "sha512-qQLMr+8o0WC4FZGQTcJiKBVC59JylcPSrTtk6usvmIDFUOCKegapy1VHQwRbFMOFyb/inzUVqHs+eMYKDM1YeQ=="], - - "ajv": ["ajv@8.17.1", "", { "dependencies": { "fast-deep-equal": "^3.1.3", "fast-uri": "^3.0.1", "json-schema-traverse": "^1.0.0", "require-from-string": "^2.0.2" } }, "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g=="], - - "ansi-escapes": ["ansi-escapes@4.3.2", "", { "dependencies": { "type-fest": "^0.21.3" } }, "sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ=="], - - "ansi-regex": ["ansi-regex@5.0.1", "", {}, "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ=="], - - "ansi-styles": ["ansi-styles@3.2.1", "", { "dependencies": { "color-convert": "^1.9.0" } }, "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA=="], - - "argparse": ["argparse@2.0.1", "", {}, "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q=="], - - "array-ify": ["array-ify@1.0.0", "", {}, "sha512-c5AMf34bKdvPhQ7tBGhqkgKNUzMr4WUs+WDtC2ZUGOUncbxKMTvqxYctiseW3+L4bA8ec+GcZ6/A/FW4m8ukng=="], - - "arrify": ["arrify@1.0.1", "", {}, "sha512-3CYzex9M9FGQjCGMGyi6/31c8GJbgb0qGyrx5HWxPd0aCwh4cB2YjMb2Xf9UuoogrMrlO9cTqnB5rI5GHZTcUA=="], - - "at-least-node": ["at-least-node@1.0.0", "", {}, "sha512-+q/t7Ekv1EDY2l6Gda6LLiX14rU9TV20Wa3ofeQmwPFZbOMo9DXrLbOjFaaclkXKWidIaopwAObQDqwWtGUjqg=="], - - "balanced-match": ["balanced-match@1.0.2", "", {}, "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw=="], - - "base64-js": ["base64-js@1.5.1", "", {}, "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA=="], - - "bl": ["bl@4.1.0", "", { "dependencies": { "buffer": "^5.5.0", "inherits": "^2.0.4", "readable-stream": "^3.4.0" } }, "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w=="], - - "brace-expansion": ["brace-expansion@1.1.12", "", { "dependencies": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" } }, "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg=="], - - "braces": ["braces@3.0.3", "", { "dependencies": { "fill-range": "^7.1.1" } }, "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA=="], - - "buffer": ["buffer@5.7.1", "", { "dependencies": { "base64-js": "^1.3.1", "ieee754": "^1.1.13" } }, "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ=="], - - "cachedir": ["cachedir@2.3.0", "", {}, "sha512-A+Fezp4zxnit6FanDmv9EqXNAi3vt9DWp51/71UEhXukb7QUuvtv9344h91dyAxuTLoSYJFU299qzR3tzwPAhw=="], - - "callsites": ["callsites@3.1.0", "", {}, "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ=="], - - "camelcase": ["camelcase@5.3.1", "", {}, "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg=="], - - "camelcase-keys": ["camelcase-keys@6.2.2", "", { "dependencies": { "camelcase": "^5.3.1", "map-obj": "^4.0.0", "quick-lru": "^4.0.1" } }, "sha512-YrwaA0vEKazPBkn0ipTiMpSajYDSe+KjQfrjhcBMxJt/znbvlHd8Pw/Vamaz5EB4Wfhs3SUR3Z9mwRu/P3s3Yg=="], - - "chalk": ["chalk@5.6.2", "", {}, "sha512-7NzBL0rN6fMUW+f7A6Io4h40qQlG+xGmtMxfbnH/K7TAtt8JQWVQK+6g0UXKMeVJoyV5EkkNsErQ8pVD3bLHbA=="], - - "chardet": ["chardet@0.7.0", "", {}, "sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA=="], - - "cli-cursor": ["cli-cursor@3.1.0", "", { "dependencies": { "restore-cursor": "^3.1.0" } }, "sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw=="], - - "cli-spinners": ["cli-spinners@2.9.2", "", {}, "sha512-ywqV+5MmyL4E7ybXgKys4DugZbX0FC6LnwrhjuykIjnK9k8OQacQ7axGKnjDXWNhns0xot3bZI5h55H8yo9cJg=="], - - "cli-truncate": ["cli-truncate@4.0.0", "", { "dependencies": { "slice-ansi": "^5.0.0", "string-width": "^7.0.0" } }, "sha512-nPdaFdQ0h/GEigbPClz11D0v/ZJEwxmeVZGeMo3Z5StPtUTkA9o1lD6QwoirYiSDzbcwn2XcjwmCp68W1IS4TA=="], - - "cli-width": ["cli-width@3.0.0", "", {}, "sha512-FxqpkPPwu1HjuN93Omfm4h8uIanXofW0RxVEW3k5RKx+mJJYSthzNhp32Kzxxy3YAEZ/Dc/EWN1vZRY0+kOhbw=="], - - "cliui": ["cliui@8.0.1", "", { "dependencies": { "string-width": "^4.2.0", "strip-ansi": "^6.0.1", "wrap-ansi": "^7.0.0" } }, "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ=="], - - "clone": ["clone@1.0.4", "", {}, "sha512-JQHZ2QMW6l3aH/j6xCqQThY/9OH4D/9ls34cgkUBiEeocRTU04tHfKPBsUK1PqZCUQM7GiA0IIXJSuXHI64Kbg=="], - - "color-convert": ["color-convert@1.9.3", "", { "dependencies": { "color-name": "1.1.3" } }, "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg=="], - - "color-name": ["color-name@1.1.3", "", {}, "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw=="], - - "colorette": ["colorette@2.0.20", "", {}, "sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w=="], - - "commander": ["commander@14.0.1", "", {}, "sha512-2JkV3gUZUVrbNA+1sjBOYLsMZ5cEEl8GTFP2a4AVz5hvasAMCQ1D2l2le/cX+pV4N6ZU17zjUahLpIXRrnWL8A=="], - - "commitizen": ["commitizen@4.3.1", "", { "dependencies": { "cachedir": "2.3.0", "cz-conventional-changelog": "3.3.0", "dedent": "0.7.0", "detect-indent": "6.1.0", "find-node-modules": "^2.1.2", "find-root": "1.1.0", "fs-extra": "9.1.0", "glob": "7.2.3", "inquirer": "8.2.5", "is-utf8": "^0.2.1", "lodash": "4.17.21", "minimist": "1.2.7", "strip-bom": "4.0.0", "strip-json-comments": "3.1.1" }, "bin": { "cz": "bin/git-cz", "git-cz": "bin/git-cz", "commitizen": "bin/commitizen" } }, "sha512-gwAPAVTy/j5YcOOebcCRIijn+mSjWJC+IYKivTu6aG8Ei/scoXgfsMRnuAk6b0GRste2J4NGxVdMN3ZpfNaVaw=="], - - "compare-func": ["compare-func@2.0.0", "", { "dependencies": { "array-ify": "^1.0.0", "dot-prop": "^5.1.0" } }, "sha512-zHig5N+tPWARooBnb0Zx1MFcdfpyJrfTJ3Y5L+IFvUm8rM74hHz66z0gw0x4tijh5CorKkKUCnW82R2vmpeCRA=="], - - "concat-map": ["concat-map@0.0.1", "", {}, "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg=="], - - "conventional-changelog": ["conventional-changelog@4.0.0", "", { "dependencies": { "conventional-changelog-angular": "^6.0.0", "conventional-changelog-atom": "^3.0.0", "conventional-changelog-codemirror": "^3.0.0", "conventional-changelog-conventionalcommits": "^6.0.0", "conventional-changelog-core": "^5.0.0", "conventional-changelog-ember": "^3.0.0", "conventional-changelog-eslint": "^4.0.0", "conventional-changelog-express": "^3.0.0", "conventional-changelog-jquery": "^4.0.0", "conventional-changelog-jshint": "^3.0.0", "conventional-changelog-preset-loader": "^3.0.0" } }, "sha512-JbZjwE1PzxQCvm+HUTIr+pbSekS8qdOZzMakdFyPtdkEWwFvwEJYONzjgMm0txCb2yBcIcfKDmg8xtCKTdecNQ=="], - - "conventional-changelog-angular": ["conventional-changelog-angular@6.0.0", "", { "dependencies": { "compare-func": "^2.0.0" } }, "sha512-6qLgrBF4gueoC7AFVHu51nHL9pF9FRjXrH+ceVf7WmAfH3gs+gEYOkvxhjMPjZu57I4AGUGoNTY8V7Hrgf1uqg=="], - - "conventional-changelog-atom": ["conventional-changelog-atom@3.0.0", "", {}, "sha512-pnN5bWpH+iTUWU3FaYdw5lJmfWeqSyrUkG+wyHBI9tC1dLNnHkbAOg1SzTQ7zBqiFrfo55h40VsGXWMdopwc5g=="], - - "conventional-changelog-cli": ["conventional-changelog-cli@3.0.0", "", { "dependencies": { "add-stream": "^1.0.0", "conventional-changelog": "^4.0.0", "meow": "^8.1.2", "tempfile": "^3.0.0" }, "bin": { "conventional-changelog": "cli.js" } }, "sha512-3zMYi0IrfNd6AAHdPMrcgCg5DbcffiqNaEBf8cYrlntXPbBIXaELTbnRmUy5TQAe0Hkgi0J6+/VmRCkkJQflcQ=="], - - "conventional-changelog-codemirror": ["conventional-changelog-codemirror@3.0.0", "", {}, "sha512-wzchZt9HEaAZrenZAUUHMCFcuYzGoZ1wG/kTRMICxsnW5AXohYMRxnyecP9ob42Gvn5TilhC0q66AtTPRSNMfw=="], - - "conventional-changelog-conventionalcommits": ["conventional-changelog-conventionalcommits@7.0.2", "", { "dependencies": { "compare-func": "^2.0.0" } }, "sha512-NKXYmMR/Hr1DevQegFB4MwfM5Vv0m4UIxKZTTYuD98lpTknaZlSRrDOG4X7wIXpGkfsYxZTghUN+Qq+T0YQI7w=="], - - "conventional-changelog-core": ["conventional-changelog-core@5.0.2", "", { "dependencies": { "add-stream": "^1.0.0", "conventional-changelog-writer": "^6.0.0", "conventional-commits-parser": "^4.0.0", "dateformat": "^3.0.3", "get-pkg-repo": "^4.2.1", "git-raw-commits": "^3.0.0", "git-remote-origin-url": "^2.0.0", "git-semver-tags": "^5.0.0", "normalize-package-data": "^3.0.3", "read-pkg": "^3.0.0", "read-pkg-up": "^3.0.0" } }, "sha512-RhQOcDweXNWvlRwUDCpaqXzbZemKPKncCWZG50Alth72WITVd6nhVk9MJ6w1k9PFNBcZ3YwkdkChE+8+ZwtUug=="], - - "conventional-changelog-ember": ["conventional-changelog-ember@3.0.0", "", {}, "sha512-7PYthCoSxIS98vWhVcSphMYM322OxptpKAuHYdVspryI0ooLDehRXWeRWgN+zWSBXKl/pwdgAg8IpLNSM1/61A=="], - - "conventional-changelog-eslint": ["conventional-changelog-eslint@4.0.0", "", {}, "sha512-nEZ9byP89hIU0dMx37JXQkE1IpMmqKtsaR24X7aM3L6Yy/uAtbb+ogqthuNYJkeO1HyvK7JsX84z8649hvp43Q=="], - - "conventional-changelog-express": ["conventional-changelog-express@3.0.0", "", {}, "sha512-HqxihpUMfIuxvlPvC6HltA4ZktQEUan/v3XQ77+/zbu8No/fqK3rxSZaYeHYant7zRxQNIIli7S+qLS9tX9zQA=="], - - "conventional-changelog-jquery": ["conventional-changelog-jquery@4.0.0", "", {}, "sha512-TTIN5CyzRMf8PUwyy4IOLmLV2DFmPtasKN+x7EQKzwSX8086XYwo+NeaeA3VUT8bvKaIy5z/JoWUvi7huUOgaw=="], - - "conventional-changelog-jshint": ["conventional-changelog-jshint@3.0.0", "", { "dependencies": { "compare-func": "^2.0.0" } }, "sha512-bQof4byF4q+n+dwFRkJ/jGf9dCNUv4/kCDcjeCizBvfF81TeimPZBB6fT4HYbXgxxfxWXNl/i+J6T0nI4by6DA=="], - - "conventional-changelog-preset-loader": ["conventional-changelog-preset-loader@3.0.0", "", {}, "sha512-qy9XbdSLmVnwnvzEisjxdDiLA4OmV3o8db+Zdg4WiFw14fP3B6XNz98X0swPPpkTd/pc1K7+adKgEDM1JCUMiA=="], - - "conventional-changelog-writer": ["conventional-changelog-writer@6.0.1", "", { "dependencies": { "conventional-commits-filter": "^3.0.0", "dateformat": "^3.0.3", "handlebars": "^4.7.7", "json-stringify-safe": "^5.0.1", "meow": "^8.1.2", "semver": "^7.0.0", "split": "^1.0.1" }, "bin": { "conventional-changelog-writer": "cli.js" } }, "sha512-359t9aHorPw+U+nHzUXHS5ZnPBOizRxfQsWT5ZDHBfvfxQOAik+yfuhKXG66CN5LEWPpMNnIMHUTCKeYNprvHQ=="], - - "conventional-commit-types": ["conventional-commit-types@3.0.0", "", {}, "sha512-SmmCYnOniSsAa9GqWOeLqc179lfr5TRu5b4QFDkbsrJ5TZjPJx85wtOr3zn+1dbeNiXDKGPbZ72IKbPhLXh/Lg=="], - - "conventional-commits-filter": ["conventional-commits-filter@3.0.0", "", { "dependencies": { "lodash.ismatch": "^4.4.0", "modify-values": "^1.0.1" } }, "sha512-1ymej8b5LouPx9Ox0Dw/qAO2dVdfpRFq28e5Y0jJEU8ZrLdy0vOSkkIInwmxErFGhg6SALro60ZrwYFVTUDo4Q=="], - - "conventional-commits-parser": ["conventional-commits-parser@5.0.0", "", { "dependencies": { "JSONStream": "^1.3.5", "is-text-path": "^2.0.0", "meow": "^12.0.1", "split2": "^4.0.0" }, "bin": { "conventional-commits-parser": "cli.mjs" } }, "sha512-ZPMl0ZJbw74iS9LuX9YIAiW8pfM5p3yh2o/NbXHbkFuZzY5jvdi5jFycEOkmBW5H5I7nA+D6f3UcsCLP2vvSEA=="], - - "core-util-is": ["core-util-is@1.0.3", "", {}, "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ=="], - - "cosmiconfig": ["cosmiconfig@9.0.0", "", { "dependencies": { "env-paths": "^2.2.1", "import-fresh": "^3.3.0", "js-yaml": "^4.1.0", "parse-json": "^5.2.0" }, "peerDependencies": { "typescript": ">=4.9.5" }, "optionalPeers": ["typescript"] }, "sha512-itvL5h8RETACmOTFc4UfIyB2RfEHi71Ax6E/PivVxq9NseKbOWpeyHEOIbmAw1rs8Ak0VursQNww7lf7YtUwzg=="], - - "cosmiconfig-typescript-loader": ["cosmiconfig-typescript-loader@6.1.0", "", { "dependencies": { "jiti": "^2.4.1" }, "peerDependencies": { "@types/node": "*", "cosmiconfig": ">=9", "typescript": ">=5" } }, "sha512-tJ1w35ZRUiM5FeTzT7DtYWAFFv37ZLqSRkGi2oeCK1gPhvaWjkAtfXvLmvE1pRfxxp9aQo6ba/Pvg1dKj05D4g=="], - - "cz-conventional-changelog": ["cz-conventional-changelog@3.3.0", "", { "dependencies": { "chalk": "^2.4.1", "commitizen": "^4.0.3", "conventional-commit-types": "^3.0.0", "lodash.map": "^4.5.1", "longest": "^2.0.1", "word-wrap": "^1.0.3" }, "optionalDependencies": { "@commitlint/load": ">6.1.1" } }, "sha512-U466fIzU5U22eES5lTNiNbZ+d8dfcHcssH4o7QsdWaCcRs/feIPCxKYSWkYBNs5mny7MvEfwpTLWjvbm94hecw=="], - - "dargs": ["dargs@8.1.0", "", {}, "sha512-wAV9QHOsNbwnWdNW2FYvE1P56wtgSbM+3SZcdGiWQILwVjACCXDCI3Ai8QlCjMDB8YK5zySiXZYBiwGmNY3lnw=="], - - "dateformat": ["dateformat@3.0.3", "", {}, "sha512-jyCETtSl3VMZMWeRo7iY1FL19ges1t55hMo5yaam4Jrsm5EPL89UQkoQRyiI+Yf4k8r2ZpdngkV8hr1lIdjb3Q=="], - - "debug": ["debug@4.4.3", "", { "dependencies": { "ms": "^2.1.3" } }, "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA=="], - - "decamelize": ["decamelize@1.2.0", "", {}, "sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA=="], - - "decamelize-keys": ["decamelize-keys@1.1.1", "", { "dependencies": { "decamelize": "^1.1.0", "map-obj": "^1.0.0" } }, "sha512-WiPxgEirIV0/eIOMcnFBA3/IJZAZqKnwAwWyvvdi4lsr1WCN22nhdf/3db3DoZcUjTV2SqfzIwNyp6y2xs3nmg=="], - - "dedent": ["dedent@0.7.0", "", {}, "sha512-Q6fKUPqnAHAyhiUgFU7BUzLiv0kd8saH9al7tnu5Q/okj6dnupxyTgFIBjVzJATdfIAm9NAsvXNzjaKa+bxVyA=="], - - "defaults": ["defaults@1.0.4", "", { "dependencies": { "clone": "^1.0.2" } }, "sha512-eFuaLoy/Rxalv2kr+lqMlUnrDWV+3j4pljOIJgLIhI058IQfWJ7vXhyEIHu+HtC738klGALYxOKDO0bQP3tg8A=="], - - "detect-file": ["detect-file@1.0.0", "", {}, "sha512-DtCOLG98P007x7wiiOmfI0fi3eIKyWiLTGJ2MDnVi/E04lWGbf+JzrRHMm0rgIIZJGtHpKpbVgLWHrv8xXpc3Q=="], - - "detect-indent": ["detect-indent@6.1.0", "", {}, "sha512-reYkTUJAZb9gUuZ2RvVCNhVHdg62RHnJ7WJl8ftMi4diZ6NWlciOzQN88pUhSELEwflJht4oQDv0F0BMlwaYtA=="], - - "dot-prop": ["dot-prop@5.3.0", "", { "dependencies": { "is-obj": "^2.0.0" } }, "sha512-QM8q3zDe58hqUqjraQOmzZ1LIH9SWQJTlEKCH4kJ2oQvLZk7RbQXvtDM2XEq3fwkV9CCvvH4LA0AV+ogFsBM2Q=="], - - "emoji-regex": ["emoji-regex@8.0.0", "", {}, "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A=="], - - "env-paths": ["env-paths@2.2.1", "", {}, "sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A=="], - - "environment": ["environment@1.1.0", "", {}, "sha512-xUtoPkMggbz0MPyPiIWr1Kp4aeWJjDZ6SMvURhimjdZgsRuDplF5/s9hcgGhyXMhs+6vpnuoiZ2kFiu3FMnS8Q=="], - - "error-ex": ["error-ex@1.3.2", "", { "dependencies": { "is-arrayish": "^0.2.1" } }, "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g=="], - - "escalade": ["escalade@3.2.0", "", {}, "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA=="], - - "escape-string-regexp": ["escape-string-regexp@1.0.5", "", {}, "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg=="], - - "eventemitter3": ["eventemitter3@5.0.1", "", {}, "sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA=="], - - "expand-tilde": ["expand-tilde@2.0.2", "", { "dependencies": { "homedir-polyfill": "^1.0.1" } }, "sha512-A5EmesHW6rfnZ9ysHQjPdJRni0SRar0tjtG5MNtm9n5TUvsYU8oozprtRD4AqHxcZWWlVuAmQo2nWKfN9oyjTw=="], - - "external-editor": ["external-editor@3.1.0", "", { "dependencies": { "chardet": "^0.7.0", "iconv-lite": "^0.4.24", "tmp": "^0.0.33" } }, "sha512-hMQ4CX1p1izmuLYyZqLMO/qGNw10wSv9QDCPfzXfyFrOaCSSoRfqE1Kf1s5an66J5JZC62NewG+mK49jOCtQew=="], - - "fast-deep-equal": ["fast-deep-equal@3.1.3", "", {}, "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q=="], - - "fast-uri": ["fast-uri@3.1.0", "", {}, "sha512-iPeeDKJSWf4IEOasVVrknXpaBV0IApz/gp7S2bb7Z4Lljbl2MGJRqInZiUrQwV16cpzw/D3S5j5Julj/gT52AA=="], - - "figures": ["figures@3.2.0", "", { "dependencies": { "escape-string-regexp": "^1.0.5" } }, "sha512-yaduQFRKLXYOGgEn6AZau90j3ggSOyiqXU0F9JZfeXYhNa+Jk4X+s45A2zg5jns87GAFa34BBm2kXw4XpNcbdg=="], - - "fill-range": ["fill-range@7.1.1", "", { "dependencies": { "to-regex-range": "^5.0.1" } }, "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg=="], - - "find-node-modules": ["find-node-modules@2.1.3", "", { "dependencies": { "findup-sync": "^4.0.0", "merge": "^2.1.1" } }, "sha512-UC2I2+nx1ZuOBclWVNdcnbDR5dlrOdVb7xNjmT/lHE+LsgztWks3dG7boJ37yTS/venXw84B/mAW9uHVoC5QRg=="], - - "find-root": ["find-root@1.1.0", "", {}, "sha512-NKfW6bec6GfKc0SGx1e07QZY9PE99u0Bft/0rzSD5k3sO/vwkVUpDUKVm5Gpp5Ue3YfShPFTX2070tDs5kB9Ng=="], - - "find-up": ["find-up@7.0.0", "", { "dependencies": { "locate-path": "^7.2.0", "path-exists": "^5.0.0", "unicorn-magic": "^0.1.0" } }, "sha512-YyZM99iHrqLKjmt4LJDj58KI+fYyufRLBSYcqycxf//KpBk9FoewoGX0450m9nB44qrZnovzC2oeP5hUibxc/g=="], - - "findup-sync": ["findup-sync@4.0.0", "", { "dependencies": { "detect-file": "^1.0.0", "is-glob": "^4.0.0", "micromatch": "^4.0.2", "resolve-dir": "^1.0.1" } }, "sha512-6jvvn/12IC4quLBL1KNokxC7wWTvYncaVUYSoxWw7YykPLuRrnv4qdHcSOywOI5RpkOVGeQRtWM8/q+G6W6qfQ=="], - - "fs-extra": ["fs-extra@9.1.0", "", { "dependencies": { "at-least-node": "^1.0.0", "graceful-fs": "^4.2.0", "jsonfile": "^6.0.1", "universalify": "^2.0.0" } }, "sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ=="], - - "fs.realpath": ["fs.realpath@1.0.0", "", {}, "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw=="], - - "function-bind": ["function-bind@1.1.2", "", {}, "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA=="], - - "get-caller-file": ["get-caller-file@2.0.5", "", {}, "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg=="], - - "get-east-asian-width": ["get-east-asian-width@1.4.0", "", {}, "sha512-QZjmEOC+IT1uk6Rx0sX22V6uHWVwbdbxf1faPqJ1QhLdGgsRGCZoyaQBm/piRdJy/D2um6hM1UP7ZEeQ4EkP+Q=="], - - "get-pkg-repo": ["get-pkg-repo@4.2.1", "", { "dependencies": { "@hutson/parse-repository-url": "^3.0.0", "hosted-git-info": "^4.0.0", "through2": "^2.0.0", "yargs": "^16.2.0" }, "bin": { "get-pkg-repo": "src/cli.js" } }, "sha512-2+QbHjFRfGB74v/pYWjd5OhU3TDIC2Gv/YKUTk/tCvAz0pkn/Mz6P3uByuBimLOcPvN2jYdScl3xGFSrx0jEcA=="], - - "git-cz": ["git-cz@4.9.0", "", { "bin": { "git-cz": "bin/git-cz.js", "gitcz": "bin/git-cz.js" } }, "sha512-cSRL8IIOXU7UFLdbziCYqg8f8InwLwqHezkiRHNSph7oZqGv0togId1kMTfKil6gzK0VaSXeVBb4oDl0fQCHiw=="], - - "git-raw-commits": ["git-raw-commits@4.0.0", "", { "dependencies": { "dargs": "^8.0.0", "meow": "^12.0.1", "split2": "^4.0.0" }, "bin": { "git-raw-commits": "cli.mjs" } }, "sha512-ICsMM1Wk8xSGMowkOmPrzo2Fgmfo4bMHLNX6ytHjajRJUqvHOw/TFapQ+QG75c3X/tTDDhOSRPGC52dDbNM8FQ=="], - - "git-remote-origin-url": ["git-remote-origin-url@2.0.0", "", { "dependencies": { "gitconfiglocal": "^1.0.0", "pify": "^2.3.0" } }, "sha512-eU+GGrZgccNJcsDH5LkXR3PB9M958hxc7sbA8DFJjrv9j4L2P/eZfKhM+QD6wyzpiv+b1BpK0XrYCxkovtjSLw=="], - - "git-semver-tags": ["git-semver-tags@5.0.1", "", { "dependencies": { "meow": "^8.1.2", "semver": "^7.0.0" }, "bin": { "git-semver-tags": "cli.js" } }, "sha512-hIvOeZwRbQ+7YEUmCkHqo8FOLQZCEn18yevLHADlFPZY02KJGsu5FZt9YW/lybfK2uhWFI7Qg/07LekJiTv7iA=="], - - "gitconfiglocal": ["gitconfiglocal@1.0.0", "", { "dependencies": { "ini": "^1.3.2" } }, "sha512-spLUXeTAVHxDtKsJc8FkFVgFtMdEN9qPGpL23VfSHx4fP4+Ds097IXLvymbnDH8FnmxX5Nr9bPw3A+AQ6mWEaQ=="], - - "glob": ["glob@7.2.3", "", { "dependencies": { "fs.realpath": "^1.0.0", "inflight": "^1.0.4", "inherits": "2", "minimatch": "^3.1.1", "once": "^1.3.0", "path-is-absolute": "^1.0.0" } }, "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q=="], - - "global-directory": ["global-directory@4.0.1", "", { "dependencies": { "ini": "4.1.1" } }, "sha512-wHTUcDUoZ1H5/0iVqEudYW4/kAlN5cZ3j/bXn0Dpbizl9iaUVeWSHqiOjsgk6OW2bkLclbBjzewBz6weQ1zA2Q=="], - - "global-modules": ["global-modules@1.0.0", "", { "dependencies": { "global-prefix": "^1.0.1", "is-windows": "^1.0.1", "resolve-dir": "^1.0.0" } }, "sha512-sKzpEkf11GpOFuw0Zzjzmt4B4UZwjOcG757PPvrfhxcLFbq0wpsgpOqxpxtxFiCG4DtG93M6XRVbF2oGdev7bg=="], - - "global-prefix": ["global-prefix@1.0.2", "", { "dependencies": { "expand-tilde": "^2.0.2", "homedir-polyfill": "^1.0.1", "ini": "^1.3.4", "is-windows": "^1.0.1", "which": "^1.2.14" } }, "sha512-5lsx1NUDHtSjfg0eHlmYvZKv8/nVqX4ckFbM+FrGcQ+04KWcWFo9P5MxPZYSzUvyzmdTbI7Eix8Q4IbELDqzKg=="], - - "graceful-fs": ["graceful-fs@4.2.11", "", {}, "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ=="], - - "handlebars": ["handlebars@4.7.8", "", { "dependencies": { "minimist": "^1.2.5", "neo-async": "^2.6.2", "source-map": "^0.6.1", "wordwrap": "^1.0.0" }, "optionalDependencies": { "uglify-js": "^3.1.4" }, "bin": { "handlebars": "bin/handlebars" } }, "sha512-vafaFqs8MZkRrSX7sFVUdo3ap/eNiLnb4IakshzvP56X5Nr1iGKAIqdX6tMlm6HcNRIkr6AxO5jFEoJzzpT8aQ=="], - - "hard-rejection": ["hard-rejection@2.1.0", "", {}, "sha512-VIZB+ibDhx7ObhAe7OVtoEbuP4h/MuOTHJ+J8h/eBXotJYl0fBgR72xDFCKgIh22OJZIOVNxBMWuhAr10r8HdA=="], - - "has-flag": ["has-flag@3.0.0", "", {}, "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw=="], - - "hasown": ["hasown@2.0.2", "", { "dependencies": { "function-bind": "^1.1.2" } }, "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ=="], - - "homedir-polyfill": ["homedir-polyfill@1.0.3", "", { "dependencies": { "parse-passwd": "^1.0.0" } }, "sha512-eSmmWE5bZTK2Nou4g0AI3zZ9rswp7GRKoKXS1BLUkvPviOqs4YTN1djQIqrXy9k5gEtdLPy86JjRwsNM9tnDcA=="], - - "hosted-git-info": ["hosted-git-info@4.1.0", "", { "dependencies": { "lru-cache": "^6.0.0" } }, "sha512-kyCuEOWjJqZuDbRHzL8V93NzQhwIB71oFWSyzVo+KPZI+pnQPPxucdkrOZvkLRnrf5URsQM+IJ09Dw29cRALIA=="], - - "iconv-lite": ["iconv-lite@0.4.24", "", { "dependencies": { "safer-buffer": ">= 2.1.2 < 3" } }, "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA=="], - - "ieee754": ["ieee754@1.2.1", "", {}, "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA=="], - - "import-fresh": ["import-fresh@3.3.1", "", { "dependencies": { "parent-module": "^1.0.0", "resolve-from": "^4.0.0" } }, "sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ=="], - - "import-meta-resolve": ["import-meta-resolve@4.2.0", "", {}, "sha512-Iqv2fzaTQN28s/FwZAoFq0ZSs/7hMAHJVX+w8PZl3cY19Pxk6jFFalxQoIfW2826i/fDLXv8IiEZRIT0lDuWcg=="], - - "indent-string": ["indent-string@4.0.0", "", {}, "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg=="], - - "inflight": ["inflight@1.0.6", "", { "dependencies": { "once": "^1.3.0", "wrappy": "1" } }, "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA=="], - - "inherits": ["inherits@2.0.4", "", {}, "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ=="], - - "ini": ["ini@4.1.1", "", {}, "sha512-QQnnxNyfvmHFIsj7gkPcYymR8Jdw/o7mp5ZFihxn6h8Ci6fh3Dx4E1gPjpQEpIuPo9XVNY/ZUwh4BPMjGyL01g=="], - - "inquirer": ["inquirer@8.2.5", "", { "dependencies": { "ansi-escapes": "^4.2.1", "chalk": "^4.1.1", "cli-cursor": "^3.1.0", "cli-width": "^3.0.0", "external-editor": "^3.0.3", "figures": "^3.0.0", "lodash": "^4.17.21", "mute-stream": "0.0.8", "ora": "^5.4.1", "run-async": "^2.4.0", "rxjs": "^7.5.5", "string-width": "^4.1.0", "strip-ansi": "^6.0.0", "through": "^2.3.6", "wrap-ansi": "^7.0.0" } }, "sha512-QAgPDQMEgrDssk1XiwwHoOGYF9BAbUcc1+j+FhEvaOt8/cKRqyLn0U5qA6F74fGhTMGxf92pOvPBeh29jQJDTQ=="], - - "is-arrayish": ["is-arrayish@0.2.1", "", {}, "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg=="], - - "is-core-module": ["is-core-module@2.16.1", "", { "dependencies": { "hasown": "^2.0.2" } }, "sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w=="], - - "is-extglob": ["is-extglob@2.1.1", "", {}, "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ=="], - - "is-fullwidth-code-point": ["is-fullwidth-code-point@3.0.0", "", {}, "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg=="], - - "is-glob": ["is-glob@4.0.3", "", { "dependencies": { "is-extglob": "^2.1.1" } }, "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg=="], - - "is-interactive": ["is-interactive@1.0.0", "", {}, "sha512-2HvIEKRoqS62guEC+qBjpvRubdX910WCMuJTZ+I9yvqKU2/12eSL549HMwtabb4oupdj2sMP50k+XJfB/8JE6w=="], - - "is-number": ["is-number@7.0.0", "", {}, "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng=="], - - "is-obj": ["is-obj@2.0.0", "", {}, "sha512-drqDG3cbczxxEJRoOXcOjtdp1J/lyp1mNn0xaznRs8+muBhgQcrnbspox5X5fOw0HnMnbfDzvnEMEtqDEJEo8w=="], - - "is-plain-obj": ["is-plain-obj@1.1.0", "", {}, "sha512-yvkRyxmFKEOQ4pNXCmJG5AEQNlXJS5LaONXo5/cLdTZdWvsZ1ioJEonLGAosKlMWE8lwUy/bJzMjcw8az73+Fg=="], - - "is-text-path": ["is-text-path@2.0.0", "", { "dependencies": { "text-extensions": "^2.0.0" } }, "sha512-+oDTluR6WEjdXEJMnC2z6A4FRwFoYuvShVVEGsS7ewc0UTi2QtAKMDJuL4BDEVt+5T7MjFo12RP8ghOM75oKJw=="], - - "is-unicode-supported": ["is-unicode-supported@0.1.0", "", {}, "sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw=="], - - "is-utf8": ["is-utf8@0.2.1", "", {}, "sha512-rMYPYvCzsXywIsldgLaSoPlw5PfoB/ssr7hY4pLfcodrA5M/eArza1a9VmTiNIBNMjOGr1Ow9mTyU2o69U6U9Q=="], - - "is-windows": ["is-windows@1.0.2", "", {}, "sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA=="], - - "isarray": ["isarray@1.0.0", "", {}, "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ=="], - - "isexe": ["isexe@2.0.0", "", {}, "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw=="], - - "jiti": ["jiti@2.5.1", "", { "bin": { "jiti": "lib/jiti-cli.mjs" } }, "sha512-twQoecYPiVA5K/h6SxtORw/Bs3ar+mLUtoPSc7iMXzQzK8d7eJ/R09wmTwAjiamETn1cXYPGfNnu7DMoHgu12w=="], - - "js-tokens": ["js-tokens@4.0.0", "", {}, "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ=="], - - "js-yaml": ["js-yaml@4.1.0", "", { "dependencies": { "argparse": "^2.0.1" }, "bin": { "js-yaml": "bin/js-yaml.js" } }, "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA=="], - - "json-parse-better-errors": ["json-parse-better-errors@1.0.2", "", {}, "sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw=="], - - "json-parse-even-better-errors": ["json-parse-even-better-errors@2.3.1", "", {}, "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w=="], - - "json-schema-traverse": ["json-schema-traverse@1.0.0", "", {}, "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug=="], - - "json-stringify-safe": ["json-stringify-safe@5.0.1", "", {}, "sha512-ZClg6AaYvamvYEE82d3Iyd3vSSIjQ+odgjaTzRuO3s7toCdFKczob2i0zCh7JE8kWn17yvAWhUVxvqGwUalsRA=="], - - "jsonfile": ["jsonfile@6.2.0", "", { "dependencies": { "universalify": "^2.0.0" }, "optionalDependencies": { "graceful-fs": "^4.1.6" } }, "sha512-FGuPw30AdOIUTRMC2OMRtQV+jkVj2cfPqSeWXv1NEAJ1qZ5zb1X6z1mFhbfOB/iy3ssJCD+3KuZ8r8C3uVFlAg=="], - - "jsonparse": ["jsonparse@1.3.1", "", {}, "sha512-POQXvpdL69+CluYsillJ7SUhKvytYjW9vG/GKpnf+xP8UWgYEM/RaMzHHofbALDiKbbP1W8UEYmgGl39WkPZsg=="], - - "kind-of": ["kind-of@6.0.3", "", {}, "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw=="], - - "lilconfig": ["lilconfig@3.1.3", "", {}, "sha512-/vlFKAoH5Cgt3Ie+JLhRbwOsCQePABiU3tJ1egGvyQ+33R/vcwM2Zl2QR/LzjsBeItPt3oSVXapn+m4nQDvpzw=="], - - "lines-and-columns": ["lines-and-columns@1.2.4", "", {}, "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg=="], - - "lint-staged": ["lint-staged@16.1.6", "", { "dependencies": { "chalk": "^5.6.0", "commander": "^14.0.0", "debug": "^4.4.1", "lilconfig": "^3.1.3", "listr2": "^9.0.3", "micromatch": "^4.0.8", "nano-spawn": "^1.0.2", "pidtree": "^0.6.0", "string-argv": "^0.3.2", "yaml": "^2.8.1" }, "bin": { "lint-staged": "bin/lint-staged.js" } }, "sha512-U4kuulU3CKIytlkLlaHcGgKscNfJPNTiDF2avIUGFCv7K95/DCYQ7Ra62ydeRWmgQGg9zJYw2dzdbztwJlqrow=="], - - "listr2": ["listr2@9.0.3", "", { "dependencies": { "cli-truncate": "^4.0.0", "colorette": "^2.0.20", "eventemitter3": "^5.0.1", "log-update": "^6.1.0", "rfdc": "^1.4.1", "wrap-ansi": "^9.0.0" } }, "sha512-0aeh5HHHgmq1KRdMMDHfhMWQmIT/m7nRDTlxlFqni2Sp0had9baqsjJRvDGdlvgd6NmPE0nPloOipiQJGFtTHQ=="], - - "load-json-file": ["load-json-file@4.0.0", "", { "dependencies": { "graceful-fs": "^4.1.2", "parse-json": "^4.0.0", "pify": "^3.0.0", "strip-bom": "^3.0.0" } }, "sha512-Kx8hMakjX03tiGTLAIdJ+lL0htKnXjEZN6hk/tozf/WOuYGdZBJrZ+rCJRbVCugsjB3jMLn9746NsQIf5VjBMw=="], - - "locate-path": ["locate-path@7.2.0", "", { "dependencies": { "p-locate": "^6.0.0" } }, "sha512-gvVijfZvn7R+2qyPX8mAuKcFGDf6Nc61GdvGafQsHL0sBIxfKzA+usWn4GFC/bk+QdwPUD4kWFJLhElipq+0VA=="], - - "lodash": ["lodash@4.17.21", "", {}, "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg=="], - - "lodash.camelcase": ["lodash.camelcase@4.3.0", "", {}, "sha512-TwuEnCnxbc3rAvhf/LbG7tJUDzhqXyFnv3dtzLOPgCG/hODL7WFnsbwktkD7yUV0RrreP/l1PALq/YSg6VvjlA=="], - - "lodash.ismatch": ["lodash.ismatch@4.4.0", "", {}, "sha512-fPMfXjGQEV9Xsq/8MTSgUf255gawYRbjwMyDbcvDhXgV7enSZA0hynz6vMPnpAb5iONEzBHBPsT+0zes5Z301g=="], - - "lodash.isplainobject": ["lodash.isplainobject@4.0.6", "", {}, "sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA=="], - - "lodash.kebabcase": ["lodash.kebabcase@4.1.1", "", {}, "sha512-N8XRTIMMqqDgSy4VLKPnJ/+hpGZN+PHQiJnSenYqPaVV/NCqEogTnAdZLQiGKhxX+JCs8waWq2t1XHWKOmlY8g=="], - - "lodash.map": ["lodash.map@4.6.0", "", {}, "sha512-worNHGKLDetmcEYDvh2stPCrrQRkP20E4l0iIS7F8EvzMqBBi7ltvFN5m1HvTf1P7Jk1txKhvFcmYsCr8O2F1Q=="], - - "lodash.merge": ["lodash.merge@4.6.2", "", {}, "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ=="], - - "lodash.mergewith": ["lodash.mergewith@4.6.2", "", {}, "sha512-GK3g5RPZWTRSeLSpgP8Xhra+pnjBC56q9FZYe1d5RN3TJ35dbkGy3YqBSMbyCrlbi+CM9Z3Jk5yTL7RCsqboyQ=="], - - "lodash.snakecase": ["lodash.snakecase@4.1.1", "", {}, "sha512-QZ1d4xoBHYUeuouhEq3lk3Uq7ldgyFXGBhg04+oRLnIz8o9T65Eh+8YdroUwn846zchkA9yDsDl5CVVaV2nqYw=="], - - "lodash.startcase": ["lodash.startcase@4.4.0", "", {}, "sha512-+WKqsK294HMSc2jEbNgpHpd0JfIBhp7rEV4aqXWqFr6AlXov+SlcgB1Fv01y2kGe3Gc8nMW7VA0SrGuSkRfIEg=="], - - "lodash.uniq": ["lodash.uniq@4.5.0", "", {}, "sha512-xfBaXQd9ryd9dlSDvnvI0lvxfLJlYAZzXomUYzLKtUeOQvOP5piqAWuGtrhWeqaXK9hhoM/iyJc5AV+XfsX3HQ=="], - - "lodash.upperfirst": ["lodash.upperfirst@4.3.1", "", {}, "sha512-sReKOYJIJf74dhJONhU4e0/shzi1trVbSWDOhKYE5XV2O+H7Sb2Dihwuc7xWxVl+DgFPyTqIN3zMfT9cq5iWDg=="], - - "log-symbols": ["log-symbols@4.1.0", "", { "dependencies": { "chalk": "^4.1.0", "is-unicode-supported": "^0.1.0" } }, "sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg=="], - - "log-update": ["log-update@6.1.0", "", { "dependencies": { "ansi-escapes": "^7.0.0", "cli-cursor": "^5.0.0", "slice-ansi": "^7.1.0", "strip-ansi": "^7.1.0", "wrap-ansi": "^9.0.0" } }, "sha512-9ie8ItPR6tjY5uYJh8K/Zrv/RMZ5VOlOWvtZdEHYSTFKZfIBPQa9tOAEeAWhd+AnIneLJ22w5fjOYtoutpWq5w=="], - - "longest": ["longest@2.0.1", "", {}, "sha512-Ajzxb8CM6WAnFjgiloPsI3bF+WCxcvhdIG3KNA2KN962+tdBsHcuQ4k4qX/EcS/2CRkcc0iAkR956Nib6aXU/Q=="], - - "lru-cache": ["lru-cache@6.0.0", "", { "dependencies": { "yallist": "^4.0.0" } }, "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA=="], - - "map-obj": ["map-obj@4.3.0", "", {}, "sha512-hdN1wVrZbb29eBGiGjJbeP8JbKjq1urkHJ/LIP/NY48MZ1QVXUsQBV1G1zvYFHn1XE06cwjBsOI2K3Ulnj1YXQ=="], - - "meow": ["meow@8.1.2", "", { "dependencies": { "@types/minimist": "^1.2.0", "camelcase-keys": "^6.2.2", "decamelize-keys": "^1.1.0", "hard-rejection": "^2.1.0", "minimist-options": "4.1.0", "normalize-package-data": "^3.0.0", "read-pkg-up": "^7.0.1", "redent": "^3.0.0", "trim-newlines": "^3.0.0", "type-fest": "^0.18.0", "yargs-parser": "^20.2.3" } }, "sha512-r85E3NdZ+mpYk1C6RjPFEMSE+s1iZMuHtsHAqY0DT3jZczl0diWUZ8g6oU7h0M9cD2EL+PzaYghhCLzR0ZNn5Q=="], - - "merge": ["merge@2.1.1", "", {}, "sha512-jz+Cfrg9GWOZbQAnDQ4hlVnQky+341Yk5ru8bZSe6sIDTCIg8n9i/u7hSQGSVOF3C7lH6mGtqjkiT9G4wFLL0w=="], - - "micromatch": ["micromatch@4.0.8", "", { "dependencies": { "braces": "^3.0.3", "picomatch": "^2.3.1" } }, "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA=="], - - "mimic-fn": ["mimic-fn@2.1.0", "", {}, "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg=="], - - "mimic-function": ["mimic-function@5.0.1", "", {}, "sha512-VP79XUPxV2CigYP3jWwAUFSku2aKqBH7uTAapFWCBqutsbmDo96KY5o8uh6U+/YSIn5OxJnXp73beVkpqMIGhA=="], - - "min-indent": ["min-indent@1.0.1", "", {}, "sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg=="], - - "minimatch": ["minimatch@3.1.2", "", { "dependencies": { "brace-expansion": "^1.1.7" } }, "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw=="], - - "minimist": ["minimist@1.2.7", "", {}, "sha512-bzfL1YUZsP41gmu/qjrEk0Q6i2ix/cVeAhbCbqH9u3zYutS1cLg00qhrD0M2MVdCcx4Sc0UpP2eBWo9rotpq6g=="], - - "minimist-options": ["minimist-options@4.1.0", "", { "dependencies": { "arrify": "^1.0.1", "is-plain-obj": "^1.1.0", "kind-of": "^6.0.3" } }, "sha512-Q4r8ghd80yhO/0j1O3B2BjweX3fiHg9cdOwjJd2J76Q135c+NDxGCqdYKQ1SKBuFfgWbAUzBfvYjPUEeNgqN1A=="], - - "modify-values": ["modify-values@1.0.1", "", {}, "sha512-xV2bxeN6F7oYjZWTe/YPAy6MN2M+sL4u/Rlm2AHCIVGfo2p1yGmBHQ6vHehl4bRTZBdHu3TSkWdYgkwpYzAGSw=="], - - "ms": ["ms@2.1.3", "", {}, "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="], - - "mute-stream": ["mute-stream@0.0.8", "", {}, "sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA=="], - - "nano-spawn": ["nano-spawn@1.0.3", "", {}, "sha512-jtpsQDetTnvS2Ts1fiRdci5rx0VYws5jGyC+4IYOTnIQ/wwdf6JdomlHBwqC3bJYOvaKu0C2GSZ1A60anrYpaA=="], - - "neo-async": ["neo-async@2.6.2", "", {}, "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw=="], - - "normalize-package-data": ["normalize-package-data@3.0.3", "", { "dependencies": { "hosted-git-info": "^4.0.1", "is-core-module": "^2.5.0", "semver": "^7.3.4", "validate-npm-package-license": "^3.0.1" } }, "sha512-p2W1sgqij3zMMyRC067Dg16bfzVH+w7hyegmpIvZ4JNjqtGOVAIvLmjBx3yP7YTe9vKJgkoNOPjwQGogDoMXFA=="], - - "once": ["once@1.4.0", "", { "dependencies": { "wrappy": "1" } }, "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w=="], - - "onetime": ["onetime@5.1.2", "", { "dependencies": { "mimic-fn": "^2.1.0" } }, "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg=="], - - "ora": ["ora@5.4.1", "", { "dependencies": { "bl": "^4.1.0", "chalk": "^4.1.0", "cli-cursor": "^3.1.0", "cli-spinners": "^2.5.0", "is-interactive": "^1.0.0", "is-unicode-supported": "^0.1.0", "log-symbols": "^4.1.0", "strip-ansi": "^6.0.0", "wcwidth": "^1.0.1" } }, "sha512-5b6Y85tPxZZ7QytO+BQzysW31HJku27cRIlkbAXaNx+BdcVi+LlRFmVXzeF6a7JCwJpyw5c4b+YSVImQIrBpuQ=="], - - "os-tmpdir": ["os-tmpdir@1.0.2", "", {}, "sha512-D2FR03Vir7FIu45XBY20mTb+/ZSWB00sjU9jdQXt83gDrI4Ztz5Fs7/yy74g2N5SVQY4xY1qDr4rNddwYRVX0g=="], - - "p-limit": ["p-limit@4.0.0", "", { "dependencies": { "yocto-queue": "^1.0.0" } }, "sha512-5b0R4txpzjPWVw/cXXUResoD4hb6U/x9BH08L7nw+GN1sezDzPdxeRvpc9c433fZhBan/wusjbCsqwqm4EIBIQ=="], - - "p-locate": ["p-locate@6.0.0", "", { "dependencies": { "p-limit": "^4.0.0" } }, "sha512-wPrq66Llhl7/4AGC6I+cqxT07LhXvWL08LNXz1fENOw0Ap4sRZZ/gZpTTJ5jpurzzzfS2W/Ge9BY3LgLjCShcw=="], - - "p-try": ["p-try@2.2.0", "", {}, "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ=="], - - "parent-module": ["parent-module@1.0.1", "", { "dependencies": { "callsites": "^3.0.0" } }, "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g=="], - - "parse-json": ["parse-json@5.2.0", "", { "dependencies": { "@babel/code-frame": "^7.0.0", "error-ex": "^1.3.1", "json-parse-even-better-errors": "^2.3.0", "lines-and-columns": "^1.1.6" } }, "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg=="], - - "parse-passwd": ["parse-passwd@1.0.0", "", {}, "sha512-1Y1A//QUXEZK7YKz+rD9WydcE1+EuPr6ZBgKecAB8tmoW6UFv0NREVJe1p+jRxtThkcbbKkfwIbWJe/IeE6m2Q=="], - - "path-exists": ["path-exists@5.0.0", "", {}, "sha512-RjhtfwJOxzcFmNOi6ltcbcu4Iu+FL3zEj83dk4kAS+fVpTxXLO1b38RvJgT/0QwvV/L3aY9TAnyv0EOqW4GoMQ=="], - - "path-is-absolute": ["path-is-absolute@1.0.1", "", {}, "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg=="], - - "path-parse": ["path-parse@1.0.7", "", {}, "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw=="], - - "path-type": ["path-type@3.0.0", "", { "dependencies": { "pify": "^3.0.0" } }, "sha512-T2ZUsdZFHgA3u4e5PfPbjd7HDDpxPnQb5jN0SrDsjNSuVXHJqtwTnWqG0B1jZrgmJ/7lj1EmVIByWt1gxGkWvg=="], - - "picocolors": ["picocolors@1.1.1", "", {}, "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA=="], - - "picomatch": ["picomatch@2.3.1", "", {}, "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA=="], - - "pidtree": ["pidtree@0.6.0", "", { "bin": { "pidtree": "bin/pidtree.js" } }, "sha512-eG2dWTVw5bzqGRztnHExczNxt5VGsE6OwTeCG3fdUf9KBsZzO3R5OIIIzWR+iZA0NtZ+RDVdaoE2dK1cn6jH4g=="], - - "pify": ["pify@2.3.0", "", {}, "sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog=="], - - "process-nextick-args": ["process-nextick-args@2.0.1", "", {}, "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag=="], - - "quick-lru": ["quick-lru@4.0.1", "", {}, "sha512-ARhCpm70fzdcvNQfPoy49IaanKkTlRWF2JMzqhcJbhSFRZv7nPTvZJdcY7301IPmvW+/p0RgIWnQDLJxifsQ7g=="], - - "read-pkg": ["read-pkg@3.0.0", "", { "dependencies": { "load-json-file": "^4.0.0", "normalize-package-data": "^2.3.2", "path-type": "^3.0.0" } }, "sha512-BLq/cCO9two+lBgiTYNqD6GdtK8s4NpaWrl6/rCO9w0TUS8oJl7cmToOZfRYllKTISY6nt1U7jQ53brmKqY6BA=="], - - "read-pkg-up": ["read-pkg-up@7.0.1", "", { "dependencies": { "find-up": "^4.1.0", "read-pkg": "^5.2.0", "type-fest": "^0.8.1" } }, "sha512-zK0TB7Xd6JpCLmlLmufqykGE+/TlOePD6qKClNW7hHDKFh/J7/7gCWGR7joEQEW1bKq3a3yUZSObOoWLFQ4ohg=="], - - "readable-stream": ["readable-stream@3.6.2", "", { "dependencies": { "inherits": "^2.0.3", "string_decoder": "^1.1.1", "util-deprecate": "^1.0.1" } }, "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA=="], - - "redent": ["redent@3.0.0", "", { "dependencies": { "indent-string": "^4.0.0", "strip-indent": "^3.0.0" } }, "sha512-6tDA8g98We0zd0GvVeMT9arEOnTw9qM03L9cJXaCjrip1OO764RDBLBfrB4cwzNGDj5OA5ioymC9GkizgWJDUg=="], - - "require-directory": ["require-directory@2.1.1", "", {}, "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q=="], - - "require-from-string": ["require-from-string@2.0.2", "", {}, "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw=="], - - "resolve": ["resolve@1.22.10", "", { "dependencies": { "is-core-module": "^2.16.0", "path-parse": "^1.0.7", "supports-preserve-symlinks-flag": "^1.0.0" }, "bin": { "resolve": "bin/resolve" } }, "sha512-NPRy+/ncIMeDlTAsuqwKIiferiawhefFJtkNSW0qZJEqMEb+qBt/77B/jGeeek+F0uOeN05CDa6HXbbIgtVX4w=="], - - "resolve-dir": ["resolve-dir@1.0.1", "", { "dependencies": { "expand-tilde": "^2.0.0", "global-modules": "^1.0.0" } }, "sha512-R7uiTjECzvOsWSfdM0QKFNBVFcK27aHOUwdvK53BcW8zqnGdYp0Fbj82cy54+2A4P2tFM22J5kRfe1R+lM/1yg=="], - - "resolve-from": ["resolve-from@5.0.0", "", {}, "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw=="], - - "restore-cursor": ["restore-cursor@3.1.0", "", { "dependencies": { "onetime": "^5.1.0", "signal-exit": "^3.0.2" } }, "sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA=="], - - "rfdc": ["rfdc@1.4.1", "", {}, "sha512-q1b3N5QkRUWUl7iyylaaj3kOpIT0N2i9MqIEQXP73GVsN9cw3fdx8X63cEmWhJGi2PPCF23Ijp7ktmd39rawIA=="], - - "run-async": ["run-async@2.4.1", "", {}, "sha512-tvVnVv01b8c1RrA6Ep7JkStj85Guv/YrMcwqYQnwjsAS2cTmmPGBBjAjpCW7RrSodNSoE2/qg9O4bceNvUuDgQ=="], - - "rxjs": ["rxjs@7.8.2", "", { "dependencies": { "tslib": "^2.1.0" } }, "sha512-dhKf903U/PQZY6boNNtAGdWbG85WAbjT/1xYoZIC7FAY0yWapOBQVsVrDl58W86//e1VpMNBtRV4MaXfdMySFA=="], - - "safe-buffer": ["safe-buffer@5.2.1", "", {}, "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ=="], - - "safer-buffer": ["safer-buffer@2.1.2", "", {}, "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg=="], - - "semver": ["semver@7.7.2", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA=="], - - "signal-exit": ["signal-exit@3.0.7", "", {}, "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ=="], - - "slice-ansi": ["slice-ansi@5.0.0", "", { "dependencies": { "ansi-styles": "^6.0.0", "is-fullwidth-code-point": "^4.0.0" } }, "sha512-FC+lgizVPfie0kkhqUScwRu1O/lF6NOgJmlCgK+/LYxDCTk8sGelYaHDhFcDN+Sn3Cv+3VSa4Byeo+IMCzpMgQ=="], - - "source-map": ["source-map@0.6.1", "", {}, "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g=="], - - "spdx-correct": ["spdx-correct@3.2.0", "", { "dependencies": { "spdx-expression-parse": "^3.0.0", "spdx-license-ids": "^3.0.0" } }, "sha512-kN9dJbvnySHULIluDHy32WHRUu3Og7B9sbY7tsFLctQkIqnMh3hErYgdMjTYuqmcXX+lK5T1lnUt3G7zNswmZA=="], - - "spdx-exceptions": ["spdx-exceptions@2.5.0", "", {}, "sha512-PiU42r+xO4UbUS1buo3LPJkjlO7430Xn5SVAhdpzzsPHsjbYVflnnFdATgabnLude+Cqu25p6N+g2lw/PFsa4w=="], - - "spdx-expression-parse": ["spdx-expression-parse@3.0.1", "", { "dependencies": { "spdx-exceptions": "^2.1.0", "spdx-license-ids": "^3.0.0" } }, "sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q=="], - - "spdx-license-ids": ["spdx-license-ids@3.0.22", "", {}, "sha512-4PRT4nh1EImPbt2jASOKHX7PB7I+e4IWNLvkKFDxNhJlfjbYlleYQh285Z/3mPTHSAK/AvdMmw5BNNuYH8ShgQ=="], - - "split": ["split@1.0.1", "", { "dependencies": { "through": "2" } }, "sha512-mTyOoPbrivtXnwnIxZRFYRrPNtEFKlpB2fvjSnCQUiAA6qAZzqwna5envK4uk6OIeP17CsdF3rSBGYVBsU0Tkg=="], - - "split2": ["split2@4.2.0", "", {}, "sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg=="], - - "string-argv": ["string-argv@0.3.2", "", {}, "sha512-aqD2Q0144Z+/RqG52NeHEkZauTAUWJO8c6yTftGJKO3Tja5tUgIfmIl6kExvhtxSDP7fXB6DvzkfMpCd/F3G+Q=="], - - "string-width": ["string-width@4.2.3", "", { "dependencies": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", "strip-ansi": "^6.0.1" } }, "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g=="], - - "string_decoder": ["string_decoder@1.3.0", "", { "dependencies": { "safe-buffer": "~5.2.0" } }, "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA=="], - - "strip-ansi": ["strip-ansi@6.0.1", "", { "dependencies": { "ansi-regex": "^5.0.1" } }, "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A=="], - - "strip-bom": ["strip-bom@4.0.0", "", {}, "sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w=="], - - "strip-indent": ["strip-indent@3.0.0", "", { "dependencies": { "min-indent": "^1.0.0" } }, "sha512-laJTa3Jb+VQpaC6DseHhF7dXVqHTfJPCRDaEbid/drOhgitgYku/letMUqOXFoWV0zIIUbjpdH2t+tYj4bQMRQ=="], - - "strip-json-comments": ["strip-json-comments@3.1.1", "", {}, "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig=="], - - "supports-color": ["supports-color@5.5.0", "", { "dependencies": { "has-flag": "^3.0.0" } }, "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow=="], - - "supports-preserve-symlinks-flag": ["supports-preserve-symlinks-flag@1.0.0", "", {}, "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w=="], - - "temp-dir": ["temp-dir@2.0.0", "", {}, "sha512-aoBAniQmmwtcKp/7BzsH8Cxzv8OL736p7v1ihGb5e9DJ9kTwGWHrQrVB5+lfVDzfGrdRzXch+ig7LHaY1JTOrg=="], - - "tempfile": ["tempfile@3.0.0", "", { "dependencies": { "temp-dir": "^2.0.0", "uuid": "^3.3.2" } }, "sha512-uNFCg478XovRi85iD42egu+eSFUmmka750Jy7L5tfHI5hQKKtbPnxaSaXAbBqCDYrw3wx4tXjKwci4/QmsZJxw=="], - - "text-extensions": ["text-extensions@2.4.0", "", {}, "sha512-te/NtwBwfiNRLf9Ijqx3T0nlqZiQ2XrrtBvu+cLL8ZRrGkO0NHTug8MYFKyoSrv/sHTaSKfilUkizV6XhxMJ3g=="], - - "through": ["through@2.3.8", "", {}, "sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg=="], - - "through2": ["through2@2.0.5", "", { "dependencies": { "readable-stream": "~2.3.6", "xtend": "~4.0.1" } }, "sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ=="], - - "tinyexec": ["tinyexec@1.0.1", "", {}, "sha512-5uC6DDlmeqiOwCPmK9jMSdOuZTh8bU39Ys6yidB+UTt5hfZUPGAypSgFRiEp+jbi9qH40BLDvy85jIU88wKSqw=="], - - "tmp": ["tmp@0.0.33", "", { "dependencies": { "os-tmpdir": "~1.0.2" } }, "sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw=="], - - "to-regex-range": ["to-regex-range@5.0.1", "", { "dependencies": { "is-number": "^7.0.0" } }, "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ=="], - - "trim-newlines": ["trim-newlines@3.0.1", "", {}, "sha512-c1PTsA3tYrIsLGkJkzHF+w9F2EyxfXGo4UyJc4pFL++FMjnq0HJS69T3M7d//gKrFKwy429bouPescbjecU+Zw=="], - - "tslib": ["tslib@2.8.1", "", {}, "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="], - - "type-fest": ["type-fest@0.18.1", "", {}, "sha512-OIAYXk8+ISY+qTOwkHtKqzAuxchoMiD9Udx+FSGQDuiRR+PJKJHc2NJAXlbhkGwTt/4/nKZxELY1w3ReWOL8mw=="], - - "typescript": ["typescript@5.9.2", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-CWBzXQrc/qOkhidw1OzBTQuYRbfyxDXJMVJ1XNwUHGROVmuaeiEm3OslpZ1RV96d7SKKjZKrSJu3+t/xlw3R9A=="], - - "uglify-js": ["uglify-js@3.19.3", "", { "bin": { "uglifyjs": "bin/uglifyjs" } }, "sha512-v3Xu+yuwBXisp6QYTcH4UbH+xYJXqnq2m/LtQVWKWzYc1iehYnLixoQDN9FH6/j9/oybfd6W9Ghwkl8+UMKTKQ=="], - - "undici-types": ["undici-types@7.11.0", "", {}, "sha512-kt1ZriHTi7MU+Z/r9DOdAI3ONdaR3M3csEaRc6ewa4f4dTvX4cQCbJ4NkEn0ohE4hHtq85+PhPSTY+pO/1PwgA=="], - - "unicorn-magic": ["unicorn-magic@0.1.0", "", {}, "sha512-lRfVq8fE8gz6QMBuDM6a+LO3IAzTi05H6gCVaUpir2E1Rwpo4ZUog45KpNXKC/Mn3Yb9UDuHumeFTo9iV/D9FQ=="], - - "universalify": ["universalify@2.0.1", "", {}, "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw=="], - - "util-deprecate": ["util-deprecate@1.0.2", "", {}, "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw=="], - - "uuid": ["uuid@3.4.0", "", { "bin": { "uuid": "./bin/uuid" } }, "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A=="], - - "validate-npm-package-license": ["validate-npm-package-license@3.0.4", "", { "dependencies": { "spdx-correct": "^3.0.0", "spdx-expression-parse": "^3.0.0" } }, "sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew=="], - - "wcwidth": ["wcwidth@1.0.1", "", { "dependencies": { "defaults": "^1.0.3" } }, "sha512-XHPEwS0q6TaxcvG85+8EYkbiCux2XtWG2mkc47Ng2A77BQu9+DqIOJldST4HgPkuea7dvKSj5VgX3P1d4rW8Tg=="], - - "which": ["which@1.3.1", "", { "dependencies": { "isexe": "^2.0.0" }, "bin": { "which": "./bin/which" } }, "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ=="], - - "word-wrap": ["word-wrap@1.2.5", "", {}, "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA=="], - - "wordwrap": ["wordwrap@1.0.0", "", {}, "sha512-gvVzJFlPycKc5dZN4yPkP8w7Dc37BtP1yczEneOb4uq34pXZcvrtRTmWV8W+Ume+XCxKgbjM+nevkyFPMybd4Q=="], - - "wrap-ansi": ["wrap-ansi@7.0.0", "", { "dependencies": { "ansi-styles": "^4.0.0", "string-width": "^4.1.0", "strip-ansi": "^6.0.0" } }, "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q=="], - - "wrappy": ["wrappy@1.0.2", "", {}, "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ=="], - - "xtend": ["xtend@4.0.2", "", {}, "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ=="], - - "y18n": ["y18n@5.0.8", "", {}, "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA=="], - - "yallist": ["yallist@4.0.0", "", {}, "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A=="], - - "yaml": ["yaml@2.8.1", "", { "bin": { "yaml": "bin.mjs" } }, "sha512-lcYcMxX2PO9XMGvAJkJ3OsNMw+/7FKes7/hgerGUYWIoWu5j/+YQqcZr5JnPZWzOsEBgMbSbiSTn/dv/69Mkpw=="], - - "yargs": ["yargs@17.7.2", "", { "dependencies": { "cliui": "^8.0.1", "escalade": "^3.1.1", "get-caller-file": "^2.0.5", "require-directory": "^2.1.1", "string-width": "^4.2.3", "y18n": "^5.0.5", "yargs-parser": "^21.1.1" } }, "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w=="], - - "yargs-parser": ["yargs-parser@21.1.1", "", {}, "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw=="], - - "yocto-queue": ["yocto-queue@1.2.1", "", {}, "sha512-AyeEbWOu/TAXdxlV9wmGcR0+yh2j3vYPGOECcIj2S7MkrLyC7ne+oye2BKTItt0ii2PHk4cDy+95+LshzbXnGg=="], - - "@commitlint/parse/conventional-changelog-angular": ["conventional-changelog-angular@7.0.0", "", { "dependencies": { "compare-func": "^2.0.0" } }, "sha512-ROjNchA9LgfNMTTFSIWPzebCwOGFdgkEq45EnvvrmSLvCtAw0HSmrCs7/ty+wAeYUZyNay0YMUNYFTRL72PkBQ=="], - - "@commitlint/read/minimist": ["minimist@1.2.8", "", {}, "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA=="], - - "ansi-escapes/type-fest": ["type-fest@0.21.3", "", {}, "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w=="], - - "cli-truncate/string-width": ["string-width@7.2.0", "", { "dependencies": { "emoji-regex": "^10.3.0", "get-east-asian-width": "^1.0.0", "strip-ansi": "^7.1.0" } }, "sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ=="], - - "conventional-changelog/conventional-changelog-conventionalcommits": ["conventional-changelog-conventionalcommits@6.1.0", "", { "dependencies": { "compare-func": "^2.0.0" } }, "sha512-3cS3GEtR78zTfMzk0AizXKKIdN4OvSh7ibNz6/DPbhWWQu7LqE/8+/GqSodV+sywUR2gpJAdP/1JFf4XtN7Zpw=="], - - "conventional-changelog-core/conventional-commits-parser": ["conventional-commits-parser@4.0.0", "", { "dependencies": { "JSONStream": "^1.3.5", "is-text-path": "^1.0.1", "meow": "^8.1.2", "split2": "^3.2.2" }, "bin": { "conventional-commits-parser": "cli.js" } }, "sha512-WRv5j1FsVM5FISJkoYMR6tPk07fkKT0UodruX4je86V4owk451yjXAKzKAPOs9l7y59E2viHUS9eQ+dfUA9NSg=="], - - "conventional-changelog-core/git-raw-commits": ["git-raw-commits@3.0.0", "", { "dependencies": { "dargs": "^7.0.0", "meow": "^8.1.2", "split2": "^3.2.2" }, "bin": { "git-raw-commits": "cli.js" } }, "sha512-b5OHmZ3vAgGrDn/X0kS+9qCfNKWe4K/jFnhwzVWWg0/k5eLa3060tZShrRg8Dja5kPc+YjS0Gc6y7cRr44Lpjw=="], - - "conventional-changelog-core/read-pkg-up": ["read-pkg-up@3.0.0", "", { "dependencies": { "find-up": "^2.0.0", "read-pkg": "^3.0.0" } }, "sha512-YFzFrVvpC6frF1sz8psoHDBGF7fLPc+llq/8NB43oagqWkx8ar5zYtsTORtOjw9W2RHLpWP+zTWwBvf1bCmcSw=="], - - "conventional-commits-parser/meow": ["meow@12.1.1", "", {}, "sha512-BhXM0Au22RwUneMPwSCnyhTOizdWoIEPU9sp0Aqa1PnDMR5Wv2FGXYDjuzJEIX+Eo2Rb8xuYe5jrnm5QowQFkw=="], - - "cz-conventional-changelog/chalk": ["chalk@2.4.2", "", { "dependencies": { "ansi-styles": "^3.2.1", "escape-string-regexp": "^1.0.5", "supports-color": "^5.3.0" } }, "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ=="], - - "decamelize-keys/map-obj": ["map-obj@1.0.1", "", {}, "sha512-7N/q3lyZ+LVCp7PzuxrJr4KMbBE2hW7BT7YNia330OFxIf4d3r5zVpicP2650l7CPN6RM9zOJRl3NGpqSiw3Eg=="], - - "get-pkg-repo/yargs": ["yargs@16.2.0", "", { "dependencies": { "cliui": "^7.0.2", "escalade": "^3.1.1", "get-caller-file": "^2.0.5", "require-directory": "^2.1.1", "string-width": "^4.2.0", "y18n": "^5.0.5", "yargs-parser": "^20.2.2" } }, "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw=="], - - "git-raw-commits/meow": ["meow@12.1.1", "", {}, "sha512-BhXM0Au22RwUneMPwSCnyhTOizdWoIEPU9sp0Aqa1PnDMR5Wv2FGXYDjuzJEIX+Eo2Rb8xuYe5jrnm5QowQFkw=="], - - "gitconfiglocal/ini": ["ini@1.3.8", "", {}, "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew=="], - - "global-prefix/ini": ["ini@1.3.8", "", {}, "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew=="], - - "handlebars/minimist": ["minimist@1.2.8", "", {}, "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA=="], - - "import-fresh/resolve-from": ["resolve-from@4.0.0", "", {}, "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g=="], - - "inquirer/chalk": ["chalk@4.1.2", "", { "dependencies": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" } }, "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA=="], - - "listr2/wrap-ansi": ["wrap-ansi@9.0.2", "", { "dependencies": { "ansi-styles": "^6.2.1", "string-width": "^7.0.0", "strip-ansi": "^7.1.0" } }, "sha512-42AtmgqjV+X1VpdOfyTGOYRi0/zsoLqtXQckTmqTeybT+BDIbM/Guxo7x3pE2vtpr1ok6xRqM9OpBe+Jyoqyww=="], - - "load-json-file/parse-json": ["parse-json@4.0.0", "", { "dependencies": { "error-ex": "^1.3.1", "json-parse-better-errors": "^1.0.1" } }, "sha512-aOIos8bujGN93/8Ox/jPLh7RwVnPEysynVFE+fQZyg6jKELEHwzgKdLRFHUgXJL6kylijVSBC4BvN9OmsB48Rw=="], - - "load-json-file/pify": ["pify@3.0.0", "", {}, "sha512-C3FsVNH1udSEX48gGX1xfvwTWfsYWj5U+8/uK15BGzIGrKoUpghX8hWZwa/OFnakBiiVNmBvemTJR5mcy7iPcg=="], - - "load-json-file/strip-bom": ["strip-bom@3.0.0", "", {}, "sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA=="], - - "log-symbols/chalk": ["chalk@4.1.2", "", { "dependencies": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" } }, "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA=="], - - "log-update/ansi-escapes": ["ansi-escapes@7.1.0", "", { "dependencies": { "environment": "^1.0.0" } }, "sha512-YdhtCd19sKRKfAAUsrcC1wzm4JuzJoiX4pOJqIoW2qmKj5WzG/dL8uUJ0361zaXtHqK7gEhOwtAtz7t3Yq3X5g=="], - - "log-update/cli-cursor": ["cli-cursor@5.0.0", "", { "dependencies": { "restore-cursor": "^5.0.0" } }, "sha512-aCj4O5wKyszjMmDT4tZj93kxyydN/K5zPWSCe6/0AV/AA1pqe5ZBIw0a2ZfPQV7lL5/yb5HsUreJ6UFAF1tEQw=="], - - "log-update/slice-ansi": ["slice-ansi@7.1.2", "", { "dependencies": { "ansi-styles": "^6.2.1", "is-fullwidth-code-point": "^5.0.0" } }, "sha512-iOBWFgUX7caIZiuutICxVgX1SdxwAVFFKwt1EvMYYec/NWO5meOJ6K5uQxhrYBdQJne4KxiqZc+KptFOWFSI9w=="], - - "log-update/strip-ansi": ["strip-ansi@7.1.2", "", { "dependencies": { "ansi-regex": "^6.0.1" } }, "sha512-gmBGslpoQJtgnMAvOVqGZpEz9dyoKTCzy2nfz/n8aIFhN/jCE/rCmcxabB6jOOHV+0WNnylOxaxBQPSvcWklhA=="], - - "log-update/wrap-ansi": ["wrap-ansi@9.0.2", "", { "dependencies": { "ansi-styles": "^6.2.1", "string-width": "^7.0.0", "strip-ansi": "^7.1.0" } }, "sha512-42AtmgqjV+X1VpdOfyTGOYRi0/zsoLqtXQckTmqTeybT+BDIbM/Guxo7x3pE2vtpr1ok6xRqM9OpBe+Jyoqyww=="], - - "meow/yargs-parser": ["yargs-parser@20.2.9", "", {}, "sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w=="], - - "ora/chalk": ["chalk@4.1.2", "", { "dependencies": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" } }, "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA=="], - - "path-type/pify": ["pify@3.0.0", "", {}, "sha512-C3FsVNH1udSEX48gGX1xfvwTWfsYWj5U+8/uK15BGzIGrKoUpghX8hWZwa/OFnakBiiVNmBvemTJR5mcy7iPcg=="], - - "read-pkg/normalize-package-data": ["normalize-package-data@2.5.0", "", { "dependencies": { "hosted-git-info": "^2.1.4", "resolve": "^1.10.0", "semver": "2 || 3 || 4 || 5", "validate-npm-package-license": "^3.0.1" } }, "sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA=="], - - "read-pkg-up/find-up": ["find-up@4.1.0", "", { "dependencies": { "locate-path": "^5.0.0", "path-exists": "^4.0.0" } }, "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw=="], - - "read-pkg-up/read-pkg": ["read-pkg@5.2.0", "", { "dependencies": { "@types/normalize-package-data": "^2.4.0", "normalize-package-data": "^2.5.0", "parse-json": "^5.0.0", "type-fest": "^0.6.0" } }, "sha512-Ug69mNOpfvKDAc2Q8DRpMjjzdtrnv9HcSMX+4VsZxD1aZ6ZzrIE7rlzXBtWTyhULSMKg076AW6WR5iZpD0JiOg=="], - - "read-pkg-up/type-fest": ["type-fest@0.8.1", "", {}, "sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA=="], - - "slice-ansi/ansi-styles": ["ansi-styles@6.2.3", "", {}, "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg=="], - - "slice-ansi/is-fullwidth-code-point": ["is-fullwidth-code-point@4.0.0", "", {}, "sha512-O4L094N2/dZ7xqVdrXhh9r1KODPJpFms8B5sGdJLPy664AgvXsreZUyCQQNItZRDlYug4xStLjNp/sz3HvBowQ=="], - - "through2/readable-stream": ["readable-stream@2.3.8", "", { "dependencies": { "core-util-is": "~1.0.0", "inherits": "~2.0.3", "isarray": "~1.0.0", "process-nextick-args": "~2.0.0", "safe-buffer": "~5.1.1", "string_decoder": "~1.1.1", "util-deprecate": "~1.0.1" } }, "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA=="], - - "wrap-ansi/ansi-styles": ["ansi-styles@4.3.0", "", { "dependencies": { "color-convert": "^2.0.1" } }, "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg=="], - - "cli-truncate/string-width/emoji-regex": ["emoji-regex@10.5.0", "", {}, "sha512-lb49vf1Xzfx080OKA0o6l8DQQpV+6Vg95zyCJX9VB/BqKYlhG7N4wgROUUHRA+ZPUefLnteQOad7z1kT2bV7bg=="], - - "cli-truncate/string-width/strip-ansi": ["strip-ansi@7.1.2", "", { "dependencies": { "ansi-regex": "^6.0.1" } }, "sha512-gmBGslpoQJtgnMAvOVqGZpEz9dyoKTCzy2nfz/n8aIFhN/jCE/rCmcxabB6jOOHV+0WNnylOxaxBQPSvcWklhA=="], - - "conventional-changelog-core/conventional-commits-parser/is-text-path": ["is-text-path@1.0.1", "", { "dependencies": { "text-extensions": "^1.0.0" } }, "sha512-xFuJpne9oFz5qDaodwmmG08e3CawH/2ZV8Qqza1Ko7Sk8POWbkRdwIoAWVhqvq0XeUzANEhKo2n0IXUGBm7A/w=="], - - "conventional-changelog-core/conventional-commits-parser/split2": ["split2@3.2.2", "", { "dependencies": { "readable-stream": "^3.0.0" } }, "sha512-9NThjpgZnifTkJpzTZ7Eue85S49QwpNhZTq6GRJwObb6jnLFNGB7Qm73V5HewTROPyxD0C29xqmaI68bQtV+hg=="], - - "conventional-changelog-core/git-raw-commits/dargs": ["dargs@7.0.0", "", {}, "sha512-2iy1EkLdlBzQGvbweYRFxmFath8+K7+AKB0TlhHWkNuH+TmovaMH/Wp7V7R4u7f4SnX3OgLsU9t1NI9ioDnUpg=="], - - "conventional-changelog-core/git-raw-commits/split2": ["split2@3.2.2", "", { "dependencies": { "readable-stream": "^3.0.0" } }, "sha512-9NThjpgZnifTkJpzTZ7Eue85S49QwpNhZTq6GRJwObb6jnLFNGB7Qm73V5HewTROPyxD0C29xqmaI68bQtV+hg=="], - - "conventional-changelog-core/read-pkg-up/find-up": ["find-up@2.1.0", "", { "dependencies": { "locate-path": "^2.0.0" } }, "sha512-NWzkk0jSJtTt08+FBFMvXoeZnOJD+jTtsRmBYbAIzJdX6l7dLgR7CTubCM5/eDdPUBvLCeVasP1brfVR/9/EZQ=="], - - "get-pkg-repo/yargs/cliui": ["cliui@7.0.4", "", { "dependencies": { "string-width": "^4.2.0", "strip-ansi": "^6.0.0", "wrap-ansi": "^7.0.0" } }, "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ=="], - - "get-pkg-repo/yargs/yargs-parser": ["yargs-parser@20.2.9", "", {}, "sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w=="], - - "inquirer/chalk/ansi-styles": ["ansi-styles@4.3.0", "", { "dependencies": { "color-convert": "^2.0.1" } }, "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg=="], - - "inquirer/chalk/supports-color": ["supports-color@7.2.0", "", { "dependencies": { "has-flag": "^4.0.0" } }, "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw=="], - - "listr2/wrap-ansi/ansi-styles": ["ansi-styles@6.2.3", "", {}, "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg=="], - - "listr2/wrap-ansi/string-width": ["string-width@7.2.0", "", { "dependencies": { "emoji-regex": "^10.3.0", "get-east-asian-width": "^1.0.0", "strip-ansi": "^7.1.0" } }, "sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ=="], - - "listr2/wrap-ansi/strip-ansi": ["strip-ansi@7.1.2", "", { "dependencies": { "ansi-regex": "^6.0.1" } }, "sha512-gmBGslpoQJtgnMAvOVqGZpEz9dyoKTCzy2nfz/n8aIFhN/jCE/rCmcxabB6jOOHV+0WNnylOxaxBQPSvcWklhA=="], - - "log-symbols/chalk/ansi-styles": ["ansi-styles@4.3.0", "", { "dependencies": { "color-convert": "^2.0.1" } }, "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg=="], - - "log-symbols/chalk/supports-color": ["supports-color@7.2.0", "", { "dependencies": { "has-flag": "^4.0.0" } }, "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw=="], - - "log-update/cli-cursor/restore-cursor": ["restore-cursor@5.1.0", "", { "dependencies": { "onetime": "^7.0.0", "signal-exit": "^4.1.0" } }, "sha512-oMA2dcrw6u0YfxJQXm342bFKX/E4sG9rbTzO9ptUcR/e8A33cHuvStiYOwH7fszkZlZ1z/ta9AAoPk2F4qIOHA=="], - - "log-update/slice-ansi/ansi-styles": ["ansi-styles@6.2.3", "", {}, "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg=="], - - "log-update/slice-ansi/is-fullwidth-code-point": ["is-fullwidth-code-point@5.1.0", "", { "dependencies": { "get-east-asian-width": "^1.3.1" } }, "sha512-5XHYaSyiqADb4RnZ1Bdad6cPp8Toise4TzEjcOYDHZkTCbKgiUl7WTUCpNWHuxmDt91wnsZBc9xinNzopv3JMQ=="], - - "log-update/strip-ansi/ansi-regex": ["ansi-regex@6.2.2", "", {}, "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg=="], - - "log-update/wrap-ansi/ansi-styles": ["ansi-styles@6.2.3", "", {}, "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg=="], - - "log-update/wrap-ansi/string-width": ["string-width@7.2.0", "", { "dependencies": { "emoji-regex": "^10.3.0", "get-east-asian-width": "^1.0.0", "strip-ansi": "^7.1.0" } }, "sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ=="], - - "ora/chalk/ansi-styles": ["ansi-styles@4.3.0", "", { "dependencies": { "color-convert": "^2.0.1" } }, "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg=="], - - "ora/chalk/supports-color": ["supports-color@7.2.0", "", { "dependencies": { "has-flag": "^4.0.0" } }, "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw=="], - - "read-pkg-up/find-up/locate-path": ["locate-path@5.0.0", "", { "dependencies": { "p-locate": "^4.1.0" } }, "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g=="], - - "read-pkg-up/find-up/path-exists": ["path-exists@4.0.0", "", {}, "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w=="], - - "read-pkg-up/read-pkg/normalize-package-data": ["normalize-package-data@2.5.0", "", { "dependencies": { "hosted-git-info": "^2.1.4", "resolve": "^1.10.0", "semver": "2 || 3 || 4 || 5", "validate-npm-package-license": "^3.0.1" } }, "sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA=="], - - "read-pkg-up/read-pkg/type-fest": ["type-fest@0.6.0", "", {}, "sha512-q+MB8nYR1KDLrgr4G5yemftpMC7/QLqVndBmEEdqzmNj5dcFOO4Oo8qlwZE3ULT3+Zim1F8Kq4cBnikNhlCMlg=="], - - "read-pkg/normalize-package-data/hosted-git-info": ["hosted-git-info@2.8.9", "", {}, "sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw=="], - - "read-pkg/normalize-package-data/semver": ["semver@5.7.2", "", { "bin": { "semver": "bin/semver" } }, "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g=="], - - "through2/readable-stream/safe-buffer": ["safe-buffer@5.1.2", "", {}, "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g=="], - - "through2/readable-stream/string_decoder": ["string_decoder@1.1.1", "", { "dependencies": { "safe-buffer": "~5.1.0" } }, "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg=="], - - "wrap-ansi/ansi-styles/color-convert": ["color-convert@2.0.1", "", { "dependencies": { "color-name": "~1.1.4" } }, "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ=="], - - "cli-truncate/string-width/strip-ansi/ansi-regex": ["ansi-regex@6.2.2", "", {}, "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg=="], - - "conventional-changelog-core/conventional-commits-parser/is-text-path/text-extensions": ["text-extensions@1.9.0", "", {}, "sha512-wiBrwC1EhBelW12Zy26JeOUkQ5mRu+5o8rpsJk5+2t+Y5vE7e842qtZDQ2g1NpX/29HdyFeJ4nSIhI47ENSxlQ=="], - - "conventional-changelog-core/read-pkg-up/find-up/locate-path": ["locate-path@2.0.0", "", { "dependencies": { "p-locate": "^2.0.0", "path-exists": "^3.0.0" } }, "sha512-NCI2kiDkyR7VeEKm27Kda/iQHyKJe1Bu0FlTbYp3CqJu+9IFe9bLyAjMxf5ZDDbEg+iMPzB5zYyUTSm8wVTKmA=="], - - "inquirer/chalk/ansi-styles/color-convert": ["color-convert@2.0.1", "", { "dependencies": { "color-name": "~1.1.4" } }, "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ=="], - - "inquirer/chalk/supports-color/has-flag": ["has-flag@4.0.0", "", {}, "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ=="], - - "listr2/wrap-ansi/string-width/emoji-regex": ["emoji-regex@10.5.0", "", {}, "sha512-lb49vf1Xzfx080OKA0o6l8DQQpV+6Vg95zyCJX9VB/BqKYlhG7N4wgROUUHRA+ZPUefLnteQOad7z1kT2bV7bg=="], - - "listr2/wrap-ansi/strip-ansi/ansi-regex": ["ansi-regex@6.2.2", "", {}, "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg=="], - - "log-symbols/chalk/ansi-styles/color-convert": ["color-convert@2.0.1", "", { "dependencies": { "color-name": "~1.1.4" } }, "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ=="], - - "log-symbols/chalk/supports-color/has-flag": ["has-flag@4.0.0", "", {}, "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ=="], - - "log-update/cli-cursor/restore-cursor/onetime": ["onetime@7.0.0", "", { "dependencies": { "mimic-function": "^5.0.0" } }, "sha512-VXJjc87FScF88uafS3JllDgvAm+c/Slfz06lorj2uAY34rlUu0Nt+v8wreiImcrgAjjIHp1rXpTDlLOGw29WwQ=="], - - "log-update/cli-cursor/restore-cursor/signal-exit": ["signal-exit@4.1.0", "", {}, "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw=="], - - "log-update/wrap-ansi/string-width/emoji-regex": ["emoji-regex@10.5.0", "", {}, "sha512-lb49vf1Xzfx080OKA0o6l8DQQpV+6Vg95zyCJX9VB/BqKYlhG7N4wgROUUHRA+ZPUefLnteQOad7z1kT2bV7bg=="], - - "ora/chalk/ansi-styles/color-convert": ["color-convert@2.0.1", "", { "dependencies": { "color-name": "~1.1.4" } }, "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ=="], - - "ora/chalk/supports-color/has-flag": ["has-flag@4.0.0", "", {}, "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ=="], - - "read-pkg-up/find-up/locate-path/p-locate": ["p-locate@4.1.0", "", { "dependencies": { "p-limit": "^2.2.0" } }, "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A=="], - - "read-pkg-up/read-pkg/normalize-package-data/hosted-git-info": ["hosted-git-info@2.8.9", "", {}, "sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw=="], - - "read-pkg-up/read-pkg/normalize-package-data/semver": ["semver@5.7.2", "", { "bin": { "semver": "bin/semver" } }, "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g=="], - - "wrap-ansi/ansi-styles/color-convert/color-name": ["color-name@1.1.4", "", {}, "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA=="], - - "conventional-changelog-core/read-pkg-up/find-up/locate-path/p-locate": ["p-locate@2.0.0", "", { "dependencies": { "p-limit": "^1.1.0" } }, "sha512-nQja7m7gSKuewoVRen45CtVfODR3crN3goVQ0DDZ9N3yHxgpkuBhZqsaiotSQRrADUrne346peY7kT3TSACykg=="], - - "conventional-changelog-core/read-pkg-up/find-up/locate-path/path-exists": ["path-exists@3.0.0", "", {}, "sha512-bpC7GYwiDYQ4wYLe+FA8lhRjhQCMcQGuSgGGqDkg/QerRWw9CmGRT0iSOVRSZJ29NMLZgIzqaljJ63oaL4NIJQ=="], - - "inquirer/chalk/ansi-styles/color-convert/color-name": ["color-name@1.1.4", "", {}, "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA=="], - - "log-symbols/chalk/ansi-styles/color-convert/color-name": ["color-name@1.1.4", "", {}, "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA=="], - - "ora/chalk/ansi-styles/color-convert/color-name": ["color-name@1.1.4", "", {}, "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA=="], - - "read-pkg-up/find-up/locate-path/p-locate/p-limit": ["p-limit@2.3.0", "", { "dependencies": { "p-try": "^2.0.0" } }, "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w=="], - - "conventional-changelog-core/read-pkg-up/find-up/locate-path/p-locate/p-limit": ["p-limit@1.3.0", "", { "dependencies": { "p-try": "^1.0.0" } }, "sha512-vvcXsLAJ9Dr5rQOPk7toZQZJApBl2K4J6dANSsEuh6QI41JYcsS/qhTGa9ErIUUgK3WNQoJYvylxvjqmiqEA9Q=="], - - "conventional-changelog-core/read-pkg-up/find-up/locate-path/p-locate/p-limit/p-try": ["p-try@1.0.0", "", {}, "sha512-U1etNYuMJoIz3ZXSrrySFjsXQTWOx2/jdi86L+2pRvph/qMKL6sbcCYdH23fqsbm8TH2Gn0OybpT4eSFlCVHww=="], - } -} diff --git a/package.json b/package.json index 36a9fe19..1104e034 100644 --- a/package.json +++ b/package.json @@ -1,22 +1,21 @@ { "name": "root", - "version": "0.0.0-monorepo", + "version": "1.4.2", "private": true, "engineStrict": true, "engines": { "node": ">=22", - "bun": "1.2.5" + "bun": "1.3.2" }, "scripts": { "commit": "cz", "fmt:tf": "terraform fmt terraform", - "changelog": "./scripts/changelog.sh", - "changelog:tag": "./scripts/changelog.sh --tag", - "version": "./scripts/version.sh get", - "version:update": "./scripts/version.sh update", - "version:info": "./scripts/version.sh info" + "changeset": "changeset", + "changeset:version": "changeset version", + "changeset:status": "changeset status" }, "devDependencies": { + "@changesets/cli": "^2.27.10", "@commitlint/cli": "^19.6.1", "@commitlint/config-conventional": "^19.6.0", "commitizen": "4.3.1", diff --git a/scripts/build-quick.sh b/scripts/build-quick.sh deleted file mode 100755 index 0bcab57f..00000000 --- a/scripts/build-quick.sh +++ /dev/null @@ -1,21 +0,0 @@ -#!/bin/bash - -set -e - -echo "🔨 Quick build for local development..." - -# Get project root -SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" -PROJECT_ROOT="$(dirname "$SCRIPT_DIR")" - -# Build main lambda -echo "📦 Building lho-lambda..." -cd "$PROJECT_ROOT/apps/lho-lambda/src" -dotnet publish -c Release - -# Build authorizer -echo "🔐 Building lho-authorizer..." -cd "$PROJECT_ROOT/apps/lho-authorizer/src" -dotnet publish -c Release - -echo "✅ Build complete!" diff --git a/scripts/build.sh b/scripts/build.sh deleted file mode 100755 index 0fcee221..00000000 --- a/scripts/build.sh +++ /dev/null @@ -1,119 +0,0 @@ -#!/bin/bash - -set -e - -RED='\033[0;31m' -GREEN='\033[0;32m' -YELLOW='\033[1;33m' -BLUE='\033[0;34m' -NC='\033[0m' # No Color - -print_status() { - echo -e "${BLUE}[INFO]${NC} $1" -} - -print_success() { - echo -e "${GREEN}[SUCCESS]${NC} $1" -} - -print_warning() { - echo -e "${YELLOW}[WARNING]${NC} $1" -} - -print_error() { - echo -e "${RED}[ERROR]${NC} $1" -} - -# Get the script directory -SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" -PROJECT_ROOT="$(dirname "$SCRIPT_DIR")" - -print_status "Building .NET Lambda functions..." -print_status "Project root: $PROJECT_ROOT" - -# Configuration -CONFIGURATION="Release" -RUNTIME="linux-arm64" -OUTPUT_PATH="bin/Release/net8.0/publish" - -print_status "Cleaning previous builds..." -rm -rf "$PROJECT_ROOT/apps/lho-lambda/src/$OUTPUT_PATH" -rm -rf "$PROJECT_ROOT/apps/lho-authorizer/src/$OUTPUT_PATH" -rm -f "$PROJECT_ROOT/lambda.zip" -rm -f "$PROJECT_ROOT/authorizer.zip" - -print_status "Building main lambda (lho-lambda)..." -cd "$PROJECT_ROOT/apps/lho-lambda/src" - -if ! dotnet restore; then - print_error "Failed to restore packages for lho-lambda" - exit 1 -fi - -if ! dotnet publish \ - --configuration "$CONFIGURATION" \ - --runtime "$RUNTIME" \ - --self-contained false \ - --output "$OUTPUT_PATH"; then - print_error "Failed to publish lho-lambda" - exit 1 -fi - -print_success "Main lambda built successfully" - -print_status "Building authorizer lambda (lho-authorizer)..." -cd "$PROJECT_ROOT/apps/lho-authorizer/src" - -if ! dotnet restore; then - print_error "Failed to restore packages for lho-authorizer" - exit 1 -fi - -if ! dotnet publish \ - --configuration "$CONFIGURATION" \ - --runtime "$RUNTIME" \ - --self-contained false \ - --output "$OUTPUT_PATH"; then - print_error "Failed to publish lho-authorizer" - exit 1 -fi - -print_success "Authorizer lambda built successfully" - -print_status "Verifying build outputs..." - -MAIN_LAMBDA_OUTPUT="$PROJECT_ROOT/apps/lho-lambda/src/$OUTPUT_PATH" -AUTHORIZER_OUTPUT="$PROJECT_ROOT/apps/lho-authorizer/src/$OUTPUT_PATH" - -if [ ! -d "$MAIN_LAMBDA_OUTPUT" ]; then - print_error "Main lambda output directory not found: $MAIN_LAMBDA_OUTPUT" - exit 1 -fi - -if [ ! -d "$AUTHORIZER_OUTPUT" ]; then - print_error "Authorizer output directory not found: $AUTHORIZER_OUTPUT" - exit 1 -fi - -if [ ! -f "$MAIN_LAMBDA_OUTPUT/lho-lambda.dll" ]; then - print_error "Main lambda assembly not found: $MAIN_LAMBDA_OUTPUT/lho-lambda.dll" - exit 1 -fi - -if [ ! -f "$AUTHORIZER_OUTPUT/lhoAuthorizer.dll" ]; then - print_error "Authorizer assembly not found: $AUTHORIZER_OUTPUT/lhoAuthorizer.dll" - exit 1 -fi - -print_success "All build outputs verified successfully" - -print_status "Build Summary:" -echo " Main Lambda: $MAIN_LAMBDA_OUTPUT" -echo " Authorizer: $AUTHORIZER_OUTPUT" -echo "" -print_success "Build completed successfully! You can now run terraform apply." - -print_status "Next steps:" -echo " cd terraform" -echo " terraform plan" -echo " terraform apply" diff --git a/scripts/changelog.sh b/scripts/changelog.sh deleted file mode 100755 index a84fce87..00000000 --- a/scripts/changelog.sh +++ /dev/null @@ -1,133 +0,0 @@ -#!/bin/bash - -set -e - -# Colors for output -RED='\033[0;31m' -GREEN='\033[0;32m' -YELLOW='\033[1;33m' -BLUE='\033[0;34m' -NC='\033[0m' # No Color - -print_status() { - echo -e "${BLUE}[INFO]${NC} $1" -} - -print_success() { - echo -e "${GREEN}[SUCCESS]${NC} $1" -} - -print_warning() { - echo -e "${YELLOW}[WARNING]${NC} $1" -} - -print_error() { - echo -e "${RED}[ERROR]${NC} $1" -} - -SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" -PROJECT_ROOT="$(dirname "$SCRIPT_DIR")" - -CHANGELOG_FILE="$PROJECT_ROOT/CHANGELOG.md" -LAMBDA_CHANGELOG="$PROJECT_ROOT/apps/lho-lambda/CHANGELOG.md" -AUTHORIZER_CHANGELOG="$PROJECT_ROOT/apps/lho-authorizer/CHANGELOG.md" - -# Check if this is a pre-release -is_prerelease() { - # Check if this is a PR or non-main branch (same logic as version.sh) - if [ "${GITHUB_EVENT_NAME:-}" = "pull_request" ] || [ "${GITHUB_REF_NAME:-$(git branch --show-current)}" != "main" ]; then - return 0 # true - is pre-release - else - return 1 # false - is not pre-release - fi -} - -# Get current version from git tags -CURRENT_VERSION=$(git describe --tags --abbrev=0 2>/dev/null || echo "v0.0.0") -print_status "Current version: $CURRENT_VERSION" - -# Generate new version based on conventional commits -# Check if there are any breaking changes -BREAKING_CHANGES=$(git log ${CURRENT_VERSION}..HEAD --pretty=format:"%s" | grep -E "BREAKING CHANGE|!" | wc -l || echo "0") -FEATURES=$(git log ${CURRENT_VERSION}..HEAD --pretty=format:"%s" | grep -E "^feat" | wc -l || echo "0") -FIXES=$(git log ${CURRENT_VERSION}..HEAD --pretty=format:"%s" | grep -E "^fix" | wc -l || echo "0") - -CURRENT_VERSION_CLEAN=$(echo $CURRENT_VERSION | sed 's/v//') -IFS='.' read -ra VERSION_PARTS <<< "$CURRENT_VERSION_CLEAN" -MAJOR=${VERSION_PARTS[0]:-0} -MINOR=${VERSION_PARTS[1]:-0} -PATCH=${VERSION_PARTS[2]:-0} - -if [ "$BREAKING_CHANGES" -gt 0 ]; then - MAJOR=$((MAJOR + 1)) - MINOR=0 - PATCH=0 - RELEASE_TYPE="major" -elif [ "$FEATURES" -gt 0 ]; then - MINOR=$((MINOR + 1)) - PATCH=0 - RELEASE_TYPE="minor" -elif [ "$FIXES" -gt 0 ]; then - PATCH=$((PATCH + 1)) - RELEASE_TYPE="patch" -else - print_warning "No conventional commits found. Using patch version bump." - PATCH=$((PATCH + 1)) - RELEASE_TYPE="patch" -fi - -NEW_VERSION="$MAJOR.$MINOR.$PATCH" -NEW_VERSION_TAG="v$NEW_VERSION" - -print_status "New version will be: $NEW_VERSION_TAG ($RELEASE_TYPE)" - -# Generate changelog for root -print_status "Generating root changelog..." -bunx conventional-changelog -p angular -i "$CHANGELOG_FILE" -s -r 0 - -# Generate changelog for lambda app -print_status "Generating lambda changelog..." -bunx conventional-changelog -p angular -i "$LAMBDA_CHANGELOG" -s -r 0 \ - --commit-path="apps/lho-lambda" \ - --pkg="$PROJECT_ROOT/apps/lho-lambda/src/lho-lambda.csproj" - -# Generate changelog for authorizer app -print_status "Generating authorizer changelog..." -bunx conventional-changelog -p angular -i "$AUTHORIZER_CHANGELOG" -s -r 0 \ - --commit-path="apps/lho-authorizer" \ - --pkg="$PROJECT_ROOT/apps/lho-authorizer/src/lhoAuthorizer.csproj" - -# Create git tag only if not pre-release and --tag flag is provided -if [ "${1:-}" = "--tag" ]; then - if is_prerelease; then - print_warning "Skipping git tag creation for pre-release version" - print_status "Pre-release detected: GITHUB_EVENT_NAME=${GITHUB_EVENT_NAME:-}, branch=$(git branch --show-current)" - else - print_status "Creating git tag: $NEW_VERSION_TAG" - git add . - git commit -m "chore(release): $NEW_VERSION_TAG" || true - git tag -a "$NEW_VERSION_TAG" -m "Release $NEW_VERSION_TAG" - print_success "Tagged version $NEW_VERSION_TAG" - fi -else - print_status "Skipping git tag creation (--tag not provided)" -fi - -# Export version for CI/CD -echo "NEW_VERSION=$NEW_VERSION" >> "$GITHUB_ENV" 2>/dev/null || true -echo "NEW_VERSION_TAG=$NEW_VERSION_TAG" >> "$GITHUB_ENV" 2>/dev/null || true -echo "RELEASE_TYPE=$RELEASE_TYPE" >> "$GITHUB_ENV" 2>/dev/null || true - -print_success "Changelog generation completed!" -print_status "Summary:" -echo " Current Version: $CURRENT_VERSION" -echo " New Version: $NEW_VERSION_TAG" -echo " Release Type: $RELEASE_TYPE" -echo " Breaking Changes: $BREAKING_CHANGES" -echo " Features: $FEATURES" -echo " Fixes: $FIXES" -if is_prerelease; then - echo " Pre-release: Yes" -else - echo " Pre-release: No" -fi diff --git a/scripts/clear-buckets.sh b/scripts/clear-buckets.sh deleted file mode 100755 index 3937a54a..00000000 --- a/scripts/clear-buckets.sh +++ /dev/null @@ -1,21 +0,0 @@ -#!/bin/bash - -# usage ./clear-buckets.sh bucket1 bucket2 bucket3 - -if [ "$#" -eq 0 ]; then - echo "No arguments provided. Please provide a list of S3 bucket names." - exit 1 -fi - -for bucket_name in "$@" -do - echo "Processing bucket: $bucket_name" - - # Empty the bucket - aws s3 rm s3://$bucket_name --recursive - - # Delete the bucket - aws s3 rb s3://$bucket_name - - echo "Finished emptying and deleting bucket: $bucket_name" -done diff --git a/scripts/deploy.sh b/scripts/deploy.sh deleted file mode 100755 index 0a5ebd5e..00000000 --- a/scripts/deploy.sh +++ /dev/null @@ -1,58 +0,0 @@ -#!/bin/bash - -set -e - -# Colors for output -RED='\033[0;31m' -GREEN='\033[0;32m' -YELLOW='\033[1;33m' -BLUE='\033[0;34m' -NC='\033[0m' - -print_status() { - echo -e "${BLUE}[INFO]${NC} $1" -} - -print_success() { - echo -e "${GREEN}[SUCCESS]${NC} $1" -} - -print_error() { - echo -e "${RED}[ERROR]${NC} $1" -} - -# Check arguments -if [ "$#" -ne 1 ]; then - print_error "Usage: $0 " - print_error "Example: $0 staging" - exit 1 -fi - -ENVIRONMENT="$1" - -if [ "$ENVIRONMENT" != "staging" ] && [ "$ENVIRONMENT" != "live" ]; then - print_error "Invalid environment. Must be 'staging' or 'live'" - exit 1 -fi - -SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" -PROJECT_ROOT="$(dirname "$SCRIPT_DIR")" - -print_status "Deploying to $ENVIRONMENT environment..." - -# Step 1: Build -print_status "Step 1: Building .NET applications..." -if ! "$SCRIPT_DIR/build.sh"; then - print_error "Build failed" - exit 1 -fi - -# Step 2: Terraform -print_status "Step 2: Deploying with Terraform..." -cd "$PROJECT_ROOT/terraform" - -# Initialize terraform -print_status "Initializing Terraform..." -if ! terraform init \ - -backend-config="key=vpc/$ENVIRONMENT.tfstate" \ - -backend-config="bucket=nowplaying-$ diff --git a/scripts/run-local.sh b/scripts/run-local.sh new file mode 100755 index 00000000..0e4fe416 --- /dev/null +++ b/scripts/run-local.sh @@ -0,0 +1,21 @@ +#!/bin/bash + +set -eu + +GREEN='\033[0;32m' +BLUE='\033[0;34m' +NC='\033[0m' + +echo -e "${BLUE}Building Lambda function...${NC}" +swift build -c release + +echo -e "${GREEN}Starting local Lambda server...${NC}" +echo -e "${BLUE}Server will be available at http://localhost:7000/invoke${NC}" +echo -e "${BLUE}Press Ctrl+C to stop${NC}" +echo "" + + + + +LOCAL_LAMBDA_SERVER_ENABLED=true .build/release/lambda + diff --git a/scripts/test-routes.sh b/scripts/test-routes.sh new file mode 100755 index 00000000..42f966c1 --- /dev/null +++ b/scripts/test-routes.sh @@ -0,0 +1,44 @@ +#!/bin/bash + +# Usage: ./scripts/test-simple.sh [path] [method] +# Example: ./scripts/test-simple.sh /api/healthcheck GET +# Example: ./scripts/test-simple.sh /api/now-playing GET + +PATH_TO_TEST="${1:-/}" +METHOD="${2:-GET}" + +echo "Testing $METHOD $PATH_TO_TEST" +echo "" + +curl -X POST http://localhost:7000/invoke \ + -H "Content-Type: application/json" \ + -d "{ + \"version\": \"2.0\", + \"routeKey\": \"$METHOD $PATH_TO_TEST\", + \"rawPath\": \"$PATH_TO_TEST\", + \"rawQueryString\": \"\", + \"headers\": {}, + \"isBase64Encoded\": false, + \"requestContext\": { + \"accountId\": \"123456789012\", + \"apiId\": \"test-api-id\", + \"domainName\": \"localhost\", + \"domainPrefix\": \"test\", + \"http\": { + \"method\": \"$METHOD\", + \"path\": \"$PATH_TO_TEST\", + \"protocol\": \"HTTP/1.1\", + \"sourceIp\": \"127.0.0.1\", + \"userAgent\": \"curl/test\" + }, + \"requestId\": \"test-request-id\", + \"routeKey\": \"$METHOD $PATH_TO_TEST\", + \"stage\": \"\$default\", + \"time\": \"09/Dec/2024:00:00:00 +0000\", + \"timeEpoch\": 1702080000000 + } + }" + +echo "" +echo "" + diff --git a/scripts/version.sh b/scripts/version.sh deleted file mode 100755 index a2f24bb0..00000000 --- a/scripts/version.sh +++ /dev/null @@ -1,140 +0,0 @@ -#!/bin/bash - -set -e - -# Colors for output -RED='\033[0;31m' -GREEN='\033[0;32m' -YELLOW='\033[1;33m' -BLUE='\033[0;34m' -NC='\033[0m' - -print_status() { - echo -e "${BLUE}[INFO]${NC} $1" -} - -print_success() { - echo -e "${GREEN}[SUCCESS]${NC} $1" -} - -print_error() { - echo -e "${RED}[ERROR]${NC} $1" -} - -# Get the script directory -SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" -PROJECT_ROOT="$(dirname "$SCRIPT_DIR")" - -# Function to get version from git tag -get_version_from_git() { - git describe --tags --exact-match 2>/dev/null || git describe --tags --abbrev=0 2>/dev/null || echo "v0.0.0" -} - -# Function to get version without 'v' prefix -get_clean_version() { - local version=$(get_version_from_git) - local clean_version="${version#v}" - - # Add pre-release suffix if this is a PR or non-main branch - if [ "${GITHUB_EVENT_NAME:-}" = "pull_request" ] || [ "${GITHUB_REF_NAME:-$(git branch --show-current)}" != "main" ]; then - local git_sha_short=$(get_git_sha_short) - echo "${clean_version}-pre.${git_sha_short}" - else - echo "${clean_version}" - fi -} - -# Function to get git commit SHA -get_git_sha() { - git rev-parse HEAD 2>/dev/null || echo "unknown" -} - -# Function to get short git SHA -get_git_sha_short() { - git rev-parse --short HEAD 2>/dev/null || echo "unknown" -} - -# Function to update .NET project version -update_dotnet_version() { - local project_file="$1" - local version="$2" - - if [ ! -f "$project_file" ]; then - print_error "Project file not found: $project_file" - return 1 - fi - - print_status "Updating version in $project_file to $version" - - # Update or add Version property - if grep -q "" "$project_file"; then - sed -i.bak "s|.*|$version|" "$project_file" - else - # Add Version property to PropertyGroup - sed -i.bak "//a\\ - $version" "$project_file" - fi - - # Clean up backup file - rm -f "$project_file.bak" - - print_success "Updated version in $project_file" -} - -# Main execution -case "${1:-}" in - "get") - get_clean_version - ;; - "get-tag") - get_version_from_git - ;; - "get-sha") - get_git_sha - ;; - "get-sha-short") - get_git_sha_short - ;; - "update") - VERSION=$(get_clean_version) - print_status "Updating .NET project versions to $VERSION" - - # Update main lambda project - update_dotnet_version "$PROJECT_ROOT/apps/lho-lambda/src/lho-lambda.csproj" "$VERSION" - - # Update authorizer project - update_dotnet_version "$PROJECT_ROOT/apps/lho-authorizer/src/lhoAuthorizer.csproj" "$VERSION" - - print_success "All project versions updated to $VERSION" - ;; - "info") - VERSION=$(get_clean_version) - VERSION_TAG=$(get_version_from_git) - GIT_SHA=$(get_git_sha) - GIT_SHA_SHORT=$(get_git_sha_short) - - print_status "Version Information:" - echo " Clean Version: $VERSION" - echo " Version Tag: $VERSION_TAG" - echo " Git SHA: $GIT_SHA" - echo " Git SHA Short: $GIT_SHA_SHORT" - - # Export for CI/CD - echo "VERSION=$VERSION" >> "$GITHUB_ENV" 2>/dev/null || true - echo "VERSION_TAG=$VERSION_TAG" >> "$GITHUB_ENV" 2>/dev/null || true - echo "GIT_SHA=$GIT_SHA" >> "$GITHUB_ENV" 2>/dev/null || true - echo "GIT_SHA_SHORT=$GIT_SHA_SHORT" >> "$GITHUB_ENV" 2>/dev/null || true - ;; - *) - print_error "Usage: $0 {get|get-tag|get-sha|get-sha-short|update|info}" - echo "" - echo "Commands:" - echo " get - Get clean version (without 'v' prefix)" - echo " get-tag - Get version with 'v' prefix" - echo " get-sha - Get full git SHA" - echo " get-sha-short - Get short git SHA" - echo " update - Update .NET project files with current version" - echo " info - Show all version information and export to env" - exit 1 - ;; -esac diff --git a/terraform/authorizer.tf b/terraform/authorizer.tf index fd2329dd..a8b3b81e 100644 --- a/terraform/authorizer.tf +++ b/terraform/authorizer.tf @@ -1,6 +1,6 @@ data "archive_file" "auth_archive" { type = "zip" - source_dir = "${path.module}/../apps/lho-authorizer/src/bin/Release/net8.0/publish" + source_dir = "${path.module}/../.build/release/authorizer-package" output_path = "${path.module}/../authorizer.zip" } @@ -8,13 +8,14 @@ resource "aws_lambda_function" "api_authorizer" { filename = "${path.module}/../authorizer.zip" function_name = "${var.project_name}-api-authorizer-${var.env}" role = aws_iam_role.lambda_exec.arn - handler = "lhoAuthorizer::lhoAuthorizer.Function::FunctionHandler" + handler = "bootstrap" source_code_hash = data.archive_file.auth_archive.output_base64sha256 - runtime = "dotnet8" + runtime = "provided.al2" memory_size = 256 - architectures = ["arm64"] + architectures = ["x86_64"] timeout = 10 + environment { variables = { API_KEY = var.api_key @@ -33,8 +34,9 @@ resource "aws_apigatewayv2_authorizer" "api_key" { authorizer_uri = aws_lambda_function.api_authorizer.invoke_arn identity_sources = ["$request.header.x-api-key"] name = "api-authorizer" - authorizer_payload_format_version = "1.0" + authorizer_payload_format_version = "2.0" authorizer_result_ttl_in_seconds = 10 + enable_simple_responses = true } resource "aws_lambda_permission" "api_gw_authorizer" { @@ -44,3 +46,4 @@ resource "aws_lambda_permission" "api_gw_authorizer" { principal = "apigateway.amazonaws.com" source_arn = "${aws_apigatewayv2_api.lambda.execution_arn}/authorizers/${aws_apigatewayv2_authorizer.api_key.id}" } + diff --git a/terraform/gateway.tf b/terraform/gateway.tf index dec4d80a..c7595c1e 100644 --- a/terraform/gateway.tf +++ b/terraform/gateway.tf @@ -83,10 +83,11 @@ resource "aws_apigatewayv2_stage" "lambda" { } resource "aws_apigatewayv2_integration" "lambda" { - api_id = aws_apigatewayv2_api.lambda.id - integration_uri = aws_lambda_function.lambda.invoke_arn - integration_type = "AWS_PROXY" - integration_method = "POST" + api_id = aws_apigatewayv2_api.lambda.id + integration_uri = aws_lambda_function.lambda.invoke_arn + integration_type = "AWS_PROXY" + integration_method = "POST" + payload_format_version = "2.0" } @@ -122,13 +123,6 @@ resource "aws_apigatewayv2_route" "lambda_route_now_playing" { authorizer_id = aws_apigatewayv2_authorizer.api_key.id authorization_type = "CUSTOM" } - -resource "aws_apigatewayv2_route" "lambda_route_streak" { - api_id = aws_apigatewayv2_api.lambda.id - target = "integrations/${aws_apigatewayv2_integration.lambda.id}" - route_key = "GET /api/streak" - operation_name = "streak" -} ############################################################################## diff --git a/terraform/lambda.tf b/terraform/lambda.tf index 91dd75df..82b161a8 100644 --- a/terraform/lambda.tf +++ b/terraform/lambda.tf @@ -1,6 +1,6 @@ data "archive_file" "lambda_archive" { type = "zip" - source_dir = "${path.module}/../apps/lho-lambda/src/bin/Release/net8.0/publish" + source_file = "${path.module}/../.build/release/bootstrap" output_path = "${path.module}/../lambda.zip" } @@ -36,8 +36,8 @@ resource "aws_iam_role_policy_attachment" "lambda_policy" { resource "aws_lambda_function" "lambda" { function_name = "${var.project_name}-lambda-${var.env}" - runtime = "dotnet8" - handler = "lho-lambda::LhoLambda.LambdaEntryPoint::FunctionHandlerAsync" + runtime = "provided.al2" + handler = "bootstrap" role = aws_iam_role.lambda_exec.arn filename = "${path.module}/../lambda.zip" source_code_hash = data.archive_file.lambda_archive.output_base64sha256 @@ -47,7 +47,7 @@ resource "aws_lambda_function" "lambda" { # } description = "Now playing Lambda ${var.env}" memory_size = 256 - architectures = ["arm64"] + architectures = ["x86_64"] environment { variables = merge(var.env_vars, { VERSION = var.app_version