diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 49dad7c..c1c49eb 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -54,10 +54,10 @@ on: required: false default: "lts/*" min_node_version: - description: "Minimum Node.js version for matrix testing (default: 20, oldest non-EOL)" + description: "Minimum Node.js version for matrix testing (default: 22, oldest non-EOL LTS)" type: string required: false - default: "20" + default: "22" max_node_major: description: "Override max Node.js major version (default: 22)" type: string @@ -184,12 +184,12 @@ jobs: .gitignore debug: ${{ github.event.inputs.debug == 'true' }} node_version: ${{ github.event.inputs.node_version || 'lts/*' }} - min_node_version: ${{ github.event.inputs.min_node_version || '20' }} + min_node_version: ${{ github.event.inputs.min_node_version || '22' }} max_node_major: ${{ github.event.inputs.max_node_major || '22' }} - # LTS-only matrix (even majors: 20, 22, 24, …) on every event. Odd majors - # (21, 23, …) are non-LTS interim releases, and the native-binding test + # LTS-only matrix (even majors: 22, 24, …) on every event. Odd majors + # (23, 25, …) are non-LTS interim releases, and the native-binding test # toolchain (vitest 4 / rolldown / vite 8) excludes them via `engines` - # (`^20.19.0 || >=22.12.0`), so a "full matrix" on them only re-discovers a + # (`>=22.12.0`), so a "full matrix" on them only re-discovers a # known toolchain gap ("Cannot find native binding") rather than a real # per-version regression. workflow_dispatch can still opt out (set false). lts_only_matrix: ${{ github.event.inputs.lts_only_matrix != 'false' }} diff --git a/.github/workflows/feature-pr.yml b/.github/workflows/feature-pr.yml index ffbb123..fcee49e 100644 --- a/.github/workflows/feature-pr.yml +++ b/.github/workflows/feature-pr.yml @@ -1,7 +1,6 @@ # -# @Project: @cldmv/git-embedded -# @Filename: /.github/workflows/feature-pr.yml -# @Date: 2026-07-18 15:49:12 -07:00 (1784414952) +# @Project: @cldmv/.github +# @Filename: /examples/individual-repo-workflows/release-flow-v4/feature-pr.yml # @Author: Nate Corcoran # @Email: # @Copyright: Copyright (c) 2013-2026 Catalyzed Motivation Inc. All rights reserved. @@ -9,34 +8,28 @@ # Individual repo: .github/workflows/feature-pr.yml # -# v4 ergonomics: auto-opens (and refreshes) a PR from a code-side branch to -# the right integration branch on every push. +# v4 ergonomics: auto-opens (and refreshes) a PR from a code-side branch to the +# right integration branch on every push. # # Mapping (matches CLDMV/.github docs/conventions/branch-naming.md): # feat/*, feature/*, fix/*, release/*, chore/*, refactor/*, # docs/*, ci/*, perf/*, test/*, style/* → next # hotfix/* → hotfixes # -# Reserved branches NOT auto-PR'd: dependabot/* and copilot/* (they manage -# their own PRs); badges, gh-pages (bot-only); master/main (the target). +# Reserved branches NOT auto-PR'd: dependabot/* and copilot/* (they manage their +# own PRs); badges, gh-pages (bot-only); master/main (the target). # -# On first push: creates the PR with a categorized changelog body (same -# format the v4 release-PR machinery generates). On subsequent pushes: -# refreshes the existing PR's body with the latest categorized commits. -# Uses the shared get-commit-range + generate-comprehensive-changelog -# actions for the format, so consumer PRs look identical to release PRs -# in structure (Breaking Changes / Features / Bug Fixes / Other Changes / -# Contributors). -# -# Skipped automatically: bot pushes (your bot App's login / github-actions[bot]) -# and any push whose head commit starts with 'chore: bump version'. +# Thin caller: all job logic (target detection, changelog body, PR create/ +# refresh) lives in the reusable, pinned at @v4. Bumping the pin carries fixes +# without editing this file. The `push` trigger and its branch-prefix list stay +# here (GitHub requires the trigger local, and the list is per-repo config). name: 🔀 Feature PR (v4) on: push: branches: # CUSTOMIZE: prune this list to whichever branch prefixes your - # repo uses. Must align with the `case` statement below. + # repo uses. Must align with the `case` statement in the reusable. - 'feat/**' - 'feature/**' - 'fix/**' @@ -50,215 +43,17 @@ on: - 'style/**' - 'hotfix/**' -permissions: - contents: read - pull-requests: write - -# Serialize per-branch so a flurry of pushes doesn't race the -# "does a PR already exist?" check. concurrency: group: feature-pr-${{ github.repository }}-${{ github.ref }} cancel-in-progress: false jobs: open-pr: - # Loop guard: replace 'cldmv-bot[bot]' with your bot App's login. - if: | - github.actor != 'cldmv-bot[bot]' && - github.actor != 'github-actions[bot]' && - !startsWith(github.event.head_commit.message, 'chore: bump version') - runs-on: ubuntu-latest - steps: - - name: Determine target branch - id: target - shell: bash - run: | - branch="${GITHUB_REF#refs/heads/}" - echo "branch=$branch" >> "$GITHUB_OUTPUT" - # CUSTOMIZE: adjust the case arms to match your branch - # conventions. Anything not matched is silently skipped - # (so master/main, badges, gh-pages, dependabot/*, etc. - # are safe regardless of what fires the workflow). - # The flow_label sorts first in the PR's label list - # (the leading `!` precedes every letter alphabetically) - # so a glance at any PR's badges reveals which lane it's in. - # Lane (target) AND declared type both come from the branch - # prefix — the v4 convention requires a typed prefix, so a - # `docs/*` branch is a docs change, `fix/*` a fix, etc. The flow - # label is `! ` so it reflects what the PR actually - # is, not a blanket "feature". (Previously every next-lane branch - # got `! feature → next`, mislabelling docs/fix/chore PRs.) - case "$branch" in - hotfix/*) target="hotfixes"; type="hotfix" ;; - feat/*|feature/*) target="next"; type="feature" ;; - fix/*) target="next"; type="fix" ;; - docs/*) target="next"; type="docs" ;; - chore/*) target="next"; type="chore" ;; - refactor/*) target="next"; type="refactor" ;; - ci/*) target="next"; type="ci" ;; - perf/*) target="next"; type="perf" ;; - test/*) target="next"; type="test" ;; - style/*) target="next"; type="style" ;; - release/*) target="next"; type="release" ;; - *) - target=""; type=""; flow_label="" - echo "::notice::Branch '$branch' does not match any auto-PR pattern; skipping." - ;; - esac - # Leading `!` sorts the flow label first in the PR's badge list, - # so a glance reveals both the change type and its lane. - if [ -n "$target" ]; then - flow_label="! ${type} → ${target}" - fi - echo "target=$target" >> "$GITHUB_OUTPUT" - echo "flow_label=$flow_label" >> "$GITHUB_OUTPUT" - - - name: Create App token - id: app-token - if: steps.target.outputs.target != '' - uses: CLDMV/.github/.github/actions/github/steps/create-app-token@v4 - with: - client_id: ${{ secrets.CLDMV_BOT_APP_CLIENT_ID }} - private_key: ${{ secrets.CLDMV_BOT_APP_PRIVATE_KEY }} - env: - BOT_APP_CLIENT_ID: ${{ secrets.CLDMV_BOT_APP_CLIENT_ID }} - BOT_APP_PRIVATE_KEY: ${{ secrets.CLDMV_BOT_APP_PRIVATE_KEY }} - - - name: Check for existing PR - id: existing - if: steps.target.outputs.target != '' - shell: bash - env: - GH_TOKEN: ${{ steps.app-token.outputs.token }} - run: | - pr=$(gh pr list --repo "$GITHUB_REPOSITORY" \ - --head "${{ steps.target.outputs.branch }}" \ - --base "${{ steps.target.outputs.target }}" \ - --state open \ - --json number --jq '.[0].number // ""') - echo "number=$pr" >> "$GITHUB_OUTPUT" - if [ -n "$pr" ]; then - echo "::notice::Existing PR #$pr will be refreshed." - fi - - - name: Checkout (full history for git log) - if: steps.target.outputs.target != '' - uses: CLDMV/.github/.github/actions/common/steps/checkout-code@v4 - with: - fetch-depth: 0 - - - name: Fetch target branch ref - if: steps.target.outputs.target != '' - shell: bash - run: | - git fetch --quiet origin "${{ steps.target.outputs.target }}" - - - name: Get categorized commits (base..head) - id: commits - if: steps.target.outputs.target != '' - uses: CLDMV/.github/.github/actions/git/steps/get-commit-range@v4 - with: - base-ref: origin/${{ steps.target.outputs.target }} - head-ref: HEAD - - - name: Detect feature commits in range - id: feat - if: steps.target.outputs.target != '' - shell: bash - env: - COMMITS: ${{ steps.commits.outputs.commits }} - run: | - # `type: feature` is applied when the range contains a feature, - # mirroring the changelog's own "Features" section: get-commit- - # range tags `feat:` (and content-categorized add/new) commits - # as category "feature". Reuses the already-computed commits. - has_feature=false - if printf '%s' "$COMMITS" | jq -e 'any(.[]; .category == "feature")' >/dev/null 2>&1; then - has_feature=true - fi - echo "has_feature=$has_feature" >> "$GITHUB_OUTPUT" - echo "📊 feature detected in range: $has_feature" - - - name: Generate categorized changelog body - id: changelog - if: steps.target.outputs.target != '' - uses: CLDMV/.github/.github/actions/git/steps/generate-comprehensive-changelog@v4 - with: - commits: ${{ steps.commits.outputs.commits }} - commit-range: ${{ steps.commits.outputs.commit-range }} - env: - GITHUB_TOKEN: ${{ steps.app-token.outputs.token }} - - - name: Save body to file - if: steps.target.outputs.target != '' - shell: bash - env: - BODY: ${{ steps.changelog.outputs.changelog-content }} - run: | - printf '%s' "$BODY" > /tmp/pr-body.md - - - name: Create PR (first push) - if: steps.target.outputs.target != '' && steps.existing.outputs.number == '' - shell: bash - env: - GH_TOKEN: ${{ steps.app-token.outputs.token }} - HEAD_COMMIT_MSG: ${{ github.event.head_commit.message }} - HEAD_BRANCH: ${{ steps.target.outputs.branch }} - BASE_BRANCH: ${{ steps.target.outputs.target }} - run: | - # Title = head commit's first line — preserves the - # conventional-commit prefix the release-PR title-normalizer - # and commit-type aggregator expect. - title=$(printf '%s\n' "$HEAD_COMMIT_MSG" | head -1) - pr_url=$(gh pr create \ - --repo "$GITHUB_REPOSITORY" \ - --base "$BASE_BRANCH" \ - --head "$HEAD_BRANCH" \ - --title "$title" \ - --body-file /tmp/pr-body.md) - echo "::notice::Opened $pr_url" - # Apply the flow label (sorts first in the PR's badge list). - # `|| true` so a missing label in the repo (catalog not yet - # synced) doesn't fail the workflow. - if [ -n "${{ steps.target.outputs.flow_label }}" ]; then - gh pr edit "$pr_url" --add-label "${{ steps.target.outputs.flow_label }}" || true - fi - # Apply `type: feature` when the range implements a feature. - if [ "${{ steps.feat.outputs.has_feature }}" = "true" ]; then - gh pr edit "$pr_url" --add-label "type: feature" || true - fi - { - echo "### 🔀 Auto-opened PR" - echo "" - echo "$pr_url" - } >> "$GITHUB_STEP_SUMMARY" - - - name: Refresh existing PR body - if: steps.target.outputs.target != '' && steps.existing.outputs.number != '' - shell: bash - env: - GH_TOKEN: ${{ steps.app-token.outputs.token }} - PR_NUMBER: ${{ steps.existing.outputs.number }} - run: | - gh pr edit "$PR_NUMBER" \ - --repo "$GITHUB_REPOSITORY" \ - --body-file /tmp/pr-body.md - # Re-apply the flow label so a manual removal doesn't - # strand the PR without its lane indicator. - if [ -n "${{ steps.target.outputs.flow_label }}" ]; then - gh pr edit "$PR_NUMBER" \ - --repo "$GITHUB_REPOSITORY" \ - --add-label "${{ steps.target.outputs.flow_label }}" || true - fi - # Apply `type: feature` when the range implements a feature. - if [ "${{ steps.feat.outputs.has_feature }}" = "true" ]; then - gh pr edit "$PR_NUMBER" \ - --repo "$GITHUB_REPOSITORY" \ - --add-label "type: feature" || true - fi - echo "::notice::Refreshed PR #${PR_NUMBER} body" - { - echo "### 🔀 Refreshed PR body" - echo "" - echo "PR #${PR_NUMBER}" - } >> "$GITHUB_STEP_SUMMARY" + permissions: + contents: read + pull-requests: write + uses: CLDMV/.github/.github/workflows/workflow-feature-pr.yml@v4 + secrets: + # Map your repo/org secrets to the expected names. + BOT_APP_CLIENT_ID: ${{ secrets.CLDMV_BOT_APP_CLIENT_ID }} + BOT_APP_PRIVATE_KEY: ${{ secrets.CLDMV_BOT_APP_PRIVATE_KEY }} diff --git a/.github/workflows/hotfix-redirector.yml b/.github/workflows/hotfix-redirector.yml index 79383d5..830cad6 100644 --- a/.github/workflows/hotfix-redirector.yml +++ b/.github/workflows/hotfix-redirector.yml @@ -1,6 +1,6 @@ # -# @Project: @cldmv/git-embedded -# @Filename: /.github/workflows/hotfix-redirector.yml +# @Project: @cldmv/.github +# @Filename: /examples/individual-repo-workflows/release-flow-v4/hotfix-redirector.yml # @Date: 2026-05-22 00:00:00 -07:00 (1779778800) # @Author: Nate Corcoran # @Email: @@ -13,55 +13,39 @@ # # Two paths trigger a redirect (CLDMV/.github docs/conventions/release-flow-v4.md §5.2, §6.5): # 1. Head branch matches `hotfix/*` or `security/*` (human-driven hotfix flow). -# 2. Author is `dependabot[bot]` AND the PR body references a GHSA security -# advisory (Dependabot's security-update PRs flow into the hotfix lane; -# routine version bumps stay on `next`). +# 2. Author is `dependabot[bot]` AND its base isn't Dependabot's routine +# target-branch (default "next") — GitHub always overrides dependabot.yml's +# target-branch for security updates, so a base landing on the default +# branch instead of "next" is itself the signal. Routine version bumps stay +# on "next". # -# The redirect-hotfix-pr action owns all detection logic — it skips non-matching -# bot PRs, non-matching heads, and PRs already on `hotfixes`, and posts a -# one-time explanatory comment with the appropriate reason. +# Thin caller: all job logic (token, checkout, git identity, redirect action) +# lives in the reusable, pinned at @v4. Bumping the pin carries new requirements +# (e.g. the checkout + git identity the cherry-pick path needs) without editing +# this file. name: 🔀 Hotfix PR Redirector (v4) -# SECURITY NOTE: pull_request_target runs in the BASE repo's context with -# WRITE permissions + secrets. SAFE here because it is API-only — the -# redirect-hotfix-pr action never checks out or executes PR content. -# DO NOT add a checkout step. +# SECURITY NOTE: pull_request_target runs in the BASE repo's context with WRITE +# permissions + secrets. The reusable checks out `hotfixes` (a trusted base-repo +# branch, NOT the PR head/fork) and only cherry-picks/pushes against it. # -# `opened` only (NOT `edited`): if a maintainer manually re-targets the PR, -# we must not fight them by redirecting again. +# `opened` only (NOT `edited`): if a maintainer manually re-targets the PR, we +# must not fight them by redirecting again. on: pull_request_target: types: [opened] -permissions: - contents: read - pull-requests: write - concurrency: group: hotfix-redirector-${{ github.event.pull_request.number }} cancel-in-progress: true jobs: redirect: - name: "🔀 Redirect to hotfixes" - runs-on: ubuntu-latest - steps: - - name: Create App token - id: app-token - uses: CLDMV/.github/.github/actions/github/steps/create-app-token@v4 - with: - client_id: ${{ secrets.CLDMV_BOT_APP_CLIENT_ID }} - private_key: ${{ secrets.CLDMV_BOT_APP_PRIVATE_KEY }} - env: - BOT_APP_CLIENT_ID: ${{ secrets.CLDMV_BOT_APP_CLIENT_ID }} - BOT_APP_PRIVATE_KEY: ${{ secrets.CLDMV_BOT_APP_PRIVATE_KEY }} - - - name: Redirect hotfix/security PR to hotfixes - uses: CLDMV/.github/.github/actions/github/steps/redirect-hotfix-pr@v4 - with: - pr-number: ${{ github.event.pull_request.number }} - github-token: ${{ steps.app-token.outputs.token }} - head-ref: ${{ github.event.pull_request.head.ref }} - base-ref: ${{ github.event.pull_request.base.ref }} - user-type: ${{ github.event.pull_request.user.type }} - target-base: hotfixes + permissions: + contents: write + pull-requests: write + uses: CLDMV/.github/.github/workflows/workflow-hotfix-redirector.yml@v4 + secrets: + # Map your repo/org secrets to the expected names. + BOT_APP_CLIENT_ID: ${{ secrets.CLDMV_BOT_APP_CLIENT_ID }} + BOT_APP_PRIVATE_KEY: ${{ secrets.CLDMV_BOT_APP_PRIVATE_KEY }} diff --git a/.github/workflows/hotfixes-release.yml b/.github/workflows/hotfixes-release.yml index d7cfeea..a228e45 100644 --- a/.github/workflows/hotfixes-release.yml +++ b/.github/workflows/hotfixes-release.yml @@ -1,6 +1,6 @@ # -# @Project: @cldmv/git-embedded -# @Filename: /.github/workflows/hotfixes-release.yml +# @Project: @cldmv/.github +# @Filename: /examples/individual-repo-workflows/release-flow-v4/hotfixes-release.yml # @Date: 2026-05-22 00:00:00 -07:00 (1779778800) # @Author: Nate Corcoran # @Email: @@ -12,153 +12,44 @@ # v4 hotfix lane: maintain the ONE persistent `hotfixes → master` release PR. # # Mirror of next-release.yml but for the `hotfixes` integration branch -# (CLDMV/.github docs/conventions/release-flow-v4.md §5.4, §6.2). Fires on -# every push to `hotfixes` (hotfix/security PR squash-merges land here), and -# resolves-or-creates the persistent `hotfixes → master` release PR. Patches -# the current release independently of whatever is pending on `next`. +# (CLDMV/.github docs/conventions/release-flow-v4.md §5.4, §6.2). Fires on every +# push to `hotfixes` (hotfix/security PR squash-merges land here), and +# resolves-or-creates the persistent `hotfixes → master` release PR. # -# Same model as the next lane: the version bump rides on `hotfixes` as a -# `chore: bump version` commit, carried to master through the squash (§8.1). +# Thin caller: all job logic (plan / create / refresh) lives in the reusable, +# pinned at @v4. Bumping the pin carries fixes without editing this file. +# CUSTOMIZE `package_name` / `build_command` to match your package (same values +# as your next-release.yml). name: 🚑 Hotfixes Release (v4) on: push: branches: [hotfixes] - -permissions: - contents: write - pull-requests: write + workflow_dispatch: # manual kick — e.g. to open/refresh the PR for content already on `hotfixes` concurrency: group: hotfixes-release-${{ github.repository }} cancel-in-progress: false jobs: - plan: - # Loop guard: skip the bot's own chore-bump pushes and reset pushes. - # Replace `cldmv-bot[bot]` with your bot App's login if different. - if: | - github.actor != 'cldmv-bot[bot]' && - github.actor != 'github-actions[bot]' && - !startsWith(github.event.head_commit.message, 'chore: bump version') - name: "🔍 Plan (detect changes + resolve PR)" - runs-on: ubuntu-latest - outputs: - has-changes: ${{ steps.detect.outputs.has-changes }} - pr-number: ${{ steps.resolve.outputs.pr-number }} - steps: - - name: Create App token - id: app-token - uses: CLDMV/.github/.github/actions/github/steps/create-app-token@v4 - with: - client_id: ${{ secrets.CLDMV_BOT_APP_CLIENT_ID }} - private_key: ${{ secrets.CLDMV_BOT_APP_PRIVATE_KEY }} - env: - BOT_APP_CLIENT_ID: ${{ secrets.CLDMV_BOT_APP_CLIENT_ID }} - BOT_APP_PRIVATE_KEY: ${{ secrets.CLDMV_BOT_APP_PRIVATE_KEY }} - - - name: Checkout hotfixes - uses: CLDMV/.github/.github/actions/common/steps/checkout-code@v4 - with: - ref: hotfixes - fetch-depth: 0 - - - name: Detect master..hotfixes changes - id: detect - shell: bash - run: | - git fetch origin master --quiet - count=$(git rev-list --count origin/master..HEAD) - echo "📊 commits on hotfixes not yet on master: $count" - if [ "$count" -gt 0 ]; then - echo "has-changes=true" >> "$GITHUB_OUTPUT" - else - echo "has-changes=false" >> "$GITHUB_OUTPUT" - echo "ℹ️ hotfixes is in sync with master — nothing to release." - fi - - - name: Resolve persistent hotfixes→master PR - id: resolve - if: steps.detect.outputs.has-changes == 'true' - shell: bash - env: - GH_TOKEN: ${{ steps.app-token.outputs.token }} - run: | - pr=$(gh pr list --repo "$GITHUB_REPOSITORY" --head hotfixes --base master \ - --state open --json number --jq '.[0].number // ""') - echo "pr-number=$pr" >> "$GITHUB_OUTPUT" - if [ -n "$pr" ]; then - echo "🔁 existing hotfix release PR: #$pr — will refresh" - else - echo "🆕 no hotfix release PR yet — will create" - fi - - create: - name: "🆕 Create hotfix release PR" - needs: plan - if: needs.plan.outputs.has-changes == 'true' && needs.plan.outputs.pr-number == '' - runs-on: ubuntu-latest - steps: - - name: Create App token - id: app-token - uses: CLDMV/.github/.github/actions/github/steps/create-app-token@v4 - with: - client_id: ${{ secrets.CLDMV_BOT_APP_CLIENT_ID }} - private_key: ${{ secrets.CLDMV_BOT_APP_PRIVATE_KEY }} - env: - BOT_APP_CLIENT_ID: ${{ secrets.CLDMV_BOT_APP_CLIENT_ID }} - BOT_APP_PRIVATE_KEY: ${{ secrets.CLDMV_BOT_APP_PRIVATE_KEY }} - - # Customize `package-name` + `build-command` to match this repo — - # see notes in next-release.yml. - - name: Create release PR - uses: CLDMV/.github/.github/actions/github/jobs/create-release-pr@v4 - with: - package-name: "@cldmv/git-embedded" - build-command: "echo '✓ no build step'" - github-token: ${{ steps.app-token.outputs.token }} - - refresh: - name: "🔁 Refresh hotfix release PR #${{ needs.plan.outputs.pr-number }}" - needs: plan - if: needs.plan.outputs.has-changes == 'true' && needs.plan.outputs.pr-number != '' - runs-on: ubuntu-latest - steps: - - name: Create App token - id: app-token - uses: CLDMV/.github/.github/actions/github/steps/create-app-token@v4 - with: - client_id: ${{ secrets.CLDMV_BOT_APP_CLIENT_ID }} - private_key: ${{ secrets.CLDMV_BOT_APP_PRIVATE_KEY }} - env: - BOT_APP_CLIENT_ID: ${{ secrets.CLDMV_BOT_APP_CLIENT_ID }} - BOT_APP_PRIVATE_KEY: ${{ secrets.CLDMV_BOT_APP_PRIVATE_KEY }} - - - name: Refresh release PR - id: refresh - uses: CLDMV/.github/.github/actions/github/jobs/update-release-pr@v4 - with: - head-ref: hotfixes - pr-number: ${{ needs.plan.outputs.pr-number }} - package-name: "@cldmv/git-embedded" - build-command: "echo '✓ no build step'" - github-token: ${{ steps.app-token.outputs.token }} - - # Optional: release-PR notifier. See next-release.yml for the - # rationale. Delete the step to opt out entirely; leave a webhook - # secret unset to opt out of that one channel. - - name: Notify on release-PR version bump - if: steps.refresh.outputs.version-changed == 'true' - uses: CLDMV/.github/.github/actions/community/jobs/release-notifier@v4 - with: - event_kind: release_pr - pr_number: ${{ needs.plan.outputs.pr-number }} - version: ${{ steps.refresh.outputs.new-version }} - github_token: ${{ steps.app-token.outputs.token }} - env: - DISCORD_RELEASE_PR_PUBLIC_WEBHOOK: ${{ secrets.DISCORD_RELEASE_PR_PUBLIC_WEBHOOK }} - DISCORD_RELEASE_PR_PRIVATE_WEBHOOK: ${{ secrets.DISCORD_RELEASE_PR_PRIVATE_WEBHOOK }} - SLACK_RELEASE_PR_PUBLIC_WEBHOOK: ${{ secrets.SLACK_RELEASE_PR_PUBLIC_WEBHOOK }} - SLACK_RELEASE_PR_PRIVATE_WEBHOOK: ${{ secrets.SLACK_RELEASE_PR_PRIVATE_WEBHOOK }} - GENERIC_RELEASE_PR_PUBLIC_WEBHOOK: ${{ secrets.GENERIC_RELEASE_PR_PUBLIC_WEBHOOK }} - GENERIC_RELEASE_PR_PRIVATE_WEBHOOK: ${{ secrets.GENERIC_RELEASE_PR_PRIVATE_WEBHOOK }} + release: + permissions: + contents: write + pull-requests: write + uses: CLDMV/.github/.github/workflows/workflow-hotfixes-release.yml@v4 + with: + package_name: "@cldmv/git-embedded" + build_command: "echo '✓ no build step'" + secrets: + # Map your repo/org secrets to the expected names. + BOT_APP_CLIENT_ID: ${{ secrets.CLDMV_BOT_APP_CLIENT_ID }} + BOT_APP_PRIVATE_KEY: ${{ secrets.CLDMV_BOT_APP_PRIVATE_KEY }} + # Optional release-PR notifier webhooks — each is independently + # opt-in: leave one unset and that channel is silently skipped. + # Delete the lines you don't use. + DISCORD_RELEASE_PR_PUBLIC_WEBHOOK: ${{ secrets.DISCORD_RELEASE_PR_PUBLIC_WEBHOOK }} + DISCORD_RELEASE_PR_PRIVATE_WEBHOOK: ${{ secrets.DISCORD_RELEASE_PR_PRIVATE_WEBHOOK }} + SLACK_RELEASE_PR_PUBLIC_WEBHOOK: ${{ secrets.SLACK_RELEASE_PR_PUBLIC_WEBHOOK }} + SLACK_RELEASE_PR_PRIVATE_WEBHOOK: ${{ secrets.SLACK_RELEASE_PR_PRIVATE_WEBHOOK }} + GENERIC_RELEASE_PR_PUBLIC_WEBHOOK: ${{ secrets.GENERIC_RELEASE_PR_PUBLIC_WEBHOOK }} + GENERIC_RELEASE_PR_PRIVATE_WEBHOOK: ${{ secrets.GENERIC_RELEASE_PR_PRIVATE_WEBHOOK }} diff --git a/.github/workflows/master-commit-audit.yml b/.github/workflows/master-commit-audit.yml index ad6b2e8..44bb32a 100644 --- a/.github/workflows/master-commit-audit.yml +++ b/.github/workflows/master-commit-audit.yml @@ -48,13 +48,16 @@ jobs: uses: CLDMV/.github/.github/actions/git/jobs/audit-commit-subject@v4 with: commit_sha: ${{ github.sha }} - # Allow release commits (from the release workflow), maintenance - # commits, and standard merge commits. Customize per repo if - # your conventions differ. - allowed_patterns: | - ^release: v\d+\.\d+\.\d+( \(#\d+\))?$ - ^chore(\([^)]+\))?: .+ - ^Merge pull request #\d+ from .+ + # allowed_patterns omitted -> inherit the canonical default from + # audit-commit-subject (release/chore/merge patterns, including + # the "release: vX.Y.Z - " form). Customize only if + # this repo's conventions genuinely differ — a hardcoded copy + # here goes stale the next time the canonical default changes. + # allowed_patterns: | + # ^release: v\d+\.\d+\.\d+( - .+?)?( \(#\d+\))?$ + # ^chore(\([^)]+\))?: .+ + # ^Merge pull request #\d+ from .+ + # ^feat(\([^)]+\))?: .+ # Canonical label names from CLDMV/.github's data/github-labels.json # (note the space after each colon). Replace with names that exist # in your repo's label catalog. diff --git a/.github/workflows/next-release.yml b/.github/workflows/next-release.yml index bf0c701..5d4a9f8 100644 --- a/.github/workflows/next-release.yml +++ b/.github/workflows/next-release.yml @@ -1,6 +1,6 @@ # -# @Project: @cldmv/git-embedded -# @Filename: /.github/workflows/next-release.yml +# @Project: @cldmv/.github +# @Filename: /examples/individual-repo-workflows/release-flow-v4/next-release.yml # @Date: 2026-05-22 00:00:00 -07:00 (1779778800) # @Author: Nate Corcoran # @Email: @@ -17,19 +17,19 @@ # feature commits into a single release — that batching is v4's whole point # (see CLDMV/.github docs/conventions/release-flow-v4.md §5.3, §6.1). # -# The version bump rides on `next` as a `chore: bump version` commit pushed -# by the release-PR machinery; it's carried to master through the squash -# (§8.1 — master accepts changes only via PR squash, and the publish flow -# reads package.json as-is). +# Thin caller: all job logic (plan / create / refresh) lives in the reusable, +# pinned at @v4. Bumping the pin carries fixes without editing this file. +# CUSTOMIZE: +# - `package_name` → your npm package (or any unique identifier) +# - `build_command` → your build script, or a stub like +# `echo '✓ no build step'` for a meta package (optional; +# defaults to `npm run build:ci`) name: 🚀 Next Release (v4) on: push: branches: [next] - -permissions: - contents: write - pull-requests: write + workflow_dispatch: # manual kick — e.g. to open/refresh the PR for content already on `next` # Serialize: each run re-resolves the current PR state, so queueing (not # cancelling) avoids a create/refresh race when pushes land back-to-back. @@ -38,141 +38,24 @@ concurrency: cancel-in-progress: false jobs: - plan: - # Loop guard: the refresh/create steps push a `chore: bump version` - # commit to `next` (as the bot), and next-reset.yml force-pushes - # `next` (as the bot). Neither should re-trigger a release-PR refresh. - # Replace `cldmv-bot[bot]` with your bot App's login if different. - if: | - github.actor != 'cldmv-bot[bot]' && - github.actor != 'github-actions[bot]' && - !startsWith(github.event.head_commit.message, 'chore: bump version') - name: "🔍 Plan (detect changes + resolve PR)" - runs-on: ubuntu-latest - outputs: - has-changes: ${{ steps.detect.outputs.has-changes }} - pr-number: ${{ steps.resolve.outputs.pr-number }} - steps: - - name: Create App token - id: app-token - uses: CLDMV/.github/.github/actions/github/steps/create-app-token@v4 - with: - client_id: ${{ secrets.CLDMV_BOT_APP_CLIENT_ID }} - private_key: ${{ secrets.CLDMV_BOT_APP_PRIVATE_KEY }} - env: - BOT_APP_CLIENT_ID: ${{ secrets.CLDMV_BOT_APP_CLIENT_ID }} - BOT_APP_PRIVATE_KEY: ${{ secrets.CLDMV_BOT_APP_PRIVATE_KEY }} - - - name: Checkout next - uses: CLDMV/.github/.github/actions/common/steps/checkout-code@v4 - with: - ref: next - fetch-depth: 0 - - - name: Detect master..next changes - id: detect - shell: bash - run: | - git fetch origin master --quiet - count=$(git rev-list --count origin/master..HEAD) - echo "📊 commits on next not yet on master: $count" - if [ "$count" -gt 0 ]; then - echo "has-changes=true" >> "$GITHUB_OUTPUT" - else - echo "has-changes=false" >> "$GITHUB_OUTPUT" - echo "ℹ️ next is in sync with master — nothing to release." - fi - - - name: Resolve persistent next→master PR - id: resolve - if: steps.detect.outputs.has-changes == 'true' - shell: bash - env: - GH_TOKEN: ${{ steps.app-token.outputs.token }} - run: | - # The persistent release PR is the open PR with head=next, - # base=master. There is at most one (concurrency-serialized). - pr=$(gh pr list --repo "$GITHUB_REPOSITORY" --head next --base master \ - --state open --json number --jq '.[0].number // ""') - echo "pr-number=$pr" >> "$GITHUB_OUTPUT" - if [ -n "$pr" ]; then - echo "🔁 existing release PR: #$pr — will refresh" - else - echo "🆕 no release PR yet — will create" - fi - - create: - name: "🆕 Create release PR" - needs: plan - if: needs.plan.outputs.has-changes == 'true' && needs.plan.outputs.pr-number == '' - runs-on: ubuntu-latest - steps: - - name: Create App token - id: app-token - uses: CLDMV/.github/.github/actions/github/steps/create-app-token@v4 - with: - client_id: ${{ secrets.CLDMV_BOT_APP_CLIENT_ID }} - private_key: ${{ secrets.CLDMV_BOT_APP_PRIVATE_KEY }} - env: - BOT_APP_CLIENT_ID: ${{ secrets.CLDMV_BOT_APP_CLIENT_ID }} - BOT_APP_PRIVATE_KEY: ${{ secrets.CLDMV_BOT_APP_PRIVATE_KEY }} - - # Runs on the `next` ref → create-release-pr opens next → master and - # pushes the chore-bump commit to next. Customize: - # - `package-name` → your npm package (or any unique identifier) - # - `build-command` → your build script, or a stub like - # `echo '✓ no build step'` for a meta package - - name: Create release PR - uses: CLDMV/.github/.github/actions/github/jobs/create-release-pr@v4 - with: - package-name: "@cldmv/git-embedded" - build-command: "echo '✓ no build step'" - github-token: ${{ steps.app-token.outputs.token }} - - refresh: - name: "🔁 Refresh release PR #${{ needs.plan.outputs.pr-number }}" - needs: plan - if: needs.plan.outputs.has-changes == 'true' && needs.plan.outputs.pr-number != '' - runs-on: ubuntu-latest - steps: - - name: Create App token - id: app-token - uses: CLDMV/.github/.github/actions/github/steps/create-app-token@v4 - with: - client_id: ${{ secrets.CLDMV_BOT_APP_CLIENT_ID }} - private_key: ${{ secrets.CLDMV_BOT_APP_PRIVATE_KEY }} - env: - BOT_APP_CLIENT_ID: ${{ secrets.CLDMV_BOT_APP_CLIENT_ID }} - BOT_APP_PRIVATE_KEY: ${{ secrets.CLDMV_BOT_APP_PRIVATE_KEY }} - - - name: Refresh release PR - id: refresh - uses: CLDMV/.github/.github/actions/github/jobs/update-release-pr@v4 - with: - head-ref: next - pr-number: ${{ needs.plan.outputs.pr-number }} - package-name: "@cldmv/git-embedded" - build-command: "echo '✓ no build step'" - github-token: ${{ steps.app-token.outputs.token }} - - # Optional: release-PR notifier. Fires only when the target - # version actually changes (PR open or version-bump shift), not - # on the changelog-only refreshes that run on every push. Each - # secret is independently opt-in: leave a webhook unset and that - # channel is silently skipped. Delete this step to opt out - # entirely. - - name: Notify on release-PR version bump - if: steps.refresh.outputs.version-changed == 'true' - uses: CLDMV/.github/.github/actions/community/jobs/release-notifier@v4 - with: - event_kind: release_pr - pr_number: ${{ needs.plan.outputs.pr-number }} - version: ${{ steps.refresh.outputs.new-version }} - github_token: ${{ steps.app-token.outputs.token }} - env: - DISCORD_RELEASE_PR_PUBLIC_WEBHOOK: ${{ secrets.DISCORD_RELEASE_PR_PUBLIC_WEBHOOK }} - DISCORD_RELEASE_PR_PRIVATE_WEBHOOK: ${{ secrets.DISCORD_RELEASE_PR_PRIVATE_WEBHOOK }} - SLACK_RELEASE_PR_PUBLIC_WEBHOOK: ${{ secrets.SLACK_RELEASE_PR_PUBLIC_WEBHOOK }} - SLACK_RELEASE_PR_PRIVATE_WEBHOOK: ${{ secrets.SLACK_RELEASE_PR_PRIVATE_WEBHOOK }} - GENERIC_RELEASE_PR_PUBLIC_WEBHOOK: ${{ secrets.GENERIC_RELEASE_PR_PUBLIC_WEBHOOK }} - GENERIC_RELEASE_PR_PRIVATE_WEBHOOK: ${{ secrets.GENERIC_RELEASE_PR_PRIVATE_WEBHOOK }} + release: + permissions: + contents: write + pull-requests: write + uses: CLDMV/.github/.github/workflows/workflow-next-release.yml@v4 + with: + package_name: "@cldmv/git-embedded" + build_command: "echo '✓ no build step'" + secrets: + # Map your repo/org secrets to the expected names. + BOT_APP_CLIENT_ID: ${{ secrets.CLDMV_BOT_APP_CLIENT_ID }} + BOT_APP_PRIVATE_KEY: ${{ secrets.CLDMV_BOT_APP_PRIVATE_KEY }} + # Optional release-PR notifier webhooks — each is independently + # opt-in: leave one unset and that channel is silently skipped. + # Delete the lines you don't use. + DISCORD_RELEASE_PR_PUBLIC_WEBHOOK: ${{ secrets.DISCORD_RELEASE_PR_PUBLIC_WEBHOOK }} + DISCORD_RELEASE_PR_PRIVATE_WEBHOOK: ${{ secrets.DISCORD_RELEASE_PR_PRIVATE_WEBHOOK }} + SLACK_RELEASE_PR_PUBLIC_WEBHOOK: ${{ secrets.SLACK_RELEASE_PR_PUBLIC_WEBHOOK }} + SLACK_RELEASE_PR_PRIVATE_WEBHOOK: ${{ secrets.SLACK_RELEASE_PR_PRIVATE_WEBHOOK }} + GENERIC_RELEASE_PR_PUBLIC_WEBHOOK: ${{ secrets.GENERIC_RELEASE_PR_PUBLIC_WEBHOOK }} + GENERIC_RELEASE_PR_PRIVATE_WEBHOOK: ${{ secrets.GENERIC_RELEASE_PR_PRIVATE_WEBHOOK }} diff --git a/.github/workflows/next-reset.yml b/.github/workflows/next-reset.yml index f19a665..87ba088 100644 --- a/.github/workflows/next-reset.yml +++ b/.github/workflows/next-reset.yml @@ -1,6 +1,6 @@ # -# @Project: @cldmv/git-embedded -# @Filename: /.github/workflows/next-reset.yml +# @Project: @cldmv/.github +# @Filename: /examples/individual-repo-workflows/release-flow-v4/next-reset.yml # @Date: 2026-05-22 00:00:00 -07:00 (1779778800) # @Author: Nate Corcoran # @Email: @@ -16,184 +16,28 @@ # - `next` depends on which lane released: # * normal release (next → master, or a v3-style feat → master): # force-reset `next` to master HEAD (§7.1). -# * hotfix release (hotfixes → master): MERGE master into `next` -# instead, so next's accumulated feature work is preserved (§7.2, -# option B). The merge is a no-op (204) when next has no extra work. +# * hotfix release (hotfixes → master): MERGE master into `next` instead, +# so next's accumulated feature work is preserved (§7.2, option B). # -# The released lane is detected from the PR head ref behind the squash -# commit's trailing "(#N)". -# -# wait-for-tags gate: a release also fires update-major-version-tags, which -# rolls the major tags. Jobs resolve `uses: ...@vN` at job start, so without -# this gate the sync job can run the PREVIOUS release's action code. The gate -# polls the RELEASED major's tag — parsed from the `release: vX.Y.Z` commit -# — until it matches the release commit. -# -# Self-healing: no-ops pre-cutover (neither integration branch exists), but -# post-cutover it RECREATES a branch that went missing — e.g. branch-retention -# deleting `next` as a merged PR head. force-reset-branch creates the ref -# when it's absent. +# Thin caller: all job logic (the wait-for-tags gate + the branch sync) lives in +# the reusable, pinned at @v4. Bumping the pin carries fixes without editing +# this file. name: ♻️ Next/Hotfixes Reset (v4) on: push: branches: [master, main] -permissions: - contents: write - concurrency: group: next-reset-${{ github.repository }} cancel-in-progress: false jobs: - wait-for-tags: - # Only fire on a release commit (the squash-merge of a release PR). - if: startsWith(github.event.head_commit.message, 'release:') - name: "⏳ Wait for the released major tag to roll forward" - runs-on: ubuntu-latest - timeout-minutes: 5 - steps: - - name: Poll the released major tag until it matches the release commit - shell: bash - env: - TARGET_SHA: ${{ github.sha }} - REPO: ${{ github.repository }} - COMMIT_MSG: ${{ github.event.head_commit.message }} - run: | - echo "🔍 Release commit: $TARGET_SHA" - # Parse the released MAJOR from the `release: vX.Y.Z` subject - # and poll THAT tag (e.g. @v4 for v4.x). update-major-version- - # tags rolls @v to the release commit; a hardcoded @v3 - # would never match on a major bump (which creates @v4). - major=$(printf '%s' "$COMMIT_MSG" | grep -oiE 'release:[^0-9]*v?[0-9]+' | grep -oE '[0-9]+$' | head -1) - if [ -z "$major" ]; then - echo "⚠️ Could not parse a major version from the commit subject — skipping the gate." - exit 0 - fi - tag="v${major}" - echo "⏳ Gating on @${tag}…" - max_attempts=24 # 24 * 5s = 120s - for attempt in $(seq 1 $max_attempts); do - sha=$(git ls-remote "https://github.com/${REPO}.git" "refs/tags/${tag}^{}" 2>/dev/null | awk '{print $1}') - [ -z "$sha" ] && sha=$(git ls-remote "https://github.com/${REPO}.git" "refs/tags/${tag}" 2>/dev/null | awk '{print $1}') - echo "Attempt $attempt/$max_attempts: @${tag} → ${sha:-}" - if [ "$sha" = "$TARGET_SHA" ]; then - echo "✅ @${tag} matches the release commit — safe to proceed" - exit 0 - fi - [ "$attempt" -lt "$max_attempts" ] && sleep 5 - done - echo "⚠️ Timed out waiting for @${tag} — proceeding anyway." - - sync-branches: - name: "♻️ Sync next + hotfixes to master" - needs: wait-for-tags - if: startsWith(github.event.head_commit.message, 'release:') - runs-on: ubuntu-latest - steps: - - name: Create App token - id: app-token - uses: CLDMV/.github/.github/actions/github/steps/create-app-token@v4 - with: - client_id: ${{ secrets.CLDMV_BOT_APP_CLIENT_ID }} - private_key: ${{ secrets.CLDMV_BOT_APP_PRIVATE_KEY }} - # Pushing/merging master's tree (which includes - # .github/workflows/**) requires contents + workflows write. - permission_contents: "true" - permission_workflows: "true" - env: - BOT_APP_CLIENT_ID: ${{ secrets.CLDMV_BOT_APP_CLIENT_ID }} - BOT_APP_PRIVATE_KEY: ${{ secrets.CLDMV_BOT_APP_PRIVATE_KEY }} - - - name: Checkout master - uses: CLDMV/.github/.github/actions/common/steps/checkout-code@v4 - with: - fetch-depth: 0 - - - name: Determine released lane - id: lane - shell: bash - env: - GH_TOKEN: ${{ steps.app-token.outputs.token }} - COMMIT_MSG: ${{ github.event.head_commit.message }} - run: | - # The squash commit ends with "(#N)" — the merged PR number. - prnum=$(printf '%s' "$COMMIT_MSG" | grep -oE '#[0-9]+' | tail -1 | tr -d '#') - head="" - if [ -n "$prnum" ]; then - head=$(gh pr view "$prnum" --repo "$GITHUB_REPOSITORY" \ - --json headRefName --jq '.headRefName' 2>/dev/null || echo "") - fi - echo "released PR #${prnum:-?} head ref: ${head:-}" - if [ "$head" = "hotfixes" ]; then - echo "lane=hotfix" >> "$GITHUB_OUTPUT" - else - echo "lane=other" >> "$GITHUB_OUTPUT" - fi - - - name: Guard — is this repo on v4? (do next/hotfixes exist?) - id: guard - shell: bash - run: | - next_exists=false; hotfixes_exists=false - if git ls-remote --exit-code --heads origin next >/dev/null 2>&1; then - next_exists=true - git fetch origin next:refs/remotes/origin/next --quiet || true - fi - if git ls-remote --exit-code --heads origin hotfixes >/dev/null 2>&1; then - hotfixes_exists=true - git fetch origin hotfixes:refs/remotes/origin/hotfixes --quiet || true - fi - # "v4 adopted" = at least one integration branch exists. Keeps - # the reset a no-op on pre-cutover repos (neither exists) while - # letting it RECREATE a branch that went missing post-cutover - # (e.g. one was deleted as a merged PR head). force-reset-branch - # creates if absent. - v4_adopted=false - { [ "$next_exists" = true ] || [ "$hotfixes_exists" = true ]; } && v4_adopted=true - { - echo "next-exists=$next_exists" - echo "hotfixes-exists=$hotfixes_exists" - echo "v4-adopted=$v4_adopted" - } >> "$GITHUB_OUTPUT" - echo "ℹ️ next=$next_exists hotfixes=$hotfixes_exists v4-adopted=$v4_adopted lane=${{ steps.lane.outputs.lane }}" - - # hotfixes always tracks master after a release — created if missing. - - name: Ensure hotfixes = master HEAD (reset; create if missing) - if: steps.guard.outputs.v4-adopted == 'true' - uses: CLDMV/.github/.github/actions/git/steps/force-reset-branch@v4 - with: - target-branch: hotfixes - source-ref: master - github-token: ${{ steps.app-token.outputs.token }} - - # Normal release → next is force-reset (its work just shipped), and - # recreated if it was deleted on merge. - - name: Ensure next = master HEAD (normal release; create if missing) - if: steps.guard.outputs.v4-adopted == 'true' && steps.lane.outputs.lane != 'hotfix' - uses: CLDMV/.github/.github/actions/git/steps/force-reset-branch@v4 - with: - target-branch: next - source-ref: master - github-token: ${{ steps.app-token.outputs.token }} - - # Hotfix release + next still exists → merge master into next to - # preserve its accumulated feature work (§7.2 option B). - - name: Merge master into next (hotfix release; next exists) - if: steps.guard.outputs.v4-adopted == 'true' && steps.lane.outputs.lane == 'hotfix' && steps.guard.outputs.next-exists == 'true' - uses: CLDMV/.github/.github/actions/github/steps/merge-master-into-branch@v4 - with: - target-branch: next - source-ref: master - github-token: ${{ steps.app-token.outputs.token }} - - # Hotfix release but next is MISSING (deleted) → recreate it at master - # HEAD; there's no accumulated work to preserve. - - name: Recreate next at master HEAD (hotfix release; next missing) - if: steps.guard.outputs.v4-adopted == 'true' && steps.lane.outputs.lane == 'hotfix' && steps.guard.outputs.next-exists == 'false' - uses: CLDMV/.github/.github/actions/git/steps/force-reset-branch@v4 - with: - target-branch: next - source-ref: master - github-token: ${{ steps.app-token.outputs.token }} + sync: + permissions: + contents: write + uses: CLDMV/.github/.github/workflows/workflow-next-reset.yml@v4 + secrets: + # Map your repo/org secrets to the expected names. + BOT_APP_CLIENT_ID: ${{ secrets.CLDMV_BOT_APP_CLIENT_ID }} + BOT_APP_PRIVATE_KEY: ${{ secrets.CLDMV_BOT_APP_PRIVATE_KEY }} diff --git a/.github/workflows/pr-title-normalizer.yml b/.github/workflows/pr-title-normalizer.yml index f7dd668..adba973 100644 --- a/.github/workflows/pr-title-normalizer.yml +++ b/.github/workflows/pr-title-normalizer.yml @@ -1,6 +1,6 @@ # -# @Project: @cldmv/git-embedded -# @Filename: /.github/workflows/pr-title-normalizer.yml +# @Project: @cldmv/.github +# @Filename: /examples/individual-repo-workflows/release-flow-v4/pr-title-normalizer.yml # @Date: 2026-05-22 00:00:00 -07:00 (1779778800) # @Author: Nate Corcoran # @Email: @@ -9,56 +9,37 @@ # Individual repo: .github/workflows/pr-title-normalizer.yml # -# Normalize contributor PR titles to Conventional Commits format, derived -# from the highest-priority commit in the PR. The release flow expects this -# shape, so a v4 repo wants this enabled. (Also backportable to v3 repos — -# it wires the normalize-pr-title action, shipped in v3.3.0; the action owns -# all skip logic: bot authors, the long-running release PRs, titles already -# starting with `release:`, and titles that already conform.) +# Normalize contributor PR titles to Conventional Commits format, derived from +# the highest-priority commit in the PR. The release flow expects this shape, so +# a v4 repo wants this enabled. (Also backportable to v3 repos — the underlying +# action shipped in v3.3.0; it owns all skip logic: bot authors, the +# long-running release PRs, titles already starting with `release:`, and titles +# that already conform.) +# +# Thin caller: all job logic lives in the reusable, pinned at @v4. Bumping the +# pin carries fixes without editing this file. name: 🏷️ PR Title Normalizer -# SECURITY NOTE: pull_request_target runs in the BASE repo's context with -# WRITE permissions and access to secrets. SAFE for THIS workflow because it -# is API-only — the normalize-pr-title action never checks out the PR head -# and never executes PR content. DO NOT add a checkout step. -# -# Triggers on opened + synchronize only (NOT edited): a maintainer hand- -# editing the title must not kick off a re-normalize loop. +# SECURITY NOTE: pull_request_target runs in the BASE repo's context with WRITE +# permissions and access to secrets. SAFE — the reusable's path is API-only (it +# never checks out or executes PR content). Triggers on opened + synchronize +# only (NOT edited): a maintainer hand-editing the title must not kick off a +# re-normalize loop. on: pull_request_target: types: [opened, synchronize] -permissions: - contents: read - pull-requests: write - -# Collapse a burst of pushes to one normalize run per PR; the newest push -# carries the authoritative commit set, so cancelling an in-flight run is fine. concurrency: group: pr-title-normalizer-${{ github.event.pull_request.number }} cancel-in-progress: true jobs: normalize: - name: "✏️ Normalize title" - runs-on: ubuntu-latest - steps: - - name: Create App token (falls back to GITHUB_TOKEN) - id: app-token - uses: CLDMV/.github/.github/actions/github/steps/create-app-token@v4 - with: - client_id: ${{ secrets.CLDMV_BOT_APP_CLIENT_ID }} - private_key: ${{ secrets.CLDMV_BOT_APP_PRIVATE_KEY }} - env: - BOT_APP_CLIENT_ID: ${{ secrets.CLDMV_BOT_APP_CLIENT_ID }} - BOT_APP_PRIVATE_KEY: ${{ secrets.CLDMV_BOT_APP_PRIVATE_KEY }} - - - name: Normalize PR title - uses: CLDMV/.github/.github/actions/github/steps/normalize-pr-title@v4 - with: - pr-number: ${{ github.event.pull_request.number }} - github-token: ${{ steps.app-token.outputs.token }} - base-ref: ${{ github.event.pull_request.base.ref }} - head-ref: ${{ github.event.pull_request.head.ref }} - user-type: ${{ github.event.pull_request.user.type }} - user-login: ${{ github.event.pull_request.user.login }} + permissions: + contents: read + pull-requests: write + uses: CLDMV/.github/.github/workflows/workflow-pr-title-normalizer.yml@v4 + secrets: + # Map your repo/org secrets to the expected names. + BOT_APP_CLIENT_ID: ${{ secrets.CLDMV_BOT_APP_CLIENT_ID }} + BOT_APP_PRIVATE_KEY: ${{ secrets.CLDMV_BOT_APP_PRIVATE_KEY }} diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index 9914a0b..814b87a 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -63,7 +63,7 @@ on: description: "Minimum Node.js version for matrix testing (enables matrix when set)" type: string required: false - default: "20" + default: "22" max_node_major: description: "Override max Node.js major version (default: 22)" type: string @@ -101,7 +101,7 @@ jobs: publish_to_github_packages: ${{ github.event.inputs.publish_to_github_packages != 'false' }} publish_command: "" github_packages_publish_command: "" - min_node_version: ${{ github.event.inputs.min_node_version || '20' }} + min_node_version: ${{ github.event.inputs.min_node_version || '22' }} max_node_major: ${{ github.event.inputs.max_node_major || '22' }} test_command: "npm test" # Use defaults: NODE_ENV=development, NODE_OPTIONS=--conditions=development # test_command: "NODE_OPTIONS='--conditions=slothlet-dev' npm test" # Override NODE_OPTIONS only diff --git a/package-lock.json b/package-lock.json index 2b2f0fc..35c4e0e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,18 +1,18 @@ { "name": "@cldmv/git-embedded", - "version": "1.1.2", + "version": "1.1.3", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@cldmv/git-embedded", - "version": "1.1.2", + "version": "1.1.3", "license": "Apache-2.0", "dependencies": { "@cldmv/slothlet": "^3.7.0", "@cldmv/wisp": "^1.0.1", - "chalk": "^5.4.1", - "commander": "^14.0.0", + "chalk": "^6.0.0", + "commander": "^15.0.0", "marked": "^15.0.12", "marked-terminal": "^7.3.0" }, @@ -22,11 +22,11 @@ "devDependencies": { "@cldmv/vitest-runner": "^1.2.0", "@eslint/js": "^9.18.0", - "@eslint/json": "^0.10.0", - "@eslint/markdown": "^6.2.2", + "@eslint/json": "^2.0.1", + "@eslint/markdown": "^8.0.3", "@vitest/coverage-v8": "^4.1.10", - "eslint": "^9.18.0", - "globals": "^15.14.0", + "eslint": "^10.8.0", + "globals": "^17.7.0", "prettier": "^3.4.2", "vitest": "^4.1.10" }, @@ -99,28 +99,36 @@ } }, "node_modules/@cldmv/slothlet": { - "version": "3.7.0", - "resolved": "https://registry.npmjs.org/@cldmv/slothlet/-/slothlet-3.7.0.tgz", - "integrity": "sha512-HZmZlj+Wmo+952UDTnrlBR8Cv05hNTv3DEdQDbOL6nPh0BKqgSOdtt9UOby+6Lvgi1eOaRhrpIq+uFWWcsYlPQ==", + "version": "3.12.2", + "resolved": "https://registry.npmjs.org/@cldmv/slothlet/-/slothlet-3.12.2.tgz", + "integrity": "sha512-YRYdh5uhY0ynsgcT6nWFOzHyVsFlp8XhFkF52atq6WA/HgHPD/D/cxruSx2eDZUfHL/t2W4uspIINgsfQbatqg==", "license": "Apache-2.0", "bin": { "slothlet": "bin/slothlet.mjs" }, "engines": { - "node": ">=20.19.0" + "node": ">=22.0.0" }, "funding": { "type": "github", "url": "https://github.com/sponsors/shinrai" }, "optionalDependencies": { - "@rolldown/binding-linux-x64-gnu": "1.0.0-rc.17" + "@rolldown/binding-linux-x64-gnu": "1.0.3" }, "peerDependencies": { + "@cldmv/slothlet-i18n": "^3.11.0", + "@cldmv/slothlet-types": "^3.11.0", "esbuild": "^0.28.0", "typescript": "^6.0.3" }, "peerDependenciesMeta": { + "@cldmv/slothlet-i18n": { + "optional": true + }, + "@cldmv/slothlet-types": { + "optional": true + }, "esbuild": { "optional": true }, @@ -148,6 +156,19 @@ "vitest": ">=1.0.0" } }, + "node_modules/@cldmv/vitest-runner/node_modules/chalk": { + "version": "5.6.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.6.2.tgz", + "integrity": "sha512-7NzBL0rN6fMUW+f7A6Io4h40qQlG+xGmtMxfbnH/K7TAtt8JQWVQK+6g0UXKMeVJoyV5EkkNsErQ8pVD3bLHbA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.17.0 || ^14.13 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, "node_modules/@cldmv/wisp": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/@cldmv/wisp/-/wisp-1.0.1.tgz", @@ -248,94 +269,44 @@ } }, "node_modules/@eslint/config-array": { - "version": "0.21.2", - "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.21.2.tgz", - "integrity": "sha512-nJl2KGTlrf9GjLimgIru+V/mzgSK0ABCDQRvxw5BjURL7WfH5uoWmizbH7QB6MmnMBd8cIC9uceWnezL1VZWWw==", + "version": "0.23.5", + "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.23.5.tgz", + "integrity": "sha512-Y3kKLvC1dvTOT+oGlqNQ1XLqK6D1HU2YXPc52NmAlJZbMMWDzGYXMiPRJ8TYD39muD/OTjlZmNJ4ib7dvSrMBA==", "dev": true, "license": "Apache-2.0", "dependencies": { - "@eslint/object-schema": "^2.1.7", + "@eslint/object-schema": "^3.0.5", "debug": "^4.3.1", - "minimatch": "^3.1.5" + "minimatch": "^10.2.4" }, "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + "node": "^20.19.0 || ^22.13.0 || >=24" } }, "node_modules/@eslint/config-helpers": { - "version": "0.4.2", - "resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.4.2.tgz", - "integrity": "sha512-gBrxN88gOIf3R7ja5K9slwNayVcZgK6SOUORm2uBzTeIEfeVaIhOpCtTox3P6R7o2jLFwLFTLnC7kU/RGcYEgw==", + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.7.0.tgz", + "integrity": "sha512-DObd/KKUsU+FaFv4PLxSRenpXfQWmPXXP3pPZ6/K1PCrMu2vQpMDMuQe/BqYeoLcz8ro0bVDF1RxOJgfVEdhUw==", "dev": true, "license": "Apache-2.0", "dependencies": { - "@eslint/core": "^0.17.0" + "@eslint/core": "^1.2.1" }, "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - } - }, - "node_modules/@eslint/config-helpers/node_modules/@eslint/core": { - "version": "0.17.0", - "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.17.0.tgz", - "integrity": "sha512-yL/sLrpmtDaFEiUj1osRP4TI2MDz1AddJL+jZ7KSqvBuliN4xqYY54IfdN8qD8Toa6g1iloph1fxQNkjOxrrpQ==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@types/json-schema": "^7.0.15" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + "node": "^20.19.0 || ^22.13.0 || >=24" } }, "node_modules/@eslint/core": { - "version": "0.10.0", - "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.10.0.tgz", - "integrity": "sha512-gFHJ+xBOo4G3WRlR1e/3G8A6/KZAH6zcE/hkLRCZTi/B9avAG365QhFA8uOGzTMqgTghpn7/fSnscW++dpMSAw==", + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@eslint/core/-/core-1.2.1.tgz", + "integrity": "sha512-MwcE1P+AZ4C6DWlpin/OmOA54mmIZ/+xZuJiQd4SyB29oAJjN30UW9wkKNptW2ctp4cEsvhlLY/CsQ1uoHDloQ==", "dev": true, "license": "Apache-2.0", "dependencies": { "@types/json-schema": "^7.0.15" }, "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - } - }, - "node_modules/@eslint/eslintrc": { - "version": "3.3.5", - "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-3.3.5.tgz", - "integrity": "sha512-4IlJx0X0qftVsN5E+/vGujTRIFtwuLbNsVUe7TO6zYPDR1O6nFwvwhIKEKSrl6dZchmYBITazxKoUYOjdtjlRg==", - "dev": true, - "license": "MIT", - "dependencies": { - "ajv": "^6.14.0", - "debug": "^4.3.2", - "espree": "^10.0.1", - "globals": "^14.0.0", - "ignore": "^5.2.0", - "import-fresh": "^3.2.1", - "js-yaml": "^4.1.1", - "minimatch": "^3.1.5", - "strip-json-comments": "^3.1.1" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, - "node_modules/@eslint/eslintrc/node_modules/globals": { - "version": "14.0.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-14.0.0.tgz", - "integrity": "sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": "^20.19.0 || ^22.13.0 || >=24" } }, "node_modules/@eslint/js": { @@ -352,119 +323,69 @@ } }, "node_modules/@eslint/json": { - "version": "0.10.0", - "resolved": "https://registry.npmjs.org/@eslint/json/-/json-0.10.0.tgz", - "integrity": "sha512-aW0181XofoAwuB0sI263bhwnvAlb6OGnGGxHjTZQNLFErfsAXCnwRD+Uxx6/pK73XtKuQ2w1wbhiTJ2BC/erAQ==", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@eslint/json/-/json-2.0.1.tgz", + "integrity": "sha512-Thz2j92ceUF3Bq/0TuWb3MWn3Z+Cwc8k5ptF0fakl2D4Mp8mSx07Xr1aQM4R5NoihzarWUdxfOmQ8DesGy4jOg==", "dev": true, "license": "Apache-2.0", "dependencies": { - "@eslint/core": "^0.10.0", - "@eslint/plugin-kit": "^0.2.5", - "@humanwhocodes/momoa": "^3.3.4", + "@eslint/core": "^1.2.1", + "@eslint/plugin-kit": "^0.7.2", + "@humanwhocodes/momoa": "^3.3.10", "natural-compare": "^1.4.0" }, "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + "node": "^20.19.0 || ^22.13.0 || >=24" } }, "node_modules/@eslint/markdown": { - "version": "6.6.0", - "resolved": "https://registry.npmjs.org/@eslint/markdown/-/markdown-6.6.0.tgz", - "integrity": "sha512-IsWPy2jU3gaQDlioDC4sT4I4kG1hX1OMWs/q2sWwJrPoMASHW/Z4SDw+6Aql6EsHejGbagYuJbFq9Zvx+Y1b1Q==", + "version": "8.0.3", + "resolved": "https://registry.npmjs.org/@eslint/markdown/-/markdown-8.0.3.tgz", + "integrity": "sha512-rBTSSShrq7e4O+PWfeE4azH4/CWPNrC+VGxBXiW00o3vYVJnznsZiDayj3KC9JztIMVRZxZHn2nrDIUau/4j7A==", "dev": true, "license": "MIT", "workspaces": [ "examples/*" ], "dependencies": { - "@eslint/core": "^0.14.0", - "@eslint/plugin-kit": "^0.3.1", + "@eslint/core": "^1.2.1", + "@eslint/plugin-kit": "^0.7.2", "github-slugger": "^2.0.0", "mdast-util-from-markdown": "^2.0.2", "mdast-util-frontmatter": "^2.0.1", - "mdast-util-gfm": "^3.0.0", + "mdast-util-gfm": "^3.1.0", + "mdast-util-math": "^3.0.0", "micromark-extension-frontmatter": "^2.0.0", - "micromark-extension-gfm": "^3.0.0" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - } - }, - "node_modules/@eslint/markdown/node_modules/@eslint/core": { - "version": "0.14.0", - "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.14.0.tgz", - "integrity": "sha512-qIbV0/JZr7iSDjqAc60IqbLdsj9GDt16xQtWD+B78d/HAlvysGdZZ6rpJHGAc2T0FQx1X6thsSPdnoiGKdNtdg==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@types/json-schema": "^7.0.15" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - } - }, - "node_modules/@eslint/markdown/node_modules/@eslint/plugin-kit": { - "version": "0.3.5", - "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.3.5.tgz", - "integrity": "sha512-Z5kJ+wU3oA7MMIqVR9tyZRtjYPr4OC004Q4Rw7pgOKUOKkJfZ3O24nz3WYfGRpMDNmcOi3TwQOmgm7B7Tpii0w==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@eslint/core": "^0.15.2", - "levn": "^0.4.1" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - } - }, - "node_modules/@eslint/markdown/node_modules/@eslint/plugin-kit/node_modules/@eslint/core": { - "version": "0.15.2", - "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.15.2.tgz", - "integrity": "sha512-78Md3/Rrxh83gCxoUc0EiciuOHsIITzLy53m3d9UyiW8y9Dj2D29FeETqyKA+BRK76tnTp6RXWb3pCay8Oyomg==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@types/json-schema": "^7.0.15" + "micromark-extension-gfm": "^3.0.0", + "micromark-extension-math": "^3.1.0", + "micromark-util-normalize-identifier": "^2.0.1" }, "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + "node": "^20.19.0 || ^22.13.0 || >=24" } }, "node_modules/@eslint/object-schema": { - "version": "2.1.7", - "resolved": "https://registry.npmjs.org/@eslint/object-schema/-/object-schema-2.1.7.tgz", - "integrity": "sha512-VtAOaymWVfZcmZbp6E2mympDIHvyjXs/12LqWYjVw6qjrfF+VK+fyG33kChz3nnK+SU5/NeHOqrTEHS8sXO3OA==", + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/@eslint/object-schema/-/object-schema-3.0.5.tgz", + "integrity": "sha512-vqTaUEgxzm+YDSdElad6PiRoX4t8VGDjCtt05zn4nU810UIx/uNEV7/lZJ6KwFThKZOzOxzXy48da+No7HZaMw==", "dev": true, "license": "Apache-2.0", "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + "node": "^20.19.0 || ^22.13.0 || >=24" } }, "node_modules/@eslint/plugin-kit": { - "version": "0.2.8", - "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.2.8.tgz", - "integrity": "sha512-ZAoA40rNMPwSm+AeHpCq8STiNAwzWLJuP8Xv4CHIc9wv/PSuExjMrmjfYNj682vW0OOiZ1HKxzvjQr9XZIisQA==", + "version": "0.7.2", + "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.7.2.tgz", + "integrity": "sha512-+CNAzxglkrpNf/kKywqQfk74QjtceuOE7Qm+AF8miRvPF/wmmK5+OJOgVh3AVTT3RP2mH3+FOaxlE5v72owk0A==", "dev": true, "license": "Apache-2.0", "dependencies": { - "@eslint/core": "^0.13.0", + "@eslint/core": "^1.2.1", "levn": "^0.4.1" }, "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - } - }, - "node_modules/@eslint/plugin-kit/node_modules/@eslint/core": { - "version": "0.13.0", - "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.13.0.tgz", - "integrity": "sha512-yfkgDw1KR66rkT5A8ci4irzDysN7FRpq3ttJolR88OqQikAWqwA8j5VZyas+vjyBNFIJ7MfybJ9plMILI2UrCw==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@types/json-schema": "^7.0.15" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + "node": "^20.19.0 || ^22.13.0 || >=24" } }, "node_modules/@humanfs/core": { @@ -766,9 +687,9 @@ } }, "node_modules/@rolldown/binding-linux-x64-gnu": { - "version": "1.0.0-rc.17", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-gnu/-/binding-linux-x64-gnu-1.0.0-rc.17.tgz", - "integrity": "sha512-cLnjV3xfo7KslbU41Z7z8BH/E1y5mzUYzAqih1d1MDaIGZRCMqTijqLv76/P7fyHuvUcfGsIpqCdddbxLLK9rA==", + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-gnu/-/binding-linux-x64-gnu-1.0.3.tgz", + "integrity": "sha512-B8m6tD5+/N5FeNQFbKlLA/2yVq9ycQP1SeedyEYYKWBNR3ZQbkvIUcNnDNM03lO1l5F2roiiFJGgvoLLyZXtSg==", "cpu": [ "x64" ], @@ -939,6 +860,13 @@ "dev": true, "license": "MIT" }, + "node_modules/@types/esrecurse": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/@types/esrecurse/-/esrecurse-4.3.1.tgz", + "integrity": "sha512-xJBAbDifo5hpffDBuHl0Y8ywswbiAp/Wi7Y/GtAgSlZyIABppyurxVueOPE8LUQOxdlgi6Zqce7uoEpqNTeiUw==", + "dev": true, + "license": "MIT" + }, "node_modules/@types/estree": { "version": "1.0.9", "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.9.tgz", @@ -946,6 +874,16 @@ "dev": true, "license": "MIT" }, + "node_modules/@types/hast": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/@types/hast/-/hast-3.0.5.tgz", + "integrity": "sha512-rp/ezSWaD1m44dPKICGhiskI13nVr7qTloFwDa/IYkhhf5nzwP+zIQcIJh3WIFSBOy/H1PzB40jPjMDksN4F+g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/unist": "*" + } + }, "node_modules/@types/json-schema": { "version": "7.0.15", "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", @@ -953,6 +891,13 @@ "dev": true, "license": "MIT" }, + "node_modules/@types/katex": { + "version": "0.16.8", + "resolved": "https://registry.npmjs.org/@types/katex/-/katex-0.16.8.tgz", + "integrity": "sha512-trgaNyfU+Xh2Tc+ABIb44a5AYUpicB3uwirOioeOkNPPbmgRNtcWyDeeFRzjPZENO9Vq8gvVqfhaaXWLlevVwg==", + "dev": true, + "license": "MIT" + }, "node_modules/@types/mdast": { "version": "4.0.4", "resolved": "https://registry.npmjs.org/@types/mdast/-/mdast-4.0.4.tgz", @@ -1122,9 +1067,9 @@ } }, "node_modules/acorn": { - "version": "8.16.0", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.16.0.tgz", - "integrity": "sha512-UVJyE9MttOsBQIDKw1skb9nAwQuR5wuGD3+82K6JgJlm/Y+KI92oNsMNGZCYdDsVtRHSak0pcV5Dno5+4jh9sw==", + "version": "8.18.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.18.0.tgz", + "integrity": "sha512-lGq+9yr1/GuAWaVYIHRjvvySG5/4VfKIvC8EWxStPdcDh/Ka7FG3twP6v4d5BkravUilhIAsG4Qj83t02LWUPQ==", "dev": true, "license": "MIT", "bin": { @@ -1209,13 +1154,6 @@ "integrity": "sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A==", "license": "MIT" }, - "node_modules/argparse": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", - "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", - "dev": true, - "license": "Python-2.0" - }, "node_modules/assertion-error": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-2.0.1.tgz", @@ -1239,31 +1177,26 @@ } }, "node_modules/balanced-match": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", - "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", - "dev": true, - "license": "MIT" - }, - "node_modules/brace-expansion": { - "version": "1.1.14", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.14.tgz", - "integrity": "sha512-MWPGfDxnyzKU7rNOW9SP/c50vi3xrmrua/+6hfPbCS2ABNWfx24vPidzvC7krjU/RTo235sV776ymlsMtGKj8g==", + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz", + "integrity": "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==", "dev": true, "license": "MIT", - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" + "engines": { + "node": "18 || 20 || >=22" } }, - "node_modules/callsites": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", - "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", + "node_modules/brace-expansion": { + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.8.tgz", + "integrity": "sha512-JZyDyq3D4AUifKTPOB7DELf6XsB3WdPuNxCtob1vFXPsSXhdAiHBWJ/tJ8HAc9aH84BK+5JFZLNkJKx3G9kzQg==", "dev": true, "license": "MIT", + "dependencies": { + "balanced-match": "^4.0.2" + }, "engines": { - "node": ">=6" + "node": "20 || >=22" } }, "node_modules/ccount": { @@ -1288,12 +1221,12 @@ } }, "node_modules/chalk": { - "version": "5.6.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.6.2.tgz", - "integrity": "sha512-7NzBL0rN6fMUW+f7A6Io4h40qQlG+xGmtMxfbnH/K7TAtt8JQWVQK+6g0UXKMeVJoyV5EkkNsErQ8pVD3bLHbA==", + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-6.0.0.tgz", + "integrity": "sha512-2uNTXIuTTxk7ciZgAU1BQcgnchcG0xXnrs6jzkQfj9SsRa9M2s5zE8WT96hS6KmG4MzWHSrvH43DF1m4XRkrFg==", "license": "MIT", "engines": { - "node": "^12.17.0 || ^14.13 || >=16.0.0" + "node": ">=22" }, "funding": { "url": "https://github.com/chalk/chalk?sponsor=1" @@ -1401,21 +1334,14 @@ "license": "MIT" }, "node_modules/commander": { - "version": "14.0.3", - "resolved": "https://registry.npmjs.org/commander/-/commander-14.0.3.tgz", - "integrity": "sha512-H+y0Jo/T1RZ9qPP4Eh1pkcQcLRglraJaSLoyOtHxu6AapkjWVCy2Sit1QQ4x3Dng8qDlSsZEet7g5Pq06MvTgw==", + "version": "15.0.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-15.0.0.tgz", + "integrity": "sha512-z67u4ZhzCL/Tydu1lJARtEZYWbWaN7oYLHbsuzocr6y4N6WZAagG3RQ4FW61V1/0+jImpj293XfrcYnd1qxtPg==", "license": "MIT", "engines": { - "node": ">=20" + "node": ">=22.12.0" } }, - "node_modules/concat-map": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", - "dev": true, - "license": "MIT" - }, "node_modules/convert-source-map": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", @@ -1565,33 +1491,33 @@ } }, "node_modules/eslint": { - "version": "9.39.4", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.39.4.tgz", - "integrity": "sha512-XoMjdBOwe/esVgEvLmNsD3IRHkm7fbKIUGvrleloJXUZgDHig2IPWNniv+GwjyJXzuNqVjlr5+4yVUZjycJwfQ==", + "version": "10.8.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-10.8.0.tgz", + "integrity": "sha512-nuKKvN+oIBO0koN7Tm7dlkmnkc21mtt0QJLwAKzjLq14y6lRTdVG36MZHJ8eQHwdJMwZbQNMlPOYedMq/oVJvQ==", "dev": true, "license": "MIT", + "workspaces": [ + "packages/*" + ], "dependencies": { "@eslint-community/eslint-utils": "^4.8.0", - "@eslint-community/regexpp": "^4.12.1", - "@eslint/config-array": "^0.21.2", - "@eslint/config-helpers": "^0.4.2", - "@eslint/core": "^0.17.0", - "@eslint/eslintrc": "^3.3.5", - "@eslint/js": "9.39.4", - "@eslint/plugin-kit": "^0.4.1", + "@eslint-community/regexpp": "^4.12.2", + "@eslint/config-array": "^0.23.5", + "@eslint/config-helpers": "^0.7.0", + "@eslint/core": "^1.2.1", + "@eslint/plugin-kit": "^0.7.2", "@humanfs/node": "^0.16.6", "@humanwhocodes/module-importer": "^1.0.1", "@humanwhocodes/retry": "^0.4.2", "@types/estree": "^1.0.6", "ajv": "^6.14.0", - "chalk": "^4.0.0", "cross-spawn": "^7.0.6", "debug": "^4.3.2", "escape-string-regexp": "^4.0.0", - "eslint-scope": "^8.4.0", - "eslint-visitor-keys": "^4.2.1", - "espree": "^10.4.0", - "esquery": "^1.5.0", + "eslint-scope": "^9.1.2", + "eslint-visitor-keys": "^5.0.1", + "espree": "^11.2.0", + "esquery": "^1.7.0", "esutils": "^2.0.2", "fast-deep-equal": "^3.1.3", "file-entry-cache": "^8.0.0", @@ -1601,8 +1527,7 @@ "imurmurhash": "^0.1.4", "is-glob": "^4.0.0", "json-stable-stringify-without-jsonify": "^1.0.1", - "lodash.merge": "^4.6.2", - "minimatch": "^3.1.5", + "minimatch": "^10.2.5", "natural-compare": "^1.4.0", "optionator": "^0.9.3" }, @@ -1610,7 +1535,7 @@ "eslint": "bin/eslint.js" }, "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + "node": "^20.19.0 || ^22.13.0 || >=24" }, "funding": { "url": "https://eslint.org/donate" @@ -1625,92 +1550,50 @@ } }, "node_modules/eslint-scope": { - "version": "8.4.0", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-8.4.0.tgz", - "integrity": "sha512-sNXOfKCn74rt8RICKMvJS7XKV/Xk9kA7DyJr8mJik3S7Cwgy3qlkkmyS2uQB3jiJg6VNdZd/pDBJu0nvG2NlTg==", + "version": "9.1.2", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-9.1.2.tgz", + "integrity": "sha512-xS90H51cKw0jltxmvmHy2Iai1LIqrfbw57b79w/J7MfvDfkIkFZ+kj6zC3BjtUwh150HsSSdxXZcsuv72miDFQ==", "dev": true, "license": "BSD-2-Clause", "dependencies": { + "@types/esrecurse": "^4.3.1", + "@types/estree": "^1.0.8", "esrecurse": "^4.3.0", "estraverse": "^5.2.0" }, "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + "node": "^20.19.0 || ^22.13.0 || >=24" }, "funding": { "url": "https://opencollective.com/eslint" } }, "node_modules/eslint-visitor-keys": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.1.tgz", - "integrity": "sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==", + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-5.0.1.tgz", + "integrity": "sha512-tD40eHxA35h0PEIZNeIjkHoDR4YjjJp34biM0mDvplBe//mB+IHCqHDGV7pxF+7MklTvighcCPPZC7ynWyjdTA==", "dev": true, "license": "Apache-2.0", "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + "node": "^20.19.0 || ^22.13.0 || >=24" }, "funding": { "url": "https://opencollective.com/eslint" } }, - "node_modules/eslint/node_modules/@eslint/core": { - "version": "0.17.0", - "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.17.0.tgz", - "integrity": "sha512-yL/sLrpmtDaFEiUj1osRP4TI2MDz1AddJL+jZ7KSqvBuliN4xqYY54IfdN8qD8Toa6g1iloph1fxQNkjOxrrpQ==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@types/json-schema": "^7.0.15" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - } - }, - "node_modules/eslint/node_modules/@eslint/plugin-kit": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.4.1.tgz", - "integrity": "sha512-43/qtrDUokr7LJqoF2c3+RInu/t4zfrpYdoSDfYyhg52rwLV6TnOvdG4fXm7IkSB3wErkcmJS9iEhjVtOSEjjA==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@eslint/core": "^0.17.0", - "levn": "^0.4.1" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - } - }, - "node_modules/eslint/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, "node_modules/espree": { - "version": "10.4.0", - "resolved": "https://registry.npmjs.org/espree/-/espree-10.4.0.tgz", - "integrity": "sha512-j6PAQ2uUr79PZhBjP5C5fhl8e39FmRnOjsD5lGnWrFU8i2G776tBK7+nP8KuQUTTyAZUwfQqXAgrVH5MbH9CYQ==", + "version": "11.2.0", + "resolved": "https://registry.npmjs.org/espree/-/espree-11.2.0.tgz", + "integrity": "sha512-7p3DrVEIopW1B1avAGLuCSh1jubc01H2JHc8B4qqGblmg5gI9yumBgACjWo4JlIc04ufug4xJ3SQI8HkS/Rgzw==", "dev": true, "license": "BSD-2-Clause", "dependencies": { - "acorn": "^8.15.0", + "acorn": "^8.16.0", "acorn-jsx": "^5.3.2", - "eslint-visitor-keys": "^4.2.1" + "eslint-visitor-keys": "^5.0.1" }, "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + "node": "^20.19.0 || ^22.13.0 || >=24" }, "funding": { "url": "https://opencollective.com/eslint" @@ -1940,9 +1823,9 @@ } }, "node_modules/globals": { - "version": "15.15.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-15.15.0.tgz", - "integrity": "sha512-7ACyT3wmyp3I61S4fG682L0VA2RGD9otkqGJIwNUMF1SWUombIIk+af1unuDYgMm082aHYwD+mzJvv9Iu8dsgg==", + "version": "17.8.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-17.8.0.tgz", + "integrity": "sha512-Zz/LMDZScFmkakeL2cTHzf+PbWKdpU3uclqkZT7TjDG58j5WPt0PpA+n9uPI24fZtlw07q0OtEi84K+umsRzqQ==", "dev": true, "license": "MIT", "engines": { @@ -1987,23 +1870,6 @@ "node": ">= 4" } }, - "node_modules/import-fresh": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.1.tgz", - "integrity": "sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "parent-module": "^1.0.0", - "resolve-from": "^4.0.0" - }, - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/imurmurhash": { "version": "0.1.4", "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", @@ -2099,29 +1965,6 @@ "dev": true, "license": "MIT" }, - "node_modules/js-yaml": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.3.0.tgz", - "integrity": "sha512-1td788aAnnZ5qs7V2QIRl1owjtYpbKt749Y3xauqQgwIIGF/xXWz1wMTEBx5O3LK3lXLVuqXPdPxj2BoFHaW9Q==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/puzrin" - }, - { - "type": "github", - "url": "https://github.com/sponsors/nodeca" - } - ], - "license": "MIT", - "dependencies": { - "argparse": "^2.0.1" - }, - "bin": { - "js-yaml": "bin/js-yaml.js" - } - }, "node_modules/json-buffer": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", @@ -2143,6 +1986,33 @@ "dev": true, "license": "MIT" }, + "node_modules/katex": { + "version": "0.16.47", + "resolved": "https://registry.npmjs.org/katex/-/katex-0.16.47.tgz", + "integrity": "sha512-Eeo8Ys1doU1z+x8AZsPpQu+p/QcZBI5PeOo7QGQdy2x2m0MU/hYagBbGOmXwr5KVbEfVuWv9LpnQWeehogurjg==", + "dev": true, + "funding": [ + "https://opencollective.com/katex", + "https://github.com/sponsors/katex" + ], + "license": "MIT", + "dependencies": { + "commander": "^8.3.0" + }, + "bin": { + "katex": "cli.js" + } + }, + "node_modules/katex/node_modules/commander": { + "version": "8.3.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-8.3.0.tgz", + "integrity": "sha512-OkTL9umf+He2DZkUq8f8J9of7yL6RJKI24dVITBmNfZBmri9zYZQrKkuXiKhyfPSu8tUhnVBB1iKXevvnlR4Ww==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 12" + } + }, "node_modules/keyv": { "version": "4.5.4", "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", @@ -2456,13 +2326,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/lodash.merge": { - "version": "4.6.2", - "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", - "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", - "dev": true, - "license": "MIT" - }, "node_modules/longest-streak": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/longest-streak/-/longest-streak-3.1.0.tgz", @@ -2556,6 +2419,18 @@ "marked": ">=1 <16" } }, + "node_modules/marked-terminal/node_modules/chalk": { + "version": "5.6.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.6.2.tgz", + "integrity": "sha512-7NzBL0rN6fMUW+f7A6Io4h40qQlG+xGmtMxfbnH/K7TAtt8JQWVQK+6g0UXKMeVJoyV5EkkNsErQ8pVD3bLHbA==", + "license": "MIT", + "engines": { + "node": "^12.17.0 || ^14.13 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, "node_modules/mdast-util-find-and-replace": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/mdast-util-find-and-replace/-/mdast-util-find-and-replace-3.0.2.tgz", @@ -2750,6 +2625,26 @@ "url": "https://opencollective.com/unified" } }, + "node_modules/mdast-util-math": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/mdast-util-math/-/mdast-util-math-3.0.0.tgz", + "integrity": "sha512-Tl9GBNeG/AhJnQM221bJR2HPvLOSnLE/T9cJI9tlc6zwQk2nPk/4f0cHkOdEixQPC/j8UtKDdITswvLAy1OZ1w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0", + "@types/mdast": "^4.0.0", + "devlop": "^1.0.0", + "longest-streak": "^3.0.0", + "mdast-util-from-markdown": "^2.0.0", + "mdast-util-to-markdown": "^2.1.0", + "unist-util-remove-position": "^5.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, "node_modules/mdast-util-phrasing": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/mdast-util-phrasing/-/mdast-util-phrasing-4.1.0.tgz", @@ -3017,6 +2912,26 @@ "url": "https://opencollective.com/unified" } }, + "node_modules/micromark-extension-math": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/micromark-extension-math/-/micromark-extension-math-3.1.0.tgz", + "integrity": "sha512-lvEqd+fHjATVs+2v/8kg9i5Q0AP2k85H0WUOwpIVvUML8BapsMvh1XAogmQjOCsLpoKRCVQqEkQBB3NhVBcsOg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/katex": "^0.16.0", + "devlop": "^1.0.0", + "katex": "^0.16.0", + "micromark-factory-space": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, "node_modules/micromark-factory-destination": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/micromark-factory-destination/-/micromark-factory-destination-2.0.1.tgz", @@ -3410,16 +3325,19 @@ "license": "MIT" }, "node_modules/minimatch": { - "version": "3.1.5", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz", - "integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==", + "version": "10.2.6", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.6.tgz", + "integrity": "sha512-vpLQEs+VLCr1nU0BXS07maYoFwlDAH0gngQuuttxIwutDFEMHq2blX+8vpgxDdK3J1PwjCJiep77OitTZ4Ll1A==", "dev": true, - "license": "ISC", + "license": "BlueOak-1.0.0", "dependencies": { - "brace-expansion": "^1.1.7" + "brace-expansion": "^5.0.8" }, "engines": { - "node": "*" + "node": "18 || 20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" } }, "node_modules/ms": { @@ -3554,19 +3472,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/parent-module": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", - "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", - "dev": true, - "license": "MIT", - "dependencies": { - "callsites": "^3.0.0" - }, - "engines": { - "node": ">=6" - } - }, "node_modules/parse5": { "version": "5.1.1", "resolved": "https://registry.npmjs.org/parse5/-/parse5-5.1.1.tgz", @@ -3675,9 +3580,9 @@ } }, "node_modules/prettier": { - "version": "3.8.3", - "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.8.3.tgz", - "integrity": "sha512-7igPTM53cGHMW8xWuVTydi2KO233VFiTNyF5hLJqpilHfmn8C8gPf+PS7dUT64YcXFbiMGZxS9pCSxL/Dxm/Jw==", + "version": "3.9.6", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.9.6.tgz", + "integrity": "sha512-OpN0zzVdiaiAhxpuuj5efpIS4sY9j7bY6uR5mnj5yPzGkdkjNKSJeUThPb60Jw29QuAZgA4o+/iB49kFiaBX6g==", "dev": true, "license": "MIT", "bin": { @@ -3709,16 +3614,6 @@ "node": ">=0.10.0" } }, - "node_modules/resolve-from": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", - "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=4" - } - }, "node_modules/rolldown": { "version": "1.1.5", "resolved": "https://registry.npmjs.org/rolldown/-/rolldown-1.1.5.tgz", @@ -3887,19 +3782,6 @@ "node": ">=8" } }, - "node_modules/strip-json-comments": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", - "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/supports-color": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", @@ -4037,6 +3919,21 @@ "url": "https://opencollective.com/unified" } }, + "node_modules/unist-util-remove-position": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/unist-util-remove-position/-/unist-util-remove-position-5.0.0.tgz", + "integrity": "sha512-Hp5Kh3wLxv0PHj9m2yZhhLt58KzPtEYKQQ4yxfYFEO7EvHwzyDYnduhHnY1mDxoqr7VUwVuHXk9RXKIiYS1N8Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0", + "unist-util-visit": "^5.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, "node_modules/unist-util-stringify-position": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/unist-util-stringify-position/-/unist-util-stringify-position-4.0.0.tgz", diff --git a/package.json b/package.json index 2005b55..43a0b09 100644 --- a/package.json +++ b/package.json @@ -1,11 +1,11 @@ { "name": "@cldmv/git-embedded", - "version": "1.1.2", + "version": "1.1.3", "description": "Manage embedded git repositories (anonymous gitlinks) without .gitmodules. Provides hooks that restore standard git-command ergonomics for embedded children while keeping the child's origin URL out of the public parent repo.", "type": "module", "license": "Apache-2.0", "engines": { - "node": ">=20.19.0" + "node": ">=22.12.0" }, "author": { "name": "Shinrai", @@ -75,24 +75,25 @@ "lint": "eslint --config .configs/eslint.config.mjs .", "lint:fix": "eslint --config .configs/eslint.config.mjs . --fix", "format": "prettier --config .configs/.prettierrc --write .", - "format:check": "prettier --config .configs/.prettierrc --check ." + "format:check": "prettier --config .configs/.prettierrc --check .", + "build": "echo 'no build step - stopgap for CI coverage-badge; see tracking issue'" }, "dependencies": { "@cldmv/slothlet": "^3.7.0", "@cldmv/wisp": "^1.0.1", - "chalk": "^5.4.1", - "commander": "^14.0.0", + "chalk": "^6.0.0", + "commander": "^15.0.0", "marked": "^15.0.12", "marked-terminal": "^7.3.0" }, "devDependencies": { "@cldmv/vitest-runner": "^1.2.0", "@eslint/js": "^9.18.0", - "@eslint/json": "^0.10.0", - "@eslint/markdown": "^6.2.2", + "@eslint/json": "^2.0.1", + "@eslint/markdown": "^8.0.3", "@vitest/coverage-v8": "^4.1.10", - "eslint": "^9.18.0", - "globals": "^15.14.0", + "eslint": "^10.8.0", + "globals": "^17.7.0", "prettier": "^3.4.2", "vitest": "^4.1.10" }