diff --git a/.github/workflows/commitlint.yml b/.github/workflows/commitlint.yml new file mode 100644 index 00000000..d35cce34 --- /dev/null +++ b/.github/workflows/commitlint.yml @@ -0,0 +1,52 @@ +name: Commitlint + +on: + pull_request: + types: [opened, edited, synchronize, reopened] + +jobs: + commitlint: + name: Lint Commit Messages + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v5 + with: + fetch-depth: 0 + + - name: Setup pnpm + uses: pnpm/action-setup@v4 + with: + version: 9.0.0 + + - name: Setup Node.js + uses: actions/setup-node@v6 + with: + node-version: 20 + cache: 'pnpm' + + - name: Install dependencies + run: pnpm install --frozen-lockfile + + # Lint only the commits this branch adds, using the merge-base with the + # live origin/main as the lower bound (not the PR's recorded base SHA, + # which goes stale as main moves forward). The merge-base is robust even + # when the branch is behind main: it always resolves to the point where + # this branch diverged, so the range is exactly the new commits. + # + # main MUST be fetched with full history (no --depth). A shallow fetch + # hides the merge-base, so `git log
..HEAD` can no longer exclude + # shared history and the range balloons to include unrelated historical + # commits. See docs/release-hardening-plan.md (AC6). + - name: Resolve lint range + id: range + env: + HEAD_SHA: ${{ github.event.pull_request.head.sha }} + run: | + set -euo pipefail + git fetch --no-tags origin main:refs/remotes/origin/main + base=$(git merge-base origin/main "$HEAD_SHA") + echo "base=${base}" >> "$GITHUB_OUTPUT" + echo "Linting ${base}..${HEAD_SHA}" + + - name: Lint commits + run: pnpm exec commitlint --from ${{ steps.range.outputs.base }} --to ${{ github.event.pull_request.head.sha }} --verbose diff --git a/.husky/commit-msg b/.husky/commit-msg new file mode 100755 index 00000000..2e6b87e2 --- /dev/null +++ b/.husky/commit-msg @@ -0,0 +1 @@ +pnpm exec commitlint --edit "$1" diff --git a/PUBLISHING.md b/PUBLISHING.md index 6e46964d..83d1abd0 100644 --- a/PUBLISHING.md +++ b/PUBLISHING.md @@ -2,6 +2,8 @@ This guide is for project maintainers who need to set up publishing infrastructure or troubleshoot release issues. +> **Hit something not covered here?** [`RELEASE-RUNBOOK.md`](./RELEASE-RUNBOOK.md) catalogues specific failure modes (EPUBLISHCONFLICT-after-success, transient registry 5xx, provenance attestation failure, expired `NPM_TOKEN`, OTP/2FA, `workspace:*` not rewritten, peer-dep skew, dist-tag drift) with concrete state-check and recovery commands. + ## How Publishing Works The repository uses [Changesets](https://github.com/changesets/changesets) with GitHub Actions for automated publishing. @@ -53,6 +55,10 @@ Required packages: - Automatic provenance generation - Audit trail of all publishes +### `NPM_TOKEN` fallback (token type matters) + +The workflow keeps `NPM_TOKEN` as a fallback for any package where Trusted Publishing isn't configured yet. If you set one, generate it as an **Automation token** — not a Publish or personal-user token. Automation tokens explicitly bypass npm's 2FA-on-publish, which CI cannot satisfy. A Publish token will fail every publish with `EOTP` / "need a one-time password" (see [`RELEASE-RUNBOOK.md` §5](./RELEASE-RUNBOOK.md#5-otp--2fa-error-class-wrong-token-type)). Remove `NPM_TOKEN` once all three packages are on Trusted Publishing. + ## Troubleshooting ### "Version Packages" PR Not Created diff --git a/RELEASE-RUNBOOK.md b/RELEASE-RUNBOOK.md new file mode 100644 index 00000000..9d6bdf80 --- /dev/null +++ b/RELEASE-RUNBOOK.md @@ -0,0 +1,275 @@ +# Release Runbook (Web-SDK-specific) + +When a release encounters something the generic Changesets / npm flow doesn't cover, this runbook is the place to look. For the normal happy-path flow and generic publish troubleshooting (auth, "Version Packages" PR not appearing, deprecate / unpublish), see [PUBLISHING.md](./PUBLISHING.md). + +This runbook covers failure modes that survive the [`changesets/action`](https://github.com/changesets/action) abstraction — either because they happen below it (npm registry, OIDC provider) or because the operator needs to understand them to diagnose what they're seeing in workflow logs. + +For the equivalent runbook on the React Native side, see [`platform-sdk-reactnative-expo`'s RELEASE-RUNBOOK.md](https://github.com/youversion/platform-sdk-reactnative-expo/blob/main/RELEASE-RUNBOOK.md). The two repos share the same Changesets flow, so most procedures here apply there too — diverging only on platform-specific concerns (peer-dep skew on RN, React/React-DOM compatibility on Web). + +--- + +## 1. `EPUBLISHCONFLICT` after success + +**Symptom.** `pnpm publish` (invoked by `changeset publish`) exits non-zero with `EPUBLISHCONFLICT` or `You cannot publish over the previously published versions`, but `npm view @youversion/platform-react-ui@$VERSION version` returns `$VERSION`. The registry accepted the publish; the response came back as a conflict because npm's deduplication fired before the response was sent — or because a previous retry attempt already landed. + +**Why it happens.** npm publish is not idempotent: re-publishing the same version-tarball pair returns `EPUBLISHCONFLICT` even when the registry already has exactly what you tried to publish. Changesets-action treats this as a hard fail by default. + +**State check.** + +```bash +for p in @youversion/platform-core @youversion/platform-react-hooks @youversion/platform-react-ui; do + echo "== $p @ $VERSION ==" + npm view "${p}@${VERSION}" version 2>/dev/null || echo " NOT on npm" +done +``` + +If `$VERSION` is on the registry for all packages that should have shipped, the release is **complete** — the workflow's non-zero exit is a false negative. + +**Recovery.** + +- **All packages on registry, but workflow failed.** No re-publish needed. Changesets tags and releases **per package** — `@youversion/platform-core@$VERSION`, `@youversion/platform-react-hooks@$VERSION`, `@youversion/platform-react-ui@$VERSION` — not a single bare `$VERSION` tag. Verify each tag was pushed and each GitHub release exists, and create any that are missing: + ```bash + for p in @youversion/platform-core @youversion/platform-react-hooks @youversion/platform-react-ui; do + tag="${p}@${VERSION}" + git ls-remote --exit-code origin "refs/tags/${tag}" >/dev/null 2>&1 \ + || { git tag "$tag" && git push origin "$tag"; } + gh release view "$tag" >/dev/null 2>&1 \ + || gh release create "$tag" --notes-file CHANGELOG-entry.md # changesets-generated body for that package + done + ``` +- **Some packages on registry, others not.** Re-run the workflow. Changesets-action will skip the published packages (it reads the registry before publishing each) and retry the rest. If a specific package keeps failing, see #3 or #4. + +**Expected end state.** All three packages at `$VERSION` on npm, with a matching `@$VERSION` tag and GitHub release each. + +--- + +## 2. Registry transient failure (timeout / 5xx / network) + +**Symptom.** `pnpm publish` exits non-zero with one of: `ETIMEDOUT`, `ECONNRESET`, `EAI_AGAIN`, `503 Service Unavailable`, `502 Bad Gateway`, `504 Gateway Time-out`, `fetch failed`, `Could not resolve host`. + +**State check.** Same as #1 — `npm view` each package. Transient failures may or may not have recorded the publish before the error surfaced. + +**Recovery.** Re-run the release workflow from the Actions tab. Changesets-action re-reads the registry per package before publishing; anything that landed during the failed run is skipped automatically. If the registry is genuinely down (status page red), wait and re-run later. + +If retries are wanted *automatically*, that's a Changesets-action upstream feature request — the project doesn't expose retry config today. Watch the workflow and re-trigger manually if needed. + +**Expected end state.** All three packages at `$VERSION` on npm. + +--- + +## 3. Provenance attestation failure (publish succeeded, attestation missing) + +**Symptom.** `pnpm publish` exits non-zero with text mentioning `attestation`, `provenance`, `OIDC`, or `Sigstore`. The package itself may or may not have published — provenance generation is a separate step from the publish itself and either can fail independently. + +**Possible underlying causes.** + +- `id-token: write` permission missing from the workflow (it is present in [`release.yml`](.github/workflows/release.yml)). +- The npm-side trust policy for this repo / workflow isn't configured for one of the three packages. +- Sigstore / npm provenance service has a transient outage (rare; treat as transient). + +**State check.** `npm view @` — if the version is there, the publish succeeded. Check the provenance badge on the package page (`https://www.npmjs.com/package//v/`): + +```bash +npm view "@youversion/platform-react-ui@${VERSION}" --json | jq '.dist.attestations' +``` + +If `.dist.attestations` is `null` or absent, the publish landed without provenance. + +**Recovery.** + +- **Version is on registry without provenance.** Release is usable; consumers will install fine but lose the supply-chain attestation for this version. To attest after the fact: not currently supported by npm — you'd have to bump the version and re-publish. + 1. Verify `id-token: write` is in `release.yml`'s `permissions:` block. + 2. Verify the npm trust policy (`https://www.npmjs.com/package//access` → Trusted publishers) lists this workflow. + 3. If both are correct, treat as transient and accept the missing attestation for this version. Next release should attest cleanly. +- **Version not on registry.** Re-run the workflow. Changesets-action publishes attestation alongside; both succeed or fail per-package. + +**Expected end state.** All three packages at `$VERSION` on npm, ideally with provenance attestations. + +--- + +## 4. Expired / invalid `NPM_TOKEN` (fallback auth path) + +**Symptom.** `pnpm publish` exits non-zero with `E401`, `EAUTH`, `EUNAUTHORIZED`, `E403`, or `EFORBIDDEN`. The workflow is using the `NODE_AUTH_TOKEN` (sourced from `NPM_TOKEN` secret) fallback path because Trusted Publishing handed off — usually because OIDC isn't configured for that specific package, or the OIDC token was rejected. + +**State check.** On npm, verify the token at `https://www.npmjs.com/settings//tokens` — is it expired? Was it revoked? Is the package in scope for that token? + +**Recovery.** + +1. Generate a new **Automation** token (not Publish, not Read-only) on npm with publish rights for `@youversion/*`. **The token type matters** — see #5. +2. Update the `NPM_TOKEN` repo secret at `https://github.com/youversion/platform-sdk-react/settings/secrets/actions`. +3. Re-run the workflow. + +> **Better long-term fix:** configure Trusted Publishing on the failing package(s) so this repo doesn't need `NPM_TOKEN` at all. See [PUBLISHING.md](./PUBLISHING.md#setting-up-trusted-publishing). + +**Expected end state.** All three packages at `$VERSION` on npm. + +--- + +## 5. OTP / 2FA error class (wrong token type) + +**Symptom.** `pnpm publish` exits non-zero with `EOTP`, `need a one-time password`, `OTP required`, or similar. The token in use is a **Publish** token or a personal user token, not an **Automation** token — npm is requiring a 2FA OTP on every publish call. + +**Why it happens.** Automation tokens explicitly bypass 2FA-on-publish for CI use. Publish tokens require 2FA on every publish. Personal user tokens require 2FA if the npm account has 2FA enabled (which it should). CI cannot prompt for an OTP, so the publish fails immediately. + +**State check.** Error message is unambiguous — no further checks needed. + +**Recovery.** + +1. On `https://www.npmjs.com/settings//tokens`, generate an **Automation** token with publish rights for `@youversion/*`. The token type is the picker at the top of the new-token form — pick **Automation**, not **Publish**, not **Read and publish**. +2. Replace the `NPM_TOKEN` repo secret. +3. Re-run the workflow. + +> **Token-type cheat sheet:** Automation = bypass 2FA, CI-safe. Publish = require 2FA, manual use only. Read-only = read public packages, no publish. + +**Expected end state.** All three packages at `$VERSION` on npm. + +--- + +## 6. `workspace:*` not rewritten in published tarballs + +**Symptom.** Consumers install `@youversion/platform-react-ui@$VERSION` (or `…-react-hooks@$VERSION`) and see `Cannot resolve workspace:* outside a workspace` or `Unsupported URL Type "workspace:": workspace:*` in install logs. + +**Why it happens.** `pnpm publish` (invoked by `changeset publish`) rewrites `workspace:*` to a real version range at pack time. If a publish ran through a non-pnpm tool, or `publishConfig` was edited in a way pnpm doesn't recognize, the literal `workspace:*` reference can ship. + +**State check.** The dependency graph: `…-react-ui` depends on `…-core` AND `…-react-hooks`; `…-react-hooks` depends on `…-core`. Inspect any published tarball: + +```bash +mkdir -p /tmp/inspect-ui && cd /tmp/inspect-ui +npm pack "@youversion/platform-react-ui@$VERSION" +tar -xzf youversion-platform-react-ui-${VERSION}.tgz +node -p "require('./package/package.json').dependencies" +``` + +The `…-core` and `…-react-hooks` deps should be `^$VERSION` (or `~$VERSION`), not `workspace:*`. + +**Recovery.** If a published tarball is broken: + +1. Deprecate it: `npm deprecate @youversion/platform-react-ui@$VERSION "broken workspace:* — use ${NEXT_VERSION}"`. +2. Land a fix on `main` (usually a `publishConfig` cleanup), add a Changeset, and let the normal release flow ship a patch. + +**Why not unpublish?** npm's 72-hour unpublish window has historically been narrowed; prefer deprecation + a patch release. + +--- + +## 7. Peer-dep version skew with consumer projects + +**Symptom.** Consumers report install errors like `incorrect peer dependency` or runtime errors after upgrading. Their `react` / `react-dom` doesn't satisfy the SDK's `peerDependencies` range. Typical signatures: hooks fail with "Invalid hook call" when React major versions mismatch; SSR output diverges when `react-dom` is on a different minor than what the SDK was built against. + +**State check.** + +```bash +npm view "@youversion/platform-react-ui@${VERSION}" peerDependencies +npm view "@youversion/platform-react-hooks@${VERSION}" peerDependencies +npm view "@youversion/platform-core@${VERSION}" peerDependencies +``` + +Cross-reference with the consumer's `package.json`. + +**Recovery — two options:** + +- **Consumer upgrades.** Document required peer ranges in the Changeset body / GitHub release notes. Consumers update `react` / `react-dom` to a satisfying version. +- **SDK widens the range.** If a swath of consumers is affected and the SDK *would actually work* with the older peer, ship a patch widening `peerDependencies`. Be conservative — widening too far papers over a real incompatibility (e.g. an actual React 19-only API the SDK depends on) and produces opaque runtime errors instead of install errors. + +**Prevention.** When bumping a major peer range (`react >=18` → `react >=19`), describe the consumer impact in the Changeset body. The Changeset becomes part of the release notes consumers read before upgrading. + +--- + +## 8. Dist-tag drift (next / beta channels) + +**Symptom.** A pre-release shipped to `latest` instead of `next` / `beta`, breaking consumers on the stable channel. Or: a stable release landed on `next` and the pre-release channel now points at a stable version. + +**Why it happens.** Changesets-action publishes to `latest` by default. Pre-release mode is controlled by `pnpm changeset pre enter ` / `pnpm changeset pre exit`, which writes `.changeset/pre.json`. If the pre-mode state file is missing or stale when a release runs, the dist-tag is wrong. + +**State check.** + +```bash +npm view "@youversion/platform-react-ui" dist-tags +cat .changeset/pre.json 2>/dev/null || echo "not in pre-release mode" +``` + +**Recovery.** + +```bash +# Move latest off the bad version +npm dist-tag add @youversion/platform-react-ui@ latest + +# Point next at the right pre-release +npm dist-tag add @youversion/platform-react-ui@ next +``` + +Repeat for `…-react-hooks` and `…-core`. + +**Prevention.** Before opening a release that should hit `next`, confirm `.changeset/pre.json` exists and references the right tag. After the pre-release window, run `pnpm changeset pre exit` and commit the deletion in the same PR that ships the first stable. + +--- + +## 9. Rogue tag (tag on origin without a matching release / publish) + +**Symptom.** A Changesets tag (`@$VERSION`, e.g. `@youversion/platform-core@$VERSION`) exists on origin, but one or more of: the GitHub release is missing, the npm packages were never published, or the tag points at the wrong commit. Usually the tail of a run that died after the tag push but before (or during) publish — the inverse of #1, where the registry landed but the tags didn't. + +**Why it happens.** Changesets-action pushes the version-bump commit and the per-package tags as part of merging the "Version Packages" PR, then publishes. If the job is killed between those steps, or a tag was pushed by hand during a manual recovery, a tag can outlive the work it was supposed to mark. Note the tags are **per package** — not a single bare `$VERSION` — so a partial failure can leave some package tags present and others missing. + +**State check.** + +```bash +# What does origin have, where does each tag point, and is each version on npm? +for p in @youversion/platform-core @youversion/platform-react-hooks @youversion/platform-react-ui; do + tag="${p}@${VERSION}" + echo "== ${tag} ==" + git ls-remote origin "refs/tags/${tag}" || echo " no tag on origin" + git rev-parse "${tag}^{commit}" 2>/dev/null || echo " not fetched locally" + gh release view "${tag}" >/dev/null 2>&1 && echo " release exists" || echo " no release" + npm view "${tag}" version 2>/dev/null || echo " NOT on npm" +done +``` + +**Recovery.** + +- **Tag is correct, but release / publish are missing.** Leave the tag. Re-run the release workflow — `changesets/action` will publish the packages that aren't on the registry yet. If a GitHub release is still missing afterward, create it from the tag: `gh release create "@$VERSION" --notes-file `. +- **A tag points at the wrong commit, or was pushed for a version that should never have shipped.** Delete that package's tag on both sides, then let the normal flow re-create it: + ```bash + tag="@youversion/platform-core@${VERSION}" # repeat per affected package + git push origin ":refs/tags/${tag}" # delete remote tag + git tag -d "${tag}" # delete local tag + ``` + Only delete a tag if that package's version is **not** on npm. If npm already has `@$VERSION`, deleting the tag desyncs git from the registry — instead, re-point the tag at the published commit and keep it. + +**Expected end state.** For every package: either the tag is deleted (because that version never shipped) or it points at the published commit and has a matching GitHub release + npm version. + +--- + +## 10. Wrong version input (bad bump in the "Version Packages" PR) + +**Symptom.** The "Version Packages" PR proposes a version that's wrong — a major bump for a patch-only change, a skipped number, or a downgrade. There is no manual `VERSION` field in this repo (unlike the Swift orchestrator); the version is computed by Changesets from the `.changeset/*.md` files' declared bump levels. + +**Why it happens.** A changeset declared the wrong bump level (`major` where `patch` was meant, or vice versa), or multiple changesets stacked to a larger bump than intended. + +**State check.** Read the proposed bump before merging: + +```bash +gh pr view --json title,files | jq . # the "chore: version packages" PR +cat .changeset/*.md # the bump levels feeding the computed version +``` + +The version in the PR's `package.json` diffs is the version that will publish the moment you merge. + +**Recovery (before merge — the safe path).** + +1. **Do not merge the "Version Packages" PR.** Close it, or leave it open. +2. Fix the bump at the source on `main`: edit or delete the offending `.changeset/*.md` entry (the front-matter line like `'@youversion/platform-react-ui': minor`). +3. Push to `main`. Changesets-action regenerates the "Version Packages" PR with the corrected version. + +**Recovery (after merge — already published).** npm publish is not reversible (see #1 / #6). If a wrong version already shipped: + +- **Too-high version (e.g. an accidental major).** You can't reclaim the number. Accept it, deprecate if it's confusing (`npm deprecate …`), and continue from there — SemVer only requires monotonic increase, not contiguity. +- **Broken release.** Deprecate it and ship a corrected patch via the normal flow (see #6). + +**Prevention.** Treat the "Version Packages" PR as the version approval gate — review the computed version in its diff before merging. This is also the **breaking-change approval point**: a `major` bump only ships when a human merges that PR, so a reviewer can block a `major` there. See the breaking-change note in [`docs/release-hardening-plan.md`](./docs/release-hardening-plan.md#breaking-change-approval). + +**Expected end state.** The intended version (and no other) is on npm with a matching tag and GitHub release. + +--- + +## When something isn't here + +These docs are deliberately limited to failure modes that aren't already handled by Changesets-action's normal behavior. Generic publish issues (missing changeset, "Version Packages" PR didn't appear, npm scope misconfigured) are covered in [PUBLISHING.md](./PUBLISHING.md). If you hit something not in either place, capture the symptom + recovery in a PR adding a new section here so the next operator doesn't start cold. diff --git a/commitlint.config.js b/commitlint.config.js new file mode 100644 index 00000000..bc143bc6 --- /dev/null +++ b/commitlint.config.js @@ -0,0 +1,36 @@ +// Commit message linting for the React SDK. +// +// Ported from platform-sdk-kotlin's working setup, with two React-specific +// adaptations: +// +// 1. The release commit. Kotlin uses semantic-release, whose bot emits +// "chore(release): ...". This repo uses Changesets, whose release commit +// subject is "chore: version packages" (see .github/workflows/release.yml). +// We ignore that subject so the bot's own commit never fails the lint +// range on a workflow re-run. +// +// 2. The ticket prefix. About half of this repo's history leads with a +// YouVersion ticket id, e.g. "YPE-1234: feat(ui): ..." or +// "YPE-1234 - feat(ui): ...". We allow an optional leading ticket +// reference before the conventional type/scope/subject so the team's +// existing convention keeps passing. +// +// This repo is ESM ("type": "module"), so the config uses `export default` +// rather than Kotlin's `module.exports`. +export default { + extends: ['@commitlint/config-conventional'], + ignores: [(message) => /^chore: version packages(?:\r?\n|$)/i.test(message)], + parserPreset: { + parserOpts: { + // Optional "YPE-1234: " or "YPE-1234 - " prefix, then the standard + // conventional header. Capture groups after the optional prefix must stay + // aligned with headerCorrespondence below. + headerPattern: /^(?:YPE-\d+(?:: | - ))?(\w+)(?:\(([^)]*)\))?(!)?: (.+)$/, + headerCorrespondence: ['type', 'scope', 'breaking', 'subject'], + }, + }, + rules: { + 'body-max-line-length': [0, 'always'], + 'footer-max-line-length': [0, 'always'], + }, +}; diff --git a/docs/release-hardening-decisions.md b/docs/release-hardening-decisions.md new file mode 100644 index 00000000..424c7aad --- /dev/null +++ b/docs/release-hardening-decisions.md @@ -0,0 +1,70 @@ +# Release Hardening — Open Decisions (YPE-2486) + +**Status:** 🟡 Blocked. [PR #268](https://github.com/youversion/platform-sdk-react/pull/268) does not land until the decisions below are made. Some need @davidfedor. + +Review feedback (jhampton, `CHANGES_REQUESTED`, 2026-06-29) raised three items that are **decisions, not bugs**. Decide each deliberately, then pin it two ways: + +- **Deterministic** — code/config (`commitlint.config.js`, workflows, `package.json`, hooks). +- **Semi-deterministic** — [`.greptile/rules.md`](https://www.greptile.com/docs/code-review/custom-standards), `AGENTS.md`. + +Update each decision's status as it lands. + +--- + +## Decision 1 — Commit-lint subject model 🔴 + +**Problem.** [`commitlint.config.js`](../commitlint.config.js) allows a leading `YPE-####` prefix, then enforces strict conventional. Wrong assumption: external contributors don't open PRs with a ticket number (it's added to the PR title later), and much of the repo's own history fails strict conventional anyway (e.g. `YPE-1146 - Prep React SDK...`, `Add Language detection...`). + +**Options.** +1. **Lint the PR title, not commits** (squash-merge makes the title the commit). Ticket can be added to the title post-acceptance; contributors aren't blocked mid-branch. +2. **Keep per-commit linting, relaxed** — drop the prefix parser, loosen `subject-case`. +3. **Strict per-commit as-is** — most consistent, most friction, contradicts "ticket added later." + +**Recommend:** Option 1. **Decides:** @davidfedor. +**Pin:** PR-title lint job in `commitlint.yml`; drop/trim the parser in `commitlint.config.js`; note the convention in `.greptile/rules.md` + `AGENTS.md`. + +--- + +## Decision 2 — Package-manager lane 🔴 + +**Problem.** The [`commit-msg`](../.husky/commit-msg) hook runs bare `pnpm exec` — uses whatever pnpm is on PATH, not the pinned `pnpm@9.0.0`. A newer global pnpm fails commits. The repo also never states/enforces that it's a pnpm workspace. + +**Options.** +1. **pnpm + Corepack** — `corepack pnpm exec` in hooks (activates the pinned version); README rationale; optional CI guard. +2. **pnpm, no Corepack** — document it, rely on `engines`/`packageManager`; mismatch can still bite locally. +3. **Pure node** — not viable; the repo uses `workspace:*` (pnpm-only). + +**Recommend:** Option 1. **Decides:** @davidfedor (approve README rationale). +**Pin:** `corepack pnpm exec` in hooks + optional CI check; README "Package Manager" section; `.greptile/rules.md` flag for bare `pnpm exec` in hooks. + +--- + +## Decision 3 — Node-version policy 🟠 + +**Problem.** Versions are inconsistent: CI/commitlint on Node 20, release on Node 24, `engines.node >=20`. The commitlint break is already fixed (pinned to v19, Node ≥18, commit `98a3a86`) but there's no stated policy. + +**Options.** +1. **Standardize on Node 20** across CI/hooks; new dev-deps must support it. +2. **Raise floor to Node 22** — bigger blast radius (consumers, contributors). +3. **Per-workflow, documented** — allow release to differ, write down why. + +**Recommend:** Option 1. **Decides:** team. +**Pin:** align `node-version` across `ci.yml`/`commitlint.yml`/`release.yml`; `AGENTS.md` note "new dev-deps must support `engines.node`." + +--- + +## Already fixed (no decision) + +| Item | Fix | +| --- | --- | +| commitlint `@21` needs Node ≥22.12 | Pinned to `19.8.1` — `98a3a86` | +| Runbook used bare `$VERSION` tag; repo tags per-package | §1/§9 loop over `@$VERSION` — `5f56a0d` | +| Release-ignore regex too broad | Anchored to `(?:\r?\n\|$)` — `5f56a0d` | +| Commit-lint range too wide (shallow fetch) | `git merge-base origin/main HEAD` + full fetch — `6ab6118` | + +## Next + +1. Decide 1–3 (consult @davidfedor). +2. Implement deterministic guardrails. +3. Add `.greptile/rules.md` + `AGENTS.md` rules. +4. Update [`release-hardening-plan.md`](./release-hardening-plan.md), then unblock PR #268. diff --git a/docs/release-hardening-plan.md b/docs/release-hardening-plan.md new file mode 100644 index 00000000..1bea44e9 --- /dev/null +++ b/docs/release-hardening-plan.md @@ -0,0 +1,84 @@ +# Release Hardening Plan (YPE-2486) + +## Why this doc exists + +YPE-2486 was opened to port [`platform-sdk-swift`'s YPE-2684 release hardening](https://github.com/youversion/platform-sdk-swift) to the React SDK. The Swift hardening was built around a custom bash orchestrator (`scripts/release.sh`) with explicit version inputs, tag-state resume mode, and bounded retry on `pod trunk push`. + +The React SDK does not use that shape — it uses [Changesets](https://github.com/changesets/changesets) with `changesets/action@v1`, triggered on push to `main`. Most of YPE-2486's "Constant across SDKs" scope assumes the bash-orchestrator model and is structurally inapplicable here; the one registry-agnostic item that *does* apply — commit-lint (AC6) — was ported from the Kotlin SDK. This document records what the team decided to do instead. + +The sibling RN-Expo SDK ([`platform-sdk-reactnative-expo`](https://github.com/youversion/platform-sdk-reactnative-expo)) made the same call — see its [`docs/release-hardening-plan.md`](https://github.com/youversion/platform-sdk-reactnative-expo/blob/main/docs/release-hardening-plan.md) for the parallel pivot. + +## Decision: keep Changesets, harden around the edges + +Three options were on the table: + +1. **Adopt the Swift model.** Replace Changesets with a `workflow_dispatch`-triggered bash orchestrator that takes a `version` input and implements resume mode, bounded retry, and per-package idempotency. Faithful to the Swift hardening, but rips out a working release model and introduces ~1000 LoC of bash to maintain. +2. **Keep Changesets, layer Swift-style hardening on top.** Add `npm view` pre-publish checks, `EPUBLISHCONFLICT` handling, and retry logic inside the workflow alongside `changesets/action`. Higher complexity than option 3 with marginal upside — Changesets already handles most of these cases. +3. **Keep Changesets, document the failure surface, and accept what's already idempotent.** Land the failure-mode runbook and engineering plan called for by YPE-2486 ACs 4 / 5 / 7, port the registry-agnostic items that *do* apply (commit-lint, AC6), document the breaking-change gate, drop the AC items that conflict with Changesets, and update the ticket scope to match. + +**Option 3 was chosen.** Changesets-action already: + +- Re-reads the registry per package before publishing, so a retry skips packages that landed. +- Treats publishing as atomic per package (one tarball uploads or none does — no partial-package state). +- Re-runs cleanly on workflow retry: if the "Version Packages" PR didn't merge yet, the next run regenerates it; if the publish failed mid-way, the next run picks up the missing packages. + +The hardening Swift needed (tag-resume, bounded retry, per-package idempotency) is mostly built in for free here. What's actually missing is **documentation** — operators need to know how to map an npm error message to a recovery, what failure classes are transient vs hard, and what an Automation token vs Publish token means for CI. + +## What landed under YPE-2486 + +| AC | Deliverable | Status | +| --- | --- | --- | +| 4 | `RELEASE-RUNBOOK.md` with 10 failure modes: EPUBLISHCONFLICT-after-success, transient 5xx, provenance attestation failure, expired NPM_TOKEN, OTP/2FA, workspace:* rewrite, peer-dep skew, dist-tag drift, rogue tag, wrong version input | ✅ Landed (all AC4-named modes covered: registry transient, EPUBLISHCONFLICT-after-success, provenance, NPM_TOKEN, OTP/2FA, rogue tag, wrong VERSION input) | +| 5 | Operator guide: `PUBLISHING.md` retained, cross-links runbook | ✅ Landed (cross-link added) | +| 6 | Commit-lint workflow anchored at `origin/main` tip, ignores the release commit | ✅ Landed (ported from the Kotlin SDK) | +| 7 | This engineering plan | ✅ This document | +| BREAKING CHANGE requires manual approval | The "Version Packages" PR merge is the approval gate | ✅ Documented below | + +## What was dropped from scope (and why) + +| Original AC item | Why dropped | +| --- | --- | +| 1. Manual `workflow_dispatch` with explicit version input | Conflicts with Changesets' auto-on-merge model. Versions come from `.changeset/*.md` files, not workflow inputs. Operator override is done by hand-editing the "Version Packages" PR before merging. | +| 2. Resume-on-re-dispatch in a custom script | `changesets/action` is idempotent by design — re-running it from the Actions tab is the recovery path. No custom resume logic needed. | +| 3. Custom `npm view` idempotency check + EPUBLISHCONFLICT handling | Changesets-action does per-package registry checks before publishing. EPUBLISHCONFLICT-after-success is documented in the runbook so operators recognize the symptom; no in-band classifier needed. | +| Bounded retry on registry-publish | Not supported by `changesets/action` upstream. Workflow re-trigger from Actions UI fills the gap. | +| Dist-tag preservation in resume mode | Changesets handles dist-tag via `.changeset/pre.json`; #8 in the runbook covers the manual recovery if it drifts. | + +These items aren't impossible to add; they just don't pull their weight against the Changesets baseline. If the release model ever pivots (e.g. to manual-dispatch for slow-rolling enterprise channels), they should be re-evaluated. + +## Commit linting (AC6) + +Ported from the Kotlin SDK's working setup ([`commitlint.yml`](https://github.com/youversion/platform-sdk-kotlin/blob/main/.github/workflows/commitlint.yml) + [`commitlint.config.js`](https://github.com/youversion/platform-sdk-kotlin/blob/main/commitlint.config.js)), which is the reference this AC was modeled on: + +- **CI** — [`.github/workflows/commitlint.yml`](../.github/workflows/commitlint.yml) runs on PRs and anchors the lint range at the **merge-base with the live `origin/main`** (`git fetch origin main` full history → `base=$(git merge-base origin/main )` → `commitlint --from --to `), not the PR's recorded base SHA. The recorded base goes stale as `main` advances and would re-lint already-merged commits; the merge-base lints only commits new to the branch, and stays correct even when the branch is behind main. Note: `main` must be fetched with full history — a shallow (`--depth=1`) fetch hides the merge-base and balloons the range to include unrelated historical commits. +- **Local** — a husky `commit-msg` hook ([`.husky/commit-msg`](../.husky/commit-msg)) runs `commitlint --edit` so authors catch a bad message at commit time, before pushing. +- **Config** — [`commitlint.config.js`](../commitlint.config.js) extends `@commitlint/config-conventional`. + +Two React-specific deltas from the Kotlin original: + +- **Release-commit ignore.** Kotlin (semantic-release) ignores `chore(release):`. This repo (Changesets) emits `chore: version packages` — the config ignores that subject instead, so the release bot's own commit never fails the lint range on a workflow re-run. +- **Ticket prefix allowed.** ~Half this repo's history leads with a ticket id (`YPE-1234: feat(ui): …` / `YPE-1234 - feat(ui): …`). A custom `headerPattern` permits an optional leading ticket reference before the conventional `type(scope): subject`, so the team's existing convention keeps passing. Note: `config-conventional`'s `subject-case` rule still applies, so capitalized subjects (`feat(ui): Move …`) are flagged — a minor habit change, consistent with the Kotlin reference. + +## BREAKING CHANGE approval + +The ticket requires that a breaking change can't ship without manual approval. In the Changesets model this gate already exists and is structural — there is no separate workflow to add: + +- A breaking change is declared as a `major` bump in its `.changeset/*.md` entry. +- That bump flows into the **"Version Packages" PR**, where the new `major` version is visible in the `package.json` diffs. +- Nothing publishes until a human **merges that PR**. The merge is the manual approval — a reviewer can block a `major` bump there, or send it back to correct the bump level before anything reaches npm. + +So the approval point for breaking changes is the same review-and-merge step every release passes through; no auto-publish path bypasses it (the workflow only publishes *after* the version PR merges to `main`). Recovery for a wrong bump that slips through is in [`RELEASE-RUNBOOK.md` §10](../RELEASE-RUNBOOK.md#10-wrong-version-input-bad-bump-in-the-version-packages-pr). + +## What's *not* in this PR but worth knowing + +- **Trusted Publishing** is configured for all three packages (`@youversion/platform-core`, `…-react-hooks`, `…-react-ui`) — see [`PUBLISHING.md`](../PUBLISHING.md). `NPM_TOKEN` is kept as a fallback only. +- **Provenance** is on by default (`publishConfig.provenance: true` in each package + `NPM_CONFIG_PROVENANCE: true` in [`release.yml`](../.github/workflows/release.yml)). +- **Three-package fixed group** in [`.changeset/config.json`](../.changeset/config.json) ensures every release ships all three packages at the same version. +- **The RN SDK matches this same shape** so contributors and maintainers don't have to relearn the release flow per repo. + +## Related work / cross-references + +- [`platform-sdk-reactnative-expo`'s YPE-2790](https://github.com/youversion/platform-sdk-reactnative-expo) — parallel pivot on the RN repo, same Changesets model, slim runbook with RN-specific failure modes. +- [`platform-sdk-swift`'s YPE-2684](https://github.com/youversion/platform-sdk-swift) — original Swift hardening; the React SDK does not adopt its orchestrator shape but borrows its failure-class taxonomy. +- [`changesets/action`](https://github.com/changesets/action) — upstream action behavior. +- [npm Trusted Publishing](https://docs.npmjs.com/trusted-publishers) — the OIDC auth path used here. diff --git a/package.json b/package.json index de1ecc21..77addaf8 100644 --- a/package.json +++ b/package.json @@ -36,6 +36,8 @@ }, "devDependencies": { "@changesets/cli": "2.29.7", + "@commitlint/cli": "19.8.1", + "@commitlint/config-conventional": "19.8.1", "@eslint/eslintrc": "3.3.1", "@microsoft/api-extractor": "7.53.1", "@types/node": "24.9.1", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 2cb53637..441912be 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -18,6 +18,12 @@ importers: '@changesets/cli': specifier: 2.29.7 version: 2.29.7(@types/node@24.9.1) + '@commitlint/cli': + specifier: 19.8.1 + version: 19.8.1(@types/node@24.9.1)(typescript@5.9.3) + '@commitlint/config-conventional': + specifier: 19.8.1 + version: 19.8.1 '@eslint/eslintrc': specifier: 3.3.1 version: 3.3.1 @@ -706,6 +712,75 @@ packages: '@changesets/write@0.4.0': resolution: {integrity: sha512-CdTLvIOPiCNuH71pyDu3rA+Q0n65cmAbXnwWH84rKGiFumFzkmHNT8KHTMEchcxN+Kl8I54xGUhJ7l3E7X396Q==} + '@commitlint/cli@19.8.1': + resolution: {integrity: sha512-LXUdNIkspyxrlV6VDHWBmCZRtkEVRpBKxi2Gtw3J54cGWhLCTouVD/Q6ZSaSvd2YaDObWK8mDjrz3TIKtaQMAA==} + engines: {node: '>=v18'} + hasBin: true + + '@commitlint/config-conventional@19.8.1': + resolution: {integrity: sha512-/AZHJL6F6B/G959CsMAzrPKKZjeEiAVifRyEwXxcT6qtqbPwGw+iQxmNS+Bu+i09OCtdNRW6pNpBvgPrtMr9EQ==} + engines: {node: '>=v18'} + + '@commitlint/config-validator@19.8.1': + resolution: {integrity: sha512-0jvJ4u+eqGPBIzzSdqKNX1rvdbSU1lPNYlfQQRIFnBgLy26BtC0cFnr7c/AyuzExMxWsMOte6MkTi9I3SQ3iGQ==} + engines: {node: '>=v18'} + + '@commitlint/ensure@19.8.1': + resolution: {integrity: sha512-mXDnlJdvDzSObafjYrOSvZBwkD01cqB4gbnnFuVyNpGUM5ijwU/r/6uqUmBXAAOKRfyEjpkGVZxaDsCVnHAgyw==} + engines: {node: '>=v18'} + + '@commitlint/execute-rule@19.8.1': + resolution: {integrity: sha512-YfJyIqIKWI64Mgvn/sE7FXvVMQER/Cd+s3hZke6cI1xgNT/f6ZAz5heND0QtffH+KbcqAwXDEE1/5niYayYaQA==} + engines: {node: '>=v18'} + + '@commitlint/format@19.8.1': + resolution: {integrity: sha512-kSJj34Rp10ItP+Eh9oCItiuN/HwGQMXBnIRk69jdOwEW9llW9FlyqcWYbHPSGofmjsqeoxa38UaEA5tsbm2JWw==} + engines: {node: '>=v18'} + + '@commitlint/is-ignored@19.8.1': + resolution: {integrity: sha512-AceOhEhekBUQ5dzrVhDDsbMaY5LqtN8s1mqSnT2Kz1ERvVZkNihrs3Sfk1Je/rxRNbXYFzKZSHaPsEJJDJV8dg==} + engines: {node: '>=v18'} + + '@commitlint/lint@19.8.1': + resolution: {integrity: sha512-52PFbsl+1EvMuokZXLRlOsdcLHf10isTPlWwoY1FQIidTsTvjKXVXYb7AvtpWkDzRO2ZsqIgPK7bI98x8LRUEw==} + engines: {node: '>=v18'} + + '@commitlint/load@19.8.1': + resolution: {integrity: sha512-9V99EKG3u7z+FEoe4ikgq7YGRCSukAcvmKQuTtUyiYPnOd9a2/H9Ak1J9nJA1HChRQp9OA/sIKPugGS+FK/k1A==} + engines: {node: '>=v18'} + + '@commitlint/message@19.8.1': + resolution: {integrity: sha512-+PMLQvjRXiU+Ae0Wc+p99EoGEutzSXFVwQfa3jRNUZLNW5odZAyseb92OSBTKCu+9gGZiJASt76Cj3dLTtcTdg==} + engines: {node: '>=v18'} + + '@commitlint/parse@19.8.1': + resolution: {integrity: sha512-mmAHYcMBmAgJDKWdkjIGq50X4yB0pSGpxyOODwYmoexxxiUCy5JJT99t1+PEMK7KtsCtzuWYIAXYAiKR+k+/Jw==} + engines: {node: '>=v18'} + + '@commitlint/read@19.8.1': + resolution: {integrity: sha512-03Jbjb1MqluaVXKHKRuGhcKWtSgh3Jizqy2lJCRbRrnWpcM06MYm8th59Xcns8EqBYvo0Xqb+2DoZFlga97uXQ==} + engines: {node: '>=v18'} + + '@commitlint/resolve-extends@19.8.1': + resolution: {integrity: sha512-GM0mAhFk49I+T/5UCYns5ayGStkTt4XFFrjjf0L4S26xoMTSkdCf9ZRO8en1kuopC4isDFuEm7ZOm/WRVeElVg==} + engines: {node: '>=v18'} + + '@commitlint/rules@19.8.1': + resolution: {integrity: sha512-Hnlhd9DyvGiGwjfjfToMi1dsnw1EXKGJNLTcsuGORHz6SS9swRgkBsou33MQ2n51/boIDrbsg4tIBbRpEWK2kw==} + engines: {node: '>=v18'} + + '@commitlint/to-lines@19.8.1': + resolution: {integrity: sha512-98Mm5inzbWTKuZQr2aW4SReY6WUukdWXuZhrqf1QdKPZBCCsXuG87c+iP0bwtD6DBnmVVQjgp4whoHRVixyPBg==} + engines: {node: '>=v18'} + + '@commitlint/top-level@19.8.1': + resolution: {integrity: sha512-Ph8IN1IOHPSDhURCSXBz44+CIu+60duFwRsg6HqaISFHQHbmBtxVw4ZrFNIYUzEP7WwrNPxa2/5qJ//NK1FGcw==} + engines: {node: '>=v18'} + + '@commitlint/types@19.8.1': + resolution: {integrity: sha512-/yCrWGCoA1SVKOks25EGadP9Pnj0oAIHGpl2wH2M2Y46dPM2ueb8wyCVOD7O3WCTkaJ0IkKvzhl1JY7+uCT2Dw==} + engines: {node: '>=v18'} + '@csstools/color-helpers@5.1.0': resolution: {integrity: sha512-S11EXWJyy0Mz5SYvRmY8nJYTFFd1LCNV+7cXyAgQtOOuzb4EsgfqDufL+9esx72/eLhsRdGZwaldu/h+E4t4BA==} engines: {node: '>=18'} @@ -2862,6 +2937,9 @@ packages: '@types/chai@5.2.3': resolution: {integrity: sha512-Mw558oeA9fFbv65/y4mHtXDs9bPnFMZAL/jxdPFUpOHHIXX91mcgEHbS5Lahr+pwZFR8A7GQleRWeI6cGFC2UA==} + '@types/conventional-commits-parser@5.0.2': + resolution: {integrity: sha512-BgT2szDXnVypgpNxOK8aL5SGjUdaQbC++WZNjF1Qge3Og2+zhHj+RWhmehLhYyvQwqAmvezruVfOf8+3m74W+g==} + '@types/deep-eql@4.0.2': resolution: {integrity: sha512-c9h9dVVMigMPc4bwTvC5dxqtqJZwQPePsWjPlpSOnojbor6pGqdk541lfA7AqFQr5pB1BRdq0juY9db81BwyFw==} @@ -3112,6 +3190,10 @@ packages: '@webcontainer/env@1.1.1': resolution: {integrity: sha512-6aN99yL695Hi9SuIk1oC88l9o0gmxL1nGWWQ/kNy81HigJ0FoaoTXpytCj6ItzgyCEwA9kF1wixsTuv5cjsgng==} + JSONStream@1.3.5: + resolution: {integrity: sha512-E+iruNOY8VV9s4JEbe1aNEm6MiszPRr/UfcHMz0TQh1BXSxHK+ASV1R6W4HpjBhSeS+54PIsAMCBmwD06LLsqQ==} + hasBin: true + accepts@2.0.0: resolution: {integrity: sha512-5cvg6CtKwfgdmVqY1WIiXKc3Q1bkRqGLi+2W/6ao+6Y7gu/RCwRuAhGEzh5B4KlszSuTLgZYuqFqo5bImjNKng==} engines: {node: '>= 0.6'} @@ -3219,6 +3301,9 @@ packages: resolution: {integrity: sha512-LHE+8BuR7RYGDKvnrmcuSq3tDcKv9OFEXQt/HpbZhY7V6h0zlUXutnAD82GiFx9rdieCMjkvtcsPqBwgUl1Iiw==} engines: {node: '>= 0.4'} + array-ify@1.0.0: + resolution: {integrity: sha512-c5AMf34bKdvPhQ7tBGhqkgKNUzMr4WUs+WDtC2ZUGOUncbxKMTvqxYctiseW3+L4bA8ec+GcZ6/A/FW4m8ukng==} + array-includes@3.1.9: resolution: {integrity: sha512-FmeCCAenzH0KH381SPT5FZmiA/TmpndpcaShhfgEN9eCVjnFBqq3l1xrI42y8+PPLI6hypzou4GXw00WHmPBLQ==} engines: {node: '>= 0.4'} @@ -3459,6 +3544,9 @@ packages: resolution: {integrity: sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==} engines: {node: '>= 6'} + compare-func@2.0.0: + resolution: {integrity: sha512-zHig5N+tPWARooBnb0Zx1MFcdfpyJrfTJ3Y5L+IFvUm8rM74hHz66z0gw0x4tijh5CorKkKUCnW82R2vmpeCRA==} + concat-map@0.0.1: resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==} @@ -3482,6 +3570,19 @@ packages: resolution: {integrity: sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==} engines: {node: '>= 0.6'} + conventional-changelog-angular@7.0.0: + resolution: {integrity: sha512-ROjNchA9LgfNMTTFSIWPzebCwOGFdgkEq45EnvvrmSLvCtAw0HSmrCs7/ty+wAeYUZyNay0YMUNYFTRL72PkBQ==} + engines: {node: '>=16'} + + conventional-changelog-conventionalcommits@7.0.2: + resolution: {integrity: sha512-NKXYmMR/Hr1DevQegFB4MwfM5Vv0m4UIxKZTTYuD98lpTknaZlSRrDOG4X7wIXpGkfsYxZTghUN+Qq+T0YQI7w==} + engines: {node: '>=16'} + + conventional-commits-parser@5.0.0: + resolution: {integrity: sha512-ZPMl0ZJbw74iS9LuX9YIAiW8pfM5p3yh2o/NbXHbkFuZzY5jvdi5jFycEOkmBW5H5I7nA+D6f3UcsCLP2vvSEA==} + engines: {node: '>=16'} + hasBin: true + convert-source-map@1.9.0: resolution: {integrity: sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==} @@ -3508,6 +3609,14 @@ packages: resolution: {integrity: sha512-tJtZBBHA6vjIAaF6EnIaq6laBBP9aq/Y3ouVJjEfoHbRBcHBAHYcMh/w8LDrk2PvIMMq8gmopa5D4V8RmbrxGw==} engines: {node: '>= 0.10'} + cosmiconfig-typescript-loader@6.3.0: + resolution: {integrity: sha512-Akr82WH1Wfqatyiqpj8HDkO2o2KmJRu1FhKfSNJP3K4IdXwHfEyL7MOb62i1AGQVLtIQM+iCE9CGOtrfhR+mmA==} + engines: {node: '>=v18'} + peerDependencies: + '@types/node': '*' + cosmiconfig: '>=9' + typescript: '>=5' + cosmiconfig@9.0.1: resolution: {integrity: sha512-hr4ihw+DBqcvrsEDioRO31Z17x71pUYoNe/4h6Z0wB72p7MU7/9gH8Q3s12NFhHPfYBBOV3qyfUxmr/Yn3shnQ==} engines: {node: '>=14'} @@ -3554,6 +3663,10 @@ packages: csstype@3.1.3: resolution: {integrity: sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==} + dargs@8.1.0: + resolution: {integrity: sha512-wAV9QHOsNbwnWdNW2FYvE1P56wtgSbM+3SZcdGiWQILwVjACCXDCI3Ai8QlCjMDB8YK5zySiXZYBiwGmNY3lnw==} + engines: {node: '>=12'} + data-uri-to-buffer@4.0.1: resolution: {integrity: sha512-0R9ikRb668HB7QDxT1vkpuUBtqc53YyAwMwGeUFKRojY/NWKvdZ+9UYtRfGmhqNbRkTSVpMbmyhXipFFv2cb/A==} engines: {node: '>= 12'} @@ -3701,6 +3814,10 @@ packages: domutils@3.2.2: resolution: {integrity: sha512-6kZKyUajlDuqlHKVX1w7gyslj9MPIXzIFiz/rGu35uC1wMi+kMhQwGhl4lt9unC9Vb9INnY9Z3/ZA3+FhASLaw==} + dot-prop@5.3.0: + resolution: {integrity: sha512-QM8q3zDe58hqUqjraQOmzZ1LIH9SWQJTlEKCH4kJ2oQvLZk7RbQXvtDM2XEq3fwkV9CCvvH4LA0AV+ogFsBM2Q==} + engines: {node: '>=8'} + dotenv-cli@7.4.2: resolution: {integrity: sha512-SbUj8l61zIbzyhIbg0FwPJq6+wjbzdn9oEtozQpZ6kW2ihCcapKVZj49oCT3oPM+mgQm+itgvUQcG5szxVrZTA==} hasBin: true @@ -4071,6 +4188,10 @@ packages: resolution: {integrity: sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==} engines: {node: '>=10'} + find-up@7.0.0: + resolution: {integrity: sha512-YyZM99iHrqLKjmt4LJDj58KI+fYyufRLBSYcqycxf//KpBk9FoewoGX0450m9nB44qrZnovzC2oeP5hUibxc/g==} + engines: {node: '>=18'} + fix-dts-default-cjs-exports@1.0.1: resolution: {integrity: sha512-pVIECanWFC61Hzl2+oOCtoJ3F17kglZC/6N94eRWycFgBH35hHx0Li604ZIzhseh97mf2p0cv7vVrOZGoqhlEg==} @@ -4199,6 +4320,12 @@ packages: resolution: {integrity: sha512-w9UMqWwJxHNOvoNzSJ2oPF5wvYcvP7jUvYzhp67yEhTi17ZDBBC1z9pTdGuzjD+EFIqLSYRweZjqfiPzQ06Ebg==} engines: {node: '>= 0.4'} + git-raw-commits@4.0.0: + resolution: {integrity: sha512-ICsMM1Wk8xSGMowkOmPrzo2Fgmfo4bMHLNX6ytHjajRJUqvHOw/TFapQ+QG75c3X/tTDDhOSRPGC52dDbNM8FQ==} + engines: {node: '>=16'} + deprecated: Deprecated and no longer maintained. Use @conventional-changelog/git-client instead. + hasBin: true + glob-parent@5.1.2: resolution: {integrity: sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==} engines: {node: '>= 6'} @@ -4224,6 +4351,10 @@ packages: resolution: {integrity: sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==} deprecated: Glob versions prior to v9 are no longer supported + global-directory@4.0.1: + resolution: {integrity: sha512-wHTUcDUoZ1H5/0iVqEudYW4/kAlN5cZ3j/bXn0Dpbizl9iaUVeWSHqiOjsgk6OW2bkLclbBjzewBz6weQ1zA2Q==} + engines: {node: '>=18'} + globals@13.24.0: resolution: {integrity: sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==} engines: {node: '>=8'} @@ -4382,6 +4513,9 @@ packages: resolution: {integrity: sha512-rKtvo6a868b5Hu3heneU+L4yEQ4jYKLtjpnPeUdK7h0yzXGmyBTypknlkCvHFBqfX9YlorEiMM6Dnq/5atfHkw==} engines: {node: '>=8'} + import-meta-resolve@4.2.0: + resolution: {integrity: sha512-Iqv2fzaTQN28s/FwZAoFq0ZSs/7hMAHJVX+w8PZl3cY19Pxk6jFFalxQoIfW2826i/fDLXv8IiEZRIT0lDuWcg==} + imurmurhash@0.1.4: resolution: {integrity: sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==} engines: {node: '>=0.8.19'} @@ -4397,6 +4531,10 @@ packages: inherits@2.0.4: resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==} + ini@4.1.1: + resolution: {integrity: sha512-QQnnxNyfvmHFIsj7gkPcYymR8Jdw/o7mp5ZFihxn6h8Ci6fh3Dx4E1gPjpQEpIuPo9XVNY/ZUwh4BPMjGyL01g==} + engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} + internal-slot@1.1.0: resolution: {integrity: sha512-4gd7VpWNQNB4UKKCFFVcp1AVv+FMOgs9NKzjHKusc8jTMhd5eL1NqQqOpE0KzMds804/yHlglp3uxgluOqAPLw==} engines: {node: '>= 0.4'} @@ -4505,6 +4643,10 @@ packages: resolution: {integrity: sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==} engines: {node: '>=0.12.0'} + is-obj@2.0.0: + resolution: {integrity: sha512-drqDG3cbczxxEJRoOXcOjtdp1J/lyp1mNn0xaznRs8+muBhgQcrnbspox5X5fOw0HnMnbfDzvnEMEtqDEJEo8w==} + engines: {node: '>=8'} + is-obj@3.0.0: resolution: {integrity: sha512-IlsXEHOjtKhpN8r/tRFj2nDyTmHvcfNeu/nrRIcXE17ROeatXchkojffa1SpdqW4cr/Fj6QkEf/Gn4zf6KKvEQ==} engines: {node: '>=12'} @@ -4559,6 +4701,10 @@ packages: resolution: {integrity: sha512-9gGx6GTtCQM73BgmHQXfDmLtfjjTUDSyoxTCbp5WtoixAhfgsDirWIcVQ/IHpvI5Vgd5i/J5F7B9cN/WlVbC/w==} engines: {node: '>= 0.4'} + is-text-path@2.0.0: + resolution: {integrity: sha512-+oDTluR6WEjdXEJMnC2z6A4FRwFoYuvShVVEGsS7ewc0UTi2QtAKMDJuL4BDEVt+5T7MjFo12RP8ghOM75oKJw==} + engines: {node: '>=8'} + is-typed-array@1.1.15: resolution: {integrity: sha512-p3EcsicXjit7SaskXHs1hA91QxgTw46Fv6EFKKGS5DRFLD8yKnohjF3hxoju94b/OcMZoQukzpPpBE9uLVKzgQ==} engines: {node: '>= 0.4'} @@ -4716,6 +4862,10 @@ packages: jsonfile@6.2.0: resolution: {integrity: sha512-FGuPw30AdOIUTRMC2OMRtQV+jkVj2cfPqSeWXv1NEAJ1qZ5zb1X6z1mFhbfOB/iy3ssJCD+3KuZ8r8C3uVFlAg==} + jsonparse@1.3.1: + resolution: {integrity: sha512-POQXvpdL69+CluYsillJ7SUhKvytYjW9vG/GKpnf+xP8UWgYEM/RaMzHHofbALDiKbbP1W8UEYmgGl39WkPZsg==} + engines: {'0': node >= 0.2.0} + jsx-ast-utils@3.3.5: resolution: {integrity: sha512-ZZow9HBI5O6EPgSJLUb8n2NKgmVWTwCvHGwFuJlMjvLFqlGG6pjirPhtdsseaLZjSibD8eegzmYpUZwoIlj2cQ==} engines: {node: '>=4.0'} @@ -4924,15 +5074,40 @@ packages: resolution: {integrity: sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==} engines: {node: '>=10'} + locate-path@7.2.0: + resolution: {integrity: sha512-gvVijfZvn7R+2qyPX8mAuKcFGDf6Nc61GdvGafQsHL0sBIxfKzA+usWn4GFC/bk+QdwPUD4kWFJLhElipq+0VA==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + + lodash.camelcase@4.3.0: + resolution: {integrity: sha512-TwuEnCnxbc3rAvhf/LbG7tJUDzhqXyFnv3dtzLOPgCG/hODL7WFnsbwktkD7yUV0RrreP/l1PALq/YSg6VvjlA==} + + lodash.isplainobject@4.0.6: + resolution: {integrity: sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA==} + + lodash.kebabcase@4.1.1: + resolution: {integrity: sha512-N8XRTIMMqqDgSy4VLKPnJ/+hpGZN+PHQiJnSenYqPaVV/NCqEogTnAdZLQiGKhxX+JCs8waWq2t1XHWKOmlY8g==} + lodash.merge@4.6.2: resolution: {integrity: sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==} + lodash.mergewith@4.6.2: + resolution: {integrity: sha512-GK3g5RPZWTRSeLSpgP8Xhra+pnjBC56q9FZYe1d5RN3TJ35dbkGy3YqBSMbyCrlbi+CM9Z3Jk5yTL7RCsqboyQ==} + + lodash.snakecase@4.1.1: + resolution: {integrity: sha512-QZ1d4xoBHYUeuouhEq3lk3Uq7ldgyFXGBhg04+oRLnIz8o9T65Eh+8YdroUwn846zchkA9yDsDl5CVVaV2nqYw==} + lodash.sortby@4.7.0: resolution: {integrity: sha512-HDWXG8isMntAyRF5vZ7xKuEvOhT4AhlRt/3czTSjvGUxjYCBVRQY48ViDHyfYz9VIoBkW4TMGQNapx+l3RUwdA==} lodash.startcase@4.4.0: resolution: {integrity: sha512-+WKqsK294HMSc2jEbNgpHpd0JfIBhp7rEV4aqXWqFr6AlXov+SlcgB1Fv01y2kGe3Gc8nMW7VA0SrGuSkRfIEg==} + lodash.uniq@4.5.0: + resolution: {integrity: sha512-xfBaXQd9ryd9dlSDvnvI0lvxfLJlYAZzXomUYzLKtUeOQvOP5piqAWuGtrhWeqaXK9hhoM/iyJc5AV+XfsX3HQ==} + + lodash.upperfirst@4.3.1: + resolution: {integrity: sha512-sReKOYJIJf74dhJONhU4e0/shzi1trVbSWDOhKYE5XV2O+H7Sb2Dihwuc7xWxVl+DgFPyTqIN3zMfT9cq5iWDg==} + lodash@4.17.21: resolution: {integrity: sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==} @@ -5020,6 +5195,10 @@ packages: resolution: {integrity: sha512-aisnrDP4GNe06UcKFnV5bfMNPBUw4jsLGaWwWfnH3v02GnBuXX2MCVn5RbrWo0j3pczUilYblq7fQ7Nw2t5XKw==} engines: {node: '>= 0.8'} + meow@12.1.1: + resolution: {integrity: sha512-BhXM0Au22RwUneMPwSCnyhTOizdWoIEPU9sp0Aqa1PnDMR5Wv2FGXYDjuzJEIX+Eo2Rb8xuYe5jrnm5QowQFkw==} + engines: {node: '>=16.10'} + merge-descriptors@2.0.0: resolution: {integrity: sha512-Snk314V5ayFLhp3fkUREub6WtjBfPdCPY1Ln8/8munuLuiYhsABgBVWsozAG+MWMbVEvcdcpbi9R7ww22l9Q3g==} engines: {node: '>=18'} @@ -5278,6 +5457,10 @@ packages: resolution: {integrity: sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==} engines: {node: '>=10'} + p-limit@4.0.0: + resolution: {integrity: sha512-5b0R4txpzjPWVw/cXXUResoD4hb6U/x9BH08L7nw+GN1sezDzPdxeRvpc9c433fZhBan/wusjbCsqwqm4EIBIQ==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + p-locate@4.1.0: resolution: {integrity: sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==} engines: {node: '>=8'} @@ -5286,6 +5469,10 @@ packages: resolution: {integrity: sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==} engines: {node: '>=10'} + p-locate@6.0.0: + resolution: {integrity: sha512-wPrq66Llhl7/4AGC6I+cqxT07LhXvWL08LNXz1fENOw0Ap4sRZZ/gZpTTJ5jpurzzzfS2W/Ge9BY3LgLjCShcw==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + p-map@2.1.0: resolution: {integrity: sha512-y3b8Kpd8OAN444hxfBbFfj1FY/RjtTd8tzYwhUqNYXx0fXx2iX4maP4Qr6qhIKbQXI02wTLAda4fYUbDagTUFw==} engines: {node: '>=6'} @@ -5332,6 +5519,10 @@ packages: resolution: {integrity: sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==} engines: {node: '>=8'} + path-exists@5.0.0: + resolution: {integrity: sha512-RjhtfwJOxzcFmNOi6ltcbcu4Iu+FL3zEj83dk4kAS+fVpTxXLO1b38RvJgT/0QwvV/L3aY9TAnyv0EOqW4GoMQ==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + path-is-absolute@1.0.1: resolution: {integrity: sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==} engines: {node: '>=0.10.0'} @@ -5870,6 +6061,10 @@ packages: spawndamnit@3.0.1: resolution: {integrity: sha512-MmnduQUuHCoFckZoWnXsTg7JaiLBJrKFj9UI2MbRPGaJeVpsLcVBu6P/IGZovziM/YBsellCmsprgNA+w0CzVg==} + split2@4.2.0: + resolution: {integrity: sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg==} + engines: {node: '>= 10.x'} + sprintf-js@1.0.3: resolution: {integrity: sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==} @@ -6036,6 +6231,10 @@ packages: resolution: {integrity: sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w==} engines: {node: '>=8'} + text-extensions@2.4.0: + resolution: {integrity: sha512-te/NtwBwfiNRLf9Ijqx3T0nlqZiQ2XrrtBvu+cLL8ZRrGkO0NHTug8MYFKyoSrv/sHTaSKfilUkizV6XhxMJ3g==} + engines: {node: '>=8'} + text-table@0.2.0: resolution: {integrity: sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==} @@ -6046,6 +6245,9 @@ packages: thenify@3.3.1: resolution: {integrity: sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw==} + through@2.3.8: + resolution: {integrity: sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==} + tiny-invariant@1.3.3: resolution: {integrity: sha512-+FbBPE1o9QAYvviau/qC5SE3caw21q3xkvWKBtja5vgqOWIHHJ3ioaq1VPfn/Szqctz2bU/oYeKd9/z5BL+PVg==} @@ -6284,6 +6486,10 @@ packages: undici-types@7.16.0: resolution: {integrity: sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw==} + unicorn-magic@0.1.0: + resolution: {integrity: sha512-lRfVq8fE8gz6QMBuDM6a+LO3IAzTi05H6gCVaUpir2E1Rwpo4ZUog45KpNXKC/Mn3Yb9UDuHumeFTo9iV/D9FQ==} + engines: {node: '>=18'} + unicorn-magic@0.3.0: resolution: {integrity: sha512-+QBBXBCvifc56fsbuxZQ6Sic3wqqc3WWaqxs58gvJrcOuN83HGTCwz3oS5phzU9LthRNE9VrJCFCLUgHeeFnfA==} engines: {node: '>=18'} @@ -6615,6 +6821,10 @@ packages: resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==} engines: {node: '>=10'} + yocto-queue@1.2.2: + resolution: {integrity: sha512-4LCcse/U2MHZ63HAJVE+v71o7yOdIe4cZ70Wpf8D/IyjDKYQLV5GD46B+hSTjJsvV5PztjvHoU580EftxjDZFQ==} + engines: {node: '>=12.20'} + yoctocolors-cjs@2.1.3: resolution: {integrity: sha512-U/PBtDf35ff0D8X8D0jfdzHYEPFxAI7jJlxZXwCSez5M3190m+QobIfh+sWDWSHMCWWJN2AWamkegn6vr6YBTw==} engines: {node: '>=18'} @@ -7187,6 +7397,116 @@ snapshots: human-id: 4.1.1 prettier: 2.8.8 + '@commitlint/cli@19.8.1(@types/node@24.9.1)(typescript@5.9.3)': + dependencies: + '@commitlint/format': 19.8.1 + '@commitlint/lint': 19.8.1 + '@commitlint/load': 19.8.1(@types/node@24.9.1)(typescript@5.9.3) + '@commitlint/read': 19.8.1 + '@commitlint/types': 19.8.1 + tinyexec: 1.0.2 + yargs: 17.7.2 + transitivePeerDependencies: + - '@types/node' + - typescript + + '@commitlint/config-conventional@19.8.1': + dependencies: + '@commitlint/types': 19.8.1 + conventional-changelog-conventionalcommits: 7.0.2 + + '@commitlint/config-validator@19.8.1': + dependencies: + '@commitlint/types': 19.8.1 + ajv: 8.18.0 + + '@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 + + '@commitlint/execute-rule@19.8.1': {} + + '@commitlint/format@19.8.1': + dependencies: + '@commitlint/types': 19.8.1 + chalk: 5.6.2 + + '@commitlint/is-ignored@19.8.1': + dependencies: + '@commitlint/types': 19.8.1 + semver: 7.7.3 + + '@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 + + '@commitlint/load@19.8.1(@types/node@24.9.1)(typescript@5.9.3)': + 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.6.2 + cosmiconfig: 9.0.1(typescript@5.9.3) + cosmiconfig-typescript-loader: 6.3.0(@types/node@24.9.1)(cosmiconfig@9.0.1(typescript@5.9.3))(typescript@5.9.3) + lodash.isplainobject: 4.0.6 + lodash.merge: 4.6.2 + lodash.uniq: 4.5.0 + transitivePeerDependencies: + - '@types/node' + - typescript + + '@commitlint/message@19.8.1': {} + + '@commitlint/parse@19.8.1': + dependencies: + '@commitlint/types': 19.8.1 + conventional-changelog-angular: 7.0.0 + conventional-commits-parser: 5.0.0 + + '@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.2 + + '@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.2.0 + lodash.mergewith: 4.6.2 + resolve-from: 5.0.0 + + '@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 + + '@commitlint/to-lines@19.8.1': {} + + '@commitlint/top-level@19.8.1': + dependencies: + find-up: 7.0.0 + + '@commitlint/types@19.8.1': + dependencies: + '@types/conventional-commits-parser': 5.0.2 + chalk: 5.6.2 + '@csstools/color-helpers@5.1.0': {} '@csstools/css-calc@2.1.4(@csstools/css-parser-algorithms@3.0.5(@csstools/css-tokenizer@3.0.4))(@csstools/css-tokenizer@3.0.4)': @@ -9266,6 +9586,10 @@ snapshots: '@types/deep-eql': 4.0.2 assertion-error: 2.0.1 + '@types/conventional-commits-parser@5.0.2': + dependencies: + '@types/node': 24.11.0 + '@types/deep-eql@4.0.2': {} '@types/doctrine@0.0.9': {} @@ -9701,6 +10025,11 @@ snapshots: '@webcontainer/env@1.1.1': {} + JSONStream@1.3.5: + dependencies: + jsonparse: 1.3.1 + through: 2.3.8 + accepts@2.0.0: dependencies: mime-types: 3.0.2 @@ -9801,6 +10130,8 @@ snapshots: call-bound: 1.0.4 is-array-buffer: 3.0.5 + array-ify@1.0.0: {} + array-includes@3.1.9: dependencies: call-bind: 1.0.8 @@ -10055,6 +10386,11 @@ snapshots: commander@4.1.1: {} + compare-func@2.0.0: + dependencies: + array-ify: 1.0.0 + dot-prop: 5.3.0 + concat-map@0.0.1: {} concurrently@9.2.1: @@ -10074,6 +10410,21 @@ snapshots: content-type@1.0.5: {} + conventional-changelog-angular@7.0.0: + dependencies: + compare-func: 2.0.0 + + conventional-changelog-conventionalcommits@7.0.2: + dependencies: + compare-func: 2.0.0 + + conventional-commits-parser@5.0.0: + dependencies: + JSONStream: 1.3.5 + is-text-path: 2.0.0 + meow: 12.1.1 + split2: 4.2.0 + convert-source-map@1.9.0: {} convert-source-map@2.0.0: {} @@ -10091,6 +10442,13 @@ snapshots: object-assign: 4.1.1 vary: 1.1.2 + cosmiconfig-typescript-loader@6.3.0(@types/node@24.9.1)(cosmiconfig@9.0.1(typescript@5.9.3))(typescript@5.9.3): + dependencies: + '@types/node': 24.9.1 + cosmiconfig: 9.0.1(typescript@5.9.3) + jiti: 2.6.1 + typescript: 5.9.3 + cosmiconfig@9.0.1(typescript@5.9.3): dependencies: env-paths: 2.2.1 @@ -10142,6 +10500,8 @@ snapshots: csstype@3.1.3: {} + dargs@8.1.0: {} + data-uri-to-buffer@4.0.1: {} data-urls@5.0.0: @@ -10261,6 +10621,10 @@ snapshots: domelementtype: 2.3.0 domhandler: 5.0.3 + dot-prop@5.3.0: + dependencies: + is-obj: 2.0.0 + dotenv-cli@7.4.2: dependencies: cross-spawn: 7.0.6 @@ -10896,6 +11260,12 @@ snapshots: locate-path: 6.0.0 path-exists: 4.0.0 + find-up@7.0.0: + dependencies: + locate-path: 7.2.0 + path-exists: 5.0.0 + unicorn-magic: 0.1.0 + fix-dts-default-cjs-exports@1.0.1: dependencies: magic-string: 0.30.19 @@ -11030,6 +11400,12 @@ snapshots: es-errors: 1.3.0 get-intrinsic: 1.3.0 + git-raw-commits@4.0.0: + dependencies: + dargs: 8.1.0 + meow: 12.1.1 + split2: 4.2.0 + glob-parent@5.1.2: dependencies: is-glob: 4.0.3 @@ -11071,6 +11447,10 @@ snapshots: once: 1.4.0 path-is-absolute: 1.0.1 + global-directory@4.0.1: + dependencies: + ini: 4.1.1 + globals@13.24.0: dependencies: type-fest: 0.20.2 @@ -11214,6 +11594,8 @@ snapshots: import-lazy@4.0.0: {} + import-meta-resolve@4.2.0: {} + imurmurhash@0.1.4: {} indent-string@4.0.0: {} @@ -11225,6 +11607,8 @@ snapshots: inherits@2.0.4: {} + ini@4.1.1: {} + internal-slot@1.1.0: dependencies: es-errors: 1.3.0 @@ -11323,6 +11707,8 @@ snapshots: is-number@7.0.0: {} + is-obj@2.0.0: {} + is-obj@3.0.0: {} is-path-inside@3.0.3: {} @@ -11367,6 +11753,10 @@ snapshots: has-symbols: 1.1.0 safe-regex-test: 1.1.0 + is-text-path@2.0.0: + dependencies: + text-extensions: 2.4.0 + is-typed-array@1.1.15: dependencies: which-typed-array: 1.1.19 @@ -11560,6 +11950,8 @@ snapshots: optionalDependencies: graceful-fs: 4.2.11 + jsonparse@1.3.1: {} + jsx-ast-utils@3.3.5: dependencies: array-includes: 3.1.9 @@ -11742,12 +12134,30 @@ snapshots: dependencies: p-locate: 5.0.0 + locate-path@7.2.0: + dependencies: + p-locate: 6.0.0 + + lodash.camelcase@4.3.0: {} + + lodash.isplainobject@4.0.6: {} + + lodash.kebabcase@4.1.1: {} + lodash.merge@4.6.2: {} + lodash.mergewith@4.6.2: {} + + lodash.snakecase@4.1.1: {} + lodash.sortby@4.7.0: {} lodash.startcase@4.4.0: {} + lodash.uniq@4.5.0: {} + + lodash.upperfirst@4.3.1: {} + lodash@4.17.21: {} log-symbols@6.0.0: @@ -11817,6 +12227,8 @@ snapshots: media-typer@1.1.0: {} + meow@12.1.1: {} + merge-descriptors@2.0.0: {} merge-source-map@1.1.0: @@ -12122,6 +12534,10 @@ snapshots: dependencies: yocto-queue: 0.1.0 + p-limit@4.0.0: + dependencies: + yocto-queue: 1.2.2 + p-locate@4.1.0: dependencies: p-limit: 2.3.0 @@ -12130,6 +12546,10 @@ snapshots: dependencies: p-limit: 3.1.0 + p-locate@6.0.0: + dependencies: + p-limit: 4.0.0 + p-map@2.1.0: {} p-try@2.2.0: {} @@ -12169,6 +12589,8 @@ snapshots: path-exists@4.0.0: {} + path-exists@5.0.0: {} + path-is-absolute@1.0.1: {} path-key@3.1.1: {} @@ -12810,6 +13232,8 @@ snapshots: cross-spawn: 7.0.6 signal-exit: 4.1.0 + split2@4.2.0: {} + sprintf-js@1.0.3: {} stackback@0.0.2: {} @@ -13000,6 +13424,8 @@ snapshots: glob: 7.2.3 minimatch: 3.1.2 + text-extensions@2.4.0: {} + text-table@0.2.0: {} thenify-all@1.6.0: @@ -13010,6 +13436,8 @@ snapshots: dependencies: any-promise: 1.3.0 + through@2.3.8: {} + tiny-invariant@1.3.3: {} tinybench@2.9.0: {} @@ -13278,6 +13706,8 @@ snapshots: undici-types@7.16.0: {} + unicorn-magic@0.1.0: {} + unicorn-magic@0.3.0: {} universalify@0.1.2: {} @@ -13619,6 +14049,8 @@ snapshots: yocto-queue@0.1.0: {} + yocto-queue@1.2.2: {} + yoctocolors-cjs@2.1.3: {} yoctocolors@2.1.2: {}