From 58b46db4735ad6b62ac5fe695ad5d1669afa5715 Mon Sep 17 00:00:00 2001 From: Alex Date: Tue, 24 Feb 2026 09:24:33 -0800 Subject: [PATCH 01/13] add configurable merge conflict resolution Replace hardcoded solveMergeConflict() with config-driven resolver that supports file-level and line-level resolution. Rules defined in .github/conflict-resolution.yml override built-in defaults. - Add js-yaml and minimatch dependencies - Create src/conflict-resolution.js with resolver factory - Modify integration-merge.js to use resolver - Update AGENTS.md and add spec documentation Co-Authored-By: Claude Opus 4.5 --- AGENTS.md | 73 +++++++ package.json | 2 + specs/configurable-conflict-resolution.md | 88 ++++++++ src/conflict-resolution.js | 236 ++++++++++++++++++++++ src/integration-merge.js | 52 ++--- 5 files changed, 416 insertions(+), 35 deletions(-) create mode 100644 AGENTS.md create mode 100644 specs/configurable-conflict-resolution.md create mode 100644 src/conflict-resolution.js diff --git a/AGENTS.md b/AGENTS.md new file mode 100644 index 0000000..cc934c0 --- /dev/null +++ b/AGENTS.md @@ -0,0 +1,73 @@ +## Project Overview + +A GitHub Action that automatically integrates approved Pull Requests into a staging/integration branch via squash merges. It filters PRs by label, handles merge conflicts for known files, and tracks integration status through GitHub labels. + +## Commands + +- **Run locally:** `npm start` (runs `node src/main.js`, requires GitHub Action environment variables) +- **Install dependencies:** `npm install` +- **Manual test:** Trigger via GitHub Actions workflow dispatch (`.github/workflows/test_workflow.yml`) +- No test suite, linter, or build step is configured. + +## Architecture + +**Entry flow:** `entrypoint.js` → installs npm deps → dynamically imports `src/main.js` + +**Source files (`src/`):** + +- **main.js** — Reads GitHub Action inputs, creates Octokit client, calls `integrationMerge()`, sets `haveUpdates` output +- **integration-merge.js** — Core orchestration: fetches open PRs (max 25, oldest first), filters by approve label, clones repo into temp dir, resets integration branch to master, squash-merges each PR, delegates conflict resolution to `conflict-resolution.js`, writes integration data, pushes, and updates labels +- **conflict-resolution.js** — Configurable merge conflict resolver. Loads rules from `.github/conflict-resolution.yml` or uses built-in defaults. Supports file-level resolution (checkout theirs/ours) and line-level resolution (auto-resolve hunks matching ignore patterns) +- **git.js** — Thin wrapper spawning `git` subprocesses. Exports functions for clone, fetch PR, branch management, squash merge, push (force-with-lease), conflict listing/resolution, etc. Defines custom `ExitError` class +- **common.js** — `tmpdir(callback)` utility that creates a temp directory, runs the callback, then cleans up + +**Key behaviors:** + +- Conflict resolution is configurable via `.github/conflict-resolution.yml`. Without a config file, built-in defaults apply: `version.rb` and `db/schema.rb` use file-level "theirs", `package.json` uses line-level "theirs" for version-only conflicts. Unresolvable conflicts cause the PR to be skipped. +- Squash commit messages follow the format: `{PR title} (#{number}) (sha:{7-char-hash})` +- SHA signatures are compared to detect if PRs are already integrated, avoiding redundant work +- `package.json` version is stamped with `stage.0.{timestamp}` format on integration +- Uses ES modules (`"type": "module"` in package.json) + +**Conflict resolution config format (`.github/conflict-resolution.yml`):** + +```yaml +"version.rb": + resolve: file # file-level: checkout entire file + side: theirs # theirs (default) or ours + +"package.json": + resolve: lines # line-level: resolve hunks matching ignore patterns + side: theirs + ignore: # required for resolve: lines + - '"version"\s*:' # regex patterns; all non-blank lines must match + +"db/*.rb": # glob patterns supported + resolve: file + side: theirs +``` + +Built-in defaults (when no config file exists): + +| Pattern | Resolve | Side | Ignore | +|----------------|---------|--------|------------------| +| `version.rb` | file | theirs | — | +| `db/schema.rb` | file | theirs | — | +| `package.json` | lines | theirs | `"version"\s*:` | + +## Action Configuration + +Inputs: `repository`, `github_token`, `token_with_workflow_scope`, `master_branch` (default: "master"), `integration_branch` (default: "stage"), `approve_label` (default: "Approved"), `integrated_label` (default: "Integrated") + +Output: `haveUpdates` — "yes" or "no" + +Runtime: node16 (per action.yml) + + +## Formatting + +- all MD files should be readable in raw text, this includes: + - table columns width alignment + +- code files should be structured so that top level methods will be on top of the file and private, implementation specifics moved to the bottom of it. + - e.g. humans read from top to bottom, and the file structure should reflect this diff --git a/package.json b/package.json index b0c4945..9da203f 100644 --- a/package.json +++ b/package.json @@ -16,6 +16,8 @@ "@octokit/rest": "^19.0.5", "@actions/github": "^2.2.0", "fs-extra": "^10.1.0", + "js-yaml": "^4.1.0", + "minimatch": "^9.0.0", "tmp": "^0.2.1" } diff --git a/specs/configurable-conflict-resolution.md b/specs/configurable-conflict-resolution.md new file mode 100644 index 0000000..973efe1 --- /dev/null +++ b/specs/configurable-conflict-resolution.md @@ -0,0 +1,88 @@ +# Spec: Configurable Merge Conflict Resolution + +## Overview + +Replace the hardcoded conflict resolution in `solveMergeConflict()` (`src/integration-merge.js:186-209`) with a config-driven system. Rules are defined per-repository in a YAML file with two resolution levels: file-level and line-level. + +## Config File + +**Location:** `.github/conflict-resolution.yml` in the target repository. + +Loaded at runtime after cloning. If absent, built-in defaults apply. If present with YAML syntax errors or invalid structure, the action **fails immediately** with a clear error message. Rules referencing non-existent files are not errors. + +## Config Format + +```yaml +"version.rb": + resolve: file + side: theirs + +"db/schema.rb": + resolve: file + +"db/*.rb": + resolve: file + side: ours + +"package.json": + resolve: lines + side: theirs + ignore: + - '"version"\s*:' + - '"someField"\s*:' +``` + +**Every entry has the same structure:** + +| Field | Required | Description | +|-----------------|-----------------------|---------------------------------------------------------| +| key (top-level) | yes | Exact file path or glob pattern | +| `resolve` | yes | `"file"` or `"lines"` | +| `side` | no | `"theirs"` or `"ours"`. Default: `"theirs"` | +| `ignore` | when `resolve: lines` | Array of regex patterns matching line content to ignore | + +## Resolution Logic + +**File-level (`resolve: file`):** +Runs `git checkout --{side} -- ` then `git add `. Same as current behavior. + +**Line-level (`resolve: lines`):** +1. Parse git conflict markers (`<<<<<<<`, `=======`, `>>>>>>>`) in the conflicted file +2. For each conflict hunk, check if **all** changed lines (both sides) match at least one `ignore` regex +3. Matching hunks: resolve by accepting the file-wide `side` +4. Non-matching hunks: resolution fails for this file +5. All hunks resolved → write merged file and stage it +6. Any hunk unresolved → file resolution fails, PR is skipped + +## Built-in Defaults + +When **no config file** exists: + +| File | Resolve | Side | Ignore | +|----------------|---------|--------|-----------------| +| `version.rb` | file | theirs | — | +| `db/schema.rb` | file | theirs | — | +| `package.json` | lines | theirs | `"version"\s*:` | + +When a config file **exists**, it **fully overrides** these defaults. + +## Rule Matching + +When a conflicted file matches multiple rules (e.g. exact path and a glob), the **first matching rule** wins (top-to-bottom order in YAML). + +## Error Handling + +| Condition | Behavior | +|------------------------------------------------|--------------------------------------| +| Config file missing | Use built-in defaults | +| YAML syntax error | Fail action immediately | +| Invalid rule (bad `resolve`/`side`/`ignore`) | Fail action immediately | +| Rule references non-existent file | Not an error | +| Line-level hunk has no matching ignore pattern | File resolution fails, PR is skipped | + +## Files Changed + +- **`src/integration-merge.js`** — Remove `solveMergeConflict()`; create resolver after clone; use `resolver.resolve()` in merge conflict loop +- **`src/conflict-resolution.js`** (new) — Config loading/validation, conflict marker parsing, line-level resolution logic +- **`action.yml`** — No changes (config lives in the target repo) +- **New dependencies** — `js-yaml` for YAML parsing, `minimatch` for glob pattern matching diff --git a/src/conflict-resolution.js b/src/conflict-resolution.js new file mode 100644 index 0000000..3c6e606 --- /dev/null +++ b/src/conflict-resolution.js @@ -0,0 +1,236 @@ +import * as core from '@actions/core'; +import fse from 'fs-extra'; +import yaml from 'js-yaml'; +import { minimatch } from 'minimatch'; +import path from 'path'; + + +const CONFIG_PATH = '.github/conflict-resolution.yml'; + +const BUILT_IN_RULES = [ + { pattern: 'version.rb', resolve: 'file', side: 'theirs', ignore: null }, + { pattern: 'db/schema.rb', resolve: 'file', side: 'theirs', ignore: null }, + { pattern: 'package.json', resolve: 'lines', side: 'theirs', ignore: [/"version"\s*:/] }, +]; + + +// --- Public API --- + +export async function createConflictResolver(repoDir, git) { + const rules = await loadConfig(repoDir); + + return { + async resolve(filePath) { + const rule = findMatchingRule(rules, filePath); + + if (!rule) { + core.info(` ${filePath}: no conflict resolution rule matched`); + return false; + } + + if (rule.resolve === 'file') { + core.info(` resolve with '${rule.side}' ${filePath}`); + await git.checkoutConflictedFile(repoDir, filePath, rule.side); + return true; + } + + // resolve === 'lines' + return await resolveLineLevel(repoDir, filePath, rule, git); + } + }; +} + + +// --- Config loading --- + +async function loadConfig(repoDir) { + const configFile = path.join(repoDir, CONFIG_PATH); + + if (!await fse.pathExists(configFile)) { + core.info('No conflict-resolution config found, using built-in defaults'); + return BUILT_IN_RULES; + } + + core.info(`Loading conflict resolution config from ${CONFIG_PATH}`); + const content = await fse.readFile(configFile, 'utf8'); + const config = yaml.load(content); + + if (!config || typeof config !== 'object') { + throw new Error(`${CONFIG_PATH} must contain a YAML mapping of pattern rules`); + } + + const rules = []; + for (const [pattern, rule] of Object.entries(config)) { + if (!rule || typeof rule !== 'object') { + throw new Error(`Rule '${pattern}': must be a YAML mapping with at least a 'resolve' key`); + } + rules.push(validateRule(pattern, rule)); + } + + if (rules.length === 0) { + throw new Error(`${CONFIG_PATH} must contain at least one rule`); + } + + return rules; +} + + +// --- Line-level resolution --- + +async function resolveLineLevel(repoDir, filePath, rule, git) { + const fullPath = path.join(repoDir, filePath); + const content = await fse.readFile(fullPath, 'utf8'); + const lines = content.split('\n'); + + const hunks = parseConflictHunks(lines); + if (hunks.length === 0) { + core.info(` ${filePath}: no valid conflict markers found`); + return false; + } + + for (const hunk of hunks) { + if (!isHunkIgnorable(hunk, rule.ignore)) { + core.info(` ${filePath}: conflict contains non-ignorable changes`); + return false; + } + } + + core.info(` resolved ${hunks.length} conflict hunk(s) with '${rule.side}' in ${filePath}`); + const resolved = buildResolvedContent(lines, hunks, rule.side); + await fse.writeFile(fullPath, resolved, 'utf8'); + await git.addFile(repoDir, filePath); + return true; +} + + +// --- Implementation details --- + +function validateRule(pattern, rule) { + if (typeof pattern !== 'string' || pattern.length === 0) { + throw new Error(`Invalid pattern: must be a non-empty string, got '${pattern}'`); + } + + const resolve = rule.resolve; + if (resolve !== 'file' && resolve !== 'lines') { + throw new Error(`Rule '${pattern}': 'resolve' must be "file" or "lines", got "${resolve}"`); + } + + const side = rule.side || 'theirs'; + if (side !== 'theirs' && side !== 'ours') { + throw new Error(`Rule '${pattern}': 'side' must be "theirs" or "ours", got "${side}"`); + } + + let compiledIgnore = null; + if (resolve === 'lines') { + if (!Array.isArray(rule.ignore) || rule.ignore.length === 0) { + throw new Error(`Rule '${pattern}': 'resolve: lines' requires a non-empty 'ignore' array of regex patterns`); + } + compiledIgnore = rule.ignore.map(pat => { + try { + return new RegExp(pat); + } catch (e) { + throw new Error(`Rule '${pattern}': invalid regex '${pat}': ${e.message}`); + } + }); + } + + return { pattern, resolve, side, ignore: compiledIgnore }; +} + + +function findMatchingRule(rules, filePath) { + for (const rule of rules) { + if (rule.pattern === filePath || minimatch(filePath, rule.pattern)) { + return rule; + } + } + return null; +} + + +function parseConflictHunks(lines) { + const hunks = []; + let i = 0; + + while (i < lines.length) { + if (!lines[i].startsWith('<<<<<<<')) { + i++; + continue; + } + + const startLine = i; + i++; + + const oursLines = []; + while (i < lines.length && !lines[i].startsWith('=======')) { + oursLines.push(lines[i]); + i++; + } + + if (i >= lines.length) { + // malformed — missing ======= + return []; + } + i++; // skip ======= + + const theirsLines = []; + while (i < lines.length && !lines[i].startsWith('>>>>>>>')) { + theirsLines.push(lines[i]); + i++; + } + + if (i >= lines.length) { + // malformed — missing >>>>>>> + return []; + } + + hunks.push({ startLine, endLine: i, oursLines, theirsLines }); + i++; // skip >>>>>>> + } + + return hunks; +} + + +function isHunkIgnorable(hunk, ignorePatterns) { + const allLines = [...hunk.oursLines, ...hunk.theirsLines]; + for (const line of allLines) { + if (line.trim().length === 0) { + continue; + } + const matchesAny = ignorePatterns.some(re => re.test(line)); + if (!matchesAny) { + return false; + } + } + return true; +} + + +function buildResolvedContent(lines, hunks, side) { + const result = []; + let lineIdx = 0; + + for (const hunk of hunks) { + // add non-conflict lines before this hunk + while (lineIdx < hunk.startLine) { + result.push(lines[lineIdx]); + lineIdx++; + } + // replace hunk with chosen side + const replacement = side === 'theirs' ? hunk.theirsLines : hunk.oursLines; + for (const line of replacement) { + result.push(line); + } + // skip past the hunk in the original + lineIdx = hunk.endLine + 1; + } + + // add remaining lines after last hunk + while (lineIdx < lines.length) { + result.push(lines[lineIdx]); + lineIdx++; + } + + return result.join('\n'); +} diff --git a/src/integration-merge.js b/src/integration-merge.js index 79b0996..fd004b6 100644 --- a/src/integration-merge.js +++ b/src/integration-merge.js @@ -2,6 +2,7 @@ import * as core from '@actions/core'; import fse from 'fs-extra'; import { tmpdir } from './common.js'; +import { createConflictResolver } from './conflict-resolution.js'; import git from './git.js'; @@ -50,6 +51,14 @@ async function integrationMerge({octokit, gitToken, masterBranch, integrationBra // to use local setup - comment git.clone above, and uncomment line below // await git.checkout(path, masterBranch); + let resolver; + try { + resolver = await createConflictResolver(path, git); + } catch (e) { + core.setFailed(`Conflict resolution config error: ${e.message}`); + return false; + } + if (await arePrsAlreadyIntegrated(pullRequests, path, masterBranch, integrationBranch)) { core.info("All PRs already integrated."); return false; @@ -84,21 +93,20 @@ async function integrationMerge({octokit, gitToken, masterBranch, integrationBra let files = await git.listMergeConflicts(path); core.info(` conflicting files: ${files}`); - let resolved = true + let resolved = true; for (const file of files) { - if (!await solveMergeConflict(git, path, file)) { - // there are more conflicts then we can solve... - // rollback merge and proceed with next PR - core.info(` ! PR ${prNumber} merge failed. Skipping.`); - await git.reset(path); - failedPrs.push(pullRequest); + if (!await resolver.resolve(file)) { resolved = false; break; } } - // skip this PR merge - if (!resolved) continue; + if (!resolved) { + core.info(` ! PR ${prNumber} merge failed. Skipping.`); + await git.reset(path); + failedPrs.push(pullRequest); + continue; + } } await git.commit(path, commitMessage); @@ -183,32 +191,6 @@ async function arePrsAlreadyIntegrated(pullRequests, gitPath, masterBranch, inte } -async function solveMergeConflict(git, path, file) { - - if (file === "version.rb") { - core.info(" resolve with 'theirs' version.rb"); - await git.checkoutConflictedFile(path, "version.rb", "theirs"); - return true - } - else if (file === "db/schema.rb") { - core.info(" resolve with 'theirs' db/schema.rb"); - await git.checkoutConflictedFile(path, "db/schema.rb", "theirs"); - return true - } - else if (file === "package.json") { - // this is not perfectly fine - // we need to check if the conflict happens on the version line only - // and set version to some text, so other file parts can be auto-merged - // if there more unresolved conflicts then version -> we can't resolve - core.info(" resolve with 'theirs' package.json"); - await git.checkoutConflictedFile(path, "package.json", "theirs"); - return true - } - - return false -} - - async function logIntegrationData(git, path, pullRequests) { // add file with all the PR titles const titles = pullRequests.map(pr => `${pr.title} (#${pr.number})`).join("\n"); From 89d7aa63134b9a20651c66ad83fa1f6a217e5f46 Mon Sep 17 00:00:00 2001 From: Alex Date: Tue, 24 Feb 2026 09:25:41 -0800 Subject: [PATCH 02/13] add CLAUDE.md as a reference to AGENTS.md --- CLAUDE.md | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 CLAUDE.md diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 0000000..c692215 --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1,3 @@ +# CLAUDE.md + +see @AGENTS.md From f953a7ae5490b72fdc129857f6ed244618d3fb11 Mon Sep 17 00:00:00 2001 From: Alex Date: Wed, 25 Feb 2026 04:01:40 -0800 Subject: [PATCH 03/13] update README with usage docs and conflict resolution config Co-Authored-By: Claude Opus 4.5 --- README.md | 109 +++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 108 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 4bbcf65..54d9dad 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,111 @@ # pr-integration-action -GitHub action that integrates approved PRs into specified branch +GitHub Action that automatically integrates approved Pull Requests into a staging branch via squash merges. +## How it works + +1. Fetches open PRs with the specified approval label +2. Resets integration (stage) branch to master/main +3. Squash-merges each PR in creation order +4. Auto-resolves known merge conflicts (configurable) +5. Pushes integration branch and reflects integration status with PR labels + +## Inputs + +| Input | Required | Default | Description | +|----------------------------|----------|----------------------|--------------------------------------------------| +| `repository` | no | `github.repository` | Repository name with owner (e.g. `my-org/repo`) | +| `github_token` | no | `github.token` | GitHub token for API access | +| `token_with_workflow_scope`| no | — | PAT with workflow scope for workflow file changes| +| `master_branch` | no | `master` | Name of the main branch | +| `integration_branch` | no | `stage` | Target branch for integrated PRs | +| `approve_label` | no | `Approved` | Label that marks PRs ready for integration | +| `integrated_label` | no | `Integrated` | Label applied to successfully integrated PRs | + +## Outputs + +| Output | Description | +|---------------|------------------------------------------------------| +| `haveUpdates` | `"yes"` if PRs were integrated, `"no"` otherwise | + +## Usage + +```yaml +name: Stage deployment of approved PR + +on: + # trigger when master branch modified + push: + branches: [ master ] + # trigger on new PRs or when label set/reset from PR + pull_request: + branches: [ master ] + types: + - labeled + - unlabeled + - synchronize + - reopened + # manual trigger + workflow_dispatch: + +jobs: + integrate: + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v3 + with: + fetch-depth: 0 + + - name: Integrate PRs + uses: boldtrail/pr-integration-action@v3 + with: + github_token: ${{ secrets.GITHUB_TOKEN }} + token_with_workflow_scope: ${{ secrets.GHUB_TOKEN_WITH_WORKFLOW_SCOPE }} + # optional params below + master_branch: main + integration_branch: stage + approve_label: Approved + integrated_label: Integrated + + - name: Deploy + if: contains(steps.integration.outputs.haveUpdates, 'yes') + run: echo Deploy 'stage' branch to stage environment +``` + +## Conflict Resolution + +By default, the action auto-resolves conflicts in `version.rb`, `db/schema.rb`, and `package.json`. + +To customize, create `.github/conflict-resolution.yml` in your repository: + +```yaml +"version.rb": + resolve: file + side: theirs + +"db/schema.rb": + resolve: file + side: theirs + +"package.json": + resolve: lines + side: theirs + ignore: + - '"version"\s*:' + +"config/*.yml": + resolve: file + side: ours +``` + +| Field | Required | Values | Description | +|-----------|-----------------------|---------------------|------------------------------------| +| `resolve` | yes | `file` / `lines` | Resolution strategy | +| `side` | no | `theirs` / `ours` | Which side wins (default: `theirs`)| +| `ignore` | when `resolve: lines` | array of regex | Patterns for auto-resolvable lines | + +- **`resolve: file`** — accepts entire file from chosen side +- **`resolve: lines`** — resolves only if all conflicting lines match `ignore` patterns + +PRs with unresolvable conflicts are skipped, and not labeled with Integrated label From 31dcc083d91bd6de6a4d21a96a23b3f2d432fff7 Mon Sep 17 00:00:00 2001 From: Alex Date: Wed, 25 Feb 2026 04:08:56 -0800 Subject: [PATCH 04/13] rename 'resolve' config parameter to 'scope' Co-Authored-By: Claude Opus 4.5 --- AGENTS.md | 18 ++++++------ README.md | 22 +++++++------- specs/configurable-conflict-resolution.md | 36 +++++++++++------------ src/conflict-resolution.js | 24 +++++++-------- 4 files changed, 50 insertions(+), 50 deletions(-) diff --git a/AGENTS.md b/AGENTS.md index cc934c0..90d648f 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -33,27 +33,27 @@ A GitHub Action that automatically integrates approved Pull Requests into a stag ```yaml "version.rb": - resolve: file # file-level: checkout entire file + scope: file # file-level: checkout entire file side: theirs # theirs (default) or ours "package.json": - resolve: lines # line-level: resolve hunks matching ignore patterns + scope: lines # line-level: resolve hunks matching ignore patterns side: theirs - ignore: # required for resolve: lines + ignore: # required for scope: lines - '"version"\s*:' # regex patterns; all non-blank lines must match "db/*.rb": # glob patterns supported - resolve: file + scope: file side: theirs ``` Built-in defaults (when no config file exists): -| Pattern | Resolve | Side | Ignore | -|----------------|---------|--------|------------------| -| `version.rb` | file | theirs | — | -| `db/schema.rb` | file | theirs | — | -| `package.json` | lines | theirs | `"version"\s*:` | +| Pattern | Scope | Side | Ignore | +|----------------|-------|--------|-----------------| +| `version.rb` | file | theirs | — | +| `db/schema.rb` | file | theirs | — | +| `package.json` | lines | theirs | `"version"\s*:` | ## Action Configuration diff --git a/README.md b/README.md index 54d9dad..0093e54 100644 --- a/README.md +++ b/README.md @@ -81,31 +81,31 @@ To customize, create `.github/conflict-resolution.yml` in your repository: ```yaml "version.rb": - resolve: file + scope: file side: theirs "db/schema.rb": - resolve: file + scope: file side: theirs "package.json": - resolve: lines + scope: lines side: theirs ignore: - '"version"\s*:' "config/*.yml": - resolve: file + scope: file side: ours ``` -| Field | Required | Values | Description | -|-----------|-----------------------|---------------------|------------------------------------| -| `resolve` | yes | `file` / `lines` | Resolution strategy | -| `side` | no | `theirs` / `ours` | Which side wins (default: `theirs`)| -| `ignore` | when `resolve: lines` | array of regex | Patterns for auto-resolvable lines | +| Field | Required | Values | Description | +|----------|---------------------|-------------------|------------------------------------| +| `scope` | yes | `file` / `lines` | Resolution strategy | +| `side` | no | `theirs` / `ours` | Which side wins (default: `theirs`)| +| `ignore` | when `scope: lines` | array of regex | Patterns for auto-resolvable lines | -- **`resolve: file`** — accepts entire file from chosen side -- **`resolve: lines`** — resolves only if all conflicting lines match `ignore` patterns +- **`scope: file`** — accepts entire file from chosen side +- **`scope: lines`** — resolves only if all conflicting lines match `ignore` patterns PRs with unresolvable conflicts are skipped, and not labeled with Integrated label diff --git a/specs/configurable-conflict-resolution.md b/specs/configurable-conflict-resolution.md index 973efe1..89f5def 100644 --- a/specs/configurable-conflict-resolution.md +++ b/specs/configurable-conflict-resolution.md @@ -14,18 +14,18 @@ Loaded at runtime after cloning. If absent, built-in defaults apply. If present ```yaml "version.rb": - resolve: file + scope: file side: theirs "db/schema.rb": - resolve: file + scope: file "db/*.rb": - resolve: file + scope: file side: ours "package.json": - resolve: lines + scope: lines side: theirs ignore: - '"version"\s*:' @@ -34,19 +34,19 @@ Loaded at runtime after cloning. If absent, built-in defaults apply. If present **Every entry has the same structure:** -| Field | Required | Description | -|-----------------|-----------------------|---------------------------------------------------------| -| key (top-level) | yes | Exact file path or glob pattern | -| `resolve` | yes | `"file"` or `"lines"` | -| `side` | no | `"theirs"` or `"ours"`. Default: `"theirs"` | -| `ignore` | when `resolve: lines` | Array of regex patterns matching line content to ignore | +| Field | Required | Description | +|-----------------|---------------------|---------------------------------------------------------| +| key (top-level) | yes | Exact file path or glob pattern | +| `scope` | yes | `"file"` or `"lines"` | +| `side` | no | `"theirs"` or `"ours"`. Default: `"theirs"` | +| `ignore` | when `scope: lines` | Array of regex patterns matching line content to ignore | ## Resolution Logic -**File-level (`resolve: file`):** +**File-level (`scope: file`):** Runs `git checkout --{side} -- ` then `git add `. Same as current behavior. -**Line-level (`resolve: lines`):** +**Line-level (`scope: lines`):** 1. Parse git conflict markers (`<<<<<<<`, `=======`, `>>>>>>>`) in the conflicted file 2. For each conflict hunk, check if **all** changed lines (both sides) match at least one `ignore` regex 3. Matching hunks: resolve by accepting the file-wide `side` @@ -58,11 +58,11 @@ Runs `git checkout --{side} -- ` then `git add `. Same as current be When **no config file** exists: -| File | Resolve | Side | Ignore | -|----------------|---------|--------|-----------------| -| `version.rb` | file | theirs | — | -| `db/schema.rb` | file | theirs | — | -| `package.json` | lines | theirs | `"version"\s*:` | +| File | Scope | Side | Ignore | +|----------------|-------|--------|-----------------| +| `version.rb` | file | theirs | — | +| `db/schema.rb` | file | theirs | — | +| `package.json` | lines | theirs | `"version"\s*:` | When a config file **exists**, it **fully overrides** these defaults. @@ -76,7 +76,7 @@ When a conflicted file matches multiple rules (e.g. exact path and a glob), the |------------------------------------------------|--------------------------------------| | Config file missing | Use built-in defaults | | YAML syntax error | Fail action immediately | -| Invalid rule (bad `resolve`/`side`/`ignore`) | Fail action immediately | +| Invalid rule (bad `scope`/`side`/`ignore`) | Fail action immediately | | Rule references non-existent file | Not an error | | Line-level hunk has no matching ignore pattern | File resolution fails, PR is skipped | diff --git a/src/conflict-resolution.js b/src/conflict-resolution.js index 3c6e606..93b0cde 100644 --- a/src/conflict-resolution.js +++ b/src/conflict-resolution.js @@ -8,9 +8,9 @@ import path from 'path'; const CONFIG_PATH = '.github/conflict-resolution.yml'; const BUILT_IN_RULES = [ - { pattern: 'version.rb', resolve: 'file', side: 'theirs', ignore: null }, - { pattern: 'db/schema.rb', resolve: 'file', side: 'theirs', ignore: null }, - { pattern: 'package.json', resolve: 'lines', side: 'theirs', ignore: [/"version"\s*:/] }, + { pattern: 'version.rb', scope: 'file', side: 'theirs', ignore: null }, + { pattern: 'db/schema.rb', scope: 'file', side: 'theirs', ignore: null }, + { pattern: 'package.json', scope: 'lines', side: 'theirs', ignore: [/"version"\s*:/] }, ]; @@ -28,13 +28,13 @@ export async function createConflictResolver(repoDir, git) { return false; } - if (rule.resolve === 'file') { + if (rule.scope === 'file') { core.info(` resolve with '${rule.side}' ${filePath}`); await git.checkoutConflictedFile(repoDir, filePath, rule.side); return true; } - // resolve === 'lines' + // scope === 'lines' return await resolveLineLevel(repoDir, filePath, rule, git); } }; @@ -62,7 +62,7 @@ async function loadConfig(repoDir) { const rules = []; for (const [pattern, rule] of Object.entries(config)) { if (!rule || typeof rule !== 'object') { - throw new Error(`Rule '${pattern}': must be a YAML mapping with at least a 'resolve' key`); + throw new Error(`Rule '${pattern}': must be a YAML mapping with at least a 'scope' key`); } rules.push(validateRule(pattern, rule)); } @@ -110,9 +110,9 @@ function validateRule(pattern, rule) { throw new Error(`Invalid pattern: must be a non-empty string, got '${pattern}'`); } - const resolve = rule.resolve; - if (resolve !== 'file' && resolve !== 'lines') { - throw new Error(`Rule '${pattern}': 'resolve' must be "file" or "lines", got "${resolve}"`); + const scope = rule.scope; + if (scope !== 'file' && scope !== 'lines') { + throw new Error(`Rule '${pattern}': 'scope' must be "file" or "lines", got "${scope}"`); } const side = rule.side || 'theirs'; @@ -121,9 +121,9 @@ function validateRule(pattern, rule) { } let compiledIgnore = null; - if (resolve === 'lines') { + if (scope === 'lines') { if (!Array.isArray(rule.ignore) || rule.ignore.length === 0) { - throw new Error(`Rule '${pattern}': 'resolve: lines' requires a non-empty 'ignore' array of regex patterns`); + throw new Error(`Rule '${pattern}': 'scope: lines' requires a non-empty 'ignore' array of regex patterns`); } compiledIgnore = rule.ignore.map(pat => { try { @@ -134,7 +134,7 @@ function validateRule(pattern, rule) { }); } - return { pattern, resolve, side, ignore: compiledIgnore }; + return { pattern, scope, side, ignore: compiledIgnore }; } From 25c89381be328c9a2cd068e9e0d87966c3627888 Mon Sep 17 00:00:00 2001 From: Alex Date: Wed, 25 Feb 2026 04:09:43 -0800 Subject: [PATCH 05/13] reference future version in readme --- README.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 0093e54..33ed3d5 100644 --- a/README.md +++ b/README.md @@ -24,9 +24,9 @@ GitHub Action that automatically integrates approved Pull Requests into a stagin ## Outputs -| Output | Description | -|---------------|------------------------------------------------------| -| `haveUpdates` | `"yes"` if PRs were integrated, `"no"` otherwise | +| Output | Description | +|---------------|----------------------------------------------------------| +| `haveUpdates` | `"yes"` if any PRs were integrated, `"no"` otherwise | ## Usage @@ -58,7 +58,7 @@ jobs: fetch-depth: 0 - name: Integrate PRs - uses: boldtrail/pr-integration-action@v3 + uses: boldtrail/pr-integration-action@v4 with: github_token: ${{ secrets.GITHUB_TOKEN }} token_with_workflow_scope: ${{ secrets.GHUB_TOKEN_WITH_WORKFLOW_SCOPE }} From d6a74d53119495c8982c0b5ca6ac8561849e9d80 Mon Sep 17 00:00:00 2001 From: Alex Date: Wed, 25 Feb 2026 04:44:05 -0800 Subject: [PATCH 06/13] add integration tests for conflict resolution Co-Authored-By: Claude Opus 4.5 --- package.json | 9 +- test/conflict-resolution.test.js | 200 +++++++++++++++++++++++++++ test/fixtures/invalid-regex.yml | 5 + test/fixtures/invalid-scope.yml | 3 + test/fixtures/valid-config.yml | 17 +++ test/fixtures/version-and-other.json | 10 ++ test/fixtures/version-only.json | 9 ++ 7 files changed, 251 insertions(+), 2 deletions(-) create mode 100644 test/conflict-resolution.test.js create mode 100644 test/fixtures/invalid-regex.yml create mode 100644 test/fixtures/invalid-scope.yml create mode 100644 test/fixtures/valid-config.yml create mode 100644 test/fixtures/version-and-other.json create mode 100644 test/fixtures/version-only.json diff --git a/package.json b/package.json index 9da203f..dbb85b7 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "pr-integration-action", - "version": "3.0.3", + "version": "4.0.4", "description": "GitHub action that integrates approved PRs into specified branch", "author": "Brokermint", "license": "MIT", @@ -8,7 +8,12 @@ "type": "module", "scripts": { - "start": "node src/main.js" + "start": "node src/main.js", + "test": "vitest run" + }, + + "devDependencies": { + "vitest": "^1.6.0" }, "dependencies": { diff --git a/test/conflict-resolution.test.js b/test/conflict-resolution.test.js new file mode 100644 index 0000000..f1b03c1 --- /dev/null +++ b/test/conflict-resolution.test.js @@ -0,0 +1,200 @@ +import { describe, it, expect, beforeEach, vi } from 'vitest'; +import { createConflictResolver } from '../src/conflict-resolution.js'; +import fse from 'fs-extra'; +import path from 'path'; +import { fileURLToPath } from 'url'; + +const __dirname = path.dirname(fileURLToPath(import.meta.url)); +const FIXTURES_DIR = path.join(__dirname, 'fixtures'); + + +// Mock @actions/core to suppress log output during tests +vi.mock('@actions/core', () => ({ + info: vi.fn(), +})); + + +function createMockGit() { + return { + checkoutConflictedFile: vi.fn().mockResolvedValue(undefined), + addFile: vi.fn().mockResolvedValue(undefined), + }; +} + + +async function setupTempRepo(configFixture = null) { + const tmpDir = path.join(__dirname, '.tmp-test-' + Date.now()); + await fse.ensureDir(tmpDir); + + if (configFixture) { + const configDir = path.join(tmpDir, '.github'); + await fse.ensureDir(configDir); + await fse.copy( + path.join(FIXTURES_DIR, configFixture), + path.join(configDir, 'conflict-resolution.yml') + ); + } + + return tmpDir; +} + + +async function cleanupTempRepo(tmpDir) { + await fse.remove(tmpDir); +} + + +describe('conflict-resolution', () => { + + describe('config loading', () => { + + it('uses built-in defaults when no config file exists', async () => { + const tmpDir = await setupTempRepo(null); + const git = createMockGit(); + + try { + const resolver = await createConflictResolver(tmpDir, git); + const result = await resolver.resolve('version.rb'); + + expect(result).toBe(true); + expect(git.checkoutConflictedFile).toHaveBeenCalledWith(tmpDir, 'version.rb', 'theirs'); + } finally { + await cleanupTempRepo(tmpDir); + } + }); + + it('throws error for invalid scope value', async () => { + const tmpDir = await setupTempRepo('invalid-scope.yml'); + + try { + await expect(createConflictResolver(tmpDir, createMockGit())) + .rejects.toThrow(/'scope' must be "file" or "lines"/); + } finally { + await cleanupTempRepo(tmpDir); + } + }); + + it('throws error for invalid regex pattern', async () => { + const tmpDir = await setupTempRepo('invalid-regex.yml'); + + try { + await expect(createConflictResolver(tmpDir, createMockGit())) + .rejects.toThrow(/invalid regex/); + } finally { + await cleanupTempRepo(tmpDir); + } + }); + + }); + + + describe('file-level resolution', () => { + + it('resolves file with "theirs" side', async () => { + const tmpDir = await setupTempRepo('valid-config.yml'); + const git = createMockGit(); + + try { + const resolver = await createConflictResolver(tmpDir, git); + const result = await resolver.resolve('db/schema.rb'); + + expect(result).toBe(true); + expect(git.checkoutConflictedFile).toHaveBeenCalledWith(tmpDir, 'db/schema.rb', 'theirs'); + } finally { + await cleanupTempRepo(tmpDir); + } + }); + + it('matches glob patterns and uses correct side', async () => { + const tmpDir = await setupTempRepo('valid-config.yml'); + const git = createMockGit(); + + try { + const resolver = await createConflictResolver(tmpDir, git); + const result = await resolver.resolve('db/migrate.rb'); + + expect(result).toBe(true); + expect(git.checkoutConflictedFile).toHaveBeenCalledWith(tmpDir, 'db/migrate.rb', 'ours'); + } finally { + await cleanupTempRepo(tmpDir); + } + }); + + }); + + + describe('line-level resolution', () => { + + it('resolves conflict when all lines match ignore patterns', async () => { + const tmpDir = await setupTempRepo('valid-config.yml'); + const git = createMockGit(); + + // Copy fixture to temp repo + const conflictContent = await fse.readFile( + path.join(FIXTURES_DIR, 'version-only.json'), + 'utf8' + ); + await fse.writeFile(path.join(tmpDir, 'package.json'), conflictContent); + + try { + const resolver = await createConflictResolver(tmpDir, git); + const result = await resolver.resolve('package.json'); + + expect(result).toBe(true); + expect(git.addFile).toHaveBeenCalledWith(tmpDir, 'package.json'); + + // Verify the resolved content + const resolved = await fse.readFile(path.join(tmpDir, 'package.json'), 'utf8'); + expect(resolved).toContain('"version": "2.0.0"'); + expect(resolved).not.toContain('<<<<<<<'); + expect(resolved).not.toContain('>>>>>>>'); + } finally { + await cleanupTempRepo(tmpDir); + } + }); + + it('returns false when conflict contains non-ignorable changes', async () => { + const tmpDir = await setupTempRepo('valid-config.yml'); + const git = createMockGit(); + + // Copy fixture with non-ignorable changes + const conflictContent = await fse.readFile( + path.join(FIXTURES_DIR, 'version-and-other.json'), + 'utf8' + ); + await fse.writeFile(path.join(tmpDir, 'package.json'), conflictContent); + + try { + const resolver = await createConflictResolver(tmpDir, git); + const result = await resolver.resolve('package.json'); + + expect(result).toBe(false); + expect(git.addFile).not.toHaveBeenCalled(); + } finally { + await cleanupTempRepo(tmpDir); + } + }); + + }); + + + describe('no matching rule', () => { + + it('returns false for files with no matching rule', async () => { + const tmpDir = await setupTempRepo('valid-config.yml'); + const git = createMockGit(); + + try { + const resolver = await createConflictResolver(tmpDir, git); + const result = await resolver.resolve('unknown.txt'); + + expect(result).toBe(false); + expect(git.checkoutConflictedFile).not.toHaveBeenCalled(); + } finally { + await cleanupTempRepo(tmpDir); + } + }); + + }); + +}); diff --git a/test/fixtures/invalid-regex.yml b/test/fixtures/invalid-regex.yml new file mode 100644 index 0000000..b566355 --- /dev/null +++ b/test/fixtures/invalid-regex.yml @@ -0,0 +1,5 @@ +"package.json": + scope: lines + side: theirs + ignore: + - '[invalid(' diff --git a/test/fixtures/invalid-scope.yml b/test/fixtures/invalid-scope.yml new file mode 100644 index 0000000..42f2ef8 --- /dev/null +++ b/test/fixtures/invalid-scope.yml @@ -0,0 +1,3 @@ +"some-file.txt": + scope: invalid + side: theirs diff --git a/test/fixtures/valid-config.yml b/test/fixtures/valid-config.yml new file mode 100644 index 0000000..3c3783f --- /dev/null +++ b/test/fixtures/valid-config.yml @@ -0,0 +1,17 @@ +"version.rb": + scope: file + side: theirs + +"db/schema.rb": + scope: file + side: theirs + +"package.json": + scope: lines + side: theirs + ignore: + - '"version"\s*:' + +"db/*.rb": + scope: file + side: ours diff --git a/test/fixtures/version-and-other.json b/test/fixtures/version-and-other.json new file mode 100644 index 0000000..df99034 --- /dev/null +++ b/test/fixtures/version-and-other.json @@ -0,0 +1,10 @@ +{ + "name": "test-package", +<<<<<<< HEAD + "version": "1.0.0", + "description": "Original description" +======= + "version": "2.0.0", + "description": "Changed description" +>>>>>>> branch +} diff --git a/test/fixtures/version-only.json b/test/fixtures/version-only.json new file mode 100644 index 0000000..6b7a109 --- /dev/null +++ b/test/fixtures/version-only.json @@ -0,0 +1,9 @@ +{ + "name": "test-package", +<<<<<<< HEAD + "version": "1.0.0", +======= + "version": "2.0.0", +>>>>>>> branch + "description": "A test package" +} From 82d4b32d58a96c9d49bb4fd34ef9b964f196d905 Mon Sep 17 00:00:00 2001 From: Alex Date: Wed, 25 Feb 2026 04:54:17 -0800 Subject: [PATCH 07/13] rename 'ignore' config parameter to 'line_patterns' Co-Authored-By: Claude Opus 4.5 --- AGENTS.md | 8 ++++---- README.md | 14 ++++++------- specs/configurable-conflict-resolution.md | 22 ++++++++++----------- src/conflict-resolution.js | 24 +++++++++++------------ test/conflict-resolution.test.js | 2 +- test/fixtures/invalid-regex.yml | 2 +- test/fixtures/valid-config.yml | 2 +- 7 files changed, 37 insertions(+), 37 deletions(-) diff --git a/AGENTS.md b/AGENTS.md index 90d648f..5139ce9 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -17,7 +17,7 @@ A GitHub Action that automatically integrates approved Pull Requests into a stag - **main.js** — Reads GitHub Action inputs, creates Octokit client, calls `integrationMerge()`, sets `haveUpdates` output - **integration-merge.js** — Core orchestration: fetches open PRs (max 25, oldest first), filters by approve label, clones repo into temp dir, resets integration branch to master, squash-merges each PR, delegates conflict resolution to `conflict-resolution.js`, writes integration data, pushes, and updates labels -- **conflict-resolution.js** — Configurable merge conflict resolver. Loads rules from `.github/conflict-resolution.yml` or uses built-in defaults. Supports file-level resolution (checkout theirs/ours) and line-level resolution (auto-resolve hunks matching ignore patterns) +- **conflict-resolution.js** — Configurable merge conflict resolver. Loads rules from `.github/conflict-resolution.yml` or uses built-in defaults. Supports file-level resolution (checkout theirs/ours) and line-level resolution (auto-resolve hunks matching line_patterns) - **git.js** — Thin wrapper spawning `git` subprocesses. Exports functions for clone, fetch PR, branch management, squash merge, push (force-with-lease), conflict listing/resolution, etc. Defines custom `ExitError` class - **common.js** — `tmpdir(callback)` utility that creates a temp directory, runs the callback, then cleans up @@ -37,9 +37,9 @@ A GitHub Action that automatically integrates approved Pull Requests into a stag side: theirs # theirs (default) or ours "package.json": - scope: lines # line-level: resolve hunks matching ignore patterns + scope: lines # line-level: resolve hunks matching line_patterns side: theirs - ignore: # required for scope: lines + line_patterns: # required for scope: lines - '"version"\s*:' # regex patterns; all non-blank lines must match "db/*.rb": # glob patterns supported @@ -49,7 +49,7 @@ A GitHub Action that automatically integrates approved Pull Requests into a stag Built-in defaults (when no config file exists): -| Pattern | Scope | Side | Ignore | +| Pattern | Scope | Side | Line Patterns | |----------------|-------|--------|-----------------| | `version.rb` | file | theirs | — | | `db/schema.rb` | file | theirs | — | diff --git a/README.md b/README.md index 33ed3d5..98a4061 100644 --- a/README.md +++ b/README.md @@ -91,7 +91,7 @@ To customize, create `.github/conflict-resolution.yml` in your repository: "package.json": scope: lines side: theirs - ignore: + line_patterns: - '"version"\s*:' "config/*.yml": @@ -99,13 +99,13 @@ To customize, create `.github/conflict-resolution.yml` in your repository: side: ours ``` -| Field | Required | Values | Description | -|----------|---------------------|-------------------|------------------------------------| -| `scope` | yes | `file` / `lines` | Resolution strategy | -| `side` | no | `theirs` / `ours` | Which side wins (default: `theirs`)| -| `ignore` | when `scope: lines` | array of regex | Patterns for auto-resolvable lines | +| Field | Required | Values | Description | +|-----------------|---------------------|-------------------|------------------------------------| +| `scope` | yes | `file` / `lines` | Resolution strategy | +| `side` | no | `theirs` / `ours` | Which side wins (default: `theirs`)| +| `line_patterns` | when `scope: lines` | array of regex | Patterns for auto-resolvable lines | - **`scope: file`** — accepts entire file from chosen side -- **`scope: lines`** — resolves only if all conflicting lines match `ignore` patterns +- **`scope: lines`** — resolves only if all conflicting lines match `line_patterns` PRs with unresolvable conflicts are skipped, and not labeled with Integrated label diff --git a/specs/configurable-conflict-resolution.md b/specs/configurable-conflict-resolution.md index 89f5def..4bdaa1c 100644 --- a/specs/configurable-conflict-resolution.md +++ b/specs/configurable-conflict-resolution.md @@ -27,19 +27,19 @@ Loaded at runtime after cloning. If absent, built-in defaults apply. If present "package.json": scope: lines side: theirs - ignore: + line_patterns: - '"version"\s*:' - '"someField"\s*:' ``` **Every entry has the same structure:** -| Field | Required | Description | -|-----------------|---------------------|---------------------------------------------------------| -| key (top-level) | yes | Exact file path or glob pattern | -| `scope` | yes | `"file"` or `"lines"` | -| `side` | no | `"theirs"` or `"ours"`. Default: `"theirs"` | -| `ignore` | when `scope: lines` | Array of regex patterns matching line content to ignore | +| Field | Required | Description | +|-----------------|---------------------|-----------------------------------------------------------| +| key (top-level) | yes | Exact file path or glob pattern | +| `scope` | yes | `"file"` or `"lines"` | +| `side` | no | `"theirs"` or `"ours"`. Default: `"theirs"` | +| `line_patterns` | when `scope: lines` | Array of regex patterns matching auto-resolvable lines | ## Resolution Logic @@ -48,7 +48,7 @@ Runs `git checkout --{side} -- ` then `git add `. Same as current be **Line-level (`scope: lines`):** 1. Parse git conflict markers (`<<<<<<<`, `=======`, `>>>>>>>`) in the conflicted file -2. For each conflict hunk, check if **all** changed lines (both sides) match at least one `ignore` regex +2. For each conflict hunk, check if **all** changed lines (both sides) match at least one `line_patterns` regex 3. Matching hunks: resolve by accepting the file-wide `side` 4. Non-matching hunks: resolution fails for this file 5. All hunks resolved → write merged file and stage it @@ -58,7 +58,7 @@ Runs `git checkout --{side} -- ` then `git add `. Same as current be When **no config file** exists: -| File | Scope | Side | Ignore | +| File | Scope | Side | Line Patterns | |----------------|-------|--------|-----------------| | `version.rb` | file | theirs | — | | `db/schema.rb` | file | theirs | — | @@ -76,9 +76,9 @@ When a conflicted file matches multiple rules (e.g. exact path and a glob), the |------------------------------------------------|--------------------------------------| | Config file missing | Use built-in defaults | | YAML syntax error | Fail action immediately | -| Invalid rule (bad `scope`/`side`/`ignore`) | Fail action immediately | +| Invalid rule (bad `scope`/`side`/`line_patterns`) | Fail action immediately | | Rule references non-existent file | Not an error | -| Line-level hunk has no matching ignore pattern | File resolution fails, PR is skipped | +| Line-level hunk has no matching line_patterns | File resolution fails, PR is skipped | ## Files Changed diff --git a/src/conflict-resolution.js b/src/conflict-resolution.js index 93b0cde..78b33ce 100644 --- a/src/conflict-resolution.js +++ b/src/conflict-resolution.js @@ -8,9 +8,9 @@ import path from 'path'; const CONFIG_PATH = '.github/conflict-resolution.yml'; const BUILT_IN_RULES = [ - { pattern: 'version.rb', scope: 'file', side: 'theirs', ignore: null }, - { pattern: 'db/schema.rb', scope: 'file', side: 'theirs', ignore: null }, - { pattern: 'package.json', scope: 'lines', side: 'theirs', ignore: [/"version"\s*:/] }, + { pattern: 'version.rb', scope: 'file', side: 'theirs', linePatterns: null }, + { pattern: 'db/schema.rb', scope: 'file', side: 'theirs', linePatterns: null }, + { pattern: 'package.json', scope: 'lines', side: 'theirs', linePatterns: [/"version"\s*:/] }, ]; @@ -89,8 +89,8 @@ async function resolveLineLevel(repoDir, filePath, rule, git) { } for (const hunk of hunks) { - if (!isHunkIgnorable(hunk, rule.ignore)) { - core.info(` ${filePath}: conflict contains non-ignorable changes`); + if (!isHunkResolvable(hunk, rule.linePatterns)) { + core.info(` ${filePath}: conflict contains lines not matching line_patterns`); return false; } } @@ -120,12 +120,12 @@ function validateRule(pattern, rule) { throw new Error(`Rule '${pattern}': 'side' must be "theirs" or "ours", got "${side}"`); } - let compiledIgnore = null; + let compiledLinePatterns = null; if (scope === 'lines') { - if (!Array.isArray(rule.ignore) || rule.ignore.length === 0) { - throw new Error(`Rule '${pattern}': 'scope: lines' requires a non-empty 'ignore' array of regex patterns`); + if (!Array.isArray(rule.line_patterns) || rule.line_patterns.length === 0) { + throw new Error(`Rule '${pattern}': 'scope: lines' requires a non-empty 'line_patterns' array of regex patterns`); } - compiledIgnore = rule.ignore.map(pat => { + compiledLinePatterns = rule.line_patterns.map(pat => { try { return new RegExp(pat); } catch (e) { @@ -134,7 +134,7 @@ function validateRule(pattern, rule) { }); } - return { pattern, scope, side, ignore: compiledIgnore }; + return { pattern, scope, side, linePatterns: compiledLinePatterns }; } @@ -192,13 +192,13 @@ function parseConflictHunks(lines) { } -function isHunkIgnorable(hunk, ignorePatterns) { +function isHunkResolvable(hunk, linePatterns) { const allLines = [...hunk.oursLines, ...hunk.theirsLines]; for (const line of allLines) { if (line.trim().length === 0) { continue; } - const matchesAny = ignorePatterns.some(re => re.test(line)); + const matchesAny = linePatterns.some(re => re.test(line)); if (!matchesAny) { return false; } diff --git a/test/conflict-resolution.test.js b/test/conflict-resolution.test.js index f1b03c1..d3545a2 100644 --- a/test/conflict-resolution.test.js +++ b/test/conflict-resolution.test.js @@ -125,7 +125,7 @@ describe('conflict-resolution', () => { describe('line-level resolution', () => { - it('resolves conflict when all lines match ignore patterns', async () => { + it('resolves conflict when all lines match line_patterns', async () => { const tmpDir = await setupTempRepo('valid-config.yml'); const git = createMockGit(); diff --git a/test/fixtures/invalid-regex.yml b/test/fixtures/invalid-regex.yml index b566355..554ba69 100644 --- a/test/fixtures/invalid-regex.yml +++ b/test/fixtures/invalid-regex.yml @@ -1,5 +1,5 @@ "package.json": scope: lines side: theirs - ignore: + line_patterns: - '[invalid(' diff --git a/test/fixtures/valid-config.yml b/test/fixtures/valid-config.yml index 3c3783f..0975a3b 100644 --- a/test/fixtures/valid-config.yml +++ b/test/fixtures/valid-config.yml @@ -9,7 +9,7 @@ "package.json": scope: lines side: theirs - ignore: + line_patterns: - '"version"\s*:' "db/*.rb": From 5f0dc74ad7158c1df3a65396b9c43354544b18de Mon Sep 17 00:00:00 2001 From: Alex Date: Wed, 25 Feb 2026 05:09:01 -0800 Subject: [PATCH 08/13] add blank line to test fixture for whitespace handling coverage Co-Authored-By: Claude Opus 4.5 --- test/fixtures/version-only.json | 1 + 1 file changed, 1 insertion(+) diff --git a/test/fixtures/version-only.json b/test/fixtures/version-only.json index 6b7a109..ae3c9f6 100644 --- a/test/fixtures/version-only.json +++ b/test/fixtures/version-only.json @@ -4,6 +4,7 @@ "version": "1.0.0", ======= "version": "2.0.0", + >>>>>>> branch "description": "A test package" } From 9d86842e8744393d095a1684fc7c965f23a57e92 Mon Sep 17 00:00:00 2001 From: Alex Date: Wed, 25 Feb 2026 06:13:49 -0800 Subject: [PATCH 09/13] add CI workflow for testing and release tagging - runs tests on PR open/update/reopen and push to main - checks version tag doesn't already exist (fails early) - creates version tags (v4.0.4) and moves major tag (v4) on main push - writes job summary with usage examples Co-Authored-By: Claude Opus 4.5 --- .github/workflows/ci-and-release.yml | 79 ++++++++ AGENTS.md | 5 +- specs/2026-02-25 github-ci-workflow.md | 254 +++++++++++++++++++++++++ 3 files changed, 336 insertions(+), 2 deletions(-) create mode 100644 .github/workflows/ci-and-release.yml create mode 100644 specs/2026-02-25 github-ci-workflow.md diff --git a/.github/workflows/ci-and-release.yml b/.github/workflows/ci-and-release.yml new file mode 100644 index 0000000..5afed96 --- /dev/null +++ b/.github/workflows/ci-and-release.yml @@ -0,0 +1,79 @@ +name: CI and Release + +on: + pull_request: + branches: [main] + types: [opened, synchronize, reopened] + push: + branches: [main] + workflow_dispatch: + +concurrency: + group: ${{ github.workflow }}-${{ github.head_ref || github.ref }} + cancel-in-progress: true + +permissions: + contents: write + +jobs: + test-and-release: + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Check version tag does not exist + run: | + VERSION=$(node -p "require('./package.json').version") + if git ls-remote --tags origin | grep -q "refs/tags/v${VERSION}$"; then + echo "::error::Tag v${VERSION} already exists. Bump version in package.json first." + exit 1 + fi + echo "VERSION=${VERSION}" >> $GITHUB_ENV + + - name: Setup Node.js + uses: actions/setup-node@v4 + with: + node-version: '20' + cache: 'npm' + + - name: Install dependencies + run: npm ci + + - name: Run tests + run: npm test + + - name: Create version tags + if: github.event_name == 'push' && github.ref == 'refs/heads/main' + run: | + VERSION="${{ env.VERSION }}" + MAJOR=$(echo "$VERSION" | cut -d. -f1) + + git config user.name "github-actions[bot]" + git config user.email "github-actions[bot]@users.noreply.github.com" + + git tag "v${VERSION}" + git push origin --delete "v${MAJOR}" 2>/dev/null || true + git tag -f "v${MAJOR}" + git push origin "v${VERSION}" "v${MAJOR}" + + - name: Write job summary + if: github.event_name == 'push' && github.ref == 'refs/heads/main' + run: | + VERSION="${{ env.VERSION }}" + MAJOR=$(echo "$VERSION" | cut -d. -f1) + SHA="${{ github.sha }}" + + cat >> $GITHUB_STEP_SUMMARY << EOF + ## Release Summary + + | Item | Value | + |--------------|----------------------------------| + | Version | \`${VERSION}\` | + | Commit | \`${SHA:0:7}\` | + | Tags Created | \`v${VERSION}\`, \`v${MAJOR}\` | + + Users can reference this action as: + - \`uses: boldtrail/pr-integration-action@v${VERSION}\` - exact version + - \`uses: boldtrail/pr-integration-action@v${MAJOR}\` - latest v${MAJOR}.x.x + EOF diff --git a/AGENTS.md b/AGENTS.md index 5139ce9..131f090 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -6,8 +6,9 @@ A GitHub Action that automatically integrates approved Pull Requests into a stag - **Run locally:** `npm start` (runs `node src/main.js`, requires GitHub Action environment variables) - **Install dependencies:** `npm install` +- **Run tests:** `npm test` (vitest, tests in `test/`) - **Manual test:** Trigger via GitHub Actions workflow dispatch (`.github/workflows/test_workflow.yml`) -- No test suite, linter, or build step is configured. +- No linter or build step is configured. ## Architecture @@ -61,7 +62,7 @@ Inputs: `repository`, `github_token`, `token_with_workflow_scope`, `master_branc Output: `haveUpdates` — "yes" or "no" -Runtime: node16 (per action.yml) +Runtime: node20 (per action.yml) ## Formatting diff --git a/specs/2026-02-25 github-ci-workflow.md b/specs/2026-02-25 github-ci-workflow.md new file mode 100644 index 0000000..7e5d1fc --- /dev/null +++ b/specs/2026-02-25 github-ci-workflow.md @@ -0,0 +1,254 @@ +# GitHub CI Workflow Specification + +## Overview + +A CI workflow that runs tests on pull requests and creates version tags on successful merges to main. + +## File Location + +`.github/workflows/ci-and-release.yml` + +## Triggers + +| Event | Condition | Behavior | +|-------------------|------------------------------------|---------------------| +| `pull_request` | targeting `main`, types: opened, synchronize, reopened | Run tests only | +| `push` | to `main` branch | Run tests + release | +| `workflow_dispatch` | manual trigger | Run tests only | + +## Concurrency + +- Group: `${{ github.workflow }}-${{ github.head_ref || github.ref }}` +- Cancel in-progress: `true` +- Effect: New pushes to same PR/branch cancel previous runs + +## Job Structure + +Single job named `test-and-release` with conditional steps. + +## Steps + +### 1. Checkout + +```yaml +- uses: actions/checkout@v4 +``` + +### 2. Check Version Tag (all triggers) + +**Condition:** Always runs (no `if`) + +```bash +VERSION=$(node -p "require('./package.json').version") +if git ls-remote --tags origin | grep -q "refs/tags/v${VERSION}$"; then + echo "::error::Tag v${VERSION} already exists. Bump version in package.json first." + exit 1 +fi +echo "VERSION=${VERSION}" >> $GITHUB_ENV +``` + +**Purpose:** Fail early if the version tag already exists. Catches version conflicts at PR time rather than merge time. + +### 3. Setup Node.js + +```yaml +- uses: actions/setup-node@v4 + with: + node-version: '20' + cache: 'npm' +``` + +### 4. Install Dependencies + +```bash +npm ci +``` + +### 5. Run Tests + +```bash +npm test +``` + +### 6. Create Version Tags (main push only) + +**Condition:** `github.event_name == 'push' && github.ref == 'refs/heads/main'` + +```bash +VERSION="${{ env.VERSION }}" +MAJOR=$(echo "$VERSION" | cut -d. -f1) + +# Create exact version tag +git tag "v${VERSION}" + +# Force-move major version tag +git push origin --delete "v${MAJOR}" 2>/dev/null || true +git tag -f "v${MAJOR}" + +# Push both tags +git push origin "v${VERSION}" "v${MAJOR}" +``` + +**Tag Details:** +- Type: Lightweight (no annotation) +- Exact version: `v4.0.4` (new tag, fails if exists) +- Major version: `v4` (force-moved to latest release) + +### 7. Write Job Summary (main push only) + +**Condition:** `github.event_name == 'push' && github.ref == 'refs/heads/main'` + +```bash +VERSION="${{ env.VERSION }}" +MAJOR=$(echo "$VERSION" | cut -d. -f1) +SHA="${{ github.sha }}" + +cat >> $GITHUB_STEP_SUMMARY << EOF +## Release Summary + +| Item | Value | +|----------------|--------------------------------------| +| Version | \`${VERSION}\` | +| Commit | \`${SHA:0:7}\` | +| Tags Created | \`v${VERSION}\`, \`v${MAJOR}\` | + +Users can pin to: +- \`uses: boldtrail/pr-integration-action@v${VERSION}\` - exact version +- \`uses: boldtrail/pr-integration-action@v${MAJOR}\` - latest v${MAJOR}.x.x +EOF +``` + +## Permissions + +```yaml +permissions: + contents: write # Required for pushing tags +``` + +## Token + +Uses default `GITHUB_TOKEN` - no PAT required. + +## Environment Variables + +| Variable | Source | Used In | +|-----------|----------------------------------------|----------------------------| +| `VERSION` | Extracted from `package.json` | Tag creation, job summary | + +## Error Handling + +| Scenario | Behavior | +|------------------------------|-----------------------------------------------| +| Version tag already exists | Fail immediately (on any trigger, including PRs) | +| Tests fail | Job fails, no tags created | +| Tag push fails | Job fails (should not happen with proper permissions) | + +## Pre-release Versions + +Handled identically to regular releases: +- `4.0.5-beta.1` creates `v4.0.5-beta.1` and moves `v4` + +## Complete Workflow + +```yaml +name: CI and Release + +on: + pull_request: + branches: [main] + types: [opened, synchronize, reopened] + push: + branches: [main] + workflow_dispatch: + +concurrency: + group: ${{ github.workflow }}-${{ github.head_ref || github.ref }} + cancel-in-progress: true + +permissions: + contents: write + +jobs: + test-and-release: + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Check version tag does not exist + run: | + VERSION=$(node -p "require('./package.json').version") + if git ls-remote --tags origin | grep -q "refs/tags/v${VERSION}$"; then + echo "::error::Tag v${VERSION} already exists. Bump version in package.json first." + exit 1 + fi + echo "VERSION=${VERSION}" >> $GITHUB_ENV + + - name: Setup Node.js + uses: actions/setup-node@v4 + with: + node-version: '20' + cache: 'npm' + + - name: Install dependencies + run: npm ci + + - name: Run tests + run: npm test + + - name: Create version tags + if: github.event_name == 'push' && github.ref == 'refs/heads/main' + run: | + VERSION="${{ env.VERSION }}" + MAJOR=$(echo "$VERSION" | cut -d. -f1) + + git config user.name "github-actions[bot]" + git config user.email "github-actions[bot]@users.noreply.github.com" + + git tag "v${VERSION}" + git push origin --delete "v${MAJOR}" 2>/dev/null || true + git tag -f "v${MAJOR}" + git push origin "v${VERSION}" "v${MAJOR}" + + - name: Write job summary + if: github.event_name == 'push' && github.ref == 'refs/heads/main' + run: | + VERSION="${{ env.VERSION }}" + MAJOR=$(echo "$VERSION" | cut -d. -f1) + SHA="${{ github.sha }}" + + cat >> $GITHUB_STEP_SUMMARY << EOF + ## Release Summary + + | Item | Value | + |----------------|--------------------------------------| + | Version | \`${VERSION}\` | + | Commit | \`${SHA:0:7}\` | + | Tags Created | \`v${VERSION}\`, \`v${MAJOR}\` | + + Users can pin to: + - \`uses: boldtrail/pr-integration-action@v${VERSION}\` - exact version + - \`uses: boldtrail/pr-integration-action@v${MAJOR}\` - latest v${MAJOR}.x.x + EOF +``` + +## Test Matrix + +| Trigger | Tests Run | Tags Created | Version Check | +|---------------------------|-----------|--------------|---------------| +| PR opened | Yes | No | Yes | +| PR updated (new commits) | Yes | No | Yes | +| Push to main | Yes | Yes | Yes | +| Manual dispatch | Yes | No | Yes | + +## Edge Cases + +| Scenario | Expected Behavior | +|-------------------------------------------|------------------------------------------------| +| PR with same version as existing tag | Fail immediately with clear error | +| Main push with existing version tag | Fail before tests with clear error | +| Tests fail on main push | No tags created, job fails | +| Major tag `v4` doesn't exist yet | Creates both `v4.0.4` and `v4` successfully | +| Network error during tag push | Job fails, manual retry needed | +| workflow_dispatch (any branch) | Tests run, no tags created | +| Two PRs with same new version | Both pass initially; after first merges, second's CI is stale (would fail on re-run or merge) | From ec35b9091b8e59dca2ca69c4958200825d04f9d7 Mon Sep 17 00:00:00 2001 From: Alex Date: Wed, 25 Feb 2026 06:14:11 -0800 Subject: [PATCH 10/13] update to node 20 --- action.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/action.yml b/action.yml index 892ce71..bf71e71 100644 --- a/action.yml +++ b/action.yml @@ -40,5 +40,5 @@ outputs: description: Indicates whether any new or updated PRs were integrated. Values 'yes', 'no' runs: - using: 'node16' + using: 'node20' main: 'entrypoint.js' From bf7b823dc7b795146645db6370e081a6851b48cf Mon Sep 17 00:00:00 2001 From: Alex Date: Wed, 25 Feb 2026 06:18:23 -0800 Subject: [PATCH 11/13] make major tag update atomic using refspec force prefix Avoids race condition where delete succeeds but push fails, leaving major tag missing. Now uses +v${MAJOR} refspec to force-push in a single operation. Co-Authored-By: Claude Opus 4.5 --- .github/workflows/ci-and-release.yml | 3 +-- specs/2026-02-25 github-ci-workflow.md | 10 ++++------ 2 files changed, 5 insertions(+), 8 deletions(-) diff --git a/.github/workflows/ci-and-release.yml b/.github/workflows/ci-and-release.yml index 5afed96..2b8ab39 100644 --- a/.github/workflows/ci-and-release.yml +++ b/.github/workflows/ci-and-release.yml @@ -53,9 +53,8 @@ jobs: git config user.email "github-actions[bot]@users.noreply.github.com" git tag "v${VERSION}" - git push origin --delete "v${MAJOR}" 2>/dev/null || true git tag -f "v${MAJOR}" - git push origin "v${VERSION}" "v${MAJOR}" + git push origin "v${VERSION}" "+v${MAJOR}" - name: Write job summary if: github.event_name == 'push' && github.ref == 'refs/heads/main' diff --git a/specs/2026-02-25 github-ci-workflow.md b/specs/2026-02-25 github-ci-workflow.md index 7e5d1fc..4b06e7a 100644 --- a/specs/2026-02-25 github-ci-workflow.md +++ b/specs/2026-02-25 github-ci-workflow.md @@ -81,12 +81,11 @@ MAJOR=$(echo "$VERSION" | cut -d. -f1) # Create exact version tag git tag "v${VERSION}" -# Force-move major version tag -git push origin --delete "v${MAJOR}" 2>/dev/null || true +# Force-move major version tag (+ prefix forces just that ref) git tag -f "v${MAJOR}" -# Push both tags -git push origin "v${VERSION}" "v${MAJOR}" +# Push both tags atomically (+ forces v${MAJOR} only) +git push origin "v${VERSION}" "+v${MAJOR}" ``` **Tag Details:** @@ -206,9 +205,8 @@ jobs: git config user.email "github-actions[bot]@users.noreply.github.com" git tag "v${VERSION}" - git push origin --delete "v${MAJOR}" 2>/dev/null || true git tag -f "v${MAJOR}" - git push origin "v${VERSION}" "v${MAJOR}" + git push origin "v${VERSION}" "+v${MAJOR}" - name: Write job summary if: github.event_name == 'push' && github.ref == 'refs/heads/main' From 27bb31d3d744320681374f5043fcfaf6a9455101 Mon Sep 17 00:00:00 2001 From: Alex Date: Wed, 25 Feb 2026 06:42:16 -0800 Subject: [PATCH 12/13] update dependencies to latest stable versions MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - vitest: 1.6.0 → 4.0.18 - @actions/core: 1.6.0 → 3.0.0 - @actions/github: 2.2.0 → 9.0.0 - @octokit/rest: 19.0.5 → 22.0.1 - fs-extra: 10.1.0 → 11.3.3 - js-yaml: 4.1.0 → 4.1.1 - minimatch: 9.0.0 → 10.2.3 - tmp: 0.2.1 → 0.2.5 Co-Authored-By: Claude Opus 4.5 --- package-lock.json | 1846 +++++++++++++++++++++++++++++++++++++++++++++ package.json | 16 +- 2 files changed, 1854 insertions(+), 8 deletions(-) create mode 100644 package-lock.json diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 0000000..7774b8f --- /dev/null +++ b/package-lock.json @@ -0,0 +1,1846 @@ +{ + "name": "pr-integration-action", + "version": "4.0.4", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "pr-integration-action", + "version": "4.0.4", + "license": "MIT", + "dependencies": { + "@actions/core": "^3.0.0", + "@actions/github": "^9.0.0", + "@octokit/rest": "^22.0.1", + "fs-extra": "^11.3.3", + "js-yaml": "^4.1.1", + "minimatch": "^10.2.3", + "tmp": "^0.2.5" + }, + "devDependencies": { + "vitest": "^4.0.18" + } + }, + "node_modules/@actions/core": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@actions/core/-/core-3.0.0.tgz", + "integrity": "sha512-zYt6cz+ivnTmiT/ksRVriMBOiuoUpDCJJlZ5KPl2/FRdvwU3f7MPh9qftvbkXJThragzUZieit2nyHUyw53Seg==", + "license": "MIT", + "dependencies": { + "@actions/exec": "^3.0.0", + "@actions/http-client": "^4.0.0" + } + }, + "node_modules/@actions/exec": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@actions/exec/-/exec-3.0.0.tgz", + "integrity": "sha512-6xH/puSoNBXb72VPlZVm7vQ+svQpFyA96qdDBvhB8eNZOE8LtPf9L4oAsfzK/crCL8YZ+19fKYVnM63Sl+Xzlw==", + "license": "MIT", + "dependencies": { + "@actions/io": "^3.0.2" + } + }, + "node_modules/@actions/github": { + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/@actions/github/-/github-9.0.0.tgz", + "integrity": "sha512-yJ0RoswsAaKcvkmpCE4XxBRiy/whH2SdTBHWzs0gi4wkqTDhXMChjSdqBz/F4AeiDlP28rQqL33iHb+kjAMX6w==", + "license": "MIT", + "dependencies": { + "@actions/http-client": "^3.0.2", + "@octokit/core": "^7.0.6", + "@octokit/plugin-paginate-rest": "^14.0.0", + "@octokit/plugin-rest-endpoint-methods": "^17.0.0", + "@octokit/request": "^10.0.7", + "@octokit/request-error": "^7.1.0", + "undici": "^6.23.0" + } + }, + "node_modules/@actions/github/node_modules/@actions/http-client": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/@actions/http-client/-/http-client-3.0.2.tgz", + "integrity": "sha512-JP38FYYpyqvUsz+Igqlc/JG6YO9PaKuvqjM3iGvaLqFnJ7TFmcLyy2IDrY0bI0qCQug8E9K+elv5ZNfw62ZJzA==", + "license": "MIT", + "dependencies": { + "tunnel": "^0.0.6", + "undici": "^6.23.0" + } + }, + "node_modules/@actions/http-client": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@actions/http-client/-/http-client-4.0.0.tgz", + "integrity": "sha512-QuwPsgVMsD6qaPD57GLZi9sqzAZCtiJT8kVBCDpLtxhL5MydQ4gS+DrejtZZPdIYyB1e95uCK9Luyds7ybHI3g==", + "license": "MIT", + "dependencies": { + "tunnel": "^0.0.6", + "undici": "^6.23.0" + } + }, + "node_modules/@actions/io": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/@actions/io/-/io-3.0.2.tgz", + "integrity": "sha512-nRBchcMM+QK1pdjO7/idu86rbJI5YHUKCvKs0KxnSYbVe3F51UfGxuZX4Qy/fWlp6l7gWFwIkrOzN+oUK03kfw==", + "license": "MIT" + }, + "node_modules/@esbuild/aix-ppc64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.27.3.tgz", + "integrity": "sha512-9fJMTNFTWZMh5qwrBItuziu834eOCUcEqymSH7pY+zoMVEZg3gcPuBNxH1EvfVYe9h0x/Ptw8KBzv7qxb7l8dg==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.27.3.tgz", + "integrity": "sha512-i5D1hPY7GIQmXlXhs2w8AWHhenb00+GxjxRncS2ZM7YNVGNfaMxgzSGuO8o8SJzRc/oZwU2bcScvVERk03QhzA==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.27.3.tgz", + "integrity": "sha512-YdghPYUmj/FX2SYKJ0OZxf+iaKgMsKHVPF1MAq/P8WirnSpCStzKJFjOjzsW0QQ7oIAiccHdcqjbHmJxRb/dmg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-x64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.27.3.tgz", + "integrity": "sha512-IN/0BNTkHtk8lkOM8JWAYFg4ORxBkZQf9zXiEOfERX/CzxW3Vg1ewAhU7QSWQpVIzTW+b8Xy+lGzdYXV6UZObQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-arm64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.27.3.tgz", + "integrity": "sha512-Re491k7ByTVRy0t3EKWajdLIr0gz2kKKfzafkth4Q8A5n1xTHrkqZgLLjFEHVD+AXdUGgQMq+Godfq45mGpCKg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-x64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.27.3.tgz", + "integrity": "sha512-vHk/hA7/1AckjGzRqi6wbo+jaShzRowYip6rt6q7VYEDX4LEy1pZfDpdxCBnGtl+A5zq8iXDcyuxwtv3hNtHFg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-arm64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.27.3.tgz", + "integrity": "sha512-ipTYM2fjt3kQAYOvo6vcxJx3nBYAzPjgTCk7QEgZG8AUO3ydUhvelmhrbOheMnGOlaSFUoHXB6un+A7q4ygY9w==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-x64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.27.3.tgz", + "integrity": "sha512-dDk0X87T7mI6U3K9VjWtHOXqwAMJBNN2r7bejDsc+j03SEjtD9HrOl8gVFByeM0aJksoUuUVU9TBaZa2rgj0oA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.27.3.tgz", + "integrity": "sha512-s6nPv2QkSupJwLYyfS+gwdirm0ukyTFNl3KTgZEAiJDd+iHZcbTPPcWCcRYH+WlNbwChgH2QkE9NSlNrMT8Gfw==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.27.3.tgz", + "integrity": "sha512-sZOuFz/xWnZ4KH3YfFrKCf1WyPZHakVzTiqji3WDc0BCl2kBwiJLCXpzLzUBLgmp4veFZdvN5ChW4Eq/8Fc2Fg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ia32": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.27.3.tgz", + "integrity": "sha512-yGlQYjdxtLdh0a3jHjuwOrxQjOZYD/C9PfdbgJJF3TIZWnm/tMd/RcNiLngiu4iwcBAOezdnSLAwQDPqTmtTYg==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-loong64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.27.3.tgz", + "integrity": "sha512-WO60Sn8ly3gtzhyjATDgieJNet/KqsDlX5nRC5Y3oTFcS1l0KWba+SEa9Ja1GfDqSF1z6hif/SkpQJbL63cgOA==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-mips64el": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.27.3.tgz", + "integrity": "sha512-APsymYA6sGcZ4pD6k+UxbDjOFSvPWyZhjaiPyl/f79xKxwTnrn5QUnXR5prvetuaSMsb4jgeHewIDCIWljrSxw==", + "cpu": [ + "mips64el" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ppc64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.27.3.tgz", + "integrity": "sha512-eizBnTeBefojtDb9nSh4vvVQ3V9Qf9Df01PfawPcRzJH4gFSgrObw+LveUyDoKU3kxi5+9RJTCWlj4FjYXVPEA==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-riscv64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.27.3.tgz", + "integrity": "sha512-3Emwh0r5wmfm3ssTWRQSyVhbOHvqegUDRd0WhmXKX2mkHJe1SFCMJhagUleMq+Uci34wLSipf8Lagt4LlpRFWQ==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-s390x": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.27.3.tgz", + "integrity": "sha512-pBHUx9LzXWBc7MFIEEL0yD/ZVtNgLytvx60gES28GcWMqil8ElCYR4kvbV2BDqsHOvVDRrOxGySBM9Fcv744hw==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-x64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.27.3.tgz", + "integrity": "sha512-Czi8yzXUWIQYAtL/2y6vogER8pvcsOsk5cpwL4Gk5nJqH5UZiVByIY8Eorm5R13gq+DQKYg0+JyQoytLQas4dA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-arm64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.27.3.tgz", + "integrity": "sha512-sDpk0RgmTCR/5HguIZa9n9u+HVKf40fbEUt+iTzSnCaGvY9kFP0YKBWZtJaraonFnqef5SlJ8/TiPAxzyS+UoA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-x64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.27.3.tgz", + "integrity": "sha512-P14lFKJl/DdaE00LItAukUdZO5iqNH7+PjoBm+fLQjtxfcfFE20Xf5CrLsmZdq5LFFZzb5JMZ9grUwvtVYzjiA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-arm64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.27.3.tgz", + "integrity": "sha512-AIcMP77AvirGbRl/UZFTq5hjXK+2wC7qFRGoHSDrZ5v5b8DK/GYpXW3CPRL53NkvDqb9D+alBiC/dV0Fb7eJcw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-x64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.27.3.tgz", + "integrity": "sha512-DnW2sRrBzA+YnE70LKqnM3P+z8vehfJWHXECbwBmH/CU51z6FiqTQTHFenPlHmo3a8UgpLyH3PT+87OViOh1AQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openharmony-arm64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.27.3.tgz", + "integrity": "sha512-NinAEgr/etERPTsZJ7aEZQvvg/A6IsZG/LgZy+81wON2huV7SrK3e63dU0XhyZP4RKGyTm7aOgmQk0bGp0fy2g==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/sunos-x64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.27.3.tgz", + "integrity": "sha512-PanZ+nEz+eWoBJ8/f8HKxTTD172SKwdXebZ0ndd953gt1HRBbhMsaNqjTyYLGLPdoWHy4zLU7bDVJztF5f3BHA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-arm64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.27.3.tgz", + "integrity": "sha512-B2t59lWWYrbRDw/tjiWOuzSsFh1Y/E95ofKz7rIVYSQkUYBjfSgf6oeYPNWHToFRr2zx52JKApIcAS/D5TUBnA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-ia32": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.27.3.tgz", + "integrity": "sha512-QLKSFeXNS8+tHW7tZpMtjlNb7HKau0QDpwm49u0vUp9y1WOF+PEzkU84y9GqYaAVW8aH8f3GcBck26jh54cX4Q==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-x64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.27.3.tgz", + "integrity": "sha512-4uJGhsxuptu3OcpVAzli+/gWusVGwZZHTlS63hh++ehExkVT8SgiEf7/uC/PclrPPkLhZqGgCTjd0VWLo6xMqA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.5.5", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz", + "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==", + "dev": true, + "license": "MIT" + }, + "node_modules/@octokit/auth-token": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/@octokit/auth-token/-/auth-token-6.0.0.tgz", + "integrity": "sha512-P4YJBPdPSpWTQ1NU4XYdvHvXJJDxM6YwpS0FZHRgP7YFkdVxsWcpWGy/NVqlAA7PcPCnMacXlRm1y2PFZRWL/w==", + "license": "MIT", + "engines": { + "node": ">= 20" + } + }, + "node_modules/@octokit/core": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/@octokit/core/-/core-7.0.6.tgz", + "integrity": "sha512-DhGl4xMVFGVIyMwswXeyzdL4uXD5OGILGX5N8Y+f6W7LhC1Ze2poSNrkF/fedpVDHEEZ+PHFW0vL14I+mm8K3Q==", + "license": "MIT", + "dependencies": { + "@octokit/auth-token": "^6.0.0", + "@octokit/graphql": "^9.0.3", + "@octokit/request": "^10.0.6", + "@octokit/request-error": "^7.0.2", + "@octokit/types": "^16.0.0", + "before-after-hook": "^4.0.0", + "universal-user-agent": "^7.0.0" + }, + "engines": { + "node": ">= 20" + } + }, + "node_modules/@octokit/endpoint": { + "version": "11.0.3", + "resolved": "https://registry.npmjs.org/@octokit/endpoint/-/endpoint-11.0.3.tgz", + "integrity": "sha512-FWFlNxghg4HrXkD3ifYbS/IdL/mDHjh9QcsNyhQjN8dplUoZbejsdpmuqdA76nxj2xoWPs7p8uX2SNr9rYu0Ag==", + "license": "MIT", + "dependencies": { + "@octokit/types": "^16.0.0", + "universal-user-agent": "^7.0.2" + }, + "engines": { + "node": ">= 20" + } + }, + "node_modules/@octokit/graphql": { + "version": "9.0.3", + "resolved": "https://registry.npmjs.org/@octokit/graphql/-/graphql-9.0.3.tgz", + "integrity": "sha512-grAEuupr/C1rALFnXTv6ZQhFuL1D8G5y8CN04RgrO4FIPMrtm+mcZzFG7dcBm+nq+1ppNixu+Jd78aeJOYxlGA==", + "license": "MIT", + "dependencies": { + "@octokit/request": "^10.0.6", + "@octokit/types": "^16.0.0", + "universal-user-agent": "^7.0.0" + }, + "engines": { + "node": ">= 20" + } + }, + "node_modules/@octokit/openapi-types": { + "version": "27.0.0", + "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-27.0.0.tgz", + "integrity": "sha512-whrdktVs1h6gtR+09+QsNk2+FO+49j6ga1c55YZudfEG+oKJVvJLQi3zkOm5JjiUXAagWK2tI2kTGKJ2Ys7MGA==", + "license": "MIT" + }, + "node_modules/@octokit/plugin-paginate-rest": { + "version": "14.0.0", + "resolved": "https://registry.npmjs.org/@octokit/plugin-paginate-rest/-/plugin-paginate-rest-14.0.0.tgz", + "integrity": "sha512-fNVRE7ufJiAA3XUrha2omTA39M6IXIc6GIZLvlbsm8QOQCYvpq/LkMNGyFlB1d8hTDzsAXa3OKtybdMAYsV/fw==", + "license": "MIT", + "dependencies": { + "@octokit/types": "^16.0.0" + }, + "engines": { + "node": ">= 20" + }, + "peerDependencies": { + "@octokit/core": ">=6" + } + }, + "node_modules/@octokit/plugin-request-log": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/@octokit/plugin-request-log/-/plugin-request-log-6.0.0.tgz", + "integrity": "sha512-UkOzeEN3W91/eBq9sPZNQ7sUBvYCqYbrrD8gTbBuGtHEuycE4/awMXcYvx6sVYo7LypPhmQwwpUe4Yyu4QZN5Q==", + "license": "MIT", + "engines": { + "node": ">= 20" + }, + "peerDependencies": { + "@octokit/core": ">=6" + } + }, + "node_modules/@octokit/plugin-rest-endpoint-methods": { + "version": "17.0.0", + "resolved": "https://registry.npmjs.org/@octokit/plugin-rest-endpoint-methods/-/plugin-rest-endpoint-methods-17.0.0.tgz", + "integrity": "sha512-B5yCyIlOJFPqUUeiD0cnBJwWJO8lkJs5d8+ze9QDP6SvfiXSz1BF+91+0MeI1d2yxgOhU/O+CvtiZ9jSkHhFAw==", + "license": "MIT", + "dependencies": { + "@octokit/types": "^16.0.0" + }, + "engines": { + "node": ">= 20" + }, + "peerDependencies": { + "@octokit/core": ">=6" + } + }, + "node_modules/@octokit/request": { + "version": "10.0.8", + "resolved": "https://registry.npmjs.org/@octokit/request/-/request-10.0.8.tgz", + "integrity": "sha512-SJZNwY9pur9Agf7l87ywFi14W+Hd9Jg6Ifivsd33+/bGUQIjNujdFiXII2/qSlN2ybqUHfp5xpekMEjIBTjlSw==", + "license": "MIT", + "dependencies": { + "@octokit/endpoint": "^11.0.3", + "@octokit/request-error": "^7.0.2", + "@octokit/types": "^16.0.0", + "fast-content-type-parse": "^3.0.0", + "json-with-bigint": "^3.5.3", + "universal-user-agent": "^7.0.2" + }, + "engines": { + "node": ">= 20" + } + }, + "node_modules/@octokit/request-error": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/@octokit/request-error/-/request-error-7.1.0.tgz", + "integrity": "sha512-KMQIfq5sOPpkQYajXHwnhjCC0slzCNScLHs9JafXc4RAJI+9f+jNDlBNaIMTvazOPLgb4BnlhGJOTbnN0wIjPw==", + "license": "MIT", + "dependencies": { + "@octokit/types": "^16.0.0" + }, + "engines": { + "node": ">= 20" + } + }, + "node_modules/@octokit/rest": { + "version": "22.0.1", + "resolved": "https://registry.npmjs.org/@octokit/rest/-/rest-22.0.1.tgz", + "integrity": "sha512-Jzbhzl3CEexhnivb1iQ0KJ7s5vvjMWcmRtq5aUsKmKDrRW6z3r84ngmiFKFvpZjpiU/9/S6ITPFRpn5s/3uQJw==", + "license": "MIT", + "dependencies": { + "@octokit/core": "^7.0.6", + "@octokit/plugin-paginate-rest": "^14.0.0", + "@octokit/plugin-request-log": "^6.0.0", + "@octokit/plugin-rest-endpoint-methods": "^17.0.0" + }, + "engines": { + "node": ">= 20" + } + }, + "node_modules/@octokit/types": { + "version": "16.0.0", + "resolved": "https://registry.npmjs.org/@octokit/types/-/types-16.0.0.tgz", + "integrity": "sha512-sKq+9r1Mm4efXW1FCk7hFSeJo4QKreL/tTbR0rz/qx/r1Oa2VV83LTA/H/MuCOX7uCIJmQVRKBcbmWoySjAnSg==", + "license": "MIT", + "dependencies": { + "@octokit/openapi-types": "^27.0.0" + } + }, + "node_modules/@rollup/rollup-android-arm-eabi": { + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.59.0.tgz", + "integrity": "sha512-upnNBkA6ZH2VKGcBj9Fyl9IGNPULcjXRlg0LLeaioQWueH30p6IXtJEbKAgvyv+mJaMxSm1l6xwDXYjpEMiLMg==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-android-arm64": { + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.59.0.tgz", + "integrity": "sha512-hZ+Zxj3SySm4A/DylsDKZAeVg0mvi++0PYVceVyX7hemkw7OreKdCvW2oQ3T1FMZvCaQXqOTHb8qmBShoqk69Q==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-darwin-arm64": { + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.59.0.tgz", + "integrity": "sha512-W2Psnbh1J8ZJw0xKAd8zdNgF9HRLkdWwwdWqubSVk0pUuQkoHnv7rx4GiF9rT4t5DIZGAsConRE3AxCdJ4m8rg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-darwin-x64": { + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.59.0.tgz", + "integrity": "sha512-ZW2KkwlS4lwTv7ZVsYDiARfFCnSGhzYPdiOU4IM2fDbL+QGlyAbjgSFuqNRbSthybLbIJ915UtZBtmuLrQAT/w==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-freebsd-arm64": { + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.59.0.tgz", + "integrity": "sha512-EsKaJ5ytAu9jI3lonzn3BgG8iRBjV4LxZexygcQbpiU0wU0ATxhNVEpXKfUa0pS05gTcSDMKpn3Sx+QB9RlTTA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-freebsd-x64": { + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.59.0.tgz", + "integrity": "sha512-d3DuZi2KzTMjImrxoHIAODUZYoUUMsuUiY4SRRcJy6NJoZ6iIqWnJu9IScV9jXysyGMVuW+KNzZvBLOcpdl3Vg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-linux-arm-gnueabihf": { + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.59.0.tgz", + "integrity": "sha512-t4ONHboXi/3E0rT6OZl1pKbl2Vgxf9vJfWgmUoCEVQVxhW6Cw/c8I6hbbu7DAvgp82RKiH7TpLwxnJeKv2pbsw==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm-musleabihf": { + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.59.0.tgz", + "integrity": "sha512-CikFT7aYPA2ufMD086cVORBYGHffBo4K8MQ4uPS/ZnY54GKj36i196u8U+aDVT2LX4eSMbyHtyOh7D7Zvk2VvA==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-gnu": { + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.59.0.tgz", + "integrity": "sha512-jYgUGk5aLd1nUb1CtQ8E+t5JhLc9x5WdBKew9ZgAXg7DBk0ZHErLHdXM24rfX+bKrFe+Xp5YuJo54I5HFjGDAA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-musl": { + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.59.0.tgz", + "integrity": "sha512-peZRVEdnFWZ5Bh2KeumKG9ty7aCXzzEsHShOZEFiCQlDEepP1dpUl/SrUNXNg13UmZl+gzVDPsiCwnV1uI0RUA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-loong64-gnu": { + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.59.0.tgz", + "integrity": "sha512-gbUSW/97f7+r4gHy3Jlup8zDG190AuodsWnNiXErp9mT90iCy9NKKU0Xwx5k8VlRAIV2uU9CsMnEFg/xXaOfXg==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-loong64-musl": { + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-musl/-/rollup-linux-loong64-musl-4.59.0.tgz", + "integrity": "sha512-yTRONe79E+o0FWFijasoTjtzG9EBedFXJMl888NBEDCDV9I2wGbFFfJQQe63OijbFCUZqxpHz1GzpbtSFikJ4Q==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-ppc64-gnu": { + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.59.0.tgz", + "integrity": "sha512-sw1o3tfyk12k3OEpRddF68a1unZ5VCN7zoTNtSn2KndUE+ea3m3ROOKRCZxEpmT9nsGnogpFP9x6mnLTCaoLkA==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-ppc64-musl": { + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-musl/-/rollup-linux-ppc64-musl-4.59.0.tgz", + "integrity": "sha512-+2kLtQ4xT3AiIxkzFVFXfsmlZiG5FXYW7ZyIIvGA7Bdeuh9Z0aN4hVyXS/G1E9bTP/vqszNIN/pUKCk/BTHsKA==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-gnu": { + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.59.0.tgz", + "integrity": "sha512-NDYMpsXYJJaj+I7UdwIuHHNxXZ/b/N2hR15NyH3m2qAtb/hHPA4g4SuuvrdxetTdndfj9b1WOmy73kcPRoERUg==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-musl": { + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.59.0.tgz", + "integrity": "sha512-nLckB8WOqHIf1bhymk+oHxvM9D3tyPndZH8i8+35p/1YiVoVswPid2yLzgX7ZJP0KQvnkhM4H6QZ5m0LzbyIAg==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-s390x-gnu": { + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.59.0.tgz", + "integrity": "sha512-oF87Ie3uAIvORFBpwnCvUzdeYUqi2wY6jRFWJAy1qus/udHFYIkplYRW+wo+GRUP4sKzYdmE1Y3+rY5Gc4ZO+w==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-gnu": { + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.59.0.tgz", + "integrity": "sha512-3AHmtQq/ppNuUspKAlvA8HtLybkDflkMuLK4DPo77DfthRb71V84/c4MlWJXixZz4uruIH4uaa07IqoAkG64fg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-musl": { + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.59.0.tgz", + "integrity": "sha512-2UdiwS/9cTAx7qIUZB/fWtToJwvt0Vbo0zmnYt7ED35KPg13Q0ym1g442THLC7VyI6JfYTP4PiSOWyoMdV2/xg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-openbsd-x64": { + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-openbsd-x64/-/rollup-openbsd-x64-4.59.0.tgz", + "integrity": "sha512-M3bLRAVk6GOwFlPTIxVBSYKUaqfLrn8l0psKinkCFxl4lQvOSz8ZrKDz2gxcBwHFpci0B6rttydI4IpS4IS/jQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ] + }, + "node_modules/@rollup/rollup-openharmony-arm64": { + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.59.0.tgz", + "integrity": "sha512-tt9KBJqaqp5i5HUZzoafHZX8b5Q2Fe7UjYERADll83O4fGqJ49O1FsL6LpdzVFQcpwvnyd0i+K/VSwu/o/nWlA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ] + }, + "node_modules/@rollup/rollup-win32-arm64-msvc": { + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.59.0.tgz", + "integrity": "sha512-V5B6mG7OrGTwnxaNUzZTDTjDS7F75PO1ae6MJYdiMu60sq0CqN5CVeVsbhPxalupvTX8gXVSU9gq+Rx1/hvu6A==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-ia32-msvc": { + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.59.0.tgz", + "integrity": "sha512-UKFMHPuM9R0iBegwzKF4y0C4J9u8C6MEJgFuXTBerMk7EJ92GFVFYBfOZaSGLu6COf7FxpQNqhNS4c4icUPqxA==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-gnu": { + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.59.0.tgz", + "integrity": "sha512-laBkYlSS1n2L8fSo1thDNGrCTQMmxjYY5G0WFWjFFYZkKPjsMBsgJfGf4TLxXrF6RyhI60L8TMOjBMvXiTcxeA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-msvc": { + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.59.0.tgz", + "integrity": "sha512-2HRCml6OztYXyJXAvdDXPKcawukWY2GpR5/nxKp4iBgiO3wcoEGkAaqctIbZcNB6KlUQBIqt8VYkNSj2397EfA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@standard-schema/spec": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@standard-schema/spec/-/spec-1.1.0.tgz", + "integrity": "sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/chai": { + "version": "5.2.3", + "resolved": "https://registry.npmjs.org/@types/chai/-/chai-5.2.3.tgz", + "integrity": "sha512-Mw558oeA9fFbv65/y4mHtXDs9bPnFMZAL/jxdPFUpOHHIXX91mcgEHbS5Lahr+pwZFR8A7GQleRWeI6cGFC2UA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/deep-eql": "*", + "assertion-error": "^2.0.1" + } + }, + "node_modules/@types/deep-eql": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/@types/deep-eql/-/deep-eql-4.0.2.tgz", + "integrity": "sha512-c9h9dVVMigMPc4bwTvC5dxqtqJZwQPePsWjPlpSOnojbor6pGqdk541lfA7AqFQr5pB1BRdq0juY9db81BwyFw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/estree": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz", + "integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==", + "dev": true, + "license": "MIT" + }, + "node_modules/@vitest/expect": { + "version": "4.0.18", + "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-4.0.18.tgz", + "integrity": "sha512-8sCWUyckXXYvx4opfzVY03EOiYVxyNrHS5QxX3DAIi5dpJAAkyJezHCP77VMX4HKA2LDT/Jpfo8i2r5BE3GnQQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@standard-schema/spec": "^1.0.0", + "@types/chai": "^5.2.2", + "@vitest/spy": "4.0.18", + "@vitest/utils": "4.0.18", + "chai": "^6.2.1", + "tinyrainbow": "^3.0.3" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/mocker": { + "version": "4.0.18", + "resolved": "https://registry.npmjs.org/@vitest/mocker/-/mocker-4.0.18.tgz", + "integrity": "sha512-HhVd0MDnzzsgevnOWCBj5Otnzobjy5wLBe4EdeeFGv8luMsGcYqDuFRMcttKWZA5vVO8RFjexVovXvAM4JoJDQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/spy": "4.0.18", + "estree-walker": "^3.0.3", + "magic-string": "^0.30.21" + }, + "funding": { + "url": "https://opencollective.com/vitest" + }, + "peerDependencies": { + "msw": "^2.4.9", + "vite": "^6.0.0 || ^7.0.0-0" + }, + "peerDependenciesMeta": { + "msw": { + "optional": true + }, + "vite": { + "optional": true + } + } + }, + "node_modules/@vitest/pretty-format": { + "version": "4.0.18", + "resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-4.0.18.tgz", + "integrity": "sha512-P24GK3GulZWC5tz87ux0m8OADrQIUVDPIjjj65vBXYG17ZeU3qD7r+MNZ1RNv4l8CGU2vtTRqixrOi9fYk/yKw==", + "dev": true, + "license": "MIT", + "dependencies": { + "tinyrainbow": "^3.0.3" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/runner": { + "version": "4.0.18", + "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-4.0.18.tgz", + "integrity": "sha512-rpk9y12PGa22Jg6g5M3UVVnTS7+zycIGk9ZNGN+m6tZHKQb7jrP7/77WfZy13Y/EUDd52NDsLRQhYKtv7XfPQw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/utils": "4.0.18", + "pathe": "^2.0.3" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/snapshot": { + "version": "4.0.18", + "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-4.0.18.tgz", + "integrity": "sha512-PCiV0rcl7jKQjbgYqjtakly6T1uwv/5BQ9SwBLekVg/EaYeQFPiXcgrC2Y7vDMA8dM1SUEAEV82kgSQIlXNMvA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/pretty-format": "4.0.18", + "magic-string": "^0.30.21", + "pathe": "^2.0.3" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/spy": { + "version": "4.0.18", + "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-4.0.18.tgz", + "integrity": "sha512-cbQt3PTSD7P2OARdVW3qWER5EGq7PHlvE+QfzSC0lbwO+xnt7+XH06ZzFjFRgzUX//JmpxrCu92VdwvEPlWSNw==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/utils": { + "version": "4.0.18", + "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-4.0.18.tgz", + "integrity": "sha512-msMRKLMVLWygpK3u2Hybgi4MNjcYJvwTb0Ru09+fOyCXIgT5raYP041DRRdiJiI3k/2U6SEbAETB3YtBrUkCFA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/pretty-format": "4.0.18", + "tinyrainbow": "^3.0.3" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "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==", + "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", + "integrity": "sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + } + }, + "node_modules/balanced-match": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz", + "integrity": "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==", + "license": "MIT", + "engines": { + "node": "18 || 20 || >=22" + } + }, + "node_modules/before-after-hook": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/before-after-hook/-/before-after-hook-4.0.0.tgz", + "integrity": "sha512-q6tR3RPqIB1pMiTRMFcZwuG5T8vwp+vUvEG0vuI6B+Rikh5BfPp2fQ82c925FOs+b0lcFQ8CFrL+KbilfZFhOQ==", + "license": "Apache-2.0" + }, + "node_modules/brace-expansion": { + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.3.tgz", + "integrity": "sha512-fy6KJm2RawA5RcHkLa1z/ScpBeA762UF9KmZQxwIbDtRJrgLzM10depAiEQ+CXYcoiqW1/m96OAAoke2nE9EeA==", + "license": "MIT", + "dependencies": { + "balanced-match": "^4.0.2" + }, + "engines": { + "node": "18 || 20 || >=22" + } + }, + "node_modules/chai": { + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/chai/-/chai-6.2.2.tgz", + "integrity": "sha512-NUPRluOfOiTKBKvWPtSD4PhFvWCqOi0BGStNWs57X9js7XGTprSmFoz5F0tWhR4WPjNeR9jXqdC7/UpSJTnlRg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + } + }, + "node_modules/es-module-lexer": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-1.7.0.tgz", + "integrity": "sha512-jEQoCwk8hyb2AZziIOLhDqpm5+2ww5uIE6lkO/6jcOCusfk6LhMHpXXfBLXTZ7Ydyt0j4VoUQv6uGNYbdW+kBA==", + "dev": true, + "license": "MIT" + }, + "node_modules/esbuild": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.27.3.tgz", + "integrity": "sha512-8VwMnyGCONIs6cWue2IdpHxHnAjzxnw2Zr7MkVxB2vjmQ2ivqGFb4LEG3SMnv0Gb2F/G/2yA8zUaiL1gywDCCg==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.27.3", + "@esbuild/android-arm": "0.27.3", + "@esbuild/android-arm64": "0.27.3", + "@esbuild/android-x64": "0.27.3", + "@esbuild/darwin-arm64": "0.27.3", + "@esbuild/darwin-x64": "0.27.3", + "@esbuild/freebsd-arm64": "0.27.3", + "@esbuild/freebsd-x64": "0.27.3", + "@esbuild/linux-arm": "0.27.3", + "@esbuild/linux-arm64": "0.27.3", + "@esbuild/linux-ia32": "0.27.3", + "@esbuild/linux-loong64": "0.27.3", + "@esbuild/linux-mips64el": "0.27.3", + "@esbuild/linux-ppc64": "0.27.3", + "@esbuild/linux-riscv64": "0.27.3", + "@esbuild/linux-s390x": "0.27.3", + "@esbuild/linux-x64": "0.27.3", + "@esbuild/netbsd-arm64": "0.27.3", + "@esbuild/netbsd-x64": "0.27.3", + "@esbuild/openbsd-arm64": "0.27.3", + "@esbuild/openbsd-x64": "0.27.3", + "@esbuild/openharmony-arm64": "0.27.3", + "@esbuild/sunos-x64": "0.27.3", + "@esbuild/win32-arm64": "0.27.3", + "@esbuild/win32-ia32": "0.27.3", + "@esbuild/win32-x64": "0.27.3" + } + }, + "node_modules/estree-walker": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-3.0.3.tgz", + "integrity": "sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/estree": "^1.0.0" + } + }, + "node_modules/expect-type": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/expect-type/-/expect-type-1.3.0.tgz", + "integrity": "sha512-knvyeauYhqjOYvQ66MznSMs83wmHrCycNEN6Ao+2AeYEfxUIkuiVxdEa1qlGEPK+We3n0THiDciYSsCcgW/DoA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/fast-content-type-parse": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/fast-content-type-parse/-/fast-content-type-parse-3.0.0.tgz", + "integrity": "sha512-ZvLdcY8P+N8mGQJahJV5G4U88CSvT1rP8ApL6uETe88MBXrBHAkZlSEySdUlyztF7ccb+Znos3TFqaepHxdhBg==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fastify" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fastify" + } + ], + "license": "MIT" + }, + "node_modules/fdir": { + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", + "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12.0.0" + }, + "peerDependencies": { + "picomatch": "^3 || ^4" + }, + "peerDependenciesMeta": { + "picomatch": { + "optional": true + } + } + }, + "node_modules/fs-extra": { + "version": "11.3.3", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-11.3.3.tgz", + "integrity": "sha512-VWSRii4t0AFm6ixFFmLLx1t7wS1gh+ckoa84aOeapGum0h+EZd1EhEumSB+ZdDLnEPuucsVB9oB7cxJHap6Afg==", + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + }, + "engines": { + "node": ">=14.14" + } + }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/graceful-fs": { + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", + "license": "ISC" + }, + "node_modules/js-yaml": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.1.tgz", + "integrity": "sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==", + "license": "MIT", + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/json-with-bigint": { + "version": "3.5.3", + "resolved": "https://registry.npmjs.org/json-with-bigint/-/json-with-bigint-3.5.3.tgz", + "integrity": "sha512-QObKu6nxy7NsxqR0VK4rkXnsNr5L9ElJaGEg+ucJ6J7/suoKZ0n+p76cu9aCqowytxEbwYNzvrMerfMkXneF5A==", + "license": "MIT" + }, + "node_modules/jsonfile": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.2.0.tgz", + "integrity": "sha512-FGuPw30AdOIUTRMC2OMRtQV+jkVj2cfPqSeWXv1NEAJ1qZ5zb1X6z1mFhbfOB/iy3ssJCD+3KuZ8r8C3uVFlAg==", + "license": "MIT", + "dependencies": { + "universalify": "^2.0.0" + }, + "optionalDependencies": { + "graceful-fs": "^4.1.6" + } + }, + "node_modules/magic-string": { + "version": "0.30.21", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.21.tgz", + "integrity": "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.5" + } + }, + "node_modules/minimatch": { + "version": "10.2.3", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.3.tgz", + "integrity": "sha512-Rwi3pnapEqirPSbWbrZaa6N3nmqq4Xer/2XooiOKyV3q12ML06f7MOuc5DVH8ONZIFhwIYQ3yzPH4nt7iWHaTg==", + "license": "BlueOak-1.0.0", + "dependencies": { + "brace-expansion": "^5.0.2" + }, + "engines": { + "node": "18 || 20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/nanoid": { + "version": "3.3.11", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz", + "integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } + }, + "node_modules/obug": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/obug/-/obug-2.1.1.tgz", + "integrity": "sha512-uTqF9MuPraAQ+IsnPf366RG4cP9RtUi7MLO1N3KEc+wb0a6yKpeL0lmk2IB1jY5KHPAlTc6T/JRdC/YqxHNwkQ==", + "dev": true, + "funding": [ + "https://github.com/sponsors/sxzz", + "https://opencollective.com/debug" + ], + "license": "MIT" + }, + "node_modules/pathe": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/pathe/-/pathe-2.0.3.tgz", + "integrity": "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==", + "dev": true, + "license": "MIT" + }, + "node_modules/picocolors": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", + "dev": true, + "license": "ISC" + }, + "node_modules/picomatch": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", + "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/postcss": { + "version": "8.5.6", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.6.tgz", + "integrity": "sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "nanoid": "^3.3.11", + "picocolors": "^1.1.1", + "source-map-js": "^1.2.1" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, + "node_modules/rollup": { + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.59.0.tgz", + "integrity": "sha512-2oMpl67a3zCH9H79LeMcbDhXW/UmWG/y2zuqnF2jQq5uq9TbM9TVyXvA4+t+ne2IIkBdrLpAaRQAvo7YI/Yyeg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/estree": "1.0.8" + }, + "bin": { + "rollup": "dist/bin/rollup" + }, + "engines": { + "node": ">=18.0.0", + "npm": ">=8.0.0" + }, + "optionalDependencies": { + "@rollup/rollup-android-arm-eabi": "4.59.0", + "@rollup/rollup-android-arm64": "4.59.0", + "@rollup/rollup-darwin-arm64": "4.59.0", + "@rollup/rollup-darwin-x64": "4.59.0", + "@rollup/rollup-freebsd-arm64": "4.59.0", + "@rollup/rollup-freebsd-x64": "4.59.0", + "@rollup/rollup-linux-arm-gnueabihf": "4.59.0", + "@rollup/rollup-linux-arm-musleabihf": "4.59.0", + "@rollup/rollup-linux-arm64-gnu": "4.59.0", + "@rollup/rollup-linux-arm64-musl": "4.59.0", + "@rollup/rollup-linux-loong64-gnu": "4.59.0", + "@rollup/rollup-linux-loong64-musl": "4.59.0", + "@rollup/rollup-linux-ppc64-gnu": "4.59.0", + "@rollup/rollup-linux-ppc64-musl": "4.59.0", + "@rollup/rollup-linux-riscv64-gnu": "4.59.0", + "@rollup/rollup-linux-riscv64-musl": "4.59.0", + "@rollup/rollup-linux-s390x-gnu": "4.59.0", + "@rollup/rollup-linux-x64-gnu": "4.59.0", + "@rollup/rollup-linux-x64-musl": "4.59.0", + "@rollup/rollup-openbsd-x64": "4.59.0", + "@rollup/rollup-openharmony-arm64": "4.59.0", + "@rollup/rollup-win32-arm64-msvc": "4.59.0", + "@rollup/rollup-win32-ia32-msvc": "4.59.0", + "@rollup/rollup-win32-x64-gnu": "4.59.0", + "@rollup/rollup-win32-x64-msvc": "4.59.0", + "fsevents": "~2.3.2" + } + }, + "node_modules/siginfo": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/siginfo/-/siginfo-2.0.0.tgz", + "integrity": "sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==", + "dev": true, + "license": "ISC" + }, + "node_modules/source-map-js": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", + "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/stackback": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/stackback/-/stackback-0.0.2.tgz", + "integrity": "sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==", + "dev": true, + "license": "MIT" + }, + "node_modules/std-env": { + "version": "3.10.0", + "resolved": "https://registry.npmjs.org/std-env/-/std-env-3.10.0.tgz", + "integrity": "sha512-5GS12FdOZNliM5mAOxFRg7Ir0pWz8MdpYm6AY6VPkGpbA7ZzmbzNcBJQ0GPvvyWgcY7QAhCgf9Uy89I03faLkg==", + "dev": true, + "license": "MIT" + }, + "node_modules/tinybench": { + "version": "2.9.0", + "resolved": "https://registry.npmjs.org/tinybench/-/tinybench-2.9.0.tgz", + "integrity": "sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg==", + "dev": true, + "license": "MIT" + }, + "node_modules/tinyexec": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-1.0.2.tgz", + "integrity": "sha512-W/KYk+NFhkmsYpuHq5JykngiOCnxeVL8v8dFnqxSD8qEEdRfXk1SDM6JzNqcERbcGYj9tMrDQBYV9cjgnunFIg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + } + }, + "node_modules/tinyglobby": { + "version": "0.2.15", + "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.15.tgz", + "integrity": "sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "fdir": "^6.5.0", + "picomatch": "^4.0.3" + }, + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/SuperchupuDev" + } + }, + "node_modules/tinyrainbow": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/tinyrainbow/-/tinyrainbow-3.0.3.tgz", + "integrity": "sha512-PSkbLUoxOFRzJYjjxHJt9xro7D+iilgMX/C9lawzVuYiIdcihh9DXmVibBe8lmcFrRi/VzlPjBxbN7rH24q8/Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/tmp": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.2.5.tgz", + "integrity": "sha512-voyz6MApa1rQGUxT3E+BK7/ROe8itEx7vD8/HEvt4xwXucvQ5G5oeEiHkmHZJuBO21RpOf+YYm9MOivj709jow==", + "license": "MIT", + "engines": { + "node": ">=14.14" + } + }, + "node_modules/tunnel": { + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/tunnel/-/tunnel-0.0.6.tgz", + "integrity": "sha512-1h/Lnq9yajKY2PEbBadPXj3VxsDDu844OnaAo52UVmIzIvwwtBPIuNvkjuzBlTWpfJyUbG3ez0KSBibQkj4ojg==", + "license": "MIT", + "engines": { + "node": ">=0.6.11 <=0.7.0 || >=0.7.3" + } + }, + "node_modules/undici": { + "version": "6.23.0", + "resolved": "https://registry.npmjs.org/undici/-/undici-6.23.0.tgz", + "integrity": "sha512-VfQPToRA5FZs/qJxLIinmU59u0r7LXqoJkCzinq3ckNJp3vKEh7jTWN589YQ5+aoAC/TGRLyJLCPKcLQbM8r9g==", + "license": "MIT", + "engines": { + "node": ">=18.17" + } + }, + "node_modules/universal-user-agent": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/universal-user-agent/-/universal-user-agent-7.0.3.tgz", + "integrity": "sha512-TmnEAEAsBJVZM/AADELsK76llnwcf9vMKuPz8JflO1frO8Lchitr0fNaN9d+Ap0BjKtqWqd/J17qeDnXh8CL2A==", + "license": "ISC" + }, + "node_modules/universalify": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", + "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==", + "license": "MIT", + "engines": { + "node": ">= 10.0.0" + } + }, + "node_modules/vite": { + "version": "7.3.1", + "resolved": "https://registry.npmjs.org/vite/-/vite-7.3.1.tgz", + "integrity": "sha512-w+N7Hifpc3gRjZ63vYBXA56dvvRlNWRczTdmCBBa+CotUzAPf5b7YMdMR/8CQoeYE5LX3W4wj6RYTgonm1b9DA==", + "dev": true, + "license": "MIT", + "dependencies": { + "esbuild": "^0.27.0", + "fdir": "^6.5.0", + "picomatch": "^4.0.3", + "postcss": "^8.5.6", + "rollup": "^4.43.0", + "tinyglobby": "^0.2.15" + }, + "bin": { + "vite": "bin/vite.js" + }, + "engines": { + "node": "^20.19.0 || >=22.12.0" + }, + "funding": { + "url": "https://github.com/vitejs/vite?sponsor=1" + }, + "optionalDependencies": { + "fsevents": "~2.3.3" + }, + "peerDependencies": { + "@types/node": "^20.19.0 || >=22.12.0", + "jiti": ">=1.21.0", + "less": "^4.0.0", + "lightningcss": "^1.21.0", + "sass": "^1.70.0", + "sass-embedded": "^1.70.0", + "stylus": ">=0.54.8", + "sugarss": "^5.0.0", + "terser": "^5.16.0", + "tsx": "^4.8.1", + "yaml": "^2.4.2" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + }, + "jiti": { + "optional": true + }, + "less": { + "optional": true + }, + "lightningcss": { + "optional": true + }, + "sass": { + "optional": true + }, + "sass-embedded": { + "optional": true + }, + "stylus": { + "optional": true + }, + "sugarss": { + "optional": true + }, + "terser": { + "optional": true + }, + "tsx": { + "optional": true + }, + "yaml": { + "optional": true + } + } + }, + "node_modules/vitest": { + "version": "4.0.18", + "resolved": "https://registry.npmjs.org/vitest/-/vitest-4.0.18.tgz", + "integrity": "sha512-hOQuK7h0FGKgBAas7v0mSAsnvrIgAvWmRFjmzpJ7SwFHH3g1k2u37JtYwOwmEKhK6ZO3v9ggDBBm0La1LCK4uQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/expect": "4.0.18", + "@vitest/mocker": "4.0.18", + "@vitest/pretty-format": "4.0.18", + "@vitest/runner": "4.0.18", + "@vitest/snapshot": "4.0.18", + "@vitest/spy": "4.0.18", + "@vitest/utils": "4.0.18", + "es-module-lexer": "^1.7.0", + "expect-type": "^1.2.2", + "magic-string": "^0.30.21", + "obug": "^2.1.1", + "pathe": "^2.0.3", + "picomatch": "^4.0.3", + "std-env": "^3.10.0", + "tinybench": "^2.9.0", + "tinyexec": "^1.0.2", + "tinyglobby": "^0.2.15", + "tinyrainbow": "^3.0.3", + "vite": "^6.0.0 || ^7.0.0", + "why-is-node-running": "^2.3.0" + }, + "bin": { + "vitest": "vitest.mjs" + }, + "engines": { + "node": "^20.0.0 || ^22.0.0 || >=24.0.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + }, + "peerDependencies": { + "@edge-runtime/vm": "*", + "@opentelemetry/api": "^1.9.0", + "@types/node": "^20.0.0 || ^22.0.0 || >=24.0.0", + "@vitest/browser-playwright": "4.0.18", + "@vitest/browser-preview": "4.0.18", + "@vitest/browser-webdriverio": "4.0.18", + "@vitest/ui": "4.0.18", + "happy-dom": "*", + "jsdom": "*" + }, + "peerDependenciesMeta": { + "@edge-runtime/vm": { + "optional": true + }, + "@opentelemetry/api": { + "optional": true + }, + "@types/node": { + "optional": true + }, + "@vitest/browser-playwright": { + "optional": true + }, + "@vitest/browser-preview": { + "optional": true + }, + "@vitest/browser-webdriverio": { + "optional": true + }, + "@vitest/ui": { + "optional": true + }, + "happy-dom": { + "optional": true + }, + "jsdom": { + "optional": true + } + } + }, + "node_modules/why-is-node-running": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/why-is-node-running/-/why-is-node-running-2.3.0.tgz", + "integrity": "sha512-hUrmaWBdVDcxvYqnyh09zunKzROWjbZTiNy8dBEjkS7ehEDQibXJ7XvlmtbwuTclUiIyN+CyXQD4Vmko8fNm8w==", + "dev": true, + "license": "MIT", + "dependencies": { + "siginfo": "^2.0.0", + "stackback": "0.0.2" + }, + "bin": { + "why-is-node-running": "cli.js" + }, + "engines": { + "node": ">=8" + } + } + } +} diff --git a/package.json b/package.json index dbb85b7..55fdeb8 100644 --- a/package.json +++ b/package.json @@ -13,17 +13,17 @@ }, "devDependencies": { - "vitest": "^1.6.0" + "vitest": "^4.0.18" }, "dependencies": { - "@actions/core": "^1.6.0", - "@octokit/rest": "^19.0.5", - "@actions/github": "^2.2.0", - "fs-extra": "^10.1.0", - "js-yaml": "^4.1.0", - "minimatch": "^9.0.0", - "tmp": "^0.2.1" + "@actions/core": "^3.0.0", + "@actions/github": "^9.0.0", + "@octokit/rest": "^22.0.1", + "fs-extra": "^11.3.3", + "js-yaml": "^4.1.1", + "minimatch": "^10.2.3", + "tmp": "^0.2.5" } } From bbe20ddeef3a6548e03555a6daf905d78d41d9a8 Mon Sep 17 00:00:00 2001 From: Alex Date: Wed, 25 Feb 2026 06:51:23 -0800 Subject: [PATCH 13/13] apply date based spec naming format --- ...solution.md => 2026-02-23 configurable-conflict-resolution.md} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename specs/{configurable-conflict-resolution.md => 2026-02-23 configurable-conflict-resolution.md} (100%) diff --git a/specs/configurable-conflict-resolution.md b/specs/2026-02-23 configurable-conflict-resolution.md similarity index 100% rename from specs/configurable-conflict-resolution.md rename to specs/2026-02-23 configurable-conflict-resolution.md