From 80f324b508cc48600ed72a913aef63f9a97fc0dd Mon Sep 17 00:00:00 2001 From: Christian Findlay <16697547+MelbourneDeveloper@users.noreply.github.com> Date: Sat, 23 May 2026 09:27:56 +1000 Subject: [PATCH 01/17] agent pmo --- .claude/skills/ci-prep/SKILL.md | 121 ++++++++ .claude/skills/code-dedup/SKILL.md | 108 +++++++ .claude/skills/fix-bug/SKILL.md | 67 +++++ .claude/skills/spec-check/SKILL.md | 331 +++++++++++++++++++++ .claude/skills/submit-pr/SKILL.md | 39 +++ .claude/skills/upgrade-packages/SKILL.md | 95 ++++++ .claude/skills/website-audit/SKILL.md | 180 ++++++++++++ .clinerules/00-read-instructions.md | 2 + .cursorrules | 2 + .devcontainer/devcontainer.json | 25 ++ .github/copilot-instructions.md | 2 + .github/pull_request_template.md | 10 + .github/workflows/ci.yml | 67 +++++ .github/workflows/release.yml | 123 ++++++++ .gitignore | 77 +++++ .prettierrc.json | 12 + .vscode/extensions.json | 11 + .windsurfrules | 2 + AGENTS.md | 2 + CLAUDE.md | 2 + Makefile | 107 +++++++ coverage-thresholds.json | 5 + eslint.config.mjs | 58 ++++ opencode.json | 5 + src/constants.ts | 47 +++ src/git/GitRepo.ts | 99 +++++++ src/git/GitRunner.ts | 83 ++++++ src/git/parsers.ts | 242 +++++++++++++++ src/git/types.ts | 57 ++++ src/logger.ts | 15 + src/result.ts | 22 ++ src/test/unit/parsers.test.ts | 355 +++++++++++++++++++++++ src/test/unit/result.test.ts | 130 +++++++++ src/test/unit/uri.test.ts | 149 ++++++++++ src/ui/uri.ts | 89 ++++++ tsconfig.json | 35 +++ 36 files changed, 2776 insertions(+) create mode 100644 .claude/skills/ci-prep/SKILL.md create mode 100644 .claude/skills/code-dedup/SKILL.md create mode 100644 .claude/skills/fix-bug/SKILL.md create mode 100644 .claude/skills/spec-check/SKILL.md create mode 100644 .claude/skills/submit-pr/SKILL.md create mode 100644 .claude/skills/upgrade-packages/SKILL.md create mode 100644 .claude/skills/website-audit/SKILL.md create mode 100644 .clinerules/00-read-instructions.md create mode 100644 .cursorrules create mode 100644 .devcontainer/devcontainer.json create mode 100644 .github/copilot-instructions.md create mode 100644 .github/pull_request_template.md create mode 100644 .github/workflows/ci.yml create mode 100644 .github/workflows/release.yml create mode 100644 .gitignore create mode 100644 .prettierrc.json create mode 100644 .vscode/extensions.json create mode 100644 .windsurfrules create mode 100644 AGENTS.md create mode 100644 Makefile create mode 100644 coverage-thresholds.json create mode 100644 eslint.config.mjs create mode 100644 opencode.json create mode 100644 src/constants.ts create mode 100644 src/git/GitRepo.ts create mode 100644 src/git/GitRunner.ts create mode 100644 src/git/parsers.ts create mode 100644 src/git/types.ts create mode 100644 src/logger.ts create mode 100644 src/result.ts create mode 100644 src/test/unit/parsers.test.ts create mode 100644 src/test/unit/result.test.ts create mode 100644 src/test/unit/uri.test.ts create mode 100644 src/ui/uri.ts create mode 100644 tsconfig.json diff --git a/.claude/skills/ci-prep/SKILL.md b/.claude/skills/ci-prep/SKILL.md new file mode 100644 index 0000000..89a0bcb --- /dev/null +++ b/.claude/skills/ci-prep/SKILL.md @@ -0,0 +1,121 @@ +--- +name: ci-prep +description: Prepares the current branch for CI by running the exact same steps locally and fixing issues. If CI is already failing, fetches the GH Actions logs first to diagnose. Use before pushing, when CI is red, or when the user says "fix ci". +argument-hint: "[--failing] [optional job name to focus on]" +--- + + +# CI Prep + +Prepare the current state for CI. If CI is already failing, fetch and analyze the logs first. + +## Arguments + +- `--failing` — Indicates a GitHub Actions run is already failing. When present, you MUST execute **Step 1** before doing anything else. +- Any other argument is treated as a job name to focus on (but all failures are still reported). + +If `--failing` is NOT passed, skip directly to **Step 2**. + +## Step 1 — Fetch failed CI logs (only when `--failing`) + +You MUST do this before any other work. + +```bash +BRANCH=$(git branch --show-current) +PR_JSON=$(gh pr list --head "$BRANCH" --state open --json number,title,url --limit 1) +``` + +If the JSON array is empty, **stop immediately**: +> No open PR found for branch `$BRANCH`. Create a PR first. + +Otherwise fetch the logs: + +```bash +PR_NUMBER=$(echo "$PR_JSON" | jq -r '.[0].number') +gh pr checks "$PR_NUMBER" +RUN_ID=$(gh run list --branch "$BRANCH" --limit 1 --json databaseId --jq '.[0].databaseId') +gh run view "$RUN_ID" +gh run view "$RUN_ID" --log-failed +``` + +Read **every line** of `--log-failed` output. For each failure note the exact file, line, and error message. If a job name argument was provided, prioritize that job but still report all failures. + +## Step 2 — Analyze the CI workflow + +1. Find the CI workflow file. Look in `.github/workflows/` for `ci.yml`, `build.yml`, `test.yml`, `checks.yml`, `main.yml`, `pull_request.yml`, or any workflow triggered on `pull_request` or `push`. +2. Read the workflow file completely. Parse every job and every step. +3. Extract the ordered list of commands the CI actually runs. In a spec-compliant repo this is `make lint → make test → make build` (REPO-STANDARDS-SPEC [MAKE-TARGETS]), but the actual CI may use `npm`, `cargo`, `dotnet`, raw shell commands, or anything else. Extract what is *actually there*. +4. Note any environment variables, matrix strategies, or conditional steps that affect execution. + +**Do NOT assume the steps are `make lint`, `make test`, `make build`.** The actual CI may run different commands, in a different order. Extract what the CI *actually does*. If you find extra targets beyond the 7 in [MAKE-TARGETS] (e.g. `make fmt-check`, `make coverage-check`), flag them in your final report — they should be consolidated by the agent-pmo skill. + +### Release workflow blocker scan + +If `.github/workflows/release.yml` exists, scan it before broad local CI. These are critical blockers +and must be fixed before release work is considered CI-ready: + +- Tag-triggered jobs checking out `ref: main` instead of the tagged SHA. +- Any `git commit`, `git push`, branch mutation, or tag mutation during release. +- Version bump commits after the tag already exists. +- Ad hoc `sed` version stamping of structured files instead of a first-class stamper/build input. +- Missing tests that pass a test version into the same stamper used by release. +- Native VSIX releases without Node `22.x`, `npx vsce package --target `, one VSIX per + target, target-suffixed filenames, and package-content verification. +- VS Code native-binary activation that reads or mutates PATH, uses package-manager/global installs + as normal startup sources, or copies bundled VSIX binaries after install. + +## Step 3 — Run each CI step locally, in order + +Work through failures in this priority order: + +1. **Formatting** — run auto-formatters first to clear noise +2. **Compilation errors** — must compile before lint/test +3. **Lint violations** — fix the code pattern +4. **Runtime / test failures** — fix source code to satisfy the test + +For each command extracted from the CI workflow: + +1. Run the command exactly as CI would run it (adjusting only for local environment differences like not needing `actions/checkout`). +2. If the step fails, **stop and fix the issues** before continuing to the next step. +3. After fixing, re-run the same step to confirm it passes. +4. Move to the next step only after the current one succeeds. + +### Hard constraints + +- **NEVER modify test files** — fix the source code, not the tests +- **NEVER add suppressions** (`// eslint-disable`, `// @ts-ignore`, `// @ts-nocheck`) +- **NEVER use `any` in TypeScript** to silence type errors +- **NEVER delete or ignore failing tests** +- **NEVER remove assertions** + +If stuck on the same failure after 5 attempts, ask the user for help. + +## Step 4 — Report + +- List every step that was run and its result (pass/fail/fixed). +- If any step could not be fixed, report what failed and why. +- Confirm whether the branch is ready to push. + +## Step 5 — Remote CI follow-up (only when `--failing`) + +Once all CI steps pass locally: + +1. Report the local fixes and exact commands that now pass. +2. Do not commit or push. The user owns source-control writes. +3. If the user pushes, monitor the new run until completion or failure. +4. Upon failure, go back to Step 1. + +## Rules + +- **Always read the CI workflow first.** Never assume what commands CI runs. +- Do not commit or push from this skill. +- Fix issues found in each step before moving to the next +- Never skip steps or suppress errors +- If the CI workflow has multiple jobs, run all of them (respecting dependency order) +- Skip steps that are CI-infrastructure-only (checkout, setup-node/python/rust actions, cache steps, artifact uploads) — focus on the actual build/test/lint commands + +## Success criteria + +- Every command that CI runs has been executed locally and passed +- All fixes are applied to the working tree +- The CI passes successfully (if you are correcting and existing failure) diff --git a/.claude/skills/code-dedup/SKILL.md b/.claude/skills/code-dedup/SKILL.md new file mode 100644 index 0000000..1b1c593 --- /dev/null +++ b/.claude/skills/code-dedup/SKILL.md @@ -0,0 +1,108 @@ +--- +name: code-dedup +description: Searches for duplicate code, duplicate tests, and dead code, then safely merges or removes them. Use when the user says "deduplicate", "find duplicates", "remove dead code", "DRY up", or "code dedup". Requires test coverage — refuses to touch untested code. +--- + + +# Code Dedup + +Carefully search for duplicate code, duplicate tests, and dead code across the Diffy repo. Merge duplicates and delete dead code — but only when test coverage proves the change is safe. + +## Prerequisites — hard gate + +Before touching ANY code, verify these conditions. If any fail, stop and report why. + +1. Run `make test` — all tests must pass. If tests fail, stop. Do not dedup a broken codebase. +2. Run `make test` — tests are fail-fast AND enforce the coverage threshold from `coverage-thresholds.json`. If anything fails, stop and fix it before deduping. +3. Verify the project uses **static typing**. TypeScript with `tsconfig.json` `"strict": true` is required (which Diffy enforces — see CLAUDE.md). If `strict` is off, STOP and refuse. + +## Steps + +Copy this checklist and track progress: + +``` +Dedup Progress: +- [ ] Step 1: Prerequisites passed (tests green, coverage met, strict TS) +- [ ] Step 2: Dead code scan complete +- [ ] Step 3: Duplicate code scan complete +- [ ] Step 4: Duplicate test scan complete +- [ ] Step 5: Changes applied +- [ ] Step 6: Verification passed (tests green, coverage stable) +``` + +### Step 1 — Inventory test coverage + +Before deciding what to touch, understand what is tested. + +1. Run `make test` to confirm green baseline. `make test` is fail-fast AND enforces the coverage threshold from `coverage-thresholds.json` (REPO-STANDARDS-SPEC [TEST-RULES], [COVERAGE-THRESHOLDS-JSON]). It exits non-zero on any test failure OR coverage shortfall. +2. Note the current coverage percentage — this is the floor. It must not drop. +3. Identify which files/modules have coverage and which do not. Only files WITH coverage are candidates for dedup. + +### Step 2 — Scan for dead code + +Search for code that is never called, never imported, never referenced. + +1. Look for unused exports, unused functions, unused classes, unused variables. +2. Check TypeScript: `noUnusedLocals`/`noUnusedParameters` are already enabled in Diffy's `tsconfig.json`. Look for unexported helpers with zero references. +3. For each candidate: **grep the entire codebase** (including `src/test/`, `package.json` `contributes`, `scripts/`, configs) for references. Only mark as dead if truly zero references. +4. List all dead code found with file paths and line numbers. Do NOT delete yet. + +### Step 3 — Scan for duplicate code + +Search for code blocks that do the same thing in multiple places. + +1. Look for functions/methods with identical or near-identical logic. +2. Look for copy-pasted blocks (same structure, maybe different variable names). +3. Look for multiple implementations of the same algorithm or pattern. +4. Check across module boundaries — Diffy's layered architecture (`src/git/`, `src/ui/`, `src/providers/`, `src/commands/`) is a natural place for accidental duplication. +5. For each duplicate pair: note both locations, what they do, and how they differ (if at all). +6. List all duplicates found. Do NOT merge yet. + +### Step 4 — Scan for duplicate tests + +Search for tests that verify the same behavior. + +1. Look for test functions with identical assertions against the same code paths. +2. Look for test fixtures/helpers that are duplicated across `src/test/unit/` and `src/test/suite/`. +3. Look for E2E tests that fully cover what a unit test also covers (per CLAUDE.md, E2E is preferred — keep the E2E, mark the unit test as redundant only if E2E genuinely subsumes its coverage). +4. List all duplicate tests found. Do NOT delete yet. + +### Step 5 — Apply changes (one at a time) + +For each change, follow this cycle: **change → test → verify coverage → continue or revert**. + +#### 5a. Remove dead code +- Delete dead code identified in Step 2 +- After each deletion: run `make test` (fail-fast + coverage + threshold all in one) +- If `make test` exits non-zero (test failure OR coverage drop): **revert immediately** and investigate +- Dead code removal should never break tests or drop coverage + +#### 5b. Merge duplicate code +- For each duplicate pair: extract the shared logic into a single function/module +- Update all call sites to use the shared version +- After each merge: run `make test` +- If tests fail: **revert immediately**. The duplicates may have subtle differences you missed. +- If coverage drops: the shared code must have equivalent test coverage. Add tests if needed before proceeding. + +#### 5c. Remove duplicate tests +- Delete the redundant test (keep the more thorough one) +- After each deletion: run `make test` +- If coverage drops below threshold, `make test` exits non-zero — **revert immediately**. The "duplicate" test was covering something the other wasn't. + +### Step 6 — Final verification + +1. Run `make lint` — ESLint + tsc --noEmit must pass clean. +2. Run `make test` — tests must pass AND coverage must remain ≥ the baseline from Step 1. +3. Report: what was removed, what was merged, final coverage vs baseline. + +(Only the 7 standard targets exist — `make lint` and `make test` cover linting and coverage checks respectively.) + +## Rules + +- **No test coverage = do not touch.** If a file has no tests covering it, leave it alone entirely. You cannot safely dedup what you cannot verify. +- **Coverage must not drop.** If removing or merging code causes coverage to decrease, revert and investigate. The coverage floor from Step 1 is sacred. +- **Strict TypeScript only.** Diffy enforces `strict: true`. If that ever changes, refuse to dedup until it's re-enabled. +- **One change at a time.** Make one dedup change, run tests, verify coverage. Never batch multiple dedup changes before testing. +- **When in doubt, leave it.** If two code blocks look similar but you're not 100% sure they're functionally identical, leave both. False dedup is worse than duplication. +- **Preserve public API surface.** Do not change exported function signatures, command IDs, URI scheme, or `package.json` `contributes` keys that the extension exposes. +- **Three similar lines is fine.** Do not create abstractions for trivial duplication. Per CLAUDE.md: three similar lines is better than a premature abstraction. Only dedup when the shared logic is substantial (>10 lines) or when there are 3+ copies. diff --git a/.claude/skills/fix-bug/SKILL.md b/.claude/skills/fix-bug/SKILL.md new file mode 100644 index 0000000..0f0b4a4 --- /dev/null +++ b/.claude/skills/fix-bug/SKILL.md @@ -0,0 +1,67 @@ +--- +name: fix-bug +description: Fix a bug using test-driven development. Use when the user reports a bug, describes unexpected behavior, wants to fix a defect, or says something is broken. Enforces a strict test-first workflow where a failing test must be written and verified before any fix is attempted. +argument-hint: "[bug description]" +allowed-tools: Read, Grep, Glob, Edit, Write, Bash +--- + + +# Bug Fix Skill — Test-First Workflow + +You MUST follow this exact workflow. Do NOT skip steps. Do NOT fix the bug before writing a failing test. + +## Step 1: Understand the Bug + +- Read the bug description: $ARGUMENTS +- Investigate the codebase to understand the relevant code +- Identify the root cause (or narrow down candidates) +- Summarize your understanding of the bug to the user before proceeding + +## Step 2: Write a Failing Test + +- Write a test that **directly exercises the buggy behavior** +- The test must assert the **correct/expected** behavior — so it FAILS against the current broken code +- The test name should clearly describe the bug (e.g., `test_orange_color_not_applied_to_head`) +- Use the project's existing test framework and conventions (Diffy uses mocha for unit and `@vscode/test-electron` for E2E — pick the right tier per CLAUDE.md) + +## Step 3: Run the Test — Confirm It FAILS + +- Run ONLY the new test (not the full suite) +- **Verify the test FAILS** and that it fails **because of the bug**, not for some other reason (typo, import error, wrong selector, etc.) +- If the test passes: your test does not capture the bug. Go back to Step 2 +- If the test fails for the wrong reason: fix the test, not the code. Go back to Step 2 +- **Repeat until the test fails specifically because of the bug** + +## Step 4: Show Failure to User + +- Show the user the test code and the failure output +- Explicitly ask: "This test fails because of the bug. Can you confirm this captures the issue before I fix it?" +- **STOP and WAIT for user acknowledgment before proceeding** +- Do NOT continue to Step 5 until the user confirms + +## Step 5: Fix the Bug + +- Make the **minimum change** needed to fix the bug +- Do not refactor, clean up, or "improve" surrounding code +- Do not change the test + +## Step 6: Run the Test — Confirm It PASSES + +- Run the new test again +- **Verify it PASSES** +- If it fails: go back to Step 5 and adjust the fix +- **Repeat until the test passes** + +## Step 7: Run the Full Test Suite + +- Run `make test` to make sure nothing else broke (fail-fast + coverage threshold per CLAUDE.md) +- If other tests fail: fix the regression without breaking the new test +- Report the final result to the user + +## Rules + +- NEVER fix the bug before the failing test is written and confirmed +- NEVER skip asking the user to acknowledge the test failure +- NEVER modify the test to make it pass — modify the source code +- If you cannot write a test for the bug, explain why and ask the user how to proceed +- Keep the fix minimal — one bug, one fix, one test diff --git a/.claude/skills/spec-check/SKILL.md b/.claude/skills/spec-check/SKILL.md new file mode 100644 index 0000000..bf296df --- /dev/null +++ b/.claude/skills/spec-check/SKILL.md @@ -0,0 +1,331 @@ +--- +name: spec-check +description: Audit spec/plan documents against the codebase. Ensures every spec section has implementing code, tests, and matching logic. Use when the user says "check specs", "spec audit", or "verify specs". +argument-hint: "[optional spec ID or filename filter]" +--- + + +# spec-check + +> **Portable skill.** This skill adapts to the current repository. The agent MUST inspect the repo structure and use judgment to apply these instructions appropriately. + +Audit spec/plan documents against the codebase. Ensures every spec section has implementing code, tests, and that the code logic matches the spec. + +In Diffy, the primary spec lives at [docs/specs/spec.md](../../../docs/specs/spec.md) and the live plan/TODO list at [docs/plans/plan.md](../../../docs/plans/plan.md). + +## Arguments + +- `$ARGUMENTS` — optional spec name or ID to check (e.g., `AUTH-TOKEN-VERIFY` or `repo-standards`). If empty, check ALL specs. Spec IDs are descriptive slugs, NEVER numbered (see Step 1). + +## Instructions + +Follow these steps exactly. Be strict and pedantic. Stop on the first failure. + +--- + +### Step 1: Validate spec ID structure + +Before checking code/test references, verify that the specs themselves are well-formed. + +1. Find all spec documents (see locations in Step 2). +2. Extract every section ID using the regex `\[([A-Z][A-Z0-9]*(-[A-Z0-9]+)+)\]`. +3. **Flag invalid IDs:** + - Numbered IDs (`[SPEC-001]`, `[REQ-003]`, `[CI-004]`) — must be renamed to descriptive hierarchical slugs. + - Single-word IDs (`[TIMEOUT]`) — must have a group prefix. + - IDs with trailing numbers (`[FEAT-AUTH-01]`) — the number is meaningless, remove it. +4. **Check group clustering:** The first word of each ID is its group. All sections in the same group MUST appear together (adjacent) in the document. If they're scattered, flag it. +5. **Check for missing IDs:** Any heading that defines a requirement or behavior should have an ID. Flag headings in spec files that look like they define behavior but lack an ID. + +If any ID violations are found, report them all and **STOP**: +``` +SPEC ID VIOLATIONS: + +- docs/specs/AUTH-SPEC.md line 12: [SPEC-001] → rename to descriptive ID (e.g., [AUTH-LOGIN]) +- docs/specs/AUTH-SPEC.md line 30: [AUTH-TOKEN-VERIFY] and [AUTH-LOGIN] are not adjacent (scattered group) +- docs/specs/CI-SPEC.md line 5: "## Coverage thresholds" has no spec ID + +Fix spec IDs first, then re-run spec-check. +``` + +If all IDs are valid, proceed to Step 2. + +--- + +### Step 2: Find all spec/plan documents + +Search for markdown files that contain spec sections with IDs. Look in these locations: + +- `docs/*.md` +- `docs/**/*.md` +- `SPEC.md` +- `PLAN.md` +- `specs/*.md` + +Use Glob to find candidate files, then use Grep to confirm they contain spec IDs. + +**Spec ID patterns** — IDs appear in square brackets, typically at the start of a heading or section line. Match this regex pattern: + +``` +\[([A-Z][A-Z0-9]*(-[A-Z0-9]+)+)\] +``` + +Spec IDs are **hierarchical descriptive slugs, NEVER numbered.** The format is `[GROUP-TOPIC]` or `[GROUP-TOPIC-DETAIL]`. The first word is the **group** — all sections sharing the same group MUST appear together in the spec's table of contents. IDs are uppercase, hyphen-separated, unique across the repo, and MUST NOT contain sequential numbers. + +The hierarchy depth varies by repo: two words for simple repos (`[AUTH-LOGIN]`), three for most (`[AUTH-TOKEN-VERIFY]`), four for complex domains (`[AUTH-OAUTH-REFRESH-FLOW]`). The hierarchy mirrors the spec document's heading structure. + +Examples of valid spec IDs (note how groups cluster): +- `[AUTH-LOGIN]`, `[AUTH-TOKEN-VERIFY]`, `[AUTH-TOKEN-REFRESH]` — all in the AUTH group +- `[CI-TIMEOUT]`, `[CI-LINT]`, `[CI-RELEASE]` — all in the CI group +- `[LINT-ESLINT]`, `[LINT-RUFF]` — all in the LINT group +- `[FEAT-DARK-MODE]`, `[FEAT-SEARCH-FILTER]` — all in the FEAT group + +Examples of INVALID spec IDs: +- `[SPEC-001]` — numbered, meaningless +- `[FEAT-AUTH-01]` — trailing number +- `[REQ-003]` — sequential index, no group hierarchy +- `[CI-004]` — numbered, tells the reader nothing +- `[TIMEOUT]` — no group prefix, ungrouped + +For each file, extract every spec ID and its associated section title (the heading text after the ID) and the full section content (everything until the next heading of equal or higher level). + +--- + +### Step 3: Filter specs + +- If `$ARGUMENTS` is non-empty, filter the discovered specs: + - If it matches a spec ID exactly (e.g., `AUTH-TOKEN-VERIFY`), check only that spec. + - If it matches a partial name (e.g., `repo-standards`), check all specs in files whose path contains that string. +- If `$ARGUMENTS` is empty, process ALL discovered specs. + +If filtering produces zero specs, report an error: +``` +ERROR: No specs found matching "$ARGUMENTS". Discovered spec files: [list them] +``` + +--- + +### Step 4: Check each spec section + +For EACH spec section that has an ID, perform checks A, B, and C below. **Stop on the first failure.** + +#### Check A: Code references the spec ID + +Search the entire codebase for the spec ID string, **excluding** these directories: +- `docs/` +- `node_modules/` +- `.git/` +- `*.md` files (markdown is docs, not code) + +Use Grep with the literal spec ID (e.g., `[AUTH-TOKEN-VERIFY]`) to find references in code files. + +Code files should contain comments referencing the spec ID. The search must catch **all** comment styles across languages: + +**C-style `//` comments** (JavaScript, TypeScript, Rust, C#, F#, Java, Kotlin, Go, Swift, Dart): +- `// Implements [AUTH-TOKEN-VERIFY]` +- `// [AUTH-TOKEN-VERIFY]` +- `// Tests [AUTH-TOKEN-VERIFY]` (also counts as a code reference) +- `/// Implements [AUTH-TOKEN-VERIFY]` (doc comments) + +**Hash `#` comments** (Python, Ruby, Shell/Bash, YAML, TOML): +- `# Implements [AUTH-TOKEN-VERIFY]` +- `# [AUTH-TOKEN-VERIFY]` +- `# Tests [AUTH-TOKEN-VERIFY]` + +**HTML/XML comments** (HTML, CSS, SVG, XML, XAML, JSX templates): +- `` +- `` + +**ML-style comments** (F#, OCaml): +- `(* Implements [AUTH-TOKEN-VERIFY] *)` + +**Lua comments:** +- `-- Implements [AUTH-TOKEN-VERIFY]` + +**CSS comments:** +- `/* Implements [AUTH-TOKEN-VERIFY] */` + +**The key rule:** any comment in any language containing the exact spec ID string (e.g., `[AUTH-TOKEN-VERIFY]`) counts as a valid code reference. The Grep search uses the literal spec ID string, so it naturally matches all comment styles. Do NOT restrict the search to specific comment prefixes — just search for the spec ID string itself. + +**If NO code files reference the spec ID:** + +``` +SPEC VIOLATION: [AUTH-TOKEN-VERIFY] "Section Title" has no implementing code. + +Every spec section must have at least one code file that references it via a comment +containing the spec ID (e.g., `// Implements [AUTH-TOKEN-VERIFY]`). + +ACTION REQUIRED: Add a comment referencing [AUTH-TOKEN-VERIFY] in the file(s) that implement +this spec section, then re-run spec-check. +``` + +**STOP HERE. Do not continue to other checks.** + +#### Check B: Tests reference the spec ID + +Search test files for the spec ID. Test files are found in: +- `test/` +- `tests/` +- `**/*.test.*` +- `**/*.spec.*` +- `**/*_test.*` +- `**/test_*.*` +- `**/*Tests.*` +- `**/*Test.*` + +Use Grep to search these locations for the literal spec ID string. + +Tests should contain the spec ID in comments, test names, or annotations. The search must catch **all** test frameworks across languages: + +**JavaScript/TypeScript** (Jest, Mocha, Vitest, Playwright): +- `// Tests [AUTH-TOKEN-VERIFY]` +- `describe('[AUTH-TOKEN-VERIFY] Authentication flow', () => ...)` +- `test('[AUTH-TOKEN-VERIFY] should verify token', () => ...)` +- `it('[AUTH-TOKEN-VERIFY] verifies token', () => ...)` + +**Python** (pytest, unittest): +- `# Tests [AUTH-TOKEN-VERIFY]` +- `def test_auth_token_verify_flow():` +- `class TestAuthTokenVerify:` + +**Rust:** +- `// Tests [AUTH-TOKEN-VERIFY]` +- `#[test] // Tests [AUTH-TOKEN-VERIFY]` + +**C#** (xUnit, NUnit, MSTest): +- `// Tests [AUTH-TOKEN-VERIFY]` +- `[Fact] // Tests [AUTH-TOKEN-VERIFY]` +- `[Test] // Tests [AUTH-TOKEN-VERIFY]` +- `[TestMethod] // Tests [AUTH-TOKEN-VERIFY]` + +**F#** (xUnit, Expecto): +- `// Tests [AUTH-TOKEN-VERIFY]` +- `[] // Tests [AUTH-TOKEN-VERIFY]` +- `testCase "[AUTH-TOKEN-VERIFY] description" <| fun () ->` + +**Java/Kotlin** (JUnit, TestNG): +- `// Tests [AUTH-TOKEN-VERIFY]` +- `@Test // Tests [AUTH-TOKEN-VERIFY]` + +**Go:** +- `// Tests [AUTH-TOKEN-VERIFY]` +- `func TestAuthTokenVerify(t *testing.T) { // Tests [AUTH-TOKEN-VERIFY]` + +**Swift** (XCTest): +- `// Tests [AUTH-TOKEN-VERIFY]` +- `func testAuthTokenVerify() { // Tests [AUTH-TOKEN-VERIFY]` + +**Dart** (flutter_test): +- `// Tests [AUTH-TOKEN-VERIFY]` +- `test('[AUTH-TOKEN-VERIFY] description', () { ... });` + +**Ruby** (RSpec, Minitest): +- `# Tests [AUTH-TOKEN-VERIFY]` +- `describe '[AUTH-TOKEN-VERIFY] Authentication' do` +- `it '[AUTH-TOKEN-VERIFY] verifies token' do` + +**Shell** (bats, shunit2): +- `# Tests [AUTH-TOKEN-VERIFY]` +- `@test "[AUTH-TOKEN-VERIFY] description" {` + +**The key rule:** same as Check A — search for the literal spec ID string in test files. Any occurrence of the exact spec ID in a test file counts. Do NOT restrict to specific patterns — just search for the spec ID string itself. + +**If NO test files reference the spec ID:** + +``` +SPEC VIOLATION: [AUTH-TOKEN-VERIFY] "Section Title" has no tests. + +Every spec section must have corresponding tests that reference the spec ID. + +ACTION REQUIRED: Add tests for [AUTH-TOKEN-VERIFY] with a comment or test name containing +the spec ID, then re-run spec-check. +``` + +**STOP HERE. Do not continue to other checks.** + +#### Check C: Code logic matches the spec + +This is the most critical check. You must: + +1. **Read the spec section content carefully.** Understand exactly what behavior, logic, ordering, conditions, and constraints the spec describes. + +2. **Read the implementing code.** Use the references found in Check A to locate the implementing files. Read the relevant functions/sections. + +3. **Compare spec vs. code.** Be SENSITIVE and PEDANTIC. Check for: + - **Ordering violations** — If the spec says A happens before B, the code must do A before B. + - **Missing conditions** — If the spec says "only when X", the code must have that condition. + - **Extra behavior** — If the code does something the spec doesn't mention, flag it only if it contradicts the spec. + - **Wrong logic** — If the spec says "greater than" but code uses "greater than or equal", that's a violation. + - **Missing steps** — If the spec describes 5 steps but code only implements 3, that's a violation. + - **Wrong defaults** — If the spec says "default to X" but code defaults to Y, that's a violation. + +4. **If the code deviates from the spec**, report a detailed error: + +``` +SPEC VIOLATION: [AUTH-TOKEN-VERIFY] Code does not match spec. + +SPEC SAYS: +> "The authentication flow must verify the token expiry before checking permissions" +> (from docs/specs/AUTH-SPEC.md, line 42) + +CODE DOES: +> `if (hasPermission(user)) { verifyToken(token); }` (src/auth.ts:42) + +DEVIATION: The code checks permissions BEFORE verifying token expiry. +The spec explicitly requires token expiry verification FIRST. + +ACTION REQUIRED: Reorder the logic in src/auth.ts to verify token expiry +before checking permissions, as specified in [AUTH-TOKEN-VERIFY]. +``` + +**STOP HERE. Do not continue to other specs.** + +5. **If the code matches the spec**, this check passes. Move to the next spec. + +--- + +### Step 5: Report results + +#### On failure (any check fails): + +Output ONLY the first violation found. Use the exact error format shown above. Do not summarize other specs. Do not offer to fix the code. Just report the violation. + +End with: +``` +spec-check FAILED. Fix the violation above and re-run. +``` + +#### On success (all specs pass): + +Output a summary table: + +``` +spec-check PASSED. All specs verified. + +| Spec ID | Title | Code References | Test References | Logic Match | +|----------------|--------------------------|-----------------|-----------------|-------------| +| [AUTH-TOKEN-VERIFY] | Authentication flow | src/auth.ts | tests/auth.test.ts | PASS | +| [RATE-LIMIT-CONFIG] | Rate limiting | src/rate.ts | tests/rate.test.ts | PASS | +| ... | ... | ... | ... | ... | + +Checked N spec sections across M files. All have implementing code, tests, and matching logic. +``` + +--- + +## Search strategy summary + +1. **Validate spec IDs:** Check all IDs are hierarchical, descriptive, grouped, and non-numbered +2. **Find spec files:** Glob for `docs/**/*.md`, `SPEC.md`, `PLAN.md`, `specs/**/*.md` +3. **Extract spec IDs:** Grep for `\[[A-Z][A-Z0-9]*(-[A-Z0-9]+)+\]` in those files +4. **Find code refs:** Grep for the literal spec ID in all files, excluding `docs/`, `node_modules/`, `.git/`, `*.md` +5. **Find test refs:** Grep for the literal spec ID in test directories and test file patterns +6. **Read and compare:** Read the spec section content and the implementing code, compare logic + +## Key principles + +- **Fail fast.** Stop on the first violation. One fix at a time. +- **Be pedantic.** If the spec says it, the code must do it. No "close enough". +- **Quote everything.** Always quote the spec text and the code in error messages so the developer sees exactly what's wrong. +- **Be actionable.** Every error must tell the developer what file to change and what to do. +- **Exclude docs from code search.** Markdown files are documentation, not implementation. Only search actual code files for spec references. +- **No numbered IDs.** Spec IDs are hierarchical descriptive slugs (`[AUTH-TOKEN-VERIFY]`), NEVER sequential numbers (`[SPEC-001]`). The first word is the group — sections sharing a group must be adjacent in the TOC. If you encounter numbered or ungrouped IDs, flag them as a violation. diff --git a/.claude/skills/submit-pr/SKILL.md b/.claude/skills/submit-pr/SKILL.md new file mode 100644 index 0000000..63d336d --- /dev/null +++ b/.claude/skills/submit-pr/SKILL.md @@ -0,0 +1,39 @@ +--- +name: submit-pr +description: Creates a pull request with a well-structured description after verifying CI passes. Use when the user asks to submit, create, or open a pull request. +disable-model-invocation: true +--- + + +# Submit PR + +Create a pull request for the current branch with a well-structured description. + +## Steps + +*NOTE: if you already ran make ci in this session and it passed, you can skip step 1.* + +1. Run `make ci` — must pass completely before creating PR +2. **Generate the diff against main.** Run `git diff main...HEAD > /tmp/pr-diff.txt` to capture the full diff between the current branch and the head of main. This is the ONLY source of truth for what the PR contains. **Warning:** the diff can be very large. If the diff file exceeds context limits, process it in chunks (e.g., read sections with `head`/`tail` or split by file) rather than trying to load it all at once. +3. **Derive the PR title and description SOLELY from the diff.** Read the diff output and summarize what changed. Ignore commit messages, branch names, and any other metadata — only the actual code/content diff matters. +4. Write PR body using the template in `.github/pull_request_template.md` +5. Fill in (based on the diff analysis from step 3): + - TLDR: one sentence + - What Was Added: new files, features, deps + - What Was Changed/Deleted: modified behaviour + - How Tests Prove It Works: specific test names or output + - Spec/Doc Changes: if any + - Breaking Changes: yes/no + description +6. Use `gh pr create` with the filled template + +## Rules + +- Never create a PR if `make ci` fails +- PR description must be specific and tight — no vague placeholders +- Link to the relevant GitHub issue if one exists + +## Success criteria + +- `make ci` passed +- PR created with `gh pr create` +- PR URL returned to user diff --git a/.claude/skills/upgrade-packages/SKILL.md b/.claude/skills/upgrade-packages/SKILL.md new file mode 100644 index 0000000..05a5043 --- /dev/null +++ b/.claude/skills/upgrade-packages/SKILL.md @@ -0,0 +1,95 @@ +--- +name: upgrade-packages +description: Upgrade all dependencies/packages to their latest versions. Diffy is TypeScript/Node — use when the user says "upgrade packages", "update dependencies", "bump versions", "update packages", or "upgrade deps". +argument-hint: "[--check-only] [--major] [package-name]" +--- + + +# Upgrade Packages + +Upgrade Diffy's npm dependencies to their latest compatible (or latest major, if `--major`) versions. + +## Arguments + +- `--check-only` — List outdated packages without upgrading. Stop after Step 2. +- `--major` — Include major version bumps (breaking changes). Without this flag, stay within semver-compatible ranges. +- Any other argument is treated as a specific package name to upgrade (instead of all packages). + +## Step 1 — Detect package manager + +Diffy uses **npm** (package manifest: `package.json`, lockfile: `package-lock.json`). If for some reason the repo has migrated to yarn or pnpm, adapt accordingly (`yarn outdated`/`yarn up` or `pnpm outdated`/`pnpm update`). + +If `package.json` is missing, stop and tell the user. + +## Step 2 — List outdated packages + +Run BEFORE upgrading anything. Show the user what will change. + +```bash +npm outdated +``` + +**Read the docs:** https://docs.npmjs.com/cli/v10/commands/npm-update + +If `--check-only` was passed, **stop here** and report the outdated list. + +## Step 3 — Read the official upgrade docs + +**Before running any upgrade command, you MUST fetch and read the official documentation URL above.** Use WebFetch to retrieve the page. This ensures you use the correct flags and understand the behavior. Do not guess at flags or options from memory. + +## Step 4 — Upgrade packages + +Run the upgrade. If a specific package name was given as an argument, upgrade only that package. + +```bash +npm update # semver-compatible (within package.json ranges) +# --major flag: +npx npm-check-updates -u && npm install # bump package.json to latest majors +``` + +### Diffy-specific cautions + +- `@types/vscode` and `@types/node` must remain compatible with the `engines.vscode` minimum declared in `package.json`. A major bump that requires a newer VSCode runtime is a breaking change to consumers. +- `@vscode/test-electron`, `mocha`, `c8`, and `vsce` (`@vscode/vsce`) are tightly coupled to the extension test/release pipeline — review their changelogs before bumping. +- `typescript`, `eslint`, `typescript-eslint`, `prettier` — major bumps frequently change rule defaults; rerun `make lint` and `make fmt CHECK=1` after the upgrade. + +## Step 5 — Verify the upgrade + +After upgrading, run the project's build and test suite to confirm nothing broke: + +```bash +make ci +``` + +If tests fail: +1. Read the failure output carefully +2. Check the changelog / migration guide for the upgraded packages (fetch the release notes URL if available) +3. Fix breaking changes in the code +4. Re-run tests +5. If stuck after 3 attempts on the same failure, report it to the user with the error details and the package that caused it + +## Step 6 — Report + +Provide a summary: + +- Packages upgraded (old version -> new version) +- Packages skipped (and why, e.g., major version bump without `--major` flag) +- Build/test result after upgrade +- Any breaking changes that were fixed +- Any packages that could not be upgraded (with error details) + +## Rules + +- **Always list outdated packages first** before upgrading anything +- **Always read the official docs** for the package manager before running upgrade commands +- **Always run `make ci` after upgrading** to catch breakage immediately +- **Never remove packages** unless they were explicitly deprecated and replaced +- **Never downgrade packages** unless rolling back a broken upgrade +- **Never modify `package-lock.json` manually** — let npm regenerate it +- **Commit nothing** — leave changes in the working tree for the user to review + +## Success criteria + +- All outdated packages upgraded to latest compatible (or latest major if `--major`) +- `make ci` passes +- User has a clear summary of what changed diff --git a/.claude/skills/website-audit/SKILL.md b/.claude/skills/website-audit/SKILL.md new file mode 100644 index 0000000..520714e --- /dev/null +++ b/.claude/skills/website-audit/SKILL.md @@ -0,0 +1,180 @@ +--- +name: website-audit +description: Audits a website for SEO, AI search performance, structured data, mobile usability, broken links, and social media cards. Fixes issues found. Use when the user mentions "audit website", "SEO", "fix search ranking", "AI search", "structured data", "social media cards", or "website performance". +--- + + +# Website Audit + +Performs a comprehensive website audit and fixes issues affecting search visibility and AI discoverability. + +Copy this checklist and track your progress: + +``` +Audit Progress: +- [ ] Step 1: Read guidelines +- [ ] Step 2: Audit AI search readiness +- [ ] Step 3: Audit SEO and keywords +- [ ] Step 4: Audit crawling and indexing +- [ ] Step 5: Audit broken links and canonicalization +- [ ] Step 6: Audit mobile usability +- [ ] Step 7: Audit structured data +- [ ] Step 8: Audit social media cards +- [ ] Step 9: Audit For Unsubstantiated Claims +- [ ] Step 10: Audit Design Compliance +- [ ] Step 11: Test with Playwright +- [ ] Step 12: Report findings +``` + +- Check the outputted HTML/CSS/JavaScript AFTER the website is generated by the static content generator. - Don't just check the static content before the website is generated. +- Fix issues at the core where the static content templates are stored - not in the outputted HTML (e.g. _site) +- Never manually edit the generated website content directly +- ENSURE THE FOOTER HAS A copyright link to nimblesite.co + +## Step 1 — Read guidelines + +Fetch and read each of these before auditing. These are the authoritative references for every step that follows. + +- [Google's guidance on using generative AI content](https://developers.google.com/search/docs/fundamentals/using-gen-ai-content) +- [Top ways to ensure content performs well in Google's AI experiences](https://developers.google.com/search/blog/2025/05/succeeding-in-ai-search) +- [SEO Starter Guide](https://developers.google.com/search/docs/fundamentals/seo-starter-guide) + +If the repo has a business plan doc, take it into account + +Identify the website source files in the repo. Determine the framework (static site generator, Next.js, Hugo, etc.) so you know where to find templates, metadata, and content. + +## Step 2 — Audit AI search readiness + +Apply the guidance from the AI search article. Check: + +1. **Content quality** — Is content original, expert-level, and comprehensive? Flag thin or duplicated pages. +2. **Clear structure** — Do pages use descriptive headings, lists, and concise answers to likely questions? +3. **Entity clarity** — Are key terms, products, and concepts defined clearly so AI can extract them? +4. **Freshness signals** — Are dates, update timestamps, and authorship present? + +Fix issues directly in the source files. For each fix, note what changed and why. + +## Step 3 — Audit SEO and keywords + +1. Search [Google Trends](https://trends.google.com/home) for trending keywords related to the website's content. +2. Review each page's ``, `<meta name="description">`, and `<h1>` tags. +3. Check for keyword opportunities — can trending terms be naturally inserted into headings, descriptions, or body content? +4. Verify each page has a unique, descriptive title (50-60 chars) and meta description (150-160 chars). +5. Check image `alt` attributes describe the image content and include relevant keywords where natural. + +Apply the [SEO Starter Guide](https://developers.google.com/search/docs/fundamentals/seo-starter-guide) principles. Fix issues directly. + +## Step 4 — Audit crawling and indexing + +Reference: [Overview of crawling and indexing topics](https://developers.google.com/search/docs/crawling-indexing) + +1. **robots.txt** — Locate and review it. Verify it doesn't block important pages. Reference: [robots.txt spec](https://developers.google.com/search/docs/crawling-indexing/robots-txt) +2. **Sitemap** — Locate the sitemap (or sitemap index). Verify all important pages are listed and no dead URLs are included. Reference: [Sitemap guidelines](https://developers.google.com/search/docs/crawling-indexing/sitemaps/large-sitemaps) +3. **Meta robots tags** — Check for unintended `noindex` or `nofollow` directives on pages that should be indexed. + +Note: robots.txt and sitemaps are often auto-generated. If so, check the generator config rather than the output file. + +## Step 5 — Audit broken links and canonicalization + +Reference: [What is canonicalization](https://developers.google.com/search/docs/crawling-indexing/canonicalization) + +1. Check all internal links resolve to valid pages (no 404s). +2. Verify `<link rel="canonical">` tags are present and point to the correct URL. +3. Check for duplicate content accessible via multiple URLs (with/without trailing slash, www vs non-www). +4. Verify redirects use 301 (permanent) not 302 (temporary) where appropriate. + +## Step 6 — Audit mobile usability + +Reference: [Mobile-first indexing best practices](https://developers.google.com/search/docs/crawling-indexing/mobile/mobile-sites-mobile-first-indexing) + +1. Verify the `<meta name="viewport">` tag is present and correctly configured. +2. Check that content is identical between mobile and desktop (mobile-first indexing requires this). +3. Verify touch targets are adequately sized (min 48x48px). +4. Check font sizes are readable without zooming (min 16px body text). + +## Step 7 — Audit structured data + +Reference: [Structured data guidelines](https://developers.google.com/search/docs/appearance/structured-data/sd-policies) + +1. Check for existing JSON-LD `<script type="application/ld+json">` blocks. +2. Verify the structured data matches the page content (no misleading markup). +3. Add missing structured data where appropriate: + - **Organization/Person** on the homepage + - **Article/BlogPosting** on blog posts (with author, datePublished, dateModified) + - **BreadcrumbList** for navigation + - **FAQ** for pages with question/answer content +4. Validate JSON-LD syntax is correct. + +## Step 8 — Audit social media cards + +Reference: [Implementing Social Media Preview Cards](https://documentation.platformos.com/use-cases/implementing-social-media-preview-cards) + +Check every page template includes: + +**Open Graph (Facebook/LinkedIn):** +- `og:title`, `og:description`, `og:image`, `og:url`, `og:type` + +**Twitter Card:** +- `twitter:card`, `twitter:title`, `twitter:description`, `twitter:image` + +Verify `og:image` dimensions are at least 1200x630px. Fix missing or incomplete tags. + +## Step 9 - Audit For Unsubstantiated Claims + +Ensure that all claims are backed up with a link to a reputable source. As an example, this claim isn't valid as content unless it links to an authority that found this through research + +> Research shows teams with strong DevEx perform 4-5x better across speed, quality, and engagement + +Search for the authoritative URL and add a link to the URL. If it is not available, change the claim to something that can be substatiated. + +## Step 10 — Audit Design Compliance + +Read the design system docs and view the design screens in the designsystem folder. + +## Step 11 — Test with Playwright + +Build and run the website locally using `make website-run` (or the project's equivalent dev server command). + +**Desktop tests (1280x720):** + +1. Navigate to the homepage — take a screenshot. +2. Navigate to each major section — verify pages load without errors. +3. Check the browser console for JavaScript errors. +4. Verify all navigation links work. + +**Mobile tests (375x667, iPhone SE):** + +1. Resize the browser to mobile dimensions. +2. Navigate to the homepage — take a screenshot. +3. Verify the layout is responsive (no horizontal overflow, readable text). +4. Test navigation menu (hamburger menu if applicable). + +If any page fails to load or has console errors, fix the issue and retest. + +## Step 12 — Report findings + +Summarize the audit results: + +``` +## Website Audit Report + +### Fixed +- [List each issue fixed with file and line reference] + +### Warnings (manual review needed) +- [Issues that need human judgment] + +### Passed +- [Areas that passed audit with no issues] + +### Screenshots +- [Reference Playwright screenshots taken] +``` + +## Rules + +- **Fix issues directly** — don't just report them. Only flag issues as warnings when they require human judgment (e.g., content tone, keyword selection). +- **One step at a time** — complete each step before moving to the next. +- **Preserve existing content** — improve structure and metadata without rewriting the author's voice. +- **No keyword stuffing** — keywords must read naturally in context. +- **Respect the framework** — edit templates/configs, not generated output files. diff --git a/.clinerules/00-read-instructions.md b/.clinerules/00-read-instructions.md new file mode 100644 index 0000000..48aaceb --- /dev/null +++ b/.clinerules/00-read-instructions.md @@ -0,0 +1,2 @@ +<!-- agent-pmo:74cf183 --> +@CLAUDE.md diff --git a/.cursorrules b/.cursorrules new file mode 100644 index 0000000..043e911 --- /dev/null +++ b/.cursorrules @@ -0,0 +1,2 @@ +# agent-pmo:74cf183 +@CLAUDE.md diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json new file mode 100644 index 0000000..68df27c --- /dev/null +++ b/.devcontainer/devcontainer.json @@ -0,0 +1,25 @@ +{ + "_agent_pmo": "74cf183", + "name": "Diffy (Node.js)", + "image": "mcr.microsoft.com/devcontainers/typescript-node:1-20", + "remoteUser": "vscode", + "postCreateCommand": "make setup", + "customizations": { + "vscode": { + "extensions": [ + "dbaeumer.vscode-eslint", + "esbenp.prettier-vscode", + "usernamehw.errorlens", + "ms-vscode.vscode-typescript-next" + ], + "settings": { + "editor.formatOnSave": true, + "editor.defaultFormatter": "esbenp.prettier-vscode", + "eslint.validate": ["typescript", "javascript"], + "editor.codeActionsOnSave": { + "source.fixAll.eslint": "explicit" + } + } + } + } +} diff --git a/.github/copilot-instructions.md b/.github/copilot-instructions.md new file mode 100644 index 0000000..48aaceb --- /dev/null +++ b/.github/copilot-instructions.md @@ -0,0 +1,2 @@ +<!-- agent-pmo:74cf183 --> +@CLAUDE.md diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md new file mode 100644 index 0000000..9734233 --- /dev/null +++ b/.github/pull_request_template.md @@ -0,0 +1,10 @@ +<!-- agent-pmo:74cf183 --> +## TLDR +<!-- One sentence: what does this PR do? --> + +## Details +<!-- New functionality, new files, new dependencies. What changed? --> + +## How Do The Automated Tests Prove It Works? +<!-- Name specific tests or describe what the test output demonstrates. --> +<!-- "Tests pass" is not acceptable. Be specific. --> diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..ba8fe3b --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,67 @@ +# agent-pmo:74cf183 +name: CI + +on: + pull_request: + branches: [main] + push: + branches: [main] + +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + +jobs: + ci: + name: CI + runs-on: ${{ matrix.os }} + timeout-minutes: 10 + strategy: + fail-fast: false + matrix: + os: [ubuntu-latest, macos-latest, windows-latest] + steps: + - uses: actions/checkout@v4 + + - uses: actions/setup-node@v4 + with: + node-version: '20' + cache: 'npm' + + - run: npm ci + + # E2E suite runs under xvfb on Linux so @vscode/test-electron can launch. + - name: Format check + run: make fmt CHECK=1 + + - name: Lint + run: make lint + + - name: Test (Linux, xvfb) + if: runner.os == 'Linux' + run: xvfb-run -a make test + + - name: Test (macOS / Windows) + if: runner.os != 'Linux' + run: make test + + - name: Upload coverage + uses: actions/upload-artifact@v4 + if: always() + with: + name: coverage-report-${{ matrix.os }} + path: | + coverage/ + lcov.info + retention-days: 7 + + - name: Build + run: make build + + - name: Upload build artifacts + uses: actions/upload-artifact@v4 + with: + name: build-${{ matrix.os }} + path: | + out/ + retention-days: 7 diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 0000000..887909a --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,123 @@ +# agent-pmo:74cf183 +name: Release + +on: + push: + tags: + - 'v*' + +permissions: + contents: write + +jobs: + version: + name: Extract release version + runs-on: ubuntu-latest + timeout-minutes: 10 + outputs: + version: ${{ steps.extract.outputs.version }} + source_sha: ${{ steps.extract.outputs.source_sha }} + steps: + - name: Extract version from tag + id: extract + run: | + echo "version=${GITHUB_REF_NAME#v}" >> "$GITHUB_OUTPUT" + echo "source_sha=${GITHUB_SHA}" >> "$GITHUB_OUTPUT" + + build-vsix: + name: Build VSIX (${{ matrix.artifact_name }}) + needs: version + strategy: + fail-fast: false + matrix: + include: + - os: ubuntu-latest + vsce_target: linux-x64 + artifact_name: linux-x64 + - os: ubuntu-latest + vsce_target: linux-arm64 + artifact_name: linux-arm64 + - os: macos-latest + vsce_target: darwin-x64 + artifact_name: darwin-x64 + - os: macos-latest + vsce_target: darwin-arm64 + artifact_name: darwin-arm64 + - os: windows-latest + vsce_target: win32-x64 + artifact_name: win32-x64 + - os: windows-latest + vsce_target: win32-arm64 + artifact_name: win32-arm64 + runs-on: ${{ matrix.os }} + timeout-minutes: 10 + steps: + - uses: actions/checkout@v4 + with: + ref: ${{ needs.version.outputs.source_sha }} + + # VSIX packaging requires Node 22.x per [CI-VSIX-PLATFORM]. + - uses: actions/setup-node@v4 + with: + node-version: '22.x' + cache: 'npm' + + - name: Stamp version + run: node scripts/stamp-release-version.mjs "${{ needs.version.outputs.version }}" + + - name: Install dependencies + run: npm ci + + - name: Build + run: npm run build + + - name: Package VSIX + run: npx vsce package --target ${{ matrix.vsce_target }} --out diffy-${{ needs.version.outputs.version }}-${{ matrix.vsce_target }}.vsix + + - name: Upload VSIX + uses: actions/upload-artifact@v4 + with: + name: vsix-${{ matrix.artifact_name }} + path: diffy-${{ needs.version.outputs.version }}-${{ matrix.vsce_target }}.vsix + + release: + name: Create release + needs: [version, build-vsix] + runs-on: ubuntu-latest + timeout-minutes: 10 + steps: + - uses: actions/checkout@v4 + with: + ref: ${{ needs.version.outputs.source_sha }} + - uses: actions/download-artifact@v4 + with: + path: artifacts/ + + - name: Create GitHub release + uses: softprops/action-gh-release@v2 + with: + files: artifacts/**/*.vsix + generate_release_notes: true + + publish: + name: Publish to VS Code Marketplace + needs: [version, release] + runs-on: ubuntu-latest + timeout-minutes: 10 + steps: + - uses: actions/checkout@v4 + with: + ref: ${{ needs.version.outputs.source_sha }} + - uses: actions/download-artifact@v4 + with: + path: artifacts/ + + - uses: actions/setup-node@v4 + with: + node-version: '22.x' + + # Publish all platform-specific VSIX files in a single vsce invocation. + - name: Publish to Marketplace + run: npx vsce publish --packagePath $(find artifacts -name '*.vsix') + env: + VSCE_PAT: ${{ secrets.VSCE_PAT }} diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..03c5ce6 --- /dev/null +++ b/.gitignore @@ -0,0 +1,77 @@ +# agent-pmo:74cf183 +# ============================================================================= +# UNIVERSAL — applies to every repo in this portfolio +# ============================================================================= + +# OS +.DS_Store +.DS_Store? +._* +.Spotlight-V100 +.Trashes +Thumbs.db +ehthumbs.db +Desktop.ini + +# Editor swap/temp files only — never ignore the config dirs themselves +*.swp +*.swo +*~ +*.sublime-project +*.sublime-workspace + +# IDE config dirs MUST be committed — they carry VS Code settings, extensions, +# launch configs, and title-bar colorization that the team shares. +# AI agent config dirs MUST also be committed — they carry skills and instructions. +# DO NOT add .vscode/, .idea/, .claude/, .codex/, .agents/, .cline/, .opencode/, +# .github/copilot-instructions.md, .cursorrules, .windsurfrules, or similar. + +# Portfolio-wide tooling +.too_many_cooks/ +.commandtree/ +.playwright-mcp/ +.deslop-cache/ +.ghissues/ +coordination/ +logs/ +nohup.out + +# Coverage artifacts (all languages) +coverage/ +lcov.info +htmlcov/ +.coverage +coverage.xml +coverage.out +coverage-summary.json +TestResults/ + +# Secrets / local overrides +.env +.env.local +.env.*.local +*.local +*.secret +*.pem +*.key +!*.pub.key + +# Temporary +tmp/ +temp/ +scratch/ + +# ============================================================================= +# TypeScript / Node.js / VSCode extension +# ============================================================================= +node_modules/ +dist/ +out/ +build/ +*.vsix +*.tgz +.npm/ +.cache/ +.vscode-test/ +.vscode-test-web/ +.nyc_output/ diff --git a/.prettierrc.json b/.prettierrc.json new file mode 100644 index 0000000..19bbb42 --- /dev/null +++ b/.prettierrc.json @@ -0,0 +1,12 @@ +{ + "tabWidth": 2, + "useTabs": false, + "printWidth": 120, + "semi": true, + "singleQuote": false, + "trailingComma": "es5", + "bracketSpacing": true, + "arrowParens": "always", + "endOfLine": "lf", + "bracketSameLine": false +} diff --git a/.vscode/extensions.json b/.vscode/extensions.json new file mode 100644 index 0000000..ada5174 --- /dev/null +++ b/.vscode/extensions.json @@ -0,0 +1,11 @@ +{ + "_agent_pmo": "74cf183", + "recommendations": [ + "nimblesite.commandtree", + "nimblesite.too-many-cooks", + "nimblesite.typeDiagram", + "dbaeumer.vscode-eslint", + "esbenp.prettier-vscode", + "usernamehw.errorlens" + ] +} diff --git a/.windsurfrules b/.windsurfrules new file mode 100644 index 0000000..043e911 --- /dev/null +++ b/.windsurfrules @@ -0,0 +1,2 @@ +# agent-pmo:74cf183 +@CLAUDE.md diff --git a/AGENTS.md b/AGENTS.md new file mode 100644 index 0000000..48aaceb --- /dev/null +++ b/AGENTS.md @@ -0,0 +1,2 @@ +<!-- agent-pmo:74cf183 --> +@CLAUDE.md diff --git a/CLAUDE.md b/CLAUDE.md index 57b2736..a95d44e 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -1,5 +1,7 @@ # Diffy — Agent Instructions +<!-- agent-pmo:74cf183 --> + ⚠️ KILLING A VSCODE PROCESS — EVEN IN THE BROWSER — WILL BE MET WITH INSTANT, EXTREME VIOLENCE! > ⚠️ **TOKEN DISCIPLINE.** Check file size first. `Grep` over `Read`. Use `offset`/`limit`. diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..4604103 --- /dev/null +++ b/Makefile @@ -0,0 +1,107 @@ +# agent-pmo:74cf183 +# ============================================================================= +# Standard Makefile — Diffy (VSCode extension, TypeScript) +# Cross-platform: Linux, macOS, Windows (via GNU Make) +# ============================================================================= + +.PHONY: build test lint fmt clean ci setup package help + +# --------------------------------------------------------------------------- +# OS Detection +# --------------------------------------------------------------------------- +ifeq ($(OS),Windows_NT) + SHELL := powershell.exe + .SHELLFLAGS := -NoProfile -Command + RM = Remove-Item -Recurse -Force -ErrorAction SilentlyContinue + MKDIR = New-Item -ItemType Directory -Force + HOME ?= $(USERPROFILE) +else + RM = rm -rf + MKDIR = mkdir -p +endif + +# --------------------------------------------------------------------------- +# Coverage — single source of truth is coverage-thresholds.json +# See REPO-STANDARDS-SPEC [COVERAGE-THRESHOLDS-JSON]. +# --------------------------------------------------------------------------- +COVERAGE_THRESHOLDS_FILE := coverage-thresholds.json + +# ============================================================================= +# Standard Targets +# +# These 7 targets are portfolio-wide and identical across every repo. +# Do NOT add extra public targets here — put them in the Repo-Specific +# Targets section at the bottom of this file. +# See REPO-STANDARDS-SPEC [MAKE-TARGETS]. +# ============================================================================= + +## build: Compile TypeScript to out/ +build: + @echo "==> Building..." + npm run build + +## test: Fail-fast tests + coverage + threshold enforcement. +## See REPO-STANDARDS-SPEC [TEST-RULES] and [COVERAGE-THRESHOLDS-JSON]. +test: + @echo "==> Testing (fail-fast + coverage + threshold)..." + npm run test:coverage + $(MAKE) _coverage_check + +## lint: ESLint + tsc --noEmit (read-only). No formatting. +lint: + @echo "==> Linting..." + npm run lint + npm run typecheck + +## fmt: Prettier in-place. Pass CHECK=1 for read-only check (CI use). +fmt: + @echo "==> Formatting$(if $(CHECK), (check mode),)..." + npm run fmt$(if $(CHECK),:check,) + +## clean: Remove all build artifacts +clean: + @echo "==> Cleaning..." + $(RM) out dist coverage .vscode-test .nyc_output *.vsix + +## ci: lint + test + build (full CI simulation) +ci: lint test build + +## setup: Post-create dev environment setup (used by devcontainer) +setup: + @echo "==> Setting up development environment..." + npm ci + @echo "==> Setup complete. Run 'make ci' to validate." + +# --------------------------------------------------------------------------- +# Internal sub-recipes — underscore-prefixed, NOT in .PHONY, NOT public. +# --------------------------------------------------------------------------- + +_coverage_check: + @THRESHOLD=$$(jq -r '.default_threshold' "$(COVERAGE_THRESHOLDS_FILE)"); \ + npx c8 check-coverage --lines $$THRESHOLD + +## help: List all available targets +help: + @echo "Standard targets:" + @echo " build - Compile TypeScript to out/" + @echo " test - Fail-fast tests + coverage + threshold enforcement" + @echo " lint - ESLint + tsc --noEmit (read-only, no formatting)" + @echo " fmt - Format code in-place (CHECK=1 for read-only CI check)" + @echo " clean - Remove build artifacts and VSIX files" + @echo " ci - lint + test + build (full CI simulation)" + @echo " setup - Install npm dependencies" + @echo "" + @echo "Repo-specific targets:" + @echo " package - Build the .vsix bundle via vsce" + +# ============================================================================= +# Repo-Specific Targets +# +# Targets below this line are specific to this repo and are NOT part of the +# standard 7-target interface. +# ============================================================================= + +## package: Build the .vsix bundle via vsce +package: build + @echo "==> Packaging VSIX..." + npx vsce package diff --git a/coverage-thresholds.json b/coverage-thresholds.json new file mode 100644 index 0000000..446d033 --- /dev/null +++ b/coverage-thresholds.json @@ -0,0 +1,5 @@ +{ + "_agent_pmo": "74cf183", + "_doc": "Single source of truth for code coverage thresholds. See REPO-STANDARDS-SPEC [COVERAGE-THRESHOLDS-JSON]. NO GitHub repo variables. NO env vars. NO public `make coverage-check` target. This file is read by the internal `_coverage_check` recipe inside `make test`. `make test` exits non-zero if measured coverage < threshold. Thresholds are monotonically increasing — only ratchet UP, never down.", + "default_threshold": 0 +} diff --git a/eslint.config.mjs b/eslint.config.mjs new file mode 100644 index 0000000..b5a7045 --- /dev/null +++ b/eslint.config.mjs @@ -0,0 +1,58 @@ +// agent-pmo:74cf183 +// @ts-check +import eslint from "@eslint/js"; +import tseslint from "typescript-eslint"; + +export default tseslint.config( + eslint.configs.recommended, + ...tseslint.configs.strictTypeChecked, + { + languageOptions: { + parserOptions: { + projectService: true, + tsconfigRootDir: import.meta.dirname, + }, + }, + rules: { + "@typescript-eslint/no-explicit-any": "error", + "@typescript-eslint/no-unsafe-assignment": "error", + "@typescript-eslint/no-unsafe-call": "error", + "@typescript-eslint/no-unsafe-member-access": "error", + "@typescript-eslint/no-unsafe-return": "error", + "@typescript-eslint/no-floating-promises": "error", + "@typescript-eslint/no-misused-promises": "error", + "@typescript-eslint/await-thenable": "error", + "@typescript-eslint/no-unnecessary-type-assertion": "error", + "@typescript-eslint/prefer-nullish-coalescing": "error", + "@typescript-eslint/prefer-optional-chain": "error", + "@typescript-eslint/no-non-null-assertion": "error", + "@typescript-eslint/strict-boolean-expressions": "error", + "@typescript-eslint/no-unused-vars": [ + "error", + { argsIgnorePattern: "^_" }, + ], + "@typescript-eslint/consistent-type-imports": [ + "error", + { prefer: "type-imports" }, + ], + "@typescript-eslint/no-shadow": "error", + "no-throw-literal": "off", + "@typescript-eslint/only-throw-error": "error", + eqeqeq: ["error", "always"], + "no-console": "warn", + curly: ["error", "all"], + }, + }, + { + ignores: [ + "out/", + "dist/", + "**/*.d.ts", + "node_modules/", + "coverage/", + ".vscode-test/", + "**/*.js", + "**/*.mjs", + ], + }, +); diff --git a/opencode.json b/opencode.json new file mode 100644 index 0000000..0c8379c --- /dev/null +++ b/opencode.json @@ -0,0 +1,5 @@ +{ + "_agent_pmo": "74cf183", + "$schema": "https://opencode.ai/config.json", + "instructions": ["CLAUDE.md"] +} diff --git a/src/constants.ts b/src/constants.ts new file mode 100644 index 0000000..460a3bc --- /dev/null +++ b/src/constants.ts @@ -0,0 +1,47 @@ +export const SCHEME = 'diffy'; + +export const OUTPUT_CHANNEL_NAME = 'Diffy'; + +export const COMMAND_IDS = { + compareWith: 'diffy.compareWith', + compareWithWorkingCopy: 'diffy.compareWithWorkingCopy', + compareWithPrevious: 'diffy.compareWithPrevious', + compareTwoCommits: 'diffy.compareTwoCommits', + compareFileWithCommit: 'diffy.compareFileWithCommit', + reopenLast: 'diffy.reopenLast', + showLogs: 'diffy.showLogs', +} as const; + +export const BUILT_IN_COMMANDS = { + diff: 'vscode.diff', +} as const; + +export const CONTEXT_KEYS = { + gitAvailable: 'diffy.gitAvailable', +} as const; + +export const MEMENTO_KEYS = { + lastComparison: 'diffy.lastComparison', +} as const; + +export const URI_AUTHORITIES = { + commit: 'commit', + index: 'index', +} as const; + +export const DEFAULT_LOG_LIMIT = 200; + +export const SHORT_SHA_LEN = 7; + +export const GIT_BINARY = 'git'; + +export const NUL = '\x00'; +export const TAB = '\t'; +export const LF = '\n'; + +export const GIT_LOG_FORMAT = '%H%x00%h%x00%an%x00%at%x00%s'; + +export const REFS_FORMAT = '%(refname)%00%(refname:short)%00%(objectname)'; + +export const REF_PREFIX_HEADS = 'refs/heads/'; +export const REF_PREFIX_TAGS = 'refs/tags/'; diff --git a/src/git/GitRepo.ts b/src/git/GitRepo.ts new file mode 100644 index 0000000..cf6ca9c --- /dev/null +++ b/src/git/GitRepo.ts @@ -0,0 +1,99 @@ +import { DEFAULT_LOG_LIMIT, GIT_LOG_FORMAT, REFS_FORMAT } from '../constants'; +import { type Result, ok, err, andThen } from '../result'; +import type { GitRunner } from './GitRunner'; +import { + parseLog, + parseNameStatus, + parseNumstat, + parseRefs, +} from './parsers'; +import type { + ChangedFile, + Commit, + CommitRev, + DiffStat, + DiffyAddressableRev, + GitError, + Ref, + RevSpec, + Sha, +} from './types'; + +export interface DiffSides { + readonly from: CommitRev; + readonly to: RevSpec; +} + +export interface ShowArgs { + readonly rev: DiffyAddressableRev; + readonly path: string; +} + +export interface LogArgs { + readonly limit?: number; + readonly ref?: string; +} + +export interface GitRepo { + log(args?: LogArgs): Promise<Result<readonly Commit[], GitError>>; + nameStatus(args: DiffSides): Promise<Result<readonly ChangedFile[], GitError>>; + numstat(args: DiffSides): Promise<Result<readonly DiffStat[], GitError>>; + show(args: ShowArgs): Promise<Result<string, GitError>>; + refs(): Promise<Result<readonly Ref[], GitError>>; + revParse(name: string): Promise<Result<Sha, GitError>>; +} + +const buildLogArgs = ({ limit, ref }: { limit: number; ref?: string }): readonly string[] => { + const base = ['log', `--max-count=${limit}`, `--format=${GIT_LOG_FORMAT}`, '-z']; + return ref === undefined ? base : [...base, ref]; +}; + +const buildDiffArgs = ({ from, to }: DiffSides, subcommand: 'name-status' | 'numstat'): readonly string[] => { + const head = ['diff', `--${subcommand}`, '-z']; + if (to.kind === 'commit') return [...head, from.sha, to.sha]; + if (to.kind === 'workingCopy') return [...head, from.sha]; + return [...head, from.sha, '--cached']; +}; + +const showSpec = ({ rev, path }: ShowArgs): string => + rev.kind === 'commit' ? `${rev.sha}:${path}` : `:${path}`; + +const trimSha = (stdout: string): Result<Sha, GitError> => { + const sha = stdout.trim(); + if (sha.length === 0) return err({ kind: 'parseError', message: 'revParse: empty output' }); + return ok(sha); +}; + +export const createGitRepo = ({ + runner, + cwd, +}: { + runner: GitRunner; + cwd: string; +}): GitRepo => ({ + log: async (args = {}) => { + const limit = args.limit ?? DEFAULT_LOG_LIMIT; + const r = await runner.run({ args: buildLogArgs({ limit, ref: args.ref }), cwd }); + return andThen(r, parseLog); + }, + nameStatus: async (args) => { + const r = await runner.run({ args: buildDiffArgs(args, 'name-status'), cwd }); + return andThen(r, parseNameStatus); + }, + numstat: async (args) => { + const r = await runner.run({ args: buildDiffArgs(args, 'numstat'), cwd }); + return andThen(r, parseNumstat); + }, + show: (args) => runner.run({ args: ['show', showSpec(args)], cwd }), + refs: async () => { + const r = await runner.run({ + args: ['for-each-ref', '-z', `--format=${REFS_FORMAT}`], + cwd, + }); + return andThen(r, parseRefs); + }, + revParse: async (name) => { + const r = await runner.run({ args: ['rev-parse', '--verify', name], cwd }); + return andThen(r, trimSha); + }, +}); diff --git a/src/git/GitRunner.ts b/src/git/GitRunner.ts new file mode 100644 index 0000000..881c27a --- /dev/null +++ b/src/git/GitRunner.ts @@ -0,0 +1,83 @@ +import { spawn, type ChildProcessWithoutNullStreams } from 'node:child_process'; +import { GIT_BINARY } from '../constants'; +import { type Result, ok, err } from '../result'; +import { logger as defaultLogger, type Logger } from '../logger'; +import type { GitError } from './types'; + +export interface GitRunArgs { + readonly args: readonly string[]; + readonly cwd: string; +} + +export interface GitRunner { + run(args: GitRunArgs): Promise<Result<string, GitError>>; +} + +type Resolver = (r: Result<string, GitError>) => void; + +interface WireArgs { + readonly child: ChildProcessWithoutNullStreams; + readonly argCount: number; + readonly subcommand: string; + readonly logger: Logger; + readonly resolve: Resolver; +} + +const finishRun = (params: { + code: number | null; + subcommand: string; + stdout: string; + stderr: string; +}): Result<string, GitError> => { + if (params.code === 0) return ok(params.stdout); + return err({ + kind: 'nonZeroExit', + message: `git ${params.subcommand} exited ${params.code ?? '?'}`, + stderr: params.stderr, + exitCode: params.code ?? -1, + }); +}; + +const wireSubprocess = ({ child, argCount, subcommand, logger, resolve }: WireArgs): void => { + let stdout = ''; + let stderr = ''; + child.stdout.on('data', (chunk: Buffer) => { + stdout += chunk.toString('utf8'); + }); + child.stderr.on('data', (chunk: Buffer) => { + stderr += chunk.toString('utf8'); + }); + child.on('error', (e: Error) => { + logger.warn({ argCount }, 'git.run.spawnFailed'); + resolve(err({ kind: 'spawnFailed', message: e.message })); + }); + child.on('close', (code: number | null) => { + logger.debug({ exitCode: code, stdoutLen: stdout.length }, 'git.run.end'); + resolve(finishRun({ code, subcommand, stdout, stderr })); + }); +}; + +const runGit = ({ + args, + cwd, + logger, +}: GitRunArgs & { logger: Logger }): Promise<Result<string, GitError>> => { + logger.debug({ argCount: args.length }, 'git.run.start'); + return new Promise<Result<string, GitError>>((resolve) => { + const child = spawn(GIT_BINARY, [...args], { cwd }); + wireSubprocess({ + child, + argCount: args.length, + subcommand: args[0] ?? '', + logger, + resolve, + }); + }); +}; + +export const createGitRunner = ( + deps: { logger?: Logger } = {}, +): GitRunner => { + const logger = deps.logger ?? defaultLogger; + return { run: (a) => runGit({ ...a, logger }) }; +}; diff --git a/src/git/parsers.ts b/src/git/parsers.ts new file mode 100644 index 0000000..f4f7cb5 --- /dev/null +++ b/src/git/parsers.ts @@ -0,0 +1,242 @@ +import { NUL, TAB, REF_PREFIX_HEADS, REF_PREFIX_TAGS } from '../constants'; +import { type Result, ok, err } from '../result'; +import type { + ChangedFile, + ChangedFileStatus, + Commit, + DiffStat, + GitError, + Ref, + RefType, +} from './types'; + +const LOG_FIELDS_PER_RECORD = 5; +const REF_FIELDS_PER_RECORD = 3; +const NUMSTAT_TAB_FIELDS = 3; +const CHAR_CODE_DIGIT_LO = 48; +const CHAR_CODE_DIGIT_HI = 57; + +const errParse = (message: string): Result<never, GitError> => + err({ kind: 'parseError', message }); + +const stripTrailingEmpty = (arr: readonly string[]): readonly string[] => { + if (arr.length === 0) return arr; + const last = arr[arr.length - 1]; + return last === '' ? arr.slice(0, -1) : arr; +}; + +const isAllDigits = (s: string): boolean => { + if (s.length === 0) return false; + for (let i = 0; i < s.length; i++) { + const c = s.charCodeAt(i); + if (c < CHAR_CODE_DIGIT_LO || c > CHAR_CODE_DIGIT_HI) return false; + } + return true; +}; + +const refTypeFromName = (fullName: string): RefType => { + if (fullName.startsWith(REF_PREFIX_HEADS)) return 'branch'; + if (fullName.startsWith(REF_PREFIX_TAGS)) return 'tag'; + return 'other'; +}; + +const parseLogRecord = (fields: readonly string[]): Result<Commit, GitError> => { + const [sha, shortSha, author, atStr, subject] = fields; + if ( + sha === undefined || + shortSha === undefined || + author === undefined || + atStr === undefined || + subject === undefined + ) { + return errParse('parseLog: missing field'); + } + if (!isAllDigits(atStr)) return errParse('parseLog: invalid timestamp'); + const authorTime = Number.parseInt(atStr, 10); + return ok({ sha, shortSha, author, authorTime, subject }); +}; + +export const parseLog = (stdout: string): Result<readonly Commit[], GitError> => { + if (stdout.length === 0) return ok([]); + const tokens = stripTrailingEmpty(stdout.split(NUL)); + if (tokens.length % LOG_FIELDS_PER_RECORD !== 0) { + return errParse('parseLog: field count not multiple of 5'); + } + const commits: Commit[] = []; + for (let i = 0; i < tokens.length; i += LOG_FIELDS_PER_RECORD) { + const slice = tokens.slice(i, i + LOG_FIELDS_PER_RECORD); + const r = parseLogRecord(slice); + if (!r.ok) return r; + commits.push(r.value); + } + return ok(commits); +}; + +interface ParsedStatus { + readonly status: ChangedFileStatus; + readonly similarity?: number; + readonly isRename: boolean; +} + +const parseStatusToken = (raw: string): Result<ParsedStatus, GitError> => { + if (raw.length === 0) return errParse('parseNameStatus: empty status token'); + const first = raw.charAt(0); + if (first === 'A' || first === 'M' || first === 'D') { + if (raw.length !== 1) return errParse('parseNameStatus: extra chars on status'); + return ok({ status: first, isRename: false }); + } + if (first === 'R' || first === 'C') { + const digits = raw.slice(1); + if (!isAllDigits(digits)) return errParse('parseNameStatus: bad similarity'); + return ok({ status: first, similarity: Number.parseInt(digits, 10), isRename: true }); + } + return errParse(`parseNameStatus: unknown status '${first}'`); +}; + +const buildSimple = ( + parsed: ParsedStatus, + tokens: readonly string[], + i: number, +): Result<{ file: ChangedFile; next: number }, GitError> => { + const path = tokens[i + 1]; + if (path === undefined) return errParse('parseNameStatus: missing path'); + return ok({ file: { status: parsed.status, path }, next: i + 2 }); +}; + +const buildRenameOrCopy = ( + parsed: ParsedStatus, + tokens: readonly string[], + i: number, +): Result<{ file: ChangedFile; next: number }, GitError> => { + const oldPath = tokens[i + 1]; + const newPath = tokens[i + 2]; + if (oldPath === undefined || newPath === undefined) { + return errParse('parseNameStatus: rename missing paths'); + } + const similarity = parsed.similarity; + if (similarity === undefined) return errParse('parseNameStatus: rename missing similarity'); + return ok({ + file: { status: parsed.status, path: newPath, oldPath, similarity }, + next: i + 3, + }); +}; + +const readOneNameStatus = ( + tokens: readonly string[], + i: number, +): Result<{ file: ChangedFile; next: number }, GitError> => { + const head = tokens[i]; + if (head === undefined) return errParse('parseNameStatus: missing status'); + const parsed = parseStatusToken(head); + if (!parsed.ok) return parsed; + return parsed.value.isRename + ? buildRenameOrCopy(parsed.value, tokens, i) + : buildSimple(parsed.value, tokens, i); +}; + +export const parseNameStatus = ( + stdout: string, +): Result<readonly ChangedFile[], GitError> => { + if (stdout.length === 0) return ok([]); + const tokens = stripTrailingEmpty(stdout.split(NUL)); + const out: ChangedFile[] = []; + let i = 0; + while (i < tokens.length) { + const r = readOneNameStatus(tokens, i); + if (!r.ok) return r; + out.push(r.value.file); + i = r.value.next; + } + return ok(out); +}; + +interface NumstatHead { + readonly added: number; + readonly deleted: number; + readonly binary: boolean; + readonly pathField: string; +} + +const splitNumstatHead = (head: string): Result<NumstatHead, GitError> => { + const parts = head.split(TAB); + if (parts.length !== NUMSTAT_TAB_FIELDS) { + return errParse('parseNumstat: expected 3 tab-fields'); + } + const [a, d, p] = parts; + if (a === undefined || d === undefined || p === undefined) { + return errParse('parseNumstat: missing tab fields'); + } + if (a === '-' && d === '-') return ok({ added: 0, deleted: 0, binary: true, pathField: p }); + if (!isAllDigits(a) || !isAllDigits(d)) { + return errParse('parseNumstat: non-numeric counts'); + } + return ok({ + added: Number.parseInt(a, 10), + deleted: Number.parseInt(d, 10), + binary: false, + pathField: p, + }); +}; + +const readOneNumstat = ( + tokens: readonly string[], + i: number, +): Result<{ stat: DiffStat; next: number }, GitError> => { + const head = tokens[i]; + if (head === undefined) return errParse('parseNumstat: missing record'); + const fields = splitNumstatHead(head); + if (!fields.ok) return fields; + const { added, deleted, binary, pathField } = fields.value; + if (pathField !== '') { + return ok({ stat: { path: pathField, added, deleted, binary }, next: i + 1 }); + } + const oldPath = tokens[i + 1]; + const newPath = tokens[i + 2]; + if (oldPath === undefined || newPath === undefined) { + return errParse('parseNumstat: rename missing paths'); + } + return ok({ + stat: { path: newPath, oldPath, added, deleted, binary }, + next: i + 3, + }); +}; + +export const parseNumstat = (stdout: string): Result<readonly DiffStat[], GitError> => { + if (stdout.length === 0) return ok([]); + const tokens = stripTrailingEmpty(stdout.split(NUL)); + const out: DiffStat[] = []; + let i = 0; + while (i < tokens.length) { + const r = readOneNumstat(tokens, i); + if (!r.ok) return r; + out.push(r.value.stat); + i = r.value.next; + } + return ok(out); +}; + +const parseRefRecord = (fields: readonly string[]): Result<Ref, GitError> => { + const [fullName, shortName, sha] = fields; + if (fullName === undefined || shortName === undefined || sha === undefined) { + return errParse('parseRefs: missing field'); + } + if (fullName === '' || shortName === '' || sha === '') { + return errParse('parseRefs: empty field'); + } + return ok({ name: shortName, fullName, sha, type: refTypeFromName(fullName) }); +}; + +export const parseRefs = (stdout: string): Result<readonly Ref[], GitError> => { + if (stdout.length === 0) return ok([]); + const tokens = stripTrailingEmpty(stdout.split(NUL)); + if (tokens.length % REF_FIELDS_PER_RECORD !== 0) { + return errParse('parseRefs: field count not multiple of 3'); + } + const refs: Ref[] = []; + for (let i = 0; i < tokens.length; i += REF_FIELDS_PER_RECORD) { + const r = parseRefRecord(tokens.slice(i, i + REF_FIELDS_PER_RECORD)); + if (!r.ok) return r; + refs.push(r.value); + } + return ok(refs); +}; diff --git a/src/git/types.ts b/src/git/types.ts new file mode 100644 index 0000000..c2a1331 --- /dev/null +++ b/src/git/types.ts @@ -0,0 +1,57 @@ +export type Sha = string; + +export type ChangedFileStatus = 'A' | 'M' | 'D' | 'R' | 'C'; + +export interface ChangedFile { + readonly status: ChangedFileStatus; + readonly path: string; + readonly oldPath?: string; + readonly similarity?: number; +} + +export interface Commit { + readonly sha: Sha; + readonly shortSha: string; + readonly author: string; + readonly authorTime: number; + readonly subject: string; +} + +export interface DiffStat { + readonly path: string; + readonly oldPath?: string; + readonly added: number; + readonly deleted: number; + readonly binary: boolean; +} + +export type CommitRev = { readonly kind: 'commit'; readonly sha: Sha }; +export type WorkingCopyRev = { readonly kind: 'workingCopy' }; +export type IndexRev = { readonly kind: 'index' }; + +export type RevSpec = CommitRev | WorkingCopyRev | IndexRev; + +export type DiffyAddressableRev = CommitRev | IndexRev; + +export type RefType = 'branch' | 'tag' | 'other'; + +export interface Ref { + readonly name: string; + readonly fullName: string; + readonly sha: Sha; + readonly type: RefType; +} + +export type GitErrorKind = + | 'spawnFailed' + | 'nonZeroExit' + | 'parseError' + | 'notARepo' + | 'notFound'; + +export interface GitError { + readonly kind: GitErrorKind; + readonly message: string; + readonly stderr?: string; + readonly exitCode?: number; +} diff --git a/src/logger.ts b/src/logger.ts new file mode 100644 index 0000000..9a9b25c --- /dev/null +++ b/src/logger.ts @@ -0,0 +1,15 @@ +import pino from 'pino'; + +export interface Logger { + trace(obj: object, msg?: string): void; + debug(obj: object, msg?: string): void; + info(obj: object, msg?: string): void; + warn(obj: object, msg?: string): void; + error(obj: object, msg?: string): void; +} + +export const logger: Logger = pino({ + level: process.env['DIFFY_LOG_LEVEL'] ?? 'info', + base: undefined, + timestamp: pino.stdTimeFunctions.isoTime, +}); diff --git a/src/result.ts b/src/result.ts new file mode 100644 index 0000000..246e723 --- /dev/null +++ b/src/result.ts @@ -0,0 +1,22 @@ +export type Ok<T> = { readonly ok: true; readonly value: T }; +export type Err<E> = { readonly ok: false; readonly error: E }; +export type Result<T, E> = Ok<T> | Err<E>; + +export const ok = <T>(value: T): Ok<T> => ({ ok: true, value }); + +export const err = <E>(error: E): Err<E> => ({ ok: false, error }); + +export const isOk = <T, E>(r: Result<T, E>): r is Ok<T> => r.ok; + +export const isErr = <T, E>(r: Result<T, E>): r is Err<E> => !r.ok; + +export const map = <T, U, E>(r: Result<T, E>, f: (t: T) => U): Result<U, E> => + r.ok ? ok(f(r.value)) : r; + +export const andThen = <T, U, E>( + r: Result<T, E>, + f: (t: T) => Result<U, E>, +): Result<U, E> => (r.ok ? f(r.value) : r); + +export const unwrapOr = <T, E>(r: Result<T, E>, fallback: T): T => + r.ok ? r.value : fallback; diff --git a/src/test/unit/parsers.test.ts b/src/test/unit/parsers.test.ts new file mode 100644 index 0000000..05e9caf --- /dev/null +++ b/src/test/unit/parsers.test.ts @@ -0,0 +1,355 @@ +import { strict as assert } from 'node:assert'; +import { + parseLog, + parseNameStatus, + parseNumstat, + parseRefs, +} from '../../git/parsers'; + +const NUL = '\x00'; +const TAB = '\t'; + +const logRecord = ( + sha: string, + short: string, + author: string, + at: string, + subject: string, +): string => [sha, short, author, at, subject].join(NUL); + +describe('parseLog', () => { + it('returns empty array for empty input', () => { + const r = parseLog(''); + assert.equal(r.ok, true); + if (r.ok) assert.deepEqual(r.value, []); + }); + + it('parses a single commit with trailing NUL', () => { + const stdout = logRecord('abc123def456', 'abc123d', 'Alice', '1700000000', 'init') + NUL; + const r = parseLog(stdout); + assert.equal(r.ok, true); + if (!r.ok) return; + assert.equal(r.value.length, 1); + assert.deepEqual(r.value[0], { + sha: 'abc123def456', + shortSha: 'abc123d', + author: 'Alice', + authorTime: 1700000000, + subject: 'init', + }); + }); + + it('parses two commits separated by NUL', () => { + const a = logRecord('aaaa1111', 'aaaa111', 'Alice', '1700000000', 'first'); + const b = logRecord('bbbb2222', 'bbbb222', 'Bob', '1700000100', 'second commit'); + const r = parseLog(`${a}${NUL}${b}${NUL}`); + assert.equal(r.ok, true); + if (!r.ok) return; + assert.equal(r.value.length, 2); + assert.equal(r.value[0]?.sha, 'aaaa1111'); + assert.equal(r.value[0]?.subject, 'first'); + assert.equal(r.value[1]?.sha, 'bbbb2222'); + assert.equal(r.value[1]?.subject, 'second commit'); + assert.equal(r.value[1]?.author, 'Bob'); + assert.equal(r.value[1]?.authorTime, 1700000100); + }); + + it('parses without trailing NUL when field count matches', () => { + const stdout = logRecord('abcd1234', 'abcd123', 'Alice', '1700000000', 'init'); + const r = parseLog(stdout); + assert.equal(r.ok, true); + if (r.ok) assert.equal(r.value.length, 1); + }); + + it('preserves an empty subject', () => { + const stdout = logRecord('abcd1234', 'abcd123', 'Alice', '1700000000', '') + NUL; + const r = parseLog(stdout); + assert.equal(r.ok, true); + if (r.ok) assert.equal(r.value[0]?.subject, ''); + }); + + it('preserves unicode and spaces in author/subject', () => { + const stdout = + logRecord('abcd1234', 'abcd123', 'Élise Müller', '1700000000', 'feat: 日本語 fix ⚡') + NUL; + const r = parseLog(stdout); + assert.equal(r.ok, true); + if (!r.ok) return; + assert.equal(r.value[0]?.author, 'Élise Müller'); + assert.equal(r.value[0]?.subject, 'feat: 日本語 fix ⚡'); + }); + + it('errors when field count is not a multiple of 5', () => { + const r = parseLog(`a${NUL}b${NUL}c${NUL}d${NUL}`); + assert.equal(r.ok, false); + if (!r.ok) { + assert.equal(r.error.kind, 'parseError'); + assert.match(r.error.message, /multiple of 5/); + } + }); + + it('errors when timestamp is not numeric', () => { + const stdout = logRecord('a', 'a', 'Alice', 'NOT_A_NUMBER', 'x') + NUL; + const r = parseLog(stdout); + assert.equal(r.ok, false); + if (!r.ok) assert.match(r.error.message, /timestamp/); + }); + + it('errors when timestamp is empty', () => { + const stdout = logRecord('a', 'a', 'Alice', '', 'x') + NUL; + const r = parseLog(stdout); + assert.equal(r.ok, false); + if (!r.ok) assert.match(r.error.message, /timestamp/); + }); + + it('errors when timestamp has leading/trailing whitespace', () => { + const stdout = logRecord('a', 'a', 'Alice', ' 1700000000', 'x') + NUL; + const r = parseLog(stdout); + assert.equal(r.ok, false); + }); +}); + +describe('parseNameStatus', () => { + it('returns empty array for empty input', () => { + const r = parseNameStatus(''); + assert.equal(r.ok, true); + if (r.ok) assert.deepEqual(r.value, []); + }); + + it('parses an Added file', () => { + const r = parseNameStatus(`A${NUL}new.txt${NUL}`); + assert.equal(r.ok, true); + if (!r.ok) return; + assert.deepEqual(r.value, [{ status: 'A', path: 'new.txt' }]); + }); + + it('parses a Modified file', () => { + const r = parseNameStatus(`M${NUL}a.txt${NUL}`); + assert.equal(r.ok, true); + if (r.ok) assert.deepEqual(r.value, [{ status: 'M', path: 'a.txt' }]); + }); + + it('parses a Deleted file', () => { + const r = parseNameStatus(`D${NUL}gone.txt${NUL}`); + assert.equal(r.ok, true); + if (r.ok) assert.deepEqual(r.value, [{ status: 'D', path: 'gone.txt' }]); + }); + + it('parses a Rename with similarity', () => { + const r = parseNameStatus(`R100${NUL}old.txt${NUL}new.txt${NUL}`); + assert.equal(r.ok, true); + if (!r.ok) return; + assert.deepEqual(r.value, [ + { status: 'R', path: 'new.txt', oldPath: 'old.txt', similarity: 100 }, + ]); + }); + + it('parses a Copy with similarity', () => { + const r = parseNameStatus(`C75${NUL}src.txt${NUL}dst.txt${NUL}`); + assert.equal(r.ok, true); + if (!r.ok) return; + assert.deepEqual(r.value, [ + { status: 'C', path: 'dst.txt', oldPath: 'src.txt', similarity: 75 }, + ]); + }); + + it('parses a mixed batch', () => { + const stdout = + `A${NUL}added.txt${NUL}` + + `M${NUL}modified.txt${NUL}` + + `D${NUL}deleted.txt${NUL}` + + `R98${NUL}was.txt${NUL}now.txt${NUL}` + + `C50${NUL}src.txt${NUL}dst.txt${NUL}`; + const r = parseNameStatus(stdout); + assert.equal(r.ok, true); + if (!r.ok) return; + assert.equal(r.value.length, 5); + assert.equal(r.value[0]?.status, 'A'); + assert.equal(r.value[1]?.status, 'M'); + assert.equal(r.value[2]?.status, 'D'); + assert.equal(r.value[3]?.status, 'R'); + assert.equal(r.value[3]?.similarity, 98); + assert.equal(r.value[4]?.status, 'C'); + assert.equal(r.value[4]?.similarity, 50); + }); + + it('preserves spaces, unicode, and quotes in paths', () => { + const stdout = `M${NUL}path with spaces/日本語 "name".txt${NUL}`; + const r = parseNameStatus(stdout); + assert.equal(r.ok, true); + if (!r.ok) return; + assert.equal(r.value[0]?.path, 'path with spaces/日本語 "name".txt'); + }); + + it('errors on unknown status letter', () => { + const r = parseNameStatus(`X${NUL}a.txt${NUL}`); + assert.equal(r.ok, false); + if (!r.ok) assert.match(r.error.message, /unknown status/); + }); + + it('errors when A/M/D has trailing chars', () => { + const r = parseNameStatus(`A100${NUL}a.txt${NUL}`); + assert.equal(r.ok, false); + if (!r.ok) assert.match(r.error.message, /extra chars/); + }); + + it('errors on rename without similarity digits', () => { + const r = parseNameStatus(`R${NUL}old${NUL}new${NUL}`); + assert.equal(r.ok, false); + if (!r.ok) assert.match(r.error.message, /similarity/); + }); + + it('errors on rename with non-digit similarity', () => { + const r = parseNameStatus(`R1x0${NUL}old${NUL}new${NUL}`); + assert.equal(r.ok, false); + if (!r.ok) assert.match(r.error.message, /similarity/); + }); + + it('errors on truncated rename (missing new path)', () => { + const r = parseNameStatus(`R100${NUL}old.txt${NUL}`); + assert.equal(r.ok, false); + if (!r.ok) assert.match(r.error.message, /missing paths/); + }); + + it('errors when simple status is missing its path', () => { + const r = parseNameStatus(`M${NUL}`); + assert.equal(r.ok, false); + }); +}); + +describe('parseNumstat', () => { + it('returns empty array for empty input', () => { + const r = parseNumstat(''); + assert.equal(r.ok, true); + if (r.ok) assert.deepEqual(r.value, []); + }); + + it('parses a regular numstat record', () => { + const r = parseNumstat(`12${TAB}3${TAB}hello.txt${NUL}`); + assert.equal(r.ok, true); + if (!r.ok) return; + assert.deepEqual(r.value, [ + { path: 'hello.txt', added: 12, deleted: 3, binary: false }, + ]); + }); + + it('parses a binary marker ("-" / "-")', () => { + const r = parseNumstat(`-${TAB}-${TAB}image.png${NUL}`); + assert.equal(r.ok, true); + if (!r.ok) return; + assert.deepEqual(r.value, [ + { path: 'image.png', added: 0, deleted: 0, binary: true }, + ]); + }); + + it('parses a rename record', () => { + const r = parseNumstat(`3${TAB}1${TAB}${NUL}old.txt${NUL}new.txt${NUL}`); + assert.equal(r.ok, true); + if (!r.ok) return; + assert.equal(r.value.length, 1); + assert.deepEqual(r.value[0], { + path: 'new.txt', + oldPath: 'old.txt', + added: 3, + deleted: 1, + binary: false, + }); + }); + + it('parses a mixed batch with regular, binary, and rename', () => { + const stdout = + `2${TAB}1${TAB}a.txt${NUL}` + + `-${TAB}-${TAB}logo.png${NUL}` + + `5${TAB}0${TAB}${NUL}old.txt${NUL}new.txt${NUL}`; + const r = parseNumstat(stdout); + assert.equal(r.ok, true); + if (!r.ok) return; + assert.equal(r.value.length, 3); + assert.equal(r.value[0]?.path, 'a.txt'); + assert.equal(r.value[0]?.binary, false); + assert.equal(r.value[1]?.binary, true); + assert.equal(r.value[2]?.path, 'new.txt'); + assert.equal(r.value[2]?.oldPath, 'old.txt'); + }); + + it('preserves spaces and unicode in paths', () => { + const r = parseNumstat(`1${TAB}1${TAB}dir with spaces/日本語.txt${NUL}`); + assert.equal(r.ok, true); + if (r.ok) assert.equal(r.value[0]?.path, 'dir with spaces/日本語.txt'); + }); + + it('errors when tab count is wrong', () => { + const r = parseNumstat(`1${TAB}2${NUL}`); + assert.equal(r.ok, false); + if (!r.ok) assert.match(r.error.message, /3 tab-fields/); + }); + + it('errors when counts are non-numeric (and not binary marker)', () => { + const r = parseNumstat(`abc${TAB}def${TAB}p${NUL}`); + assert.equal(r.ok, false); + if (!r.ok) assert.match(r.error.message, /non-numeric/); + }); + + it('errors on truncated rename', () => { + const r = parseNumstat(`3${TAB}1${TAB}${NUL}old.txt${NUL}`); + assert.equal(r.ok, false); + if (!r.ok) assert.match(r.error.message, /rename missing paths/); + }); +}); + +describe('parseRefs', () => { + it('returns empty array for empty input', () => { + const r = parseRefs(''); + assert.equal(r.ok, true); + if (r.ok) assert.deepEqual(r.value, []); + }); + + it('parses a branch ref', () => { + const r = parseRefs(`refs/heads/main${NUL}main${NUL}abc1234${NUL}`); + assert.equal(r.ok, true); + if (!r.ok) return; + assert.deepEqual(r.value, [ + { name: 'main', fullName: 'refs/heads/main', sha: 'abc1234', type: 'branch' }, + ]); + }); + + it('parses a tag ref', () => { + const r = parseRefs(`refs/tags/v1.0${NUL}v1.0${NUL}def5678${NUL}`); + assert.equal(r.ok, true); + if (!r.ok) return; + assert.deepEqual(r.value, [ + { name: 'v1.0', fullName: 'refs/tags/v1.0', sha: 'def5678', type: 'tag' }, + ]); + }); + + it('classifies a non-branch, non-tag ref as other', () => { + const r = parseRefs(`refs/remotes/origin/main${NUL}origin/main${NUL}abc1234${NUL}`); + assert.equal(r.ok, true); + if (r.ok) assert.equal(r.value[0]?.type, 'other'); + }); + + it('parses a mixed batch of branches and tags', () => { + const stdout = + `refs/heads/main${NUL}main${NUL}aaa${NUL}` + + `refs/heads/dev${NUL}dev${NUL}bbb${NUL}` + + `refs/tags/v1.0${NUL}v1.0${NUL}ccc${NUL}`; + const r = parseRefs(stdout); + assert.equal(r.ok, true); + if (!r.ok) return; + assert.equal(r.value.length, 3); + assert.equal(r.value[0]?.type, 'branch'); + assert.equal(r.value[1]?.type, 'branch'); + assert.equal(r.value[2]?.type, 'tag'); + }); + + it('errors when field count is not a multiple of 3', () => { + const r = parseRefs(`refs/heads/main${NUL}main${NUL}`); + assert.equal(r.ok, false); + if (!r.ok) assert.match(r.error.message, /multiple of 3/); + }); + + it('errors when a ref field is empty', () => { + const r = parseRefs(`refs/heads/main${NUL}${NUL}abc${NUL}`); + assert.equal(r.ok, false); + if (!r.ok) assert.match(r.error.message, /empty field/); + }); +}); diff --git a/src/test/unit/result.test.ts b/src/test/unit/result.test.ts new file mode 100644 index 0000000..6c694f6 --- /dev/null +++ b/src/test/unit/result.test.ts @@ -0,0 +1,130 @@ +import { strict as assert } from 'node:assert'; +import { + ok, + err, + isOk, + isErr, + map, + andThen, + unwrapOr, + type Result, +} from '../../result'; + +describe('Result', () => { + describe('ok / err constructors', () => { + it('ok wraps the value and discriminates as ok=true', () => { + const r = ok(42); + assert.equal(r.ok, true); + assert.equal(r.value, 42); + }); + + it('err wraps the error and discriminates as ok=false', () => { + const r = err('boom'); + assert.equal(r.ok, false); + assert.equal(r.error, 'boom'); + }); + + it('ok preserves reference identity of the wrapped value', () => { + const obj = { x: 1 }; + const r = ok(obj); + assert.equal(r.ok, true); + assert.strictEqual(r.value, obj); + }); + + it('err preserves reference identity of the wrapped error', () => { + const e = new Error('x'); + const r = err(e); + assert.equal(r.ok, false); + assert.strictEqual(r.error, e); + }); + }); + + describe('isOk / isErr', () => { + it('isOk returns true on Ok and false on Err', () => { + assert.equal(isOk(ok(1)), true); + assert.equal(isOk(err('x')), false); + }); + + it('isErr returns true on Err and false on Ok', () => { + assert.equal(isErr(err('x')), true); + assert.equal(isErr(ok(1)), false); + }); + + it('isOk narrows the union for the compiler (value is accessible)', () => { + const r: Result<number, string> = ok(7); + if (isOk(r)) { + assert.equal(r.value + 1, 8); + } else { + assert.fail('expected ok branch'); + } + }); + + it('isErr narrows the union for the compiler (error is accessible)', () => { + const r: Result<number, string> = err('nope'); + if (isErr(r)) { + assert.equal(r.error.toUpperCase(), 'NOPE'); + } else { + assert.fail('expected err branch'); + } + }); + }); + + describe('map', () => { + it('applies the function on Ok', () => { + const r = map(ok(2), (n) => n * 3); + assert.equal(r.ok, true); + if (r.ok) assert.equal(r.value, 6); + }); + + it('passes Err through unchanged', () => { + const original: Result<number, string> = err('bad'); + const r = map(original, (n) => n * 3); + assert.equal(r.ok, false); + if (!r.ok) assert.equal(r.error, 'bad'); + }); + + it('supports type-changing maps', () => { + const r = map(ok(5), (n) => `n=${n}`); + assert.equal(r.ok, true); + if (r.ok) assert.equal(r.value, 'n=5'); + }); + }); + + describe('andThen', () => { + it('chains Ok into another Ok', () => { + const r = andThen(ok(2), (n) => ok(n + 1)); + assert.equal(r.ok, true); + if (r.ok) assert.equal(r.value, 3); + }); + + it('chains Ok into an Err and propagates it', () => { + const start: Result<number, string> = ok(2); + const r = andThen(start, (_n): Result<number, string> => err('downstream')); + assert.equal(r.ok, false); + if (!r.ok) assert.equal(r.error, 'downstream'); + }); + + it('short-circuits on Err without invoking the function', () => { + let called = false; + const start: Result<number, string> = err('upstream'); + const r = andThen(start, (n) => { + called = true; + return ok(n); + }); + assert.equal(called, false); + assert.equal(r.ok, false); + if (!r.ok) assert.equal(r.error, 'upstream'); + }); + }); + + describe('unwrapOr', () => { + it('returns the Ok value', () => { + assert.equal(unwrapOr(ok(10), 99), 10); + }); + + it('returns the fallback on Err', () => { + const r: Result<number, string> = err('x'); + assert.equal(unwrapOr(r, 99), 99); + }); + }); +}); diff --git a/src/test/unit/uri.test.ts b/src/test/unit/uri.test.ts new file mode 100644 index 0000000..462fa3d --- /dev/null +++ b/src/test/unit/uri.test.ts @@ -0,0 +1,149 @@ +import { strict as assert } from 'node:assert'; +import { buildDiffyUri, parseDiffyUri } from '../../ui/uri'; +import type { DiffyAddressableRev } from '../../git/types'; + +const SHA = 'abc1234def567890fedcba0987654321aaaaaaaa'; + +const commit = (sha: string): DiffyAddressableRev => ({ kind: 'commit', sha }); +const index = (): DiffyAddressableRev => ({ kind: 'index' }); + +const roundTrip = (rev: DiffyAddressableRev, path: string): void => { + const uri = buildDiffyUri(rev, path); + const parsed = parseDiffyUri(uri); + assert.equal(parsed.ok, true, `parse failed for ${uri}`); + if (!parsed.ok) return; + assert.deepEqual(parsed.value.rev, rev); + assert.equal(parsed.value.path, path); +}; + +describe('buildDiffyUri', () => { + it('builds a commit URI with sha and path', () => { + const uri = buildDiffyUri(commit(SHA), 'src/file.ts'); + assert.equal(uri, `diffy://commit/${SHA}/src/file.ts`); + }); + + it('builds an index URI without sha', () => { + const uri = buildDiffyUri(index(), 'src/file.ts'); + assert.equal(uri, 'diffy://index/src/file.ts'); + }); + + it('percent-encodes spaces in path segments', () => { + const uri = buildDiffyUri(commit(SHA), 'a folder/b file.ts'); + assert.equal(uri, `diffy://commit/${SHA}/a%20folder/b%20file.ts`); + }); + + it('percent-encodes ? and # so they are not parsed as query/fragment', () => { + const uri = buildDiffyUri(commit(SHA), 'weird?name#here.ts'); + assert.equal(uri, `diffy://commit/${SHA}/weird%3Fname%23here.ts`); + }); + + it('preserves forward slashes as segment separators', () => { + const uri = buildDiffyUri(commit(SHA), 'a/b/c/d.ts'); + assert.equal(uri, `diffy://commit/${SHA}/a/b/c/d.ts`); + }); + + it('encodes unicode characters in path segments', () => { + const uri = buildDiffyUri(commit(SHA), 'src/日本語.ts'); + const encoded = encodeURIComponent('日本語'); + assert.equal(uri, `diffy://commit/${SHA}/src/${encoded}.ts`); + }); +}); + +describe('parseDiffyUri', () => { + it('parses a commit URI and decodes the path', () => { + const r = parseDiffyUri(`diffy://commit/${SHA}/src/file.ts`); + assert.equal(r.ok, true); + if (!r.ok) return; + assert.deepEqual(r.value.rev, { kind: 'commit', sha: SHA }); + assert.equal(r.value.path, 'src/file.ts'); + }); + + it('parses an index URI and decodes the path', () => { + const r = parseDiffyUri('diffy://index/src/file.ts'); + assert.equal(r.ok, true); + if (!r.ok) return; + assert.deepEqual(r.value.rev, { kind: 'index' }); + assert.equal(r.value.path, 'src/file.ts'); + }); + + it('rejects a non-diffy scheme', () => { + const r = parseDiffyUri('file:///some/path.ts'); + assert.equal(r.ok, false); + if (!r.ok) assert.equal(r.error.kind, 'invalidScheme'); + }); + + it('rejects an unknown authority', () => { + const r = parseDiffyUri(`diffy://stash/${SHA}/x.ts`); + assert.equal(r.ok, false); + if (!r.ok) assert.equal(r.error.kind, 'invalidAuthority'); + }); + + it('rejects a commit URI with no path after the sha', () => { + const r = parseDiffyUri(`diffy://commit/${SHA}/`); + assert.equal(r.ok, false); + if (!r.ok) assert.equal(r.error.kind, 'emptyPath'); + }); + + it('rejects a commit URI with no sha', () => { + const r = parseDiffyUri('diffy://commit//file.ts'); + assert.equal(r.ok, false); + if (!r.ok) assert.equal(r.error.kind, 'missingSha'); + }); + + it('rejects an index URI with no path', () => { + const r = parseDiffyUri('diffy://index/'); + assert.equal(r.ok, false); + if (!r.ok) assert.equal(r.error.kind, 'emptyPath'); + }); + + it('rejects a malformed URI with no scheme separator', () => { + const r = parseDiffyUri('not-a-uri'); + assert.equal(r.ok, false); + if (!r.ok) assert.equal(r.error.kind, 'malformed'); + }); + + it('rejects a URI missing the authority/path slash', () => { + const r = parseDiffyUri('diffy://commit'); + assert.equal(r.ok, false); + if (!r.ok) assert.equal(r.error.kind, 'malformed'); + }); + + it('rejects a URI with malformed percent encoding', () => { + const r = parseDiffyUri(`diffy://commit/${SHA}/bad%ZZpath.ts`); + assert.equal(r.ok, false); + if (!r.ok) assert.equal(r.error.kind, 'badEncoding'); + }); +}); + +describe('buildDiffyUri ↔ parseDiffyUri round-trip', () => { + it('round-trips a simple commit URI', () => { + roundTrip(commit(SHA), 'src/file.ts'); + }); + + it('round-trips a simple index URI', () => { + roundTrip(index(), 'src/file.ts'); + }); + + it('round-trips paths with spaces', () => { + roundTrip(commit(SHA), 'a folder/b file.ts'); + roundTrip(index(), 'a folder/b file.ts'); + }); + + it('round-trips paths with unicode', () => { + roundTrip(commit(SHA), 'src/日本語/файл.ts'); + roundTrip(index(), 'src/日本語/файл.ts'); + }); + + it('round-trips paths containing # and ?', () => { + roundTrip(commit(SHA), 'weird?name#here.ts'); + roundTrip(index(), 'q=1&r=2#frag.ts'); + }); + + it('round-trips paths with quotes and brackets', () => { + roundTrip(commit(SHA), 'a "b" [c] (d).ts'); + }); + + it('round-trips deeply nested paths', () => { + roundTrip(commit(SHA), 'a/b/c/d/e/f/g/h/i/j.ts'); + }); +}); diff --git a/src/ui/uri.ts b/src/ui/uri.ts new file mode 100644 index 0000000..da29fa4 --- /dev/null +++ b/src/ui/uri.ts @@ -0,0 +1,89 @@ +import { SCHEME, URI_AUTHORITIES } from '../constants'; +import { type Result, ok, err } from '../result'; +import type { DiffyAddressableRev, Sha } from '../git/types'; + +export type DiffyUriParseError = + | { readonly kind: 'invalidScheme'; readonly got: string } + | { readonly kind: 'invalidAuthority'; readonly got: string } + | { readonly kind: 'missingSha' } + | { readonly kind: 'emptyPath' } + | { readonly kind: 'badEncoding'; readonly raw: string } + | { readonly kind: 'malformed'; readonly reason: string }; + +export interface DiffyUriComponents { + readonly rev: DiffyAddressableRev; + readonly path: string; +} + +const SCHEME_SEP = '://'; +const PATH_SEP = '/'; + +const encodePath = (path: string): string => + path.split(PATH_SEP).map(encodeURIComponent).join(PATH_SEP); + +const decodePath = (encoded: string): Result<string, DiffyUriParseError> => { + try { + return ok(encoded.split(PATH_SEP).map(decodeURIComponent).join(PATH_SEP)); + } catch { + return err({ kind: 'badEncoding', raw: encoded }); + } +}; + +export const buildDiffyUri = (rev: DiffyAddressableRev, path: string): string => { + const encoded = encodePath(path); + if (rev.kind === 'commit') { + return `${SCHEME}${SCHEME_SEP}${URI_AUTHORITIES.commit}/${rev.sha}/${encoded}`; + } + return `${SCHEME}${SCHEME_SEP}${URI_AUTHORITIES.index}/${encoded}`; +}; + +const parseCommitUri = (rest: string): Result<DiffyUriComponents, DiffyUriParseError> => { + const slash = rest.indexOf(PATH_SEP); + if (slash <= 0) return err({ kind: 'missingSha' }); + const sha: Sha = rest.slice(0, slash); + const encodedPath = rest.slice(slash + 1); + if (encodedPath === '') return err({ kind: 'emptyPath' }); + const decoded = decodePath(encodedPath); + if (!decoded.ok) return decoded; + return ok({ rev: { kind: 'commit', sha }, path: decoded.value }); +}; + +const parseIndexUri = (rest: string): Result<DiffyUriComponents, DiffyUriParseError> => { + if (rest === '') return err({ kind: 'emptyPath' }); + const decoded = decodePath(rest); + if (!decoded.ok) return decoded; + return ok({ rev: { kind: 'index' }, path: decoded.value }); +}; + +interface UriSplit { + readonly scheme: string; + readonly authority: string; + readonly rest: string; +} + +const splitUri = (uri: string): Result<UriSplit, DiffyUriParseError> => { + const sep = uri.indexOf(SCHEME_SEP); + if (sep < 0) return err({ kind: 'malformed', reason: 'no scheme separator' }); + const scheme = uri.slice(0, sep); + const afterScheme = uri.slice(sep + SCHEME_SEP.length); + const firstSlash = afterScheme.indexOf(PATH_SEP); + if (firstSlash < 0) return err({ kind: 'malformed', reason: 'no authority/path separator' }); + return ok({ + scheme, + authority: afterScheme.slice(0, firstSlash), + rest: afterScheme.slice(firstSlash + 1), + }); +}; + +export const parseDiffyUri = ( + uri: string, +): Result<DiffyUriComponents, DiffyUriParseError> => { + const split = splitUri(uri); + if (!split.ok) return split; + if (split.value.scheme !== SCHEME) { + return err({ kind: 'invalidScheme', got: split.value.scheme }); + } + if (split.value.authority === URI_AUTHORITIES.commit) return parseCommitUri(split.value.rest); + if (split.value.authority === URI_AUTHORITIES.index) return parseIndexUri(split.value.rest); + return err({ kind: 'invalidAuthority', got: split.value.authority }); +}; diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 0000000..8ac7978 --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,35 @@ +{ + "_agent_pmo": "74cf183", + "compilerOptions": { + "target": "ES2022", + "module": "Node16", + "moduleResolution": "Node16", + "lib": ["ES2022"], + "strict": true, + "noImplicitAny": true, + "strictNullChecks": true, + "strictFunctionTypes": true, + "strictBindCallApply": true, + "strictPropertyInitialization": true, + "noImplicitThis": true, + "alwaysStrict": true, + "noUnusedLocals": true, + "noUnusedParameters": true, + "noImplicitReturns": true, + "noFallthroughCasesInSwitch": true, + "exactOptionalPropertyTypes": true, + "noUncheckedIndexedAccess": true, + "noImplicitOverride": true, + "noPropertyAccessFromIndexSignature": true, + "forceConsistentCasingInFileNames": true, + "skipLibCheck": true, + "resolveJsonModule": true, + "declaration": true, + "declarationMap": true, + "sourceMap": true, + "outDir": "out", + "rootDir": "src" + }, + "include": ["src/**/*.ts"], + "exclude": ["node_modules", "out", "dist", "coverage", ".vscode-test"] +} From 37821c5ecae45613668b7e0b00ddab8bbcd84041 Mon Sep 17 00:00:00 2001 From: Christian Findlay <16697547+MelbourneDeveloper@users.noreply.github.com> Date: Sat, 23 May 2026 21:29:54 +1000 Subject: [PATCH 02/17] fixes --- .vscodeignore | 33 + README.md | 127 + coverage-thresholds.json | 2 +- package-lock.json | 6829 ++++++++++++++++++++++++ package.json | 96 + scripts/run-coverage.mjs | 39 + src/commands/compareFileWithCommit.ts | 51 + src/commands/compareTwoCommits.ts | 36 + src/commands/compareWith.ts | 48 + src/commands/compareWithPrevious.ts | 49 + src/commands/compareWithWorkingCopy.ts | 40 + src/commands/flow.ts | 202 + src/commands/historyItem.ts | 30 + src/commands/reopenLast.ts | 38 + src/commands/shared.ts | 106 + src/extension.ts | 121 + src/git/GitRepo.ts | 15 +- src/git/parsers.ts | 15 +- src/git/repoMatch.ts | 39 + src/logger.ts | 60 +- src/providers/DiffyContentProvider.ts | 48 + src/state.ts | 44 + src/test/runTests.ts | 49 + src/test/suite/activation.test.ts | 59 + src/test/suite/commands.test.ts | 379 ++ src/test/suite/contentProvider.test.ts | 71 + src/test/suite/helpers.ts | 167 + src/test/suite/index.ts | 26 + src/test/unit/format.test.ts | 164 + src/test/unit/historyItem.test.ts | 45 + src/test/unit/parsers.test.ts | 28 +- src/test/unit/repoMatch.test.ts | 75 + src/test/unit/result.test.ts | 2 +- src/test/unit/state.test.ts | 70 + src/ui/CommitPicker.ts | 36 + src/ui/FilePicker.ts | 40 + src/ui/RefPicker.ts | 33 + src/ui/SideBPicker.ts | 49 + src/ui/cancelled.ts | 5 + src/ui/format/fileItem.ts | 37 + src/ui/format/refLabel.ts | 11 + src/ui/format/relativeTime.ts | 20 + src/ui/runQuickPick.ts | 69 + src/vscodeGitApi.ts | 27 + test-fixtures/repo-seed/seed.sh | 57 + 45 files changed, 9550 insertions(+), 37 deletions(-) create mode 100644 .vscodeignore create mode 100644 README.md create mode 100644 package-lock.json create mode 100644 package.json create mode 100644 scripts/run-coverage.mjs create mode 100644 src/commands/compareFileWithCommit.ts create mode 100644 src/commands/compareTwoCommits.ts create mode 100644 src/commands/compareWith.ts create mode 100644 src/commands/compareWithPrevious.ts create mode 100644 src/commands/compareWithWorkingCopy.ts create mode 100644 src/commands/flow.ts create mode 100644 src/commands/historyItem.ts create mode 100644 src/commands/reopenLast.ts create mode 100644 src/commands/shared.ts create mode 100644 src/extension.ts create mode 100644 src/git/repoMatch.ts create mode 100644 src/providers/DiffyContentProvider.ts create mode 100644 src/state.ts create mode 100644 src/test/runTests.ts create mode 100644 src/test/suite/activation.test.ts create mode 100644 src/test/suite/commands.test.ts create mode 100644 src/test/suite/contentProvider.test.ts create mode 100644 src/test/suite/helpers.ts create mode 100644 src/test/suite/index.ts create mode 100644 src/test/unit/format.test.ts create mode 100644 src/test/unit/historyItem.test.ts create mode 100644 src/test/unit/repoMatch.test.ts create mode 100644 src/test/unit/state.test.ts create mode 100644 src/ui/CommitPicker.ts create mode 100644 src/ui/FilePicker.ts create mode 100644 src/ui/RefPicker.ts create mode 100644 src/ui/SideBPicker.ts create mode 100644 src/ui/cancelled.ts create mode 100644 src/ui/format/fileItem.ts create mode 100644 src/ui/format/refLabel.ts create mode 100644 src/ui/format/relativeTime.ts create mode 100644 src/ui/runQuickPick.ts create mode 100644 src/vscodeGitApi.ts create mode 100755 test-fixtures/repo-seed/seed.sh diff --git a/.vscodeignore b/.vscodeignore new file mode 100644 index 0000000..fa1dc30 --- /dev/null +++ b/.vscodeignore @@ -0,0 +1,33 @@ +.deslop-cache/** +.ghissues/** +.commandtree/** +.claude/** +.clinerules/** +.devcontainer/** +.github/** +.vscode/** +.vscode-test/** +.cursorrules +.windsurfrules +opencode.json +AGENTS.md +CLAUDE.md +.gitignore +.prettierrc.json +eslint.config.mjs +tsconfig.json +Makefile +coverage/** +coverage-thresholds.json +docs/** +scripts/** +src/** +test-fixtures/** +out/test/** +out/scripts/** +**/*.map +**/*.ts +!out/**/*.d.ts +**/tsconfig*.json +**/.eslintrc* +node_modules/.cache/** diff --git a/README.md b/README.md new file mode 100644 index 0000000..eb89023 --- /dev/null +++ b/README.md @@ -0,0 +1,127 @@ +# Diffy + +Pick two things and diff them — context-menu git diffing in VSCode. + +Diffy adds **no panels, no sidebars, no activity-bar icons**. Every feature hangs off the menus VSCode already has (SCM history, SCM changes, editor tab, file explorer) and a handful of palette commands. Pick a commit on the left, pick anything (another commit, a branch/tag, the index, the working copy) on the right, then drill through changed files with a QuickPick that stays open for as long as you need it. + +--- + +## How to access every command + +There are **seven** commands. Three are reachable from the Command Palette, four are reachable only from a right-click menu (because they need a target — a commit, a file — that the menu provides). + +### From the SCM **History view** (right-click a commit) + +Open the **Source Control** view (Ctrl/Cmd+Shift+G), expand a repository's **History** section, and **right-click any commit**. Three Diffy entries appear: + +| Menu label | Command ID | What it does | +| --- | --- | --- | +| **Diffy: Compare with…** | `diffy.compareWith` | The right-clicked commit is Side A. A QuickPick asks what Side B is: **Working Copy**, **Index**, **Pick a commit…**, or **Pick a branch or tag…**. Then a file QuickPick lists every changed file; selecting one opens `vscode.diff`. The picker stays open so you can open many files in a row. | +| **Diffy: Compare with Working Copy** | `diffy.compareWithWorkingCopy` | Same as above, but Side B is hardcoded to your on-disk working copy. Skips the Side B prompt. | +| **Diffy: Compare with Previous** | `diffy.compareWithPrevious` | Side A is the right-clicked commit, Side B is its first parent (`<sha>^1`). Use this for a classic "what changed in this commit?" view. | + +> These three are intentionally **hidden from the Command Palette** — they only make sense when invoked against a specific commit, which the right-click target provides. + +### From the SCM **Changes** view (right-click a changed file) + +In the Source Control view, **right-click any file** under **Changes**, **Staged Changes**, or **Merge Changes**: + +| Menu label | Command ID | What it does | +| --- | --- | --- | +| **Diffy: Compare with Commit…** | `diffy.compareFileWithCommit` | Pick a commit from a log QuickPick. Opens a single-file diff: that file at the chosen commit ↔ your working copy. | + +### From the **editor tab title** (right-click the file's tab) + +**Right-click the tab** of any open text file: + +| Menu label | Command ID | What it does | +| --- | --- | --- | +| **Diffy: Compare with Commit…** | `diffy.compareFileWithCommit` | Same as above, but the target file is the one whose tab you right-clicked. | + +### From the **File Explorer** (right-click a file in the tree) + +**Right-click any file** (not a folder) in the Explorer: + +| Menu label | Command ID | What it does | +| --- | --- | --- | +| **Diffy: Compare with Commit…** | `diffy.compareFileWithCommit` | Same again — the target is the file you clicked in the tree. | + +### From the **Command Palette** (Ctrl/Cmd+Shift+P) + +Type `Diffy:` to filter. Three entries are listed: + +| Palette label | Command ID | What it does | +| --- | --- | --- | +| **Diffy: Compare Two Commits** | `diffy.compareTwoCommits` | No target needed. QuickPick chain: pick repo → pick Side A commit → pick Side B (working copy / index / commit / ref) → pick files. | +| **Diffy: Compare with Commit…** | `diffy.compareFileWithCommit` | Uses the **currently focused editor's file** as the target. Same flow as the right-click version. If no editor is focused, Diffy will tell you to open a file first. | +| **Diffy: Reopen Last Comparison** | `diffy.reopenLast` | Reopens the file picker for the last A↔B comparison you made (stored per-workspace). Handy after closing the picker mid-review. | + +> **`Diffy: Show Logs`** (`diffy.showLogs`) exists but is hidden from the palette; it's reserved for the extension to surface its OutputChannel programmatically. Open it manually via **View → Output → "Diffy"**. + +--- + +## Quick visual reference + +``` +Source Control (Ctrl/Cmd+Shift+G) +└── History + └── <right-click a commit> + ├── Diffy: Compare with… ← pick anything for Side B + ├── Diffy: Compare with Working Copy ← Side B = on-disk + └── Diffy: Compare with Previous ← Side B = parent commit + +Source Control → Changes / Staged Changes +└── <right-click a file> + └── Diffy: Compare with Commit… ← pick commit; diff vs working copy + +Editor tab (right-click) ──┐ +File Explorer (right-click file) ──┼──► Diffy: Compare with Commit… + └── (uses that file as the target) + +Command Palette (Ctrl/Cmd+Shift+P) +├── Diffy: Compare Two Commits +├── Diffy: Compare with Commit… ← uses focused editor's file +└── Diffy: Reopen Last Comparison +``` + +--- + +## What Side B can be + +Whenever Diffy asks you to pick Side B, you get four choices: + +- **Working Copy** — the on-disk files in the repo (uncommitted changes included). +- **Index** — the staging area (what `git diff --cached` would compare against). +- **Pick a commit…** — choose from a QuickPick of recent log entries. +- **Pick a branch or tag…** — choose from all refs; resolved to its tip commit. + +## Diff titles + +Tabs that Diffy opens are titled human-first, e.g. `a1b2c3d ↔ Working Copy — src/foo.ts` or `a1b2c3d ↔ 9f8e7d6 — src/foo.ts`. No internal labels, no debug strings. + +## Requirements + +- VSCode `^1.85.0` +- Node `>=20` (for local development) +- Git on `PATH` +- The built-in **Git** extension (Diffy depends on it via `extensionDependencies`) + +## Development + +Standard make targets: + +```sh +make setup # install deps +make build # tsc +make test # fail-fast unit + e2e, enforces coverage threshold +make lint +make fmt +make ci # what CI runs +make package # build .vsix +``` + +See [CLAUDE.md](CLAUDE.md) for the full architecture and contributor rules. + +## License + +MIT diff --git a/coverage-thresholds.json b/coverage-thresholds.json index 446d033..3b711ee 100644 --- a/coverage-thresholds.json +++ b/coverage-thresholds.json @@ -1,5 +1,5 @@ { "_agent_pmo": "74cf183", "_doc": "Single source of truth for code coverage thresholds. See REPO-STANDARDS-SPEC [COVERAGE-THRESHOLDS-JSON]. NO GitHub repo variables. NO env vars. NO public `make coverage-check` target. This file is read by the internal `_coverage_check` recipe inside `make test`. `make test` exits non-zero if measured coverage < threshold. Thresholds are monotonically increasing — only ratchet UP, never down.", - "default_threshold": 0 + "default_threshold": 95 } diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 0000000..727f904 --- /dev/null +++ b/package-lock.json @@ -0,0 +1,6829 @@ +{ + "name": "diffy", + "version": "0.1.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "diffy", + "version": "0.1.0", + "license": "MIT", + "dependencies": { + "pino": "^9.5.0" + }, + "devDependencies": { + "@eslint/js": "^9.13.0", + "@types/mocha": "^10.0.9", + "@types/node": "^20.16.0", + "@types/vscode": "^1.85.0", + "@vscode/test-electron": "^2.4.1", + "@vscode/vsce": "^3.2.0", + "c8": "^10.1.2", + "eslint": "^9.13.0", + "glob": "^11.0.0", + "mocha": "^10.8.2", + "prettier": "^3.3.3", + "typescript": "^5.6.3", + "typescript-eslint": "^8.12.2" + }, + "engines": { + "node": ">=20", + "vscode": "^1.85.0" + } + }, + "node_modules/@azu/format-text": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@azu/format-text/-/format-text-1.0.2.tgz", + "integrity": "sha512-Swi4N7Edy1Eqq82GxgEECXSSLyn6GOb5htRFPzBDdUkECGXtlf12ynO5oJSpWKPwCaUssOu7NfhDcCWpIC6Ywg==", + "dev": true, + "license": "BSD-3-Clause" + }, + "node_modules/@azu/style-format": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@azu/style-format/-/style-format-1.0.1.tgz", + "integrity": "sha512-AHcTojlNBdD/3/KxIKlg8sxIWHfOtQszLvOpagLTO+bjC3u7SAszu1lf//u7JJC50aUSH+BVWDD/KvaA6Gfn5g==", + "dev": true, + "license": "WTFPL", + "dependencies": { + "@azu/format-text": "^1.0.1" + } + }, + "node_modules/@azure/abort-controller": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/@azure/abort-controller/-/abort-controller-2.1.2.tgz", + "integrity": "sha512-nBrLsEWm4J2u5LpAPjxADTlq3trDgVZZXHNKabeXZtpq3d3AbN/KGO82R87rdDz5/lYB024rtEf10/q0urNgsA==", + "dev": true, + "license": "MIT", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@azure/core-auth": { + "version": "1.10.1", + "resolved": "https://registry.npmjs.org/@azure/core-auth/-/core-auth-1.10.1.tgz", + "integrity": "sha512-ykRMW8PjVAn+RS6ww5cmK9U2CyH9p4Q88YJwvUslfuMmN98w/2rdGRLPqJYObapBCdzBVeDgYWdJnFPFb7qzpg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@azure/abort-controller": "^2.1.2", + "@azure/core-util": "^1.13.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@azure/core-client": { + "version": "1.10.1", + "resolved": "https://registry.npmjs.org/@azure/core-client/-/core-client-1.10.1.tgz", + "integrity": "sha512-Nh5PhEOeY6PrnxNPsEHRr9eimxLwgLlpmguQaHKBinFYA/RU9+kOYVOQqOrTsCL+KSxrLLl1gD8Dk5BFW/7l/w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@azure/abort-controller": "^2.1.2", + "@azure/core-auth": "^1.10.0", + "@azure/core-rest-pipeline": "^1.22.0", + "@azure/core-tracing": "^1.3.0", + "@azure/core-util": "^1.13.0", + "@azure/logger": "^1.3.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@azure/core-rest-pipeline": { + "version": "1.23.0", + "resolved": "https://registry.npmjs.org/@azure/core-rest-pipeline/-/core-rest-pipeline-1.23.0.tgz", + "integrity": "sha512-Evs1INHo+jUjwHi1T6SG6Ua/LHOQBCLuKEEE6efIpt4ZOoNonaT1kP32GoOcdNDbfqsD2445CPri3MubBy5DEQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@azure/abort-controller": "^2.1.2", + "@azure/core-auth": "^1.10.0", + "@azure/core-tracing": "^1.3.0", + "@azure/core-util": "^1.13.0", + "@azure/logger": "^1.3.0", + "@typespec/ts-http-runtime": "^0.3.4", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@azure/core-tracing": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/@azure/core-tracing/-/core-tracing-1.3.1.tgz", + "integrity": "sha512-9MWKevR7Hz8kNzzPLfX4EAtGM2b8mr50HPDBvio96bURP/9C+HjdH3sBlLSNNrvRAr5/k/svoH457gB5IKpmwQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@azure/core-util": { + "version": "1.13.1", + "resolved": "https://registry.npmjs.org/@azure/core-util/-/core-util-1.13.1.tgz", + "integrity": "sha512-XPArKLzsvl0Hf0CaGyKHUyVgF7oDnhKoP85Xv6M4StF/1AhfORhZudHtOyf2s+FcbuQ9dPRAjB8J2KvRRMUK2A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@azure/abort-controller": "^2.1.2", + "@typespec/ts-http-runtime": "^0.3.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@azure/identity": { + "version": "4.13.1", + "resolved": "https://registry.npmjs.org/@azure/identity/-/identity-4.13.1.tgz", + "integrity": "sha512-5C/2WD5Vb1lHnZS16dNQRPMjN6oV/Upba+C9nBIs15PmOi6A3ZGs4Lr2u60zw4S04gi+u3cEXiqTVP7M4Pz3kw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@azure/abort-controller": "^2.0.0", + "@azure/core-auth": "^1.9.0", + "@azure/core-client": "^1.9.2", + "@azure/core-rest-pipeline": "^1.17.0", + "@azure/core-tracing": "^1.0.0", + "@azure/core-util": "^1.11.0", + "@azure/logger": "^1.0.0", + "@azure/msal-browser": "^5.5.0", + "@azure/msal-node": "^5.1.0", + "open": "^10.1.0", + "tslib": "^2.2.0" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@azure/logger": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/@azure/logger/-/logger-1.3.0.tgz", + "integrity": "sha512-fCqPIfOcLE+CGqGPd66c8bZpwAji98tZ4JI9i/mlTNTlsIWslCfpg48s/ypyLxZTump5sypjrKn2/kY7q8oAbA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typespec/ts-http-runtime": "^0.3.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@azure/msal-browser": { + "version": "5.11.0", + "resolved": "https://registry.npmjs.org/@azure/msal-browser/-/msal-browser-5.11.0.tgz", + "integrity": "sha512-zkGNYS3TwY8lUpPIafAmsFCYZbgFixY9y/LZB9GUg0IILoHTqpN26j5OrkL1AQThh/YdZsawe4iWXfp85lFVxg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@azure/msal-common": "16.6.2" + }, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/@azure/msal-common": { + "version": "16.6.2", + "resolved": "https://registry.npmjs.org/@azure/msal-common/-/msal-common-16.6.2.tgz", + "integrity": "sha512-hQjjsekAjB00cM1EmatWJlzhEoK2Qhz7Rj5gvM6tYf8iL7RM3tkxlpU9fG0+ofkulzg9AEEA6dIEnSmDr5ZqUA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/@azure/msal-node": { + "version": "5.2.2", + "resolved": "https://registry.npmjs.org/@azure/msal-node/-/msal-node-5.2.2.tgz", + "integrity": "sha512-toS+2AePxqyzb0YOKttDOOiSl3jrkK9aiqIvpurpis0O34QcIS5gToqrgT39p04Dpxw3YoUU0lxJKTpSFFfA6Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@azure/msal-common": "16.6.2", + "jsonwebtoken": "^9.0.0" + }, + "engines": { + "node": ">=20" + } + }, + "node_modules/@babel/code-frame": { + "version": "7.29.0", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.29.0.tgz", + "integrity": "sha512-9NhCeYjq9+3uxgdtp20LSiJXJvN0FeCtNGpJxuMFZ1Kv3cWUNb6DOhJwUvcVCzKGR66cw4njwM6hrJLqgOwbcw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-validator-identifier": "^7.28.5", + "js-tokens": "^4.0.0", + "picocolors": "^1.1.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-identifier": { + "version": "7.28.5", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.28.5.tgz", + "integrity": "sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@bcoe/v8-coverage": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@bcoe/v8-coverage/-/v8-coverage-1.0.2.tgz", + "integrity": "sha512-6zABk/ECA/QYSCQ1NGiVwwbQerUCZ+TQbp64Q3AgmfNvurHH0j8TtXa1qbShXA6qqkpAj4V5W8pP6mLe1mcMqA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + } + }, + "node_modules/@eslint-community/eslint-utils": { + "version": "4.9.1", + "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.9.1.tgz", + "integrity": "sha512-phrYmNiYppR7znFEdqgfWHXR6NCkZEK7hwWDHZUjit/2/U0r6XvkDl0SYnoM51Hq7FhCGdLDT6zxCCOY1hexsQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "eslint-visitor-keys": "^3.4.3" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + }, + "peerDependencies": { + "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" + } + }, + "node_modules/@eslint-community/eslint-utils/node_modules/eslint-visitor-keys": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", + "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@eslint-community/regexpp": { + "version": "4.12.2", + "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.12.2.tgz", + "integrity": "sha512-EriSTlt5OC9/7SXkRSCAhfSxxoSUgBm33OH+IkwbdpgoqsSsUg7y3uh+IICI/Qg4BBWr3U2i39RpmycbxMq4ew==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.0.0 || ^14.0.0 || >=16.0.0" + } + }, + "node_modules/@eslint/config-array": { + "version": "0.21.2", + "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.21.2.tgz", + "integrity": "sha512-nJl2KGTlrf9GjLimgIru+V/mzgSK0ABCDQRvxw5BjURL7WfH5uoWmizbH7QB6MmnMBd8cIC9uceWnezL1VZWWw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@eslint/object-schema": "^2.1.7", + "debug": "^4.3.1", + "minimatch": "^3.1.5" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/config-helpers": { + "version": "0.4.2", + "resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.4.2.tgz", + "integrity": "sha512-gBrxN88gOIf3R7ja5K9slwNayVcZgK6SOUORm2uBzTeIEfeVaIhOpCtTox3P6R7o2jLFwLFTLnC7kU/RGcYEgw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@eslint/core": "^0.17.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/core": { + "version": "0.17.0", + "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.17.0.tgz", + "integrity": "sha512-yL/sLrpmtDaFEiUj1osRP4TI2MDz1AddJL+jZ7KSqvBuliN4xqYY54IfdN8qD8Toa6g1iloph1fxQNkjOxrrpQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@types/json-schema": "^7.0.15" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/eslintrc": { + "version": "3.3.5", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-3.3.5.tgz", + "integrity": "sha512-4IlJx0X0qftVsN5E+/vGujTRIFtwuLbNsVUe7TO6zYPDR1O6nFwvwhIKEKSrl6dZchmYBITazxKoUYOjdtjlRg==", + "dev": true, + "license": "MIT", + "dependencies": { + "ajv": "^6.14.0", + "debug": "^4.3.2", + "espree": "^10.0.1", + "globals": "^14.0.0", + "ignore": "^5.2.0", + "import-fresh": "^3.2.1", + "js-yaml": "^4.1.1", + "minimatch": "^3.1.5", + "strip-json-comments": "^3.1.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@eslint/eslintrc/node_modules/ajv": { + "version": "6.15.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.15.0.tgz", + "integrity": "sha512-fgFx7Hfoq60ytK2c7DhnF8jIvzYgOMxfugjLOSMHjLIPgenqa7S7oaagATUq99mV6IYvN2tRmC0wnTYX6iPbMw==", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/@eslint/eslintrc/node_modules/json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true, + "license": "MIT" + }, + "node_modules/@eslint/js": { + "version": "9.39.4", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.39.4.tgz", + "integrity": "sha512-nE7DEIchvtiFTwBw4Lfbu59PG+kCofhjsKaCWzxTpt4lfRjRMqG6uMBzKXuEcyXhOHoUp9riAm7/aWYGhXZ9cw==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://eslint.org/donate" + } + }, + "node_modules/@eslint/object-schema": { + "version": "2.1.7", + "resolved": "https://registry.npmjs.org/@eslint/object-schema/-/object-schema-2.1.7.tgz", + "integrity": "sha512-VtAOaymWVfZcmZbp6E2mympDIHvyjXs/12LqWYjVw6qjrfF+VK+fyG33kChz3nnK+SU5/NeHOqrTEHS8sXO3OA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/plugin-kit": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.4.1.tgz", + "integrity": "sha512-43/qtrDUokr7LJqoF2c3+RInu/t4zfrpYdoSDfYyhg52rwLV6TnOvdG4fXm7IkSB3wErkcmJS9iEhjVtOSEjjA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@eslint/core": "^0.17.0", + "levn": "^0.4.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@humanfs/core": { + "version": "0.19.2", + "resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.2.tgz", + "integrity": "sha512-UhXNm+CFMWcbChXywFwkmhqjs3PRCmcSa/hfBgLIb7oQ5HNb1wS0icWsGtSAUNgefHeI+eBrA8I1fxmbHsGdvA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@humanfs/types": "^0.15.0" + }, + "engines": { + "node": ">=18.18.0" + } + }, + "node_modules/@humanfs/node": { + "version": "0.16.8", + "resolved": "https://registry.npmjs.org/@humanfs/node/-/node-0.16.8.tgz", + "integrity": "sha512-gE1eQNZ3R++kTzFUpdGlpmy8kDZD/MLyHqDwqjkVQI0JMdI1D51sy1H958PNXYkM2rAac7e5/CnIKZrHtPh3BQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@humanfs/core": "^0.19.2", + "@humanfs/types": "^0.15.0", + "@humanwhocodes/retry": "^0.4.0" + }, + "engines": { + "node": ">=18.18.0" + } + }, + "node_modules/@humanfs/types": { + "version": "0.15.0", + "resolved": "https://registry.npmjs.org/@humanfs/types/-/types-0.15.0.tgz", + "integrity": "sha512-ZZ1w0aoQkwuUuC7Yf+7sdeaNfqQiiLcSRbfI08oAxqLtpXQr9AIVX7Ay7HLDuiLYAaFPu8oBYNq/QIi9URHJ3Q==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=18.18.0" + } + }, + "node_modules/@humanwhocodes/module-importer": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", + "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=12.22" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, + "node_modules/@humanwhocodes/retry": { + "version": "0.4.3", + "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.4.3.tgz", + "integrity": "sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=18.18" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, + "node_modules/@isaacs/cliui": { + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-9.0.0.tgz", + "integrity": "sha512-AokJm4tuBHillT+FpMtxQ60n8ObyXBatq7jD2/JA9dxbDDokKQm8KMht5ibGzLVU9IJDIKK4TPKgMHEYMn3lMg==", + "dev": true, + "license": "BlueOak-1.0.0", + "engines": { + "node": ">=18" + } + }, + "node_modules/@istanbuljs/schema": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.6.tgz", + "integrity": "sha512-+Sg6GCR/wy1oSmQDFq4LQDAhm3ETKnorxN+y5nbLULOR3P0c14f2Wurzj3/xqPXtasLFfHd5iRFQ7AJt4KH2cw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", + "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.0.0" + } + }, + "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/@jridgewell/trace-mapping": { + "version": "0.3.31", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz", + "integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" + } + }, + "node_modules/@nodelib/fs.scandir": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", + "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@nodelib/fs.stat": "2.0.5", + "run-parallel": "^1.1.9" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.stat": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", + "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.walk": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", + "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@nodelib/fs.scandir": "2.1.5", + "fastq": "^1.6.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@pinojs/redact": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/@pinojs/redact/-/redact-0.4.0.tgz", + "integrity": "sha512-k2ENnmBugE/rzQfEcdWHcCY+/FM3VLzH9cYEsbdsoqrvzAKRhUZeRNhAZvB8OitQJ1TBed3yqWtdjzS6wJKBwg==", + "license": "MIT" + }, + "node_modules/@pkgjs/parseargs": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", + "integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==", + "dev": true, + "license": "MIT", + "optional": true, + "engines": { + "node": ">=14" + } + }, + "node_modules/@secretlint/config-creator": { + "version": "10.2.2", + "resolved": "https://registry.npmjs.org/@secretlint/config-creator/-/config-creator-10.2.2.tgz", + "integrity": "sha512-BynOBe7Hn3LJjb3CqCHZjeNB09s/vgf0baBaHVw67w7gHF0d25c3ZsZ5+vv8TgwSchRdUCRrbbcq5i2B1fJ2QQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@secretlint/types": "^10.2.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@secretlint/config-loader": { + "version": "10.2.2", + "resolved": "https://registry.npmjs.org/@secretlint/config-loader/-/config-loader-10.2.2.tgz", + "integrity": "sha512-ndjjQNgLg4DIcMJp4iaRD6xb9ijWQZVbd9694Ol2IszBIbGPPkwZHzJYKICbTBmh6AH/pLr0CiCaWdGJU7RbpQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@secretlint/profiler": "^10.2.2", + "@secretlint/resolver": "^10.2.2", + "@secretlint/types": "^10.2.2", + "ajv": "^8.17.1", + "debug": "^4.4.1", + "rc-config-loader": "^4.1.3" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@secretlint/core": { + "version": "10.2.2", + "resolved": "https://registry.npmjs.org/@secretlint/core/-/core-10.2.2.tgz", + "integrity": "sha512-6rdwBwLP9+TO3rRjMVW1tX+lQeo5gBbxl1I5F8nh8bgGtKwdlCMhMKsBWzWg1ostxx/tIG7OjZI0/BxsP8bUgw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@secretlint/profiler": "^10.2.2", + "@secretlint/types": "^10.2.2", + "debug": "^4.4.1", + "structured-source": "^4.0.0" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@secretlint/formatter": { + "version": "10.2.2", + "resolved": "https://registry.npmjs.org/@secretlint/formatter/-/formatter-10.2.2.tgz", + "integrity": "sha512-10f/eKV+8YdGKNQmoDUD1QnYL7TzhI2kzyx95vsJKbEa8akzLAR5ZrWIZ3LbcMmBLzxlSQMMccRmi05yDQ5YDA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@secretlint/resolver": "^10.2.2", + "@secretlint/types": "^10.2.2", + "@textlint/linter-formatter": "^15.2.0", + "@textlint/module-interop": "^15.2.0", + "@textlint/types": "^15.2.0", + "chalk": "^5.4.1", + "debug": "^4.4.1", + "pluralize": "^8.0.0", + "strip-ansi": "^7.1.0", + "table": "^6.9.0", + "terminal-link": "^4.0.0" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@secretlint/formatter/node_modules/chalk": { + "version": "5.6.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.6.2.tgz", + "integrity": "sha512-7NzBL0rN6fMUW+f7A6Io4h40qQlG+xGmtMxfbnH/K7TAtt8JQWVQK+6g0UXKMeVJoyV5EkkNsErQ8pVD3bLHbA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.17.0 || ^14.13 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/@secretlint/node": { + "version": "10.2.2", + "resolved": "https://registry.npmjs.org/@secretlint/node/-/node-10.2.2.tgz", + "integrity": "sha512-eZGJQgcg/3WRBwX1bRnss7RmHHK/YlP/l7zOQsrjexYt6l+JJa5YhUmHbuGXS94yW0++3YkEJp0kQGYhiw1DMQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@secretlint/config-loader": "^10.2.2", + "@secretlint/core": "^10.2.2", + "@secretlint/formatter": "^10.2.2", + "@secretlint/profiler": "^10.2.2", + "@secretlint/source-creator": "^10.2.2", + "@secretlint/types": "^10.2.2", + "debug": "^4.4.1", + "p-map": "^7.0.3" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@secretlint/profiler": { + "version": "10.2.2", + "resolved": "https://registry.npmjs.org/@secretlint/profiler/-/profiler-10.2.2.tgz", + "integrity": "sha512-qm9rWfkh/o8OvzMIfY8a5bCmgIniSpltbVlUVl983zDG1bUuQNd1/5lUEeWx5o/WJ99bXxS7yNI4/KIXfHexig==", + "dev": true, + "license": "MIT" + }, + "node_modules/@secretlint/resolver": { + "version": "10.2.2", + "resolved": "https://registry.npmjs.org/@secretlint/resolver/-/resolver-10.2.2.tgz", + "integrity": "sha512-3md0cp12e+Ae5V+crPQYGd6aaO7ahw95s28OlULGyclyyUtf861UoRGS2prnUrKh7MZb23kdDOyGCYb9br5e4w==", + "dev": true, + "license": "MIT" + }, + "node_modules/@secretlint/secretlint-formatter-sarif": { + "version": "10.2.2", + "resolved": "https://registry.npmjs.org/@secretlint/secretlint-formatter-sarif/-/secretlint-formatter-sarif-10.2.2.tgz", + "integrity": "sha512-ojiF9TGRKJJw308DnYBucHxkpNovDNu1XvPh7IfUp0A12gzTtxuWDqdpuVezL7/IP8Ua7mp5/VkDMN9OLp1doQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "node-sarif-builder": "^3.2.0" + } + }, + "node_modules/@secretlint/secretlint-rule-no-dotenv": { + "version": "10.2.2", + "resolved": "https://registry.npmjs.org/@secretlint/secretlint-rule-no-dotenv/-/secretlint-rule-no-dotenv-10.2.2.tgz", + "integrity": "sha512-KJRbIShA9DVc5Va3yArtJ6QDzGjg3PRa1uYp9As4RsyKtKSSZjI64jVca57FZ8gbuk4em0/0Jq+uy6485wxIdg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@secretlint/types": "^10.2.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@secretlint/secretlint-rule-preset-recommend": { + "version": "10.2.2", + "resolved": "https://registry.npmjs.org/@secretlint/secretlint-rule-preset-recommend/-/secretlint-rule-preset-recommend-10.2.2.tgz", + "integrity": "sha512-K3jPqjva8bQndDKJqctnGfwuAxU2n9XNCPtbXVI5JvC7FnQiNg/yWlQPbMUlBXtBoBGFYp08A94m6fvtc9v+zA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@secretlint/source-creator": { + "version": "10.2.2", + "resolved": "https://registry.npmjs.org/@secretlint/source-creator/-/source-creator-10.2.2.tgz", + "integrity": "sha512-h6I87xJfwfUTgQ7irWq7UTdq/Bm1RuQ/fYhA3dtTIAop5BwSFmZyrchph4WcoEvbN460BWKmk4RYSvPElIIvxw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@secretlint/types": "^10.2.2", + "istextorbinary": "^9.5.0" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@secretlint/types": { + "version": "10.2.2", + "resolved": "https://registry.npmjs.org/@secretlint/types/-/types-10.2.2.tgz", + "integrity": "sha512-Nqc90v4lWCXyakD6xNyNACBJNJ0tNCwj2WNk/7ivyacYHxiITVgmLUFXTBOeCdy79iz6HtN9Y31uw/jbLrdOAg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@sindresorhus/merge-streams": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/@sindresorhus/merge-streams/-/merge-streams-2.3.0.tgz", + "integrity": "sha512-LtoMMhxAlorcGhmFYI+LhPgbPZCkgP6ra1YL604EeF6U98pLlQ3iWIGMdWSC+vWmPBWBNgmDBAhnAobLROJmwg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@textlint/ast-node-types": { + "version": "15.7.1", + "resolved": "https://registry.npmjs.org/@textlint/ast-node-types/-/ast-node-types-15.7.1.tgz", + "integrity": "sha512-Wii5UgUKFEh9Uv6wbq1zr4/Kf+dtjiUuzPrrXzKp8H+ifkvKNzi23V4Nz+6wVyHQn5T28AFuc8VH8OtzvGYecA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@textlint/linter-formatter": { + "version": "15.7.1", + "resolved": "https://registry.npmjs.org/@textlint/linter-formatter/-/linter-formatter-15.7.1.tgz", + "integrity": "sha512-TdwZ/debWYFD05K3CcoHtwvnCrza29wZxD+BjDTk/V5N7iRqkK1dTTHSD4A8AIgROLiDkHJmIKQbasbmsg8AvA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@azu/format-text": "^1.0.2", + "@azu/style-format": "^1.0.1", + "@textlint/module-interop": "15.7.1", + "@textlint/resolver": "15.7.1", + "@textlint/types": "15.7.1", + "chalk": "^4.1.2", + "debug": "^4.4.3", + "js-yaml": "^4.1.1", + "lodash": "^4.18.1", + "pluralize": "^2.0.0", + "string-width": "^4.2.3", + "strip-ansi": "^6.0.1", + "table": "^6.9.0", + "text-table": "^0.2.0" + } + }, + "node_modules/@textlint/linter-formatter/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/@textlint/linter-formatter/node_modules/pluralize": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/pluralize/-/pluralize-2.0.0.tgz", + "integrity": "sha512-TqNZzQCD4S42De9IfnnBvILN7HAW7riLqsCyp8lgjXeysyPlX5HhqKAcJHHHb9XskE4/a+7VGC9zzx8Ls0jOAw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@textlint/linter-formatter/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@textlint/module-interop": { + "version": "15.7.1", + "resolved": "https://registry.npmjs.org/@textlint/module-interop/-/module-interop-15.7.1.tgz", + "integrity": "sha512-Jg+sQW2L/cRJypk59wtcMUVVpt8vmit5ZMT3gUnFwevP3A6Qp1HfOtUy9ObT4hBX3lOSGT/ekcCDxR1pL7uH1g==", + "dev": true, + "license": "MIT" + }, + "node_modules/@textlint/resolver": { + "version": "15.7.1", + "resolved": "https://registry.npmjs.org/@textlint/resolver/-/resolver-15.7.1.tgz", + "integrity": "sha512-8XnO0pgF6mXnm41VvWmBbEIdGPhiCUt31uLZkOis1ECeg/1SoUcIT6Mx/F0e1rukq8l0UlOSeY9a31CsvRMK0g==", + "dev": true, + "license": "MIT" + }, + "node_modules/@textlint/types": { + "version": "15.7.1", + "resolved": "https://registry.npmjs.org/@textlint/types/-/types-15.7.1.tgz", + "integrity": "sha512-Vye/GmFNBTgVzZFtIFJTmLB+s2A7oIADxNG6r9UhfPuY+Czv0z5G3xeyFZZudPlfxURsKUyPIU5XsjOFqVp33A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@textlint/ast-node-types": "15.7.1" + } + }, + "node_modules/@types/estree": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.9.tgz", + "integrity": "sha512-GhdPgy1el4/ImP05X05Uw4cw2/M93BCUmnEvWZNStlCzEKME4Fkk+YpoA5OiHNQmoS7Cafb8Xa3Pya8m1Qrzeg==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/istanbul-lib-coverage": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.6.tgz", + "integrity": "sha512-2QF/t/auWm0lsy8XtKVPG19v3sSOQlJe/YHZgfjb/KBBHOGSV+J2q/S671rcq9uTBrLAXmZpqJiaQbMT+zNU1w==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/json-schema": { + "version": "7.0.15", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", + "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/mocha": { + "version": "10.0.10", + "resolved": "https://registry.npmjs.org/@types/mocha/-/mocha-10.0.10.tgz", + "integrity": "sha512-xPyYSz1cMPnJQhl0CLMH68j3gprKZaTjG3s5Vi+fDgx+uhG9NOXwbVt52eFS8ECyXhyKcjDLCBEqBExKuiZb7Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/node": { + "version": "20.19.41", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.19.41.tgz", + "integrity": "sha512-ECymXOukMnOoVkC2bb1Vc/w/836DXncOg5m8Xj1RH7xSHZJWNYY6Zh7EH477vcnD5egKNNfy2RpNOmuChhFPgQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "undici-types": "~6.21.0" + } + }, + "node_modules/@types/normalize-package-data": { + "version": "2.4.4", + "resolved": "https://registry.npmjs.org/@types/normalize-package-data/-/normalize-package-data-2.4.4.tgz", + "integrity": "sha512-37i+OaWTh9qeK4LSHPsyRC7NahnGotNuZvjLSgcPzblpHB3rrCJxAOgI5gCdKm7coonsaX1Of0ILiTcnZjbfxA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/sarif": { + "version": "2.1.7", + "resolved": "https://registry.npmjs.org/@types/sarif/-/sarif-2.1.7.tgz", + "integrity": "sha512-kRz0VEkJqWLf1LLVN4pT1cg1Z9wAuvI6L97V3m2f5B76Tg8d413ddvLBPTEHAZJlnn4XSvu0FkZtViCQGVyrXQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/vscode": { + "version": "1.120.0", + "resolved": "https://registry.npmjs.org/@types/vscode/-/vscode-1.120.0.tgz", + "integrity": "sha512-feaT4Rst+FkTch5zz/ZbNCxoIvo55YU80Be2kiL7OJcod4+CUYf2lUBPdIJzozNnSEMq1VRTGrWEcCGFB3fBmA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@typescript-eslint/eslint-plugin": { + "version": "8.59.4", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.59.4.tgz", + "integrity": "sha512-PegsU+XfyJJNjd4+u/k6f9yTyp0lEXXiPopUNobZcIAUJFGICFLN+sP0Rb3JehVmiij1Ph0dFGYqODoRo/2+6A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/regexpp": "^4.12.2", + "@typescript-eslint/scope-manager": "8.59.4", + "@typescript-eslint/type-utils": "8.59.4", + "@typescript-eslint/utils": "8.59.4", + "@typescript-eslint/visitor-keys": "8.59.4", + "ignore": "^7.0.5", + "natural-compare": "^1.4.0", + "ts-api-utils": "^2.5.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "@typescript-eslint/parser": "^8.59.4", + "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", + "typescript": ">=4.8.4 <6.1.0" + } + }, + "node_modules/@typescript-eslint/eslint-plugin/node_modules/ignore": { + "version": "7.0.5", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-7.0.5.tgz", + "integrity": "sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, + "node_modules/@typescript-eslint/parser": { + "version": "8.59.4", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.59.4.tgz", + "integrity": "sha512-zORHqO/tuhxY1zWuTvMUqddRxpiFJ72xVfcNoWpqdLjs6lfPbuQBJuW4pk+49/uBMy7Ssr4bzgjiKmmDB1UbZQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/scope-manager": "8.59.4", + "@typescript-eslint/types": "8.59.4", + "@typescript-eslint/typescript-estree": "8.59.4", + "@typescript-eslint/visitor-keys": "8.59.4", + "debug": "^4.4.3" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", + "typescript": ">=4.8.4 <6.1.0" + } + }, + "node_modules/@typescript-eslint/project-service": { + "version": "8.59.4", + "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.59.4.tgz", + "integrity": "sha512-Ly00Vu4oAacfDeHp2Zg85ioNG6l8HG+tN1D7J+xTHSxu9y0awYKJ2zH1rFBn8ZSfuGK+7FxK3Cgl3uAz0aZZLg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/tsconfig-utils": "^8.59.4", + "@typescript-eslint/types": "^8.59.4", + "debug": "^4.4.3" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "typescript": ">=4.8.4 <6.1.0" + } + }, + "node_modules/@typescript-eslint/scope-manager": { + "version": "8.59.4", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.59.4.tgz", + "integrity": "sha512-mUeR/3H1WrTAddJrwut8OoPjfauaztMQmRwV5fQTUyNVJCLiUXXe4lGEyYIL2oFDpP7UtgbGJXCt72wT0z2S3Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "8.59.4", + "@typescript-eslint/visitor-keys": "8.59.4" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/tsconfig-utils": { + "version": "8.59.4", + "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.59.4.tgz", + "integrity": "sha512-DLCpnKgD4alVxTBSKulK+gU1KCqOgUXfDRDXh2mZgzokQKa/70ax93I2uVO3m/LLvIAtWZIFoiifudmIqAxpMA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "typescript": ">=4.8.4 <6.1.0" + } + }, + "node_modules/@typescript-eslint/type-utils": { + "version": "8.59.4", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.59.4.tgz", + "integrity": "sha512-uonTuPAAKr9XaBGqJ3LjYTh72zy5DyGesljO9gtmk/eFW0W1fRHjnwVYKB35Lm8d5Q5CluEW3gPHjTvZTmgrfA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "8.59.4", + "@typescript-eslint/typescript-estree": "8.59.4", + "@typescript-eslint/utils": "8.59.4", + "debug": "^4.4.3", + "ts-api-utils": "^2.5.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", + "typescript": ">=4.8.4 <6.1.0" + } + }, + "node_modules/@typescript-eslint/types": { + "version": "8.59.4", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.59.4.tgz", + "integrity": "sha512-F1o7WJcCq+bc8dwcO/YsSEOudAH8RDtaOhM6wcAQhcUsFhnWQl81JKy48q1hoxAU0qrzM89+31GYh1515Zde3Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/typescript-estree": { + "version": "8.59.4", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.59.4.tgz", + "integrity": "sha512-F+RuOmcDXo4+TPdfd/TCLS3m2nw8gE9XXyZLrA3JBfaA5tz9TtdkyD3YJFmPxulyc2cKbEok/CvFE3MgSLWnag==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/project-service": "8.59.4", + "@typescript-eslint/tsconfig-utils": "8.59.4", + "@typescript-eslint/types": "8.59.4", + "@typescript-eslint/visitor-keys": "8.59.4", + "debug": "^4.4.3", + "minimatch": "^10.2.2", + "semver": "^7.7.3", + "tinyglobby": "^0.2.15", + "ts-api-utils": "^2.5.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "typescript": ">=4.8.4 <6.1.0" + } + }, + "node_modules/@typescript-eslint/typescript-estree/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==", + "dev": true, + "license": "MIT", + "engines": { + "node": "18 || 20 || >=22" + } + }, + "node_modules/@typescript-eslint/typescript-estree/node_modules/brace-expansion": { + "version": "5.0.6", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.6.tgz", + "integrity": "sha512-kLpxurY4Z4r9sgMsyG0Z9uzsBlgiU/EFKhj/h91/8yHu0edo7XuixOIH3VcJ8kkxs6/jPzoI6U9Vj3WqbMQ94g==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^4.0.2" + }, + "engines": { + "node": "18 || 20 || >=22" + } + }, + "node_modules/@typescript-eslint/typescript-estree/node_modules/minimatch": { + "version": "10.2.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.5.tgz", + "integrity": "sha512-MULkVLfKGYDFYejP07QOurDLLQpcjk7Fw+7jXS2R2czRQzR56yHRveU5NDJEOviH+hETZKSkIk5c+T23GjFUMg==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "brace-expansion": "^5.0.5" + }, + "engines": { + "node": "18 || 20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/@typescript-eslint/utils": { + "version": "8.59.4", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.59.4.tgz", + "integrity": "sha512-cYXeNAUsG4lJo5dbc1FcKm+JwIWrj1/UpTORsC6tGMjEZ81DYcvIr9/ueikhMa/Y/gDQYGp+YX9/xQrXje5BJw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/eslint-utils": "^4.9.1", + "@typescript-eslint/scope-manager": "8.59.4", + "@typescript-eslint/types": "8.59.4", + "@typescript-eslint/typescript-estree": "8.59.4" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", + "typescript": ">=4.8.4 <6.1.0" + } + }, + "node_modules/@typescript-eslint/visitor-keys": { + "version": "8.59.4", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.59.4.tgz", + "integrity": "sha512-U3gxVaDVnuZKhSspW/MzMxE1kq7zOdc072FcSNoqA1I9p8HyKbBFfEHoWckBAMgNMph4MamwS5iTVzFmrnt8TQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "8.59.4", + "eslint-visitor-keys": "^5.0.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/visitor-keys/node_modules/eslint-visitor-keys": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-5.0.1.tgz", + "integrity": "sha512-tD40eHxA35h0PEIZNeIjkHoDR4YjjJp34biM0mDvplBe//mB+IHCqHDGV7pxF+7MklTvighcCPPZC7ynWyjdTA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=24" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@typespec/ts-http-runtime": { + "version": "0.3.5", + "resolved": "https://registry.npmjs.org/@typespec/ts-http-runtime/-/ts-http-runtime-0.3.5.tgz", + "integrity": "sha512-yURCknZhvywvQItHMMmFSo+fq5arCUIyz/CVk7jD89MSai7dkaX8ufjCWp3NttLojoTVbcE72ri+be/TnEbMHw==", + "dev": true, + "license": "MIT", + "dependencies": { + "http-proxy-agent": "^7.0.0", + "https-proxy-agent": "^7.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@vscode/test-electron": { + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/@vscode/test-electron/-/test-electron-2.5.2.tgz", + "integrity": "sha512-8ukpxv4wYe0iWMRQU18jhzJOHkeGKbnw7xWRX3Zw1WJA4cEKbHcmmLPdPrPtL6rhDcrlCZN+xKRpv09n4gRHYg==", + "dev": true, + "license": "MIT", + "dependencies": { + "http-proxy-agent": "^7.0.2", + "https-proxy-agent": "^7.0.5", + "jszip": "^3.10.1", + "ora": "^8.1.0", + "semver": "^7.6.2" + }, + "engines": { + "node": ">=16" + } + }, + "node_modules/@vscode/vsce": { + "version": "3.9.1", + "resolved": "https://registry.npmjs.org/@vscode/vsce/-/vsce-3.9.1.tgz", + "integrity": "sha512-MPn5p+DoudI+3GfJSpAZZraE1lgLv0LcwbH3+xy7RgEhty3UIkmUMUA+5jPTDaxXae00AnX5u77FxGM8FhfKKA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@azure/identity": "^4.1.0", + "@secretlint/node": "^10.1.2", + "@secretlint/secretlint-formatter-sarif": "^10.1.2", + "@secretlint/secretlint-rule-no-dotenv": "^10.1.2", + "@secretlint/secretlint-rule-preset-recommend": "^10.1.2", + "@vscode/vsce-sign": "^2.0.0", + "azure-devops-node-api": "^12.5.0", + "chalk": "^4.1.2", + "cheerio": "^1.0.0-rc.9", + "cockatiel": "^3.1.2", + "commander": "^12.1.0", + "form-data": "^4.0.0", + "glob": "^11.0.0", + "hosted-git-info": "^4.0.2", + "jsonc-parser": "^3.2.0", + "leven": "^3.1.0", + "markdown-it": "^14.1.0", + "mime": "^1.3.4", + "minimatch": "^3.0.3", + "parse-semver": "^1.1.1", + "read": "^1.0.7", + "secretlint": "^10.1.2", + "semver": "^7.5.2", + "tmp": "^0.2.3", + "typed-rest-client": "^1.8.4", + "url-join": "^4.0.1", + "xml2js": "^0.5.0", + "yauzl": "^3.2.1", + "yazl": "^2.2.2" + }, + "bin": { + "vsce": "vsce" + }, + "engines": { + "node": ">= 20" + }, + "optionalDependencies": { + "keytar": "^7.7.0" + } + }, + "node_modules/@vscode/vsce-sign": { + "version": "2.0.9", + "resolved": "https://registry.npmjs.org/@vscode/vsce-sign/-/vsce-sign-2.0.9.tgz", + "integrity": "sha512-8IvaRvtFyzUnGGl3f5+1Cnor3LqaUWvhaUjAYO8Y39OUYlOf3cRd+dowuQYLpZcP3uwSG+mURwjEBOSq4SOJ0g==", + "dev": true, + "hasInstallScript": true, + "license": "SEE LICENSE IN LICENSE.txt", + "optionalDependencies": { + "@vscode/vsce-sign-alpine-arm64": "2.0.6", + "@vscode/vsce-sign-alpine-x64": "2.0.6", + "@vscode/vsce-sign-darwin-arm64": "2.0.6", + "@vscode/vsce-sign-darwin-x64": "2.0.6", + "@vscode/vsce-sign-linux-arm": "2.0.6", + "@vscode/vsce-sign-linux-arm64": "2.0.6", + "@vscode/vsce-sign-linux-x64": "2.0.6", + "@vscode/vsce-sign-win32-arm64": "2.0.6", + "@vscode/vsce-sign-win32-x64": "2.0.6" + } + }, + "node_modules/@vscode/vsce-sign-alpine-arm64": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/@vscode/vsce-sign-alpine-arm64/-/vsce-sign-alpine-arm64-2.0.6.tgz", + "integrity": "sha512-wKkJBsvKF+f0GfsUuGT0tSW0kZL87QggEiqNqK6/8hvqsXvpx8OsTEc3mnE1kejkh5r+qUyQ7PtF8jZYN0mo8Q==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "SEE LICENSE IN LICENSE.txt", + "optional": true, + "os": [ + "alpine" + ] + }, + "node_modules/@vscode/vsce-sign-alpine-x64": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/@vscode/vsce-sign-alpine-x64/-/vsce-sign-alpine-x64-2.0.6.tgz", + "integrity": "sha512-YoAGlmdK39vKi9jA18i4ufBbd95OqGJxRvF3n6ZbCyziwy3O+JgOpIUPxv5tjeO6gQfx29qBivQ8ZZTUF2Ba0w==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "SEE LICENSE IN LICENSE.txt", + "optional": true, + "os": [ + "alpine" + ] + }, + "node_modules/@vscode/vsce-sign-darwin-arm64": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/@vscode/vsce-sign-darwin-arm64/-/vsce-sign-darwin-arm64-2.0.6.tgz", + "integrity": "sha512-5HMHaJRIQuozm/XQIiJiA0W9uhdblwwl2ZNDSSAeXGO9YhB9MH5C4KIHOmvyjUnKy4UCuiP43VKpIxW1VWP4tQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "SEE LICENSE IN LICENSE.txt", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@vscode/vsce-sign-darwin-x64": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/@vscode/vsce-sign-darwin-x64/-/vsce-sign-darwin-x64-2.0.6.tgz", + "integrity": "sha512-25GsUbTAiNfHSuRItoQafXOIpxlYj+IXb4/qarrXu7kmbH94jlm5sdWSCKrrREs8+GsXF1b+l3OB7VJy5jsykw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "SEE LICENSE IN LICENSE.txt", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@vscode/vsce-sign-linux-arm": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/@vscode/vsce-sign-linux-arm/-/vsce-sign-linux-arm-2.0.6.tgz", + "integrity": "sha512-UndEc2Xlq4HsuMPnwu7420uqceXjs4yb5W8E2/UkaHBB9OWCwMd3/bRe/1eLe3D8kPpxzcaeTyXiK3RdzS/1CA==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "SEE LICENSE IN LICENSE.txt", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@vscode/vsce-sign-linux-arm64": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/@vscode/vsce-sign-linux-arm64/-/vsce-sign-linux-arm64-2.0.6.tgz", + "integrity": "sha512-cfb1qK7lygtMa4NUl2582nP7aliLYuDEVpAbXJMkDq1qE+olIw/es+C8j1LJwvcRq1I2yWGtSn3EkDp9Dq5FdA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "SEE LICENSE IN LICENSE.txt", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@vscode/vsce-sign-linux-x64": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/@vscode/vsce-sign-linux-x64/-/vsce-sign-linux-x64-2.0.6.tgz", + "integrity": "sha512-/olerl1A4sOqdP+hjvJ1sbQjKN07Y3DVnxO4gnbn/ahtQvFrdhUi0G1VsZXDNjfqmXw57DmPi5ASnj/8PGZhAA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "SEE LICENSE IN LICENSE.txt", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@vscode/vsce-sign-win32-arm64": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/@vscode/vsce-sign-win32-arm64/-/vsce-sign-win32-arm64-2.0.6.tgz", + "integrity": "sha512-ivM/MiGIY0PJNZBoGtlRBM/xDpwbdlCWomUWuLmIxbi1Cxe/1nooYrEQoaHD8ojVRgzdQEUzMsRbyF5cJJgYOg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "SEE LICENSE IN LICENSE.txt", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@vscode/vsce-sign-win32-x64": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/@vscode/vsce-sign-win32-x64/-/vsce-sign-win32-x64-2.0.6.tgz", + "integrity": "sha512-mgth9Kvze+u8CruYMmhHw6Zgy3GRX2S+Ed5oSokDEK5vPEwGGKnmuXua9tmFhomeAnhgJnL4DCna3TiNuGrBTQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "SEE LICENSE IN LICENSE.txt", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/acorn": { + "version": "8.16.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.16.0.tgz", + "integrity": "sha512-UVJyE9MttOsBQIDKw1skb9nAwQuR5wuGD3+82K6JgJlm/Y+KI92oNsMNGZCYdDsVtRHSak0pcV5Dno5+4jh9sw==", + "dev": true, + "license": "MIT", + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/acorn-jsx": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", + "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" + } + }, + "node_modules/agent-base": { + "version": "7.1.4", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.4.tgz", + "integrity": "sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 14" + } + }, + "node_modules/ajv": { + "version": "8.20.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.20.0.tgz", + "integrity": "sha512-Thbli+OlOj+iMPYFBVBfJ3OmCAnaSyNn4M1vz9T6Gka5Jt9ba/HIR56joy65tY6kx/FCF5VXNB819Y7/GUrBGA==", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.3", + "fast-uri": "^3.0.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/ansi-colors": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.3.tgz", + "integrity": "sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/ansi-escapes": { + "version": "7.3.0", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-7.3.0.tgz", + "integrity": "sha512-BvU8nYgGQBxcmMuEeUEmNTvrMVjJNSH7RgW24vXexN4Ven6qCvy4TntnvlnwnMLTVlcRQQdbRY8NKnaIoeWDNg==", + "dev": true, + "license": "MIT", + "dependencies": { + "environment": "^1.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/ansi-regex": { + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.2.2.tgz", + "integrity": "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/anymatch": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", + "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", + "dev": true, + "license": "ISC", + "dependencies": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "dev": true, + "license": "Python-2.0" + }, + "node_modules/astral-regex": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-2.0.0.tgz", + "integrity": "sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/atomic-sleep": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/atomic-sleep/-/atomic-sleep-1.0.0.tgz", + "integrity": "sha512-kNOjDqAh7px0XWNI+4QbzoiR/nTkHAWNud2uvnJquD1/x5a7EQZMJT0AczqK0Qn67oY/TTQ1LbUKajZpp3I9tQ==", + "license": "MIT", + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/azure-devops-node-api": { + "version": "12.5.0", + "resolved": "https://registry.npmjs.org/azure-devops-node-api/-/azure-devops-node-api-12.5.0.tgz", + "integrity": "sha512-R5eFskGvOm3U/GzeAuxRkUsAl0hrAwGgWn6zAd2KrZmrEhWZVqLew4OOupbQlXUuojUzpGtq62SmdhJ06N88og==", + "dev": true, + "license": "MIT", + "dependencies": { + "tunnel": "0.0.6", + "typed-rest-client": "^1.8.4" + } + }, + "node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true, + "license": "MIT" + }, + "node_modules/base64-js": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", + "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", + "optional": true + }, + "node_modules/binary-extensions": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz", + "integrity": "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/binaryextensions": { + "version": "6.11.0", + "resolved": "https://registry.npmjs.org/binaryextensions/-/binaryextensions-6.11.0.tgz", + "integrity": "sha512-sXnYK/Ij80TO3lcqZVV2YgfKN5QjUWIRk/XSm2J/4bd/lPko3lvk0O4ZppH6m+6hB2/GTu+ptNwVFe1xh+QLQw==", + "dev": true, + "license": "Artistic-2.0", + "dependencies": { + "editions": "^6.21.0" + }, + "engines": { + "node": ">=4" + }, + "funding": { + "url": "https://bevry.me/fund" + } + }, + "node_modules/bl": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz", + "integrity": "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "buffer": "^5.5.0", + "inherits": "^2.0.4", + "readable-stream": "^3.4.0" + } + }, + "node_modules/bl/node_modules/readable-stream": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", + "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/boolbase": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz", + "integrity": "sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==", + "dev": true, + "license": "ISC" + }, + "node_modules/boundary": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/boundary/-/boundary-2.0.0.tgz", + "integrity": "sha512-rJKn5ooC9u8q13IMCrW0RSp31pxBCHE3y9V/tp3TdWSLf8Em3p6Di4NBpfzbJge9YjjFEsD0RtFEjtvHL5VyEA==", + "dev": true, + "license": "BSD-2-Clause" + }, + "node_modules/brace-expansion": { + "version": "1.1.14", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.14.tgz", + "integrity": "sha512-MWPGfDxnyzKU7rNOW9SP/c50vi3xrmrua/+6hfPbCS2ABNWfx24vPidzvC7krjU/RTo235sV776ymlsMtGKj8g==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/braces": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", + "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", + "dev": true, + "license": "MIT", + "dependencies": { + "fill-range": "^7.1.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/browser-stdout": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/browser-stdout/-/browser-stdout-1.3.1.tgz", + "integrity": "sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw==", + "dev": true, + "license": "ISC" + }, + "node_modules/buffer": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", + "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", + "optional": true, + "dependencies": { + "base64-js": "^1.3.1", + "ieee754": "^1.1.13" + } + }, + "node_modules/buffer-crc32": { + "version": "0.2.13", + "resolved": "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-0.2.13.tgz", + "integrity": "sha512-VO9Ht/+p3SN7SKWqcrgEzjGbRSJYTx+Q1pTQC0wrWqHx0vpJraQ6GtHx8tvcg1rlK1byhU5gccxgOgj7B0TDkQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": "*" + } + }, + "node_modules/buffer-equal-constant-time": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz", + "integrity": "sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA==", + "dev": true, + "license": "BSD-3-Clause" + }, + "node_modules/bundle-name": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/bundle-name/-/bundle-name-4.1.0.tgz", + "integrity": "sha512-tjwM5exMg6BGRI+kNmTntNsvdZS1X8BFYS6tnJ2hdH0kVxM6/eVZ2xy+FqStSWvYmtfFMDLIxurorHwDKfDz5Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "run-applescript": "^7.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/c8": { + "version": "10.1.3", + "resolved": "https://registry.npmjs.org/c8/-/c8-10.1.3.tgz", + "integrity": "sha512-LvcyrOAaOnrrlMpW22n690PUvxiq4Uf9WMhQwNJ9vgagkL/ph1+D4uvjvDA5XCbykrc0sx+ay6pVi9YZ1GnhyA==", + "dev": true, + "license": "ISC", + "dependencies": { + "@bcoe/v8-coverage": "^1.0.1", + "@istanbuljs/schema": "^0.1.3", + "find-up": "^5.0.0", + "foreground-child": "^3.1.1", + "istanbul-lib-coverage": "^3.2.0", + "istanbul-lib-report": "^3.0.1", + "istanbul-reports": "^3.1.6", + "test-exclude": "^7.0.1", + "v8-to-istanbul": "^9.0.0", + "yargs": "^17.7.2", + "yargs-parser": "^21.1.1" + }, + "bin": { + "c8": "bin/c8.js" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "monocart-coverage-reports": "^2" + }, + "peerDependenciesMeta": { + "monocart-coverage-reports": { + "optional": true + } + } + }, + "node_modules/call-bind-apply-helpers": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", + "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/call-bound": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.4.tgz", + "integrity": "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "get-intrinsic": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/callsites": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", + "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/camelcase": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", + "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/cheerio": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/cheerio/-/cheerio-1.2.0.tgz", + "integrity": "sha512-WDrybc/gKFpTYQutKIK6UvfcuxijIZfMfXaYm8NMsPQxSYvf+13fXUJ4rztGGbJcBQ/GF55gvrZ0Bc0bj/mqvg==", + "dev": true, + "license": "MIT", + "dependencies": { + "cheerio-select": "^2.1.0", + "dom-serializer": "^2.0.0", + "domhandler": "^5.0.3", + "domutils": "^3.2.2", + "encoding-sniffer": "^0.2.1", + "htmlparser2": "^10.1.0", + "parse5": "^7.3.0", + "parse5-htmlparser2-tree-adapter": "^7.1.0", + "parse5-parser-stream": "^7.1.2", + "undici": "^7.19.0", + "whatwg-mimetype": "^4.0.0" + }, + "engines": { + "node": ">=20.18.1" + }, + "funding": { + "url": "https://github.com/cheeriojs/cheerio?sponsor=1" + } + }, + "node_modules/cheerio-select": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/cheerio-select/-/cheerio-select-2.1.0.tgz", + "integrity": "sha512-9v9kG0LvzrlcungtnJtpGNxY+fzECQKhK4EGJX2vByejiMX84MFNQw4UxPJl3bFbTMw+Dfs37XaIkCwTZfLh4g==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "boolbase": "^1.0.0", + "css-select": "^5.1.0", + "css-what": "^6.1.0", + "domelementtype": "^2.3.0", + "domhandler": "^5.0.3", + "domutils": "^3.0.1" + }, + "funding": { + "url": "https://github.com/sponsors/fb55" + } + }, + "node_modules/chokidar": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz", + "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==", + "dev": true, + "license": "MIT", + "dependencies": { + "anymatch": "~3.1.2", + "braces": "~3.0.2", + "glob-parent": "~5.1.2", + "is-binary-path": "~2.1.0", + "is-glob": "~4.0.1", + "normalize-path": "~3.0.0", + "readdirp": "~3.6.0" + }, + "engines": { + "node": ">= 8.10.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + }, + "optionalDependencies": { + "fsevents": "~2.3.2" + } + }, + "node_modules/chokidar/node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/chownr": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.4.tgz", + "integrity": "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==", + "dev": true, + "license": "ISC", + "optional": true + }, + "node_modules/cli-cursor": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-5.0.0.tgz", + "integrity": "sha512-aCj4O5wKyszjMmDT4tZj93kxyydN/K5zPWSCe6/0AV/AA1pqe5ZBIw0a2ZfPQV7lL5/yb5HsUreJ6UFAF1tEQw==", + "dev": true, + "license": "MIT", + "dependencies": { + "restore-cursor": "^5.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/cli-spinners": { + "version": "2.9.2", + "resolved": "https://registry.npmjs.org/cli-spinners/-/cli-spinners-2.9.2.tgz", + "integrity": "sha512-ywqV+5MmyL4E7ybXgKys4DugZbX0FC6LnwrhjuykIjnK9k8OQacQ7axGKnjDXWNhns0xot3bZI5h55H8yo9cJg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/cliui": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", + "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", + "dev": true, + "license": "ISC", + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.1", + "wrap-ansi": "^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/cliui/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/cliui/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/cockatiel": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/cockatiel/-/cockatiel-3.2.1.tgz", + "integrity": "sha512-gfrHV6ZPkquExvMh9IOkKsBzNDk6sDuZ6DdBGUBkvFnTCqCxzpuq48RySgP0AnaqQkw2zynOFj9yly6T1Q2G5Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=16" + } + }, + "node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true, + "license": "MIT" + }, + "node_modules/combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "dev": true, + "license": "MIT", + "dependencies": { + "delayed-stream": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/commander": { + "version": "12.1.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-12.1.0.tgz", + "integrity": "sha512-Vw8qHK3bZM9y/P10u3Vib8o/DdkvA2OtPtZvD871QKjy74Wj1WSKFILMPRPSdUSx5RFK1arlJzEtA4PkFgnbuA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + } + }, + "node_modules/concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", + "dev": true, + "license": "MIT" + }, + "node_modules/convert-source-map": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", + "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", + "dev": true, + "license": "MIT" + }, + "node_modules/core-util-is": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz", + "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/cross-spawn": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", + "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", + "dev": true, + "license": "MIT", + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/css-select": { + "version": "5.2.2", + "resolved": "https://registry.npmjs.org/css-select/-/css-select-5.2.2.tgz", + "integrity": "sha512-TizTzUddG/xYLA3NXodFM0fSbNizXjOKhqiQQwvhlspadZokn1KDy0NZFS0wuEubIYAV5/c1/lAr0TaaFXEXzw==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "boolbase": "^1.0.0", + "css-what": "^6.1.0", + "domhandler": "^5.0.2", + "domutils": "^3.0.1", + "nth-check": "^2.0.1" + }, + "funding": { + "url": "https://github.com/sponsors/fb55" + } + }, + "node_modules/css-what": { + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/css-what/-/css-what-6.2.2.tgz", + "integrity": "sha512-u/O3vwbptzhMs3L1fQE82ZSLHQQfto5gyZzwteVIEyeaY5Fc7R4dapF/BvRoSYFeqfBk4m0V1Vafq5Pjv25wvA==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">= 6" + }, + "funding": { + "url": "https://github.com/sponsors/fb55" + } + }, + "node_modules/debug": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/decamelize": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-4.0.0.tgz", + "integrity": "sha512-9iE1PgSik9HeIIw2JO94IidnE3eBoQrFJ3w7sFuzSX4DpmZ3v5sZpUiV5Swcf6mQEF+Y0ru8Neo+p+nyh2J+hQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/decompress-response": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-6.0.0.tgz", + "integrity": "sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "mimic-response": "^3.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/deep-extend": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz", + "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==", + "dev": true, + "license": "MIT", + "optional": true, + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/deep-is": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", + "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/default-browser": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/default-browser/-/default-browser-5.5.0.tgz", + "integrity": "sha512-H9LMLr5zwIbSxrmvikGuI/5KGhZ8E2zH3stkMgM5LpOWDutGM2JZaj460Udnf1a+946zc7YBgrqEWwbk7zHvGw==", + "dev": true, + "license": "MIT", + "dependencies": { + "bundle-name": "^4.1.0", + "default-browser-id": "^5.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/default-browser-id": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/default-browser-id/-/default-browser-id-5.0.1.tgz", + "integrity": "sha512-x1VCxdX4t+8wVfd1so/9w+vQ4vx7lKd2Qp5tDRutErwmR85OgmfX7RlLRMWafRMY7hbEiXIbudNrjOAPa/hL8Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/define-lazy-prop": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/define-lazy-prop/-/define-lazy-prop-3.0.0.tgz", + "integrity": "sha512-N+MeXYoqr3pOgn8xfyRPREN7gHakLYjhsHhWGT3fWAiL4IkAt0iDw14QiiEm2bE30c5XX5q0FtAA3CK5f9/BUg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/detect-libc": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz", + "integrity": "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==", + "dev": true, + "license": "Apache-2.0", + "optional": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/diff": { + "version": "5.2.2", + "resolved": "https://registry.npmjs.org/diff/-/diff-5.2.2.tgz", + "integrity": "sha512-vtcDfH3TOjP8UekytvnHH1o1P4FcUdt4eQ1Y+Abap1tk/OB2MWQvcwS2ClCd1zuIhc3JKOx6p3kod8Vfys3E+A==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.3.1" + } + }, + "node_modules/dom-serializer": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-2.0.0.tgz", + "integrity": "sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==", + "dev": true, + "license": "MIT", + "dependencies": { + "domelementtype": "^2.3.0", + "domhandler": "^5.0.2", + "entities": "^4.2.0" + }, + "funding": { + "url": "https://github.com/cheeriojs/dom-serializer?sponsor=1" + } + }, + "node_modules/domelementtype": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.3.0.tgz", + "integrity": "sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fb55" + } + ], + "license": "BSD-2-Clause" + }, + "node_modules/domhandler": { + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-5.0.3.tgz", + "integrity": "sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "domelementtype": "^2.3.0" + }, + "engines": { + "node": ">= 4" + }, + "funding": { + "url": "https://github.com/fb55/domhandler?sponsor=1" + } + }, + "node_modules/domutils": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/domutils/-/domutils-3.2.2.tgz", + "integrity": "sha512-6kZKyUajlDuqlHKVX1w7gyslj9MPIXzIFiz/rGu35uC1wMi+kMhQwGhl4lt9unC9Vb9INnY9Z3/ZA3+FhASLaw==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "dom-serializer": "^2.0.0", + "domelementtype": "^2.3.0", + "domhandler": "^5.0.3" + }, + "funding": { + "url": "https://github.com/fb55/domutils?sponsor=1" + } + }, + "node_modules/dunder-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", + "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.1", + "es-errors": "^1.3.0", + "gopd": "^1.2.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/eastasianwidth": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", + "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==", + "dev": true, + "license": "MIT" + }, + "node_modules/ecdsa-sig-formatter": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz", + "integrity": "sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "safe-buffer": "^5.0.1" + } + }, + "node_modules/editions": { + "version": "6.22.0", + "resolved": "https://registry.npmjs.org/editions/-/editions-6.22.0.tgz", + "integrity": "sha512-UgGlf8IW75je7HZjNDpJdCv4cGJWIi6yumFdZ0R7A8/CIhQiWUjyGLCxdHpd8bmyD1gnkfUNK0oeOXqUS2cpfQ==", + "dev": true, + "license": "Artistic-2.0", + "dependencies": { + "version-range": "^4.15.0" + }, + "engines": { + "ecmascript": ">= es5", + "node": ">=4" + }, + "funding": { + "url": "https://bevry.me/fund" + } + }, + "node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true, + "license": "MIT" + }, + "node_modules/encoding-sniffer": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/encoding-sniffer/-/encoding-sniffer-0.2.1.tgz", + "integrity": "sha512-5gvq20T6vfpekVtqrYQsSCFZ1wEg5+wW0/QaZMWkFr6BqD3NfKs0rLCx4rrVlSWJeZb5NBJgVLswK/w2MWU+Gw==", + "dev": true, + "license": "MIT", + "dependencies": { + "iconv-lite": "^0.6.3", + "whatwg-encoding": "^3.1.1" + }, + "funding": { + "url": "https://github.com/fb55/encoding-sniffer?sponsor=1" + } + }, + "node_modules/end-of-stream": { + "version": "1.4.5", + "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.5.tgz", + "integrity": "sha512-ooEGc6HP26xXq/N+GCGOT0JKCLDGrq2bQUZrQ7gyrJiZANJ/8YDTxTpQBXGMn+WbIQXNVpyWymm7KYVICQnyOg==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "once": "^1.4.0" + } + }, + "node_modules/entities": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz", + "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.12" + }, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, + "node_modules/environment": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/environment/-/environment-1.1.0.tgz", + "integrity": "sha512-xUtoPkMggbz0MPyPiIWr1Kp4aeWJjDZ6SMvURhimjdZgsRuDplF5/s9hcgGhyXMhs+6vpnuoiZ2kFiu3FMnS8Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/es-define-property": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", + "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-errors": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", + "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-object-atoms": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.2.tgz", + "integrity": "sha512-HWcBoN6NileqtSydK2FqHbS/LoDd2pqrnQHLyJzBj4kOp/ky2MWMN694xOfkK8/SnUsW2DH7EfyVlydKCsm1Zw==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-set-tostringtag": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz", + "integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.6", + "has-tostringtag": "^1.0.2", + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/escalade": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", + "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/eslint": { + "version": "9.39.4", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.39.4.tgz", + "integrity": "sha512-XoMjdBOwe/esVgEvLmNsD3IRHkm7fbKIUGvrleloJXUZgDHig2IPWNniv+GwjyJXzuNqVjlr5+4yVUZjycJwfQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/eslint-utils": "^4.8.0", + "@eslint-community/regexpp": "^4.12.1", + "@eslint/config-array": "^0.21.2", + "@eslint/config-helpers": "^0.4.2", + "@eslint/core": "^0.17.0", + "@eslint/eslintrc": "^3.3.5", + "@eslint/js": "9.39.4", + "@eslint/plugin-kit": "^0.4.1", + "@humanfs/node": "^0.16.6", + "@humanwhocodes/module-importer": "^1.0.1", + "@humanwhocodes/retry": "^0.4.2", + "@types/estree": "^1.0.6", + "ajv": "^6.14.0", + "chalk": "^4.0.0", + "cross-spawn": "^7.0.6", + "debug": "^4.3.2", + "escape-string-regexp": "^4.0.0", + "eslint-scope": "^8.4.0", + "eslint-visitor-keys": "^4.2.1", + "espree": "^10.4.0", + "esquery": "^1.5.0", + "esutils": "^2.0.2", + "fast-deep-equal": "^3.1.3", + "file-entry-cache": "^8.0.0", + "find-up": "^5.0.0", + "glob-parent": "^6.0.2", + "ignore": "^5.2.0", + "imurmurhash": "^0.1.4", + "is-glob": "^4.0.0", + "json-stable-stringify-without-jsonify": "^1.0.1", + "lodash.merge": "^4.6.2", + "minimatch": "^3.1.5", + "natural-compare": "^1.4.0", + "optionator": "^0.9.3" + }, + "bin": { + "eslint": "bin/eslint.js" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://eslint.org/donate" + }, + "peerDependencies": { + "jiti": "*" + }, + "peerDependenciesMeta": { + "jiti": { + "optional": true + } + } + }, + "node_modules/eslint-scope": { + "version": "8.4.0", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-8.4.0.tgz", + "integrity": "sha512-sNXOfKCn74rt8RICKMvJS7XKV/Xk9kA7DyJr8mJik3S7Cwgy3qlkkmyS2uQB3jiJg6VNdZd/pDBJu0nvG2NlTg==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "esrecurse": "^4.3.0", + "estraverse": "^5.2.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint-visitor-keys": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.1.tgz", + "integrity": "sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint/node_modules/ajv": { + "version": "6.15.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.15.0.tgz", + "integrity": "sha512-fgFx7Hfoq60ytK2c7DhnF8jIvzYgOMxfugjLOSMHjLIPgenqa7S7oaagATUq99mV6IYvN2tRmC0wnTYX6iPbMw==", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/eslint/node_modules/json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true, + "license": "MIT" + }, + "node_modules/espree": { + "version": "10.4.0", + "resolved": "https://registry.npmjs.org/espree/-/espree-10.4.0.tgz", + "integrity": "sha512-j6PAQ2uUr79PZhBjP5C5fhl8e39FmRnOjsD5lGnWrFU8i2G776tBK7+nP8KuQUTTyAZUwfQqXAgrVH5MbH9CYQ==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "acorn": "^8.15.0", + "acorn-jsx": "^5.3.2", + "eslint-visitor-keys": "^4.2.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/esquery": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.7.0.tgz", + "integrity": "sha512-Ap6G0WQwcU/LHsvLwON1fAQX9Zp0A2Y6Y/cJBl9r/JbW90Zyg4/zbG6zzKa2OTALELarYHmKu0GhpM5EO+7T0g==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "estraverse": "^5.1.0" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/esrecurse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", + "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "estraverse": "^5.2.0" + }, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=4.0" + } + }, + "node_modules/esutils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/expand-template": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/expand-template/-/expand-template-2.0.3.tgz", + "integrity": "sha512-XYfuKMvj4O35f/pOXLObndIRvyQ+/+6AhODh+OKWj9S9498pHHn/IMszH+gt0fBCRWMNfk1ZSp5x3AifmnI2vg==", + "dev": true, + "license": "(MIT OR WTFPL)", + "optional": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/fast-glob": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.3.tgz", + "integrity": "sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.2", + "merge2": "^1.3.0", + "micromatch": "^4.0.8" + }, + "engines": { + "node": ">=8.6.0" + } + }, + "node_modules/fast-glob/node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/fast-json-stable-stringify": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", + "dev": true, + "license": "MIT" + }, + "node_modules/fast-levenshtein": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", + "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", + "dev": true, + "license": "MIT" + }, + "node_modules/fast-uri": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.1.2.tgz", + "integrity": "sha512-rVjf7ArG3LTk+FS6Yw81V1DLuZl1bRbNrev6Tmd/9RaroeeRRJhAt7jg/6YFxbvAQXUCavSoZhPPj6oOx+5KjQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fastify" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fastify" + } + ], + "license": "BSD-3-Clause" + }, + "node_modules/fastq": { + "version": "1.20.1", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.20.1.tgz", + "integrity": "sha512-GGToxJ/w1x32s/D2EKND7kTil4n8OVk/9mycTc4VDza13lOvpUZTGX3mFSCtV9ksdGBVzvsyAVLM6mHFThxXxw==", + "dev": true, + "license": "ISC", + "dependencies": { + "reusify": "^1.0.4" + } + }, + "node_modules/file-entry-cache": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-8.0.0.tgz", + "integrity": "sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "flat-cache": "^4.0.0" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/fill-range": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", + "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", + "dev": true, + "license": "MIT", + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/find-up": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", + "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", + "dev": true, + "license": "MIT", + "dependencies": { + "locate-path": "^6.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/flat": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/flat/-/flat-5.0.2.tgz", + "integrity": "sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ==", + "dev": true, + "license": "BSD-3-Clause", + "bin": { + "flat": "cli.js" + } + }, + "node_modules/flat-cache": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-4.0.1.tgz", + "integrity": "sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==", + "dev": true, + "license": "MIT", + "dependencies": { + "flatted": "^3.2.9", + "keyv": "^4.5.4" + }, + "engines": { + "node": ">=16" + } + }, + "node_modules/flatted": { + "version": "3.4.2", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.4.2.tgz", + "integrity": "sha512-PjDse7RzhcPkIJwy5t7KPWQSZ9cAbzQXcafsetQoD7sOJRQlGikNbx7yZp2OotDnJyrDcbyRq3Ttb18iYOqkxA==", + "dev": true, + "license": "ISC" + }, + "node_modules/foreground-child": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.1.tgz", + "integrity": "sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==", + "dev": true, + "license": "ISC", + "dependencies": { + "cross-spawn": "^7.0.6", + "signal-exit": "^4.0.1" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/form-data": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.5.tgz", + "integrity": "sha512-8RipRLol37bNs2bhoV67fiTEvdTrbMUYcFTiy3+wuuOnUog2QBHCZWXDRijWQfAkhBj2Uf5UnVaiWwA5vdd82w==", + "dev": true, + "license": "MIT", + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "es-set-tostringtag": "^2.1.0", + "hasown": "^2.0.2", + "mime-types": "^2.1.12" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/fs-constants": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs-constants/-/fs-constants-1.0.0.tgz", + "integrity": "sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==", + "dev": true, + "license": "MIT", + "optional": true + }, + "node_modules/fs-extra": { + "version": "11.3.5", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-11.3.5.tgz", + "integrity": "sha512-eKpRKAovdpZtR1WopLHxlBWvAgPny3c4gX1G5Jhwmmw4XJj0ifSD5qB5TOo8hmA0wlRKDAOAhEE1yVPgs6Fgcg==", + "dev": true, + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + }, + "engines": { + "node": ">=14.14" + } + }, + "node_modules/fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", + "dev": true, + "license": "ISC" + }, + "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/function-bind": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-caller-file": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", + "dev": true, + "license": "ISC", + "engines": { + "node": "6.* || 8.* || >= 10.*" + } + }, + "node_modules/get-east-asian-width": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/get-east-asian-width/-/get-east-asian-width-1.6.0.tgz", + "integrity": "sha512-QRbvDIbx6YklUe6RxeTeleMR0yv3cYH6PsPZHcnVn7xv7zO1BHN8r0XETu8n6Ye3Q+ahtSarc3WgtNWmehIBfA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/get-intrinsic": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", + "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "es-define-property": "^1.0.1", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.1.1", + "function-bind": "^1.1.2", + "get-proto": "^1.0.1", + "gopd": "^1.2.0", + "has-symbols": "^1.1.0", + "hasown": "^2.0.2", + "math-intrinsics": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", + "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", + "dev": true, + "license": "MIT", + "dependencies": { + "dunder-proto": "^1.0.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/github-from-package": { + "version": "0.0.0", + "resolved": "https://registry.npmjs.org/github-from-package/-/github-from-package-0.0.0.tgz", + "integrity": "sha512-SyHy3T1v2NUXn29OsWdxmK6RwHD+vkj3v8en8AOBZ1wBQ/hCAQ5bAQTD02kW4W9tUp/3Qh6J8r9EvntiyCmOOw==", + "dev": true, + "license": "MIT", + "optional": true + }, + "node_modules/glob": { + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-11.1.0.tgz", + "integrity": "sha512-vuNwKSaKiqm7g0THUBu2x7ckSs3XJLXE+2ssL7/MfTGPLLcrJQ/4Uq1CjPTtO5cCIiRxqvN6Twy1qOwhL0Xjcw==", + "deprecated": "Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "foreground-child": "^3.3.1", + "jackspeak": "^4.1.1", + "minimatch": "^10.1.1", + "minipass": "^7.1.2", + "package-json-from-dist": "^1.0.0", + "path-scurry": "^2.0.0" + }, + "bin": { + "glob": "dist/esm/bin.mjs" + }, + "engines": { + "node": "20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/glob-parent": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", + "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", + "dev": true, + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.3" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/glob/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==", + "dev": true, + "license": "MIT", + "engines": { + "node": "18 || 20 || >=22" + } + }, + "node_modules/glob/node_modules/brace-expansion": { + "version": "5.0.6", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.6.tgz", + "integrity": "sha512-kLpxurY4Z4r9sgMsyG0Z9uzsBlgiU/EFKhj/h91/8yHu0edo7XuixOIH3VcJ8kkxs6/jPzoI6U9Vj3WqbMQ94g==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^4.0.2" + }, + "engines": { + "node": "18 || 20 || >=22" + } + }, + "node_modules/glob/node_modules/minimatch": { + "version": "10.2.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.5.tgz", + "integrity": "sha512-MULkVLfKGYDFYejP07QOurDLLQpcjk7Fw+7jXS2R2czRQzR56yHRveU5NDJEOviH+hETZKSkIk5c+T23GjFUMg==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "brace-expansion": "^5.0.5" + }, + "engines": { + "node": "18 || 20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/globals": { + "version": "14.0.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-14.0.0.tgz", + "integrity": "sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/globby": { + "version": "14.1.0", + "resolved": "https://registry.npmjs.org/globby/-/globby-14.1.0.tgz", + "integrity": "sha512-0Ia46fDOaT7k4og1PDW4YbodWWr3scS2vAr2lTbsplOt2WkKp0vQbkI9wKis/T5LV/dqPjO3bpS/z6GTJB82LA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@sindresorhus/merge-streams": "^2.1.0", + "fast-glob": "^3.3.3", + "ignore": "^7.0.3", + "path-type": "^6.0.0", + "slash": "^5.1.0", + "unicorn-magic": "^0.3.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/globby/node_modules/ignore": { + "version": "7.0.5", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-7.0.5.tgz", + "integrity": "sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, + "node_modules/gopd": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", + "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/graceful-fs": { + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/has-symbols": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", + "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-tostringtag": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz", + "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-symbols": "^1.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/hasown": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.3.tgz", + "integrity": "sha512-ej4AhfhfL2Q2zpMmLo7U1Uv9+PyhIZpgQLGT1F9miIGmiCJIoCgSmczFdrc97mWT4kVY72KA+WnnhJ5pghSvSg==", + "dev": true, + "license": "MIT", + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/he": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", + "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==", + "dev": true, + "license": "MIT", + "bin": { + "he": "bin/he" + } + }, + "node_modules/hosted-git-info": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-4.1.0.tgz", + "integrity": "sha512-kyCuEOWjJqZuDbRHzL8V93NzQhwIB71oFWSyzVo+KPZI+pnQPPxucdkrOZvkLRnrf5URsQM+IJ09Dw29cRALIA==", + "dev": true, + "license": "ISC", + "dependencies": { + "lru-cache": "^6.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/html-escaper": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz", + "integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==", + "dev": true, + "license": "MIT" + }, + "node_modules/htmlparser2": { + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-10.1.0.tgz", + "integrity": "sha512-VTZkM9GWRAtEpveh7MSF6SjjrpNVNNVJfFup7xTY3UpFtm67foy9HDVXneLtFVt4pMz5kZtgNcvCniNFb1hlEQ==", + "dev": true, + "funding": [ + "https://github.com/fb55/htmlparser2?sponsor=1", + { + "type": "github", + "url": "https://github.com/sponsors/fb55" + } + ], + "license": "MIT", + "dependencies": { + "domelementtype": "^2.3.0", + "domhandler": "^5.0.3", + "domutils": "^3.2.2", + "entities": "^7.0.1" + } + }, + "node_modules/htmlparser2/node_modules/entities": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/entities/-/entities-7.0.1.tgz", + "integrity": "sha512-TWrgLOFUQTH994YUyl1yT4uyavY5nNB5muff+RtWaqNVCAK408b5ZnnbNAUEWLTCpum9w6arT70i1XdQ4UeOPA==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.12" + }, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, + "node_modules/http-proxy-agent": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-7.0.2.tgz", + "integrity": "sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig==", + "dev": true, + "license": "MIT", + "dependencies": { + "agent-base": "^7.1.0", + "debug": "^4.3.4" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/https-proxy-agent": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.6.tgz", + "integrity": "sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==", + "dev": true, + "license": "MIT", + "dependencies": { + "agent-base": "^7.1.2", + "debug": "4" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/iconv-lite": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", + "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", + "dev": true, + "license": "MIT", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/ieee754": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", + "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "BSD-3-Clause", + "optional": true + }, + "node_modules/ignore": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", + "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, + "node_modules/immediate": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/immediate/-/immediate-3.0.6.tgz", + "integrity": "sha512-XXOFtyqDjNDAQxVfYxuF7g9Il/IbWmmlQg2MYKOH8ExIT1qg6xc4zyS3HaEEATgs1btfzxq15ciUiY7gjSXRGQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/import-fresh": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.1.tgz", + "integrity": "sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "parent-module": "^1.0.0", + "resolve-from": "^4.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.8.19" + } + }, + "node_modules/index-to-position": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/index-to-position/-/index-to-position-1.2.0.tgz", + "integrity": "sha512-Yg7+ztRkqslMAS2iFaU+Oa4KTSidr63OsFGlOrJoW981kIYO3CGCS3wA95P1mUi/IVSJkn0D479KTJpVpvFNuw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", + "deprecated": "This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful.", + "dev": true, + "license": "ISC", + "dependencies": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/ini": { + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz", + "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==", + "dev": true, + "license": "ISC", + "optional": true + }, + "node_modules/is-binary-path": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", + "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", + "dev": true, + "license": "MIT", + "dependencies": { + "binary-extensions": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-docker": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-3.0.0.tgz", + "integrity": "sha512-eljcgEDlEns/7AXFosB5K/2nCM4P7FQPkGc/DWLy5rmFEWvZayGrik1d9/QIY5nJ4f9YsVvBkA6kJpHn9rISdQ==", + "dev": true, + "license": "MIT", + "bin": { + "is-docker": "cli.js" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-inside-container": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-inside-container/-/is-inside-container-1.0.0.tgz", + "integrity": "sha512-KIYLCCJghfHZxqjYBE7rEy0OBuTd5xCHS7tHVgvCLkx7StIoaxwNW3hCALgEUjFfeRk+MG/Qxmp/vtETEF3tRA==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-docker": "^3.0.0" + }, + "bin": { + "is-inside-container": "cli.js" + }, + "engines": { + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-interactive": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-interactive/-/is-interactive-2.0.0.tgz", + "integrity": "sha512-qP1vozQRI+BMOPcjFzrjXuQvdak2pHNUMZoeG2eRbiSqyvbEf/wQtEOTOX1guk6E3t36RkaqiSt8A/6YElNxLQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/is-plain-obj": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-2.1.0.tgz", + "integrity": "sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/is-unicode-supported": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz", + "integrity": "sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-wsl": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-3.1.1.tgz", + "integrity": "sha512-e6rvdUCiQCAuumZslxRJWR/Doq4VpPR82kqclvcS0efgt430SlGIk05vdCN58+VrzgtIcfNODjozVielycD4Sw==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-inside-container": "^1.0.0" + }, + "engines": { + "node": ">=16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "dev": true, + "license": "ISC" + }, + "node_modules/istanbul-lib-coverage": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.2.tgz", + "integrity": "sha512-O8dpsF+r0WV/8MNRKfnmrtCWhuKjxrq2w+jpzBL5UZKTi2LeVWnWOmWRxFlesJONmc+wLAGvKQZEOanko0LFTg==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=8" + } + }, + "node_modules/istanbul-lib-report": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-3.0.1.tgz", + "integrity": "sha512-GCfE1mtsHGOELCU8e/Z7YWzpmybrx/+dSTfLrvY8qRmaY6zXTKWn6WQIjaAFw069icm6GVMNkgu0NzI4iPZUNw==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "istanbul-lib-coverage": "^3.0.0", + "make-dir": "^4.0.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/istanbul-reports": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.2.0.tgz", + "integrity": "sha512-HGYWWS/ehqTV3xN10i23tkPkpH46MLCIMFNCaaKNavAXTF1RkqxawEPtnjnGZ6XKSInBKkiOA5BKS+aZiY3AvA==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "html-escaper": "^2.0.0", + "istanbul-lib-report": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/istextorbinary": { + "version": "9.5.0", + "resolved": "https://registry.npmjs.org/istextorbinary/-/istextorbinary-9.5.0.tgz", + "integrity": "sha512-5mbUj3SiZXCuRf9fT3ibzbSSEWiy63gFfksmGfdOzujPjW3k+z8WvIBxcJHBoQNlaZaiyB25deviif2+osLmLw==", + "dev": true, + "license": "Artistic-2.0", + "dependencies": { + "binaryextensions": "^6.11.0", + "editions": "^6.21.0", + "textextensions": "^6.11.0" + }, + "engines": { + "node": ">=4" + }, + "funding": { + "url": "https://bevry.me/fund" + } + }, + "node_modules/jackspeak": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-4.2.3.tgz", + "integrity": "sha512-ykkVRwrYvFm1nb2AJfKKYPr0emF6IiXDYUaFx4Zn9ZuIH7MrzEZ3sD5RlqGXNRpHtvUHJyOnCEFxOlNDtGo7wg==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "@isaacs/cliui": "^9.0.0" + }, + "engines": { + "node": "20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/js-yaml": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.1.tgz", + "integrity": "sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==", + "dev": true, + "license": "MIT", + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/json-buffer": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", + "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", + "dev": true, + "license": "MIT" + }, + "node_modules/json-stable-stringify-without-jsonify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", + "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", + "dev": true, + "license": "MIT" + }, + "node_modules/json5": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", + "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", + "dev": true, + "license": "MIT", + "bin": { + "json5": "lib/cli.js" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/jsonc-parser": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/jsonc-parser/-/jsonc-parser-3.3.1.tgz", + "integrity": "sha512-HUgH65KyejrUFPvHFPbqOY0rsFip3Bo5wb4ngvdi1EpCYWUQDC5V+Y7mZws+DLkr4M//zQJoanu1SP+87Dv1oQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/jsonfile": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.2.1.tgz", + "integrity": "sha512-zwOTdL3rFQ/lRdBnntKVOX6k5cKJwEc1HdilT71BWEu7J41gXIB2MRp+vxduPSwZJPWBxEzv4yH1wYLJGUHX4Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "universalify": "^2.0.0" + }, + "optionalDependencies": { + "graceful-fs": "^4.1.6" + } + }, + "node_modules/jsonwebtoken": { + "version": "9.0.3", + "resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-9.0.3.tgz", + "integrity": "sha512-MT/xP0CrubFRNLNKvxJ2BYfy53Zkm++5bX9dtuPbqAeQpTVe0MQTFhao8+Cp//EmJp244xt6Drw/GVEGCUj40g==", + "dev": true, + "license": "MIT", + "dependencies": { + "jws": "^4.0.1", + "lodash.includes": "^4.3.0", + "lodash.isboolean": "^3.0.3", + "lodash.isinteger": "^4.0.4", + "lodash.isnumber": "^3.0.3", + "lodash.isplainobject": "^4.0.6", + "lodash.isstring": "^4.0.1", + "lodash.once": "^4.0.0", + "ms": "^2.1.1", + "semver": "^7.5.4" + }, + "engines": { + "node": ">=12", + "npm": ">=6" + } + }, + "node_modules/jszip": { + "version": "3.10.1", + "resolved": "https://registry.npmjs.org/jszip/-/jszip-3.10.1.tgz", + "integrity": "sha512-xXDvecyTpGLrqFrvkrUSoxxfJI5AH7U8zxxtVclpsUtMCq4JQ290LY8AW5c7Ggnr/Y/oK+bQMbqK2qmtk3pN4g==", + "dev": true, + "license": "(MIT OR GPL-3.0-or-later)", + "dependencies": { + "lie": "~3.3.0", + "pako": "~1.0.2", + "readable-stream": "~2.3.6", + "setimmediate": "^1.0.5" + } + }, + "node_modules/jwa": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/jwa/-/jwa-2.0.1.tgz", + "integrity": "sha512-hRF04fqJIP8Abbkq5NKGN0Bbr3JxlQ+qhZufXVr0DvujKy93ZCbXZMHDL4EOtodSbCWxOqR8MS1tXA5hwqCXDg==", + "dev": true, + "license": "MIT", + "dependencies": { + "buffer-equal-constant-time": "^1.0.1", + "ecdsa-sig-formatter": "1.0.11", + "safe-buffer": "^5.0.1" + } + }, + "node_modules/jws": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/jws/-/jws-4.0.1.tgz", + "integrity": "sha512-EKI/M/yqPncGUUh44xz0PxSidXFr/+r0pA70+gIYhjv+et7yxM+s29Y+VGDkovRofQem0fs7Uvf4+YmAdyRduA==", + "dev": true, + "license": "MIT", + "dependencies": { + "jwa": "^2.0.1", + "safe-buffer": "^5.0.1" + } + }, + "node_modules/keytar": { + "version": "7.9.0", + "resolved": "https://registry.npmjs.org/keytar/-/keytar-7.9.0.tgz", + "integrity": "sha512-VPD8mtVtm5JNtA2AErl6Chp06JBfy7diFQ7TQQhdpWOl6MrCRB+eRbvAZUsbGQS9kiMq0coJsy0W0vHpDCkWsQ==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "dependencies": { + "node-addon-api": "^4.3.0", + "prebuild-install": "^7.0.1" + } + }, + "node_modules/keyv": { + "version": "4.5.4", + "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", + "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", + "dev": true, + "license": "MIT", + "dependencies": { + "json-buffer": "3.0.1" + } + }, + "node_modules/leven": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/leven/-/leven-3.1.0.tgz", + "integrity": "sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/levn": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", + "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "prelude-ls": "^1.2.1", + "type-check": "~0.4.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/lie": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/lie/-/lie-3.3.0.tgz", + "integrity": "sha512-UaiMJzeWRlEujzAuw5LokY1L5ecNQYZKfmyZ9L7wDHb/p5etKaxXhohBcrw0EYby+G/NA52vRSN4N39dxHAIwQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "immediate": "~3.0.5" + } + }, + "node_modules/linkify-it": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/linkify-it/-/linkify-it-5.0.0.tgz", + "integrity": "sha512-5aHCbzQRADcdP+ATqnDuhhJ/MRIqDkZX5pyjFHRRysS8vZ5AbqGEoFIb6pYHPZ+L/OC2Lc+xT8uHVVR5CAK/wQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "uc.micro": "^2.0.0" + } + }, + "node_modules/locate-path": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", + "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-locate": "^5.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/lodash": { + "version": "4.18.1", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.18.1.tgz", + "integrity": "sha512-dMInicTPVE8d1e5otfwmmjlxkZoUpiVLwyeTdUsi/Caj/gfzzblBcCE5sRHV/AsjuCmxWrte2TNGSYuCeCq+0Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/lodash.includes": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/lodash.includes/-/lodash.includes-4.3.0.tgz", + "integrity": "sha512-W3Bx6mdkRTGtlJISOvVD/lbqjTlPPUDTMnlXZFnVwi9NKJ6tiAk6LVdlhZMm17VZisqhKcgzpO5Wz91PCt5b0w==", + "dev": true, + "license": "MIT" + }, + "node_modules/lodash.isboolean": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/lodash.isboolean/-/lodash.isboolean-3.0.3.tgz", + "integrity": "sha512-Bz5mupy2SVbPHURB98VAcw+aHh4vRV5IPNhILUCsOzRmsTmSQ17jIuqopAentWoehktxGd9e/hbIXq980/1QJg==", + "dev": true, + "license": "MIT" + }, + "node_modules/lodash.isinteger": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/lodash.isinteger/-/lodash.isinteger-4.0.4.tgz", + "integrity": "sha512-DBwtEWN2caHQ9/imiNeEA5ys1JoRtRfY3d7V9wkqtbycnAmTvRRmbHKDV4a0EYc678/dia0jrte4tjYwVBaZUA==", + "dev": true, + "license": "MIT" + }, + "node_modules/lodash.isnumber": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/lodash.isnumber/-/lodash.isnumber-3.0.3.tgz", + "integrity": "sha512-QYqzpfwO3/CWf3XP+Z+tkQsfaLL/EnUlXWVkIk5FUPc4sBdTehEqZONuyRt2P67PXAk+NXmTBcc97zw9t1FQrw==", + "dev": true, + "license": "MIT" + }, + "node_modules/lodash.isplainobject": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz", + "integrity": "sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA==", + "dev": true, + "license": "MIT" + }, + "node_modules/lodash.isstring": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/lodash.isstring/-/lodash.isstring-4.0.1.tgz", + "integrity": "sha512-0wJxfxH1wgO3GrbuP+dTTk7op+6L41QCXbGINEmD+ny/G/eCqGzxyCsh7159S+mgDDcoarnBw6PC1PS5+wUGgw==", + "dev": true, + "license": "MIT" + }, + "node_modules/lodash.merge": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", + "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/lodash.once": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/lodash.once/-/lodash.once-4.1.1.tgz", + "integrity": "sha512-Sb487aTOCr9drQVL8pIxOzVhafOjZN9UU54hiN8PU3uAiSV7lx1yYNpbNmex2PK6dSJoNTSJUUswT651yww3Mg==", + "dev": true, + "license": "MIT" + }, + "node_modules/lodash.truncate": { + "version": "4.4.2", + "resolved": "https://registry.npmjs.org/lodash.truncate/-/lodash.truncate-4.4.2.tgz", + "integrity": "sha512-jttmRe7bRse52OsWIMDLaXxWqRAmtIUccAQ3garviCqJjafXOfNMO0yMfNpdD6zbGaTU0P5Nz7e7gAT6cKmJRw==", + "dev": true, + "license": "MIT" + }, + "node_modules/log-symbols": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz", + "integrity": "sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==", + "dev": true, + "license": "MIT", + "dependencies": { + "chalk": "^4.1.0", + "is-unicode-supported": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dev": true, + "license": "ISC", + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/make-dir": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-4.0.0.tgz", + "integrity": "sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw==", + "dev": true, + "license": "MIT", + "dependencies": { + "semver": "^7.5.3" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/markdown-it": { + "version": "14.1.1", + "resolved": "https://registry.npmjs.org/markdown-it/-/markdown-it-14.1.1.tgz", + "integrity": "sha512-BuU2qnTti9YKgK5N+IeMubp14ZUKUUw7yeJbkjtosvHiP0AZ5c8IAgEMk79D0eC8F23r4Ac/q8cAIFdm2FtyoA==", + "dev": true, + "license": "MIT", + "dependencies": { + "argparse": "^2.0.1", + "entities": "^4.4.0", + "linkify-it": "^5.0.0", + "mdurl": "^2.0.0", + "punycode.js": "^2.3.1", + "uc.micro": "^2.1.0" + }, + "bin": { + "markdown-it": "bin/markdown-it.mjs" + } + }, + "node_modules/math-intrinsics": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", + "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/mdurl": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/mdurl/-/mdurl-2.0.0.tgz", + "integrity": "sha512-Lf+9+2r+Tdp5wXDXC4PcIBjTDtq4UKjCPMQhKIuzpJNW0b96kVqSwW0bT7FhRSfmAiFYgP+SCRvdrDozfh0U5w==", + "dev": true, + "license": "MIT" + }, + "node_modules/merge2": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", + "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 8" + } + }, + "node_modules/micromatch": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", + "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", + "dev": true, + "license": "MIT", + "dependencies": { + "braces": "^3.0.3", + "picomatch": "^2.3.1" + }, + "engines": { + "node": ">=8.6" + } + }, + "node_modules/mime": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", + "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", + "dev": true, + "license": "MIT", + "bin": { + "mime": "cli.js" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "dev": true, + "license": "MIT", + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mimic-function": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/mimic-function/-/mimic-function-5.0.1.tgz", + "integrity": "sha512-VP79XUPxV2CigYP3jWwAUFSku2aKqBH7uTAapFWCBqutsbmDo96KY5o8uh6U+/YSIn5OxJnXp73beVkpqMIGhA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/mimic-response": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-3.1.0.tgz", + "integrity": "sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==", + "dev": true, + "license": "MIT", + "optional": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/minimatch": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz", + "integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/minimist": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", + "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", + "dev": true, + "license": "MIT", + "optional": true, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/minipass": { + "version": "7.1.3", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.3.tgz", + "integrity": "sha512-tEBHqDnIoM/1rXME1zgka9g6Q2lcoCkxHLuc7ODJ5BxbP5d4c2Z5cGgtXAku59200Cx7diuHTOYfSBD8n6mm8A==", + "dev": true, + "license": "BlueOak-1.0.0", + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, + "node_modules/mkdirp-classic": { + "version": "0.5.3", + "resolved": "https://registry.npmjs.org/mkdirp-classic/-/mkdirp-classic-0.5.3.tgz", + "integrity": "sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A==", + "dev": true, + "license": "MIT", + "optional": true + }, + "node_modules/mocha": { + "version": "10.8.2", + "resolved": "https://registry.npmjs.org/mocha/-/mocha-10.8.2.tgz", + "integrity": "sha512-VZlYo/WE8t1tstuRmqgeyBgCbJc/lEdopaa+axcKzTBJ+UIdlAB9XnmvTCAH4pwR4ElNInaedhEBmZD8iCSVEg==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-colors": "^4.1.3", + "browser-stdout": "^1.3.1", + "chokidar": "^3.5.3", + "debug": "^4.3.5", + "diff": "^5.2.0", + "escape-string-regexp": "^4.0.0", + "find-up": "^5.0.0", + "glob": "^8.1.0", + "he": "^1.2.0", + "js-yaml": "^4.1.0", + "log-symbols": "^4.1.0", + "minimatch": "^5.1.6", + "ms": "^2.1.3", + "serialize-javascript": "^6.0.2", + "strip-json-comments": "^3.1.1", + "supports-color": "^8.1.1", + "workerpool": "^6.5.1", + "yargs": "^16.2.0", + "yargs-parser": "^20.2.9", + "yargs-unparser": "^2.0.0" + }, + "bin": { + "_mocha": "bin/_mocha", + "mocha": "bin/mocha.js" + }, + "engines": { + "node": ">= 14.0.0" + } + }, + "node_modules/mocha/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/mocha/node_modules/brace-expansion": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.1.0.tgz", + "integrity": "sha512-TN1kCZAgdgweJhWWpgKYrQaMNHcDULHkWwQIspdtjV4Y5aurRdZpjAqn6yX3FPqTA9ngHCc4hJxMAMgGfve85w==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/mocha/node_modules/cliui": { + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", + "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", + "dev": true, + "license": "ISC", + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.0", + "wrap-ansi": "^7.0.0" + } + }, + "node_modules/mocha/node_modules/glob": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-8.1.0.tgz", + "integrity": "sha512-r8hpEjiQEYlF2QU0df3dS+nxxSIreXQS1qRhMJM0Q5NDdR386C7jb7Hwwod8Fgiuex+k0GFjgft18yvxm5XoCQ==", + "deprecated": "Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me", + "dev": true, + "license": "ISC", + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^5.0.1", + "once": "^1.3.0" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/mocha/node_modules/minimatch": { + "version": "5.1.9", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.9.tgz", + "integrity": "sha512-7o1wEA2RyMP7Iu7GNba9vc0RWWGACJOCZBJX2GJWip0ikV+wcOsgVuY9uE8CPiyQhkGFSlhuSkZPavN7u1c2Fw==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/mocha/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/mocha/node_modules/supports-color": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", + "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/supports-color?sponsor=1" + } + }, + "node_modules/mocha/node_modules/yargs": { + "version": "16.2.0", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", + "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==", + "dev": true, + "license": "MIT", + "dependencies": { + "cliui": "^7.0.2", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.0", + "y18n": "^5.0.5", + "yargs-parser": "^20.2.2" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/mocha/node_modules/yargs-parser": { + "version": "20.2.9", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.9.tgz", + "integrity": "sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=10" + } + }, + "node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true, + "license": "MIT" + }, + "node_modules/mute-stream": { + "version": "0.0.8", + "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.8.tgz", + "integrity": "sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA==", + "dev": true, + "license": "ISC" + }, + "node_modules/napi-build-utils": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/napi-build-utils/-/napi-build-utils-2.0.0.tgz", + "integrity": "sha512-GEbrYkbfF7MoNaoh2iGG84Mnf/WZfB0GdGEsM8wz7Expx/LlWf5U8t9nvJKXSp3qr5IsEbK04cBGhol/KwOsWA==", + "dev": true, + "license": "MIT", + "optional": true + }, + "node_modules/natural-compare": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", + "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", + "dev": true, + "license": "MIT" + }, + "node_modules/node-abi": { + "version": "3.92.0", + "resolved": "https://registry.npmjs.org/node-abi/-/node-abi-3.92.0.tgz", + "integrity": "sha512-KdHvFWZjEKDf0cakgFjebl371GPsISX2oZHcuyKqM7DtogIsHrqKeLTo8wBHxaXRAQlY2PsPlZmfo+9ZCxEREQ==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "semver": "^7.3.5" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/node-addon-api": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-4.3.0.tgz", + "integrity": "sha512-73sE9+3UaLYYFmDsFZnqCInzPyh3MqIwZO9cw58yIqAZhONrrabrYyYe3TuIqtIiOuTXVhsGau8hcrhhwSsDIQ==", + "dev": true, + "license": "MIT", + "optional": true + }, + "node_modules/node-sarif-builder": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/node-sarif-builder/-/node-sarif-builder-3.4.0.tgz", + "integrity": "sha512-tGnJW6OKRii9u/b2WiUViTJS+h7Apxx17qsMUjsUeNDiMMX5ZFf8F8Fcz7PAQ6omvOxHZtvDTmOYKJQwmfpjeg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/sarif": "^2.1.7", + "fs-extra": "^11.1.1" + }, + "engines": { + "node": ">=20" + } + }, + "node_modules/normalize-package-data": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-6.0.2.tgz", + "integrity": "sha512-V6gygoYb/5EmNI+MEGrWkC+e6+Rr7mTmfHrxDbLzxQogBkgzo76rkok0Am6thgSF7Mv2nLOajAJj5vDJZEFn7g==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "hosted-git-info": "^7.0.0", + "semver": "^7.3.5", + "validate-npm-package-license": "^3.0.4" + }, + "engines": { + "node": "^16.14.0 || >=18.0.0" + } + }, + "node_modules/normalize-package-data/node_modules/hosted-git-info": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-7.0.2.tgz", + "integrity": "sha512-puUZAUKT5m8Zzvs72XWy3HtvVbTWljRE66cP60bxJzAqf2DgICo7lYTY2IHUmLnNpjYvw5bvmoHvPc0QO2a62w==", + "dev": true, + "license": "ISC", + "dependencies": { + "lru-cache": "^10.0.1" + }, + "engines": { + "node": "^16.14.0 || >=18.0.0" + } + }, + "node_modules/normalize-package-data/node_modules/lru-cache": { + "version": "10.4.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", + "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/nth-check": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-2.1.1.tgz", + "integrity": "sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "boolbase": "^1.0.0" + }, + "funding": { + "url": "https://github.com/fb55/nth-check?sponsor=1" + } + }, + "node_modules/object-inspect": { + "version": "1.13.4", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz", + "integrity": "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/on-exit-leak-free": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/on-exit-leak-free/-/on-exit-leak-free-2.1.2.tgz", + "integrity": "sha512-0eJJY6hXLGf1udHwfNftBqH+g73EU4B504nZeKpz1sYRKafAghwxEJunB2O7rDZkL4PGfsMVnTXZ2EjibbqcsA==", + "license": "MIT", + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "dev": true, + "license": "ISC", + "dependencies": { + "wrappy": "1" + } + }, + "node_modules/onetime": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-7.0.0.tgz", + "integrity": "sha512-VXJjc87FScF88uafS3JllDgvAm+c/Slfz06lorj2uAY34rlUu0Nt+v8wreiImcrgAjjIHp1rXpTDlLOGw29WwQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "mimic-function": "^5.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/open": { + "version": "10.2.0", + "resolved": "https://registry.npmjs.org/open/-/open-10.2.0.tgz", + "integrity": "sha512-YgBpdJHPyQ2UE5x+hlSXcnejzAvD0b22U2OuAP+8OnlJT+PjWPxtgmGqKKc+RgTM63U9gN0YzrYc71R2WT/hTA==", + "dev": true, + "license": "MIT", + "dependencies": { + "default-browser": "^5.2.1", + "define-lazy-prop": "^3.0.0", + "is-inside-container": "^1.0.0", + "wsl-utils": "^0.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/optionator": { + "version": "0.9.4", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz", + "integrity": "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==", + "dev": true, + "license": "MIT", + "dependencies": { + "deep-is": "^0.1.3", + "fast-levenshtein": "^2.0.6", + "levn": "^0.4.1", + "prelude-ls": "^1.2.1", + "type-check": "^0.4.0", + "word-wrap": "^1.2.5" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/ora": { + "version": "8.2.0", + "resolved": "https://registry.npmjs.org/ora/-/ora-8.2.0.tgz", + "integrity": "sha512-weP+BZ8MVNnlCm8c0Qdc1WSWq4Qn7I+9CJGm7Qali6g44e/PUzbjNqJX5NJ9ljlNMosfJvg1fKEGILklK9cwnw==", + "dev": true, + "license": "MIT", + "dependencies": { + "chalk": "^5.3.0", + "cli-cursor": "^5.0.0", + "cli-spinners": "^2.9.2", + "is-interactive": "^2.0.0", + "is-unicode-supported": "^2.0.0", + "log-symbols": "^6.0.0", + "stdin-discarder": "^0.2.2", + "string-width": "^7.2.0", + "strip-ansi": "^7.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/ora/node_modules/chalk": { + "version": "5.6.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.6.2.tgz", + "integrity": "sha512-7NzBL0rN6fMUW+f7A6Io4h40qQlG+xGmtMxfbnH/K7TAtt8JQWVQK+6g0UXKMeVJoyV5EkkNsErQ8pVD3bLHbA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.17.0 || ^14.13 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/ora/node_modules/emoji-regex": { + "version": "10.6.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-10.6.0.tgz", + "integrity": "sha512-toUI84YS5YmxW219erniWD0CIVOo46xGKColeNQRgOzDorgBi1v4D71/OFzgD9GO2UGKIv1C3Sp8DAn0+j5w7A==", + "dev": true, + "license": "MIT" + }, + "node_modules/ora/node_modules/is-unicode-supported": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-2.1.0.tgz", + "integrity": "sha512-mE00Gnza5EEB3Ds0HfMyllZzbBrmLOX3vfWoj9A9PEnTfratQ/BcaJOuMhnkhjXvb2+FkY3VuHqtAGpTPmglFQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/ora/node_modules/log-symbols": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-6.0.0.tgz", + "integrity": "sha512-i24m8rpwhmPIS4zscNzK6MSEhk0DUWa/8iYQWxhffV8jkI4Phvs3F+quL5xvS0gdQR0FyTCMMH33Y78dDTzzIw==", + "dev": true, + "license": "MIT", + "dependencies": { + "chalk": "^5.3.0", + "is-unicode-supported": "^1.3.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/ora/node_modules/log-symbols/node_modules/is-unicode-supported": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-1.3.0.tgz", + "integrity": "sha512-43r2mRvz+8JRIKnWJ+3j8JtjRKZ6GmjzfaE/qiBJnikNnYv/6bagRJ1kUhNk8R5EX/GkobD+r+sfxCPJsiKBLQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/ora/node_modules/string-width": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-7.2.0.tgz", + "integrity": "sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "emoji-regex": "^10.3.0", + "get-east-asian-width": "^1.0.0", + "strip-ansi": "^7.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-limit": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "yocto-queue": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-locate": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", + "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-limit": "^3.0.2" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-map": { + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/p-map/-/p-map-7.0.4.tgz", + "integrity": "sha512-tkAQEw8ysMzmkhgw8k+1U/iPhWNhykKnSk4Rd5zLoPJCuJaGRPo6YposrZgaxHKzDHdDWWZvE/Sk7hsL2X/CpQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/package-json-from-dist": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz", + "integrity": "sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==", + "dev": true, + "license": "BlueOak-1.0.0" + }, + "node_modules/pako": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/pako/-/pako-1.0.11.tgz", + "integrity": "sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==", + "dev": true, + "license": "(MIT AND Zlib)" + }, + "node_modules/parent-module": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", + "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", + "dev": true, + "license": "MIT", + "dependencies": { + "callsites": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/parse-json": { + "version": "8.3.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-8.3.0.tgz", + "integrity": "sha512-ybiGyvspI+fAoRQbIPRddCcSTV9/LsJbf0e/S85VLowVGzRmokfneg2kwVW/KU5rOXrPSbF1qAKPMgNTqqROQQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.26.2", + "index-to-position": "^1.1.0", + "type-fest": "^4.39.1" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/parse-semver": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/parse-semver/-/parse-semver-1.1.1.tgz", + "integrity": "sha512-Eg1OuNntBMH0ojvEKSrvDSnwLmvVuUOSdylH/pSCPNMIspLlweJyIWXCE+k/5hm3cj/EBUYwmWkjhBALNP4LXQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "semver": "^5.1.0" + } + }, + "node_modules/parse-semver/node_modules/semver": { + "version": "5.7.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", + "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver" + } + }, + "node_modules/parse5": { + "version": "7.3.0", + "resolved": "https://registry.npmjs.org/parse5/-/parse5-7.3.0.tgz", + "integrity": "sha512-IInvU7fabl34qmi9gY8XOVxhYyMyuH2xUNpb2q8/Y+7552KlejkRvqvD19nMoUW/uQGGbqNpA6Tufu5FL5BZgw==", + "dev": true, + "license": "MIT", + "dependencies": { + "entities": "^6.0.0" + }, + "funding": { + "url": "https://github.com/inikulin/parse5?sponsor=1" + } + }, + "node_modules/parse5-htmlparser2-tree-adapter": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/parse5-htmlparser2-tree-adapter/-/parse5-htmlparser2-tree-adapter-7.1.0.tgz", + "integrity": "sha512-ruw5xyKs6lrpo9x9rCZqZZnIUntICjQAd0Wsmp396Ul9lN/h+ifgVV1x1gZHi8euej6wTfpqX8j+BFQxF0NS/g==", + "dev": true, + "license": "MIT", + "dependencies": { + "domhandler": "^5.0.3", + "parse5": "^7.0.0" + }, + "funding": { + "url": "https://github.com/inikulin/parse5?sponsor=1" + } + }, + "node_modules/parse5-parser-stream": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/parse5-parser-stream/-/parse5-parser-stream-7.1.2.tgz", + "integrity": "sha512-JyeQc9iwFLn5TbvvqACIF/VXG6abODeB3Fwmv/TGdLk2LfbWkaySGY72at4+Ty7EkPZj854u4CrICqNk2qIbow==", + "dev": true, + "license": "MIT", + "dependencies": { + "parse5": "^7.0.0" + }, + "funding": { + "url": "https://github.com/inikulin/parse5?sponsor=1" + } + }, + "node_modules/parse5/node_modules/entities": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/entities/-/entities-6.0.1.tgz", + "integrity": "sha512-aN97NXWF6AWBTahfVOIrB/NShkzi5H7F9r1s9mD3cDj4Ko5f2qhhVoYMibXF7GlLveb/D2ioWay8lxI97Ven3g==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.12" + }, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, + "node_modules/path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/path-scurry": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-2.0.2.tgz", + "integrity": "sha512-3O/iVVsJAPsOnpwWIeD+d6z/7PmqApyQePUtCndjatj/9I5LylHvt5qluFaBT3I5h3r1ejfR056c+FCv+NnNXg==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "lru-cache": "^11.0.0", + "minipass": "^7.1.2" + }, + "engines": { + "node": "18 || 20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/path-scurry/node_modules/lru-cache": { + "version": "11.5.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.5.0.tgz", + "integrity": "sha512-5YgH9UJd7wVb9hIouI2adWpgqrrICkt070Dnj8EUY1+B4B2P9eRLPAkAAo6NICA7CEhOIeBHl46u9zSNpNu7zA==", + "dev": true, + "license": "BlueOak-1.0.0", + "engines": { + "node": "20 || >=22" + } + }, + "node_modules/path-type": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-6.0.0.tgz", + "integrity": "sha512-Vj7sf++t5pBD637NSfkxpHSMfWaeig5+DKWLhcqIYx6mWQz5hdJTGDVMQiJcw1ZYkhs7AazKDGpRVji1LJCZUQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/pend": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/pend/-/pend-1.2.0.tgz", + "integrity": "sha512-F3asv42UuXchdzt+xXqfW1OGlVBe+mxa2mqI0pg5yAHZPvFmY3Y6drSf/GQ1A86WgWEN9Kzh/WrgKa6iGcHXLg==", + "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": "2.3.2", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.2.tgz", + "integrity": "sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/pino": { + "version": "9.14.0", + "resolved": "https://registry.npmjs.org/pino/-/pino-9.14.0.tgz", + "integrity": "sha512-8OEwKp5juEvb/MjpIc4hjqfgCNysrS94RIOMXYvpYCdm/jglrKEiAYmiumbmGhCvs+IcInsphYDFwqrjr7398w==", + "license": "MIT", + "dependencies": { + "@pinojs/redact": "^0.4.0", + "atomic-sleep": "^1.0.0", + "on-exit-leak-free": "^2.1.0", + "pino-abstract-transport": "^2.0.0", + "pino-std-serializers": "^7.0.0", + "process-warning": "^5.0.0", + "quick-format-unescaped": "^4.0.3", + "real-require": "^0.2.0", + "safe-stable-stringify": "^2.3.1", + "sonic-boom": "^4.0.1", + "thread-stream": "^3.0.0" + }, + "bin": { + "pino": "bin.js" + } + }, + "node_modules/pino-abstract-transport": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/pino-abstract-transport/-/pino-abstract-transport-2.0.0.tgz", + "integrity": "sha512-F63x5tizV6WCh4R6RHyi2Ml+M70DNRXt/+HANowMflpgGFMAym/VKm6G7ZOQRjqN7XbGxK1Lg9t6ZrtzOaivMw==", + "license": "MIT", + "dependencies": { + "split2": "^4.0.0" + } + }, + "node_modules/pino-std-serializers": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/pino-std-serializers/-/pino-std-serializers-7.1.0.tgz", + "integrity": "sha512-BndPH67/JxGExRgiX1dX0w1FvZck5Wa4aal9198SrRhZjH3GxKQUKIBnYJTdj2HDN3UQAS06HlfcSbQj2OHmaw==", + "license": "MIT" + }, + "node_modules/pluralize": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/pluralize/-/pluralize-8.0.0.tgz", + "integrity": "sha512-Nc3IT5yHzflTfbjgqWcCPpo7DaKy4FnpB0l/zCAW0Tc7jxAiuqSxHasntB3D7887LSrA93kDJ9IXovxJYxyLCA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/prebuild-install": { + "version": "7.1.3", + "resolved": "https://registry.npmjs.org/prebuild-install/-/prebuild-install-7.1.3.tgz", + "integrity": "sha512-8Mf2cbV7x1cXPUILADGI3wuhfqWvtiLA1iclTDbFRZkgRQS0NqsPZphna9V+HyTEadheuPmjaJMsbzKQFOzLug==", + "deprecated": "No longer maintained. Please contact the author of the relevant native addon; alternatives are available.", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "detect-libc": "^2.0.0", + "expand-template": "^2.0.3", + "github-from-package": "0.0.0", + "minimist": "^1.2.3", + "mkdirp-classic": "^0.5.3", + "napi-build-utils": "^2.0.0", + "node-abi": "^3.3.0", + "pump": "^3.0.0", + "rc": "^1.2.7", + "simple-get": "^4.0.0", + "tar-fs": "^2.0.0", + "tunnel-agent": "^0.6.0" + }, + "bin": { + "prebuild-install": "bin.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/prelude-ls": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", + "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/prettier": { + "version": "3.8.3", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.8.3.tgz", + "integrity": "sha512-7igPTM53cGHMW8xWuVTydi2KO233VFiTNyF5hLJqpilHfmn8C8gPf+PS7dUT64YcXFbiMGZxS9pCSxL/Dxm/Jw==", + "dev": true, + "license": "MIT", + "bin": { + "prettier": "bin/prettier.cjs" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/prettier/prettier?sponsor=1" + } + }, + "node_modules/process-nextick-args": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", + "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==", + "dev": true, + "license": "MIT" + }, + "node_modules/process-warning": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/process-warning/-/process-warning-5.0.0.tgz", + "integrity": "sha512-a39t9ApHNx2L4+HBnQKqxxHNs1r7KF+Intd8Q/g1bUh6q0WIp9voPXJ/x0j+ZL45KF1pJd9+q2jLIRMfvEshkA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fastify" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fastify" + } + ], + "license": "MIT" + }, + "node_modules/pump": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.4.tgz", + "integrity": "sha512-VS7sjc6KR7e1ukRFhQSY5LM2uBWAUPiOPa/A3mkKmiMwSmRFUITt0xuj+/lesgnCv+dPIEYlkzrcyXgquIHMcA==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "end-of-stream": "^1.1.0", + "once": "^1.3.1" + } + }, + "node_modules/punycode": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", + "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/punycode.js": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/punycode.js/-/punycode.js-2.3.1.tgz", + "integrity": "sha512-uxFIHU0YlHYhDQtV4R9J6a52SLx28BCjT+4ieh7IGbgwVJWO+km431c4yRlREUAsAmt/uMjQUyQHNEPf0M39CA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/qs": { + "version": "6.15.2", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.15.2.tgz", + "integrity": "sha512-Rzq0KEyX/w/tEybncDgdkZrJgVUsUMk3xjh3t5bv3S1HTAtg+uOYt72+ZfwiQwKdysThkTBdL/rTi6HDmX9Ddw==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "side-channel": "^1.1.0" + }, + "engines": { + "node": ">=0.6" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/queue-microtask": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", + "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/quick-format-unescaped": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/quick-format-unescaped/-/quick-format-unescaped-4.0.4.tgz", + "integrity": "sha512-tYC1Q1hgyRuHgloV/YXs2w15unPVh8qfu/qCTfhTYamaw7fyhumKa2yGpdSo87vY32rIclj+4fWYQXUMs9EHvg==", + "license": "MIT" + }, + "node_modules/randombytes": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", + "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "safe-buffer": "^5.1.0" + } + }, + "node_modules/rc": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz", + "integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==", + "dev": true, + "license": "(BSD-2-Clause OR MIT OR Apache-2.0)", + "optional": true, + "dependencies": { + "deep-extend": "^0.6.0", + "ini": "~1.3.0", + "minimist": "^1.2.0", + "strip-json-comments": "~2.0.1" + }, + "bin": { + "rc": "cli.js" + } + }, + "node_modules/rc-config-loader": { + "version": "4.1.4", + "resolved": "https://registry.npmjs.org/rc-config-loader/-/rc-config-loader-4.1.4.tgz", + "integrity": "sha512-3GiwEzklkbXTDp52UR5nT8iXgYAx1V9ZG/kDZT7p60u2GCv2XTwQq4NzinMoMpNtXhmt3WkhYXcj6HH8HdwCEQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "debug": "^4.4.3", + "js-yaml": "^4.1.1", + "json5": "^2.2.3", + "require-from-string": "^2.0.2" + } + }, + "node_modules/rc/node_modules/strip-json-comments": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", + "integrity": "sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ==", + "dev": true, + "license": "MIT", + "optional": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/read": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/read/-/read-1.0.7.tgz", + "integrity": "sha512-rSOKNYUmaxy0om1BNjMN4ezNT6VKK+2xF4GBhc81mkH7L60i6dp8qPYrkndNLT3QPphoII3maL9PVC9XmhHwVQ==", + "dev": true, + "license": "ISC", + "dependencies": { + "mute-stream": "~0.0.4" + }, + "engines": { + "node": ">=0.8" + } + }, + "node_modules/read-pkg": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-9.0.1.tgz", + "integrity": "sha512-9viLL4/n1BJUCT1NXVTdS1jtm80yDEgR5T4yCelII49Mbj0v1rZdKqj7zCiYdbB0CuCgdrvHcNogAKTFPBocFA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/normalize-package-data": "^2.4.3", + "normalize-package-data": "^6.0.0", + "parse-json": "^8.0.0", + "type-fest": "^4.6.0", + "unicorn-magic": "^0.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/read-pkg/node_modules/unicorn-magic": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/unicorn-magic/-/unicorn-magic-0.1.0.tgz", + "integrity": "sha512-lRfVq8fE8gz6QMBuDM6a+LO3IAzTi05H6gCVaUpir2E1Rwpo4ZUog45KpNXKC/Mn3Yb9UDuHumeFTo9iV/D9FQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/readable-stream": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", + "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", + "dev": true, + "license": "MIT", + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "node_modules/readable-stream/node_modules/safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "dev": true, + "license": "MIT" + }, + "node_modules/readdirp": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", + "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", + "dev": true, + "license": "MIT", + "dependencies": { + "picomatch": "^2.2.1" + }, + "engines": { + "node": ">=8.10.0" + } + }, + "node_modules/real-require": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/real-require/-/real-require-0.2.0.tgz", + "integrity": "sha512-57frrGM/OCTLqLOAh0mhVA9VBMHd+9U7Zb2THMGdBUoZVOtGbJzjxsYGDJ3A9AYYCP4hn6y1TVbaOfzWtm5GFg==", + "license": "MIT", + "engines": { + "node": ">= 12.13.0" + } + }, + "node_modules/require-directory": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/require-from-string": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", + "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/resolve-from": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", + "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/restore-cursor": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-5.1.0.tgz", + "integrity": "sha512-oMA2dcrw6u0YfxJQXm342bFKX/E4sG9rbTzO9ptUcR/e8A33cHuvStiYOwH7fszkZlZ1z/ta9AAoPk2F4qIOHA==", + "dev": true, + "license": "MIT", + "dependencies": { + "onetime": "^7.0.0", + "signal-exit": "^4.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/reusify": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.1.0.tgz", + "integrity": "sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==", + "dev": true, + "license": "MIT", + "engines": { + "iojs": ">=1.0.0", + "node": ">=0.10.0" + } + }, + "node_modules/run-applescript": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/run-applescript/-/run-applescript-7.1.0.tgz", + "integrity": "sha512-DPe5pVFaAsinSaV6QjQ6gdiedWDcRCbUuiQfQa2wmWV7+xC9bGulGI8+TdRmoFkAPaBXk8CrAbnlY2ISniJ47Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/run-parallel": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", + "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", + "dependencies": { + "queue-microtask": "^1.2.2" + } + }, + "node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/safe-stable-stringify": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/safe-stable-stringify/-/safe-stable-stringify-2.5.0.tgz", + "integrity": "sha512-b3rppTKm9T+PsVCBEOUR46GWI7fdOs00VKZ1+9c1EWDaDMvjQc6tUwuFyIprgGgTcWoVHSKrU8H31ZHA2e0RHA==", + "license": "MIT", + "engines": { + "node": ">=10" + } + }, + "node_modules/safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", + "dev": true, + "license": "MIT" + }, + "node_modules/sax": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/sax/-/sax-1.6.0.tgz", + "integrity": "sha512-6R3J5M4AcbtLUdZmRv2SygeVaM7IhrLXu9BmnOGmmACak8fiUtOsYNWUS4uK7upbmHIBbLBeFeI//477BKLBzA==", + "dev": true, + "license": "BlueOak-1.0.0", + "engines": { + "node": ">=11.0.0" + } + }, + "node_modules/secretlint": { + "version": "10.2.2", + "resolved": "https://registry.npmjs.org/secretlint/-/secretlint-10.2.2.tgz", + "integrity": "sha512-xVpkeHV/aoWe4vP4TansF622nBEImzCY73y/0042DuJ29iKIaqgoJ8fGxre3rVSHHbxar4FdJobmTnLp9AU0eg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@secretlint/config-creator": "^10.2.2", + "@secretlint/formatter": "^10.2.2", + "@secretlint/node": "^10.2.2", + "@secretlint/profiler": "^10.2.2", + "debug": "^4.4.1", + "globby": "^14.1.0", + "read-pkg": "^9.0.1" + }, + "bin": { + "secretlint": "bin/secretlint.js" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/semver": { + "version": "7.8.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.8.1.tgz", + "integrity": "sha512-rkVq3IXh+4FDGch+KwzX3aV9W3kO54GyEgpvBzSyctDA6Xtd7RJQV1xmXbeQp5v7+VzLOfVqiutSE6GICgPFvg==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/serialize-javascript": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.2.tgz", + "integrity": "sha512-Saa1xPByTTq2gdeFZYLLo+RFE35NHZkAbqZeWNd3BpzppeVisAqpDjcp8dyf6uIvEqJRd46jemmyA4iFIeVk8g==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "randombytes": "^2.1.0" + } + }, + "node_modules/setimmediate": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz", + "integrity": "sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA==", + "dev": true, + "license": "MIT" + }, + "node_modules/shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dev": true, + "license": "MIT", + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/side-channel": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.0.tgz", + "integrity": "sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "object-inspect": "^1.13.3", + "side-channel-list": "^1.0.0", + "side-channel-map": "^1.0.1", + "side-channel-weakmap": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-list": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.1.tgz", + "integrity": "sha512-mjn/0bi/oUURjc5Xl7IaWi/OJJJumuoJFQJfDDyO46+hBWsfaVM65TBHq2eoZBhzl9EchxOijpkbRC8SVBQU0w==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "object-inspect": "^1.13.4" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-map": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/side-channel-map/-/side-channel-map-1.0.1.tgz", + "integrity": "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-weakmap": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz", + "integrity": "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3", + "side-channel-map": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/signal-exit": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", + "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/simple-concat": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/simple-concat/-/simple-concat-1.0.1.tgz", + "integrity": "sha512-cSFtAPtRhljv69IK0hTVZQ+OfE9nePi/rtJmw5UjHeVyVroEqJXP1sFztKUy1qU+xvz3u/sfYJLa947b7nAN2Q==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", + "optional": true + }, + "node_modules/simple-get": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/simple-get/-/simple-get-4.0.1.tgz", + "integrity": "sha512-brv7p5WgH0jmQJr1ZDDfKDOSeWWg+OVypG99A/5vYGPqJ6pxiaHLy8nxtFjBA7oMa01ebA9gfh1uMCFqOuXxvA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", + "optional": true, + "dependencies": { + "decompress-response": "^6.0.0", + "once": "^1.3.1", + "simple-concat": "^1.0.0" + } + }, + "node_modules/slash": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-5.1.0.tgz", + "integrity": "sha512-ZA6oR3T/pEyuqwMgAKT0/hAv8oAXckzbkmR0UkUosQ+Mc4RxGoJkRmwHgHufaenlyAgE1Mxgpdcrf75y6XcnDg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/slice-ansi": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-4.0.0.tgz", + "integrity": "sha512-qMCMfhY040cVHT43K9BFygqYbUPFZKHOg7K73mtTWJRb8pyP3fzf4Ixd5SzdEJQ6MRUg/WBnOLxghZtKKurENQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.0.0", + "astral-regex": "^2.0.0", + "is-fullwidth-code-point": "^3.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/slice-ansi?sponsor=1" + } + }, + "node_modules/sonic-boom": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/sonic-boom/-/sonic-boom-4.2.1.tgz", + "integrity": "sha512-w6AxtubXa2wTXAUsZMMWERrsIRAdrK0Sc+FUytWvYAhBJLyuI4llrMIC1DtlNSdI99EI86KZum2MMq3EAZlF9Q==", + "license": "MIT", + "dependencies": { + "atomic-sleep": "^1.0.0" + } + }, + "node_modules/spdx-correct": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.2.0.tgz", + "integrity": "sha512-kN9dJbvnySHULIluDHy32WHRUu3Og7B9sbY7tsFLctQkIqnMh3hErYgdMjTYuqmcXX+lK5T1lnUt3G7zNswmZA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "spdx-expression-parse": "^3.0.0", + "spdx-license-ids": "^3.0.0" + } + }, + "node_modules/spdx-exceptions": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.5.0.tgz", + "integrity": "sha512-PiU42r+xO4UbUS1buo3LPJkjlO7430Xn5SVAhdpzzsPHsjbYVflnnFdATgabnLude+Cqu25p6N+g2lw/PFsa4w==", + "dev": true, + "license": "CC-BY-3.0" + }, + "node_modules/spdx-expression-parse": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.1.tgz", + "integrity": "sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "spdx-exceptions": "^2.1.0", + "spdx-license-ids": "^3.0.0" + } + }, + "node_modules/spdx-license-ids": { + "version": "3.0.23", + "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.23.tgz", + "integrity": "sha512-CWLcCCH7VLu13TgOH+r8p1O/Znwhqv/dbb6lqWy67G+pT1kHmeD/+V36AVb/vq8QMIQwVShJ6Ssl5FPh0fuSdw==", + "dev": true, + "license": "CC0-1.0" + }, + "node_modules/split2": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/split2/-/split2-4.2.0.tgz", + "integrity": "sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg==", + "license": "ISC", + "engines": { + "node": ">= 10.x" + } + }, + "node_modules/stdin-discarder": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/stdin-discarder/-/stdin-discarder-0.2.2.tgz", + "integrity": "sha512-UhDfHmA92YAlNnCfhmq0VeNL5bDbiZGg7sZ2IvPsXubGkiNa9EC+tUTsjBRsYUAz87btI6/1wf4XoVvQ3uRnmQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "dev": true, + "license": "MIT", + "dependencies": { + "safe-buffer": "~5.1.0" + } + }, + "node_modules/string_decoder/node_modules/safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "dev": true, + "license": "MIT" + }, + "node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/string-width-cjs": { + "name": "string-width", + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/string-width-cjs/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/string-width-cjs/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/string-width/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/string-width/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-ansi": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.2.0.tgz", + "integrity": "sha512-yDPMNjp4WyfYBkHnjIRLfca1i6KMyGCtsVgoKe/z1+6vukgaENdgGBZt+ZmKPc4gavvEZ5OgHfHdrazhgNyG7w==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^6.2.2" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, + "node_modules/strip-ansi-cjs": { + "name": "strip-ansi", + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-ansi-cjs/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-json-comments": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", + "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/structured-source": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/structured-source/-/structured-source-4.0.0.tgz", + "integrity": "sha512-qGzRFNJDjFieQkl/sVOI2dUjHKRyL9dAJi2gCPGJLbJHBIkyOHxjuocpIEfbLioX+qSJpvbYdT49/YCdMznKxA==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "boundary": "^2.0.0" + } + }, + "node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/supports-hyperlinks": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/supports-hyperlinks/-/supports-hyperlinks-3.2.0.tgz", + "integrity": "sha512-zFObLMyZeEwzAoKCyu1B91U79K2t7ApXuQfo8OuxwXLDgcKxuwM+YvcbIhm6QWqz7mHUH1TVytR1PwVVjEuMig==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0", + "supports-color": "^7.0.0" + }, + "engines": { + "node": ">=14.18" + }, + "funding": { + "url": "https://github.com/chalk/supports-hyperlinks?sponsor=1" + } + }, + "node_modules/table": { + "version": "6.9.0", + "resolved": "https://registry.npmjs.org/table/-/table-6.9.0.tgz", + "integrity": "sha512-9kY+CygyYM6j02t5YFHbNz2FN5QmYGv9zAjVp4lCDjlCw7amdckXlEt/bjMhUIfj4ThGRE4gCUH5+yGnNuPo5A==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "ajv": "^8.0.1", + "lodash.truncate": "^4.4.2", + "slice-ansi": "^4.0.0", + "string-width": "^4.2.3", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/table/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/table/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/tar-fs": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-2.1.4.tgz", + "integrity": "sha512-mDAjwmZdh7LTT6pNleZ05Yt65HC3E+NiQzl672vQG38jIrehtJk/J3mNwIg+vShQPcLF/LV7CMnDW6vjj6sfYQ==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "chownr": "^1.1.1", + "mkdirp-classic": "^0.5.2", + "pump": "^3.0.0", + "tar-stream": "^2.1.4" + } + }, + "node_modules/tar-stream": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-2.2.0.tgz", + "integrity": "sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "bl": "^4.0.3", + "end-of-stream": "^1.4.1", + "fs-constants": "^1.0.0", + "inherits": "^2.0.3", + "readable-stream": "^3.1.1" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/tar-stream/node_modules/readable-stream": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", + "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/terminal-link": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/terminal-link/-/terminal-link-4.0.0.tgz", + "integrity": "sha512-lk+vH+MccxNqgVqSnkMVKx4VLJfnLjDBGzH16JVZjKE2DoxP57s6/vt6JmXV5I3jBcfGrxNrYtC+mPtU7WJztA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-escapes": "^7.0.0", + "supports-hyperlinks": "^3.2.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/test-exclude": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-7.0.2.tgz", + "integrity": "sha512-u9E6A+ZDYdp7a4WnarkXPZOx8Ilz46+kby6p1yZ8zsGTz9gYa6FIS7lj2oezzNKmtdyyJNNmmXDppga5GB7kSw==", + "dev": true, + "license": "ISC", + "dependencies": { + "@istanbuljs/schema": "^0.1.2", + "glob": "^10.4.1", + "minimatch": "^10.2.2" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/test-exclude/node_modules/@isaacs/cliui": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", + "integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==", + "dev": true, + "license": "ISC", + "dependencies": { + "string-width": "^5.1.2", + "string-width-cjs": "npm:string-width@^4.2.0", + "strip-ansi": "^7.0.1", + "strip-ansi-cjs": "npm:strip-ansi@^6.0.1", + "wrap-ansi": "^8.1.0", + "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/test-exclude/node_modules/ansi-styles": { + "version": "6.2.3", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.3.tgz", + "integrity": "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/test-exclude/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==", + "dev": true, + "license": "MIT", + "engines": { + "node": "18 || 20 || >=22" + } + }, + "node_modules/test-exclude/node_modules/brace-expansion": { + "version": "5.0.6", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.6.tgz", + "integrity": "sha512-kLpxurY4Z4r9sgMsyG0Z9uzsBlgiU/EFKhj/h91/8yHu0edo7XuixOIH3VcJ8kkxs6/jPzoI6U9Vj3WqbMQ94g==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^4.0.2" + }, + "engines": { + "node": "18 || 20 || >=22" + } + }, + "node_modules/test-exclude/node_modules/emoji-regex": { + "version": "9.2.2", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", + "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", + "dev": true, + "license": "MIT" + }, + "node_modules/test-exclude/node_modules/glob": { + "version": "10.5.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-10.5.0.tgz", + "integrity": "sha512-DfXN8DfhJ7NH3Oe7cFmu3NCu1wKbkReJ8TorzSAFbSKrlNaQSKfIzqYqVY8zlbs2NLBbWpRiU52GX2PbaBVNkg==", + "deprecated": "Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me", + "dev": true, + "license": "ISC", + "dependencies": { + "foreground-child": "^3.1.0", + "jackspeak": "^3.1.2", + "minimatch": "^9.0.4", + "minipass": "^7.1.2", + "package-json-from-dist": "^1.0.0", + "path-scurry": "^1.11.1" + }, + "bin": { + "glob": "dist/esm/bin.mjs" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/test-exclude/node_modules/glob/node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true, + "license": "MIT" + }, + "node_modules/test-exclude/node_modules/glob/node_modules/brace-expansion": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.1.0.tgz", + "integrity": "sha512-TN1kCZAgdgweJhWWpgKYrQaMNHcDULHkWwQIspdtjV4Y5aurRdZpjAqn6yX3FPqTA9ngHCc4hJxMAMgGfve85w==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/test-exclude/node_modules/glob/node_modules/minimatch": { + "version": "9.0.9", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.9.tgz", + "integrity": "sha512-OBwBN9AL4dqmETlpS2zasx+vTeWclWzkblfZk7KTA5j3jeOONz/tRCnZomUyvNg83wL5Zv9Ss6HMJXAgL8R2Yg==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.2" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/test-exclude/node_modules/jackspeak": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-3.4.3.tgz", + "integrity": "sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "@isaacs/cliui": "^8.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + }, + "optionalDependencies": { + "@pkgjs/parseargs": "^0.11.0" + } + }, + "node_modules/test-exclude/node_modules/lru-cache": { + "version": "10.4.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", + "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/test-exclude/node_modules/minimatch": { + "version": "10.2.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.5.tgz", + "integrity": "sha512-MULkVLfKGYDFYejP07QOurDLLQpcjk7Fw+7jXS2R2czRQzR56yHRveU5NDJEOviH+hETZKSkIk5c+T23GjFUMg==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "brace-expansion": "^5.0.5" + }, + "engines": { + "node": "18 || 20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/test-exclude/node_modules/path-scurry": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.11.1.tgz", + "integrity": "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "lru-cache": "^10.2.0", + "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" + }, + "engines": { + "node": ">=16 || 14 >=14.18" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/test-exclude/node_modules/string-width": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", + "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", + "dev": true, + "license": "MIT", + "dependencies": { + "eastasianwidth": "^0.2.0", + "emoji-regex": "^9.2.2", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/test-exclude/node_modules/wrap-ansi": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz", + "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^6.1.0", + "string-width": "^5.0.1", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/text-table": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", + "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==", + "dev": true, + "license": "MIT" + }, + "node_modules/textextensions": { + "version": "6.11.0", + "resolved": "https://registry.npmjs.org/textextensions/-/textextensions-6.11.0.tgz", + "integrity": "sha512-tXJwSr9355kFJI3lbCkPpUH5cP8/M0GGy2xLO34aZCjMXBaK3SoPnZwr/oWmo1FdCnELcs4npdCIOFtq9W3ruQ==", + "dev": true, + "license": "Artistic-2.0", + "dependencies": { + "editions": "^6.21.0" + }, + "engines": { + "node": ">=4" + }, + "funding": { + "url": "https://bevry.me/fund" + } + }, + "node_modules/thread-stream": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/thread-stream/-/thread-stream-3.1.0.tgz", + "integrity": "sha512-OqyPZ9u96VohAyMfJykzmivOrY2wfMSf3C5TtFJVgN+Hm6aj+voFhlK+kZEIv2FBh1X6Xp3DlnCOfEQ3B2J86A==", + "license": "MIT", + "dependencies": { + "real-require": "^0.2.0" + } + }, + "node_modules/tinyglobby": { + "version": "0.2.16", + "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.16.tgz", + "integrity": "sha512-pn99VhoACYR8nFHhxqix+uvsbXineAasWm5ojXoN8xEwK5Kd3/TrhNn1wByuD52UxWRLy8pu+kRMniEi6Eq9Zg==", + "dev": true, + "license": "MIT", + "dependencies": { + "fdir": "^6.5.0", + "picomatch": "^4.0.4" + }, + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/SuperchupuDev" + } + }, + "node_modules/tinyglobby/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/tinyglobby/node_modules/picomatch": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.4.tgz", + "integrity": "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "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==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14.14" + } + }, + "node_modules/to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/ts-api-utils": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-2.5.0.tgz", + "integrity": "sha512-OJ/ibxhPlqrMM0UiNHJ/0CKQkoKF243/AEmplt3qpRgkW8VG7IfOS41h7V8TjITqdByHzrjcS/2si+y4lIh8NA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18.12" + }, + "peerDependencies": { + "typescript": ">=4.8.4" + } + }, + "node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "dev": true, + "license": "0BSD" + }, + "node_modules/tunnel": { + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/tunnel/-/tunnel-0.0.6.tgz", + "integrity": "sha512-1h/Lnq9yajKY2PEbBadPXj3VxsDDu844OnaAo52UVmIzIvwwtBPIuNvkjuzBlTWpfJyUbG3ez0KSBibQkj4ojg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.6.11 <=0.7.0 || >=0.7.3" + } + }, + "node_modules/tunnel-agent": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", + "integrity": "sha512-McnNiV1l8RYeY8tBgEpuodCC1mLUdbSN+CYBL7kJsJNInOP8UjDDEwdk6Mw60vdLLrr5NHKZhMAOSrR2NZuQ+w==", + "dev": true, + "license": "Apache-2.0", + "optional": true, + "dependencies": { + "safe-buffer": "^5.0.1" + }, + "engines": { + "node": "*" + } + }, + "node_modules/type-check": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", + "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", + "dev": true, + "license": "MIT", + "dependencies": { + "prelude-ls": "^1.2.1" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/type-fest": { + "version": "4.41.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-4.41.0.tgz", + "integrity": "sha512-TeTSQ6H5YHvpqVwBRcnLDCBnDOHWYu7IvGbHT6N8AOymcr9PJGjc1GTtiWZTYg0NCgYwvnYWEkVChQAr9bjfwA==", + "dev": true, + "license": "(MIT OR CC0-1.0)", + "engines": { + "node": ">=16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/typed-rest-client": { + "version": "1.8.11", + "resolved": "https://registry.npmjs.org/typed-rest-client/-/typed-rest-client-1.8.11.tgz", + "integrity": "sha512-5UvfMpd1oelmUPRbbaVnq+rHP7ng2cE4qoQkQeAqxRL6PklkxsM0g32/HL0yfvruK6ojQ5x8EE+HF4YV6DtuCA==", + "dev": true, + "license": "MIT", + "dependencies": { + "qs": "^6.9.1", + "tunnel": "0.0.6", + "underscore": "^1.12.1" + } + }, + "node_modules/typescript": { + "version": "5.9.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz", + "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/typescript-eslint": { + "version": "8.59.4", + "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.59.4.tgz", + "integrity": "sha512-Rw6+44QNFaXtgHSjPy+Kw8hrJniMYzR85E9yLmOLcfZ91/rz+JXQbDTCmc6ccxMPY6K6PgAq26f0JCBfR7LIPQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/eslint-plugin": "8.59.4", + "@typescript-eslint/parser": "8.59.4", + "@typescript-eslint/typescript-estree": "8.59.4", + "@typescript-eslint/utils": "8.59.4" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", + "typescript": ">=4.8.4 <6.1.0" + } + }, + "node_modules/uc.micro": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/uc.micro/-/uc.micro-2.1.0.tgz", + "integrity": "sha512-ARDJmphmdvUk6Glw7y9DQ2bFkKBHwQHLi2lsaH6PPmz/Ka9sFOBsBluozhDltWmnv9u/cF6Rt87znRTPV+yp/A==", + "dev": true, + "license": "MIT" + }, + "node_modules/underscore": { + "version": "1.13.8", + "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.13.8.tgz", + "integrity": "sha512-DXtD3ZtEQzc7M8m4cXotyHR+FAS18C64asBYY5vqZexfYryNNnDc02W4hKg3rdQuqOYas1jkseX0+nZXjTXnvQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/undici": { + "version": "7.25.0", + "resolved": "https://registry.npmjs.org/undici/-/undici-7.25.0.tgz", + "integrity": "sha512-xXnp4kTyor2Zq+J1FfPI6Eq3ew5h6Vl0F/8d9XU5zZQf1tX9s2Su1/3PiMmUANFULpmksxkClamIZcaUqryHsQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=20.18.1" + } + }, + "node_modules/undici-types": { + "version": "6.21.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz", + "integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/unicorn-magic": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/unicorn-magic/-/unicorn-magic-0.3.0.tgz", + "integrity": "sha512-+QBBXBCvifc56fsbuxZQ6Sic3wqqc3WWaqxs58gvJrcOuN83HGTCwz3oS5phzU9LthRNE9VrJCFCLUgHeeFnfA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/universalify": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", + "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 10.0.0" + } + }, + "node_modules/uri-js": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", + "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "punycode": "^2.1.0" + } + }, + "node_modules/url-join": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/url-join/-/url-join-4.0.1.tgz", + "integrity": "sha512-jk1+QP6ZJqyOiuEI9AEWQfju/nB2Pw466kbA0LEZljHwKeMgd9WrAEgEGxjPDD2+TNbbb37rTyhEfrCXfuKXnA==", + "dev": true, + "license": "MIT" + }, + "node_modules/util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", + "dev": true, + "license": "MIT" + }, + "node_modules/v8-to-istanbul": { + "version": "9.3.0", + "resolved": "https://registry.npmjs.org/v8-to-istanbul/-/v8-to-istanbul-9.3.0.tgz", + "integrity": "sha512-kiGUalWN+rgBJ/1OHZsBtU4rXZOfj/7rKQxULKlIzwzQSvMJUUNgPwJEEh7gU6xEVxC0ahoOBvN2YI8GH6FNgA==", + "dev": true, + "license": "ISC", + "dependencies": { + "@jridgewell/trace-mapping": "^0.3.12", + "@types/istanbul-lib-coverage": "^2.0.1", + "convert-source-map": "^2.0.0" + }, + "engines": { + "node": ">=10.12.0" + } + }, + "node_modules/validate-npm-package-license": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz", + "integrity": "sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "spdx-correct": "^3.0.0", + "spdx-expression-parse": "^3.0.0" + } + }, + "node_modules/version-range": { + "version": "4.15.0", + "resolved": "https://registry.npmjs.org/version-range/-/version-range-4.15.0.tgz", + "integrity": "sha512-Ck0EJbAGxHwprkzFO966t4/5QkRuzh+/I1RxhLgUKKwEn+Cd8NwM60mE3AqBZg5gYODoXW0EFsQvbZjRlvdqbg==", + "dev": true, + "license": "Artistic-2.0", + "engines": { + "node": ">=4" + }, + "funding": { + "url": "https://bevry.me/fund" + } + }, + "node_modules/whatwg-encoding": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/whatwg-encoding/-/whatwg-encoding-3.1.1.tgz", + "integrity": "sha512-6qN4hJdMwfYBtE3YBTTHhoeuUrDBPZmbQaxWAqSALV/MeEnR5z1xd8UKud2RAkFoPkmB+hli1TZSnyi84xz1vQ==", + "deprecated": "Use @exodus/bytes instead for a more spec-conformant and faster implementation", + "dev": true, + "license": "MIT", + "dependencies": { + "iconv-lite": "0.6.3" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/whatwg-mimetype": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/whatwg-mimetype/-/whatwg-mimetype-4.0.0.tgz", + "integrity": "sha512-QaKxh0eNIi2mE9p2vEdzfagOKHCcj1pJ56EEHGQOVxp8r9/iszLUUV7v89x9O1p/T+NlTM5W7jW6+cz4Fq1YVg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + } + }, + "node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "license": "ISC", + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/word-wrap": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz", + "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/workerpool": { + "version": "6.5.1", + "resolved": "https://registry.npmjs.org/workerpool/-/workerpool-6.5.1.tgz", + "integrity": "sha512-Fs4dNYcsdpYSAfVxhnl1L5zTksjvOJxtC5hzMNl+1t9B8hTJTdKDyZ5ju7ztgPy+ft9tBFXoOlDNiOT9WUXZlA==", + "dev": true, + "license": "Apache-2.0" + }, + "node_modules/wrap-ansi": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/wrap-ansi-cjs": { + "name": "wrap-ansi", + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/wrap-ansi-cjs/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/wrap-ansi-cjs/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/wrap-ansi/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/wrap-ansi/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/wsl-utils": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/wsl-utils/-/wsl-utils-0.1.0.tgz", + "integrity": "sha512-h3Fbisa2nKGPxCpm89Hk33lBLsnaGBvctQopaBSOW/uIs6FTe1ATyAnKFJrzVs9vpGdsTe73WF3V4lIsk4Gacw==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-wsl": "^3.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/xml2js": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.5.0.tgz", + "integrity": "sha512-drPFnkQJik/O+uPKpqSgr22mpuFHqKdbS835iAQrUC73L2F5WkboIRd63ai/2Yg6I1jzifPFKH2NTK+cfglkIA==", + "dev": true, + "license": "MIT", + "dependencies": { + "sax": ">=0.6.0", + "xmlbuilder": "~11.0.0" + }, + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/xmlbuilder": { + "version": "11.0.1", + "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-11.0.1.tgz", + "integrity": "sha512-fDlsI/kFEx7gLvbecc0/ohLG50fugQp8ryHzMTuW9vSa1GJ0XYWKnhsUx7oie3G98+r56aTQIUB4kht42R3JvA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4.0" + } + }, + "node_modules/y18n": { + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", + "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=10" + } + }, + "node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true, + "license": "ISC" + }, + "node_modules/yargs": { + "version": "17.7.2", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", + "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", + "dev": true, + "license": "MIT", + "dependencies": { + "cliui": "^8.0.1", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.3", + "y18n": "^5.0.5", + "yargs-parser": "^21.1.1" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/yargs-parser": { + "version": "21.1.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", + "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/yargs-unparser": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/yargs-unparser/-/yargs-unparser-2.0.0.tgz", + "integrity": "sha512-7pRTIA9Qc1caZ0bZ6RYRGbHJthJWuakf+WmHK0rVeLkNrrGhfoabBNdue6kdINI6r4if7ocq9aD/n7xwKOdzOA==", + "dev": true, + "license": "MIT", + "dependencies": { + "camelcase": "^6.0.0", + "decamelize": "^4.0.0", + "flat": "^5.0.2", + "is-plain-obj": "^2.1.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/yauzl": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/yauzl/-/yauzl-3.3.1.tgz", + "integrity": "sha512-RNPCUkiE/ZgO4w8i9U5yDQVHaFDdnzaFANElRvpJteCspvmv2VqrRb9lvS6odVD+jqI/zDsxAHJVsafpcheVQQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "buffer-crc32": "~0.2.3", + "pend": "~1.2.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/yazl": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/yazl/-/yazl-2.5.1.tgz", + "integrity": "sha512-phENi2PLiHnHb6QBVot+dJnaAZ0xosj7p3fWl+znIjBDlnMI2PsZCJZ306BPTFOaHf5qdDEI8x5qFrSOBN5vrw==", + "dev": true, + "license": "MIT", + "dependencies": { + "buffer-crc32": "~0.2.3" + } + }, + "node_modules/yocto-queue": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", + "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + } + } +} diff --git a/package.json b/package.json new file mode 100644 index 0000000..19ef2d1 --- /dev/null +++ b/package.json @@ -0,0 +1,96 @@ +{ + "name": "diffy", + "displayName": "Diffy", + "description": "Pick two things and diff them — context-menu git diffing in VSCode.", + "version": "0.1.0", + "publisher": "nimblesite", + "license": "MIT", + "repository": { + "type": "git", + "url": "https://github.com/MelbourneDeveloper/Diffy.git" + }, + "main": "./out/extension.js", + "engines": { + "vscode": "^1.85.0", + "node": ">=20" + }, + "categories": ["SCM Providers"], + "activationEvents": ["onStartupFinished"], + "extensionDependencies": ["vscode.git"], + "contributes": { + "commands": [ + { "command": "diffy.compareWith", "title": "Diffy: Compare with…" }, + { "command": "diffy.compareWithWorkingCopy", "title": "Diffy: Compare with Working Copy" }, + { "command": "diffy.compareWithPrevious", "title": "Diffy: Compare with Previous" }, + { "command": "diffy.compareTwoCommits", "title": "Diffy: Compare Two Commits" }, + { "command": "diffy.compareFileWithCommit", "title": "Diffy: Compare with Commit…" }, + { "command": "diffy.reopenLast", "title": "Diffy: Reopen Last Comparison" }, + { "command": "diffy.showLogs", "title": "Diffy: Show Logs" } + ], + "menus": { + "scm/history/item/context": [ + { "command": "diffy.compareWith", "when": "scmProvider == git", "group": "diffy@1" }, + { "command": "diffy.compareWithWorkingCopy", "when": "scmProvider == git", "group": "diffy@2" }, + { "command": "diffy.compareWithPrevious", "when": "scmProvider == git", "group": "diffy@3" } + ], + "scm/resourceState/context": [ + { "command": "diffy.compareFileWithCommit", "when": "scmProvider == git", "group": "diffy@1" } + ], + "editor/title/context": [ + { "command": "diffy.compareFileWithCommit", "when": "resourceScheme == file", "group": "3_compare" } + ], + "explorer/context": [ + { "command": "diffy.compareFileWithCommit", "when": "resourceScheme == file && !explorerResourceIsFolder", "group": "3_compare" } + ], + "commandPalette": [ + { "command": "diffy.compareWith", "when": "false" }, + { "command": "diffy.compareWithWorkingCopy", "when": "false" }, + { "command": "diffy.compareWithPrevious", "when": "false" }, + { "command": "diffy.showLogs", "when": "false" } + ] + } + }, + "scripts": { + "build": "tsc -p .", + "watch": "tsc -p . -w", + "typecheck": "tsc -p . --noEmit", + "lint": "eslint src", + "fmt": "prettier --write \"src/**/*.ts\" \"*.json\" \"*.md\"", + "fmt:check": "prettier --check \"src/**/*.ts\" \"*.json\" \"*.md\"", + "pretest": "npm run build", + "test:unit": "mocha --reporter spec --timeout 10000 \"out/test/unit/**/*.test.js\"", + "test:e2e": "node ./out/test/runTests.js", + "test:coverage": "node ./scripts/run-coverage.mjs" + }, + "dependencies": { + "pino": "^9.5.0" + }, + "devDependencies": { + "@eslint/js": "^9.13.0", + "@types/mocha": "^10.0.9", + "@types/node": "^20.16.0", + "@types/vscode": "^1.85.0", + "@vscode/test-electron": "^2.4.1", + "@vscode/vsce": "^3.2.0", + "c8": "^10.1.2", + "eslint": "^9.13.0", + "glob": "^11.0.0", + "mocha": "^10.8.2", + "prettier": "^3.3.3", + "typescript": "^5.6.3", + "typescript-eslint": "^8.12.2" + }, + "c8": { + "tempDirectory": "coverage/tmp", + "reportsDirectory": "coverage/report", + "include": ["out/**/*.js"], + "exclude": [ + "out/test/**", + "out/scripts/**", + "out/git/types.js", + "**/*.d.ts" + ], + "all": true, + "reporter": ["text", "text-summary", "json-summary", "lcov"] + } +} diff --git a/scripts/run-coverage.mjs b/scripts/run-coverage.mjs new file mode 100644 index 0000000..2726276 --- /dev/null +++ b/scripts/run-coverage.mjs @@ -0,0 +1,39 @@ +#!/usr/bin/env node +// Orchestrates unit + E2E test runs, accumulating raw V8 coverage profiles +// into a single temp dir, then asks c8 to render the merged report. +// +// Why a separate script: the unit run is a node-mocha process, and the E2E +// run spawns Electron (extension host). Both write coverage to NODE_V8_COVERAGE +// when it's set in their env. We point both at the same dir, then `c8 report` +// merges the lot. + +import { spawnSync } from 'node:child_process'; +import { rmSync, mkdirSync } from 'node:fs'; +import { resolve } from 'node:path'; + +const repoRoot = resolve(import.meta.dirname, '..'); +const tempDir = resolve(repoRoot, 'coverage', 'tmp'); + +const run = (cmd, args, env) => { + process.stdout.write(`\n==> ${cmd} ${args.join(' ')}\n`); + const r = spawnSync(cmd, args, { + cwd: repoRoot, + stdio: 'inherit', + env: { ...process.env, ...env }, + }); + if (r.status !== 0) { + process.stderr.write(`\n!! ${cmd} ${args.join(' ')} exited ${r.status ?? '?'}\n`); + process.exit(r.status ?? 1); + } +}; + +rmSync(tempDir, { recursive: true, force: true }); +mkdirSync(tempDir, { recursive: true }); + +run('npm', ['run', 'build'], {}); + +const covEnv = { NODE_V8_COVERAGE: tempDir }; +run('npm', ['run', 'test:unit'], covEnv); +run('npm', ['run', 'test:e2e'], covEnv); + +run('npx', ['c8', 'report'], {}); diff --git a/src/commands/compareFileWithCommit.ts b/src/commands/compareFileWithCommit.ts new file mode 100644 index 0000000..0e9abb0 --- /dev/null +++ b/src/commands/compareFileWithCommit.ts @@ -0,0 +1,51 @@ +import * as vscode from 'vscode'; +import { findRepoForUri } from '../vscodeGitApi'; +import { pickCommit } from '../ui/CommitPicker'; +import { + type CommandDeps, + buildRepo, + openDiff, +} from './shared'; +import { reportGitError, sideAFromSha } from './flow'; + +const resolveTargetUri = (uri?: vscode.Uri): vscode.Uri | undefined => { + if (uri !== undefined) { + return uri; + } + return vscode.window.activeTextEditor?.document.uri; +}; + +const handler = async ( + deps: CommandDeps, + uri: vscode.Uri | undefined, +): Promise<void> => { + const target = resolveTargetUri(uri); + if (target === undefined) { + void vscode.window.showWarningMessage('Diffy: open a file first.'); + return; + } + const vsRepo = findRepoForUri(deps.gitApi, target); + if (vsRepo === undefined) { + void vscode.window.showWarningMessage('Diffy: file is not in a git repository.'); + return; + } + const repo = buildRepo(deps.runner, vsRepo); + const log = await repo.log({}); + if (!log.ok) { + reportGitError({ output: deps.output, op: 'log', e: log.error }); + return; + } + const commit = await pickCommit({ commits: log.value }); + if (!commit.ok) { + return; + } + await openDiff({ + revA: sideAFromSha(commit.value.sha), + revB: { kind: 'workingCopy' }, + repoRoot: vsRepo.rootUri.fsPath, + relPath: vscode.workspace.asRelativePath(target, false), + }); +}; + +export const makeCompareFileWithCommit = (deps: CommandDeps) => + async (uri?: vscode.Uri): Promise<void> => handler(deps, uri); diff --git a/src/commands/compareTwoCommits.ts b/src/commands/compareTwoCommits.ts new file mode 100644 index 0000000..b00d2e8 --- /dev/null +++ b/src/commands/compareTwoCommits.ts @@ -0,0 +1,36 @@ +import type { MementoStore } from '../state'; +import type { CommandDeps } from './shared'; +import { + drillIntoFiles, + pickRepoAndCommit, + pickSideBAndResolve, +} from './flow'; + +export const makeCompareTwoCommits = ( + deps: CommandDeps & { readonly state: MementoStore }, +) => + async (): Promise<void> => { + const start = await pickRepoAndCommit({ + runner: deps.runner, + gitApi: deps.gitApi, + output: deps.output, + }); + if (start === undefined) { + return; + } + const revB = await pickSideBAndResolve({ + repo: start.repo, + output: deps.output, + }); + if (!revB.ok) { + return; + } + await drillIntoFiles({ + repo: start.repo, + repoRoot: start.vsRepoRoot, + revA: start.revA, + revB: revB.value, + state: deps.state, + output: deps.output, + }); + }; diff --git a/src/commands/compareWith.ts b/src/commands/compareWith.ts new file mode 100644 index 0000000..5d0332d --- /dev/null +++ b/src/commands/compareWith.ts @@ -0,0 +1,48 @@ +import * as vscode from 'vscode'; +import type { MementoStore } from '../state'; +import { extractHistoryItemSha } from './historyItem'; +import { + type CommandDeps, + buildRepo, + pickRepoFrom, +} from './shared'; +import { + drillIntoFiles, + pickSideBAndResolve, + sideAFromSha, +} from './flow'; + +const handler = async ( + deps: CommandDeps & { readonly state: MementoStore }, + arg: unknown, +): Promise<void> => { + const sha = extractHistoryItemSha(arg); + if (sha === undefined) { + void vscode.window.showWarningMessage( + 'Diffy: this command must be invoked from the SCM history view.', + ); + return; + } + const vs = await pickRepoFrom(deps.gitApi); + if (!vs.ok) { + return; + } + const repo = buildRepo(deps.runner, vs.value); + const revB = await pickSideBAndResolve({ repo, output: deps.output }); + if (!revB.ok) { + return; + } + await drillIntoFiles({ + repo, + repoRoot: vs.value.rootUri.fsPath, + revA: sideAFromSha(sha), + revB: revB.value, + state: deps.state, + output: deps.output, + }); +}; + +export const makeCompareWith = ( + deps: CommandDeps & { readonly state: MementoStore }, +) => + async (arg: unknown): Promise<void> => handler(deps, arg); diff --git a/src/commands/compareWithPrevious.ts b/src/commands/compareWithPrevious.ts new file mode 100644 index 0000000..f9bddc0 --- /dev/null +++ b/src/commands/compareWithPrevious.ts @@ -0,0 +1,49 @@ +import * as vscode from 'vscode'; +import type { MementoStore } from '../state'; +import { extractHistoryItemSha } from './historyItem'; +import { + type CommandDeps, + buildRepo, + pickRepoFrom, +} from './shared'; +import { + drillIntoFiles, + reportGitError, + sideAFromSha, +} from './flow'; + +const handler = async ( + deps: CommandDeps & { readonly state: MementoStore }, + arg: unknown, +): Promise<void> => { + const sha = extractHistoryItemSha(arg); + if (sha === undefined) { + void vscode.window.showWarningMessage( + 'Diffy: this command must be invoked from the SCM history view.', + ); + return; + } + const vs = await pickRepoFrom(deps.gitApi); + if (!vs.ok) { + return; + } + const repo = buildRepo(deps.runner, vs.value); + const parent = await repo.revParse(`${sha}^1`); + if (!parent.ok) { + reportGitError({ output: deps.output, op: 'rev-parse parent', e: parent.error }); + return; + } + await drillIntoFiles({ + repo, + repoRoot: vs.value.rootUri.fsPath, + revA: sideAFromSha(sha), + revB: { kind: 'commit', sha: parent.value }, + state: deps.state, + output: deps.output, + }); +}; + +export const makeCompareWithPrevious = ( + deps: CommandDeps & { readonly state: MementoStore }, +) => + async (arg: unknown): Promise<void> => handler(deps, arg); diff --git a/src/commands/compareWithWorkingCopy.ts b/src/commands/compareWithWorkingCopy.ts new file mode 100644 index 0000000..5c6959b --- /dev/null +++ b/src/commands/compareWithWorkingCopy.ts @@ -0,0 +1,40 @@ +import * as vscode from 'vscode'; +import type { MementoStore } from '../state'; +import { extractHistoryItemSha } from './historyItem'; +import { + type CommandDeps, + buildRepo, + pickRepoFrom, +} from './shared'; +import { drillIntoFiles, sideAFromSha } from './flow'; + +const handler = async ( + deps: CommandDeps & { readonly state: MementoStore }, + arg: unknown, +): Promise<void> => { + const sha = extractHistoryItemSha(arg); + if (sha === undefined) { + void vscode.window.showWarningMessage( + 'Diffy: this command must be invoked from the SCM history view.', + ); + return; + } + const vs = await pickRepoFrom(deps.gitApi); + if (!vs.ok) { + return; + } + const repo = buildRepo(deps.runner, vs.value); + await drillIntoFiles({ + repo, + repoRoot: vs.value.rootUri.fsPath, + revA: sideAFromSha(sha), + revB: { kind: 'workingCopy' }, + state: deps.state, + output: deps.output, + }); +}; + +export const makeCompareWithWorkingCopy = ( + deps: CommandDeps & { readonly state: MementoStore }, +) => + async (arg: unknown): Promise<void> => handler(deps, arg); diff --git a/src/commands/flow.ts b/src/commands/flow.ts new file mode 100644 index 0000000..400632b --- /dev/null +++ b/src/commands/flow.ts @@ -0,0 +1,202 @@ +import * as vscode from 'vscode'; +import type { GitRepo } from '../git/GitRepo'; +import type { GitRunner } from '../git/GitRunner'; +import type { GitApi } from '../vscodeGitApi'; +import type { GitError, CommitRev, RevSpec, Sha } from '../git/types'; +import { logger } from '../logger'; +import { type Result, err, ok } from '../result'; +import { CANCELLED, type Cancelled } from '../ui/cancelled'; +import { pickCommit } from '../ui/CommitPicker'; +import { pickRef } from '../ui/RefPicker'; +import { pickSideBChoice, type SideBChoice } from '../ui/SideBPicker'; +import { mergeChangedFilesWithStats, pickFiles } from '../ui/FilePicker'; +import type { MementoStore } from '../state'; +import { buildRepo, openDiff, pickRepoFrom } from './shared'; + +export const reportGitError = ({ + output, + op, + e, +}: { + output: vscode.OutputChannel; + op: string; + e: GitError; +}): void => { + logger.error({ op, kind: e.kind }, 'git.error'); + output.appendLine(`Diffy: ${op} failed — ${e.message}`); + if (e.stderr !== undefined && e.stderr !== '') { + output.appendLine(e.stderr); + } + void vscode.window.showErrorMessage(`Diffy: ${op} failed (see Output → Diffy).`); +}; + +export const resolveSideB = async ({ + choice, + repo, + output, +}: { + choice: SideBChoice; + repo: GitRepo; + output: vscode.OutputChannel; +}): Promise<Result<RevSpec, Cancelled>> => { + if (choice.kind === 'workingCopy') { + return ok({ kind: 'workingCopy' }); + } + if (choice.kind === 'index') { + return ok({ kind: 'index' }); + } + if (choice.kind === 'pickRef') { + return resolveRefAsRev({ repo, output }); + } + return resolveCommitAsRev({ repo, output }); +}; + +const resolveRefAsRev = async ({ + repo, + output, +}: { + repo: GitRepo; + output: vscode.OutputChannel; +}): Promise<Result<RevSpec, Cancelled>> => { + const refs = await repo.refs(); + if (!refs.ok) { + reportGitError({ output, op: 'list refs', e: refs.error }); + return err(CANCELLED); + } + const picked = await pickRef({ refs: refs.value }); + if (!picked.ok) { + return err(CANCELLED); + } + const sha = await repo.revParse(picked.value.name); + if (!sha.ok) { + reportGitError({ output, op: 'rev-parse', e: sha.error }); + return err(CANCELLED); + } + return ok({ kind: 'commit', sha: sha.value }); +}; + +const resolveCommitAsRev = async ({ + repo, + output, +}: { + repo: GitRepo; + output: vscode.OutputChannel; +}): Promise<Result<RevSpec, Cancelled>> => { + const log = await repo.log({}); + if (!log.ok) { + reportGitError({ output, op: 'log', e: log.error }); + return err(CANCELLED); + } + const picked = await pickCommit({ commits: log.value }); + if (!picked.ok) { + return err(CANCELLED); + } + return ok({ kind: 'commit', sha: picked.value.sha }); +}; + +export const pickSideBAndResolve = async ({ + repo, + output, +}: { + repo: GitRepo; + output: vscode.OutputChannel; +}): Promise<Result<RevSpec, Cancelled>> => { + const choice = await pickSideBChoice(); + if (!choice.ok) { + return err(CANCELLED); + } + return resolveSideB({ choice: choice.value, repo, output }); +}; + +export const drillIntoFiles = async ({ + repo, + repoRoot, + revA, + revB, + state, + output, +}: { + repo: GitRepo; + repoRoot: string; + revA: CommitRev; + revB: RevSpec; + state: MementoStore; + output: vscode.OutputChannel; +}): Promise<void> => { + const entries = await collectChangedFiles({ repo, revA, revB, output }); + if (entries === undefined) { + return; + } + if (entries.length === 0) { + void vscode.window.showInformationMessage('Diffy: no changes between selected sides.'); + return; + } + await state.setLastComparison({ revA, revB, repoRoot }); + await pickFiles({ + entries, + onPick: (entry) => + openDiff({ revA, revB, repoRoot, relPath: entry.file.path }), + }); +}; + +const collectChangedFiles = async ({ + repo, + revA, + revB, + output, +}: { + repo: GitRepo; + revA: CommitRev; + revB: RevSpec; + output: vscode.OutputChannel; +}) => { + const ns = await repo.nameStatus({ from: revA, to: revB }); + if (!ns.ok) { + reportGitError({ output, op: 'diff --name-status', e: ns.error }); + return undefined; + } + const num = await repo.numstat({ from: revA, to: revB }); + if (!num.ok) { + reportGitError({ output, op: 'diff --numstat', e: num.error }); + return undefined; + } + return mergeChangedFilesWithStats(ns.value, num.value); +}; + +export const sideAFromSha = (sha: Sha): CommitRev => ({ kind: 'commit', sha }); + +export interface StartingPoint { + readonly vsRepoRoot: string; + readonly repo: GitRepo; + readonly revA: CommitRev; +} + +export const pickRepoAndCommit = async ({ + runner, + gitApi, + output, +}: { + runner: GitRunner; + gitApi: GitApi; + output: vscode.OutputChannel; +}): Promise<StartingPoint | undefined> => { + const vs = await pickRepoFrom(gitApi); + if (!vs.ok) { + return undefined; + } + const repo = buildRepo(runner, vs.value); + const log = await repo.log({}); + if (!log.ok) { + reportGitError({ output, op: 'log', e: log.error }); + return undefined; + } + const commit = await pickCommit({ commits: log.value }); + if (!commit.ok) { + return undefined; + } + return { + vsRepoRoot: vs.value.rootUri.fsPath, + repo, + revA: sideAFromSha(commit.value.sha), + }; +}; diff --git a/src/commands/historyItem.ts b/src/commands/historyItem.ts new file mode 100644 index 0000000..f6601d7 --- /dev/null +++ b/src/commands/historyItem.ts @@ -0,0 +1,30 @@ +const isNonEmptyString = (v: unknown): v is string => + typeof v === 'string' && v !== ''; + +const stringIdOf = (obj: object): string | undefined => { + if (!('id' in obj)) { + return undefined; + } + return isNonEmptyString(obj.id) ? obj.id : undefined; +}; + +export const extractHistoryItemSha = (arg: unknown): string | undefined => { + if (isNonEmptyString(arg)) { + return arg; + } + if (typeof arg !== 'object' || arg === null) { + return undefined; + } + const direct = stringIdOf(arg); + if (direct !== undefined) { + return direct; + } + if (!('historyItem' in arg)) { + return undefined; + } + const inner = arg.historyItem; + if (typeof inner !== 'object' || inner === null) { + return undefined; + } + return stringIdOf(inner); +}; diff --git a/src/commands/reopenLast.ts b/src/commands/reopenLast.ts new file mode 100644 index 0000000..3a38425 --- /dev/null +++ b/src/commands/reopenLast.ts @@ -0,0 +1,38 @@ +import * as vscode from 'vscode'; +import { findRepoForUri } from '../vscodeGitApi'; +import type { MementoStore } from '../state'; +import { type CommandDeps, buildRepo } from './shared'; +import { drillIntoFiles } from './flow'; + +const handler = async ( + deps: CommandDeps & { readonly state: MementoStore }, +): Promise<void> => { + const last = deps.state.getLastComparison(); + if (last === undefined) { + void vscode.window.showInformationMessage( + 'Diffy: no previous comparison to reopen.', + ); + return; + } + const vsRepo = findRepoForUri(deps.gitApi, vscode.Uri.file(last.repoRoot)); + if (vsRepo === undefined) { + void vscode.window.showWarningMessage( + 'Diffy: previous repository is no longer open.', + ); + return; + } + const repo = buildRepo(deps.runner, vsRepo); + await drillIntoFiles({ + repo, + repoRoot: vsRepo.rootUri.fsPath, + revA: last.revA, + revB: last.revB, + state: deps.state, + output: deps.output, + }); +}; + +export const makeReopenLast = ( + deps: CommandDeps & { readonly state: MementoStore }, +) => + async (): Promise<void> => handler(deps); diff --git a/src/commands/shared.ts b/src/commands/shared.ts new file mode 100644 index 0000000..8aedcd2 --- /dev/null +++ b/src/commands/shared.ts @@ -0,0 +1,106 @@ +import * as path from 'node:path'; +import * as vscode from 'vscode'; +import { BUILT_IN_COMMANDS, SHORT_SHA_LEN } from '../constants'; +import type { GitRepo } from '../git/GitRepo'; +import type { GitRunner } from '../git/GitRunner'; +import { createGitRepo } from '../git/GitRepo'; +import { + type GitApi, + type GitVsRepository, + findRepoForUri, +} from '../vscodeGitApi'; +import type { CommitRev, DiffyAddressableRev, RevSpec, Sha } from '../git/types'; +import { logger } from '../logger'; +import { type Result, err, ok } from '../result'; +import { CANCELLED, type Cancelled } from '../ui/cancelled'; +import { buildDiffyUri } from '../ui/uri'; + +export interface CommandDeps { + readonly runner: GitRunner; + readonly gitApi: GitApi; + readonly output: vscode.OutputChannel; +} + +export const shortSha = (sha: Sha): string => sha.slice(0, SHORT_SHA_LEN); + +const labelForRev = (rev: RevSpec): string => { + if (rev.kind === 'commit') { + return shortSha(rev.sha); + } + if (rev.kind === 'workingCopy') { + return 'Working Copy'; + } + return 'Index'; +}; + +export const formatDiffTitle = ({ + revA, + revB, + basename, +}: { + revA: CommitRev; + revB: RevSpec; + basename: string; +}): string => `${labelForRev(revA)} ↔ ${labelForRev(revB)} — ${basename}`; + +export const uriForRev = ({ + rev, + repoRoot, + relPath, +}: { + rev: RevSpec; + repoRoot: string; + relPath: string; +}): vscode.Uri => { + if (rev.kind === 'workingCopy') { + return vscode.Uri.file(path.join(repoRoot, relPath)); + } + const addressable: DiffyAddressableRev = + rev.kind === 'commit' ? { kind: 'commit', sha: rev.sha } : { kind: 'index' }; + return vscode.Uri.parse(buildDiffyUri(addressable, relPath)); +}; + +export const openDiff = async ({ + revA, + revB, + repoRoot, + relPath, +}: { + revA: CommitRev; + revB: RevSpec; + repoRoot: string; + relPath: string; +}): Promise<void> => { + const left = uriForRev({ rev: revA, repoRoot, relPath }); + const right = uriForRev({ rev: revB, repoRoot, relPath }); + const title = formatDiffTitle({ revA, revB, basename: path.basename(relPath) }); + logger.info({ shaA: shortSha(revA.sha), revBKind: revB.kind }, 'diff.open'); + await vscode.commands.executeCommand(BUILT_IN_COMMANDS.diff, left, right, title); +}; + +export const repoForUri = ( + api: GitApi, + uri: vscode.Uri, +): GitVsRepository | undefined => findRepoForUri(api, uri); + +export const pickRepoFrom = async ( + api: GitApi, +): Promise<Result<GitVsRepository, Cancelled>> => { + if (api.repositories.length === 0) { + return err(CANCELLED); + } + const first = api.repositories[0]; + if (api.repositories.length === 1 && first !== undefined) { + return ok(first); + } + const items = api.repositories.map((r) => ({ label: r.rootUri.fsPath, repo: r })); + const picked = await vscode.window.showQuickPick(items, { + placeHolder: 'Pick a git repository', + }); + return picked === undefined ? err(CANCELLED) : ok(picked.repo); +}; + +export const buildRepo = ( + runner: GitRunner, + vsRepo: GitVsRepository, +): GitRepo => createGitRepo({ runner, cwd: vsRepo.rootUri.fsPath }); diff --git a/src/extension.ts b/src/extension.ts new file mode 100644 index 0000000..4efa7fa --- /dev/null +++ b/src/extension.ts @@ -0,0 +1,121 @@ +import * as vscode from 'vscode'; +import { + COMMAND_IDS, + CONTEXT_KEYS, + OUTPUT_CHANNEL_NAME, +} from './constants'; +import { createGitRunner } from './git/GitRunner'; +import { type GitApi, getGitApi } from './vscodeGitApi'; +import { addLogStream, logger } from './logger'; +import { makeCompareFileWithCommit } from './commands/compareFileWithCommit'; +import { makeCompareTwoCommits } from './commands/compareTwoCommits'; +import { makeCompareWith } from './commands/compareWith'; +import { makeCompareWithPrevious } from './commands/compareWithPrevious'; +import { makeCompareWithWorkingCopy } from './commands/compareWithWorkingCopy'; +import { makeReopenLast } from './commands/reopenLast'; +import { registerDiffyContentProvider } from './providers/DiffyContentProvider'; +import type { CommandDeps } from './commands/shared'; +import { buildRepo } from './commands/shared'; +import { createMementoStore } from './state'; +import type { GitRunner } from './git/GitRunner'; + +const channelStream = ( + channel: vscode.OutputChannel, +): NodeJS.WritableStream => { + const stream = { + write(chunk: string | Uint8Array): boolean { + const text = + typeof chunk === 'string' ? chunk : Buffer.from(chunk).toString('utf8'); + channel.appendLine(text.replace(/\n$/, '')); + return true; + }, + }; + return stream as NodeJS.WritableStream; +}; + +const probeGit = async (runner: GitRunner): Promise<boolean> => { + const r = await runner.run({ args: ['--version'], cwd: process.cwd() }); + return r.ok; +}; + +const setGitAvailable = async (available: boolean): Promise<void> => { + await vscode.commands.executeCommand( + 'setContext', + CONTEXT_KEYS.gitAvailable, + available, + ); +}; + +const makeRepoResolver = ( + api: GitApi, + runner: GitRunner, +) => () => { + const first = api.repositories[0]; + if (first === undefined) { + return undefined; + } + return buildRepo(runner, first); +}; + +const registerAll = ( + context: vscode.ExtensionContext, + deps: CommandDeps & { readonly state: ReturnType<typeof createMementoStore> }, +): void => { + context.subscriptions.push( + vscode.commands.registerCommand( + COMMAND_IDS.compareWith, + makeCompareWith(deps), + ), + vscode.commands.registerCommand( + COMMAND_IDS.compareWithWorkingCopy, + makeCompareWithWorkingCopy(deps), + ), + vscode.commands.registerCommand( + COMMAND_IDS.compareWithPrevious, + makeCompareWithPrevious(deps), + ), + vscode.commands.registerCommand( + COMMAND_IDS.compareTwoCommits, + makeCompareTwoCommits(deps), + ), + vscode.commands.registerCommand( + COMMAND_IDS.compareFileWithCommit, + makeCompareFileWithCommit(deps), + ), + vscode.commands.registerCommand(COMMAND_IDS.reopenLast, makeReopenLast(deps)), + vscode.commands.registerCommand(COMMAND_IDS.showLogs, () => { + deps.output.show(true); + }), + ); +}; + +export const activate = async ( + context: vscode.ExtensionContext, +): Promise<void> => { + const output = vscode.window.createOutputChannel(OUTPUT_CHANNEL_NAME); + context.subscriptions.push(output); + addLogStream({ stream: channelStream(output), level: 'info' }); + + const runner = createGitRunner({ logger }); + const gitOk = await probeGit(runner); + await setGitAvailable(gitOk); + if (!gitOk) { + output.appendLine('Diffy: git binary not found on PATH — commands disabled.'); + } + + const api = await getGitApi(); + if (api === undefined) { + output.appendLine('Diffy: built-in git extension API unavailable.'); + return; + } + + const state = createMementoStore(context.globalState); + const deps = { runner, gitApi: api, output, state } as const; + registerDiffyContentProvider(context, makeRepoResolver(api, runner)); + registerAll(context, deps); + logger.info({ repos: api.repositories.length }, 'extension.activated'); +}; + +export const deactivate = (): void => { + logger.info({}, 'extension.deactivated'); +}; diff --git a/src/git/GitRepo.ts b/src/git/GitRepo.ts index cf6ca9c..d992ef9 100644 --- a/src/git/GitRepo.ts +++ b/src/git/GitRepo.ts @@ -43,13 +43,13 @@ export interface GitRepo { revParse(name: string): Promise<Result<Sha, GitError>>; } -const buildLogArgs = ({ limit, ref }: { limit: number; ref?: string }): readonly string[] => { - const base = ['log', `--max-count=${limit}`, `--format=${GIT_LOG_FORMAT}`, '-z']; - return ref === undefined ? base : [...base, ref]; +const buildLogArgs = (params: { limit: number; ref?: string }): readonly string[] => { + const base = ['log', `--max-count=${params.limit.toString()}`, `--format=${GIT_LOG_FORMAT}`, '-z']; + return params.ref === undefined ? base : [...base, params.ref]; }; const buildDiffArgs = ({ from, to }: DiffSides, subcommand: 'name-status' | 'numstat'): readonly string[] => { - const head = ['diff', `--${subcommand}`, '-z']; + const head = ['diff', `--${subcommand}`, '-z', '--find-renames', '--find-copies']; if (to.kind === 'commit') return [...head, from.sha, to.sha]; if (to.kind === 'workingCopy') return [...head, from.sha]; return [...head, from.sha, '--cached']; @@ -73,7 +73,10 @@ export const createGitRepo = ({ }): GitRepo => ({ log: async (args = {}) => { const limit = args.limit ?? DEFAULT_LOG_LIMIT; - const r = await runner.run({ args: buildLogArgs({ limit, ref: args.ref }), cwd }); + const logArgs = args.ref === undefined + ? buildLogArgs({ limit }) + : buildLogArgs({ limit, ref: args.ref }); + const r = await runner.run({ args: logArgs, cwd }); return andThen(r, parseLog); }, nameStatus: async (args) => { @@ -87,7 +90,7 @@ export const createGitRepo = ({ show: (args) => runner.run({ args: ['show', showSpec(args)], cwd }), refs: async () => { const r = await runner.run({ - args: ['for-each-ref', '-z', `--format=${REFS_FORMAT}`], + args: ['for-each-ref', `--format=${REFS_FORMAT}`], cwd, }); return andThen(r, parseRefs); diff --git a/src/git/parsers.ts b/src/git/parsers.ts index f4f7cb5..54dc8e0 100644 --- a/src/git/parsers.ts +++ b/src/git/parsers.ts @@ -1,4 +1,4 @@ -import { NUL, TAB, REF_PREFIX_HEADS, REF_PREFIX_TAGS } from '../constants'; +import { NUL, TAB, LF, REF_PREFIX_HEADS, REF_PREFIX_TAGS } from '../constants'; import { type Result, ok, err } from '../result'; import type { ChangedFile, @@ -228,13 +228,14 @@ const parseRefRecord = (fields: readonly string[]): Result<Ref, GitError> => { export const parseRefs = (stdout: string): Result<readonly Ref[], GitError> => { if (stdout.length === 0) return ok([]); - const tokens = stripTrailingEmpty(stdout.split(NUL)); - if (tokens.length % REF_FIELDS_PER_RECORD !== 0) { - return errParse('parseRefs: field count not multiple of 3'); - } + const lines = stdout.split(LF).filter((l) => l.length > 0); const refs: Ref[] = []; - for (let i = 0; i < tokens.length; i += REF_FIELDS_PER_RECORD) { - const r = parseRefRecord(tokens.slice(i, i + REF_FIELDS_PER_RECORD)); + for (const line of lines) { + const fields = line.split(NUL); + if (fields.length !== REF_FIELDS_PER_RECORD) { + return errParse('parseRefs: expected 3 NUL-separated fields per line'); + } + const r = parseRefRecord(fields); if (!r.ok) return r; refs.push(r.value); } diff --git a/src/git/repoMatch.ts b/src/git/repoMatch.ts new file mode 100644 index 0000000..15c6cca --- /dev/null +++ b/src/git/repoMatch.ts @@ -0,0 +1,39 @@ +interface UriLike { + readonly fsPath: string; +} + +interface RepoLike<U extends UriLike> { + readonly rootUri: U; +} + +interface ApiLike<U extends UriLike, R extends RepoLike<U>> { + readonly repositories: readonly R[]; + getRepository?(uri: U): R | null; +} + +export const matchRepoByFsPath = <R extends RepoLike<UriLike>>( + repositories: readonly R[], + targetFsPath: string, +): R | undefined => { + let best: R | undefined; + for (const r of repositories) { + const root = r.rootUri.fsPath; + if (!targetFsPath.startsWith(root)) { + continue; + } + if (best === undefined || root.length > best.rootUri.fsPath.length) { + best = r; + } + } + return best; +}; + +export const findRepoForUri = <U extends UriLike, R extends RepoLike<U>>( + api: ApiLike<U, R>, + uri: U, +): R | undefined => { + if (api.getRepository !== undefined) { + return api.getRepository(uri) ?? undefined; + } + return matchRepoByFsPath(api.repositories, uri.fsPath); +}; diff --git a/src/logger.ts b/src/logger.ts index 9a9b25c..970afcb 100644 --- a/src/logger.ts +++ b/src/logger.ts @@ -1,15 +1,53 @@ -import pino from 'pino'; +import pino, { type Logger as PinoLogger, type Level, multistream } from 'pino'; export interface Logger { - trace(obj: object, msg?: string): void; - debug(obj: object, msg?: string): void; - info(obj: object, msg?: string): void; - warn(obj: object, msg?: string): void; - error(obj: object, msg?: string): void; + trace(fields: object, msg?: string): void; + debug(fields: object, msg?: string): void; + info(fields: object, msg?: string): void; + warn(fields: object, msg?: string): void; + error(fields: object, msg?: string): void; } -export const logger: Logger = pino({ - level: process.env['DIFFY_LOG_LEVEL'] ?? 'info', - base: undefined, - timestamp: pino.stdTimeFunctions.isoTime, -}); +export interface LogStreamEntry { + readonly stream: NodeJS.WritableStream; + readonly level?: Level; +} + +const streams: LogStreamEntry[] = [{ stream: process.stdout }]; + +const envLevel = process.env['DIFFY_LOG_LEVEL']; + +const buildPino = (): PinoLogger => + pino( + { + level: envLevel ?? 'info', + base: null, + timestamp: pino.stdTimeFunctions.isoTime, + }, + multistream(streams.map((s) => ({ stream: s.stream, level: s.level ?? 'trace' }))), + ); + +let underlying: PinoLogger = buildPino(); + +export const addLogStream = (entry: LogStreamEntry): void => { + streams.push(entry); + underlying = buildPino(); +}; + +export const logger: Logger = { + trace: (fields, msg) => { + underlying.trace(fields, msg); + }, + debug: (fields, msg) => { + underlying.debug(fields, msg); + }, + info: (fields, msg) => { + underlying.info(fields, msg); + }, + warn: (fields, msg) => { + underlying.warn(fields, msg); + }, + error: (fields, msg) => { + underlying.error(fields, msg); + }, +}; diff --git a/src/providers/DiffyContentProvider.ts b/src/providers/DiffyContentProvider.ts new file mode 100644 index 0000000..45a55c1 --- /dev/null +++ b/src/providers/DiffyContentProvider.ts @@ -0,0 +1,48 @@ +import * as vscode from 'vscode'; +import { SCHEME } from '../constants'; +import type { GitRepo } from '../git/GitRepo'; +import { logger } from '../logger'; +import { parseDiffyUri } from '../ui/uri'; + +export class DiffyContentProvider implements vscode.TextDocumentContentProvider { + private readonly emitter = new vscode.EventEmitter<vscode.Uri>(); + + readonly onDidChange: vscode.Event<vscode.Uri> = this.emitter.event; + + constructor(private readonly resolveRepo: (uri: vscode.Uri) => GitRepo | undefined) {} + + async provideTextDocumentContent(uri: vscode.Uri): Promise<string> { + const parsed = parseDiffyUri(uri.toString()); + if (!parsed.ok) { + logger.warn({ kind: parsed.error.kind }, 'provider.parseFailed'); + return ''; + } + const repo = this.resolveRepo(uri); + if (repo === undefined) { + logger.warn({}, 'provider.repoUnresolved'); + return ''; + } + const r = await repo.show({ rev: parsed.value.rev, path: parsed.value.path }); + if (!r.ok) { + logger.debug({ kind: r.error.kind }, 'provider.showFailed'); + return ''; + } + return r.value; + } + + dispose(): void { + this.emitter.dispose(); + } +} + +export const registerDiffyContentProvider = ( + context: vscode.ExtensionContext, + resolver: (uri: vscode.Uri) => GitRepo | undefined, +): DiffyContentProvider => { + const provider = new DiffyContentProvider(resolver); + context.subscriptions.push( + vscode.workspace.registerTextDocumentContentProvider(SCHEME, provider), + provider, + ); + return provider; +}; diff --git a/src/state.ts b/src/state.ts new file mode 100644 index 0000000..13d866f --- /dev/null +++ b/src/state.ts @@ -0,0 +1,44 @@ +import type * as vscode from 'vscode'; +import { MEMENTO_KEYS } from './constants'; +import type { RevSpec, Sha } from './git/types'; + +export interface LastComparison { + readonly revA: { readonly kind: 'commit'; readonly sha: Sha }; + readonly revB: RevSpec; + readonly repoRoot: string; +} + +export interface MementoStore { + getLastComparison(): LastComparison | undefined; + setLastComparison(value: LastComparison): Promise<void>; + clearLastComparison(): Promise<void>; +} + +export const isLastComparison = (raw: unknown): raw is LastComparison => { + if (typeof raw !== 'object' || raw === null) { + return false; + } + if (!('repoRoot' in raw) || typeof raw.repoRoot !== 'string') { + return false; + } + if (!('revA' in raw) || typeof raw.revA !== 'object' || raw.revA === null) { + return false; + } + if (!('revB' in raw) || typeof raw.revB !== 'object' || raw.revB === null) { + return false; + } + return true; +}; + +export const createMementoStore = (memento: vscode.Memento): MementoStore => ({ + getLastComparison: () => { + const raw = memento.get<unknown>(MEMENTO_KEYS.lastComparison); + return isLastComparison(raw) ? raw : undefined; + }, + setLastComparison: async (value) => { + await memento.update(MEMENTO_KEYS.lastComparison, value); + }, + clearLastComparison: async () => { + await memento.update(MEMENTO_KEYS.lastComparison, undefined); + }, +}); diff --git a/src/test/runTests.ts b/src/test/runTests.ts new file mode 100644 index 0000000..bb05dc5 --- /dev/null +++ b/src/test/runTests.ts @@ -0,0 +1,49 @@ +import * as path from 'node:path'; +import { spawnSync } from 'node:child_process'; +import { existsSync } from 'node:fs'; +import { runTests } from '@vscode/test-electron'; + +const repoRoot = path.resolve(__dirname, '..', '..'); +const extensionDevelopmentPath = repoRoot; +const extensionTestsPath = path.resolve(__dirname, 'suite', 'index'); +const workspacePath = path.resolve(repoRoot, 'test-fixtures', 'repo-seed', 'workspace'); +const seedScript = path.resolve(repoRoot, 'test-fixtures', 'repo-seed', 'seed.sh'); + +const ensureSeedRepo = (): void => { + if (existsSync(path.join(workspacePath, '.git'))) { + return; + } + const r = spawnSync('bash', [seedScript], { stdio: 'inherit' }); + if (r.status !== 0) { + throw new Error(`seed.sh failed with exit ${r.status ?? '?'}`); + } +}; + +const main = async (): Promise<void> => { + ensureSeedRepo(); + // Claude Code's host extension sets ELECTRON_RUN_AS_NODE=1 in our shell. + // That env var, if inherited by the spawned Electron in @vscode/test-electron, + // makes Electron behave as Node and refuse to launch VS Code. Drop it. + if ('ELECTRON_RUN_AS_NODE' in process.env) { + delete process.env['ELECTRON_RUN_AS_NODE']; + } + const coverageDir = process.env['NODE_V8_COVERAGE']; + const extensionTestsEnv: Record<string, string> = { + DIFFY_E2E: '1', + }; + if (coverageDir !== undefined && coverageDir !== '') { + extensionTestsEnv['NODE_V8_COVERAGE'] = coverageDir; + } + await runTests({ + extensionDevelopmentPath, + extensionTestsPath, + extensionTestsEnv, + launchArgs: [workspacePath, '--disable-telemetry'], + }); +}; + +main().catch((e: unknown) => { + const message = e instanceof Error ? e.message : String(e); + process.stderr.write(`E2E tests failed: ${message}\n`); + process.exit(1); +}); diff --git a/src/test/suite/activation.test.ts b/src/test/suite/activation.test.ts new file mode 100644 index 0000000..a41be76 --- /dev/null +++ b/src/test/suite/activation.test.ts @@ -0,0 +1,59 @@ +import { strict as assert } from 'node:assert'; +import * as vscode from 'vscode'; +import { COMMAND_IDS, OUTPUT_CHANNEL_NAME } from '../../constants'; +import { tick } from './helpers'; + +const EXTENSION_ID = 'nimblesite.diffy'; + +const ALL_COMMAND_IDS: readonly string[] = [ + COMMAND_IDS.compareWith, + COMMAND_IDS.compareWithWorkingCopy, + COMMAND_IDS.compareWithPrevious, + COMMAND_IDS.compareTwoCommits, + COMMAND_IDS.compareFileWithCommit, + COMMAND_IDS.reopenLast, + COMMAND_IDS.showLogs, +]; + +describe('activation', () => { + it('extension is present, activates, and every command id is registered', async () => { + const ext = vscode.extensions.getExtension(EXTENSION_ID); + assert.ok(ext, `extension ${EXTENSION_ID} should be present`); + if (ext === undefined) { + return; + } + if (!ext.isActive) { + await ext.activate(); + } + assert.equal(ext.isActive, true, 'extension should be active after activate()'); + await tick(20); + const registered = await vscode.commands.getCommands(true); + for (const id of ALL_COMMAND_IDS) { + assert.ok( + registered.includes(id), + `command ${id} should be registered (was: ${registered.filter((c) => c.startsWith('diffy.')).join(', ')})`, + ); + } + }); + + it('exposes a workspace folder containing the seeded git repo', () => { + const folders = vscode.workspace.workspaceFolders; + assert.ok(folders, 'workspace folders should be set'); + if (folders === undefined) { + return; + } + assert.equal(folders.length, 1); + const folder = folders[0]; + assert.ok(folder); + assert.match(folder?.uri.fsPath ?? '', /repo-seed\/workspace$/); + }); + + it('shows logs command opens the Diffy OutputChannel without errors', async () => { + await vscode.commands.executeCommand(COMMAND_IDS.showLogs); + // We can't assert that the channel is "visible", but the command must + // have a registered handler and complete without throwing. + assert.ok(true, 'showLogs invoked'); + assert.equal(typeof OUTPUT_CHANNEL_NAME, 'string'); + assert.ok(OUTPUT_CHANNEL_NAME.length > 0); + }); +}); diff --git a/src/test/suite/commands.test.ts b/src/test/suite/commands.test.ts new file mode 100644 index 0000000..02ed665 --- /dev/null +++ b/src/test/suite/commands.test.ts @@ -0,0 +1,379 @@ +import { strict as assert } from 'node:assert'; +import * as vscode from 'vscode'; +import { COMMAND_IDS } from '../../constants'; +import { + accept, + allDiffTabs, + closeAllEditors, + dismissQuickPick, + moveNext, + openFileInEditor, + readSeedShas, + tabInputUris, + tick, + waitForDiffTab, + waitForRepoReady, + workspaceRoot, +} from './helpers'; + +const EXTENSION_ID = 'nimblesite.diffy'; + +const ensureActivated = async (): Promise<void> => { + const ext = vscode.extensions.getExtension(EXTENSION_ID); + if (ext !== undefined && !ext.isActive) { + await ext.activate(); + } + await waitForRepoReady(); + await tick(20); +}; + +const labelStrings = (tab: vscode.Tab): string => tab.label; + +const moveAndAccept = async (steps: number): Promise<void> => { + for (let i = 0; i < steps; i++) { + await moveNext(); + } + await accept(); +}; + +describe('Diffy commands — end-to-end through real QuickPick UI', () => { + before(async () => { + await ensureActivated(); + }); + + beforeEach(async () => { + await closeAllEditors(); + await dismissQuickPick(); + await tick(5); + }); + + afterEach(async () => { + await dismissQuickPick(); + await closeAllEditors(); + await tick(5); + }); + + it('reopenLast with no prior comparison shows an info toast and opens no diff', async () => { + // This test must run BEFORE any compareXxx test that sets the memento. + const before = allDiffTabs().length; + await vscode.commands.executeCommand(COMMAND_IDS.reopenLast); + await tick(40); + const after = allDiffTabs().length; + assert.equal(after, before, 'no diff should open when memento is empty'); + }); + + it('compareFileWithCommit with no uri and no active editor → warning, no diff', async () => { + await closeAllEditors(); + await tick(20); + const before = allDiffTabs().length; + await vscode.commands.executeCommand(COMMAND_IDS.compareFileWithCommit); + await tick(40); + assert.equal(allDiffTabs().length, before); + }); + + it('compareFileWithCommit with a file outside any repo → warning, no diff', async () => { + const outside = vscode.Uri.file('/tmp/diffy-not-in-any-repo.txt'); + const before = allDiffTabs().length; + await vscode.commands.executeCommand(COMMAND_IDS.compareFileWithCommit, outside); + await tick(40); + assert.equal(allDiffTabs().length, before); + }); + + it('compareWithWorkingCopy without a historyItem → warning, no diff', async () => { + const before = allDiffTabs().length; + await vscode.commands.executeCommand(COMMAND_IDS.compareWithWorkingCopy); + await tick(40); + assert.equal(allDiffTabs().length, before); + }); + + it('compareWithPrevious without a historyItem → warning, no diff', async () => { + const before = allDiffTabs().length; + await vscode.commands.executeCommand(COMMAND_IDS.compareWithPrevious); + await tick(40); + assert.equal(allDiffTabs().length, before); + }); + + it('compareWithPrevious({id:initial-commit}) → revParse fails → reports error, no diff', async () => { + const shas = readSeedShas(); + const before = allDiffTabs().length; + // commit 1 has no parent, so revParse(`${commit1}^1`) fails. + await vscode.commands.executeCommand(COMMAND_IDS.compareWithPrevious, { + id: shas.first, + }); + await tick(80); + assert.equal(allDiffTabs().length, before); + }); + + it('compareTwoCommits: top commit (sha3) vs Working Copy → diff tab opens for a.txt', async () => { + const shas = readSeedShas(); + const flow = vscode.commands.executeCommand(COMMAND_IDS.compareTwoCommits); + + // CommitPicker: top item = most recent (commit 3) + await accept(); + // SideBPicker: top item = Working Copy + await accept(); + // FilePicker: top file — accept + await accept(); + + const diffTab = await waitForDiffTab(); + const uris = tabInputUris(diffTab); + assert.equal(uris.left.scheme, 'diffy'); + assert.equal(uris.right.scheme, 'file'); + assert.match(uris.left.toString(), new RegExp(`diffy://commit/${shas.third}/`)); + assert.match(uris.right.fsPath, /a\.txt$/); + assert.match(labelStrings(diffTab), /↔ Working Copy — a\.txt$/); + assert.match(labelStrings(diffTab), new RegExp(`^${shas.third.slice(0, 7)}`)); + + await dismissQuickPick(); + await flow; + }); + + it('compareTwoCommits: pick oldest commit (sha1) vs Working Copy → diff for top changed file', async () => { + const shas = readSeedShas(); + const flow = vscode.commands.executeCommand(COMMAND_IDS.compareTwoCommits); + + // CommitPicker: navigate down 2 (commit 3 → 2 → 1) + await moveAndAccept(2); + // SideBPicker: top = Working Copy + await accept(); + // FilePicker: top + await accept(); + + const diffTab = await waitForDiffTab(); + const uris = tabInputUris(diffTab); + assert.equal(uris.left.scheme, 'diffy'); + assert.match(uris.left.toString(), new RegExp(`diffy://commit/${shas.first}/`)); + assert.match(labelStrings(diffTab), new RegExp(`^${shas.first.slice(0, 7)} ↔ Working Copy — `)); + + await dismissQuickPick(); + await flow; + }); + + it('compareWith(historyItem={id:sha1}) → SideB=Working Copy → diff opens', async () => { + const shas = readSeedShas(); + const flow = vscode.commands.executeCommand(COMMAND_IDS.compareWith, { + id: shas.first, + }); + + // SideBPicker: top = Working Copy + await accept(); + // FilePicker: top + await accept(); + + const diffTab = await waitForDiffTab(); + const uris = tabInputUris(diffTab); + assert.equal(uris.left.scheme, 'diffy'); + assert.match(uris.left.toString(), new RegExp(`diffy://commit/${shas.first}/`)); + assert.equal(uris.right.scheme, 'file'); + assert.match(labelStrings(diffTab), new RegExp(`^${shas.first.slice(0, 7)} ↔ Working Copy — `)); + + await dismissQuickPick(); + await flow; + }); + + it('compareWith(historyItem) → SideB=Index → diff has diffy://index/ on the right', async () => { + const shas = readSeedShas(); + const flow = vscode.commands.executeCommand(COMMAND_IDS.compareWith, { + historyItem: { id: shas.first }, + }); + + // SideBPicker: Working Copy, Index, Pick a commit…, Pick a branch or tag… + // Index is item 2 → moveNext × 1, accept + await moveAndAccept(1); + // FilePicker + await accept(); + + const diffTab = await waitForDiffTab(); + const uris = tabInputUris(diffTab); + assert.equal(uris.left.scheme, 'diffy'); + assert.equal(uris.right.scheme, 'diffy'); + assert.match(uris.left.toString(), new RegExp(`diffy://commit/${shas.first}/`)); + assert.match(uris.right.toString(), /^diffy:\/\/index\//); + assert.match(labelStrings(diffTab), new RegExp(`^${shas.first.slice(0, 7)} ↔ Index — `)); + + await dismissQuickPick(); + await flow; + }); + + it('compareWith → SideB=pickRef → user picks the v0.1.0 tag → diff against that ref', async () => { + const shas = readSeedShas(); + const flow = vscode.commands.executeCommand(COMMAND_IDS.compareWith, { + id: shas.third, + }); + + // SideBPicker: Working Copy, Index, Pick a commit…, Pick a branch or tag… + // pickRef is item 4 → moveNext × 3 + await moveAndAccept(3); + + // RefPicker: alphabetical — refs/heads/main first, refs/tags/v0.1.0 second + // Pick the v0.1.0 tag (item 2) → moveNext × 1 + await moveAndAccept(1); + + // FilePicker + await accept(); + const diffTab = await waitForDiffTab(); + const uris = tabInputUris(diffTab); + assert.equal(uris.left.scheme, 'diffy'); + assert.equal(uris.right.scheme, 'diffy'); + assert.match(uris.left.toString(), new RegExp(`diffy://commit/${shas.third}/`)); + assert.match(uris.right.toString(), new RegExp(`diffy://commit/${shas.second}/`)); + assert.match( + labelStrings(diffTab), + new RegExp(`^${shas.third.slice(0, 7)} ↔ ${shas.second.slice(0, 7)} — `), + ); + + await dismissQuickPick(); + await flow; + }); + + it('compareWith → SideB=pickCommit → user picks commit 1 → diff against commit 1', async () => { + const shas = readSeedShas(); + const flow = vscode.commands.executeCommand(COMMAND_IDS.compareWith, { + id: shas.third, + }); + + // SideBPicker: pickCommit is item 3 → moveNext × 2, accept + await moveAndAccept(2); + + // Inner CommitPicker: order = commit3, commit2, commit1 (newest first) + // Pick commit1 → moveNext × 2, accept + await moveAndAccept(2); + + // FilePicker + await accept(); + const diffTab = await waitForDiffTab(); + const uris = tabInputUris(diffTab); + assert.match(uris.left.toString(), new RegExp(`diffy://commit/${shas.third}/`)); + assert.match(uris.right.toString(), new RegExp(`diffy://commit/${shas.first}/`)); + + await dismissQuickPick(); + await flow; + }); + + it('compareWithWorkingCopy(historyItem={id:sha2}) → no SideB picker, straight to FilePicker', async () => { + const shas = readSeedShas(); + const flow = vscode.commands.executeCommand(COMMAND_IDS.compareWithWorkingCopy, { + id: shas.second, + }); + + // No SideBPicker; FilePicker opens directly + await accept(); + + const diffTab = await waitForDiffTab(); + const uris = tabInputUris(diffTab); + assert.equal(uris.left.scheme, 'diffy'); + assert.equal(uris.right.scheme, 'file'); + assert.match(uris.left.toString(), new RegExp(`diffy://commit/${shas.second}/`)); + assert.match(labelStrings(diffTab), new RegExp(`^${shas.second.slice(0, 7)} ↔ Working Copy — `)); + + await dismissQuickPick(); + await flow; + }); + + it('compareWithPrevious(historyItem={id:sha3}) → diffs against parent (commit 2)', async () => { + const shas = readSeedShas(); + const flow = vscode.commands.executeCommand(COMMAND_IDS.compareWithPrevious, { + id: shas.third, + }); + + // FilePicker + await accept(); + const diffTab = await waitForDiffTab(); + const uris = tabInputUris(diffTab); + assert.equal(uris.left.scheme, 'diffy'); + assert.equal(uris.right.scheme, 'diffy'); + assert.match(uris.left.toString(), new RegExp(`diffy://commit/${shas.third}/`)); + assert.match(uris.right.toString(), new RegExp(`diffy://commit/${shas.second}/`)); + assert.match( + labelStrings(diffTab), + new RegExp(`^${shas.third.slice(0, 7)} ↔ ${shas.second.slice(0, 7)} — `), + ); + + await dismissQuickPick(); + await flow; + }); + + it('compareFileWithCommit (with explicit uri arg) → CommitPicker top → diff opens for commit 3', async () => { + const aTxt = vscode.Uri.file(`${workspaceRoot()}/a.txt`); + const shas = readSeedShas(); + const flow = vscode.commands.executeCommand(COMMAND_IDS.compareFileWithCommit, aTxt); + + // CommitPicker top = commit 3 + await accept(); + + const diffTab = await waitForDiffTab(); + const uris = tabInputUris(diffTab); + assert.equal(uris.left.scheme, 'diffy'); + assert.match(uris.left.toString(), new RegExp(`diffy://commit/${shas.third}/a\\.txt$`)); + assert.equal(uris.right.scheme, 'file'); + assert.match(uris.right.fsPath, /a\.txt$/); + assert.match(labelStrings(diffTab), new RegExp(`^${shas.third.slice(0, 7)} ↔ Working Copy — a\\.txt$`)); + + await flow; + }); + + it('compareFileWithCommit (no uri arg, falls back to active editor) → moveNext to commit 1 → diff', async () => { + const shas = readSeedShas(); + await openFileInEditor('dir/c.txt'); + await tick(20); + const flow = vscode.commands.executeCommand(COMMAND_IDS.compareFileWithCommit); + + // moveNext × 2 to commit 1, accept + await moveAndAccept(2); + + const diffTab = await waitForDiffTab(); + const uris = tabInputUris(diffTab); + assert.match(uris.left.toString(), new RegExp(`diffy://commit/${shas.first}/dir/c\\.txt$`)); + assert.match(uris.right.fsPath, /dir\/c\.txt$/); + + await flow; + }); + + it('reopenLast: runs compareWithWorkingCopy first, then re-invokes reopenLast and gets the same FilePicker', async () => { + const shas = readSeedShas(); + // Setup: produce a known last-comparison entry by running compareWithWorkingCopy. + const setup = vscode.commands.executeCommand(COMMAND_IDS.compareWithWorkingCopy, { + id: shas.first, + }); + await accept(); // FilePicker + const firstDiff = await waitForDiffTab(); + const firstUris = tabInputUris(firstDiff); + assert.match(firstUris.left.toString(), new RegExp(`diffy://commit/${shas.first}/`)); + await dismissQuickPick(); + await setup; + await closeAllEditors(); + + // Now re-invoke reopenLast — state remembered the comparison. + const reflow = vscode.commands.executeCommand(COMMAND_IDS.reopenLast); + await accept(); // FilePicker again + const reDiff = await waitForDiffTab(); + const reUris = tabInputUris(reDiff); + assert.match(reUris.left.toString(), new RegExp(`diffy://commit/${shas.first}/`)); + assert.equal(reUris.right.scheme, 'file'); + + await dismissQuickPick(); + await reflow; + }); + + it('compareWith without a historyItem id shows a warning and opens no diff', async () => { + const before = allDiffTabs().length; + await vscode.commands.executeCommand(COMMAND_IDS.compareWith, undefined); + await tick(40); + const after = allDiffTabs().length; + assert.equal(after, before, 'no new diff tab should appear'); + }); + + it('compareWithPrevious(historyItem) with the wrapped { historyItem: {...} } shape still works', async () => { + const shas = readSeedShas(); + const flow = vscode.commands.executeCommand(COMMAND_IDS.compareWithPrevious, { + historyItem: { id: shas.second }, + }); + await accept(); // FilePicker + const diffTab = await waitForDiffTab(); + const uris = tabInputUris(diffTab); + assert.match(uris.left.toString(), new RegExp(`diffy://commit/${shas.second}/`)); + assert.match(uris.right.toString(), new RegExp(`diffy://commit/${shas.first}/`)); + await dismissQuickPick(); + await flow; + }); +}); diff --git a/src/test/suite/contentProvider.test.ts b/src/test/suite/contentProvider.test.ts new file mode 100644 index 0000000..11c3af1 --- /dev/null +++ b/src/test/suite/contentProvider.test.ts @@ -0,0 +1,71 @@ +import { strict as assert } from 'node:assert'; +import * as vscode from 'vscode'; +import { readSeedShas, waitForRepoReady } from './helpers'; + +const EXTENSION_ID = 'nimblesite.diffy'; + +const readDiffy = async (uriString: string): Promise<string> => { + const doc = await vscode.workspace.openTextDocument(vscode.Uri.parse(uriString)); + return doc.getText(); +}; + +describe('DiffyContentProvider (real diffy:// URIs against the seeded repo)', () => { + before(async () => { + const ext = vscode.extensions.getExtension(EXTENSION_ID); + if (ext !== undefined && !ext.isActive) { + await ext.activate(); + } + await waitForRepoReady(); + }); + + it('resolves a.txt at every committed version with the expected content', async () => { + const shas = readSeedShas(); + + const a1 = await readDiffy(`diffy://commit/${shas.first}/a.txt`); + assert.match(a1, /a\.txt v1/); + assert.match(a1, /second line v1/); + assert.doesNotMatch(a1, /third line/); + + const a2 = await readDiffy(`diffy://commit/${shas.second}/a.txt`); + assert.match(a2, /a\.txt v2 edited/); + assert.match(a2, /third line added/); + assert.doesNotMatch(a2, /working copy uncommitted/); + + const a3 = await readDiffy(`diffy://commit/${shas.third}/a.txt`); + assert.equal(a3, a2, 'a.txt is unchanged from commit 2 to commit 3'); + }); + + it('resolves dir/c.txt at v1 and v2 with the expected text', async () => { + const shas = readSeedShas(); + const c1 = await readDiffy(`diffy://commit/${shas.first}/dir/c.txt`); + assert.match(c1, /c\.txt v1/); + const c2 = await readDiffy(`diffy://commit/${shas.second}/dir/c.txt`); + assert.match(c2, /c\.txt v2 edited/); + assert.notEqual(c1, c2); + }); + + it('returns the renamed file b2.txt at the third commit but not before', async () => { + const shas = readSeedShas(); + const b2 = await readDiffy(`diffy://commit/${shas.third}/b2.txt`); + assert.match(b2, /renamed and extended/); + const missing = await readDiffy(`diffy://commit/${shas.first}/b2.txt`); + assert.equal( + missing, + '', + 'b2.txt does not exist at commit 1 and should resolve to empty', + ); + }); + + it('returns empty string for a deleted path (d.txt at commit 2)', async () => { + const shas = readSeedShas(); + const deletedAtV2 = await readDiffy(`diffy://commit/${shas.second}/d.txt`); + assert.equal(deletedAtV2, ''); + const aliveAtV1 = await readDiffy(`diffy://commit/${shas.first}/d.txt`); + assert.match(aliveAtV1, /d\.txt v1/); + }); + + it('returns empty string for a malformed diffy URI', async () => { + const bad = await readDiffy('diffy://nonsense/whatever'); + assert.equal(bad, ''); + }); +}); diff --git a/src/test/suite/helpers.ts b/src/test/suite/helpers.ts new file mode 100644 index 0000000..907e88e --- /dev/null +++ b/src/test/suite/helpers.ts @@ -0,0 +1,167 @@ +import { spawnSync } from 'node:child_process'; +import * as path from 'node:path'; +import * as vscode from 'vscode'; + +export const workspaceRoot = (): string => { + const folder = vscode.workspace.workspaceFolders?.[0]; + if (folder === undefined) { + throw new Error('No workspace folder open'); + } + return folder.uri.fsPath; +}; + +export const gitOut = (args: readonly string[]): string => { + const r = spawnSync('git', [...args], { + cwd: workspaceRoot(), + encoding: 'utf8', + }); + if (r.status !== 0) { + throw new Error(`git ${args.join(' ')} failed: ${r.stderr}`); + } + return r.stdout.trim(); +}; + +export interface SeedShas { + readonly first: string; + readonly second: string; + readonly third: string; +} + +export const readSeedShas = (): SeedShas => { + const lines = gitOut(['log', '--format=%H', '--reverse']).split('\n'); + const [first, second, third] = lines; + if ( + first === undefined || + second === undefined || + third === undefined + ) { + throw new Error(`Expected 3 commits, found ${lines.length.toString()}`); + } + return { first, second, third }; +}; + +export const tick = (count = 30): Promise<void> => + new Promise<void>((resolve) => { + let i = 0; + const step = (): void => { + i++; + if (i >= count) { + resolve(); + return; + } + setImmediate(step); + }; + setImmediate(step); + }); + +const wait = (ms: number): Promise<void> => + new Promise<void>((resolve) => { + setTimeout(resolve, ms); + }); + +const PICKER_RENDER_MS = 900; + +export const accept = async (): Promise<void> => { + await wait(PICKER_RENDER_MS); + await vscode.commands.executeCommand('workbench.action.acceptSelectedQuickOpenItem'); +}; + +export const typeText = async (text: string): Promise<void> => { + await wait(PICKER_RENDER_MS); + await vscode.commands.executeCommand('type', { text }); +}; + +export const moveNext = async (): Promise<void> => { + await wait(PICKER_RENDER_MS); + await vscode.commands.executeCommand('workbench.action.quickOpenSelectNext'); +}; + +export const dismissQuickPick = async (): Promise<void> => { + await wait(50); + await vscode.commands.executeCommand('workbench.action.closeQuickOpen'); +}; + +export const allDiffTabs = (): vscode.Tab[] => + vscode.window.tabGroups.all + .flatMap((g) => g.tabs) + .filter((t) => t.input instanceof vscode.TabInputTextDiff); + +export const waitForDiffTab = ({ + timeoutMs = 8000, +}: { + timeoutMs?: number; +} = {}): Promise<vscode.Tab> => { + return new Promise<vscode.Tab>((resolve, reject) => { + const existing = allDiffTabs()[0]; + if (existing !== undefined) { + resolve(existing); + return; + } + const timer = setTimeout(() => { + sub.dispose(); + reject(new Error('Timed out waiting for diff tab')); + }, timeoutMs); + const sub = vscode.window.tabGroups.onDidChangeTabs(() => { + const t = allDiffTabs()[0]; + if (t !== undefined) { + clearTimeout(timer); + sub.dispose(); + resolve(t); + } + }); + }); +}; + +export const closeAllEditors = async (): Promise<void> => { + await vscode.commands.executeCommand('workbench.action.closeAllEditors'); + await tick(5); +}; + +export const openFileInEditor = async (relPath: string): Promise<vscode.TextEditor> => { + const uri = vscode.Uri.file(path.join(workspaceRoot(), relPath)); + const doc = await vscode.workspace.openTextDocument(uri); + return vscode.window.showTextDocument(doc); +}; + +export const tabInputUris = ( + tab: vscode.Tab, +): { left: vscode.Uri; right: vscode.Uri } => { + if (!(tab.input instanceof vscode.TabInputTextDiff)) { + throw new Error(`Tab is not a TabInputTextDiff: ${tab.label}`); + } + return { left: tab.input.original, right: tab.input.modified }; +}; + +interface GitApiShape { + readonly repositories: readonly { readonly rootUri: vscode.Uri }[]; + onDidOpenRepository(handler: () => void): { dispose(): void }; +} + +interface GitExtensionShape { + getAPI(version: 1): GitApiShape; +} + +export const waitForRepoReady = async (timeoutMs = 15000): Promise<void> => { + const ext = vscode.extensions.getExtension<GitExtensionShape>('vscode.git'); + if (ext === undefined) { + throw new Error('vscode.git extension not present'); + } + if (!ext.isActive) { + await ext.activate(); + } + const api = ext.exports.getAPI(1); + if (api.repositories.length > 0) { + return; + } + await new Promise<void>((resolve, reject) => { + const timer = setTimeout(() => { + sub.dispose(); + reject(new Error('vscode.git never opened the seeded repo')); + }, timeoutMs); + const sub = api.onDidOpenRepository(() => { + clearTimeout(timer); + sub.dispose(); + resolve(); + }); + }); +}; diff --git a/src/test/suite/index.ts b/src/test/suite/index.ts new file mode 100644 index 0000000..d8d8308 --- /dev/null +++ b/src/test/suite/index.ts @@ -0,0 +1,26 @@ +import * as path from 'node:path'; +import Mocha from 'mocha'; +import { glob } from 'glob'; + +export const run = async (): Promise<void> => { + const mocha = new Mocha({ + ui: 'bdd', + color: true, + timeout: 60000, + reporter: 'spec', + }); + const testsRoot = __dirname; + const files = await glob('**/*.test.js', { cwd: testsRoot }); + for (const f of files) { + mocha.addFile(path.resolve(testsRoot, f)); + } + await new Promise<void>((resolve, reject) => { + mocha.run((failures) => { + if (failures > 0) { + reject(new Error(`${failures.toString()} mocha test(s) failed.`)); + } else { + resolve(); + } + }); + }); +}; diff --git a/src/test/unit/format.test.ts b/src/test/unit/format.test.ts new file mode 100644 index 0000000..664a0d0 --- /dev/null +++ b/src/test/unit/format.test.ts @@ -0,0 +1,164 @@ +import { strict as assert } from 'node:assert'; +import { formatRelative } from '../../ui/format/relativeTime'; +import { + formatCounts, + mergeChangedFilesWithStats, + statusBadge, +} from '../../ui/format/fileItem'; +import { refTypeLabel } from '../../ui/format/refLabel'; +import type { ChangedFile, DiffStat, Ref } from '../../git/types'; + +const HOUR = 60 * 60; +const DAY = 24 * HOUR; + +describe('formatRelative', () => { + it('returns "just now" for < 1 minute', () => { + assert.equal(formatRelative(100, 100), 'just now'); + assert.equal(formatRelative(100, 130), 'just now'); + assert.equal(formatRelative(100, 159), 'just now'); + }); + + it('clamps negative deltas (future timestamps) to "just now"', () => { + assert.equal(formatRelative(200, 100), 'just now'); + }); + + it('formats minutes when < 60 min', () => { + assert.equal(formatRelative(0, 60), '1m ago'); + assert.equal(formatRelative(0, 30 * 60), '30m ago'); + assert.equal(formatRelative(0, 59 * 60), '59m ago'); + }); + + it('formats hours when < 24 h', () => { + assert.equal(formatRelative(0, HOUR), '1h ago'); + assert.equal(formatRelative(0, 12 * HOUR), '12h ago'); + assert.equal(formatRelative(0, 23 * HOUR), '23h ago'); + }); + + it('formats days for older timestamps', () => { + assert.equal(formatRelative(0, DAY), '1d ago'); + assert.equal(formatRelative(0, 7 * DAY), '7d ago'); + assert.equal(formatRelative(0, 365 * DAY), '365d ago'); + }); +}); + +describe('statusBadge', () => { + it('returns single letter for A/M/D', () => { + assert.equal(statusBadge({ status: 'A', path: 'x' } as ChangedFile), 'A'); + assert.equal(statusBadge({ status: 'M', path: 'x' } as ChangedFile), 'M'); + assert.equal(statusBadge({ status: 'D', path: 'x' } as ChangedFile), 'D'); + }); + + it('appends similarity for R', () => { + const r: ChangedFile = { + status: 'R', + path: 'new', + oldPath: 'old', + similarity: 87, + }; + assert.equal(statusBadge(r), 'R87'); + }); + + it('appends similarity for C', () => { + const c: ChangedFile = { + status: 'C', + path: 'dst', + oldPath: 'src', + similarity: 100, + }; + assert.equal(statusBadge(c), 'C100'); + }); + + it('falls back to 0 when similarity is missing on R/C', () => { + const r = { status: 'R', path: 'new', oldPath: 'old' } as ChangedFile; + assert.equal(statusBadge(r), 'R0'); + const c = { status: 'C', path: 'dst', oldPath: 'src' } as ChangedFile; + assert.equal(statusBadge(c), 'C0'); + }); +}); + +describe('formatCounts', () => { + it('formats numeric +N -M for non-binary stats', () => { + const s: DiffStat = { path: 'x', added: 12, deleted: 3, binary: false }; + assert.equal(formatCounts(s), '+12 -3'); + }); + + it('reports "binary" for binary stats', () => { + const s: DiffStat = { path: 'x', added: 0, deleted: 0, binary: true }; + assert.equal(formatCounts(s), 'binary'); + }); + + it('reports 0/0 cleanly when there are no changes', () => { + const s: DiffStat = { path: 'x', added: 0, deleted: 0, binary: false }; + assert.equal(formatCounts(s), '+0 -0'); + }); +}); + +describe('mergeChangedFilesWithStats', () => { + it('joins by path', () => { + const files: ChangedFile[] = [{ status: 'M', path: 'a.txt' }]; + const stats: DiffStat[] = [{ path: 'a.txt', added: 2, deleted: 1, binary: false }]; + const r = mergeChangedFilesWithStats(files, stats); + assert.equal(r.length, 1); + assert.deepEqual(r[0], { file: files[0], stat: stats[0] }); + }); + + it('falls back to zero stats when no matching numstat row exists', () => { + const files: ChangedFile[] = [{ status: 'A', path: 'new.txt' }]; + const r = mergeChangedFilesWithStats(files, []); + assert.equal(r.length, 1); + assert.deepEqual(r[0]?.stat, { + path: 'new.txt', + added: 0, + deleted: 0, + binary: false, + }); + }); + + it('preserves order from the name-status list', () => { + const files: ChangedFile[] = [ + { status: 'M', path: 'b' }, + { status: 'M', path: 'a' }, + ]; + const stats: DiffStat[] = [ + { path: 'a', added: 1, deleted: 1, binary: false }, + { path: 'b', added: 2, deleted: 2, binary: false }, + ]; + const r = mergeChangedFilesWithStats(files, stats); + assert.equal(r[0]?.file.path, 'b'); + assert.equal(r[1]?.file.path, 'a'); + }); + + it('handles rename entries (new path differs from old)', () => { + const files: ChangedFile[] = [ + { status: 'R', path: 'b2.txt', oldPath: 'b.txt', similarity: 100 }, + ]; + const stats: DiffStat[] = [ + { path: 'b2.txt', oldPath: 'b.txt', added: 1, deleted: 0, binary: false }, + ]; + const r = mergeChangedFilesWithStats(files, stats); + assert.equal(r[0]?.stat.added, 1); + assert.equal(r[0]?.stat.oldPath, 'b.txt'); + }); +}); + +describe('refTypeLabel', () => { + it('returns "Branch" for branches', () => { + const r: Ref = { name: 'main', fullName: 'refs/heads/main', sha: 'a', type: 'branch' }; + assert.equal(refTypeLabel(r), 'Branch'); + }); + + it('returns "Tag" for tags', () => { + const r: Ref = { name: 'v1', fullName: 'refs/tags/v1', sha: 'a', type: 'tag' }; + assert.equal(refTypeLabel(r), 'Tag'); + }); + + it('returns "Ref" for other ref kinds', () => { + const r: Ref = { + name: 'origin/main', + fullName: 'refs/remotes/origin/main', + sha: 'a', + type: 'other', + }; + assert.equal(refTypeLabel(r), 'Ref'); + }); +}); diff --git a/src/test/unit/historyItem.test.ts b/src/test/unit/historyItem.test.ts new file mode 100644 index 0000000..37654e1 --- /dev/null +++ b/src/test/unit/historyItem.test.ts @@ -0,0 +1,45 @@ +import { strict as assert } from 'node:assert'; +import { extractHistoryItemSha } from '../../commands/historyItem'; + +const FULL_SHA = '02920d872f11ed22afacf3966cb1da30b72dfe94'; + +describe('extractHistoryItemSha', () => { + it('returns the value when arg is a non-empty string', () => { + assert.equal(extractHistoryItemSha(FULL_SHA), FULL_SHA); + assert.equal(extractHistoryItemSha('main'), 'main'); + }); + + it('returns undefined for an empty string', () => { + assert.equal(extractHistoryItemSha(''), undefined); + }); + + it('returns undefined for null / undefined / numbers / booleans', () => { + assert.equal(extractHistoryItemSha(null), undefined); + assert.equal(extractHistoryItemSha(undefined), undefined); + assert.equal(extractHistoryItemSha(42), undefined); + assert.equal(extractHistoryItemSha(true), undefined); + }); + + it('extracts id directly from a SourceControlHistoryItem-shaped object', () => { + assert.equal(extractHistoryItemSha({ id: FULL_SHA }), FULL_SHA); + assert.equal(extractHistoryItemSha({ id: 'main', message: 'x' }), 'main'); + }); + + it('unwraps a { historyItem: { id } } shape', () => { + assert.equal(extractHistoryItemSha({ historyItem: { id: FULL_SHA } }), FULL_SHA); + }); + + it('returns undefined when wrapper.historyItem is missing or not an object', () => { + assert.equal(extractHistoryItemSha({ something: 'else' }), undefined); + assert.equal(extractHistoryItemSha({ historyItem: null }), undefined); + assert.equal(extractHistoryItemSha({ historyItem: 'not-an-object' }), undefined); + }); + + it('returns undefined when historyItem.id is empty / missing / wrong type', () => { + assert.equal(extractHistoryItemSha({ id: '' }), undefined); + assert.equal(extractHistoryItemSha({ id: 42 }), undefined); + assert.equal(extractHistoryItemSha({ historyItem: {} }), undefined); + assert.equal(extractHistoryItemSha({ historyItem: { id: '' } }), undefined); + assert.equal(extractHistoryItemSha({ historyItem: { id: 0 } }), undefined); + }); +}); diff --git a/src/test/unit/parsers.test.ts b/src/test/unit/parsers.test.ts index 05e9caf..258414a 100644 --- a/src/test/unit/parsers.test.ts +++ b/src/test/unit/parsers.test.ts @@ -303,8 +303,8 @@ describe('parseRefs', () => { if (r.ok) assert.deepEqual(r.value, []); }); - it('parses a branch ref', () => { - const r = parseRefs(`refs/heads/main${NUL}main${NUL}abc1234${NUL}`); + it('parses a branch ref (newline-terminated record)', () => { + const r = parseRefs(`refs/heads/main${NUL}main${NUL}abc1234\n`); assert.equal(r.ok, true); if (!r.ok) return; assert.deepEqual(r.value, [ @@ -313,7 +313,7 @@ describe('parseRefs', () => { }); it('parses a tag ref', () => { - const r = parseRefs(`refs/tags/v1.0${NUL}v1.0${NUL}def5678${NUL}`); + const r = parseRefs(`refs/tags/v1.0${NUL}v1.0${NUL}def5678\n`); assert.equal(r.ok, true); if (!r.ok) return; assert.deepEqual(r.value, [ @@ -322,16 +322,16 @@ describe('parseRefs', () => { }); it('classifies a non-branch, non-tag ref as other', () => { - const r = parseRefs(`refs/remotes/origin/main${NUL}origin/main${NUL}abc1234${NUL}`); + const r = parseRefs(`refs/remotes/origin/main${NUL}origin/main${NUL}abc1234\n`); assert.equal(r.ok, true); if (r.ok) assert.equal(r.value[0]?.type, 'other'); }); it('parses a mixed batch of branches and tags', () => { const stdout = - `refs/heads/main${NUL}main${NUL}aaa${NUL}` + - `refs/heads/dev${NUL}dev${NUL}bbb${NUL}` + - `refs/tags/v1.0${NUL}v1.0${NUL}ccc${NUL}`; + `refs/heads/main${NUL}main${NUL}aaa\n` + + `refs/heads/dev${NUL}dev${NUL}bbb\n` + + `refs/tags/v1.0${NUL}v1.0${NUL}ccc\n`; const r = parseRefs(stdout); assert.equal(r.ok, true); if (!r.ok) return; @@ -341,15 +341,21 @@ describe('parseRefs', () => { assert.equal(r.value[2]?.type, 'tag'); }); - it('errors when field count is not a multiple of 3', () => { - const r = parseRefs(`refs/heads/main${NUL}main${NUL}`); + it('errors when a line has fewer than 3 NUL-separated fields', () => { + const r = parseRefs(`refs/heads/main${NUL}main\n`); assert.equal(r.ok, false); - if (!r.ok) assert.match(r.error.message, /multiple of 3/); + if (!r.ok) assert.match(r.error.message, /3 NUL-separated/); }); it('errors when a ref field is empty', () => { - const r = parseRefs(`refs/heads/main${NUL}${NUL}abc${NUL}`); + const r = parseRefs(`refs/heads/main${NUL}${NUL}abc\n`); assert.equal(r.ok, false); if (!r.ok) assert.match(r.error.message, /empty field/); }); + + it('ignores trailing blank lines', () => { + const r = parseRefs(`refs/heads/main${NUL}main${NUL}abc\n\n\n`); + assert.equal(r.ok, true); + if (r.ok) assert.equal(r.value.length, 1); + }); }); diff --git a/src/test/unit/repoMatch.test.ts b/src/test/unit/repoMatch.test.ts new file mode 100644 index 0000000..db46202 --- /dev/null +++ b/src/test/unit/repoMatch.test.ts @@ -0,0 +1,75 @@ +import { strict as assert } from 'node:assert'; +import { findRepoForUri, matchRepoByFsPath } from '../../git/repoMatch'; + +const repoAt = (fsPath: string) => ({ rootUri: { fsPath } }); + +describe('matchRepoByFsPath', () => { + it('returns undefined for an empty repository list', () => { + assert.equal(matchRepoByFsPath([], '/anywhere'), undefined); + }); + + it('returns the only repository when the target is inside its root', () => { + const r = repoAt('/Users/me/repo'); + assert.strictEqual(matchRepoByFsPath([r], '/Users/me/repo/file.txt'), r); + }); + + it('returns undefined when the target is outside every repository', () => { + const r = repoAt('/Users/me/repo'); + assert.equal(matchRepoByFsPath([r], '/Users/elsewhere/file.txt'), undefined); + }); + + it('prefers the longest matching root for nested repositories', () => { + const outer = repoAt('/Users/me/repo'); + const inner = repoAt('/Users/me/repo/sub/inner'); + const result = matchRepoByFsPath([outer, inner], '/Users/me/repo/sub/inner/file.txt'); + assert.strictEqual(result, inner); + }); + + it('order of the list does not affect the longest-match result', () => { + const outer = repoAt('/Users/me/repo'); + const inner = repoAt('/Users/me/repo/sub/inner'); + const result = matchRepoByFsPath([inner, outer], '/Users/me/repo/sub/inner/file.txt'); + assert.strictEqual(result, inner); + }); +}); + +describe('findRepoForUri', () => { + it('delegates to api.getRepository when available', () => { + const repo = repoAt('/Users/me/repo'); + const uri = { fsPath: '/Users/me/repo/file.txt' }; + const api = { + repositories: [] as readonly typeof repo[], + getRepository: (_u: typeof uri) => repo, + }; + const result = findRepoForUri(api, uri); + assert.strictEqual(result, repo); + }); + + it('falls back to fsPath matching when getRepository is absent', () => { + const repo = repoAt('/Users/me/repo'); + const uri = { fsPath: '/Users/me/repo/file.txt' }; + const api = { repositories: [repo] }; + const result = findRepoForUri(api, uri); + assert.strictEqual(result, repo); + }); + + it('returns undefined when getRepository returns null', () => { + const uri = { fsPath: '/Users/me/repo/file.txt' }; + const repo = repoAt('/Users/me/repo'); + const api = { + repositories: [] as readonly typeof repo[], + getRepository: (_u: typeof uri) => null, + }; + const result = findRepoForUri(api, uri); + assert.equal(result, undefined); + }); + + it('falls back to longest-prefix matching across multiple roots', () => { + const a = repoAt('/Users/me/repo-a'); + const b = repoAt('/Users/me/repo-b'); + const api = { repositories: [a, b] }; + assert.strictEqual(findRepoForUri(api, { fsPath: '/Users/me/repo-b/x' }), b); + assert.strictEqual(findRepoForUri(api, { fsPath: '/Users/me/repo-a/y' }), a); + assert.equal(findRepoForUri(api, { fsPath: '/Users/me/somewhere-else' }), undefined); + }); +}); diff --git a/src/test/unit/result.test.ts b/src/test/unit/result.test.ts index 6c694f6..26a7180 100644 --- a/src/test/unit/result.test.ts +++ b/src/test/unit/result.test.ts @@ -78,7 +78,7 @@ describe('Result', () => { it('passes Err through unchanged', () => { const original: Result<number, string> = err('bad'); - const r = map(original, (n) => n * 3); + const r = map<number, number, string>(original, (n) => n * 3); assert.equal(r.ok, false); if (!r.ok) assert.equal(r.error, 'bad'); }); diff --git a/src/test/unit/state.test.ts b/src/test/unit/state.test.ts new file mode 100644 index 0000000..882cbc8 --- /dev/null +++ b/src/test/unit/state.test.ts @@ -0,0 +1,70 @@ +import { strict as assert } from 'node:assert'; +import { isLastComparison } from '../../state'; + +const validRevA = { kind: 'commit', sha: 'abcdef0' }; +const validRevB = { kind: 'workingCopy' }; + +describe('isLastComparison', () => { + it('accepts a valid record with commit revA + workingCopy revB', () => { + const value = { revA: validRevA, revB: validRevB, repoRoot: '/path/to/repo' }; + assert.equal(isLastComparison(value), true); + }); + + it('accepts a valid record with two commit revs', () => { + const value = { + revA: { kind: 'commit', sha: 'a' }, + revB: { kind: 'commit', sha: 'b' }, + repoRoot: '/path', + }; + assert.equal(isLastComparison(value), true); + }); + + it('rejects null / undefined / non-object', () => { + assert.equal(isLastComparison(null), false); + assert.equal(isLastComparison(undefined), false); + assert.equal(isLastComparison('string'), false); + assert.equal(isLastComparison(42), false); + assert.equal(isLastComparison(true), false); + assert.equal(isLastComparison([]), false); // arrays are objects but missing fields + }); + + it('rejects a record missing repoRoot', () => { + const value = { revA: validRevA, revB: validRevB }; + assert.equal(isLastComparison(value), false); + }); + + it('rejects a record where repoRoot is not a string', () => { + const value = { revA: validRevA, revB: validRevB, repoRoot: 42 }; + assert.equal(isLastComparison(value), false); + }); + + it('rejects a record missing revA', () => { + const value = { revB: validRevB, repoRoot: '/p' }; + assert.equal(isLastComparison(value), false); + }); + + it('rejects a record where revA is null', () => { + const value = { revA: null, revB: validRevB, repoRoot: '/p' }; + assert.equal(isLastComparison(value), false); + }); + + it('rejects a record where revA is not an object', () => { + const value = { revA: 'commit', revB: validRevB, repoRoot: '/p' }; + assert.equal(isLastComparison(value), false); + }); + + it('rejects a record missing revB', () => { + const value = { revA: validRevA, repoRoot: '/p' }; + assert.equal(isLastComparison(value), false); + }); + + it('rejects a record where revB is null', () => { + const value = { revA: validRevA, revB: null, repoRoot: '/p' }; + assert.equal(isLastComparison(value), false); + }); + + it('rejects a record where revB is not an object', () => { + const value = { revA: validRevA, revB: 'workingCopy', repoRoot: '/p' }; + assert.equal(isLastComparison(value), false); + }); +}); diff --git a/src/ui/CommitPicker.ts b/src/ui/CommitPicker.ts new file mode 100644 index 0000000..3bfdeae --- /dev/null +++ b/src/ui/CommitPicker.ts @@ -0,0 +1,36 @@ +import type * as vscode from 'vscode'; +import { type Result, map } from '../result'; +import type { Commit } from '../git/types'; +import { showSinglePick } from './runQuickPick'; +import type { Cancelled } from './cancelled'; +import { formatRelative } from './format/relativeTime'; + +interface CommitPickItem extends vscode.QuickPickItem { + readonly commit: Commit; +} + +const toItem = (commit: Commit, now: number): CommitPickItem => ({ + label: commit.shortSha, + description: commit.subject, + detail: `${commit.author} • ${formatRelative(commit.authorTime, now)}`, + commit, +}); + +export const pickCommit = async ({ + commits, + placeholder, + now, +}: { + commits: readonly Commit[]; + placeholder?: string; + now?: number; +}): Promise<Result<Commit, Cancelled>> => { + const tNow = now ?? Math.floor(Date.now() / 1000); + const r = await showSinglePick<CommitPickItem>({ + items: commits.map((c) => toItem(c, tNow)), + placeholder: placeholder ?? 'Pick a commit', + matchOnDescription: true, + matchOnDetail: true, + }); + return map(r, (item) => item.commit); +}; diff --git a/src/ui/FilePicker.ts b/src/ui/FilePicker.ts new file mode 100644 index 0000000..147d0ee --- /dev/null +++ b/src/ui/FilePicker.ts @@ -0,0 +1,40 @@ +import type * as vscode from 'vscode'; +import { showStayOpenPick } from './runQuickPick'; +import { + type FileEntry, + formatCounts, + statusBadge, +} from './format/fileItem'; + +export { mergeChangedFilesWithStats } from './format/fileItem'; +export type { FileEntry } from './format/fileItem'; + +interface FileItem extends vscode.QuickPickItem { + readonly entry: FileEntry; +} + +const toItem = (entry: FileEntry): FileItem => ({ + label: entry.file.path, + description: formatCounts(entry.stat), + detail: statusBadge(entry.file), + entry, +}); + +export const pickFiles = ({ + entries, + onPick, + placeholder, +}: { + entries: readonly FileEntry[]; + onPick: (entry: FileEntry) => void | Promise<void>; + placeholder?: string; +}): Promise<void> => + showStayOpenPick<FileItem>( + { + items: entries.map(toItem), + placeholder: placeholder ?? 'Pick a file to diff (Esc to close)', + matchOnDescription: true, + matchOnDetail: true, + }, + (item) => onPick(item.entry), + ); diff --git a/src/ui/RefPicker.ts b/src/ui/RefPicker.ts new file mode 100644 index 0000000..6314fde --- /dev/null +++ b/src/ui/RefPicker.ts @@ -0,0 +1,33 @@ +import type * as vscode from 'vscode'; +import { type Result, map } from '../result'; +import type { Ref } from '../git/types'; +import { showSinglePick } from './runQuickPick'; +import type { Cancelled } from './cancelled'; +import { refTypeLabel } from './format/refLabel'; + +interface RefPickItem extends vscode.QuickPickItem { + readonly ref: Ref; +} + +const toItem = (ref: Ref): RefPickItem => ({ + label: ref.name, + description: ref.sha.slice(0, 7), + detail: refTypeLabel(ref), + ref, +}); + +export const pickRef = async ({ + refs, + placeholder, +}: { + refs: readonly Ref[]; + placeholder?: string; +}): Promise<Result<Ref, Cancelled>> => { + const r = await showSinglePick<RefPickItem>({ + items: refs.map(toItem), + placeholder: placeholder ?? 'Pick a branch or tag', + matchOnDescription: true, + matchOnDetail: true, + }); + return map(r, (item) => item.ref); +}; diff --git a/src/ui/SideBPicker.ts b/src/ui/SideBPicker.ts new file mode 100644 index 0000000..cf5ce2c --- /dev/null +++ b/src/ui/SideBPicker.ts @@ -0,0 +1,49 @@ +import type * as vscode from 'vscode'; +import { type Result, map } from '../result'; +import { showSinglePick } from './runQuickPick'; +import type { Cancelled } from './cancelled'; + +export type SideBChoice = + | { readonly kind: 'workingCopy' } + | { readonly kind: 'index' } + | { readonly kind: 'pickRef' } + | { readonly kind: 'pickCommit' }; + +interface SideBItem extends vscode.QuickPickItem { + readonly choice: SideBChoice; +} + +const ITEMS: readonly SideBItem[] = [ + { + label: 'Working Copy', + description: 'On-disk files in this repository', + choice: { kind: 'workingCopy' }, + }, + { + label: 'Index', + description: 'The git staging area', + choice: { kind: 'index' }, + }, + { + label: 'Pick a commit…', + description: 'Choose from recent log entries', + choice: { kind: 'pickCommit' }, + }, + { + label: 'Pick a branch or tag…', + description: 'Choose from refs in this repository', + choice: { kind: 'pickRef' }, + }, +]; + +export const pickSideBChoice = async ({ + placeholder, +}: { + placeholder?: string; +} = {}): Promise<Result<SideBChoice, Cancelled>> => { + const r = await showSinglePick<SideBItem>({ + items: ITEMS, + placeholder: placeholder ?? 'Compare against…', + }); + return map(r, (item) => item.choice); +}; diff --git a/src/ui/cancelled.ts b/src/ui/cancelled.ts new file mode 100644 index 0000000..18c270e --- /dev/null +++ b/src/ui/cancelled.ts @@ -0,0 +1,5 @@ +export interface Cancelled { + readonly cancelled: true; +} + +export const CANCELLED: Cancelled = { cancelled: true }; diff --git a/src/ui/format/fileItem.ts b/src/ui/format/fileItem.ts new file mode 100644 index 0000000..9dc03dc --- /dev/null +++ b/src/ui/format/fileItem.ts @@ -0,0 +1,37 @@ +import type { ChangedFile, DiffStat } from '../../git/types'; + +export const statusBadge = (file: ChangedFile): string => { + if (file.status === 'R') { + return `R${(file.similarity ?? 0).toString()}`; + } + if (file.status === 'C') { + return `C${(file.similarity ?? 0).toString()}`; + } + return file.status; +}; + +export const formatCounts = (stat: DiffStat): string => { + if (stat.binary) { + return 'binary'; + } + return `+${stat.added.toString()} -${stat.deleted.toString()}`; +}; + +export interface FileEntry { + readonly file: ChangedFile; + readonly stat: DiffStat; +} + +export const mergeChangedFilesWithStats = ( + files: readonly ChangedFile[], + stats: readonly DiffStat[], +): readonly FileEntry[] => { + const byPath = new Map<string, DiffStat>(); + for (const s of stats) { + byPath.set(s.path, s); + } + return files.map((f) => ({ + file: f, + stat: byPath.get(f.path) ?? { path: f.path, added: 0, deleted: 0, binary: false }, + })); +}; diff --git a/src/ui/format/refLabel.ts b/src/ui/format/refLabel.ts new file mode 100644 index 0000000..4dc9732 --- /dev/null +++ b/src/ui/format/refLabel.ts @@ -0,0 +1,11 @@ +import type { Ref } from '../../git/types'; + +export const refTypeLabel = (ref: Ref): string => { + if (ref.type === 'branch') { + return 'Branch'; + } + if (ref.type === 'tag') { + return 'Tag'; + } + return 'Ref'; +}; diff --git a/src/ui/format/relativeTime.ts b/src/ui/format/relativeTime.ts new file mode 100644 index 0000000..854b933 --- /dev/null +++ b/src/ui/format/relativeTime.ts @@ -0,0 +1,20 @@ +const SECONDS_PER_MINUTE = 60; +const MINUTES_PER_HOUR = 60; +const HOURS_PER_DAY = 24; + +export const formatRelative = (unixSeconds: number, now: number): string => { + const deltaSec = Math.max(0, now - unixSeconds); + const minutes = Math.floor(deltaSec / SECONDS_PER_MINUTE); + if (minutes < 1) { + return 'just now'; + } + if (minutes < MINUTES_PER_HOUR) { + return `${minutes.toString()}m ago`; + } + const hours = Math.floor(minutes / MINUTES_PER_HOUR); + if (hours < HOURS_PER_DAY) { + return `${hours.toString()}h ago`; + } + const days = Math.floor(hours / HOURS_PER_DAY); + return `${days.toString()}d ago`; +}; diff --git a/src/ui/runQuickPick.ts b/src/ui/runQuickPick.ts new file mode 100644 index 0000000..8059432 --- /dev/null +++ b/src/ui/runQuickPick.ts @@ -0,0 +1,69 @@ +import * as vscode from 'vscode'; +import { type Result, err, ok } from '../result'; +import { CANCELLED, type Cancelled } from './cancelled'; + +export interface QuickPickConfig<T extends vscode.QuickPickItem> { + readonly items: readonly T[]; + readonly placeholder: string; + readonly matchOnDescription?: boolean; + readonly matchOnDetail?: boolean; + readonly ignoreFocusOut?: boolean; +} + +const configurePicker = <T extends vscode.QuickPickItem>( + qp: vscode.QuickPick<T>, + config: QuickPickConfig<T>, +): void => { + qp.placeholder = config.placeholder; + qp.matchOnDescription = config.matchOnDescription ?? false; + qp.matchOnDetail = config.matchOnDetail ?? false; + qp.ignoreFocusOut = config.ignoreFocusOut ?? false; + qp.items = config.items; +}; + +export const showSinglePick = <T extends vscode.QuickPickItem>( + config: QuickPickConfig<T>, +): Promise<Result<T, Cancelled>> => { + return new Promise<Result<T, Cancelled>>((resolve) => { + const qp = vscode.window.createQuickPick<T>(); + configurePicker(qp, config); + let settled = false; + const finish = (r: Result<T, Cancelled>): void => { + if (settled) { + return; + } + settled = true; + qp.dispose(); + resolve(r); + }; + qp.onDidAccept(() => { + const choice = qp.selectedItems[0] ?? qp.activeItems[0]; + finish(choice === undefined ? err(CANCELLED) : ok(choice)); + }); + qp.onDidHide(() => { + finish(err(CANCELLED)); + }); + qp.show(); + }); +}; + +export const showStayOpenPick = <T extends vscode.QuickPickItem>( + config: QuickPickConfig<T>, + onPick: (item: T) => void | Promise<void>, +): Promise<void> => { + return new Promise<void>((resolve) => { + const qp = vscode.window.createQuickPick<T>(); + configurePicker(qp, { ...config, ignoreFocusOut: true }); + qp.onDidAccept(() => { + const choice = qp.selectedItems[0] ?? qp.activeItems[0]; + if (choice !== undefined) { + void Promise.resolve(onPick(choice)); + } + }); + qp.onDidHide(() => { + qp.dispose(); + resolve(); + }); + qp.show(); + }); +}; diff --git a/src/vscodeGitApi.ts b/src/vscodeGitApi.ts new file mode 100644 index 0000000..4f9652f --- /dev/null +++ b/src/vscodeGitApi.ts @@ -0,0 +1,27 @@ +import * as vscode from 'vscode'; + +interface GitExtensionExports { + getAPI(version: 1): GitApi; +} + +export interface GitVsRepository { + readonly rootUri: vscode.Uri; +} + +export interface GitApi { + readonly repositories: readonly GitVsRepository[]; + getRepository?(uri: vscode.Uri): GitVsRepository | null; +} + +export const getGitApi = async (): Promise<GitApi | undefined> => { + const ext = vscode.extensions.getExtension<GitExtensionExports>('vscode.git'); + if (ext === undefined) { + return undefined; + } + if (!ext.isActive) { + await ext.activate(); + } + return ext.exports.getAPI(1); +}; + +export { findRepoForUri } from './git/repoMatch'; diff --git a/test-fixtures/repo-seed/seed.sh b/test-fixtures/repo-seed/seed.sh new file mode 100755 index 0000000..1fcc910 --- /dev/null +++ b/test-fixtures/repo-seed/seed.sh @@ -0,0 +1,57 @@ +#!/usr/bin/env bash +# Builds a deterministic git repo at <this-dir>/workspace for E2E tests. +# Re-run this script to rebuild; it nukes the existing workspace first. +set -euo pipefail + +HERE="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +WORKSPACE="$HERE/workspace" + +rm -rf "$WORKSPACE" +mkdir -p "$WORKSPACE" +cd "$WORKSPACE" + +export GIT_AUTHOR_NAME="Test Author" +export GIT_AUTHOR_EMAIL="test@example.com" +export GIT_COMMITTER_NAME="Test Committer" +export GIT_COMMITTER_EMAIL="committer@example.com" +export GIT_AUTHOR_DATE="2024-01-01T00:00:00Z" +export GIT_COMMITTER_DATE="2024-01-01T00:00:00Z" + +git init -q -b main + +# --- commit 1: initial layout ----------------------------------------------- +mkdir -p dir +printf 'a.txt v1\nsecond line v1\n' > a.txt +printf 'b.txt v1\n' > b.txt +printf 'c.txt v1\nsecond v1\n' > dir/c.txt +printf 'd.txt v1\n' > d.txt +git add . +GIT_AUTHOR_DATE="2024-01-01T00:00:00Z" \ +GIT_COMMITTER_DATE="2024-01-01T00:00:00Z" \ +git commit -q -m "first: add a, b, dir/c, d" + +# --- commit 2: edit a.txt, edit dir/c.txt, delete d.txt ---------------------- +printf 'a.txt v2 edited\nsecond line v1\nthird line added\n' > a.txt +printf 'c.txt v2 edited\nsecond v1\n' > dir/c.txt +git rm -q d.txt +git add a.txt dir/c.txt +GIT_AUTHOR_DATE="2024-01-02T00:00:00Z" \ +GIT_COMMITTER_DATE="2024-01-02T00:00:00Z" \ +git commit -q -m "second: edit a and c, delete d" + +# --- commit 3: rename b.txt -> b2.txt (with a small edit) ------------------- +git mv b.txt b2.txt +printf 'b.txt v1\nrenamed and extended\n' > b2.txt +git add b2.txt +GIT_AUTHOR_DATE="2024-01-03T00:00:00Z" \ +GIT_COMMITTER_DATE="2024-01-03T00:00:00Z" \ +git commit -q -m "third: rename b -> b2 and extend" + +# Tag commit 2 so RefPicker has at least one tag to pick from +git tag v0.1.0 HEAD~1 + +# Leave a working-copy edit on a.txt so 'compare with Working Copy' has content +printf 'a.txt v2 edited\nsecond line v1\nthird line added\nworking copy uncommitted\n' > a.txt + +echo "Seeded $WORKSPACE" +git -C "$WORKSPACE" --no-pager log --oneline From 96fbeca8f339e494084b746a79c5fd9956a57dbc Mon Sep 17 00:00:00 2001 From: Christian Findlay <16697547+MelbourneDeveloper@users.noreply.github.com> Date: Sat, 23 May 2026 21:32:34 +1000 Subject: [PATCH 03/17] Add license --- LICENSE | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) create mode 100644 LICENSE diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..1ccc31c --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2026 NIMBLESITE PTY LTD + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. From 43cdb0431e4eabdf0300cffb08415c51d5f04e1f Mon Sep 17 00:00:00 2001 From: Christian Findlay <16697547+MelbourneDeveloper@users.noreply.github.com> Date: Sat, 23 May 2026 21:49:58 +1000 Subject: [PATCH 04/17] Fixes --- .github/workflows/ci.yml | 6 +- .github/workflows/release.yml | 73 +++++++- .gitignore | 3 + Makefile | 5 +- package-lock.json | 20 ++ package.json | 194 +++++++++++++++++--- schemas/shipwright.schema.json | 224 +++++++++++++++++++++++ scripts/stamp-release-version.mjs | 164 +++++++++++++++++ scripts/sync-menus.mjs | 75 ++++++++ scripts/validate-shipwright-manifest.mjs | 44 +++++ scripts/verify-versions.mjs | 134 ++++++++++++++ shipwright.json | 32 ++++ src/commands/compareFileWithCommit.ts | 52 +----- src/commands/compareFileWithRev.ts | 87 +++++++++ src/commands/compareWithRef.ts | 55 ++++++ src/commands/flow.ts | 37 +++- src/constants.ts | 4 + src/extension.ts | 18 ++ src/menus.ts | 84 +++++++++ src/test/runTests.ts | 7 +- src/test/suite/activation.test.ts | 10 +- src/test/suite/commands.test.ts | 127 +++++++++++++ src/test/unit/menus.test.ts | 154 ++++++++++++++++ src/ui/RefPicker.ts | 13 +- 24 files changed, 1523 insertions(+), 99 deletions(-) create mode 100644 schemas/shipwright.schema.json create mode 100644 scripts/stamp-release-version.mjs create mode 100644 scripts/sync-menus.mjs create mode 100644 scripts/validate-shipwright-manifest.mjs create mode 100644 scripts/verify-versions.mjs create mode 100644 shipwright.json create mode 100644 src/commands/compareFileWithRev.ts create mode 100644 src/commands/compareWithRef.ts create mode 100644 src/menus.ts create mode 100644 src/test/unit/menus.test.ts diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index ba8fe3b..b9a8ad3 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -34,9 +34,13 @@ jobs: - name: Format check run: make fmt CHECK=1 - - name: Lint + - name: Lint (includes shipwright.json schema check) run: make lint + - name: Dry-run release stamp (proves the script still runs) + shell: bash + run: node scripts/stamp-release-version.mjs 0.0.0 --dry + - name: Test (Linux, xvfb) if: runner.os == 'Linux' run: xvfb-run -a make test diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 887909a..882f1b6 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -1,4 +1,16 @@ # agent-pmo:74cf183 +# Shipwright SWR-REL-* release pipeline for Diffy. +# +# 1. version — derive semver from the v* tag, capture source SHA +# 2. ci-gate — run `make ci` on the tagged commit; publish is blocked unless this passes +# 3. validate-manifest — schema-check shipwright.json before any artifact is built +# 4. build-vsix matrix — stamp the runner working tree, verify, build, package per platform +# 5. release — assemble the GitHub Release with every VSIX +# 6. publish — push every VSIX to the VS Code Marketplace in one vsce call +# +# Source-controlled versions are never mutated. Stamping happens in the runner +# working tree only, per SWR-VERSION-BUILD-STAMPING. + name: Release on: @@ -13,7 +25,7 @@ jobs: version: name: Extract release version runs-on: ubuntu-latest - timeout-minutes: 10 + timeout-minutes: 5 outputs: version: ${{ steps.extract.outputs.version }} source_sha: ${{ steps.extract.outputs.source_sha }} @@ -24,9 +36,51 @@ jobs: echo "version=${GITHUB_REF_NAME#v}" >> "$GITHUB_OUTPUT" echo "source_sha=${GITHUB_SHA}" >> "$GITHUB_OUTPUT" + ci-gate: + name: CI gate (make ci) + needs: version + runs-on: ubuntu-latest + timeout-minutes: 15 + steps: + - uses: actions/checkout@v4 + with: + ref: ${{ needs.version.outputs.source_sha }} + - uses: actions/setup-node@v4 + with: + node-version: '22.x' + cache: 'npm' + - run: npm ci + - name: Format check + run: make fmt CHECK=1 + - name: Lint (includes shipwright.json schema check) + run: make lint + - name: Test (xvfb for @vscode/test-electron) + run: xvfb-run -a make test + - name: Build + run: make build + + validate-manifest: + name: Validate shipwright.json + needs: version + runs-on: ubuntu-latest + timeout-minutes: 5 + steps: + - uses: actions/checkout@v4 + with: + ref: ${{ needs.version.outputs.source_sha }} + - uses: actions/setup-node@v4 + with: + node-version: '22.x' + cache: 'npm' + - run: npm ci + - name: Validate shipwright.json against schema + run: npm run shipwright:validate + - name: Dry-run stamp (proves the script runs against this tag) + run: node scripts/stamp-release-version.mjs "${{ needs.version.outputs.version }}" --dry + build-vsix: name: Build VSIX (${{ matrix.artifact_name }}) - needs: version + needs: [version, ci-gate, validate-manifest] strategy: fail-fast: false matrix: @@ -50,7 +104,7 @@ jobs: vsce_target: win32-arm64 artifact_name: win32-arm64 runs-on: ${{ matrix.os }} - timeout-minutes: 10 + timeout-minutes: 15 steps: - uses: actions/checkout@v4 with: @@ -62,18 +116,25 @@ jobs: node-version: '22.x' cache: 'npm' - - name: Stamp version - run: node scripts/stamp-release-version.mjs "${{ needs.version.outputs.version }}" - - name: Install dependencies run: npm ci + - name: Stamp release version (runner tree only) + run: node scripts/stamp-release-version.mjs "${{ needs.version.outputs.version }}" + + - name: Verify every version carrier + run: node scripts/verify-versions.mjs "${{ needs.version.outputs.version }}" + - name: Build run: npm run build - name: Package VSIX run: npx vsce package --target ${{ matrix.vsce_target }} --out diffy-${{ needs.version.outputs.version }}-${{ matrix.vsce_target }}.vsix + - name: Verify VSIX bundles shipwright.json + shell: bash + run: unzip -l diffy-${{ needs.version.outputs.version }}-${{ matrix.vsce_target }}.vsix | grep -F "extension/shipwright.json" + - name: Upload VSIX uses: actions/upload-artifact@v4 with: diff --git a/.gitignore b/.gitignore index 03c5ce6..e437c3c 100644 --- a/.gitignore +++ b/.gitignore @@ -75,3 +75,6 @@ build/ .vscode-test/ .vscode-test-web/ .nyc_output/ + +# Shipwright SWR-VERSION-BUILD-STAMPING — release-time artifact, never committed +build-info.json diff --git a/Makefile b/Makefile index 4604103..ee027d8 100644 --- a/Makefile +++ b/Makefile @@ -47,11 +47,12 @@ test: npm run test:coverage $(MAKE) _coverage_check -## lint: ESLint + tsc --noEmit (read-only). No formatting. +## lint: ESLint + tsc --noEmit + shipwright.json schema check (read-only). No formatting. lint: @echo "==> Linting..." npm run lint npm run typecheck + npm run shipwright:validate ## fmt: Prettier in-place. Pass CHECK=1 for read-only check (CI use). fmt: @@ -61,7 +62,7 @@ fmt: ## clean: Remove all build artifacts clean: @echo "==> Cleaning..." - $(RM) out dist coverage .vscode-test .nyc_output *.vsix + $(RM) out dist coverage .vscode-test .nyc_output *.vsix build-info.json ## ci: lint + test + build (full CI simulation) ci: lint test build diff --git a/package-lock.json b/package-lock.json index 727f904..41b3e0c 100644 --- a/package-lock.json +++ b/package-lock.json @@ -18,6 +18,8 @@ "@types/vscode": "^1.85.0", "@vscode/test-electron": "^2.4.1", "@vscode/vsce": "^3.2.0", + "ajv": "^8.17.1", + "ajv-formats": "^3.0.1", "c8": "^10.1.2", "eslint": "^9.13.0", "glob": "^11.0.0", @@ -1474,6 +1476,24 @@ "url": "https://github.com/sponsors/epoberezkin" } }, + "node_modules/ajv-formats": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-3.0.1.tgz", + "integrity": "sha512-8iUql50EUR+uUcdRQ3HDqa6EVyo3docL8g5WJ3FNcWmu62IbkGUue/pEyLBW8VGKKucTPgqeks4fIU1DA4yowQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ajv": "^8.0.0" + }, + "peerDependencies": { + "ajv": "^8.0.0" + }, + "peerDependenciesMeta": { + "ajv": { + "optional": true + } + } + }, "node_modules/ansi-colors": { "version": "4.1.3", "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.3.tgz", diff --git a/package.json b/package.json index 19ef2d1..0e28165 100644 --- a/package.json +++ b/package.json @@ -14,59 +14,196 @@ "vscode": "^1.85.0", "node": ">=20" }, - "categories": ["SCM Providers"], - "activationEvents": ["onStartupFinished"], - "extensionDependencies": ["vscode.git"], + "categories": [ + "SCM Providers" + ], + "activationEvents": [ + "onStartupFinished" + ], + "extensionDependencies": [ + "vscode.git" + ], + "enabledApiProposals": [ + "contribSourceControlHistoryItemMenu" + ], "contributes": { "commands": [ - { "command": "diffy.compareWith", "title": "Diffy: Compare with…" }, - { "command": "diffy.compareWithWorkingCopy", "title": "Diffy: Compare with Working Copy" }, - { "command": "diffy.compareWithPrevious", "title": "Diffy: Compare with Previous" }, - { "command": "diffy.compareTwoCommits", "title": "Diffy: Compare Two Commits" }, - { "command": "diffy.compareFileWithCommit", "title": "Diffy: Compare with Commit…" }, - { "command": "diffy.reopenLast", "title": "Diffy: Reopen Last Comparison" }, - { "command": "diffy.showLogs", "title": "Diffy: Show Logs" } + { + "command": "diffy.compareWith", + "title": "Diffy: Compare with…" + }, + { + "command": "diffy.compareWithWorkingCopy", + "title": "Diffy: Compare with Working Copy" + }, + { + "command": "diffy.compareWithPrevious", + "title": "Diffy: Compare with Previous" + }, + { + "command": "diffy.compareWithBranch", + "title": "Diffy: Compare with Branch…" + }, + { + "command": "diffy.compareWithTag", + "title": "Diffy: Compare with Tag…" + }, + { + "command": "diffy.compareTwoCommits", + "title": "Diffy: Compare Two Commits" + }, + { + "command": "diffy.compareFileWithCommit", + "title": "Diffy: Compare with Commit…" + }, + { + "command": "diffy.compareFileWithBranch", + "title": "Diffy: Compare with Branch…" + }, + { + "command": "diffy.compareFileWithTag", + "title": "Diffy: Compare with Tag…" + }, + { + "command": "diffy.reopenLast", + "title": "Diffy: Reopen Last Comparison" + }, + { + "command": "diffy.showLogs", + "title": "Diffy: Show Logs" + } ], "menus": { - "scm/history/item/context": [ - { "command": "diffy.compareWith", "when": "scmProvider == git", "group": "diffy@1" }, - { "command": "diffy.compareWithWorkingCopy", "when": "scmProvider == git", "group": "diffy@2" }, - { "command": "diffy.compareWithPrevious", "when": "scmProvider == git", "group": "diffy@3" } + "scm/historyItem/context": [ + { + "command": "diffy.compareWith", + "when": "scmProvider == git", + "group": "diffy@1" + }, + { + "command": "diffy.compareWithWorkingCopy", + "when": "scmProvider == git", + "group": "diffy@2" + }, + { + "command": "diffy.compareWithPrevious", + "when": "scmProvider == git", + "group": "diffy@3" + }, + { + "command": "diffy.compareWithBranch", + "when": "scmProvider == git", + "group": "diffy@4" + }, + { + "command": "diffy.compareWithTag", + "when": "scmProvider == git", + "group": "diffy@5" + } ], "scm/resourceState/context": [ - { "command": "diffy.compareFileWithCommit", "when": "scmProvider == git", "group": "diffy@1" } + { + "command": "diffy.compareFileWithCommit", + "when": "scmProvider == git", + "group": "diffy@1" + }, + { + "command": "diffy.compareFileWithBranch", + "when": "scmProvider == git", + "group": "diffy@2" + }, + { + "command": "diffy.compareFileWithTag", + "when": "scmProvider == git", + "group": "diffy@3" + } ], "editor/title/context": [ - { "command": "diffy.compareFileWithCommit", "when": "resourceScheme == file", "group": "3_compare" } + { + "command": "diffy.compareFileWithCommit", + "when": "resourceScheme == file", + "group": "diffy@1" + }, + { + "command": "diffy.compareFileWithBranch", + "when": "resourceScheme == file", + "group": "diffy@2" + }, + { + "command": "diffy.compareFileWithTag", + "when": "resourceScheme == file", + "group": "diffy@3" + } ], "explorer/context": [ - { "command": "diffy.compareFileWithCommit", "when": "resourceScheme == file && !explorerResourceIsFolder", "group": "3_compare" } + { + "command": "diffy.compareFileWithCommit", + "when": "resourceScheme == file && !explorerResourceIsFolder", + "group": "diffy@1" + }, + { + "command": "diffy.compareFileWithBranch", + "when": "resourceScheme == file && !explorerResourceIsFolder", + "group": "diffy@2" + }, + { + "command": "diffy.compareFileWithTag", + "when": "resourceScheme == file && !explorerResourceIsFolder", + "group": "diffy@3" + } ], "commandPalette": [ - { "command": "diffy.compareWith", "when": "false" }, - { "command": "diffy.compareWithWorkingCopy", "when": "false" }, - { "command": "diffy.compareWithPrevious", "when": "false" }, - { "command": "diffy.showLogs", "when": "false" } + { + "command": "diffy.compareWith", + "when": "false" + }, + { + "command": "diffy.compareWithWorkingCopy", + "when": "false" + }, + { + "command": "diffy.compareWithPrevious", + "when": "false" + }, + { + "command": "diffy.compareWithBranch", + "when": "false" + }, + { + "command": "diffy.compareWithTag", + "when": "false" + }, + { + "command": "diffy.showLogs", + "when": "false" + } ] } }, "scripts": { - "build": "tsc -p .", + "build": "tsc -p . && node ./scripts/sync-menus.mjs", "watch": "tsc -p . -w", "typecheck": "tsc -p . --noEmit", "lint": "eslint src", "fmt": "prettier --write \"src/**/*.ts\" \"*.json\" \"*.md\"", "fmt:check": "prettier --check \"src/**/*.ts\" \"*.json\" \"*.md\"", + "sync:menus": "tsc -p . && node ./scripts/sync-menus.mjs", + "sync:menus:check": "tsc -p . && node ./scripts/sync-menus.mjs --check", "pretest": "npm run build", "test:unit": "mocha --reporter spec --timeout 10000 \"out/test/unit/**/*.test.js\"", "test:e2e": "node ./out/test/runTests.js", - "test:coverage": "node ./scripts/run-coverage.mjs" + "test:coverage": "node ./scripts/run-coverage.mjs", + "shipwright:validate": "node ./scripts/validate-shipwright-manifest.mjs", + "shipwright:stamp": "node ./scripts/stamp-release-version.mjs", + "shipwright:verify": "node ./scripts/verify-versions.mjs" }, "dependencies": { "pino": "^9.5.0" }, "devDependencies": { "@eslint/js": "^9.13.0", + "ajv": "^8.17.1", + "ajv-formats": "^3.0.1", "@types/mocha": "^10.0.9", "@types/node": "^20.16.0", "@types/vscode": "^1.85.0", @@ -83,7 +220,9 @@ "c8": { "tempDirectory": "coverage/tmp", "reportsDirectory": "coverage/report", - "include": ["out/**/*.js"], + "include": [ + "out/**/*.js" + ], "exclude": [ "out/test/**", "out/scripts/**", @@ -91,6 +230,11 @@ "**/*.d.ts" ], "all": true, - "reporter": ["text", "text-summary", "json-summary", "lcov"] + "reporter": [ + "text", + "text-summary", + "json-summary", + "lcov" + ] } } diff --git a/schemas/shipwright.schema.json b/schemas/shipwright.schema.json new file mode 100644 index 0000000..c82c75b --- /dev/null +++ b/schemas/shipwright.schema.json @@ -0,0 +1,224 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "https://nimblesite.dev/schemas/shipwright/v1.json", + "title": "Nimblesite Shipwright product manifest", + "description": "Authoritative per-product manifest declaring components, bundling, version contracts, and host policies. Lives at repo root of every product as `shipwright.json`.", + "type": "object", + "required": ["manifestVersion", "product", "components"], + "additionalProperties": false, + "properties": { + "manifestVersion": { + "type": "integer", + "const": 1, + "description": "Schema version. Increment on breaking changes." + }, + "product": { + "type": "object", + "required": ["id", "version"], + "additionalProperties": false, + "properties": { + "id": { + "type": "string", + "pattern": "^[a-z0-9][a-z0-9-]{1,63}$", + "description": "Stable product identifier. kebab-case." + }, + "displayName": { "type": "string" }, + "version": { + "type": "string", + "description": "The expected product version this manifest targets. Stamped from tag at release time.", + "pattern": "^[0-9]+\\.[0-9]+\\.[0-9]+(?:-[0-9A-Za-z.-]+)?(?:\\+[0-9A-Za-z.-]+)?$" + }, + "repository": { "type": "string", "format": "uri" }, + "homepage": { "type": "string", "format": "uri" } + } + }, + "components": { + "type": "array", + "minItems": 1, + "description": "Every deployable unit of the product: CLIs, LSPs, MCPs, sidecars, IDE extensions, assets.", + "items": { "$ref": "#/$defs/component" } + }, + "hosts": { + "type": "object", + "description": "Per-host policy bundle. A host that is absent is unsupported by the product.", + "additionalProperties": false, + "properties": { + "vscode": { "$ref": "#/$defs/hostPolicy" }, + "jetbrains": { "$ref": "#/$defs/hostPolicy" }, + "zed": { "$ref": "#/$defs/hostPolicy" }, + "cli": { "$ref": "#/$defs/hostPolicy" }, + "pkgmgr": { "$ref": "#/$defs/hostPolicy" } + } + } + }, + "$defs": { + "component": { + "type": "object", + "required": ["id", "kind"], + "additionalProperties": false, + "properties": { + "id": { + "type": "string", + "pattern": "^[a-z0-9][a-z0-9-]{1,63}$", + "description": "Component id, unique within the product." + }, + "kind": { + "type": "string", + "enum": ["cli", "lsp", "mcp", "sidecar", "dap", "tool", "extension-vscode", "extension-jetbrains", "extension-zed", "asset"] + }, + "language": { + "type": "string", + "enum": ["rust", "dotnet", "dart", "typescript", "kotlin", "javascript"] + }, + "binaryName": { + "type": "string", + "description": "argv[0] of the binary or npm bin / dotnet tool command." + }, + "expectedVersion": { + "type": "string", + "description": "semver string or ${PRODUCT_VERSION}. When set, host must verify this value against binary --version output." + }, + "platforms": { + "type": "array", + "items": { + "type": "string", + "enum": ["darwin-arm64", "darwin-x64", "linux-x64", "linux-arm64", "win32-x64", "win32-arm64", "all"] + }, + "description": "Platforms this component ships for. `all` means platform-agnostic (Node, dotnet tool, jar)." + }, + "bundled": { + "type": "object", + "description": "If set, this component is bundled inside an IDE extension artifact.", + "required": ["bundlePath"], + "additionalProperties": false, + "properties": { + "bundlePath": { + "type": "string", + "description": "Path template relative to extension root, e.g. `bin/${platform}/${binaryName}${exe}`." + }, + "perPlatformArtifact": { + "type": "boolean", + "default": true, + "description": "True = one artifact per platform (VSIX --target). False = single fat artifact." + } + } + }, + "sources": { + "type": "array", + "description": "Ordered discovery chain the host must follow. Earlier = higher priority.", + "items": { + "type": "string", + "enum": ["user-setting", "env", "path", "bundled", "pkgmgr", "dotnet-tool", "npm-global", "cargo-bin", "github-release", "lsp-initialize"] + }, + "uniqueItems": true + }, + "userSetting": { + "type": "string", + "description": "IDE settings key (e.g. `deslop.binaryPath`)." + }, + "env": { + "type": "object", + "additionalProperties": false, + "properties": { + "pathVar": { "type": "string", "description": "e.g. DESLOP_BINARY_PATH" }, + "dirVar": { "type": "string", "description": "e.g. DESLOP_BINARY_DIR" } + } + }, + "pkgmgr": { + "type": "object", + "additionalProperties": false, + "properties": { + "brew": { "type": "string" }, + "scoop": { "type": "string" }, + "apt": { "type": "string" }, + "winget": { "type": "string" } + } + }, + "dotnetTool": { + "type": "object", + "required": ["package"], + "additionalProperties": false, + "properties": { + "package": { "type": "string" }, + "command": { "type": "string" } + } + }, + "npm": { + "type": "object", + "additionalProperties": false, + "properties": { + "package": { "type": "string" }, + "bin": { "type": "string" } + } + }, + "githubRelease": { + "type": "object", + "additionalProperties": false, + "properties": { + "repo": { "type": "string", "pattern": "^[^/]+/[^/]+$" }, + "assetPattern": { "type": "string", "description": "e.g. `basilisk-${version}-${platform}.tar.gz`" }, + "checksum": { "type": "boolean", "default": true }, + "cosign": { "type": "boolean", "default": false } + } + }, + "verifyStartup": { + "type": "boolean", + "default": true, + "description": "Host must call --version (or initialize) before allowing the component to serve." + }, + "versionCheckStrategy": { + "type": "string", + "enum": ["version-flag", "version-flag-json", "lsp-initialize"], + "default": "version-flag" + }, + "required": { + "type": "boolean", + "default": true, + "description": "If true, failure to resolve + verify blocks activation." + }, + "asset": { + "type": "object", + "description": "Only for kind=asset. Non-executable payload.", + "additionalProperties": false, + "properties": { + "source": { "type": "string" }, + "target": { "type": "string" }, + "bundle": { "type": "boolean" }, + "contentHash": { "type": "boolean" }, + "downloadOnFirstUse": { "type": "boolean" } + } + } + }, + "allOf": [ + { + "if": { "properties": { "kind": { "const": "asset" } }, "required": ["kind"] }, + "then": { "required": ["asset"] } + }, + { + "if": { "properties": { "kind": { "enum": ["cli", "lsp", "mcp", "sidecar", "dap", "tool"] } }, "required": ["kind"] }, + "then": { "required": ["binaryName", "expectedVersion", "sources"] } + } + ] + }, + "hostPolicy": { + "type": "object", + "additionalProperties": false, + "properties": { + "artifact": { + "type": "string", + "enum": ["vsix-per-platform", "vsix-fat", "intellij-jar", "zed-wasm", "archive", "brew-formula", "scoop-manifest", "nuget", "pub"] + }, + "activationVerifies": { + "type": "array", + "description": "Component ids whose version the host must verify at activation.", + "items": { "type": "string" } + }, + "onMismatch": { + "type": "string", + "enum": ["error", "warn", "prompt-reinstall", "prompt-pkgmgr"], + "default": "error" + } + } + } + } +} diff --git a/scripts/stamp-release-version.mjs b/scripts/stamp-release-version.mjs new file mode 100644 index 0000000..3a1b48a --- /dev/null +++ b/scripts/stamp-release-version.mjs @@ -0,0 +1,164 @@ +#!/usr/bin/env node +// Shipwright SWR-VERSION-BUILD-STAMPING for Diffy. +// +// Stamps the release version into every deployed carrier in the runner working +// tree, then writes build-info.json. Source-controlled values are not committed +// by this script — the release workflow runs it on the tagged checkout and +// builds from the stamped tree. +// +// Carriers stamped: +// - package.json .version +// - package-lock.json .version + .packages[""].version +// - shipwright.json .product.version +// .components[].expectedVersion (literal values only) +// - build-info.json (created) { manifestVersion, version, buildTime } +// +// Usage: +// node scripts/stamp-release-version.mjs <version> # apply +// node scripts/stamp-release-version.mjs <version> --dry # report only +// +// Accepts a bare semver (1.2.3) or a v-prefixed tag (v1.2.3). Rejects anything +// else so a bad CI input fails the workflow before publish. + +import { readFileSync, writeFileSync, existsSync } from 'node:fs'; +import { resolve, dirname } from 'node:path'; +import { fileURLToPath } from 'node:url'; + +const repoRoot = resolve(dirname(fileURLToPath(import.meta.url)), '..'); + +const SEMVER = /^[0-9]+\.[0-9]+\.[0-9]+(?:-[0-9A-Za-z.-]+)?(?:\+[0-9A-Za-z.-]+)?$/; +const PRODUCT_VERSION_TEMPLATE = '${PRODUCT_VERSION}'; + +const parseArgs = (argv) => { + const positional = []; + let dryRun = false; + for (const arg of argv) { + if (arg === '--dry' || arg === '--dry-run') { + dryRun = true; + } else if (arg.startsWith('-')) { + throw new Error(`unknown argument: ${arg}`); + } else { + positional.push(arg); + } + } + if (positional.length !== 1) { + throw new Error('expected exactly one version argument'); + } + return { rawTag: positional[0], dryRun }; +}; + +const normalizeTag = (tag) => { + const candidate = tag.startsWith('v') ? tag.slice(1) : tag; + if (!SEMVER.test(candidate)) { + throw new Error(`invalid tag '${tag}'; expected semver like v1.2.3 or 1.2.3`); + } + return candidate; +}; + +const readJson = (path) => JSON.parse(readFileSync(path, 'utf8')); + +const writeJson = (path, value, dryRun) => { + const body = `${JSON.stringify(value, null, 2)}\n`; + if (!dryRun) writeFileSync(path, body); +}; + +const stampPackageJson = ({ path, version, dryRun, report }) => { + if (!existsSync(path)) return; + const pkg = readJson(path); + const before = pkg.version; + pkg.version = version; + writeJson(path, pkg, dryRun); + report.push({ path, field: 'version', before, after: version }); +}; + +const stampPackageLock = ({ path, version, dryRun, report }) => { + if (!existsSync(path)) return; + const lock = readJson(path); + const before = lock.version; + lock.version = version; + const rootPkg = lock.packages?.['']; + if (rootPkg) rootPkg.version = version; + writeJson(path, lock, dryRun); + report.push({ path, field: 'version', before, after: version }); +}; + +const stampShipwright = ({ path, version, dryRun, report }) => { + if (!existsSync(path)) return; + const manifest = readJson(path); + const before = manifest.product?.version; + if (!manifest.product) { + throw new Error(`${path} missing product object`); + } + manifest.product.version = version; + for (const component of manifest.components ?? []) { + if ( + typeof component.expectedVersion === 'string' && + component.expectedVersion !== PRODUCT_VERSION_TEMPLATE + ) { + const previous = component.expectedVersion; + component.expectedVersion = version; + report.push({ + path, + field: `components.${component.id}.expectedVersion`, + before: previous, + after: version, + }); + } + } + writeJson(path, manifest, dryRun); + report.push({ path, field: 'product.version', before, after: version }); +}; + +const writeBuildInfo = ({ path, version, dryRun, report }) => { + const info = { + manifestVersion: 1, + version, + buildTime: new Date().toISOString().replace(/\.\d{3}Z$/, 'Z'), + }; + writeJson(path, info, dryRun); + report.push({ path, field: 'created', before: '-', after: version }); +}; + +const main = () => { + const { rawTag, dryRun } = parseArgs(process.argv.slice(2)); + const version = normalizeTag(rawTag); + const report = []; + + stampPackageJson({ + path: resolve(repoRoot, 'package.json'), + version, + dryRun, + report, + }); + stampPackageLock({ + path: resolve(repoRoot, 'package-lock.json'), + version, + dryRun, + report, + }); + stampShipwright({ + path: resolve(repoRoot, 'shipwright.json'), + version, + dryRun, + report, + }); + writeBuildInfo({ + path: resolve(repoRoot, 'build-info.json'), + version, + dryRun, + report, + }); + + const mode = dryRun ? 'dry-run' : 'applied'; + process.stdout.write(`stamp-release-version (${mode}) → ${version}\n`); + for (const row of report) { + process.stdout.write(` ${row.path} :: ${row.field} : ${row.before} → ${row.after}\n`); + } +}; + +try { + main(); +} catch (error) { + process.stderr.write(`stamp-release-version: ${error.message}\n`); + process.exit(1); +} diff --git a/scripts/sync-menus.mjs b/scripts/sync-menus.mjs new file mode 100644 index 0000000..b94a30b --- /dev/null +++ b/scripts/sync-menus.mjs @@ -0,0 +1,75 @@ +#!/usr/bin/env node +import { readFile, writeFile } from 'node:fs/promises'; +import { dirname, resolve } from 'node:path'; +import { fileURLToPath } from 'node:url'; + +const here = dirname(fileURLToPath(import.meta.url)); +const repoRoot = resolve(here, '..'); +const packageJsonPath = resolve(repoRoot, 'package.json'); +const menusModulePath = resolve(repoRoot, 'out/menus.js'); + +const mode = process.argv[2] === '--check' ? 'check' : 'write'; + +const main = async () => { + const mod = await import(pathToFileUrl(menusModulePath)); + const manifest = mod.buildMenuManifest(); + const titles = mod.COMMAND_TITLES; + const pkg = JSON.parse(await readFile(packageJsonPath, 'utf8')); + + const desiredCommands = Object.entries(titles).map(([command, title]) => ({ + command, + title, + })); + + const desiredMenus = {}; + for (const [menuId, entries] of Object.entries(manifest.menus)) { + desiredMenus[menuId] = entries.map(({ command, when, group }) => ({ + command, + when, + group, + })); + } + desiredMenus.commandPalette = manifest.commandPalette.map(({ command, when }) => ({ + command, + when, + })); + + const desiredContributes = { + ...pkg.contributes, + commands: desiredCommands, + menus: desiredMenus, + }; + + const before = JSON.stringify({ + commands: pkg.contributes?.commands, + menus: pkg.contributes?.menus, + }); + const after = JSON.stringify({ + commands: desiredContributes.commands, + menus: desiredContributes.menus, + }); + + if (before === after) { + process.stdout.write('sync-menus: package.json already in sync\n'); + return; + } + + if (mode === 'check') { + process.stderr.write( + 'sync-menus: package.json is out of sync with src/menus.ts.\n' + + 'Run `npm run sync:menus` to regenerate.\n', + ); + process.exit(1); + } + + const next = { ...pkg, contributes: desiredContributes }; + await writeFile(packageJsonPath, JSON.stringify(next, null, 2) + '\n'); + process.stdout.write('sync-menus: wrote package.json\n'); +}; + +const pathToFileUrl = (p) => `file://${p}`; + +main().catch((e) => { + process.stderr.write(`sync-menus failed: ${e?.stack ?? String(e)}\n`); + process.exit(2); +}); diff --git a/scripts/validate-shipwright-manifest.mjs b/scripts/validate-shipwright-manifest.mjs new file mode 100644 index 0000000..1af0e5a --- /dev/null +++ b/scripts/validate-shipwright-manifest.mjs @@ -0,0 +1,44 @@ +#!/usr/bin/env node +// Shipwright SWR-IDE-* / SWR-VERSION-* manifest gate. +// +// Validates shipwright.json against the canonical schema bundled at +// schemas/shipwright.schema.json. Fails closed: any AJV error blocks CI. +// +// Usage: +// node scripts/validate-shipwright-manifest.mjs # validates ./shipwright.json +// node scripts/validate-shipwright-manifest.mjs path.json # validates one manifest + +import { readFileSync } from 'node:fs'; +import { resolve, dirname } from 'node:path'; +import { fileURLToPath } from 'node:url'; +import Ajv2020 from 'ajv/dist/2020.js'; +import addFormats from 'ajv-formats'; + +const repoRoot = resolve(dirname(fileURLToPath(import.meta.url)), '..'); +const schemaPath = resolve(repoRoot, 'schemas', 'shipwright.schema.json'); + +const readJson = (path) => JSON.parse(readFileSync(path, 'utf8')); + +const main = () => { + const target = process.argv[2] ?? resolve(repoRoot, 'shipwright.json'); + const ajv = new Ajv2020({ allErrors: true, strict: false }); + addFormats(ajv); + const validate = ajv.compile(readJson(schemaPath)); + const manifest = readJson(target); + if (validate(manifest)) { + process.stdout.write(`${target}: valid\n`); + return; + } + process.stderr.write(`${target}: invalid\n`); + for (const error of validate.errors ?? []) { + process.stderr.write(` ${error.instancePath || '/'} ${error.message ?? ''}\n`); + } + process.exit(1); +}; + +try { + main(); +} catch (error) { + process.stderr.write(`validate-shipwright-manifest: ${error.message}\n`); + process.exit(2); +} diff --git a/scripts/verify-versions.mjs b/scripts/verify-versions.mjs new file mode 100644 index 0000000..815f4dc --- /dev/null +++ b/scripts/verify-versions.mjs @@ -0,0 +1,134 @@ +#!/usr/bin/env node +// Shipwright SWR-REL-VERSION-VERIFY for Diffy. +// +// After scripts/stamp-release-version.mjs runs, assert that every deployed +// version carrier in the runner working tree agrees with the expected version. +// Exits non-zero on the first mismatch so publish jobs cannot run with a +// half-stamped tree. +// +// Usage: +// node scripts/verify-versions.mjs <version> # bare semver or v-prefixed +// +// Checks: +// - package.json .version +// - package-lock.json .version and .packages[""].version +// - shipwright.json .product.version +// every literal .components[].expectedVersion +// - build-info.json .version +// +// ${PRODUCT_VERSION} placeholders are accepted in expectedVersion fields. + +import { readFileSync, existsSync } from 'node:fs'; +import { resolve, dirname } from 'node:path'; +import { fileURLToPath } from 'node:url'; + +const repoRoot = resolve(dirname(fileURLToPath(import.meta.url)), '..'); +const SEMVER = /^[0-9]+\.[0-9]+\.[0-9]+(?:-[0-9A-Za-z.-]+)?(?:\+[0-9A-Za-z.-]+)?$/; +const PRODUCT_VERSION_TEMPLATE = '${PRODUCT_VERSION}'; + +const normalizeTag = (tag) => { + const candidate = tag.startsWith('v') ? tag.slice(1) : tag; + if (!SEMVER.test(candidate)) { + throw new Error(`invalid tag '${tag}'; expected semver like v1.2.3 or 1.2.3`); + } + return candidate; +}; + +const readJson = (path) => JSON.parse(readFileSync(path, 'utf8')); + +const checks = []; + +const expect = ({ label, actual, expected }) => { + checks.push({ label, actual, expected, ok: actual === expected }); +}; + +const verifyPackageJson = (version) => { + const path = resolve(repoRoot, 'package.json'); + if (!existsSync(path)) return; + const pkg = readJson(path); + expect({ label: 'package.json :: version', actual: pkg.version, expected: version }); +}; + +const verifyPackageLock = (version) => { + const path = resolve(repoRoot, 'package-lock.json'); + if (!existsSync(path)) return; + const lock = readJson(path); + expect({ label: 'package-lock.json :: version', actual: lock.version, expected: version }); + const rootPkg = lock.packages?.['']; + if (rootPkg) { + expect({ + label: 'package-lock.json :: packages[""].version', + actual: rootPkg.version, + expected: version, + }); + } +}; + +const verifyShipwright = (version) => { + const path = resolve(repoRoot, 'shipwright.json'); + if (!existsSync(path)) return; + const manifest = readJson(path); + expect({ + label: 'shipwright.json :: product.version', + actual: manifest.product?.version, + expected: version, + }); + for (const component of manifest.components ?? []) { + if (typeof component.expectedVersion !== 'string') continue; + if (component.expectedVersion === PRODUCT_VERSION_TEMPLATE) continue; + expect({ + label: `shipwright.json :: components.${component.id}.expectedVersion`, + actual: component.expectedVersion, + expected: version, + }); + } +}; + +const verifyBuildInfo = (version) => { + const path = resolve(repoRoot, 'build-info.json'); + if (!existsSync(path)) { + checks.push({ + label: 'build-info.json', + actual: 'missing', + expected: 'present', + ok: false, + }); + return; + } + const info = readJson(path); + expect({ + label: 'build-info.json :: version', + actual: info.version, + expected: version, + }); +}; + +const main = () => { + const [tag] = process.argv.slice(2); + if (!tag) throw new Error('expected version argument'); + const version = normalizeTag(tag); + + verifyPackageJson(version); + verifyPackageLock(version); + verifyShipwright(version); + verifyBuildInfo(version); + + let failed = 0; + for (const c of checks) { + const tag = c.ok ? 'OK ' : 'FAIL'; + process.stdout.write(`${tag} ${c.label} = ${c.actual}\n`); + if (!c.ok) failed += 1; + } + if (failed > 0) { + process.stderr.write(`\nverify-versions: ${failed} mismatch(es) against ${version}\n`); + process.exit(1); + } + process.stdout.write(`\nAll ${checks.length} carriers match ${version}.\n`); +}; + +try { + main(); +} catch (error) { + process.stderr.write(`verify-versions: ${error.message}\n`); + process.exit(2); +} diff --git a/shipwright.json b/shipwright.json new file mode 100644 index 0000000..630cc62 --- /dev/null +++ b/shipwright.json @@ -0,0 +1,32 @@ +{ + "manifestVersion": 1, + "product": { + "id": "diffy", + "displayName": "Diffy", + "version": "0.1.0", + "repository": "https://github.com/MelbourneDeveloper/Diffy", + "homepage": "https://github.com/MelbourneDeveloper/Diffy" + }, + "components": [ + { + "id": "diffy-vscode", + "kind": "extension-vscode", + "language": "typescript", + "expectedVersion": "${PRODUCT_VERSION}", + "platforms": [ + "darwin-arm64", + "darwin-x64", + "linux-arm64", + "linux-x64", + "win32-arm64", + "win32-x64" + ] + } + ], + "hosts": { + "vscode": { + "artifact": "vsix-per-platform", + "onMismatch": "error" + } + } +} diff --git a/src/commands/compareFileWithCommit.ts b/src/commands/compareFileWithCommit.ts index 0e9abb0..7c35b84 100644 --- a/src/commands/compareFileWithCommit.ts +++ b/src/commands/compareFileWithCommit.ts @@ -1,51 +1,5 @@ -import * as vscode from 'vscode'; -import { findRepoForUri } from '../vscodeGitApi'; -import { pickCommit } from '../ui/CommitPicker'; -import { - type CommandDeps, - buildRepo, - openDiff, -} from './shared'; -import { reportGitError, sideAFromSha } from './flow'; - -const resolveTargetUri = (uri?: vscode.Uri): vscode.Uri | undefined => { - if (uri !== undefined) { - return uri; - } - return vscode.window.activeTextEditor?.document.uri; -}; - -const handler = async ( - deps: CommandDeps, - uri: vscode.Uri | undefined, -): Promise<void> => { - const target = resolveTargetUri(uri); - if (target === undefined) { - void vscode.window.showWarningMessage('Diffy: open a file first.'); - return; - } - const vsRepo = findRepoForUri(deps.gitApi, target); - if (vsRepo === undefined) { - void vscode.window.showWarningMessage('Diffy: file is not in a git repository.'); - return; - } - const repo = buildRepo(deps.runner, vsRepo); - const log = await repo.log({}); - if (!log.ok) { - reportGitError({ output: deps.output, op: 'log', e: log.error }); - return; - } - const commit = await pickCommit({ commits: log.value }); - if (!commit.ok) { - return; - } - await openDiff({ - revA: sideAFromSha(commit.value.sha), - revB: { kind: 'workingCopy' }, - repoRoot: vsRepo.rootUri.fsPath, - relPath: vscode.workspace.asRelativePath(target, false), - }); -}; +import type { CommandDeps } from './shared'; +import { makeCompareFileWithRev } from './compareFileWithRev'; export const makeCompareFileWithCommit = (deps: CommandDeps) => - async (uri?: vscode.Uri): Promise<void> => handler(deps, uri); + makeCompareFileWithRev({ deps, source: 'commits' }); diff --git a/src/commands/compareFileWithRev.ts b/src/commands/compareFileWithRev.ts new file mode 100644 index 0000000..dd61d63 --- /dev/null +++ b/src/commands/compareFileWithRev.ts @@ -0,0 +1,87 @@ +import * as vscode from 'vscode'; +import type { GitRepo } from '../git/GitRepo'; +import type { RefType, Sha } from '../git/types'; +import type { Result } from '../result'; +import { err, ok } from '../result'; +import { CANCELLED, type Cancelled } from '../ui/cancelled'; +import { pickCommit } from '../ui/CommitPicker'; +import { findRepoForUri } from '../vscodeGitApi'; +import { + type CommandDeps, + buildRepo, + openDiff, +} from './shared'; +import { pickRefAsSha, reportGitError, sideAFromSha } from './flow'; + +export type FileRevSource = 'commits' | RefType; + +const NO_EDITOR = 'Diffy: open a file first.'; +const NOT_IN_REPO = 'Diffy: file is not in a git repository.'; + +const pickShaForFile = async ({ + repo, + source, + output, +}: { + repo: GitRepo; + source: FileRevSource; + output: vscode.OutputChannel; +}): Promise<Result<Sha, Cancelled>> => { + if (source === 'commits') { + const log = await repo.log({}); + if (!log.ok) { + reportGitError({ output, op: 'log', e: log.error }); + return err(CANCELLED); + } + const picked = await pickCommit({ commits: log.value }); + if (!picked.ok) { + return err(CANCELLED); + } + return ok(picked.value.sha); + } + return pickRefAsSha({ repo, output, filter: source }); +}; + +const resolveTargetUri = (uri?: vscode.Uri): vscode.Uri | undefined => + uri ?? vscode.window.activeTextEditor?.document.uri; + +const handler = async ({ + deps, + uri, + source, +}: { + deps: CommandDeps; + uri: vscode.Uri | undefined; + source: FileRevSource; +}): Promise<void> => { + const target = resolveTargetUri(uri); + if (target === undefined) { + void vscode.window.showWarningMessage(NO_EDITOR); + return; + } + const vsRepo = findRepoForUri(deps.gitApi, target); + if (vsRepo === undefined) { + void vscode.window.showWarningMessage(NOT_IN_REPO); + return; + } + const repo = buildRepo(deps.runner, vsRepo); + const sha = await pickShaForFile({ repo, source, output: deps.output }); + if (!sha.ok) { + return; + } + await openDiff({ + revA: sideAFromSha(sha.value), + revB: { kind: 'workingCopy' }, + repoRoot: vsRepo.rootUri.fsPath, + relPath: vscode.workspace.asRelativePath(target, false), + }); +}; + +export const makeCompareFileWithRev = ({ + deps, + source, +}: { + deps: CommandDeps; + source: FileRevSource; +}) => + async (uri?: vscode.Uri): Promise<void> => handler({ deps, uri, source }); diff --git a/src/commands/compareWithRef.ts b/src/commands/compareWithRef.ts new file mode 100644 index 0000000..56c2b09 --- /dev/null +++ b/src/commands/compareWithRef.ts @@ -0,0 +1,55 @@ +import * as vscode from 'vscode'; +import type { RefType } from '../git/types'; +import type { MementoStore } from '../state'; +import { extractHistoryItemSha } from './historyItem'; +import { + type CommandDeps, + buildRepo, + pickRepoFrom, +} from './shared'; +import { drillIntoFiles, pickRefAsSha, sideAFromSha } from './flow'; + +const NOT_FROM_HISTORY = + 'Diffy: this command must be invoked from the SCM history view.'; + +const handler = async ({ + deps, + arg, + filter, +}: { + deps: CommandDeps & { readonly state: MementoStore }; + arg: unknown; + filter: RefType; +}): Promise<void> => { + const sha = extractHistoryItemSha(arg); + if (sha === undefined) { + void vscode.window.showWarningMessage(NOT_FROM_HISTORY); + return; + } + const vs = await pickRepoFrom(deps.gitApi); + if (!vs.ok) { + return; + } + const repo = buildRepo(deps.runner, vs.value); + const target = await pickRefAsSha({ repo, output: deps.output, filter }); + if (!target.ok) { + return; + } + await drillIntoFiles({ + repo, + repoRoot: vs.value.rootUri.fsPath, + revA: sideAFromSha(sha), + revB: { kind: 'commit', sha: target.value }, + state: deps.state, + output: deps.output, + }); +}; + +export const makeCompareWithRef = ({ + deps, + filter, +}: { + deps: CommandDeps & { readonly state: MementoStore }; + filter: RefType; +}) => + async (arg: unknown): Promise<void> => handler({ deps, arg, filter }); diff --git a/src/commands/flow.ts b/src/commands/flow.ts index 400632b..f74e7b8 100644 --- a/src/commands/flow.ts +++ b/src/commands/flow.ts @@ -2,7 +2,7 @@ import * as vscode from 'vscode'; import type { GitRepo } from '../git/GitRepo'; import type { GitRunner } from '../git/GitRunner'; import type { GitApi } from '../vscodeGitApi'; -import type { GitError, CommitRev, RevSpec, Sha } from '../git/types'; +import type { GitError, CommitRev, RefType, RevSpec, Sha } from '../git/types'; import { logger } from '../logger'; import { type Result, err, ok } from '../result'; import { CANCELLED, type Cancelled } from '../ui/cancelled'; @@ -51,19 +51,34 @@ export const resolveSideB = async ({ return resolveCommitAsRev({ repo, output }); }; -const resolveRefAsRev = async ({ +const placeholderForRefFilter = (filter?: RefType): string => { + if (filter === 'branch') { + return 'Pick a branch'; + } + if (filter === 'tag') { + return 'Pick a tag'; + } + return 'Pick a branch or tag'; +}; + +export const pickRefAsSha = async ({ repo, output, + filter, }: { repo: GitRepo; output: vscode.OutputChannel; -}): Promise<Result<RevSpec, Cancelled>> => { + filter?: RefType; +}): Promise<Result<Sha, Cancelled>> => { const refs = await repo.refs(); if (!refs.ok) { reportGitError({ output, op: 'list refs', e: refs.error }); return err(CANCELLED); } - const picked = await pickRef({ refs: refs.value }); + const args = filter === undefined + ? { refs: refs.value, placeholder: placeholderForRefFilter() } + : { refs: refs.value, placeholder: placeholderForRefFilter(filter), filter }; + const picked = await pickRef(args); if (!picked.ok) { return err(CANCELLED); } @@ -72,6 +87,20 @@ const resolveRefAsRev = async ({ reportGitError({ output, op: 'rev-parse', e: sha.error }); return err(CANCELLED); } + return ok(sha.value); +}; + +const resolveRefAsRev = async ({ + repo, + output, +}: { + repo: GitRepo; + output: vscode.OutputChannel; +}): Promise<Result<RevSpec, Cancelled>> => { + const sha = await pickRefAsSha({ repo, output }); + if (!sha.ok) { + return err(CANCELLED); + } return ok({ kind: 'commit', sha: sha.value }); }; diff --git a/src/constants.ts b/src/constants.ts index 460a3bc..4cb3ae3 100644 --- a/src/constants.ts +++ b/src/constants.ts @@ -6,8 +6,12 @@ export const COMMAND_IDS = { compareWith: 'diffy.compareWith', compareWithWorkingCopy: 'diffy.compareWithWorkingCopy', compareWithPrevious: 'diffy.compareWithPrevious', + compareWithBranch: 'diffy.compareWithBranch', + compareWithTag: 'diffy.compareWithTag', compareTwoCommits: 'diffy.compareTwoCommits', compareFileWithCommit: 'diffy.compareFileWithCommit', + compareFileWithBranch: 'diffy.compareFileWithBranch', + compareFileWithTag: 'diffy.compareFileWithTag', reopenLast: 'diffy.reopenLast', showLogs: 'diffy.showLogs', } as const; diff --git a/src/extension.ts b/src/extension.ts index 4efa7fa..264f8b8 100644 --- a/src/extension.ts +++ b/src/extension.ts @@ -8,9 +8,11 @@ import { createGitRunner } from './git/GitRunner'; import { type GitApi, getGitApi } from './vscodeGitApi'; import { addLogStream, logger } from './logger'; import { makeCompareFileWithCommit } from './commands/compareFileWithCommit'; +import { makeCompareFileWithRev } from './commands/compareFileWithRev'; import { makeCompareTwoCommits } from './commands/compareTwoCommits'; import { makeCompareWith } from './commands/compareWith'; import { makeCompareWithPrevious } from './commands/compareWithPrevious'; +import { makeCompareWithRef } from './commands/compareWithRef'; import { makeCompareWithWorkingCopy } from './commands/compareWithWorkingCopy'; import { makeReopenLast } from './commands/reopenLast'; import { registerDiffyContentProvider } from './providers/DiffyContentProvider'; @@ -74,6 +76,14 @@ const registerAll = ( COMMAND_IDS.compareWithPrevious, makeCompareWithPrevious(deps), ), + vscode.commands.registerCommand( + COMMAND_IDS.compareWithBranch, + makeCompareWithRef({ deps, filter: 'branch' }), + ), + vscode.commands.registerCommand( + COMMAND_IDS.compareWithTag, + makeCompareWithRef({ deps, filter: 'tag' }), + ), vscode.commands.registerCommand( COMMAND_IDS.compareTwoCommits, makeCompareTwoCommits(deps), @@ -82,6 +92,14 @@ const registerAll = ( COMMAND_IDS.compareFileWithCommit, makeCompareFileWithCommit(deps), ), + vscode.commands.registerCommand( + COMMAND_IDS.compareFileWithBranch, + makeCompareFileWithRev({ deps, source: 'branch' }), + ), + vscode.commands.registerCommand( + COMMAND_IDS.compareFileWithTag, + makeCompareFileWithRev({ deps, source: 'tag' }), + ), vscode.commands.registerCommand(COMMAND_IDS.reopenLast, makeReopenLast(deps)), vscode.commands.registerCommand(COMMAND_IDS.showLogs, () => { deps.output.show(true); diff --git a/src/menus.ts b/src/menus.ts new file mode 100644 index 0000000..d5b770e --- /dev/null +++ b/src/menus.ts @@ -0,0 +1,84 @@ +import { COMMAND_IDS } from './constants'; + +export interface MenuEntry { + readonly command: string; + readonly when: string; + readonly group: string; +} + +export interface MenuManifest { + readonly menus: Record<string, readonly MenuEntry[]>; + readonly commandPalette: readonly { readonly command: string; readonly when: string }[]; +} + +const MENU_IDS = { + scmHistoryItem: 'scm/historyItem/context', + scmResourceState: 'scm/resourceState/context', + editorTitleContext: 'editor/title/context', + explorerContext: 'explorer/context', + commandPalette: 'commandPalette', +} as const; + +const WHEN_GIT = 'scmProvider == git'; +const WHEN_FILE = 'resourceScheme == file'; +const WHEN_FILE_NOT_FOLDER = 'resourceScheme == file && !explorerResourceIsFolder'; +const NEVER = 'false'; + +const commitLevelCommands = [ + COMMAND_IDS.compareWith, + COMMAND_IDS.compareWithWorkingCopy, + COMMAND_IDS.compareWithPrevious, + COMMAND_IDS.compareWithBranch, + COMMAND_IDS.compareWithTag, +] as const; + +const fileLevelCommands = [ + COMMAND_IDS.compareFileWithCommit, + COMMAND_IDS.compareFileWithBranch, + COMMAND_IDS.compareFileWithTag, +] as const; + +const groupedEntries = ( + commands: readonly string[], + when: string, + groupPrefix: string, +): readonly MenuEntry[] => + commands.map((command, i) => ({ + command, + when, + group: `${groupPrefix}@${i + 1}`, + })); + +const hideFromPalette = (commands: readonly string[]) => + commands.map((command) => ({ command, when: NEVER })); + +export const buildMenuManifest = (): MenuManifest => ({ + menus: { + [MENU_IDS.scmHistoryItem]: groupedEntries(commitLevelCommands, WHEN_GIT, 'diffy'), + [MENU_IDS.scmResourceState]: groupedEntries(fileLevelCommands, WHEN_GIT, 'diffy'), + [MENU_IDS.editorTitleContext]: groupedEntries(fileLevelCommands, WHEN_FILE, 'diffy'), + [MENU_IDS.explorerContext]: groupedEntries(fileLevelCommands, WHEN_FILE_NOT_FOLDER, 'diffy'), + }, + commandPalette: hideFromPalette([ + COMMAND_IDS.compareWith, + COMMAND_IDS.compareWithWorkingCopy, + COMMAND_IDS.compareWithPrevious, + COMMAND_IDS.compareWithBranch, + COMMAND_IDS.compareWithTag, + COMMAND_IDS.showLogs, + ]), +}); + +export const COMMAND_TITLES: Record<string, string> = { + [COMMAND_IDS.compareWith]: 'Diffy: Compare with…', + [COMMAND_IDS.compareWithWorkingCopy]: 'Diffy: Compare with Working Copy', + [COMMAND_IDS.compareWithPrevious]: 'Diffy: Compare with Previous', + [COMMAND_IDS.compareWithBranch]: 'Diffy: Compare with Branch…', + [COMMAND_IDS.compareWithTag]: 'Diffy: Compare with Tag…', + [COMMAND_IDS.compareTwoCommits]: 'Diffy: Compare Two Commits', + [COMMAND_IDS.compareFileWithCommit]: 'Diffy: Compare with Commit…', + [COMMAND_IDS.compareFileWithBranch]: 'Diffy: Compare with Branch…', + [COMMAND_IDS.compareFileWithTag]: 'Diffy: Compare with Tag…', + [COMMAND_IDS.reopenLast]: 'Diffy: Reopen Last Comparison', + [COMMAND_IDS.showLogs]: 'Diffy: Show Logs', +}; diff --git a/src/test/runTests.ts b/src/test/runTests.ts index bb05dc5..1c8de74 100644 --- a/src/test/runTests.ts +++ b/src/test/runTests.ts @@ -38,7 +38,12 @@ const main = async (): Promise<void> => { extensionDevelopmentPath, extensionTestsPath, extensionTestsEnv, - launchArgs: [workspacePath, '--disable-telemetry'], + launchArgs: [ + workspacePath, + '--disable-telemetry', + '--enable-proposed-api', + 'nimblesite.diffy', + ], }); }; diff --git a/src/test/suite/activation.test.ts b/src/test/suite/activation.test.ts index a41be76..41355bd 100644 --- a/src/test/suite/activation.test.ts +++ b/src/test/suite/activation.test.ts @@ -5,15 +5,7 @@ import { tick } from './helpers'; const EXTENSION_ID = 'nimblesite.diffy'; -const ALL_COMMAND_IDS: readonly string[] = [ - COMMAND_IDS.compareWith, - COMMAND_IDS.compareWithWorkingCopy, - COMMAND_IDS.compareWithPrevious, - COMMAND_IDS.compareTwoCommits, - COMMAND_IDS.compareFileWithCommit, - COMMAND_IDS.reopenLast, - COMMAND_IDS.showLogs, -]; +const ALL_COMMAND_IDS: readonly string[] = Object.values(COMMAND_IDS); describe('activation', () => { it('extension is present, activates, and every command id is registered', async () => { diff --git a/src/test/suite/commands.test.ts b/src/test/suite/commands.test.ts index 02ed665..a4e922b 100644 --- a/src/test/suite/commands.test.ts +++ b/src/test/suite/commands.test.ts @@ -376,4 +376,131 @@ describe('Diffy commands — end-to-end through real QuickPick UI', () => { await dismissQuickPick(); await flow; }); + + it('compareWithBranch(historyItem={id:sha1}) → branch-filtered RefPicker → diff vs main(=sha3)', async () => { + const shas = readSeedShas(); + const flow = vscode.commands.executeCommand(COMMAND_IDS.compareWithBranch, { + id: shas.first, + }); + + // RefPicker with branch filter shows only "main" → accept top + await accept(); + // FilePicker top + await accept(); + + const diffTab = await waitForDiffTab(); + const uris = tabInputUris(diffTab); + assert.equal(uris.left.scheme, 'diffy'); + assert.equal(uris.right.scheme, 'diffy'); + assert.match(uris.left.toString(), new RegExp(`diffy://commit/${shas.first}/`)); + assert.match(uris.right.toString(), new RegExp(`diffy://commit/${shas.third}/`)); + assert.match( + labelStrings(diffTab), + new RegExp(`^${shas.first.slice(0, 7)} ↔ ${shas.third.slice(0, 7)} — `), + ); + + await dismissQuickPick(); + await flow; + }); + + it('compareWithBranch without a historyItem → warning, no diff', async () => { + const before = allDiffTabs().length; + await vscode.commands.executeCommand(COMMAND_IDS.compareWithBranch); + await tick(40); + assert.equal(allDiffTabs().length, before); + }); + + it('compareWithTag(historyItem={id:sha1}) → tag-filtered RefPicker → diff vs v0.1.0(=sha2)', async () => { + const shas = readSeedShas(); + const flow = vscode.commands.executeCommand(COMMAND_IDS.compareWithTag, { + historyItem: { id: shas.first }, + }); + + // RefPicker with tag filter shows only "v0.1.0" → accept top + await accept(); + // FilePicker top + await accept(); + + const diffTab = await waitForDiffTab(); + const uris = tabInputUris(diffTab); + assert.equal(uris.left.scheme, 'diffy'); + assert.equal(uris.right.scheme, 'diffy'); + assert.match(uris.left.toString(), new RegExp(`diffy://commit/${shas.first}/`)); + assert.match(uris.right.toString(), new RegExp(`diffy://commit/${shas.second}/`)); + assert.match( + labelStrings(diffTab), + new RegExp(`^${shas.first.slice(0, 7)} ↔ ${shas.second.slice(0, 7)} — `), + ); + + await dismissQuickPick(); + await flow; + }); + + it('compareWithTag without a historyItem → warning, no diff', async () => { + const before = allDiffTabs().length; + await vscode.commands.executeCommand(COMMAND_IDS.compareWithTag); + await tick(40); + assert.equal(allDiffTabs().length, before); + }); + + it('compareFileWithBranch(uri:a.txt) → RefPicker shows only branches → diff opens for branch HEAD vs working copy', async () => { + const shas = readSeedShas(); + const aTxt = vscode.Uri.file(`${workspaceRoot()}/a.txt`); + const flow = vscode.commands.executeCommand(COMMAND_IDS.compareFileWithBranch, aTxt); + + // RefPicker with branch filter shows only "main" → accept + await accept(); + + const diffTab = await waitForDiffTab(); + const uris = tabInputUris(diffTab); + assert.equal(uris.left.scheme, 'diffy'); + assert.match(uris.left.toString(), new RegExp(`diffy://commit/${shas.third}/a\\.txt$`)); + assert.equal(uris.right.scheme, 'file'); + assert.match(uris.right.fsPath, /a\.txt$/); + assert.match( + labelStrings(diffTab), + new RegExp(`^${shas.third.slice(0, 7)} ↔ Working Copy — a\\.txt$`), + ); + + await flow; + }); + + it('compareFileWithTag(uri:a.txt) → RefPicker shows only tags → diff opens for v0.1.0(=sha2) vs working copy', async () => { + const shas = readSeedShas(); + const aTxt = vscode.Uri.file(`${workspaceRoot()}/a.txt`); + const flow = vscode.commands.executeCommand(COMMAND_IDS.compareFileWithTag, aTxt); + + // RefPicker with tag filter shows only "v0.1.0" → accept + await accept(); + + const diffTab = await waitForDiffTab(); + const uris = tabInputUris(diffTab); + assert.equal(uris.left.scheme, 'diffy'); + assert.match(uris.left.toString(), new RegExp(`diffy://commit/${shas.second}/a\\.txt$`)); + assert.equal(uris.right.scheme, 'file'); + assert.match(uris.right.fsPath, /a\.txt$/); + assert.match( + labelStrings(diffTab), + new RegExp(`^${shas.second.slice(0, 7)} ↔ Working Copy — a\\.txt$`), + ); + + await flow; + }); + + it('compareFileWithBranch with no uri and no active editor → warning, no diff', async () => { + await closeAllEditors(); + await tick(20); + const before = allDiffTabs().length; + await vscode.commands.executeCommand(COMMAND_IDS.compareFileWithBranch); + await tick(40); + assert.equal(allDiffTabs().length, before); + }); + + it('compareFileWithTag with a file outside any repo → warning, no diff', async () => { + const outside = vscode.Uri.file('/tmp/diffy-not-in-any-repo.txt'); + const before = allDiffTabs().length; + await vscode.commands.executeCommand(COMMAND_IDS.compareFileWithTag, outside); + await tick(40); + assert.equal(allDiffTabs().length, before); + }); }); diff --git a/src/test/unit/menus.test.ts b/src/test/unit/menus.test.ts new file mode 100644 index 0000000..c09a549 --- /dev/null +++ b/src/test/unit/menus.test.ts @@ -0,0 +1,154 @@ +import { strict as assert } from 'node:assert'; +import { readFileSync } from 'node:fs'; +import { resolve } from 'node:path'; +import { COMMAND_IDS } from '../../constants'; +import { COMMAND_TITLES, buildMenuManifest } from '../../menus'; + +const repoRoot = resolve(__dirname, '..', '..', '..'); +const readPackageJson = (): Record<string, unknown> => + JSON.parse(readFileSync(resolve(repoRoot, 'package.json'), 'utf8')) as Record< + string, + unknown + >; + +const getContributes = (pkg: Record<string, unknown>): Record<string, unknown> => { + const c = pkg['contributes']; + if (typeof c !== 'object' || c === null) { + throw new Error('package.json: contributes missing'); + } + return c as Record<string, unknown>; +}; + +describe('menu manifest — single source of truth', () => { + it('every COMMAND_ID has a human title in COMMAND_TITLES', () => { + for (const id of Object.values(COMMAND_IDS)) { + assert.ok( + COMMAND_TITLES[id] !== undefined, + `missing title for command ${id}`, + ); + assert.match(COMMAND_TITLES[id]!, /^Diffy: /, `title for ${id} must start with "Diffy: "`); + } + }); + + it('manifest contributes commands to the four user-discoverable menus', () => { + const manifest = buildMenuManifest(); + const menuIds = Object.keys(manifest.menus); + assert.deepEqual( + menuIds.sort(), + [ + 'editor/title/context', + 'explorer/context', + 'scm/historyItem/context', + 'scm/resourceState/context', + ], + 'menu IDs must match VSCode contribution points (note: singular scm/historyItem/context)', + ); + }); + + it('SCM history item menu contains all five commit-level Diffy commands', () => { + const entries = buildMenuManifest().menus['scm/historyItem/context']; + assert.ok(entries !== undefined); + const cmds = entries.map((e) => e.command); + assert.deepEqual(cmds, [ + COMMAND_IDS.compareWith, + COMMAND_IDS.compareWithWorkingCopy, + COMMAND_IDS.compareWithPrevious, + COMMAND_IDS.compareWithBranch, + COMMAND_IDS.compareWithTag, + ]); + for (const e of entries) { + assert.equal(e.when, 'scmProvider == git'); + assert.match(e.group, /^diffy@\d+$/); + } + }); + + it('editor/title/context and explorer/context and scm/resourceState/context all expose the three file-level Diffy commands', () => { + const m = buildMenuManifest().menus; + const fileLevel = [ + COMMAND_IDS.compareFileWithCommit, + COMMAND_IDS.compareFileWithBranch, + COMMAND_IDS.compareFileWithTag, + ]; + for (const menuId of [ + 'editor/title/context', + 'explorer/context', + 'scm/resourceState/context', + ]) { + const entries = m[menuId]; + assert.ok(entries !== undefined, `missing ${menuId}`); + assert.deepEqual( + entries.map((e) => e.command), + fileLevel, + `${menuId} must list the three file-level commands in order`, + ); + } + }); + + it('commandPalette block hides commit-level entries (they need a history-item arg)', () => { + const palette = buildMenuManifest().commandPalette; + const hidden = palette.map((e) => e.command); + for (const cmd of [ + COMMAND_IDS.compareWith, + COMMAND_IDS.compareWithWorkingCopy, + COMMAND_IDS.compareWithPrevious, + COMMAND_IDS.compareWithBranch, + COMMAND_IDS.compareWithTag, + COMMAND_IDS.showLogs, + ]) { + assert.ok(hidden.includes(cmd), `${cmd} should be hidden from the palette`); + } + for (const e of palette) { + assert.equal(e.when, 'false'); + } + }); + + it('package.json contributes.menus is byte-equal to what the manifest would write', () => { + const manifest = buildMenuManifest(); + const pkg = readPackageJson(); + const contributes = getContributes(pkg); + + const pkgMenus = contributes['menus'] as Record<string, unknown>; + assert.ok(pkgMenus !== undefined, 'package.json missing contributes.menus'); + + for (const [menuId, entries] of Object.entries(manifest.menus)) { + const written = pkgMenus[menuId]; + assert.deepEqual( + written, + entries.map((e) => ({ command: e.command, when: e.when, group: e.group })), + `package.json ${menuId} drifted from src/menus.ts — run 'npm run sync:menus'`, + ); + } + + assert.deepEqual( + pkgMenus['commandPalette'], + manifest.commandPalette.map((e) => ({ command: e.command, when: e.when })), + 'package.json commandPalette block drifted from src/menus.ts', + ); + }); + + it('package.json contributes.commands lists every COMMAND_TITLES entry exactly', () => { + const pkg = readPackageJson(); + const contributes = getContributes(pkg); + const cmds = contributes['commands'] as readonly { readonly command: string; readonly title: string }[]; + assert.ok(Array.isArray(cmds)); + + const expected = Object.entries(COMMAND_TITLES).map(([command, title]) => ({ + command, + title, + })); + assert.deepEqual(cmds, expected, 'package.json commands drifted — run npm run sync:menus'); + }); + + it('package.json declares the contribSourceControlHistoryItemMenu proposed API (required for the commit-row context menu)', () => { + const pkg = readPackageJson(); + const proposals = pkg['enabledApiProposals']; + assert.ok( + Array.isArray(proposals), + 'package.json must declare enabledApiProposals so SCM Graph commit menus actually appear', + ); + assert.ok( + (proposals as readonly string[]).includes('contribSourceControlHistoryItemMenu'), + 'enabledApiProposals must include contribSourceControlHistoryItemMenu', + ); + }); +}); diff --git a/src/ui/RefPicker.ts b/src/ui/RefPicker.ts index 6314fde..abac208 100644 --- a/src/ui/RefPicker.ts +++ b/src/ui/RefPicker.ts @@ -1,6 +1,6 @@ import type * as vscode from 'vscode'; import { type Result, map } from '../result'; -import type { Ref } from '../git/types'; +import type { Ref, RefType } from '../git/types'; import { showSinglePick } from './runQuickPick'; import type { Cancelled } from './cancelled'; import { refTypeLabel } from './format/refLabel'; @@ -16,15 +16,24 @@ const toItem = (ref: Ref): RefPickItem => ({ ref, }); +export const filterRefs = ( + refs: readonly Ref[], + filter?: RefType, +): readonly Ref[] => + filter === undefined ? refs : refs.filter((r) => r.type === filter); + export const pickRef = async ({ refs, placeholder, + filter, }: { refs: readonly Ref[]; placeholder?: string; + filter?: RefType; }): Promise<Result<Ref, Cancelled>> => { + const items = filterRefs(refs, filter).map(toItem); const r = await showSinglePick<RefPickItem>({ - items: refs.map(toItem), + items, placeholder: placeholder ?? 'Pick a branch or tag', matchOnDescription: true, matchOnDetail: true, From 33f00b1dbc175aa6de8302093461bfa20172d7cd Mon Sep 17 00:00:00 2001 From: Christian Findlay <16697547+MelbourneDeveloper@users.noreply.github.com> Date: Sun, 24 May 2026 08:15:59 +1000 Subject: [PATCH 05/17] Stuff --- docs/design-system.md | 597 ++++++++++++++++++++++++++++++++++ src/git/GitRepo.ts | 6 +- src/git/GitRunner.ts | 2 +- src/git/parsers.ts | 48 +-- src/test/unit/format.test.ts | 6 +- src/test/unit/menus.test.ts | 2 +- src/test/unit/parsers.test.ts | 76 ++--- src/test/unit/result.test.ts | 12 +- src/test/unit/uri.test.ts | 22 +- src/ui/uri.ts | 20 +- 10 files changed, 694 insertions(+), 97 deletions(-) create mode 100644 docs/design-system.md diff --git a/docs/design-system.md b/docs/design-system.md new file mode 100644 index 0000000..3d533c3 --- /dev/null +++ b/docs/design-system.md @@ -0,0 +1,597 @@ +# Diffy Design System + +This design system defines the visual and content standards for the future Diffy website, documentation pages, marketplace graphics, release notes, and adjacent product collateral. + +It does not define in-extension UI. Diffy remains a native VS Code extension that uses context menus, Command Palette entries, QuickPick, OutputChannel, and `vscode.diff`. + +## Product Position + +Diffy is a developer tool for comparing two git states without leaving the workflow the developer already uses. + +Core promise: + +> Pick two things and diff them. + +Design principles: + +- **Native first:** Present Diffy as a small, precise addition to VS Code, not a replacement shell. +- **Fast orientation:** Help developers understand entry points, command scope, and resulting diffs quickly. +- **Quiet confidence:** Use restrained visual emphasis, clear hierarchy, and practical examples. +- **Diff literacy:** Visual language should make "left vs right", changed files, additions, removals, and revisions immediately legible. +- **No novelty tax:** Avoid playful abstractions when a direct git, file, or VS Code concept would be clearer. + +Audience: + +- Developers reviewing commits, branches, staged work, and working-copy changes. +- Maintainers who need a lightweight extension with no custom panels or persistent UI. +- Teams evaluating the extension from the marketplace, README, or project website. + +## Brand Voice + +Diffy's voice is direct, concise, and work-focused. + +Use: + +- Short verbs: pick, compare, open, reopen, review. +- Concrete nouns: commit, branch, tag, index, working copy, file. +- Plain explanations of scope and constraints. +- Human-readable examples such as `a1b2c3d -> Working Copy - src/foo.ts`. + +Avoid: + +- Marketing claims that imply AI, automation, or code review intelligence. +- Heavy productivity language such as "10x", "revolutionary", or "magic". +- Internal implementation labels in user-facing copy. +- Explaining VS Code basics unless the page is explicitly instructional. + +Example headlines: + +- "Pick two git states and open the diff." +- "Context-menu diffing for VS Code." +- "Compare commits, branches, tags, the index, and your working copy." + +Example body copy: + +> Diffy adds focused compare commands to the VS Code surfaces you already use: SCM history, SCM changes, editor tabs, Explorer, and the Command Palette. + +## Logo Direction + +The Diffy mark should communicate comparison, pairing, and code review without looking like a separate IDE. + +Preferred concepts: + +- Two adjacent file panes with a narrow comparison gutter. +- A compact `D` monogram split into left and right halves. +- A restrained angle-bracket or bracket motif that suggests code without becoming a generic developer logo. + +Do: + +- Keep the icon readable at 16px, 32px, and 128px. +- Prefer a simple geometric shape with one accent color. +- Test against light, dark, and marketplace backgrounds. + +Do not: + +- Use VS Code product marks or GitHub marks as part of the logo. +- Build the mark around a custom sidebar, panel, activity icon, or webview concept. +- Use a complex multi-color illustration for the extension icon. + +## Color + +The palette balances a neutral developer-tool foundation with semantic diff colors. Blue is used as the primary action color, but the system should not become a single-hue blue interface. + +### Core Palette + +| Token | Hex | Use | +| ------------ | --------- | ------------------------------------ | +| `ink.950` | `#14161a` | Primary text, dark UI foundation | +| `ink.800` | `#272c33` | Secondary text on light surfaces | +| `ink.600` | `#5b6470` | Muted text, metadata | +| `ink.300` | `#b8c0ca` | Borders on dark surfaces | +| `ink.100` | `#e6e9ee` | Borders on light surfaces | +| `paper.000` | `#ffffff` | Primary page surface | +| `paper.050` | `#f7f8fa` | Alternate section background | +| `paper.100` | `#eef1f5` | Code-block and table backgrounds | +| `blue.600` | `#2563eb` | Primary links and actions | +| `blue.700` | `#1d4ed8` | Action hover state | +| `cyan.500` | `#0891b2` | Secondary accent, command highlights | +| `green.600` | `#16a34a` | Additions, success | +| `red.600` | `#dc2626` | Deletions, errors | +| `amber.500` | `#d97706` | Warnings, changed-state emphasis | +| `violet.600` | `#7c3aed` | Rare accent for branch/tag callouts | + +### Semantic Tokens + +| Token | Hex | Use | +| ---------------- | --------- | --------------------------------- | +| `text.primary` | `#14161a` | Default body text | +| `text.secondary` | `#5b6470` | Metadata and helper text | +| `surface.page` | `#ffffff` | Page background | +| `surface.subtle` | `#f7f8fa` | Alternating content bands | +| `surface.code` | `#eef1f5` | Inline code and examples | +| `border.default` | `#e6e9ee` | Default border | +| `action.primary` | `#2563eb` | Primary buttons and links | +| `diff.addition` | `#16a34a` | Added lines and `+N` indicators | +| `diff.deletion` | `#dc2626` | Deleted lines and `-N` indicators | +| `diff.modified` | `#d97706` | Modified files | +| `diff.rename` | `#0891b2` | Renamed or copied files | + +### Usage Rules + +- Body surfaces should stay mostly white or near-white. +- Use dark surfaces sparingly for code, terminal-style examples, or first-viewport contrast. +- Use green and red only when the meaning is addition/deletion, pass/fail, or success/error. +- Never rely on color alone for diff status. Pair color with text, symbols, or position. +- Maintain WCAG AA contrast for text and interactive states. + +## Typography + +### Font Stack + +Website UI: + +```css +font-family: + Inter, + ui-sans-serif, + system-ui, + -apple-system, + BlinkMacSystemFont, + "Segoe UI", + sans-serif; +``` + +Code, command IDs, SHAs, file paths: + +```css +font-family: "JetBrains Mono", "SFMono-Regular", Consolas, "Liberation Mono", monospace; +``` + +### Type Scale + +| Role | Size | Line height | Weight | Use | +| ------- | ---: | ----------: | -----: | ------------------------------ | +| Display | 48px | 56px | 700 | Homepage H1 only | +| H1 | 40px | 48px | 700 | Major page title | +| H2 | 30px | 38px | 650 | Major sections | +| H3 | 22px | 30px | 650 | Subsections and feature groups | +| Body | 16px | 26px | 400 | Default reading text | +| Small | 14px | 22px | 400 | Metadata and helper text | +| Code | 14px | 22px | 500 | Paths, command IDs, examples | +| Caption | 12px | 18px | 500 | Labels, badges, status chips | + +Rules: + +- Do not scale type with viewport width. +- Use `letter-spacing: 0`. +- Keep display type for true hero areas only. +- Keep docs content readable with a maximum line length near 72 characters. + +## Layout + +### Page Widths + +| Token | Value | Use | +| ------------------- | -------: | ------------------------------------ | +| `container.reading` | `760px` | Docs, changelog, long-form content | +| `container.content` | `1120px` | Marketing sections and feature grids | +| `container.wide` | `1280px` | Screenshots, comparison diagrams | +| `gutter.mobile` | `20px` | Mobile horizontal padding | +| `gutter.desktop` | `32px` | Desktop horizontal padding | + +### Spacing Scale + +Use an 8px spacing base. + +| Token | Value | +| --------- | -----: | +| `space.1` | `4px` | +| `space.2` | `8px` | +| `space.3` | `12px` | +| `space.4` | `16px` | +| `space.5` | `24px` | +| `space.6` | `32px` | +| `space.7` | `48px` | +| `space.8` | `64px` | +| `space.9` | `96px` | + +### Layout Rules + +- Use full-width page bands with constrained inner content. +- Do not put page sections inside floating cards. +- Cards are for repeated items, command references, release entries, and framed examples. +- Keep card radius at `8px` or less. +- Every fixed-format UI mock should have stable dimensions to prevent layout shift. +- On the homepage, the first viewport should show the product name, the promise, a real product-oriented visual, and a hint of the next section. + +## Imagery + +Website imagery should show the actual developer workflow. + +Preferred assets: + +- Clean screenshots of VS Code SCM history context menus with Diffy commands. +- File QuickPick screenshots showing changed files and stats. +- `vscode.diff` screenshots with readable left/right labels. +- Simple diagrams showing Side A, Side B, and file selection flow. +- Marketplace graphics that pair the Diffy mark with a real diff or command surface. + +Avoid: + +- Abstract gradient backgrounds without product context. +- Decorative code rain, fake dashboards, or invented web app panels. +- Blurred screenshots that hide the actual command labels. +- Cropped imagery where the user cannot identify VS Code, SCM history, QuickPick, or diff tabs. + +Screenshot treatment: + +- Use native VS Code themes for product screenshots. +- Prefer a light VS Code screenshot on a white or subtle surface. +- Provide dark-theme variants only when needed. +- Annotate sparingly with small numbered callouts or simple labels. + +## Icons + +Use a consistent outline icon set for the website, such as Lucide, when building the web UI. + +Recommended icon mappings: + +| Concept | Icon | +| ------------- | --------------------- | +| Compare | `GitCompare` | +| Commit | `GitCommitHorizontal` | +| Branch | `GitBranch` | +| Tag | `Tag` | +| File | `FileText` | +| Working copy | `FolderGit2` | +| Index/staging | `ListChecks` | +| Reopen | `History` | +| Logs | `ScrollText` | +| External link | `ExternalLink` | + +Rules: + +- Icons support recognition; they do not replace precise labels for commands. +- Icon buttons need accessible names and hover tooltips. +- Keep icon stroke width visually aligned with text weight. +- Do not introduce custom symbols for standard git concepts when a common icon exists. + +## Components + +### Header + +Purpose: + +- Orient users. +- Provide links to docs, GitHub, marketplace, releases, and install instructions. + +Structure: + +- Left: Diffy mark and wordmark. +- Center or right: Docs, GitHub, Releases. +- Right: primary install action. + +Behavior: + +- Header should be compact and sticky only on docs pages. +- On mobile, collapse links into a menu with clear labels. + +### Hero + +Purpose: + +- State what Diffy does and show it in context. + +Required content: + +- H1: `Diffy`. +- Supporting copy that includes "Pick two things and diff them." +- Primary action: install or marketplace link. +- Secondary action: view docs or GitHub. +- Product visual: screenshot or interaction mock of context-menu -> QuickPick -> diff. + +Rules: + +- Do not put the hero text in a card. +- Avoid split layouts where the screenshot is an ornamental side panel. +- The hero should leave a hint of the next section visible on common desktop and mobile viewports. + +### Command Matrix + +Purpose: + +- Make every entry point scannable. + +Fields: + +- Surface: SCM history, SCM changes, editor tab, Explorer, Command Palette. +- Command label. +- Side A. +- Side B. +- Result. + +Example: + +| Surface | Command | Side A | Side B | Result | +| ----------- | ------------------------------- | ------------- | ------------------------------------------- | ---------------------- | +| SCM history | `Diffy: Compare with...` | Picked commit | Commit, branch, tag, index, or working copy | File picker, then diff | +| Editor tab | `Diffy: Compare with Commit...` | Picked commit | Current file | Single-file diff | + +### Workflow Diagram + +Purpose: + +- Explain the flow without adding a fake product UI. + +Preferred shape: + +```text +Choose Side A -> Choose Side B -> Pick changed file -> Open VS Code diff +``` + +Rules: + +- Use real command labels and real git concepts. +- Keep diagrams horizontal on desktop and stacked on mobile. +- Avoid swimlanes unless the page needs implementation depth. + +### Feature Cards + +Use cards for repeated feature summaries only. + +Card anatomy: + +- Icon. +- Short title. +- One sentence of body text. +- Optional command label or screenshot thumbnail. + +Feature card examples: + +- "Context-menu first" +- "Compare against working copy" +- "Review changed files without leaving QuickPick" +- "Reopen the last comparison" + +### Code And Command Blocks + +Use code blocks for commands, command IDs, and examples. + +Rules: + +- Keep examples copyable. +- Do not use terminal prompts unless needed. +- Annotate command examples outside the block rather than inside comments. + +Example: + +```sh +make package +``` + +### Status And Diff Badges + +Use small badges for file states and diff stats. + +| Status | Label | Token | +| -------- | ----- | --------------- | +| Added | `A` | `diff.addition` | +| Modified | `M` | `diff.modified` | +| Deleted | `D` | `diff.deletion` | +| Renamed | `R` | `diff.rename` | +| Copied | `C` | `diff.rename` | + +Stats format: + +```text ++24 -8 +``` + +Rules: + +- Pair `+N` with addition color and `-N` with deletion color. +- Preserve a readable text form for accessibility. +- Keep badges small and stable in width for compact tables. + +### Tables + +Use tables for command references and capability matrices. + +Rules: + +- Prefer direct labels over explanatory prose inside cells. +- Use monospace for command IDs, SHAs, and file paths. +- Keep columns narrow enough for mobile wrapping. + +### Callouts + +Use callouts for important constraints, not decoration. + +Types: + +- Note: neutral guidance. +- Warning: compatibility, installation, or setup caveats. +- Constraint: product boundaries such as "No panels or webviews." + +Constraint callout example: + +> Diffy does not add a sidebar, activity-bar icon, tree view, or webview. It uses existing VS Code surfaces. + +## Motion + +Motion should clarify cause and effect. + +Use: + +- 120ms to 180ms hover and focus transitions. +- Short reveal animations for diagrams or command flows. +- Reduced-motion fallbacks for all nonessential movement. + +Avoid: + +- Long looping hero animations. +- Animated backgrounds behind reading text. +- Motion that implies the extension has a custom persistent UI. + +## Accessibility + +Baseline: + +- WCAG 2.2 AA for website pages. +- Keyboard-accessible navigation and controls. +- Visible focus rings on every interactive element. +- Alt text for screenshots that identifies the VS Code surface and command shown. +- Text alternatives for diagrams. +- Color is never the only indicator of diff status. + +Focus ring: + +```css +outline: 2px solid #2563eb; +outline-offset: 2px; +``` + +Reduced motion: + +```css +@media (prefers-reduced-motion: reduce) { + *, + *::before, + *::after { + animation-duration: 0.01ms !important; + animation-iteration-count: 1 !important; + scroll-behavior: auto !important; + transition-duration: 0.01ms !important; + } +} +``` + +## Content Patterns + +### Product One-Liner + +Use this where space is limited: + +> Context-menu git diffing in VS Code. + +### Short Description + +Use this for marketplace and social metadata: + +> Diffy lets you pick two git states and open file diffs through VS Code's native compare experience. + +### Longer Description + +Use this for the website intro: + +> Diffy adds focused compare commands to VS Code's existing SCM history, SCM changes, editor tab, Explorer, and Command Palette surfaces. Pick a commit, branch, tag, index, or working copy target, then open changed files in VS Code's built-in diff editor. + +### SEO Title Pattern + +```text +Diffy - Context-menu git diffing for VS Code +``` + +### Social Description Pattern + +```text +Pick two git states and open native VS Code diffs from SCM history, Explorer, editor tabs, or the Command Palette. +``` + +## Website Information Architecture + +Recommended initial website: + +- Home: product promise, command flow, install action, screenshots. +- Docs: command reference, workflows, requirements, troubleshooting. +- Changelog: release notes and migration notes. +- Privacy: no telemetry statement for v1 unless this changes. +- GitHub/Marketplace links: external destinations, not duplicated pages. + +Homepage section order: + +1. Hero with product visual. +2. Command surfaces overview. +3. Side A / Side B comparison flow. +4. Screenshots of real VS Code surfaces. +5. Installation and requirements. +6. Links to docs, changelog, and GitHub. + +Docs page groups: + +- Getting started. +- Command reference. +- Comparing commits. +- Comparing files. +- Reopening the last comparison. +- Troubleshooting. +- Development links. + +## CSS Token Starter + +Use these as the first pass for a future web implementation. + +```css +:root { + --diffy-ink-950: #14161a; + --diffy-ink-800: #272c33; + --diffy-ink-600: #5b6470; + --diffy-ink-300: #b8c0ca; + --diffy-ink-100: #e6e9ee; + + --diffy-paper-000: #ffffff; + --diffy-paper-050: #f7f8fa; + --diffy-paper-100: #eef1f5; + + --diffy-blue-600: #2563eb; + --diffy-blue-700: #1d4ed8; + --diffy-cyan-500: #0891b2; + --diffy-green-600: #16a34a; + --diffy-red-600: #dc2626; + --diffy-amber-500: #d97706; + --diffy-violet-600: #7c3aed; + + --diffy-text-primary: var(--diffy-ink-950); + --diffy-text-secondary: var(--diffy-ink-600); + --diffy-surface-page: var(--diffy-paper-000); + --diffy-surface-subtle: var(--diffy-paper-050); + --diffy-surface-code: var(--diffy-paper-100); + --diffy-border-default: var(--diffy-ink-100); + --diffy-action-primary: var(--diffy-blue-600); + --diffy-action-primary-hover: var(--diffy-blue-700); + --diffy-diff-addition: var(--diffy-green-600); + --diffy-diff-deletion: var(--diffy-red-600); + --diffy-diff-modified: var(--diffy-amber-500); + --diffy-diff-rename: var(--diffy-cyan-500); + + --diffy-radius-sm: 4px; + --diffy-radius-md: 8px; + --diffy-shadow-soft: 0 12px 30px rgb(20 22 26 / 10%); + + --diffy-container-reading: 760px; + --diffy-container-content: 1120px; + --diffy-container-wide: 1280px; + + --diffy-space-1: 4px; + --diffy-space-2: 8px; + --diffy-space-3: 12px; + --diffy-space-4: 16px; + --diffy-space-5: 24px; + --diffy-space-6: 32px; + --diffy-space-7: 48px; + --diffy-space-8: 64px; + --diffy-space-9: 96px; +} +``` + +## Governance + +Before publishing a new website page or asset, check: + +- The page reinforces the core promise: "Pick two things and diff them." +- Product screenshots show real VS Code surfaces. +- The page does not imply a custom panel, sidebar, webview, AI feature, or standalone app. +- Diff colors are semantic and accessible. +- Command labels match the extension manifest and README. +- Installation, requirements, and GitHub links are current. +- Copy stays direct, concise, and developer-facing. diff --git a/src/git/GitRepo.ts b/src/git/GitRepo.ts index d992ef9..73003e9 100644 --- a/src/git/GitRepo.ts +++ b/src/git/GitRepo.ts @@ -50,8 +50,8 @@ const buildLogArgs = (params: { limit: number; ref?: string }): readonly string[ const buildDiffArgs = ({ from, to }: DiffSides, subcommand: 'name-status' | 'numstat'): readonly string[] => { const head = ['diff', `--${subcommand}`, '-z', '--find-renames', '--find-copies']; - if (to.kind === 'commit') return [...head, from.sha, to.sha]; - if (to.kind === 'workingCopy') return [...head, from.sha]; + if (to.kind === 'commit') {return [...head, from.sha, to.sha];} + if (to.kind === 'workingCopy') {return [...head, from.sha];} return [...head, from.sha, '--cached']; }; @@ -60,7 +60,7 @@ const showSpec = ({ rev, path }: ShowArgs): string => const trimSha = (stdout: string): Result<Sha, GitError> => { const sha = stdout.trim(); - if (sha.length === 0) return err({ kind: 'parseError', message: 'revParse: empty output' }); + if (sha.length === 0) {return err({ kind: 'parseError', message: 'revParse: empty output' });} return ok(sha); }; diff --git a/src/git/GitRunner.ts b/src/git/GitRunner.ts index 881c27a..c47fcd1 100644 --- a/src/git/GitRunner.ts +++ b/src/git/GitRunner.ts @@ -29,7 +29,7 @@ const finishRun = (params: { stdout: string; stderr: string; }): Result<string, GitError> => { - if (params.code === 0) return ok(params.stdout); + if (params.code === 0) {return ok(params.stdout);} return err({ kind: 'nonZeroExit', message: `git ${params.subcommand} exited ${params.code ?? '?'}`, diff --git a/src/git/parsers.ts b/src/git/parsers.ts index 54dc8e0..f79d631 100644 --- a/src/git/parsers.ts +++ b/src/git/parsers.ts @@ -20,23 +20,23 @@ const errParse = (message: string): Result<never, GitError> => err({ kind: 'parseError', message }); const stripTrailingEmpty = (arr: readonly string[]): readonly string[] => { - if (arr.length === 0) return arr; + if (arr.length === 0) {return arr;} const last = arr[arr.length - 1]; return last === '' ? arr.slice(0, -1) : arr; }; const isAllDigits = (s: string): boolean => { - if (s.length === 0) return false; + if (s.length === 0) {return false;} for (let i = 0; i < s.length; i++) { const c = s.charCodeAt(i); - if (c < CHAR_CODE_DIGIT_LO || c > CHAR_CODE_DIGIT_HI) return false; + if (c < CHAR_CODE_DIGIT_LO || c > CHAR_CODE_DIGIT_HI) {return false;} } return true; }; const refTypeFromName = (fullName: string): RefType => { - if (fullName.startsWith(REF_PREFIX_HEADS)) return 'branch'; - if (fullName.startsWith(REF_PREFIX_TAGS)) return 'tag'; + if (fullName.startsWith(REF_PREFIX_HEADS)) {return 'branch';} + if (fullName.startsWith(REF_PREFIX_TAGS)) {return 'tag';} return 'other'; }; @@ -51,13 +51,13 @@ const parseLogRecord = (fields: readonly string[]): Result<Commit, GitError> => ) { return errParse('parseLog: missing field'); } - if (!isAllDigits(atStr)) return errParse('parseLog: invalid timestamp'); + if (!isAllDigits(atStr)) {return errParse('parseLog: invalid timestamp');} const authorTime = Number.parseInt(atStr, 10); return ok({ sha, shortSha, author, authorTime, subject }); }; export const parseLog = (stdout: string): Result<readonly Commit[], GitError> => { - if (stdout.length === 0) return ok([]); + if (stdout.length === 0) {return ok([]);} const tokens = stripTrailingEmpty(stdout.split(NUL)); if (tokens.length % LOG_FIELDS_PER_RECORD !== 0) { return errParse('parseLog: field count not multiple of 5'); @@ -66,7 +66,7 @@ export const parseLog = (stdout: string): Result<readonly Commit[], GitError> => for (let i = 0; i < tokens.length; i += LOG_FIELDS_PER_RECORD) { const slice = tokens.slice(i, i + LOG_FIELDS_PER_RECORD); const r = parseLogRecord(slice); - if (!r.ok) return r; + if (!r.ok) {return r;} commits.push(r.value); } return ok(commits); @@ -79,15 +79,15 @@ interface ParsedStatus { } const parseStatusToken = (raw: string): Result<ParsedStatus, GitError> => { - if (raw.length === 0) return errParse('parseNameStatus: empty status token'); + if (raw.length === 0) {return errParse('parseNameStatus: empty status token');} const first = raw.charAt(0); if (first === 'A' || first === 'M' || first === 'D') { - if (raw.length !== 1) return errParse('parseNameStatus: extra chars on status'); + if (raw.length !== 1) {return errParse('parseNameStatus: extra chars on status');} return ok({ status: first, isRename: false }); } if (first === 'R' || first === 'C') { const digits = raw.slice(1); - if (!isAllDigits(digits)) return errParse('parseNameStatus: bad similarity'); + if (!isAllDigits(digits)) {return errParse('parseNameStatus: bad similarity');} return ok({ status: first, similarity: Number.parseInt(digits, 10), isRename: true }); } return errParse(`parseNameStatus: unknown status '${first}'`); @@ -99,7 +99,7 @@ const buildSimple = ( i: number, ): Result<{ file: ChangedFile; next: number }, GitError> => { const path = tokens[i + 1]; - if (path === undefined) return errParse('parseNameStatus: missing path'); + if (path === undefined) {return errParse('parseNameStatus: missing path');} return ok({ file: { status: parsed.status, path }, next: i + 2 }); }; @@ -114,7 +114,7 @@ const buildRenameOrCopy = ( return errParse('parseNameStatus: rename missing paths'); } const similarity = parsed.similarity; - if (similarity === undefined) return errParse('parseNameStatus: rename missing similarity'); + if (similarity === undefined) {return errParse('parseNameStatus: rename missing similarity');} return ok({ file: { status: parsed.status, path: newPath, oldPath, similarity }, next: i + 3, @@ -126,9 +126,9 @@ const readOneNameStatus = ( i: number, ): Result<{ file: ChangedFile; next: number }, GitError> => { const head = tokens[i]; - if (head === undefined) return errParse('parseNameStatus: missing status'); + if (head === undefined) {return errParse('parseNameStatus: missing status');} const parsed = parseStatusToken(head); - if (!parsed.ok) return parsed; + if (!parsed.ok) {return parsed;} return parsed.value.isRename ? buildRenameOrCopy(parsed.value, tokens, i) : buildSimple(parsed.value, tokens, i); @@ -137,13 +137,13 @@ const readOneNameStatus = ( export const parseNameStatus = ( stdout: string, ): Result<readonly ChangedFile[], GitError> => { - if (stdout.length === 0) return ok([]); + if (stdout.length === 0) {return ok([]);} const tokens = stripTrailingEmpty(stdout.split(NUL)); const out: ChangedFile[] = []; let i = 0; while (i < tokens.length) { const r = readOneNameStatus(tokens, i); - if (!r.ok) return r; + if (!r.ok) {return r;} out.push(r.value.file); i = r.value.next; } @@ -166,7 +166,7 @@ const splitNumstatHead = (head: string): Result<NumstatHead, GitError> => { if (a === undefined || d === undefined || p === undefined) { return errParse('parseNumstat: missing tab fields'); } - if (a === '-' && d === '-') return ok({ added: 0, deleted: 0, binary: true, pathField: p }); + if (a === '-' && d === '-') {return ok({ added: 0, deleted: 0, binary: true, pathField: p });} if (!isAllDigits(a) || !isAllDigits(d)) { return errParse('parseNumstat: non-numeric counts'); } @@ -183,9 +183,9 @@ const readOneNumstat = ( i: number, ): Result<{ stat: DiffStat; next: number }, GitError> => { const head = tokens[i]; - if (head === undefined) return errParse('parseNumstat: missing record'); + if (head === undefined) {return errParse('parseNumstat: missing record');} const fields = splitNumstatHead(head); - if (!fields.ok) return fields; + if (!fields.ok) {return fields;} const { added, deleted, binary, pathField } = fields.value; if (pathField !== '') { return ok({ stat: { path: pathField, added, deleted, binary }, next: i + 1 }); @@ -202,13 +202,13 @@ const readOneNumstat = ( }; export const parseNumstat = (stdout: string): Result<readonly DiffStat[], GitError> => { - if (stdout.length === 0) return ok([]); + if (stdout.length === 0) {return ok([]);} const tokens = stripTrailingEmpty(stdout.split(NUL)); const out: DiffStat[] = []; let i = 0; while (i < tokens.length) { const r = readOneNumstat(tokens, i); - if (!r.ok) return r; + if (!r.ok) {return r;} out.push(r.value.stat); i = r.value.next; } @@ -227,7 +227,7 @@ const parseRefRecord = (fields: readonly string[]): Result<Ref, GitError> => { }; export const parseRefs = (stdout: string): Result<readonly Ref[], GitError> => { - if (stdout.length === 0) return ok([]); + if (stdout.length === 0) {return ok([]);} const lines = stdout.split(LF).filter((l) => l.length > 0); const refs: Ref[] = []; for (const line of lines) { @@ -236,7 +236,7 @@ export const parseRefs = (stdout: string): Result<readonly Ref[], GitError> => { return errParse('parseRefs: expected 3 NUL-separated fields per line'); } const r = parseRefRecord(fields); - if (!r.ok) return r; + if (!r.ok) {return r;} refs.push(r.value); } return ok(refs); diff --git a/src/test/unit/format.test.ts b/src/test/unit/format.test.ts index 664a0d0..88a9942 100644 --- a/src/test/unit/format.test.ts +++ b/src/test/unit/format.test.ts @@ -43,9 +43,9 @@ describe('formatRelative', () => { describe('statusBadge', () => { it('returns single letter for A/M/D', () => { - assert.equal(statusBadge({ status: 'A', path: 'x' } as ChangedFile), 'A'); - assert.equal(statusBadge({ status: 'M', path: 'x' } as ChangedFile), 'M'); - assert.equal(statusBadge({ status: 'D', path: 'x' } as ChangedFile), 'D'); + assert.equal(statusBadge({ status: 'A', path: 'x' }), 'A'); + assert.equal(statusBadge({ status: 'M', path: 'x' }), 'M'); + assert.equal(statusBadge({ status: 'D', path: 'x' }), 'D'); }); it('appends similarity for R', () => { diff --git a/src/test/unit/menus.test.ts b/src/test/unit/menus.test.ts index c09a549..2738abc 100644 --- a/src/test/unit/menus.test.ts +++ b/src/test/unit/menus.test.ts @@ -26,7 +26,7 @@ describe('menu manifest — single source of truth', () => { COMMAND_TITLES[id] !== undefined, `missing title for command ${id}`, ); - assert.match(COMMAND_TITLES[id]!, /^Diffy: /, `title for ${id} must start with "Diffy: "`); + assert.match(COMMAND_TITLES[id], /^Diffy: /, `title for ${id} must start with "Diffy: "`); } }); diff --git a/src/test/unit/parsers.test.ts b/src/test/unit/parsers.test.ts index 258414a..1456b82 100644 --- a/src/test/unit/parsers.test.ts +++ b/src/test/unit/parsers.test.ts @@ -21,14 +21,14 @@ describe('parseLog', () => { it('returns empty array for empty input', () => { const r = parseLog(''); assert.equal(r.ok, true); - if (r.ok) assert.deepEqual(r.value, []); + if (r.ok) {assert.deepEqual(r.value, []);} }); it('parses a single commit with trailing NUL', () => { const stdout = logRecord('abc123def456', 'abc123d', 'Alice', '1700000000', 'init') + NUL; const r = parseLog(stdout); assert.equal(r.ok, true); - if (!r.ok) return; + if (!r.ok) {return;} assert.equal(r.value.length, 1); assert.deepEqual(r.value[0], { sha: 'abc123def456', @@ -44,7 +44,7 @@ describe('parseLog', () => { const b = logRecord('bbbb2222', 'bbbb222', 'Bob', '1700000100', 'second commit'); const r = parseLog(`${a}${NUL}${b}${NUL}`); assert.equal(r.ok, true); - if (!r.ok) return; + if (!r.ok) {return;} assert.equal(r.value.length, 2); assert.equal(r.value[0]?.sha, 'aaaa1111'); assert.equal(r.value[0]?.subject, 'first'); @@ -58,14 +58,14 @@ describe('parseLog', () => { const stdout = logRecord('abcd1234', 'abcd123', 'Alice', '1700000000', 'init'); const r = parseLog(stdout); assert.equal(r.ok, true); - if (r.ok) assert.equal(r.value.length, 1); + if (r.ok) {assert.equal(r.value.length, 1);} }); it('preserves an empty subject', () => { const stdout = logRecord('abcd1234', 'abcd123', 'Alice', '1700000000', '') + NUL; const r = parseLog(stdout); assert.equal(r.ok, true); - if (r.ok) assert.equal(r.value[0]?.subject, ''); + if (r.ok) {assert.equal(r.value[0]?.subject, '');} }); it('preserves unicode and spaces in author/subject', () => { @@ -73,7 +73,7 @@ describe('parseLog', () => { logRecord('abcd1234', 'abcd123', 'Élise Müller', '1700000000', 'feat: 日本語 fix ⚡') + NUL; const r = parseLog(stdout); assert.equal(r.ok, true); - if (!r.ok) return; + if (!r.ok) {return;} assert.equal(r.value[0]?.author, 'Élise Müller'); assert.equal(r.value[0]?.subject, 'feat: 日本語 fix ⚡'); }); @@ -91,14 +91,14 @@ describe('parseLog', () => { const stdout = logRecord('a', 'a', 'Alice', 'NOT_A_NUMBER', 'x') + NUL; const r = parseLog(stdout); assert.equal(r.ok, false); - if (!r.ok) assert.match(r.error.message, /timestamp/); + if (!r.ok) {assert.match(r.error.message, /timestamp/);} }); it('errors when timestamp is empty', () => { const stdout = logRecord('a', 'a', 'Alice', '', 'x') + NUL; const r = parseLog(stdout); assert.equal(r.ok, false); - if (!r.ok) assert.match(r.error.message, /timestamp/); + if (!r.ok) {assert.match(r.error.message, /timestamp/);} }); it('errors when timestamp has leading/trailing whitespace', () => { @@ -112,32 +112,32 @@ describe('parseNameStatus', () => { it('returns empty array for empty input', () => { const r = parseNameStatus(''); assert.equal(r.ok, true); - if (r.ok) assert.deepEqual(r.value, []); + if (r.ok) {assert.deepEqual(r.value, []);} }); it('parses an Added file', () => { const r = parseNameStatus(`A${NUL}new.txt${NUL}`); assert.equal(r.ok, true); - if (!r.ok) return; + if (!r.ok) {return;} assert.deepEqual(r.value, [{ status: 'A', path: 'new.txt' }]); }); it('parses a Modified file', () => { const r = parseNameStatus(`M${NUL}a.txt${NUL}`); assert.equal(r.ok, true); - if (r.ok) assert.deepEqual(r.value, [{ status: 'M', path: 'a.txt' }]); + if (r.ok) {assert.deepEqual(r.value, [{ status: 'M', path: 'a.txt' }]);} }); it('parses a Deleted file', () => { const r = parseNameStatus(`D${NUL}gone.txt${NUL}`); assert.equal(r.ok, true); - if (r.ok) assert.deepEqual(r.value, [{ status: 'D', path: 'gone.txt' }]); + if (r.ok) {assert.deepEqual(r.value, [{ status: 'D', path: 'gone.txt' }]);} }); it('parses a Rename with similarity', () => { const r = parseNameStatus(`R100${NUL}old.txt${NUL}new.txt${NUL}`); assert.equal(r.ok, true); - if (!r.ok) return; + if (!r.ok) {return;} assert.deepEqual(r.value, [ { status: 'R', path: 'new.txt', oldPath: 'old.txt', similarity: 100 }, ]); @@ -146,7 +146,7 @@ describe('parseNameStatus', () => { it('parses a Copy with similarity', () => { const r = parseNameStatus(`C75${NUL}src.txt${NUL}dst.txt${NUL}`); assert.equal(r.ok, true); - if (!r.ok) return; + if (!r.ok) {return;} assert.deepEqual(r.value, [ { status: 'C', path: 'dst.txt', oldPath: 'src.txt', similarity: 75 }, ]); @@ -161,7 +161,7 @@ describe('parseNameStatus', () => { `C50${NUL}src.txt${NUL}dst.txt${NUL}`; const r = parseNameStatus(stdout); assert.equal(r.ok, true); - if (!r.ok) return; + if (!r.ok) {return;} assert.equal(r.value.length, 5); assert.equal(r.value[0]?.status, 'A'); assert.equal(r.value[1]?.status, 'M'); @@ -176,38 +176,38 @@ describe('parseNameStatus', () => { const stdout = `M${NUL}path with spaces/日本語 "name".txt${NUL}`; const r = parseNameStatus(stdout); assert.equal(r.ok, true); - if (!r.ok) return; + if (!r.ok) {return;} assert.equal(r.value[0]?.path, 'path with spaces/日本語 "name".txt'); }); it('errors on unknown status letter', () => { const r = parseNameStatus(`X${NUL}a.txt${NUL}`); assert.equal(r.ok, false); - if (!r.ok) assert.match(r.error.message, /unknown status/); + if (!r.ok) {assert.match(r.error.message, /unknown status/);} }); it('errors when A/M/D has trailing chars', () => { const r = parseNameStatus(`A100${NUL}a.txt${NUL}`); assert.equal(r.ok, false); - if (!r.ok) assert.match(r.error.message, /extra chars/); + if (!r.ok) {assert.match(r.error.message, /extra chars/);} }); it('errors on rename without similarity digits', () => { const r = parseNameStatus(`R${NUL}old${NUL}new${NUL}`); assert.equal(r.ok, false); - if (!r.ok) assert.match(r.error.message, /similarity/); + if (!r.ok) {assert.match(r.error.message, /similarity/);} }); it('errors on rename with non-digit similarity', () => { const r = parseNameStatus(`R1x0${NUL}old${NUL}new${NUL}`); assert.equal(r.ok, false); - if (!r.ok) assert.match(r.error.message, /similarity/); + if (!r.ok) {assert.match(r.error.message, /similarity/);} }); it('errors on truncated rename (missing new path)', () => { const r = parseNameStatus(`R100${NUL}old.txt${NUL}`); assert.equal(r.ok, false); - if (!r.ok) assert.match(r.error.message, /missing paths/); + if (!r.ok) {assert.match(r.error.message, /missing paths/);} }); it('errors when simple status is missing its path', () => { @@ -220,13 +220,13 @@ describe('parseNumstat', () => { it('returns empty array for empty input', () => { const r = parseNumstat(''); assert.equal(r.ok, true); - if (r.ok) assert.deepEqual(r.value, []); + if (r.ok) {assert.deepEqual(r.value, []);} }); it('parses a regular numstat record', () => { const r = parseNumstat(`12${TAB}3${TAB}hello.txt${NUL}`); assert.equal(r.ok, true); - if (!r.ok) return; + if (!r.ok) {return;} assert.deepEqual(r.value, [ { path: 'hello.txt', added: 12, deleted: 3, binary: false }, ]); @@ -235,7 +235,7 @@ describe('parseNumstat', () => { it('parses a binary marker ("-" / "-")', () => { const r = parseNumstat(`-${TAB}-${TAB}image.png${NUL}`); assert.equal(r.ok, true); - if (!r.ok) return; + if (!r.ok) {return;} assert.deepEqual(r.value, [ { path: 'image.png', added: 0, deleted: 0, binary: true }, ]); @@ -244,7 +244,7 @@ describe('parseNumstat', () => { it('parses a rename record', () => { const r = parseNumstat(`3${TAB}1${TAB}${NUL}old.txt${NUL}new.txt${NUL}`); assert.equal(r.ok, true); - if (!r.ok) return; + if (!r.ok) {return;} assert.equal(r.value.length, 1); assert.deepEqual(r.value[0], { path: 'new.txt', @@ -262,7 +262,7 @@ describe('parseNumstat', () => { `5${TAB}0${TAB}${NUL}old.txt${NUL}new.txt${NUL}`; const r = parseNumstat(stdout); assert.equal(r.ok, true); - if (!r.ok) return; + if (!r.ok) {return;} assert.equal(r.value.length, 3); assert.equal(r.value[0]?.path, 'a.txt'); assert.equal(r.value[0]?.binary, false); @@ -274,25 +274,25 @@ describe('parseNumstat', () => { it('preserves spaces and unicode in paths', () => { const r = parseNumstat(`1${TAB}1${TAB}dir with spaces/日本語.txt${NUL}`); assert.equal(r.ok, true); - if (r.ok) assert.equal(r.value[0]?.path, 'dir with spaces/日本語.txt'); + if (r.ok) {assert.equal(r.value[0]?.path, 'dir with spaces/日本語.txt');} }); it('errors when tab count is wrong', () => { const r = parseNumstat(`1${TAB}2${NUL}`); assert.equal(r.ok, false); - if (!r.ok) assert.match(r.error.message, /3 tab-fields/); + if (!r.ok) {assert.match(r.error.message, /3 tab-fields/);} }); it('errors when counts are non-numeric (and not binary marker)', () => { const r = parseNumstat(`abc${TAB}def${TAB}p${NUL}`); assert.equal(r.ok, false); - if (!r.ok) assert.match(r.error.message, /non-numeric/); + if (!r.ok) {assert.match(r.error.message, /non-numeric/);} }); it('errors on truncated rename', () => { const r = parseNumstat(`3${TAB}1${TAB}${NUL}old.txt${NUL}`); assert.equal(r.ok, false); - if (!r.ok) assert.match(r.error.message, /rename missing paths/); + if (!r.ok) {assert.match(r.error.message, /rename missing paths/);} }); }); @@ -300,13 +300,13 @@ describe('parseRefs', () => { it('returns empty array for empty input', () => { const r = parseRefs(''); assert.equal(r.ok, true); - if (r.ok) assert.deepEqual(r.value, []); + if (r.ok) {assert.deepEqual(r.value, []);} }); it('parses a branch ref (newline-terminated record)', () => { const r = parseRefs(`refs/heads/main${NUL}main${NUL}abc1234\n`); assert.equal(r.ok, true); - if (!r.ok) return; + if (!r.ok) {return;} assert.deepEqual(r.value, [ { name: 'main', fullName: 'refs/heads/main', sha: 'abc1234', type: 'branch' }, ]); @@ -315,7 +315,7 @@ describe('parseRefs', () => { it('parses a tag ref', () => { const r = parseRefs(`refs/tags/v1.0${NUL}v1.0${NUL}def5678\n`); assert.equal(r.ok, true); - if (!r.ok) return; + if (!r.ok) {return;} assert.deepEqual(r.value, [ { name: 'v1.0', fullName: 'refs/tags/v1.0', sha: 'def5678', type: 'tag' }, ]); @@ -324,7 +324,7 @@ describe('parseRefs', () => { it('classifies a non-branch, non-tag ref as other', () => { const r = parseRefs(`refs/remotes/origin/main${NUL}origin/main${NUL}abc1234\n`); assert.equal(r.ok, true); - if (r.ok) assert.equal(r.value[0]?.type, 'other'); + if (r.ok) {assert.equal(r.value[0]?.type, 'other');} }); it('parses a mixed batch of branches and tags', () => { @@ -334,7 +334,7 @@ describe('parseRefs', () => { `refs/tags/v1.0${NUL}v1.0${NUL}ccc\n`; const r = parseRefs(stdout); assert.equal(r.ok, true); - if (!r.ok) return; + if (!r.ok) {return;} assert.equal(r.value.length, 3); assert.equal(r.value[0]?.type, 'branch'); assert.equal(r.value[1]?.type, 'branch'); @@ -344,18 +344,18 @@ describe('parseRefs', () => { it('errors when a line has fewer than 3 NUL-separated fields', () => { const r = parseRefs(`refs/heads/main${NUL}main\n`); assert.equal(r.ok, false); - if (!r.ok) assert.match(r.error.message, /3 NUL-separated/); + if (!r.ok) {assert.match(r.error.message, /3 NUL-separated/);} }); it('errors when a ref field is empty', () => { const r = parseRefs(`refs/heads/main${NUL}${NUL}abc\n`); assert.equal(r.ok, false); - if (!r.ok) assert.match(r.error.message, /empty field/); + if (!r.ok) {assert.match(r.error.message, /empty field/);} }); it('ignores trailing blank lines', () => { const r = parseRefs(`refs/heads/main${NUL}main${NUL}abc\n\n\n`); assert.equal(r.ok, true); - if (r.ok) assert.equal(r.value.length, 1); + if (r.ok) {assert.equal(r.value.length, 1);} }); }); diff --git a/src/test/unit/result.test.ts b/src/test/unit/result.test.ts index 26a7180..8beadc9 100644 --- a/src/test/unit/result.test.ts +++ b/src/test/unit/result.test.ts @@ -73,20 +73,20 @@ describe('Result', () => { it('applies the function on Ok', () => { const r = map(ok(2), (n) => n * 3); assert.equal(r.ok, true); - if (r.ok) assert.equal(r.value, 6); + if (r.ok) {assert.equal(r.value, 6);} }); it('passes Err through unchanged', () => { const original: Result<number, string> = err('bad'); const r = map<number, number, string>(original, (n) => n * 3); assert.equal(r.ok, false); - if (!r.ok) assert.equal(r.error, 'bad'); + if (!r.ok) {assert.equal(r.error, 'bad');} }); it('supports type-changing maps', () => { const r = map(ok(5), (n) => `n=${n}`); assert.equal(r.ok, true); - if (r.ok) assert.equal(r.value, 'n=5'); + if (r.ok) {assert.equal(r.value, 'n=5');} }); }); @@ -94,14 +94,14 @@ describe('Result', () => { it('chains Ok into another Ok', () => { const r = andThen(ok(2), (n) => ok(n + 1)); assert.equal(r.ok, true); - if (r.ok) assert.equal(r.value, 3); + if (r.ok) {assert.equal(r.value, 3);} }); it('chains Ok into an Err and propagates it', () => { const start: Result<number, string> = ok(2); const r = andThen(start, (_n): Result<number, string> => err('downstream')); assert.equal(r.ok, false); - if (!r.ok) assert.equal(r.error, 'downstream'); + if (!r.ok) {assert.equal(r.error, 'downstream');} }); it('short-circuits on Err without invoking the function', () => { @@ -113,7 +113,7 @@ describe('Result', () => { }); assert.equal(called, false); assert.equal(r.ok, false); - if (!r.ok) assert.equal(r.error, 'upstream'); + if (!r.ok) {assert.equal(r.error, 'upstream');} }); }); diff --git a/src/test/unit/uri.test.ts b/src/test/unit/uri.test.ts index 462fa3d..6d51d2a 100644 --- a/src/test/unit/uri.test.ts +++ b/src/test/unit/uri.test.ts @@ -11,7 +11,7 @@ const roundTrip = (rev: DiffyAddressableRev, path: string): void => { const uri = buildDiffyUri(rev, path); const parsed = parseDiffyUri(uri); assert.equal(parsed.ok, true, `parse failed for ${uri}`); - if (!parsed.ok) return; + if (!parsed.ok) {return;} assert.deepEqual(parsed.value.rev, rev); assert.equal(parsed.value.path, path); }; @@ -53,7 +53,7 @@ describe('parseDiffyUri', () => { it('parses a commit URI and decodes the path', () => { const r = parseDiffyUri(`diffy://commit/${SHA}/src/file.ts`); assert.equal(r.ok, true); - if (!r.ok) return; + if (!r.ok) {return;} assert.deepEqual(r.value.rev, { kind: 'commit', sha: SHA }); assert.equal(r.value.path, 'src/file.ts'); }); @@ -61,7 +61,7 @@ describe('parseDiffyUri', () => { it('parses an index URI and decodes the path', () => { const r = parseDiffyUri('diffy://index/src/file.ts'); assert.equal(r.ok, true); - if (!r.ok) return; + if (!r.ok) {return;} assert.deepEqual(r.value.rev, { kind: 'index' }); assert.equal(r.value.path, 'src/file.ts'); }); @@ -69,49 +69,49 @@ describe('parseDiffyUri', () => { it('rejects a non-diffy scheme', () => { const r = parseDiffyUri('file:///some/path.ts'); assert.equal(r.ok, false); - if (!r.ok) assert.equal(r.error.kind, 'invalidScheme'); + if (!r.ok) {assert.equal(r.error.kind, 'invalidScheme');} }); it('rejects an unknown authority', () => { const r = parseDiffyUri(`diffy://stash/${SHA}/x.ts`); assert.equal(r.ok, false); - if (!r.ok) assert.equal(r.error.kind, 'invalidAuthority'); + if (!r.ok) {assert.equal(r.error.kind, 'invalidAuthority');} }); it('rejects a commit URI with no path after the sha', () => { const r = parseDiffyUri(`diffy://commit/${SHA}/`); assert.equal(r.ok, false); - if (!r.ok) assert.equal(r.error.kind, 'emptyPath'); + if (!r.ok) {assert.equal(r.error.kind, 'emptyPath');} }); it('rejects a commit URI with no sha', () => { const r = parseDiffyUri('diffy://commit//file.ts'); assert.equal(r.ok, false); - if (!r.ok) assert.equal(r.error.kind, 'missingSha'); + if (!r.ok) {assert.equal(r.error.kind, 'missingSha');} }); it('rejects an index URI with no path', () => { const r = parseDiffyUri('diffy://index/'); assert.equal(r.ok, false); - if (!r.ok) assert.equal(r.error.kind, 'emptyPath'); + if (!r.ok) {assert.equal(r.error.kind, 'emptyPath');} }); it('rejects a malformed URI with no scheme separator', () => { const r = parseDiffyUri('not-a-uri'); assert.equal(r.ok, false); - if (!r.ok) assert.equal(r.error.kind, 'malformed'); + if (!r.ok) {assert.equal(r.error.kind, 'malformed');} }); it('rejects a URI missing the authority/path slash', () => { const r = parseDiffyUri('diffy://commit'); assert.equal(r.ok, false); - if (!r.ok) assert.equal(r.error.kind, 'malformed'); + if (!r.ok) {assert.equal(r.error.kind, 'malformed');} }); it('rejects a URI with malformed percent encoding', () => { const r = parseDiffyUri(`diffy://commit/${SHA}/bad%ZZpath.ts`); assert.equal(r.ok, false); - if (!r.ok) assert.equal(r.error.kind, 'badEncoding'); + if (!r.ok) {assert.equal(r.error.kind, 'badEncoding');} }); }); diff --git a/src/ui/uri.ts b/src/ui/uri.ts index da29fa4..0c481ba 100644 --- a/src/ui/uri.ts +++ b/src/ui/uri.ts @@ -39,19 +39,19 @@ export const buildDiffyUri = (rev: DiffyAddressableRev, path: string): string => const parseCommitUri = (rest: string): Result<DiffyUriComponents, DiffyUriParseError> => { const slash = rest.indexOf(PATH_SEP); - if (slash <= 0) return err({ kind: 'missingSha' }); + if (slash <= 0) {return err({ kind: 'missingSha' });} const sha: Sha = rest.slice(0, slash); const encodedPath = rest.slice(slash + 1); - if (encodedPath === '') return err({ kind: 'emptyPath' }); + if (encodedPath === '') {return err({ kind: 'emptyPath' });} const decoded = decodePath(encodedPath); - if (!decoded.ok) return decoded; + if (!decoded.ok) {return decoded;} return ok({ rev: { kind: 'commit', sha }, path: decoded.value }); }; const parseIndexUri = (rest: string): Result<DiffyUriComponents, DiffyUriParseError> => { - if (rest === '') return err({ kind: 'emptyPath' }); + if (rest === '') {return err({ kind: 'emptyPath' });} const decoded = decodePath(rest); - if (!decoded.ok) return decoded; + if (!decoded.ok) {return decoded;} return ok({ rev: { kind: 'index' }, path: decoded.value }); }; @@ -63,11 +63,11 @@ interface UriSplit { const splitUri = (uri: string): Result<UriSplit, DiffyUriParseError> => { const sep = uri.indexOf(SCHEME_SEP); - if (sep < 0) return err({ kind: 'malformed', reason: 'no scheme separator' }); + if (sep < 0) {return err({ kind: 'malformed', reason: 'no scheme separator' });} const scheme = uri.slice(0, sep); const afterScheme = uri.slice(sep + SCHEME_SEP.length); const firstSlash = afterScheme.indexOf(PATH_SEP); - if (firstSlash < 0) return err({ kind: 'malformed', reason: 'no authority/path separator' }); + if (firstSlash < 0) {return err({ kind: 'malformed', reason: 'no authority/path separator' });} return ok({ scheme, authority: afterScheme.slice(0, firstSlash), @@ -79,11 +79,11 @@ export const parseDiffyUri = ( uri: string, ): Result<DiffyUriComponents, DiffyUriParseError> => { const split = splitUri(uri); - if (!split.ok) return split; + if (!split.ok) {return split;} if (split.value.scheme !== SCHEME) { return err({ kind: 'invalidScheme', got: split.value.scheme }); } - if (split.value.authority === URI_AUTHORITIES.commit) return parseCommitUri(split.value.rest); - if (split.value.authority === URI_AUTHORITIES.index) return parseIndexUri(split.value.rest); + if (split.value.authority === URI_AUTHORITIES.commit) {return parseCommitUri(split.value.rest);} + if (split.value.authority === URI_AUTHORITIES.index) {return parseIndexUri(split.value.rest);} return err({ kind: 'invalidAuthority', got: split.value.authority }); }; From d479b14852e3dc2e1e9062aed551f110f0f4c5a6 Mon Sep 17 00:00:00 2001 From: Christian Findlay <16697547+MelbourneDeveloper@users.noreply.github.com> Date: Sun, 24 May 2026 09:18:07 +1000 Subject: [PATCH 06/17] fixes --- docs/assets/diffy-logo-768.png | Bin 0 -> 17199 bytes docs/assets/diffy-logo.svg | 44 ++++ docs/assets/diffy-mark-128.png | Bin 0 -> 5123 bytes docs/assets/diffy-mark-256.png | Bin 0 -> 11586 bytes docs/assets/diffy-mark.svg | 24 ++ docs/design-system.md | 9 + eslint.config.mjs | 97 ++++++- icon.png | Bin 0 -> 5123 bytes package.json | 1 + src/commands/compareFileWithCommit.ts | 4 +- src/commands/compareFileWithRev.ts | 27 +- src/commands/compareWith.ts | 10 +- src/commands/compareWithPrevious.ts | 19 +- src/commands/compareWithRef.ts | 7 +- src/commands/compareWithWorkingCopy.ts | 12 +- src/commands/flow.ts | 77 ++++-- src/commands/reopenLast.ts | 14 +- src/commands/shared.ts | 50 +++- src/constants.ts | 115 +++++++++ src/extension.ts | 50 +++- src/git/GitRepo.ts | 14 +- src/git/GitRunner.ts | 43 +++- src/git/parsers.ts | 34 ++- src/git/repoMatch.ts | 2 +- src/git/types.ts | 27 +- src/logger.ts | 21 +- src/menus.ts | 77 +++--- src/providers/DiffyContentProvider.ts | 8 +- src/result.ts | 16 +- src/state.ts | 9 +- src/test/runTests.ts | 3 +- src/test/suite/activation.test.ts | 22 +- src/test/suite/helpers.ts | 22 +- src/test/unit/format.test.ts | 24 +- src/test/unit/menus.test.ts | 82 ++++-- src/test/unit/parsers.test.ts | 337 +++++++++++++++---------- src/test/unit/repoMatch.test.ts | 36 ++- src/test/unit/result.test.ts | 36 +-- src/test/unit/uri.test.ts | 54 ++-- src/ui/CommitPicker.ts | 5 +- src/ui/FilePicker.ts | 8 +- src/ui/RefPicker.ts | 5 +- src/ui/SideBPicker.ts | 35 +-- src/ui/format/fileItem.ts | 18 +- src/ui/format/refLabel.ts | 11 +- src/ui/format/relativeTime.ts | 4 +- src/ui/runQuickPick.ts | 24 +- src/ui/uri.ts | 103 ++++++-- src/vscodeGitApi.ts | 13 +- 49 files changed, 1143 insertions(+), 510 deletions(-) create mode 100644 docs/assets/diffy-logo-768.png create mode 100644 docs/assets/diffy-logo.svg create mode 100644 docs/assets/diffy-mark-128.png create mode 100644 docs/assets/diffy-mark-256.png create mode 100644 docs/assets/diffy-mark.svg create mode 100644 icon.png diff --git a/docs/assets/diffy-logo-768.png b/docs/assets/diffy-logo-768.png new file mode 100644 index 0000000000000000000000000000000000000000..c7c69da22d45e62c2e8156cb384f4767324278bc GIT binary patch literal 17199 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 literal 0 HcmV?d00001 diff --git a/docs/assets/diffy-logo.svg b/docs/assets/diffy-logo.svg new file mode 100644 index 0000000..f28d32b --- /dev/null +++ b/docs/assets/diffy-logo.svg @@ -0,0 +1,44 @@ +<svg xmlns="http://www.w3.org/2000/svg" width="384" height="88" viewBox="0 0 384 88" role="img" aria-labelledby="title desc"> + <title id="title">Diffy logo + Diffy wordmark with the split D-shaped diff mark. + + + + + + + + + + + + + + + + + + + Diffy + + + git diffs in VS Code + + diff --git a/docs/assets/diffy-mark-128.png b/docs/assets/diffy-mark-128.png new file mode 100644 index 0000000000000000000000000000000000000000..bd787c1077a236305ca0355e008aef07b4ae8893 GIT binary patch literal 5123 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 + Diffy mark + A split D-shaped diff mark with addition and deletion indicators. + + + + + + + + + + + + + + + + + + diff --git a/docs/design-system.md b/docs/design-system.md index 3d533c3..7207d4a 100644 --- a/docs/design-system.md +++ b/docs/design-system.md @@ -58,6 +58,15 @@ Example body copy: The Diffy mark should communicate comparison, pairing, and code review without looking like a separate IDE. +Source assets: + +- [Mark SVG](assets/diffy-mark.svg) +- [Logo SVG](assets/diffy-logo.svg) +- [128px mark PNG](assets/diffy-mark-128.png) +- [256px mark PNG](assets/diffy-mark-256.png) +- [768px logo PNG](assets/diffy-logo-768.png) +- Root extension icon: `icon.png` + Preferred concepts: - Two adjacent file panes with a narrow comparison gutter. diff --git a/eslint.config.mjs b/eslint.config.mjs index b5a7045..0824483 100644 --- a/eslint.config.mjs +++ b/eslint.config.mjs @@ -1,11 +1,18 @@ // agent-pmo:74cf183 // @ts-check +// +// MAX-STRICTNESS ESLint config: +// - tseslint strictTypeChecked + stylisticTypeChecked baselines +// - every safe extra rule promoted to "error" (no warns) +// - no exceptions, no per-file disables + import eslint from "@eslint/js"; import tseslint from "typescript-eslint"; export default tseslint.config( eslint.configs.recommended, ...tseslint.configs.strictTypeChecked, + ...tseslint.configs.stylisticTypeChecked, { languageOptions: { parserOptions: { @@ -14,36 +21,110 @@ export default tseslint.config( }, }, rules: { + // Type-safety hard rules (no `any`, no unsafe ops, no non-null !) "@typescript-eslint/no-explicit-any": "error", "@typescript-eslint/no-unsafe-assignment": "error", "@typescript-eslint/no-unsafe-call": "error", "@typescript-eslint/no-unsafe-member-access": "error", "@typescript-eslint/no-unsafe-return": "error", + "@typescript-eslint/no-unsafe-argument": "error", + "@typescript-eslint/no-unsafe-enum-comparison": "error", + "@typescript-eslint/no-non-null-assertion": "error", + "@typescript-eslint/no-confusing-non-null-assertion": "error", + "@typescript-eslint/no-unnecessary-type-assertion": "error", + "@typescript-eslint/no-unnecessary-condition": "error", + "@typescript-eslint/no-unnecessary-boolean-literal-compare": "error", + "@typescript-eslint/no-unnecessary-template-expression": "error", + "@typescript-eslint/no-unnecessary-type-arguments": "error", + "@typescript-eslint/no-redundant-type-constituents": "error", + "@typescript-eslint/no-duplicate-type-constituents": "error", + "@typescript-eslint/no-duplicate-enum-values": "error", + "@typescript-eslint/no-mixed-enums": "error", + "@typescript-eslint/no-meaningless-void-operator": "error", + "@typescript-eslint/no-invalid-void-type": "error", + "@typescript-eslint/no-confusing-void-expression": "error", + "@typescript-eslint/no-base-to-string": "error", + "@typescript-eslint/no-dynamic-delete": "error", + "@typescript-eslint/no-extraneous-class": "error", + "@typescript-eslint/no-for-in-array": "error", + "@typescript-eslint/no-useless-empty-export": "error", + "@typescript-eslint/no-misused-spread": "error", + "@typescript-eslint/no-unsafe-unary-minus": "error", + "@typescript-eslint/no-array-delete": "error", + + // Async / Promise safety "@typescript-eslint/no-floating-promises": "error", "@typescript-eslint/no-misused-promises": "error", "@typescript-eslint/await-thenable": "error", - "@typescript-eslint/no-unnecessary-type-assertion": "error", + "@typescript-eslint/promise-function-async": "error", + "@typescript-eslint/return-await": ["error", "always"], + "@typescript-eslint/require-await": "error", + + // Boolean / nullish clarity + "@typescript-eslint/strict-boolean-expressions": "error", "@typescript-eslint/prefer-nullish-coalescing": "error", "@typescript-eslint/prefer-optional-chain": "error", - "@typescript-eslint/no-non-null-assertion": "error", - "@typescript-eslint/strict-boolean-expressions": "error", - "@typescript-eslint/no-unused-vars": [ - "error", - { argsIgnorePattern: "^_" }, - ], + + // Idiomatic patterns + "@typescript-eslint/prefer-as-const": "error", + "@typescript-eslint/prefer-find": "error", + "@typescript-eslint/prefer-includes": "error", + "@typescript-eslint/prefer-readonly": "error", + "@typescript-eslint/prefer-reduce-type-parameter": "error", + "@typescript-eslint/prefer-return-this-type": "error", + "@typescript-eslint/prefer-string-starts-ends-with": "error", + "@typescript-eslint/prefer-for-of": "error", + "@typescript-eslint/prefer-function-type": "error", + "@typescript-eslint/prefer-literal-enum-member": "error", + "@typescript-eslint/unified-signatures": "error", + "@typescript-eslint/require-array-sort-compare": "error", + "@typescript-eslint/switch-exhaustiveness-check": "error", + "@typescript-eslint/restrict-plus-operands": "error", + "@typescript-eslint/restrict-template-expressions": "error", + + // Imports + naming "@typescript-eslint/consistent-type-imports": [ "error", { prefer: "type-imports" }, ], + "@typescript-eslint/consistent-type-exports": "error", + "@typescript-eslint/consistent-type-definitions": ["error", "interface"], "@typescript-eslint/no-shadow": "error", + "@typescript-eslint/no-unused-vars": [ + "error", + { argsIgnorePattern: "^_", varsIgnorePattern: "^_" }, + ], + "@typescript-eslint/no-import-type-side-effects": "error", + "@typescript-eslint/method-signature-style": ["error", "property"], + + // Errors are typed, not strings "no-throw-literal": "off", "@typescript-eslint/only-throw-error": "error", + + // Plain JS hygiene eqeqeq: ["error", "always"], - "no-console": "warn", curly: ["error", "all"], + "no-console": "error", + "no-var": "error", + "prefer-const": "error", + "no-debugger": "error", + "no-alert": "error", + "no-param-reassign": ["error", { props: true }], + "no-eval": "error", + "no-implied-eval": "off", + "@typescript-eslint/no-implied-eval": "error", + "no-return-assign": "error", + "no-self-compare": "error", + "no-unmodified-loop-condition": "error", + "no-unreachable-loop": "error", + "no-useless-concat": "error", + "no-useless-return": "error", + "prefer-template": "error", + "object-shorthand": "error", }, }, { + // Scripts and bundler config are JS — they don't get TS rules. ignores: [ "out/", "dist/", diff --git a/icon.png b/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..bd787c1077a236305ca0355e008aef07b4ae8893 GIT binary patch literal 5123 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 - makeCompareFileWithRev({ deps, source: 'commits' }); + makeCompareFileWithRev({ deps, source: FILE_REV_SOURCES.commits }); diff --git a/src/commands/compareFileWithRev.ts b/src/commands/compareFileWithRev.ts index dd61d63..9830e8b 100644 --- a/src/commands/compareFileWithRev.ts +++ b/src/commands/compareFileWithRev.ts @@ -1,6 +1,7 @@ import * as vscode from 'vscode'; +import { REV_KINDS, TITLE_PREFIX } from '../constants'; import type { GitRepo } from '../git/GitRepo'; -import type { RefType, Sha } from '../git/types'; +import type { Sha } from '../git/types'; import type { Result } from '../result'; import { err, ok } from '../result'; import { CANCELLED, type Cancelled } from '../ui/cancelled'; @@ -13,10 +14,18 @@ import { } from './shared'; import { pickRefAsSha, reportGitError, sideAFromSha } from './flow'; -export type FileRevSource = 'commits' | RefType; +export const FILE_REV_SOURCES = { + commits: 'commits', + branch: 'branch', + tag: 'tag', + other: 'other', +} as const; -const NO_EDITOR = 'Diffy: open a file first.'; -const NOT_IN_REPO = 'Diffy: file is not in a git repository.'; +export type FileRevSource = (typeof FILE_REV_SOURCES)[keyof typeof FILE_REV_SOURCES]; + +const NO_EDITOR = `${TITLE_PREFIX} open a file first.`; +const NOT_IN_REPO = `${TITLE_PREFIX} file is not in a git repository.`; +const LOG_OP = 'log'; const pickShaForFile = async ({ repo, @@ -27,10 +36,10 @@ const pickShaForFile = async ({ source: FileRevSource; output: vscode.OutputChannel; }): Promise> => { - if (source === 'commits') { + if (source === FILE_REV_SOURCES.commits) { const log = await repo.log({}); if (!log.ok) { - reportGitError({ output, op: 'log', e: log.error }); + reportGitError({ output, op: LOG_OP, e: log.error }); return err(CANCELLED); } const picked = await pickCommit({ commits: log.value }); @@ -39,7 +48,7 @@ const pickShaForFile = async ({ } return ok(picked.value.sha); } - return pickRefAsSha({ repo, output, filter: source }); + return await pickRefAsSha({ repo, output, filter: source }); }; const resolveTargetUri = (uri?: vscode.Uri): vscode.Uri | undefined => @@ -71,7 +80,7 @@ const handler = async ({ } await openDiff({ revA: sideAFromSha(sha.value), - revB: { kind: 'workingCopy' }, + revB: { kind: REV_KINDS.workingCopy }, repoRoot: vsRepo.rootUri.fsPath, relPath: vscode.workspace.asRelativePath(target, false), }); @@ -84,4 +93,4 @@ export const makeCompareFileWithRev = ({ deps: CommandDeps; source: FileRevSource; }) => - async (uri?: vscode.Uri): Promise => handler({ deps, uri, source }); + async (uri?: vscode.Uri): Promise => { await handler({ deps, uri, source }); }; diff --git a/src/commands/compareWith.ts b/src/commands/compareWith.ts index 5d0332d..8e9dd82 100644 --- a/src/commands/compareWith.ts +++ b/src/commands/compareWith.ts @@ -1,4 +1,5 @@ import * as vscode from 'vscode'; +import { TITLE_PREFIX } from '../constants'; import type { MementoStore } from '../state'; import { extractHistoryItemSha } from './historyItem'; import { @@ -12,15 +13,16 @@ import { sideAFromSha, } from './flow'; +const NOT_FROM_HISTORY = + `${TITLE_PREFIX} this command must be invoked from the SCM history view.`; + const handler = async ( deps: CommandDeps & { readonly state: MementoStore }, arg: unknown, ): Promise => { const sha = extractHistoryItemSha(arg); if (sha === undefined) { - void vscode.window.showWarningMessage( - 'Diffy: this command must be invoked from the SCM history view.', - ); + void vscode.window.showWarningMessage(NOT_FROM_HISTORY); return; } const vs = await pickRepoFrom(deps.gitApi); @@ -45,4 +47,4 @@ const handler = async ( export const makeCompareWith = ( deps: CommandDeps & { readonly state: MementoStore }, ) => - async (arg: unknown): Promise => handler(deps, arg); + async (arg: unknown): Promise => { await handler(deps, arg); }; diff --git a/src/commands/compareWithPrevious.ts b/src/commands/compareWithPrevious.ts index f9bddc0..ba5f608 100644 --- a/src/commands/compareWithPrevious.ts +++ b/src/commands/compareWithPrevious.ts @@ -1,4 +1,5 @@ import * as vscode from 'vscode'; +import { REV_KINDS, TITLE_PREFIX } from '../constants'; import type { MementoStore } from '../state'; import { extractHistoryItemSha } from './historyItem'; import { @@ -12,15 +13,17 @@ import { sideAFromSha, } from './flow'; +const REV_PARSE_PARENT_OP = 'rev-parse parent'; +const HISTORY_VIEW_WARNING = + `${TITLE_PREFIX} this command must be invoked from the SCM history view.`; + const handler = async ( deps: CommandDeps & { readonly state: MementoStore }, arg: unknown, ): Promise => { const sha = extractHistoryItemSha(arg); if (sha === undefined) { - void vscode.window.showWarningMessage( - 'Diffy: this command must be invoked from the SCM history view.', - ); + void vscode.window.showWarningMessage(HISTORY_VIEW_WARNING); return; } const vs = await pickRepoFrom(deps.gitApi); @@ -30,14 +33,18 @@ const handler = async ( const repo = buildRepo(deps.runner, vs.value); const parent = await repo.revParse(`${sha}^1`); if (!parent.ok) { - reportGitError({ output: deps.output, op: 'rev-parse parent', e: parent.error }); + reportGitError({ + output: deps.output, + op: REV_PARSE_PARENT_OP, + e: parent.error, + }); return; } await drillIntoFiles({ repo, repoRoot: vs.value.rootUri.fsPath, revA: sideAFromSha(sha), - revB: { kind: 'commit', sha: parent.value }, + revB: { kind: REV_KINDS.commit, sha: parent.value }, state: deps.state, output: deps.output, }); @@ -46,4 +53,4 @@ const handler = async ( export const makeCompareWithPrevious = ( deps: CommandDeps & { readonly state: MementoStore }, ) => - async (arg: unknown): Promise => handler(deps, arg); + async (arg: unknown): Promise => { await handler(deps, arg); }; diff --git a/src/commands/compareWithRef.ts b/src/commands/compareWithRef.ts index 56c2b09..b04e649 100644 --- a/src/commands/compareWithRef.ts +++ b/src/commands/compareWithRef.ts @@ -1,4 +1,5 @@ import * as vscode from 'vscode'; +import { REV_KINDS, TITLE_PREFIX } from '../constants'; import type { RefType } from '../git/types'; import type { MementoStore } from '../state'; import { extractHistoryItemSha } from './historyItem'; @@ -10,7 +11,7 @@ import { import { drillIntoFiles, pickRefAsSha, sideAFromSha } from './flow'; const NOT_FROM_HISTORY = - 'Diffy: this command must be invoked from the SCM history view.'; + `${TITLE_PREFIX} this command must be invoked from the SCM history view.`; const handler = async ({ deps, @@ -39,7 +40,7 @@ const handler = async ({ repo, repoRoot: vs.value.rootUri.fsPath, revA: sideAFromSha(sha), - revB: { kind: 'commit', sha: target.value }, + revB: { kind: REV_KINDS.commit, sha: target.value }, state: deps.state, output: deps.output, }); @@ -52,4 +53,4 @@ export const makeCompareWithRef = ({ deps: CommandDeps & { readonly state: MementoStore }; filter: RefType; }) => - async (arg: unknown): Promise => handler({ deps, arg, filter }); + async (arg: unknown): Promise => { await handler({ deps, arg, filter }); }; diff --git a/src/commands/compareWithWorkingCopy.ts b/src/commands/compareWithWorkingCopy.ts index 5c6959b..b0edd89 100644 --- a/src/commands/compareWithWorkingCopy.ts +++ b/src/commands/compareWithWorkingCopy.ts @@ -1,4 +1,5 @@ import * as vscode from 'vscode'; +import { REV_KINDS, TITLE_PREFIX } from '../constants'; import type { MementoStore } from '../state'; import { extractHistoryItemSha } from './historyItem'; import { @@ -8,15 +9,16 @@ import { } from './shared'; import { drillIntoFiles, sideAFromSha } from './flow'; +const NOT_FROM_HISTORY = + `${TITLE_PREFIX} this command must be invoked from the SCM history view.`; + const handler = async ( deps: CommandDeps & { readonly state: MementoStore }, arg: unknown, ): Promise => { const sha = extractHistoryItemSha(arg); if (sha === undefined) { - void vscode.window.showWarningMessage( - 'Diffy: this command must be invoked from the SCM history view.', - ); + void vscode.window.showWarningMessage(NOT_FROM_HISTORY); return; } const vs = await pickRepoFrom(deps.gitApi); @@ -28,7 +30,7 @@ const handler = async ( repo, repoRoot: vs.value.rootUri.fsPath, revA: sideAFromSha(sha), - revB: { kind: 'workingCopy' }, + revB: { kind: REV_KINDS.workingCopy }, state: deps.state, output: deps.output, }); @@ -37,4 +39,4 @@ const handler = async ( export const makeCompareWithWorkingCopy = ( deps: CommandDeps & { readonly state: MementoStore }, ) => - async (arg: unknown): Promise => handler(deps, arg); + async (arg: unknown): Promise => { await handler(deps, arg); }; diff --git a/src/commands/flow.ts b/src/commands/flow.ts index f74e7b8..0dee5d8 100644 --- a/src/commands/flow.ts +++ b/src/commands/flow.ts @@ -1,4 +1,12 @@ import * as vscode from 'vscode'; +import { + LOG_EVENTS, + REF_TYPES, + REV_KINDS, + SIDE_B_KINDS, + TITLE_PREFIX, + UI_TEXT, +} from '../constants'; import type { GitRepo } from '../git/GitRepo'; import type { GitRunner } from '../git/GitRunner'; import type { GitApi } from '../vscodeGitApi'; @@ -13,6 +21,14 @@ import { mergeChangedFilesWithStats, pickFiles } from '../ui/FilePicker'; import type { MementoStore } from '../state'; import { buildRepo, openDiff, pickRepoFrom } from './shared'; +const GIT_OPS = { + listRefs: 'list refs', + revParse: 'rev-parse', + log: 'log', + diffNameStatus: 'diff --name-status', + diffNumstat: 'diff --numstat', +} as const; + export const reportGitError = ({ output, op, @@ -22,12 +38,14 @@ export const reportGitError = ({ op: string; e: GitError; }): void => { - logger.error({ op, kind: e.kind }, 'git.error'); - output.appendLine(`Diffy: ${op} failed — ${e.message}`); + logger.error({ op, kind: e.kind }, LOG_EVENTS.gitError); + output.appendLine(`${TITLE_PREFIX} ${op} failed ${UI_TEXT.pathDash} ${e.message}`); if (e.stderr !== undefined && e.stderr !== '') { output.appendLine(e.stderr); } - void vscode.window.showErrorMessage(`Diffy: ${op} failed (see Output → Diffy).`); + void vscode.window.showErrorMessage( + `${TITLE_PREFIX} ${op} failed (see Output → Diffy).`, + ); }; export const resolveSideB = async ({ @@ -39,26 +57,26 @@ export const resolveSideB = async ({ repo: GitRepo; output: vscode.OutputChannel; }): Promise> => { - if (choice.kind === 'workingCopy') { - return ok({ kind: 'workingCopy' }); + if (choice.kind === SIDE_B_KINDS.workingCopy) { + return ok({ kind: REV_KINDS.workingCopy }); } - if (choice.kind === 'index') { - return ok({ kind: 'index' }); + if (choice.kind === SIDE_B_KINDS.index) { + return ok({ kind: REV_KINDS.index }); } - if (choice.kind === 'pickRef') { - return resolveRefAsRev({ repo, output }); + if (choice.kind === SIDE_B_KINDS.pickRef) { + return await resolveRefAsRev({ repo, output }); } - return resolveCommitAsRev({ repo, output }); + return await resolveCommitAsRev({ repo, output }); }; const placeholderForRefFilter = (filter?: RefType): string => { - if (filter === 'branch') { - return 'Pick a branch'; + if (filter === REF_TYPES.branch) { + return UI_TEXT.pickBranchPlaceholder; } - if (filter === 'tag') { - return 'Pick a tag'; + if (filter === REF_TYPES.tag) { + return UI_TEXT.pickTagPlaceholder; } - return 'Pick a branch or tag'; + return UI_TEXT.pickRefPlaceholder; }; export const pickRefAsSha = async ({ @@ -72,7 +90,7 @@ export const pickRefAsSha = async ({ }): Promise> => { const refs = await repo.refs(); if (!refs.ok) { - reportGitError({ output, op: 'list refs', e: refs.error }); + reportGitError({ output, op: GIT_OPS.listRefs, e: refs.error }); return err(CANCELLED); } const args = filter === undefined @@ -84,7 +102,7 @@ export const pickRefAsSha = async ({ } const sha = await repo.revParse(picked.value.name); if (!sha.ok) { - reportGitError({ output, op: 'rev-parse', e: sha.error }); + reportGitError({ output, op: GIT_OPS.revParse, e: sha.error }); return err(CANCELLED); } return ok(sha.value); @@ -101,7 +119,7 @@ const resolveRefAsRev = async ({ if (!sha.ok) { return err(CANCELLED); } - return ok({ kind: 'commit', sha: sha.value }); + return ok({ kind: REV_KINDS.commit, sha: sha.value }); }; const resolveCommitAsRev = async ({ @@ -113,14 +131,14 @@ const resolveCommitAsRev = async ({ }): Promise> => { const log = await repo.log({}); if (!log.ok) { - reportGitError({ output, op: 'log', e: log.error }); + reportGitError({ output, op: GIT_OPS.log, e: log.error }); return err(CANCELLED); } const picked = await pickCommit({ commits: log.value }); if (!picked.ok) { return err(CANCELLED); } - return ok({ kind: 'commit', sha: picked.value.sha }); + return ok({ kind: REV_KINDS.commit, sha: picked.value.sha }); }; export const pickSideBAndResolve = async ({ @@ -134,7 +152,7 @@ export const pickSideBAndResolve = async ({ if (!choice.ok) { return err(CANCELLED); } - return resolveSideB({ choice: choice.value, repo, output }); + return await resolveSideB({ choice: choice.value, repo, output }); }; export const drillIntoFiles = async ({ @@ -157,14 +175,14 @@ export const drillIntoFiles = async ({ return; } if (entries.length === 0) { - void vscode.window.showInformationMessage('Diffy: no changes between selected sides.'); + void vscode.window.showInformationMessage(UI_TEXT.noChanges); return; } await state.setLastComparison({ revA, revB, repoRoot }); await pickFiles({ entries, - onPick: (entry) => - openDiff({ revA, revB, repoRoot, relPath: entry.file.path }), + onPick: async (entry) => + { await openDiff({ revA, revB, repoRoot, relPath: entry.file.path }); }, }); }; @@ -181,18 +199,21 @@ const collectChangedFiles = async ({ }) => { const ns = await repo.nameStatus({ from: revA, to: revB }); if (!ns.ok) { - reportGitError({ output, op: 'diff --name-status', e: ns.error }); + reportGitError({ output, op: GIT_OPS.diffNameStatus, e: ns.error }); return undefined; } const num = await repo.numstat({ from: revA, to: revB }); if (!num.ok) { - reportGitError({ output, op: 'diff --numstat', e: num.error }); + reportGitError({ output, op: GIT_OPS.diffNumstat, e: num.error }); return undefined; } return mergeChangedFilesWithStats(ns.value, num.value); }; -export const sideAFromSha = (sha: Sha): CommitRev => ({ kind: 'commit', sha }); +export const sideAFromSha = (sha: Sha): CommitRev => ({ + kind: REV_KINDS.commit, + sha, +}); export interface StartingPoint { readonly vsRepoRoot: string; @@ -216,7 +237,7 @@ export const pickRepoAndCommit = async ({ const repo = buildRepo(runner, vs.value); const log = await repo.log({}); if (!log.ok) { - reportGitError({ output, op: 'log', e: log.error }); + reportGitError({ output, op: GIT_OPS.log, e: log.error }); return undefined; } const commit = await pickCommit({ commits: log.value }); diff --git a/src/commands/reopenLast.ts b/src/commands/reopenLast.ts index 3a38425..c9411d2 100644 --- a/src/commands/reopenLast.ts +++ b/src/commands/reopenLast.ts @@ -1,24 +1,24 @@ import * as vscode from 'vscode'; +import { TITLE_PREFIX } from '../constants'; import { findRepoForUri } from '../vscodeGitApi'; import type { MementoStore } from '../state'; import { type CommandDeps, buildRepo } from './shared'; import { drillIntoFiles } from './flow'; +const NO_PREVIOUS = `${TITLE_PREFIX} no previous comparison to reopen.`; +const REPO_GONE = `${TITLE_PREFIX} previous repository is no longer open.`; + const handler = async ( deps: CommandDeps & { readonly state: MementoStore }, ): Promise => { const last = deps.state.getLastComparison(); if (last === undefined) { - void vscode.window.showInformationMessage( - 'Diffy: no previous comparison to reopen.', - ); + void vscode.window.showInformationMessage(NO_PREVIOUS); return; } const vsRepo = findRepoForUri(deps.gitApi, vscode.Uri.file(last.repoRoot)); if (vsRepo === undefined) { - void vscode.window.showWarningMessage( - 'Diffy: previous repository is no longer open.', - ); + void vscode.window.showWarningMessage(REPO_GONE); return; } const repo = buildRepo(deps.runner, vsRepo); @@ -35,4 +35,4 @@ const handler = async ( export const makeReopenLast = ( deps: CommandDeps & { readonly state: MementoStore }, ) => - async (): Promise => handler(deps); + async (): Promise => { await handler(deps); }; diff --git a/src/commands/shared.ts b/src/commands/shared.ts index 8aedcd2..9ec1a8e 100644 --- a/src/commands/shared.ts +++ b/src/commands/shared.ts @@ -1,6 +1,12 @@ import * as path from 'node:path'; import * as vscode from 'vscode'; -import { BUILT_IN_COMMANDS, SHORT_SHA_LEN } from '../constants'; +import { + BUILT_IN_COMMANDS, + LOG_EVENTS, + REV_KINDS, + SHORT_SHA_LEN, + UI_TEXT, +} from '../constants'; import type { GitRepo } from '../git/GitRepo'; import type { GitRunner } from '../git/GitRunner'; import { createGitRepo } from '../git/GitRepo'; @@ -24,13 +30,13 @@ export interface CommandDeps { export const shortSha = (sha: Sha): string => sha.slice(0, SHORT_SHA_LEN); const labelForRev = (rev: RevSpec): string => { - if (rev.kind === 'commit') { + if (rev.kind === REV_KINDS.commit) { return shortSha(rev.sha); } - if (rev.kind === 'workingCopy') { - return 'Working Copy'; + if (rev.kind === REV_KINDS.workingCopy) { + return UI_TEXT.workingCopy; } - return 'Index'; + return UI_TEXT.indexLabel; }; export const formatDiffTitle = ({ @@ -41,7 +47,8 @@ export const formatDiffTitle = ({ revA: CommitRev; revB: RevSpec; basename: string; -}): string => `${labelForRev(revA)} ↔ ${labelForRev(revB)} — ${basename}`; +}): string => + `${labelForRev(revA)} ${UI_TEXT.pathArrow} ${labelForRev(revB)} ${UI_TEXT.pathDash} ${basename}`; export const uriForRev = ({ rev, @@ -52,11 +59,13 @@ export const uriForRev = ({ repoRoot: string; relPath: string; }): vscode.Uri => { - if (rev.kind === 'workingCopy') { + if (rev.kind === REV_KINDS.workingCopy) { return vscode.Uri.file(path.join(repoRoot, relPath)); } const addressable: DiffyAddressableRev = - rev.kind === 'commit' ? { kind: 'commit', sha: rev.sha } : { kind: 'index' }; + rev.kind === REV_KINDS.commit + ? { kind: REV_KINDS.commit, sha: rev.sha } + : { kind: REV_KINDS.index }; return vscode.Uri.parse(buildDiffyUri(addressable, relPath)); }; @@ -73,9 +82,21 @@ export const openDiff = async ({ }): Promise => { const left = uriForRev({ rev: revA, repoRoot, relPath }); const right = uriForRev({ rev: revB, repoRoot, relPath }); - const title = formatDiffTitle({ revA, revB, basename: path.basename(relPath) }); - logger.info({ shaA: shortSha(revA.sha), revBKind: revB.kind }, 'diff.open'); - await vscode.commands.executeCommand(BUILT_IN_COMMANDS.diff, left, right, title); + const title = formatDiffTitle({ + revA, + revB, + basename: path.basename(relPath), + }); + logger.info( + { shaA: shortSha(revA.sha), revBKind: revB.kind }, + LOG_EVENTS.diffOpen, + ); + await vscode.commands.executeCommand( + BUILT_IN_COMMANDS.diff, + left, + right, + title, + ); }; export const repoForUri = ( @@ -93,9 +114,12 @@ export const pickRepoFrom = async ( if (api.repositories.length === 1 && first !== undefined) { return ok(first); } - const items = api.repositories.map((r) => ({ label: r.rootUri.fsPath, repo: r })); + const items = api.repositories.map((r) => ({ + label: r.rootUri.fsPath, + repo: r, + })); const picked = await vscode.window.showQuickPick(items, { - placeHolder: 'Pick a git repository', + placeHolder: UI_TEXT.pickRepoPlaceholder, }); return picked === undefined ? err(CANCELLED) : ok(picked.repo); }; diff --git a/src/constants.ts b/src/constants.ts index 4cb3ae3..2bb8469 100644 --- a/src/constants.ts +++ b/src/constants.ts @@ -18,6 +18,7 @@ export const COMMAND_IDS = { export const BUILT_IN_COMMANDS = { diff: 'vscode.diff', + setContext: 'setContext', } as const; export const CONTEXT_KEYS = { @@ -49,3 +50,117 @@ export const REFS_FORMAT = '%(refname)%00%(refname:short)%00%(objectname)'; export const REF_PREFIX_HEADS = 'refs/heads/'; export const REF_PREFIX_TAGS = 'refs/tags/'; + +export const REV_KINDS = { + commit: 'commit', + workingCopy: 'workingCopy', + index: 'index', +} as const; + +export const REF_TYPES = { + branch: 'branch', + tag: 'tag', + other: 'other', +} as const; + +export const SIDE_B_KINDS = { + workingCopy: REV_KINDS.workingCopy, + index: REV_KINDS.index, + pickRef: 'pickRef', + pickCommit: 'pickCommit', +} as const; + +export const URI_PARSE_ERROR_KINDS = { + invalidScheme: 'invalidScheme', + invalidAuthority: 'invalidAuthority', + missingSha: 'missingSha', + emptyPath: 'emptyPath', + badEncoding: 'badEncoding', + malformed: 'malformed', +} as const; + +export const GIT_ERROR_KINDS = { + spawnFailed: 'spawnFailed', + nonZeroExit: 'nonZeroExit', + parseError: 'parseError', + notARepo: 'notARepo', + notFound: 'notFound', +} as const; + +export const CHANGED_FILE_STATUSES = { + added: 'A', + modified: 'M', + deleted: 'D', + renamed: 'R', + copied: 'C', +} as const; + +export const MENU_IDS = { + scmHistoryItem: 'scm/historyItem/context', + scmResourceState: 'scm/resourceState/context', + editorTitleContext: 'editor/title/context', + explorerContext: 'explorer/context', + commandPalette: 'commandPalette', +} as const; + +export const MENU_WHEN = { + scmGit: 'scmProvider == git', + resourceFile: 'resourceScheme == file', + resourceFileNotFolder: 'resourceScheme == file && !explorerResourceIsFolder', + never: 'false', +} as const; + +export const MENU_GROUP_PREFIX = 'diffy'; + +export const TITLE_PREFIX = 'Diffy:'; + +export const UI_TEXT = { + workingCopy: 'Working Copy', + workingCopyDescription: 'On-disk files in this repository', + indexLabel: 'Index', + indexDescription: 'The git staging area', + pickCommitLabel: 'Pick a commit…', + pickCommitDescription: 'Choose from recent log entries', + pickRefLabel: 'Pick a branch or tag…', + pickRefDescription: 'Choose from refs in this repository', + pickCommitPlaceholder: 'Pick a commit', + pickRefPlaceholder: 'Pick a branch or tag', + pickBranchPlaceholder: 'Pick a branch', + pickTagPlaceholder: 'Pick a tag', + pickRepoPlaceholder: 'Pick a git repository', + compareAgainstPlaceholder: 'Compare against…', + branchLabel: 'Branch', + tagLabel: 'Tag', + refLabel: 'Ref', + binaryStat: 'binary', + justNow: 'just now', + noChanges: 'Diffy: no changes between selected sides.', + pathArrow: '↔', + pathDash: '—', + bulletDot: '•', +} as const; + +export const LOG_EVENTS = { + extensionActivated: 'extension.activated', + extensionDeactivated: 'extension.deactivated', + gitRunStart: 'git.run.start', + gitRunEnd: 'git.run.end', + gitRunSpawnFailed: 'git.run.spawnFailed', + gitError: 'git.error', + diffOpen: 'diff.open', + providerParseFailed: 'provider.parseFailed', + providerRepoUnresolved: 'provider.repoUnresolved', + providerShowFailed: 'provider.showFailed', +} as const; + +export const VSCODE_GIT_EXTENSION_ID = 'vscode.git'; + +export const LOG_LEVELS = { + trace: 'trace', + debug: 'debug', + info: 'info', + warn: 'warn', + error: 'error', +} as const; + +export type LogLevel = (typeof LOG_LEVELS)[keyof typeof LOG_LEVELS]; diff --git a/src/extension.ts b/src/extension.ts index 264f8b8..a922897 100644 --- a/src/extension.ts +++ b/src/extension.ts @@ -1,14 +1,23 @@ import * as vscode from 'vscode'; import { + BUILT_IN_COMMANDS, COMMAND_IDS, CONTEXT_KEYS, + LOG_EVENTS, + LOG_LEVELS, OUTPUT_CHANNEL_NAME, + REF_TYPES, + TITLE_PREFIX, + UI_TEXT, } from './constants'; import { createGitRunner } from './git/GitRunner'; import { type GitApi, getGitApi } from './vscodeGitApi'; import { addLogStream, logger } from './logger'; import { makeCompareFileWithCommit } from './commands/compareFileWithCommit'; -import { makeCompareFileWithRev } from './commands/compareFileWithRev'; +import { + FILE_REV_SOURCES, + makeCompareFileWithRev, +} from './commands/compareFileWithRev'; import { makeCompareTwoCommits } from './commands/compareTwoCommits'; import { makeCompareWith } from './commands/compareWith'; import { makeCompareWithPrevious } from './commands/compareWithPrevious'; @@ -21,6 +30,13 @@ import { buildRepo } from './commands/shared'; import { createMementoStore } from './state'; import type { GitRunner } from './git/GitRunner'; +const GIT_VERSION_ARG = '--version'; +const GIT_MISSING_MESSAGE = + `${TITLE_PREFIX} git binary not found on PATH ${UI_TEXT.pathDash} commands disabled.`; +const GIT_API_MISSING_MESSAGE = + `${TITLE_PREFIX} built-in git extension API unavailable.`; +const TRAILING_NEWLINE = /\n$/; + const channelStream = ( channel: vscode.OutputChannel, ): NodeJS.WritableStream => { @@ -28,7 +44,7 @@ const channelStream = ( write(chunk: string | Uint8Array): boolean { const text = typeof chunk === 'string' ? chunk : Buffer.from(chunk).toString('utf8'); - channel.appendLine(text.replace(/\n$/, '')); + channel.appendLine(text.replace(TRAILING_NEWLINE, '')); return true; }, }; @@ -36,13 +52,13 @@ const channelStream = ( }; const probeGit = async (runner: GitRunner): Promise => { - const r = await runner.run({ args: ['--version'], cwd: process.cwd() }); + const r = await runner.run({ args: [GIT_VERSION_ARG], cwd: process.cwd() }); return r.ok; }; const setGitAvailable = async (available: boolean): Promise => { await vscode.commands.executeCommand( - 'setContext', + BUILT_IN_COMMANDS.setContext, CONTEXT_KEYS.gitAvailable, available, ); @@ -78,11 +94,11 @@ const registerAll = ( ), vscode.commands.registerCommand( COMMAND_IDS.compareWithBranch, - makeCompareWithRef({ deps, filter: 'branch' }), + makeCompareWithRef({ deps, filter: REF_TYPES.branch }), ), vscode.commands.registerCommand( COMMAND_IDS.compareWithTag, - makeCompareWithRef({ deps, filter: 'tag' }), + makeCompareWithRef({ deps, filter: REF_TYPES.tag }), ), vscode.commands.registerCommand( COMMAND_IDS.compareTwoCommits, @@ -94,13 +110,16 @@ const registerAll = ( ), vscode.commands.registerCommand( COMMAND_IDS.compareFileWithBranch, - makeCompareFileWithRev({ deps, source: 'branch' }), + makeCompareFileWithRev({ deps, source: FILE_REV_SOURCES.branch }), ), vscode.commands.registerCommand( COMMAND_IDS.compareFileWithTag, - makeCompareFileWithRev({ deps, source: 'tag' }), + makeCompareFileWithRev({ deps, source: FILE_REV_SOURCES.tag }), + ), + vscode.commands.registerCommand( + COMMAND_IDS.reopenLast, + makeReopenLast(deps), ), - vscode.commands.registerCommand(COMMAND_IDS.reopenLast, makeReopenLast(deps)), vscode.commands.registerCommand(COMMAND_IDS.showLogs, () => { deps.output.show(true); }), @@ -112,18 +131,18 @@ export const activate = async ( ): Promise => { const output = vscode.window.createOutputChannel(OUTPUT_CHANNEL_NAME); context.subscriptions.push(output); - addLogStream({ stream: channelStream(output), level: 'info' }); + addLogStream({ stream: channelStream(output), level: LOG_LEVELS.info }); const runner = createGitRunner({ logger }); const gitOk = await probeGit(runner); await setGitAvailable(gitOk); if (!gitOk) { - output.appendLine('Diffy: git binary not found on PATH — commands disabled.'); + output.appendLine(GIT_MISSING_MESSAGE); } const api = await getGitApi(); if (api === undefined) { - output.appendLine('Diffy: built-in git extension API unavailable.'); + output.appendLine(GIT_API_MISSING_MESSAGE); return; } @@ -131,9 +150,12 @@ export const activate = async ( const deps = { runner, gitApi: api, output, state } as const; registerDiffyContentProvider(context, makeRepoResolver(api, runner)); registerAll(context, deps); - logger.info({ repos: api.repositories.length }, 'extension.activated'); + logger.info( + { repos: api.repositories.length }, + LOG_EVENTS.extensionActivated, + ); }; export const deactivate = (): void => { - logger.info({}, 'extension.deactivated'); + logger.info({}, LOG_EVENTS.extensionDeactivated); }; diff --git a/src/git/GitRepo.ts b/src/git/GitRepo.ts index 73003e9..fc931c7 100644 --- a/src/git/GitRepo.ts +++ b/src/git/GitRepo.ts @@ -35,12 +35,12 @@ export interface LogArgs { } export interface GitRepo { - log(args?: LogArgs): Promise>; - nameStatus(args: DiffSides): Promise>; - numstat(args: DiffSides): Promise>; - show(args: ShowArgs): Promise>; - refs(): Promise>; - revParse(name: string): Promise>; + log: (args?: LogArgs) => Promise>; + nameStatus: (args: DiffSides) => Promise>; + numstat: (args: DiffSides) => Promise>; + show: (args: ShowArgs) => Promise>; + refs: () => Promise>; + revParse: (name: string) => Promise>; } const buildLogArgs = (params: { limit: number; ref?: string }): readonly string[] => { @@ -87,7 +87,7 @@ export const createGitRepo = ({ const r = await runner.run({ args: buildDiffArgs(args, 'numstat'), cwd }); return andThen(r, parseNumstat); }, - show: (args) => runner.run({ args: ['show', showSpec(args)], cwd }), + show: async (args) => await runner.run({ args: ['show', showSpec(args)], cwd }), refs: async () => { const r = await runner.run({ args: ['for-each-ref', `--format=${REFS_FORMAT}`], diff --git a/src/git/GitRunner.ts b/src/git/GitRunner.ts index c47fcd1..5d267c4 100644 --- a/src/git/GitRunner.ts +++ b/src/git/GitRunner.ts @@ -1,16 +1,19 @@ import { spawn, type ChildProcessWithoutNullStreams } from 'node:child_process'; -import { GIT_BINARY } from '../constants'; +import { GIT_BINARY, GIT_ERROR_KINDS, LOG_EVENTS } from '../constants'; import { type Result, ok, err } from '../result'; import { logger as defaultLogger, type Logger } from '../logger'; import type { GitError } from './types'; +const UNKNOWN_EXIT_LABEL = '?'; +const UNKNOWN_EXIT_CODE = -1; + export interface GitRunArgs { readonly args: readonly string[]; readonly cwd: string; } export interface GitRunner { - run(args: GitRunArgs): Promise>; + run: (args: GitRunArgs) => Promise>; } type Resolver = (r: Result) => void; @@ -29,16 +32,25 @@ const finishRun = (params: { stdout: string; stderr: string; }): Result => { - if (params.code === 0) {return ok(params.stdout);} + if (params.code === 0) { + return ok(params.stdout); + } + const codeLabel = params.code === null ? UNKNOWN_EXIT_LABEL : params.code.toString(); return err({ - kind: 'nonZeroExit', - message: `git ${params.subcommand} exited ${params.code ?? '?'}`, + kind: GIT_ERROR_KINDS.nonZeroExit, + message: `git ${params.subcommand} exited ${codeLabel}`, stderr: params.stderr, - exitCode: params.code ?? -1, + exitCode: params.code ?? UNKNOWN_EXIT_CODE, }); }; -const wireSubprocess = ({ child, argCount, subcommand, logger, resolve }: WireArgs): void => { +const wireSubprocess = ({ + child, + argCount, + subcommand, + logger, + resolve, +}: WireArgs): void => { let stdout = ''; let stderr = ''; child.stdout.on('data', (chunk: Buffer) => { @@ -48,22 +60,25 @@ const wireSubprocess = ({ child, argCount, subcommand, logger, resolve }: WireAr stderr += chunk.toString('utf8'); }); child.on('error', (e: Error) => { - logger.warn({ argCount }, 'git.run.spawnFailed'); - resolve(err({ kind: 'spawnFailed', message: e.message })); + logger.warn({ argCount }, LOG_EVENTS.gitRunSpawnFailed); + resolve(err({ kind: GIT_ERROR_KINDS.spawnFailed, message: e.message })); }); child.on('close', (code: number | null) => { - logger.debug({ exitCode: code, stdoutLen: stdout.length }, 'git.run.end'); + logger.debug( + { exitCode: code, stdoutLen: stdout.length }, + LOG_EVENTS.gitRunEnd, + ); resolve(finishRun({ code, subcommand, stdout, stderr })); }); }; -const runGit = ({ +const runGit = async ({ args, cwd, logger, }: GitRunArgs & { logger: Logger }): Promise> => { - logger.debug({ argCount: args.length }, 'git.run.start'); - return new Promise>((resolve) => { + logger.debug({ argCount: args.length }, LOG_EVENTS.gitRunStart); + return await new Promise>((resolve) => { const child = spawn(GIT_BINARY, [...args], { cwd }); wireSubprocess({ child, @@ -79,5 +94,5 @@ export const createGitRunner = ( deps: { logger?: Logger } = {}, ): GitRunner => { const logger = deps.logger ?? defaultLogger; - return { run: (a) => runGit({ ...a, logger }) }; + return { run: async (a) => await runGit({ ...a, logger }) }; }; diff --git a/src/git/parsers.ts b/src/git/parsers.ts index f79d631..3801ed0 100644 --- a/src/git/parsers.ts +++ b/src/git/parsers.ts @@ -1,4 +1,13 @@ -import { NUL, TAB, LF, REF_PREFIX_HEADS, REF_PREFIX_TAGS } from '../constants'; +import { + CHANGED_FILE_STATUSES, + GIT_ERROR_KINDS, + LF, + NUL, + REF_PREFIX_HEADS, + REF_PREFIX_TAGS, + REF_TYPES, + TAB, +} from '../constants'; import { type Result, ok, err } from '../result'; import type { ChangedFile, @@ -17,7 +26,7 @@ const CHAR_CODE_DIGIT_LO = 48; const CHAR_CODE_DIGIT_HI = 57; const errParse = (message: string): Result => - err({ kind: 'parseError', message }); + err({ kind: GIT_ERROR_KINDS.parseError, message }); const stripTrailingEmpty = (arr: readonly string[]): readonly string[] => { if (arr.length === 0) {return arr;} @@ -35,9 +44,13 @@ const isAllDigits = (s: string): boolean => { }; const refTypeFromName = (fullName: string): RefType => { - if (fullName.startsWith(REF_PREFIX_HEADS)) {return 'branch';} - if (fullName.startsWith(REF_PREFIX_TAGS)) {return 'tag';} - return 'other'; + if (fullName.startsWith(REF_PREFIX_HEADS)) { + return REF_TYPES.branch; + } + if (fullName.startsWith(REF_PREFIX_TAGS)) { + return REF_TYPES.tag; + } + return REF_TYPES.other; }; const parseLogRecord = (fields: readonly string[]): Result => { @@ -81,11 +94,18 @@ interface ParsedStatus { const parseStatusToken = (raw: string): Result => { if (raw.length === 0) {return errParse('parseNameStatus: empty status token');} const first = raw.charAt(0); - if (first === 'A' || first === 'M' || first === 'D') { + if ( + first === CHANGED_FILE_STATUSES.added || + first === CHANGED_FILE_STATUSES.modified || + first === CHANGED_FILE_STATUSES.deleted + ) { if (raw.length !== 1) {return errParse('parseNameStatus: extra chars on status');} return ok({ status: first, isRename: false }); } - if (first === 'R' || first === 'C') { + if ( + first === CHANGED_FILE_STATUSES.renamed || + first === CHANGED_FILE_STATUSES.copied + ) { const digits = raw.slice(1); if (!isAllDigits(digits)) {return errParse('parseNameStatus: bad similarity');} return ok({ status: first, similarity: Number.parseInt(digits, 10), isRename: true }); diff --git a/src/git/repoMatch.ts b/src/git/repoMatch.ts index 15c6cca..3445ef7 100644 --- a/src/git/repoMatch.ts +++ b/src/git/repoMatch.ts @@ -8,7 +8,7 @@ interface RepoLike { interface ApiLike> { readonly repositories: readonly R[]; - getRepository?(uri: U): R | null; + getRepository?: (uri: U) => R | null; } export const matchRepoByFsPath = >( diff --git a/src/git/types.ts b/src/git/types.ts index c2a1331..fddb31e 100644 --- a/src/git/types.ts +++ b/src/git/types.ts @@ -1,6 +1,14 @@ +import type { + CHANGED_FILE_STATUSES, + GIT_ERROR_KINDS, + REF_TYPES, + REV_KINDS, +} from '../constants'; + export type Sha = string; -export type ChangedFileStatus = 'A' | 'M' | 'D' | 'R' | 'C'; +export type ChangedFileStatus = + (typeof CHANGED_FILE_STATUSES)[keyof typeof CHANGED_FILE_STATUSES]; export interface ChangedFile { readonly status: ChangedFileStatus; @@ -25,15 +33,18 @@ export interface DiffStat { readonly binary: boolean; } -export type CommitRev = { readonly kind: 'commit'; readonly sha: Sha }; -export type WorkingCopyRev = { readonly kind: 'workingCopy' }; -export type IndexRev = { readonly kind: 'index' }; +export interface CommitRev { + readonly kind: typeof REV_KINDS.commit; + readonly sha: Sha; +} +export interface WorkingCopyRev { readonly kind: typeof REV_KINDS.workingCopy } +export interface IndexRev { readonly kind: typeof REV_KINDS.index } export type RevSpec = CommitRev | WorkingCopyRev | IndexRev; export type DiffyAddressableRev = CommitRev | IndexRev; -export type RefType = 'branch' | 'tag' | 'other'; +export type RefType = (typeof REF_TYPES)[keyof typeof REF_TYPES]; export interface Ref { readonly name: string; @@ -43,11 +54,7 @@ export interface Ref { } export type GitErrorKind = - | 'spawnFailed' - | 'nonZeroExit' - | 'parseError' - | 'notARepo' - | 'notFound'; + (typeof GIT_ERROR_KINDS)[keyof typeof GIT_ERROR_KINDS]; export interface GitError { readonly kind: GitErrorKind; diff --git a/src/logger.ts b/src/logger.ts index 970afcb..63dcc2c 100644 --- a/src/logger.ts +++ b/src/logger.ts @@ -1,11 +1,14 @@ import pino, { type Logger as PinoLogger, type Level, multistream } from 'pino'; +import { LOG_LEVELS } from './constants'; + +const ENV_LOG_LEVEL_VAR = 'DIFFY_LOG_LEVEL'; export interface Logger { - trace(fields: object, msg?: string): void; - debug(fields: object, msg?: string): void; - info(fields: object, msg?: string): void; - warn(fields: object, msg?: string): void; - error(fields: object, msg?: string): void; + trace: (fields: object, msg?: string) => void; + debug: (fields: object, msg?: string) => void; + info: (fields: object, msg?: string) => void; + warn: (fields: object, msg?: string) => void; + error: (fields: object, msg?: string) => void; } export interface LogStreamEntry { @@ -15,16 +18,18 @@ export interface LogStreamEntry { const streams: LogStreamEntry[] = [{ stream: process.stdout }]; -const envLevel = process.env['DIFFY_LOG_LEVEL']; +const envLevel = process.env[ENV_LOG_LEVEL_VAR]; const buildPino = (): PinoLogger => pino( { - level: envLevel ?? 'info', + level: envLevel ?? LOG_LEVELS.info, base: null, timestamp: pino.stdTimeFunctions.isoTime, }, - multistream(streams.map((s) => ({ stream: s.stream, level: s.level ?? 'trace' }))), + multistream( + streams.map((s) => ({ stream: s.stream, level: s.level ?? LOG_LEVELS.trace })), + ), ); let underlying: PinoLogger = buildPino(); diff --git a/src/menus.ts b/src/menus.ts index d5b770e..da7b5ec 100644 --- a/src/menus.ts +++ b/src/menus.ts @@ -1,4 +1,11 @@ -import { COMMAND_IDS } from './constants'; +import { + COMMAND_IDS, + MENU_GROUP_PREFIX, + MENU_IDS, + MENU_WHEN, + TITLE_PREFIX, + UI_TEXT, +} from './constants'; export interface MenuEntry { readonly command: string; @@ -8,21 +15,13 @@ export interface MenuEntry { export interface MenuManifest { readonly menus: Record; - readonly commandPalette: readonly { readonly command: string; readonly when: string }[]; + readonly commandPalette: readonly { + readonly command: string; + readonly when: string; + }[]; } -const MENU_IDS = { - scmHistoryItem: 'scm/historyItem/context', - scmResourceState: 'scm/resourceState/context', - editorTitleContext: 'editor/title/context', - explorerContext: 'explorer/context', - commandPalette: 'commandPalette', -} as const; - -const WHEN_GIT = 'scmProvider == git'; -const WHEN_FILE = 'resourceScheme == file'; -const WHEN_FILE_NOT_FOLDER = 'resourceScheme == file && !explorerResourceIsFolder'; -const NEVER = 'false'; +const ENTRY_INDEX_OFFSET = 1; const commitLevelCommands = [ COMMAND_IDS.compareWith, @@ -46,18 +45,34 @@ const groupedEntries = ( commands.map((command, i) => ({ command, when, - group: `${groupPrefix}@${i + 1}`, + group: `${groupPrefix}@${(i + ENTRY_INDEX_OFFSET).toString()}`, })); const hideFromPalette = (commands: readonly string[]) => - commands.map((command) => ({ command, when: NEVER })); + commands.map((command) => ({ command, when: MENU_WHEN.never })); export const buildMenuManifest = (): MenuManifest => ({ menus: { - [MENU_IDS.scmHistoryItem]: groupedEntries(commitLevelCommands, WHEN_GIT, 'diffy'), - [MENU_IDS.scmResourceState]: groupedEntries(fileLevelCommands, WHEN_GIT, 'diffy'), - [MENU_IDS.editorTitleContext]: groupedEntries(fileLevelCommands, WHEN_FILE, 'diffy'), - [MENU_IDS.explorerContext]: groupedEntries(fileLevelCommands, WHEN_FILE_NOT_FOLDER, 'diffy'), + [MENU_IDS.scmHistoryItem]: groupedEntries( + commitLevelCommands, + MENU_WHEN.scmGit, + MENU_GROUP_PREFIX, + ), + [MENU_IDS.scmResourceState]: groupedEntries( + fileLevelCommands, + MENU_WHEN.scmGit, + MENU_GROUP_PREFIX, + ), + [MENU_IDS.editorTitleContext]: groupedEntries( + fileLevelCommands, + MENU_WHEN.resourceFile, + MENU_GROUP_PREFIX, + ), + [MENU_IDS.explorerContext]: groupedEntries( + fileLevelCommands, + MENU_WHEN.resourceFileNotFolder, + MENU_GROUP_PREFIX, + ), }, commandPalette: hideFromPalette([ COMMAND_IDS.compareWith, @@ -70,15 +85,15 @@ export const buildMenuManifest = (): MenuManifest => ({ }); export const COMMAND_TITLES: Record = { - [COMMAND_IDS.compareWith]: 'Diffy: Compare with…', - [COMMAND_IDS.compareWithWorkingCopy]: 'Diffy: Compare with Working Copy', - [COMMAND_IDS.compareWithPrevious]: 'Diffy: Compare with Previous', - [COMMAND_IDS.compareWithBranch]: 'Diffy: Compare with Branch…', - [COMMAND_IDS.compareWithTag]: 'Diffy: Compare with Tag…', - [COMMAND_IDS.compareTwoCommits]: 'Diffy: Compare Two Commits', - [COMMAND_IDS.compareFileWithCommit]: 'Diffy: Compare with Commit…', - [COMMAND_IDS.compareFileWithBranch]: 'Diffy: Compare with Branch…', - [COMMAND_IDS.compareFileWithTag]: 'Diffy: Compare with Tag…', - [COMMAND_IDS.reopenLast]: 'Diffy: Reopen Last Comparison', - [COMMAND_IDS.showLogs]: 'Diffy: Show Logs', + [COMMAND_IDS.compareWith]: `${TITLE_PREFIX} Compare with…`, + [COMMAND_IDS.compareWithWorkingCopy]: `${TITLE_PREFIX} Compare with ${UI_TEXT.workingCopy}`, + [COMMAND_IDS.compareWithPrevious]: `${TITLE_PREFIX} Compare with Previous`, + [COMMAND_IDS.compareWithBranch]: `${TITLE_PREFIX} Compare with ${UI_TEXT.branchLabel}…`, + [COMMAND_IDS.compareWithTag]: `${TITLE_PREFIX} Compare with ${UI_TEXT.tagLabel}…`, + [COMMAND_IDS.compareTwoCommits]: `${TITLE_PREFIX} Compare Two Commits`, + [COMMAND_IDS.compareFileWithCommit]: `${TITLE_PREFIX} Compare with Commit…`, + [COMMAND_IDS.compareFileWithBranch]: `${TITLE_PREFIX} Compare with ${UI_TEXT.branchLabel}…`, + [COMMAND_IDS.compareFileWithTag]: `${TITLE_PREFIX} Compare with ${UI_TEXT.tagLabel}…`, + [COMMAND_IDS.reopenLast]: `${TITLE_PREFIX} Reopen Last Comparison`, + [COMMAND_IDS.showLogs]: `${TITLE_PREFIX} Show Logs`, }; diff --git a/src/providers/DiffyContentProvider.ts b/src/providers/DiffyContentProvider.ts index 45a55c1..df178fd 100644 --- a/src/providers/DiffyContentProvider.ts +++ b/src/providers/DiffyContentProvider.ts @@ -1,5 +1,5 @@ import * as vscode from 'vscode'; -import { SCHEME } from '../constants'; +import { LOG_EVENTS, SCHEME } from '../constants'; import type { GitRepo } from '../git/GitRepo'; import { logger } from '../logger'; import { parseDiffyUri } from '../ui/uri'; @@ -14,17 +14,17 @@ export class DiffyContentProvider implements vscode.TextDocumentContentProvider async provideTextDocumentContent(uri: vscode.Uri): Promise { const parsed = parseDiffyUri(uri.toString()); if (!parsed.ok) { - logger.warn({ kind: parsed.error.kind }, 'provider.parseFailed'); + logger.warn({ kind: parsed.error.kind }, LOG_EVENTS.providerParseFailed); return ''; } const repo = this.resolveRepo(uri); if (repo === undefined) { - logger.warn({}, 'provider.repoUnresolved'); + logger.warn({}, LOG_EVENTS.providerRepoUnresolved); return ''; } const r = await repo.show({ rev: parsed.value.rev, path: parsed.value.path }); if (!r.ok) { - logger.debug({ kind: r.error.kind }, 'provider.showFailed'); + logger.debug({ kind: r.error.kind }, LOG_EVENTS.providerShowFailed); return ''; } return r.value; diff --git a/src/result.ts b/src/result.ts index 246e723..3aa96b4 100644 --- a/src/result.ts +++ b/src/result.ts @@ -1,5 +1,5 @@ -export type Ok = { readonly ok: true; readonly value: T }; -export type Err = { readonly ok: false; readonly error: E }; +export interface Ok { readonly ok: true; readonly value: T } +export interface Err { readonly ok: false; readonly error: E } export type Result = Ok | Err; export const ok = (value: T): Ok => ({ ok: true, value }); @@ -20,3 +20,15 @@ export const andThen = ( export const unwrapOr = (r: Result, fallback: T): T => r.ok ? r.value : fallback; + +export function expectOk(r: Result): asserts r is Ok { + if (!r.ok) { + throw new Error(`expected Ok, got Err: ${JSON.stringify(r.error)}`); + } +} + +export function expectErr(r: Result): asserts r is Err { + if (r.ok) { + throw new Error(`expected Err, got Ok: ${JSON.stringify(r.value)}`); + } +} diff --git a/src/state.ts b/src/state.ts index 13d866f..35c2986 100644 --- a/src/state.ts +++ b/src/state.ts @@ -1,17 +1,18 @@ import type * as vscode from 'vscode'; import { MEMENTO_KEYS } from './constants'; +import type { REV_KINDS } from './constants'; import type { RevSpec, Sha } from './git/types'; export interface LastComparison { - readonly revA: { readonly kind: 'commit'; readonly sha: Sha }; + readonly revA: { readonly kind: typeof REV_KINDS.commit; readonly sha: Sha }; readonly revB: RevSpec; readonly repoRoot: string; } export interface MementoStore { - getLastComparison(): LastComparison | undefined; - setLastComparison(value: LastComparison): Promise; - clearLastComparison(): Promise; + getLastComparison: () => LastComparison | undefined; + setLastComparison: (value: LastComparison) => Promise; + clearLastComparison: () => Promise; } export const isLastComparison = (raw: unknown): raw is LastComparison => { diff --git a/src/test/runTests.ts b/src/test/runTests.ts index 1c8de74..debc148 100644 --- a/src/test/runTests.ts +++ b/src/test/runTests.ts @@ -15,7 +15,8 @@ const ensureSeedRepo = (): void => { } const r = spawnSync('bash', [seedScript], { stdio: 'inherit' }); if (r.status !== 0) { - throw new Error(`seed.sh failed with exit ${r.status ?? '?'}`); + const exitLabel = r.status === null ? '?' : r.status.toString(); + throw new Error(`seed.sh failed with exit ${exitLabel}`); } }; diff --git a/src/test/suite/activation.test.ts b/src/test/suite/activation.test.ts index 41355bd..e60d0f5 100644 --- a/src/test/suite/activation.test.ts +++ b/src/test/suite/activation.test.ts @@ -4,6 +4,7 @@ import { COMMAND_IDS, OUTPUT_CHANNEL_NAME } from '../../constants'; import { tick } from './helpers'; const EXTENSION_ID = 'nimblesite.diffy'; +const TICK_MS = 20; const ALL_COMMAND_IDS: readonly string[] = Object.values(COMMAND_IDS); @@ -11,14 +12,15 @@ describe('activation', () => { it('extension is present, activates, and every command id is registered', async () => { const ext = vscode.extensions.getExtension(EXTENSION_ID); assert.ok(ext, `extension ${EXTENSION_ID} should be present`); - if (ext === undefined) { - return; - } if (!ext.isActive) { await ext.activate(); } - assert.equal(ext.isActive, true, 'extension should be active after activate()'); - await tick(20); + assert.equal( + ext.isActive, + true, + 'extension should be active after activate()', + ); + await tick(TICK_MS); const registered = await vscode.commands.getCommands(true); for (const id of ALL_COMMAND_IDS) { assert.ok( @@ -31,20 +33,16 @@ describe('activation', () => { it('exposes a workspace folder containing the seeded git repo', () => { const folders = vscode.workspace.workspaceFolders; assert.ok(folders, 'workspace folders should be set'); - if (folders === undefined) { - return; - } assert.equal(folders.length, 1); const folder = folders[0]; assert.ok(folder); - assert.match(folder?.uri.fsPath ?? '', /repo-seed\/workspace$/); + assert.match(folder.uri.fsPath, /repo-seed\/workspace$/); }); it('shows logs command opens the Diffy OutputChannel without errors', async () => { await vscode.commands.executeCommand(COMMAND_IDS.showLogs); - // We can't assert that the channel is "visible", but the command must - // have a registered handler and complete without throwing. - assert.ok(true, 'showLogs invoked'); + // The channel's "visible" state isn't observable from the test host, but + // the command must have a registered handler and complete without throwing. assert.equal(typeof OUTPUT_CHANNEL_NAME, 'string'); assert.ok(OUTPUT_CHANNEL_NAME.length > 0); }); diff --git a/src/test/suite/helpers.ts b/src/test/suite/helpers.ts index 907e88e..56962dd 100644 --- a/src/test/suite/helpers.ts +++ b/src/test/suite/helpers.ts @@ -40,8 +40,8 @@ export const readSeedShas = (): SeedShas => { return { first, second, third }; }; -export const tick = (count = 30): Promise => - new Promise((resolve) => { +export const tick = async (count = 30): Promise => + { await new Promise((resolve) => { let i = 0; const step = (): void => { i++; @@ -52,12 +52,12 @@ export const tick = (count = 30): Promise => setImmediate(step); }; setImmediate(step); - }); + }); }; -const wait = (ms: number): Promise => - new Promise((resolve) => { +const wait = async (ms: number): Promise => + { await new Promise((resolve) => { setTimeout(resolve, ms); - }); + }); }; const PICKER_RENDER_MS = 900; @@ -86,12 +86,12 @@ export const allDiffTabs = (): vscode.Tab[] => .flatMap((g) => g.tabs) .filter((t) => t.input instanceof vscode.TabInputTextDiff); -export const waitForDiffTab = ({ +export const waitForDiffTab = async ({ timeoutMs = 8000, }: { timeoutMs?: number; } = {}): Promise => { - return new Promise((resolve, reject) => { + return await new Promise((resolve, reject) => { const existing = allDiffTabs()[0]; if (existing !== undefined) { resolve(existing); @@ -120,7 +120,7 @@ export const closeAllEditors = async (): Promise => { export const openFileInEditor = async (relPath: string): Promise => { const uri = vscode.Uri.file(path.join(workspaceRoot(), relPath)); const doc = await vscode.workspace.openTextDocument(uri); - return vscode.window.showTextDocument(doc); + return await vscode.window.showTextDocument(doc); }; export const tabInputUris = ( @@ -134,11 +134,11 @@ export const tabInputUris = ( interface GitApiShape { readonly repositories: readonly { readonly rootUri: vscode.Uri }[]; - onDidOpenRepository(handler: () => void): { dispose(): void }; + onDidOpenRepository: (handler: () => void) => { dispose: () => void }; } interface GitExtensionShape { - getAPI(version: 1): GitApiShape; + getAPI: (version: 1) => GitApiShape; } export const waitForRepoReady = async (timeoutMs = 15000): Promise => { diff --git a/src/test/unit/format.test.ts b/src/test/unit/format.test.ts index 88a9942..8077c24 100644 --- a/src/test/unit/format.test.ts +++ b/src/test/unit/format.test.ts @@ -129,15 +129,21 @@ describe('mergeChangedFilesWithStats', () => { }); it('handles rename entries (new path differs from old)', () => { - const files: ChangedFile[] = [ - { status: 'R', path: 'b2.txt', oldPath: 'b.txt', similarity: 100 }, - ]; - const stats: DiffStat[] = [ - { path: 'b2.txt', oldPath: 'b.txt', added: 1, deleted: 0, binary: false }, - ]; - const r = mergeChangedFilesWithStats(files, stats); - assert.equal(r[0]?.stat.added, 1); - assert.equal(r[0]?.stat.oldPath, 'b.txt'); + const file: ChangedFile = { + status: 'R', + path: 'b2.txt', + oldPath: 'b.txt', + similarity: 100, + }; + const stat: DiffStat = { + path: 'b2.txt', + oldPath: 'b.txt', + added: 1, + deleted: 0, + binary: false, + }; + const r = mergeChangedFilesWithStats([file], [stat]); + assert.deepEqual(r, [{ file, stat }]); }); }); diff --git a/src/test/unit/menus.test.ts b/src/test/unit/menus.test.ts index 2738abc..ed22a9f 100644 --- a/src/test/unit/menus.test.ts +++ b/src/test/unit/menus.test.ts @@ -1,9 +1,16 @@ import { strict as assert } from 'node:assert'; import { readFileSync } from 'node:fs'; import { resolve } from 'node:path'; -import { COMMAND_IDS } from '../../constants'; +import { + COMMAND_IDS, + MENU_IDS, + MENU_WHEN, + TITLE_PREFIX, +} from '../../constants'; import { COMMAND_TITLES, buildMenuManifest } from '../../menus'; +const PROPOSED_API_HISTORY_ITEM_MENU = 'contribSourceControlHistoryItemMenu'; + const repoRoot = resolve(__dirname, '..', '..', '..'); const readPackageJson = (): Record => JSON.parse(readFileSync(resolve(repoRoot, 'package.json'), 'utf8')) as Record< @@ -19,14 +26,30 @@ const getContributes = (pkg: Record): Record = return c as Record; }; +const getMenus = ( + contributes: Record, +): Record => { + const m = contributes['menus']; + if (typeof m !== 'object' || m === null) { + throw new Error('package.json: contributes.menus missing'); + } + return m as Record; +}; + +const titlePrefixPattern = new RegExp( + `^${TITLE_PREFIX.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')} `, +); + describe('menu manifest — single source of truth', () => { it('every COMMAND_ID has a human title in COMMAND_TITLES', () => { for (const id of Object.values(COMMAND_IDS)) { - assert.ok( - COMMAND_TITLES[id] !== undefined, - `missing title for command ${id}`, + const title = COMMAND_TITLES[id]; + assert.ok(title !== undefined, `missing title for command ${id}`); + assert.match( + title, + titlePrefixPattern, + `title for ${id} must start with "${TITLE_PREFIX} "`, ); - assert.match(COMMAND_TITLES[id], /^Diffy: /, `title for ${id} must start with "Diffy: "`); } }); @@ -36,17 +59,17 @@ describe('menu manifest — single source of truth', () => { assert.deepEqual( menuIds.sort(), [ - 'editor/title/context', - 'explorer/context', - 'scm/historyItem/context', - 'scm/resourceState/context', - ], + MENU_IDS.editorTitleContext, + MENU_IDS.explorerContext, + MENU_IDS.scmHistoryItem, + MENU_IDS.scmResourceState, + ].sort(), 'menu IDs must match VSCode contribution points (note: singular scm/historyItem/context)', ); }); it('SCM history item menu contains all five commit-level Diffy commands', () => { - const entries = buildMenuManifest().menus['scm/historyItem/context']; + const entries = buildMenuManifest().menus[MENU_IDS.scmHistoryItem]; assert.ok(entries !== undefined); const cmds = entries.map((e) => e.command); assert.deepEqual(cmds, [ @@ -57,7 +80,7 @@ describe('menu manifest — single source of truth', () => { COMMAND_IDS.compareWithTag, ]); for (const e of entries) { - assert.equal(e.when, 'scmProvider == git'); + assert.equal(e.when, MENU_WHEN.scmGit); assert.match(e.group, /^diffy@\d+$/); } }); @@ -70,9 +93,9 @@ describe('menu manifest — single source of truth', () => { COMMAND_IDS.compareFileWithTag, ]; for (const menuId of [ - 'editor/title/context', - 'explorer/context', - 'scm/resourceState/context', + MENU_IDS.editorTitleContext, + MENU_IDS.explorerContext, + MENU_IDS.scmResourceState, ]) { const entries = m[menuId]; assert.ok(entries !== undefined, `missing ${menuId}`); @@ -98,7 +121,7 @@ describe('menu manifest — single source of truth', () => { assert.ok(hidden.includes(cmd), `${cmd} should be hidden from the palette`); } for (const e of palette) { - assert.equal(e.when, 'false'); + assert.equal(e.when, MENU_WHEN.never); } }); @@ -106,21 +129,23 @@ describe('menu manifest — single source of truth', () => { const manifest = buildMenuManifest(); const pkg = readPackageJson(); const contributes = getContributes(pkg); - - const pkgMenus = contributes['menus'] as Record; - assert.ok(pkgMenus !== undefined, 'package.json missing contributes.menus'); + const pkgMenus = getMenus(contributes); for (const [menuId, entries] of Object.entries(manifest.menus)) { const written = pkgMenus[menuId]; assert.deepEqual( written, - entries.map((e) => ({ command: e.command, when: e.when, group: e.group })), + entries.map((e) => ({ + command: e.command, + when: e.when, + group: e.group, + })), `package.json ${menuId} drifted from src/menus.ts — run 'npm run sync:menus'`, ); } assert.deepEqual( - pkgMenus['commandPalette'], + pkgMenus[MENU_IDS.commandPalette], manifest.commandPalette.map((e) => ({ command: e.command, when: e.when })), 'package.json commandPalette block drifted from src/menus.ts', ); @@ -129,14 +154,21 @@ describe('menu manifest — single source of truth', () => { it('package.json contributes.commands lists every COMMAND_TITLES entry exactly', () => { const pkg = readPackageJson(); const contributes = getContributes(pkg); - const cmds = contributes['commands'] as readonly { readonly command: string; readonly title: string }[]; + const cmds = contributes['commands'] as readonly { + readonly command: string; + readonly title: string; + }[]; assert.ok(Array.isArray(cmds)); const expected = Object.entries(COMMAND_TITLES).map(([command, title]) => ({ command, title, })); - assert.deepEqual(cmds, expected, 'package.json commands drifted — run npm run sync:menus'); + assert.deepEqual( + cmds, + expected, + 'package.json commands drifted — run npm run sync:menus', + ); }); it('package.json declares the contribSourceControlHistoryItemMenu proposed API (required for the commit-row context menu)', () => { @@ -147,8 +179,8 @@ describe('menu manifest — single source of truth', () => { 'package.json must declare enabledApiProposals so SCM Graph commit menus actually appear', ); assert.ok( - (proposals as readonly string[]).includes('contribSourceControlHistoryItemMenu'), - 'enabledApiProposals must include contribSourceControlHistoryItemMenu', + (proposals as readonly string[]).includes(PROPOSED_API_HISTORY_ITEM_MENU), + `enabledApiProposals must include ${PROPOSED_API_HISTORY_ITEM_MENU}`, ); }); }); diff --git a/src/test/unit/parsers.test.ts b/src/test/unit/parsers.test.ts index 1456b82..39968de 100644 --- a/src/test/unit/parsers.test.ts +++ b/src/test/unit/parsers.test.ts @@ -1,10 +1,12 @@ import { strict as assert } from 'node:assert'; +import { GIT_ERROR_KINDS, REF_TYPES } from '../../constants'; import { parseLog, parseNameStatus, parseNumstat, parseRefs, } from '../../git/parsers'; +import { expectErr, expectOk } from '../../result'; const NUL = '\x00'; const TAB = '\t'; @@ -20,124 +22,156 @@ const logRecord = ( describe('parseLog', () => { it('returns empty array for empty input', () => { const r = parseLog(''); - assert.equal(r.ok, true); - if (r.ok) {assert.deepEqual(r.value, []);} + expectOk(r); + assert.deepEqual(r.value, []); }); it('parses a single commit with trailing NUL', () => { - const stdout = logRecord('abc123def456', 'abc123d', 'Alice', '1700000000', 'init') + NUL; + const stdout = + logRecord('abc123def456', 'abc123d', 'Alice', '1700000000', 'init') + NUL; const r = parseLog(stdout); - assert.equal(r.ok, true); - if (!r.ok) {return;} - assert.equal(r.value.length, 1); - assert.deepEqual(r.value[0], { - sha: 'abc123def456', - shortSha: 'abc123d', - author: 'Alice', - authorTime: 1700000000, - subject: 'init', - }); + expectOk(r); + assert.deepEqual(r.value, [ + { + sha: 'abc123def456', + shortSha: 'abc123d', + author: 'Alice', + authorTime: 1700000000, + subject: 'init', + }, + ]); }); it('parses two commits separated by NUL', () => { const a = logRecord('aaaa1111', 'aaaa111', 'Alice', '1700000000', 'first'); - const b = logRecord('bbbb2222', 'bbbb222', 'Bob', '1700000100', 'second commit'); + const b = logRecord( + 'bbbb2222', + 'bbbb222', + 'Bob', + '1700000100', + 'second commit', + ); const r = parseLog(`${a}${NUL}${b}${NUL}`); - assert.equal(r.ok, true); - if (!r.ok) {return;} - assert.equal(r.value.length, 2); - assert.equal(r.value[0]?.sha, 'aaaa1111'); - assert.equal(r.value[0]?.subject, 'first'); - assert.equal(r.value[1]?.sha, 'bbbb2222'); - assert.equal(r.value[1]?.subject, 'second commit'); - assert.equal(r.value[1]?.author, 'Bob'); - assert.equal(r.value[1]?.authorTime, 1700000100); + expectOk(r); + assert.deepEqual(r.value, [ + { + sha: 'aaaa1111', + shortSha: 'aaaa111', + author: 'Alice', + authorTime: 1700000000, + subject: 'first', + }, + { + sha: 'bbbb2222', + shortSha: 'bbbb222', + author: 'Bob', + authorTime: 1700000100, + subject: 'second commit', + }, + ]); }); it('parses without trailing NUL when field count matches', () => { const stdout = logRecord('abcd1234', 'abcd123', 'Alice', '1700000000', 'init'); const r = parseLog(stdout); - assert.equal(r.ok, true); - if (r.ok) {assert.equal(r.value.length, 1);} + expectOk(r); + assert.equal(r.value.length, 1); }); it('preserves an empty subject', () => { - const stdout = logRecord('abcd1234', 'abcd123', 'Alice', '1700000000', '') + NUL; + const stdout = + logRecord('abcd1234', 'abcd123', 'Alice', '1700000000', '') + NUL; const r = parseLog(stdout); - assert.equal(r.ok, true); - if (r.ok) {assert.equal(r.value[0]?.subject, '');} + expectOk(r); + assert.deepEqual(r.value, [ + { + sha: 'abcd1234', + shortSha: 'abcd123', + author: 'Alice', + authorTime: 1700000000, + subject: '', + }, + ]); }); it('preserves unicode and spaces in author/subject', () => { const stdout = - logRecord('abcd1234', 'abcd123', 'Élise Müller', '1700000000', 'feat: 日本語 fix ⚡') + NUL; + logRecord( + 'abcd1234', + 'abcd123', + 'Élise Müller', + '1700000000', + 'feat: 日本語 fix ⚡', + ) + NUL; const r = parseLog(stdout); - assert.equal(r.ok, true); - if (!r.ok) {return;} - assert.equal(r.value[0]?.author, 'Élise Müller'); - assert.equal(r.value[0]?.subject, 'feat: 日本語 fix ⚡'); + expectOk(r); + assert.deepEqual(r.value, [ + { + sha: 'abcd1234', + shortSha: 'abcd123', + author: 'Élise Müller', + authorTime: 1700000000, + subject: 'feat: 日本語 fix ⚡', + }, + ]); }); it('errors when field count is not a multiple of 5', () => { const r = parseLog(`a${NUL}b${NUL}c${NUL}d${NUL}`); - assert.equal(r.ok, false); - if (!r.ok) { - assert.equal(r.error.kind, 'parseError'); - assert.match(r.error.message, /multiple of 5/); - } + expectErr(r); + assert.equal(r.error.kind, GIT_ERROR_KINDS.parseError); + assert.match(r.error.message, /multiple of 5/); }); it('errors when timestamp is not numeric', () => { const stdout = logRecord('a', 'a', 'Alice', 'NOT_A_NUMBER', 'x') + NUL; const r = parseLog(stdout); - assert.equal(r.ok, false); - if (!r.ok) {assert.match(r.error.message, /timestamp/);} + expectErr(r); + assert.match(r.error.message, /timestamp/); }); it('errors when timestamp is empty', () => { const stdout = logRecord('a', 'a', 'Alice', '', 'x') + NUL; const r = parseLog(stdout); - assert.equal(r.ok, false); - if (!r.ok) {assert.match(r.error.message, /timestamp/);} + expectErr(r); + assert.match(r.error.message, /timestamp/); }); it('errors when timestamp has leading/trailing whitespace', () => { const stdout = logRecord('a', 'a', 'Alice', ' 1700000000', 'x') + NUL; const r = parseLog(stdout); - assert.equal(r.ok, false); + expectErr(r); }); }); describe('parseNameStatus', () => { it('returns empty array for empty input', () => { const r = parseNameStatus(''); - assert.equal(r.ok, true); - if (r.ok) {assert.deepEqual(r.value, []);} + expectOk(r); + assert.deepEqual(r.value, []); }); it('parses an Added file', () => { const r = parseNameStatus(`A${NUL}new.txt${NUL}`); - assert.equal(r.ok, true); - if (!r.ok) {return;} + expectOk(r); assert.deepEqual(r.value, [{ status: 'A', path: 'new.txt' }]); }); it('parses a Modified file', () => { const r = parseNameStatus(`M${NUL}a.txt${NUL}`); - assert.equal(r.ok, true); - if (r.ok) {assert.deepEqual(r.value, [{ status: 'M', path: 'a.txt' }]);} + expectOk(r); + assert.deepEqual(r.value, [{ status: 'M', path: 'a.txt' }]); }); it('parses a Deleted file', () => { const r = parseNameStatus(`D${NUL}gone.txt${NUL}`); - assert.equal(r.ok, true); - if (r.ok) {assert.deepEqual(r.value, [{ status: 'D', path: 'gone.txt' }]);} + expectOk(r); + assert.deepEqual(r.value, [{ status: 'D', path: 'gone.txt' }]); }); it('parses a Rename with similarity', () => { const r = parseNameStatus(`R100${NUL}old.txt${NUL}new.txt${NUL}`); - assert.equal(r.ok, true); - if (!r.ok) {return;} + expectOk(r); assert.deepEqual(r.value, [ { status: 'R', path: 'new.txt', oldPath: 'old.txt', similarity: 100 }, ]); @@ -145,8 +179,7 @@ describe('parseNameStatus', () => { it('parses a Copy with similarity', () => { const r = parseNameStatus(`C75${NUL}src.txt${NUL}dst.txt${NUL}`); - assert.equal(r.ok, true); - if (!r.ok) {return;} + expectOk(r); assert.deepEqual(r.value, [ { status: 'C', path: 'dst.txt', oldPath: 'src.txt', similarity: 75 }, ]); @@ -160,73 +193,71 @@ describe('parseNameStatus', () => { `R98${NUL}was.txt${NUL}now.txt${NUL}` + `C50${NUL}src.txt${NUL}dst.txt${NUL}`; const r = parseNameStatus(stdout); - assert.equal(r.ok, true); - if (!r.ok) {return;} - assert.equal(r.value.length, 5); - assert.equal(r.value[0]?.status, 'A'); - assert.equal(r.value[1]?.status, 'M'); - assert.equal(r.value[2]?.status, 'D'); - assert.equal(r.value[3]?.status, 'R'); - assert.equal(r.value[3]?.similarity, 98); - assert.equal(r.value[4]?.status, 'C'); - assert.equal(r.value[4]?.similarity, 50); + expectOk(r); + assert.deepEqual(r.value, [ + { status: 'A', path: 'added.txt' }, + { status: 'M', path: 'modified.txt' }, + { status: 'D', path: 'deleted.txt' }, + { status: 'R', path: 'now.txt', oldPath: 'was.txt', similarity: 98 }, + { status: 'C', path: 'dst.txt', oldPath: 'src.txt', similarity: 50 }, + ]); }); it('preserves spaces, unicode, and quotes in paths', () => { const stdout = `M${NUL}path with spaces/日本語 "name".txt${NUL}`; const r = parseNameStatus(stdout); - assert.equal(r.ok, true); - if (!r.ok) {return;} - assert.equal(r.value[0]?.path, 'path with spaces/日本語 "name".txt'); + expectOk(r); + assert.deepEqual(r.value, [ + { status: 'M', path: 'path with spaces/日本語 "name".txt' }, + ]); }); it('errors on unknown status letter', () => { const r = parseNameStatus(`X${NUL}a.txt${NUL}`); - assert.equal(r.ok, false); - if (!r.ok) {assert.match(r.error.message, /unknown status/);} + expectErr(r); + assert.match(r.error.message, /unknown status/); }); it('errors when A/M/D has trailing chars', () => { const r = parseNameStatus(`A100${NUL}a.txt${NUL}`); - assert.equal(r.ok, false); - if (!r.ok) {assert.match(r.error.message, /extra chars/);} + expectErr(r); + assert.match(r.error.message, /extra chars/); }); it('errors on rename without similarity digits', () => { const r = parseNameStatus(`R${NUL}old${NUL}new${NUL}`); - assert.equal(r.ok, false); - if (!r.ok) {assert.match(r.error.message, /similarity/);} + expectErr(r); + assert.match(r.error.message, /similarity/); }); it('errors on rename with non-digit similarity', () => { const r = parseNameStatus(`R1x0${NUL}old${NUL}new${NUL}`); - assert.equal(r.ok, false); - if (!r.ok) {assert.match(r.error.message, /similarity/);} + expectErr(r); + assert.match(r.error.message, /similarity/); }); it('errors on truncated rename (missing new path)', () => { const r = parseNameStatus(`R100${NUL}old.txt${NUL}`); - assert.equal(r.ok, false); - if (!r.ok) {assert.match(r.error.message, /missing paths/);} + expectErr(r); + assert.match(r.error.message, /missing paths/); }); it('errors when simple status is missing its path', () => { const r = parseNameStatus(`M${NUL}`); - assert.equal(r.ok, false); + expectErr(r); }); }); describe('parseNumstat', () => { it('returns empty array for empty input', () => { const r = parseNumstat(''); - assert.equal(r.ok, true); - if (r.ok) {assert.deepEqual(r.value, []);} + expectOk(r); + assert.deepEqual(r.value, []); }); it('parses a regular numstat record', () => { const r = parseNumstat(`12${TAB}3${TAB}hello.txt${NUL}`); - assert.equal(r.ok, true); - if (!r.ok) {return;} + expectOk(r); assert.deepEqual(r.value, [ { path: 'hello.txt', added: 12, deleted: 3, binary: false }, ]); @@ -234,8 +265,7 @@ describe('parseNumstat', () => { it('parses a binary marker ("-" / "-")', () => { const r = parseNumstat(`-${TAB}-${TAB}image.png${NUL}`); - assert.equal(r.ok, true); - if (!r.ok) {return;} + expectOk(r); assert.deepEqual(r.value, [ { path: 'image.png', added: 0, deleted: 0, binary: true }, ]); @@ -243,16 +273,16 @@ describe('parseNumstat', () => { it('parses a rename record', () => { const r = parseNumstat(`3${TAB}1${TAB}${NUL}old.txt${NUL}new.txt${NUL}`); - assert.equal(r.ok, true); - if (!r.ok) {return;} - assert.equal(r.value.length, 1); - assert.deepEqual(r.value[0], { - path: 'new.txt', - oldPath: 'old.txt', - added: 3, - deleted: 1, - binary: false, - }); + expectOk(r); + assert.deepEqual(r.value, [ + { + path: 'new.txt', + oldPath: 'old.txt', + added: 3, + deleted: 1, + binary: false, + }, + ]); }); it('parses a mixed batch with regular, binary, and rename', () => { @@ -261,70 +291,96 @@ describe('parseNumstat', () => { `-${TAB}-${TAB}logo.png${NUL}` + `5${TAB}0${TAB}${NUL}old.txt${NUL}new.txt${NUL}`; const r = parseNumstat(stdout); - assert.equal(r.ok, true); - if (!r.ok) {return;} - assert.equal(r.value.length, 3); - assert.equal(r.value[0]?.path, 'a.txt'); - assert.equal(r.value[0]?.binary, false); - assert.equal(r.value[1]?.binary, true); - assert.equal(r.value[2]?.path, 'new.txt'); - assert.equal(r.value[2]?.oldPath, 'old.txt'); + expectOk(r); + assert.deepEqual(r.value, [ + { path: 'a.txt', added: 2, deleted: 1, binary: false }, + { path: 'logo.png', added: 0, deleted: 0, binary: true }, + { + path: 'new.txt', + oldPath: 'old.txt', + added: 5, + deleted: 0, + binary: false, + }, + ]); }); it('preserves spaces and unicode in paths', () => { const r = parseNumstat(`1${TAB}1${TAB}dir with spaces/日本語.txt${NUL}`); - assert.equal(r.ok, true); - if (r.ok) {assert.equal(r.value[0]?.path, 'dir with spaces/日本語.txt');} + expectOk(r); + assert.deepEqual(r.value, [ + { + path: 'dir with spaces/日本語.txt', + added: 1, + deleted: 1, + binary: false, + }, + ]); }); it('errors when tab count is wrong', () => { const r = parseNumstat(`1${TAB}2${NUL}`); - assert.equal(r.ok, false); - if (!r.ok) {assert.match(r.error.message, /3 tab-fields/);} + expectErr(r); + assert.match(r.error.message, /3 tab-fields/); }); it('errors when counts are non-numeric (and not binary marker)', () => { const r = parseNumstat(`abc${TAB}def${TAB}p${NUL}`); - assert.equal(r.ok, false); - if (!r.ok) {assert.match(r.error.message, /non-numeric/);} + expectErr(r); + assert.match(r.error.message, /non-numeric/); }); it('errors on truncated rename', () => { const r = parseNumstat(`3${TAB}1${TAB}${NUL}old.txt${NUL}`); - assert.equal(r.ok, false); - if (!r.ok) {assert.match(r.error.message, /rename missing paths/);} + expectErr(r); + assert.match(r.error.message, /rename missing paths/); }); }); describe('parseRefs', () => { it('returns empty array for empty input', () => { const r = parseRefs(''); - assert.equal(r.ok, true); - if (r.ok) {assert.deepEqual(r.value, []);} + expectOk(r); + assert.deepEqual(r.value, []); }); it('parses a branch ref (newline-terminated record)', () => { const r = parseRefs(`refs/heads/main${NUL}main${NUL}abc1234\n`); - assert.equal(r.ok, true); - if (!r.ok) {return;} + expectOk(r); assert.deepEqual(r.value, [ - { name: 'main', fullName: 'refs/heads/main', sha: 'abc1234', type: 'branch' }, + { + name: 'main', + fullName: 'refs/heads/main', + sha: 'abc1234', + type: REF_TYPES.branch, + }, ]); }); it('parses a tag ref', () => { const r = parseRefs(`refs/tags/v1.0${NUL}v1.0${NUL}def5678\n`); - assert.equal(r.ok, true); - if (!r.ok) {return;} + expectOk(r); assert.deepEqual(r.value, [ - { name: 'v1.0', fullName: 'refs/tags/v1.0', sha: 'def5678', type: 'tag' }, + { + name: 'v1.0', + fullName: 'refs/tags/v1.0', + sha: 'def5678', + type: REF_TYPES.tag, + }, ]); }); it('classifies a non-branch, non-tag ref as other', () => { const r = parseRefs(`refs/remotes/origin/main${NUL}origin/main${NUL}abc1234\n`); - assert.equal(r.ok, true); - if (r.ok) {assert.equal(r.value[0]?.type, 'other');} + expectOk(r); + assert.deepEqual(r.value, [ + { + name: 'origin/main', + fullName: 'refs/remotes/origin/main', + sha: 'abc1234', + type: REF_TYPES.other, + }, + ]); }); it('parses a mixed batch of branches and tags', () => { @@ -333,29 +389,44 @@ describe('parseRefs', () => { `refs/heads/dev${NUL}dev${NUL}bbb\n` + `refs/tags/v1.0${NUL}v1.0${NUL}ccc\n`; const r = parseRefs(stdout); - assert.equal(r.ok, true); - if (!r.ok) {return;} - assert.equal(r.value.length, 3); - assert.equal(r.value[0]?.type, 'branch'); - assert.equal(r.value[1]?.type, 'branch'); - assert.equal(r.value[2]?.type, 'tag'); + expectOk(r); + assert.deepEqual(r.value, [ + { + name: 'main', + fullName: 'refs/heads/main', + sha: 'aaa', + type: REF_TYPES.branch, + }, + { + name: 'dev', + fullName: 'refs/heads/dev', + sha: 'bbb', + type: REF_TYPES.branch, + }, + { + name: 'v1.0', + fullName: 'refs/tags/v1.0', + sha: 'ccc', + type: REF_TYPES.tag, + }, + ]); }); it('errors when a line has fewer than 3 NUL-separated fields', () => { const r = parseRefs(`refs/heads/main${NUL}main\n`); - assert.equal(r.ok, false); - if (!r.ok) {assert.match(r.error.message, /3 NUL-separated/);} + expectErr(r); + assert.match(r.error.message, /3 NUL-separated/); }); it('errors when a ref field is empty', () => { const r = parseRefs(`refs/heads/main${NUL}${NUL}abc\n`); - assert.equal(r.ok, false); - if (!r.ok) {assert.match(r.error.message, /empty field/);} + expectErr(r); + assert.match(r.error.message, /empty field/); }); it('ignores trailing blank lines', () => { const r = parseRefs(`refs/heads/main${NUL}main${NUL}abc\n\n\n`); - assert.equal(r.ok, true); - if (r.ok) {assert.equal(r.value.length, 1);} + expectOk(r); + assert.equal(r.value.length, 1); }); }); diff --git a/src/test/unit/repoMatch.test.ts b/src/test/unit/repoMatch.test.ts index db46202..2509779 100644 --- a/src/test/unit/repoMatch.test.ts +++ b/src/test/unit/repoMatch.test.ts @@ -1,11 +1,19 @@ import { strict as assert } from 'node:assert'; import { findRepoForUri, matchRepoByFsPath } from '../../git/repoMatch'; -const repoAt = (fsPath: string) => ({ rootUri: { fsPath } }); +interface UriLike { + readonly fsPath: string; +} +interface RepoLike { + readonly rootUri: UriLike; +} + +const repoAt = (fsPath: string): RepoLike => ({ rootUri: { fsPath } }); describe('matchRepoByFsPath', () => { it('returns undefined for an empty repository list', () => { - assert.equal(matchRepoByFsPath([], '/anywhere'), undefined); + const result = matchRepoByFsPath([], '/anywhere'); + assert.equal(result, undefined); }); it('returns the only repository when the target is inside its root', () => { @@ -21,14 +29,20 @@ describe('matchRepoByFsPath', () => { it('prefers the longest matching root for nested repositories', () => { const outer = repoAt('/Users/me/repo'); const inner = repoAt('/Users/me/repo/sub/inner'); - const result = matchRepoByFsPath([outer, inner], '/Users/me/repo/sub/inner/file.txt'); + const result = matchRepoByFsPath( + [outer, inner], + '/Users/me/repo/sub/inner/file.txt', + ); assert.strictEqual(result, inner); }); it('order of the list does not affect the longest-match result', () => { const outer = repoAt('/Users/me/repo'); const inner = repoAt('/Users/me/repo/sub/inner'); - const result = matchRepoByFsPath([inner, outer], '/Users/me/repo/sub/inner/file.txt'); + const result = matchRepoByFsPath( + [inner, outer], + '/Users/me/repo/sub/inner/file.txt', + ); assert.strictEqual(result, inner); }); }); @@ -38,8 +52,8 @@ describe('findRepoForUri', () => { const repo = repoAt('/Users/me/repo'); const uri = { fsPath: '/Users/me/repo/file.txt' }; const api = { - repositories: [] as readonly typeof repo[], - getRepository: (_u: typeof uri) => repo, + repositories: [] as readonly RepoLike[], + getRepository: (_u: UriLike) => repo, }; const result = findRepoForUri(api, uri); assert.strictEqual(result, repo); @@ -55,10 +69,9 @@ describe('findRepoForUri', () => { it('returns undefined when getRepository returns null', () => { const uri = { fsPath: '/Users/me/repo/file.txt' }; - const repo = repoAt('/Users/me/repo'); const api = { - repositories: [] as readonly typeof repo[], - getRepository: (_u: typeof uri) => null, + repositories: [] as readonly RepoLike[], + getRepository: (_u: UriLike) => null, }; const result = findRepoForUri(api, uri); assert.equal(result, undefined); @@ -70,6 +83,9 @@ describe('findRepoForUri', () => { const api = { repositories: [a, b] }; assert.strictEqual(findRepoForUri(api, { fsPath: '/Users/me/repo-b/x' }), b); assert.strictEqual(findRepoForUri(api, { fsPath: '/Users/me/repo-a/y' }), a); - assert.equal(findRepoForUri(api, { fsPath: '/Users/me/somewhere-else' }), undefined); + assert.equal( + findRepoForUri(api, { fsPath: '/Users/me/somewhere-else' }), + undefined, + ); }); }); diff --git a/src/test/unit/result.test.ts b/src/test/unit/result.test.ts index 8beadc9..1afe30f 100644 --- a/src/test/unit/result.test.ts +++ b/src/test/unit/result.test.ts @@ -7,6 +7,8 @@ import { map, andThen, unwrapOr, + expectOk, + expectErr, type Result, } from '../../result'; @@ -14,27 +16,27 @@ describe('Result', () => { describe('ok / err constructors', () => { it('ok wraps the value and discriminates as ok=true', () => { const r = ok(42); - assert.equal(r.ok, true); + expectOk(r); assert.equal(r.value, 42); }); it('err wraps the error and discriminates as ok=false', () => { const r = err('boom'); - assert.equal(r.ok, false); + expectErr(r); assert.equal(r.error, 'boom'); }); it('ok preserves reference identity of the wrapped value', () => { const obj = { x: 1 }; const r = ok(obj); - assert.equal(r.ok, true); + expectOk(r); assert.strictEqual(r.value, obj); }); it('err preserves reference identity of the wrapped error', () => { const e = new Error('x'); const r = err(e); - assert.equal(r.ok, false); + expectErr(r); assert.strictEqual(r.error, e); }); }); @@ -72,36 +74,36 @@ describe('Result', () => { describe('map', () => { it('applies the function on Ok', () => { const r = map(ok(2), (n) => n * 3); - assert.equal(r.ok, true); - if (r.ok) {assert.equal(r.value, 6);} + expectOk(r); + assert.equal(r.value, 6); }); it('passes Err through unchanged', () => { const original: Result = err('bad'); const r = map(original, (n) => n * 3); - assert.equal(r.ok, false); - if (!r.ok) {assert.equal(r.error, 'bad');} + expectErr(r); + assert.equal(r.error, 'bad'); }); it('supports type-changing maps', () => { - const r = map(ok(5), (n) => `n=${n}`); - assert.equal(r.ok, true); - if (r.ok) {assert.equal(r.value, 'n=5');} + const r = map(ok(5), (n) => `n=${n.toString()}`); + expectOk(r); + assert.equal(r.value, 'n=5'); }); }); describe('andThen', () => { it('chains Ok into another Ok', () => { const r = andThen(ok(2), (n) => ok(n + 1)); - assert.equal(r.ok, true); - if (r.ok) {assert.equal(r.value, 3);} + expectOk(r); + assert.equal(r.value, 3); }); it('chains Ok into an Err and propagates it', () => { const start: Result = ok(2); const r = andThen(start, (_n): Result => err('downstream')); - assert.equal(r.ok, false); - if (!r.ok) {assert.equal(r.error, 'downstream');} + expectErr(r); + assert.equal(r.error, 'downstream'); }); it('short-circuits on Err without invoking the function', () => { @@ -112,8 +114,8 @@ describe('Result', () => { return ok(n); }); assert.equal(called, false); - assert.equal(r.ok, false); - if (!r.ok) {assert.equal(r.error, 'upstream');} + expectErr(r); + assert.equal(r.error, 'upstream'); }); }); diff --git a/src/test/unit/uri.test.ts b/src/test/unit/uri.test.ts index 6d51d2a..188edbb 100644 --- a/src/test/unit/uri.test.ts +++ b/src/test/unit/uri.test.ts @@ -1,17 +1,21 @@ import { strict as assert } from 'node:assert'; +import { REV_KINDS, URI_PARSE_ERROR_KINDS } from '../../constants'; import { buildDiffyUri, parseDiffyUri } from '../../ui/uri'; +import { expectErr, expectOk } from '../../result'; import type { DiffyAddressableRev } from '../../git/types'; const SHA = 'abc1234def567890fedcba0987654321aaaaaaaa'; -const commit = (sha: string): DiffyAddressableRev => ({ kind: 'commit', sha }); -const index = (): DiffyAddressableRev => ({ kind: 'index' }); +const commit = (sha: string): DiffyAddressableRev => ({ + kind: REV_KINDS.commit, + sha, +}); +const index = (): DiffyAddressableRev => ({ kind: REV_KINDS.index }); const roundTrip = (rev: DiffyAddressableRev, path: string): void => { const uri = buildDiffyUri(rev, path); const parsed = parseDiffyUri(uri); - assert.equal(parsed.ok, true, `parse failed for ${uri}`); - if (!parsed.ok) {return;} + expectOk(parsed); assert.deepEqual(parsed.value.rev, rev); assert.equal(parsed.value.path, path); }; @@ -52,66 +56,64 @@ describe('buildDiffyUri', () => { describe('parseDiffyUri', () => { it('parses a commit URI and decodes the path', () => { const r = parseDiffyUri(`diffy://commit/${SHA}/src/file.ts`); - assert.equal(r.ok, true); - if (!r.ok) {return;} - assert.deepEqual(r.value.rev, { kind: 'commit', sha: SHA }); + expectOk(r); + assert.deepEqual(r.value.rev, { kind: REV_KINDS.commit, sha: SHA }); assert.equal(r.value.path, 'src/file.ts'); }); it('parses an index URI and decodes the path', () => { const r = parseDiffyUri('diffy://index/src/file.ts'); - assert.equal(r.ok, true); - if (!r.ok) {return;} - assert.deepEqual(r.value.rev, { kind: 'index' }); + expectOk(r); + assert.deepEqual(r.value.rev, { kind: REV_KINDS.index }); assert.equal(r.value.path, 'src/file.ts'); }); it('rejects a non-diffy scheme', () => { const r = parseDiffyUri('file:///some/path.ts'); - assert.equal(r.ok, false); - if (!r.ok) {assert.equal(r.error.kind, 'invalidScheme');} + expectErr(r); + assert.equal(r.error.kind, URI_PARSE_ERROR_KINDS.invalidScheme); }); it('rejects an unknown authority', () => { const r = parseDiffyUri(`diffy://stash/${SHA}/x.ts`); - assert.equal(r.ok, false); - if (!r.ok) {assert.equal(r.error.kind, 'invalidAuthority');} + expectErr(r); + assert.equal(r.error.kind, URI_PARSE_ERROR_KINDS.invalidAuthority); }); it('rejects a commit URI with no path after the sha', () => { const r = parseDiffyUri(`diffy://commit/${SHA}/`); - assert.equal(r.ok, false); - if (!r.ok) {assert.equal(r.error.kind, 'emptyPath');} + expectErr(r); + assert.equal(r.error.kind, URI_PARSE_ERROR_KINDS.emptyPath); }); it('rejects a commit URI with no sha', () => { const r = parseDiffyUri('diffy://commit//file.ts'); - assert.equal(r.ok, false); - if (!r.ok) {assert.equal(r.error.kind, 'missingSha');} + expectErr(r); + assert.equal(r.error.kind, URI_PARSE_ERROR_KINDS.missingSha); }); it('rejects an index URI with no path', () => { const r = parseDiffyUri('diffy://index/'); - assert.equal(r.ok, false); - if (!r.ok) {assert.equal(r.error.kind, 'emptyPath');} + expectErr(r); + assert.equal(r.error.kind, URI_PARSE_ERROR_KINDS.emptyPath); }); it('rejects a malformed URI with no scheme separator', () => { const r = parseDiffyUri('not-a-uri'); - assert.equal(r.ok, false); - if (!r.ok) {assert.equal(r.error.kind, 'malformed');} + expectErr(r); + assert.equal(r.error.kind, URI_PARSE_ERROR_KINDS.malformed); }); it('rejects a URI missing the authority/path slash', () => { const r = parseDiffyUri('diffy://commit'); - assert.equal(r.ok, false); - if (!r.ok) {assert.equal(r.error.kind, 'malformed');} + expectErr(r); + assert.equal(r.error.kind, URI_PARSE_ERROR_KINDS.malformed); }); it('rejects a URI with malformed percent encoding', () => { const r = parseDiffyUri(`diffy://commit/${SHA}/bad%ZZpath.ts`); - assert.equal(r.ok, false); - if (!r.ok) {assert.equal(r.error.kind, 'badEncoding');} + expectErr(r); + assert.equal(r.error.kind, URI_PARSE_ERROR_KINDS.badEncoding); }); }); diff --git a/src/ui/CommitPicker.ts b/src/ui/CommitPicker.ts index 3bfdeae..9b2b582 100644 --- a/src/ui/CommitPicker.ts +++ b/src/ui/CommitPicker.ts @@ -1,4 +1,5 @@ import type * as vscode from 'vscode'; +import { UI_TEXT } from '../constants'; import { type Result, map } from '../result'; import type { Commit } from '../git/types'; import { showSinglePick } from './runQuickPick'; @@ -12,7 +13,7 @@ interface CommitPickItem extends vscode.QuickPickItem { const toItem = (commit: Commit, now: number): CommitPickItem => ({ label: commit.shortSha, description: commit.subject, - detail: `${commit.author} • ${formatRelative(commit.authorTime, now)}`, + detail: `${commit.author} ${UI_TEXT.bulletDot} ${formatRelative(commit.authorTime, now)}`, commit, }); @@ -28,7 +29,7 @@ export const pickCommit = async ({ const tNow = now ?? Math.floor(Date.now() / 1000); const r = await showSinglePick({ items: commits.map((c) => toItem(c, tNow)), - placeholder: placeholder ?? 'Pick a commit', + placeholder: placeholder ?? UI_TEXT.pickCommitPlaceholder, matchOnDescription: true, matchOnDetail: true, }); diff --git a/src/ui/FilePicker.ts b/src/ui/FilePicker.ts index 147d0ee..34171f0 100644 --- a/src/ui/FilePicker.ts +++ b/src/ui/FilePicker.ts @@ -20,7 +20,7 @@ const toItem = (entry: FileEntry): FileItem => ({ entry, }); -export const pickFiles = ({ +export const pickFiles = async ({ entries, onPick, placeholder, @@ -29,12 +29,12 @@ export const pickFiles = ({ onPick: (entry: FileEntry) => void | Promise; placeholder?: string; }): Promise => - showStayOpenPick( + { await showStayOpenPick( { items: entries.map(toItem), placeholder: placeholder ?? 'Pick a file to diff (Esc to close)', matchOnDescription: true, matchOnDetail: true, }, - (item) => onPick(item.entry), - ); + async (item) => { await onPick(item.entry); }, + ); }; diff --git a/src/ui/RefPicker.ts b/src/ui/RefPicker.ts index abac208..6efe0b7 100644 --- a/src/ui/RefPicker.ts +++ b/src/ui/RefPicker.ts @@ -1,4 +1,5 @@ import type * as vscode from 'vscode'; +import { SHORT_SHA_LEN, UI_TEXT } from '../constants'; import { type Result, map } from '../result'; import type { Ref, RefType } from '../git/types'; import { showSinglePick } from './runQuickPick'; @@ -11,7 +12,7 @@ interface RefPickItem extends vscode.QuickPickItem { const toItem = (ref: Ref): RefPickItem => ({ label: ref.name, - description: ref.sha.slice(0, 7), + description: ref.sha.slice(0, SHORT_SHA_LEN), detail: refTypeLabel(ref), ref, }); @@ -34,7 +35,7 @@ export const pickRef = async ({ const items = filterRefs(refs, filter).map(toItem); const r = await showSinglePick({ items, - placeholder: placeholder ?? 'Pick a branch or tag', + placeholder: placeholder ?? UI_TEXT.pickRefPlaceholder, matchOnDescription: true, matchOnDetail: true, }); diff --git a/src/ui/SideBPicker.ts b/src/ui/SideBPicker.ts index cf5ce2c..d593f9f 100644 --- a/src/ui/SideBPicker.ts +++ b/src/ui/SideBPicker.ts @@ -1,13 +1,14 @@ import type * as vscode from 'vscode'; +import { SIDE_B_KINDS, UI_TEXT } from '../constants'; import { type Result, map } from '../result'; import { showSinglePick } from './runQuickPick'; import type { Cancelled } from './cancelled'; export type SideBChoice = - | { readonly kind: 'workingCopy' } - | { readonly kind: 'index' } - | { readonly kind: 'pickRef' } - | { readonly kind: 'pickCommit' }; + | { readonly kind: typeof SIDE_B_KINDS.workingCopy } + | { readonly kind: typeof SIDE_B_KINDS.index } + | { readonly kind: typeof SIDE_B_KINDS.pickRef } + | { readonly kind: typeof SIDE_B_KINDS.pickCommit }; interface SideBItem extends vscode.QuickPickItem { readonly choice: SideBChoice; @@ -15,24 +16,24 @@ interface SideBItem extends vscode.QuickPickItem { const ITEMS: readonly SideBItem[] = [ { - label: 'Working Copy', - description: 'On-disk files in this repository', - choice: { kind: 'workingCopy' }, + label: UI_TEXT.workingCopy, + description: UI_TEXT.workingCopyDescription, + choice: { kind: SIDE_B_KINDS.workingCopy }, }, { - label: 'Index', - description: 'The git staging area', - choice: { kind: 'index' }, + label: UI_TEXT.indexLabel, + description: UI_TEXT.indexDescription, + choice: { kind: SIDE_B_KINDS.index }, }, { - label: 'Pick a commit…', - description: 'Choose from recent log entries', - choice: { kind: 'pickCommit' }, + label: UI_TEXT.pickCommitLabel, + description: UI_TEXT.pickCommitDescription, + choice: { kind: SIDE_B_KINDS.pickCommit }, }, { - label: 'Pick a branch or tag…', - description: 'Choose from refs in this repository', - choice: { kind: 'pickRef' }, + label: UI_TEXT.pickRefLabel, + description: UI_TEXT.pickRefDescription, + choice: { kind: SIDE_B_KINDS.pickRef }, }, ]; @@ -43,7 +44,7 @@ export const pickSideBChoice = async ({ } = {}): Promise> => { const r = await showSinglePick({ items: ITEMS, - placeholder: placeholder ?? 'Compare against…', + placeholder: placeholder ?? UI_TEXT.compareAgainstPlaceholder, }); return map(r, (item) => item.choice); }; diff --git a/src/ui/format/fileItem.ts b/src/ui/format/fileItem.ts index 9dc03dc..1da55eb 100644 --- a/src/ui/format/fileItem.ts +++ b/src/ui/format/fileItem.ts @@ -1,18 +1,19 @@ +import { CHANGED_FILE_STATUSES, UI_TEXT } from '../../constants'; import type { ChangedFile, DiffStat } from '../../git/types'; export const statusBadge = (file: ChangedFile): string => { - if (file.status === 'R') { - return `R${(file.similarity ?? 0).toString()}`; + if (file.status === CHANGED_FILE_STATUSES.renamed) { + return `${CHANGED_FILE_STATUSES.renamed}${(file.similarity ?? 0).toString()}`; } - if (file.status === 'C') { - return `C${(file.similarity ?? 0).toString()}`; + if (file.status === CHANGED_FILE_STATUSES.copied) { + return `${CHANGED_FILE_STATUSES.copied}${(file.similarity ?? 0).toString()}`; } return file.status; }; export const formatCounts = (stat: DiffStat): string => { if (stat.binary) { - return 'binary'; + return UI_TEXT.binaryStat; } return `+${stat.added.toString()} -${stat.deleted.toString()}`; }; @@ -32,6 +33,11 @@ export const mergeChangedFilesWithStats = ( } return files.map((f) => ({ file: f, - stat: byPath.get(f.path) ?? { path: f.path, added: 0, deleted: 0, binary: false }, + stat: byPath.get(f.path) ?? { + path: f.path, + added: 0, + deleted: 0, + binary: false, + }, })); }; diff --git a/src/ui/format/refLabel.ts b/src/ui/format/refLabel.ts index 4dc9732..ee34aea 100644 --- a/src/ui/format/refLabel.ts +++ b/src/ui/format/refLabel.ts @@ -1,11 +1,12 @@ +import { REF_TYPES, UI_TEXT } from '../../constants'; import type { Ref } from '../../git/types'; export const refTypeLabel = (ref: Ref): string => { - if (ref.type === 'branch') { - return 'Branch'; + if (ref.type === REF_TYPES.branch) { + return UI_TEXT.branchLabel; } - if (ref.type === 'tag') { - return 'Tag'; + if (ref.type === REF_TYPES.tag) { + return UI_TEXT.tagLabel; } - return 'Ref'; + return UI_TEXT.refLabel; }; diff --git a/src/ui/format/relativeTime.ts b/src/ui/format/relativeTime.ts index 854b933..efce351 100644 --- a/src/ui/format/relativeTime.ts +++ b/src/ui/format/relativeTime.ts @@ -1,3 +1,5 @@ +import { UI_TEXT } from '../../constants'; + const SECONDS_PER_MINUTE = 60; const MINUTES_PER_HOUR = 60; const HOURS_PER_DAY = 24; @@ -6,7 +8,7 @@ export const formatRelative = (unixSeconds: number, now: number): string => { const deltaSec = Math.max(0, now - unixSeconds); const minutes = Math.floor(deltaSec / SECONDS_PER_MINUTE); if (minutes < 1) { - return 'just now'; + return UI_TEXT.justNow; } if (minutes < MINUTES_PER_HOUR) { return `${minutes.toString()}m ago`; diff --git a/src/ui/runQuickPick.ts b/src/ui/runQuickPick.ts index 8059432..0c83329 100644 --- a/src/ui/runQuickPick.ts +++ b/src/ui/runQuickPick.ts @@ -10,23 +10,23 @@ export interface QuickPickConfig { readonly ignoreFocusOut?: boolean; } -const configurePicker = ( - qp: vscode.QuickPick, +const createConfiguredPicker = ( config: QuickPickConfig, -): void => { +): vscode.QuickPick => { + const qp = vscode.window.createQuickPick(); qp.placeholder = config.placeholder; qp.matchOnDescription = config.matchOnDescription ?? false; qp.matchOnDetail = config.matchOnDetail ?? false; qp.ignoreFocusOut = config.ignoreFocusOut ?? false; qp.items = config.items; + return qp; }; -export const showSinglePick = ( +export const showSinglePick = async ( config: QuickPickConfig, -): Promise> => { - return new Promise>((resolve) => { - const qp = vscode.window.createQuickPick(); - configurePicker(qp, config); +): Promise> => + await new Promise>((resolve) => { + const qp = createConfiguredPicker(config); let settled = false; const finish = (r: Result): void => { if (settled) { @@ -45,15 +45,13 @@ export const showSinglePick = ( }); qp.show(); }); -}; -export const showStayOpenPick = ( +export const showStayOpenPick = async ( config: QuickPickConfig, onPick: (item: T) => void | Promise, ): Promise => { - return new Promise((resolve) => { - const qp = vscode.window.createQuickPick(); - configurePicker(qp, { ...config, ignoreFocusOut: true }); + await new Promise((resolve) => { + const qp = createConfiguredPicker({ ...config, ignoreFocusOut: true }); qp.onDidAccept(() => { const choice = qp.selectedItems[0] ?? qp.activeItems[0]; if (choice !== undefined) { diff --git a/src/ui/uri.ts b/src/ui/uri.ts index 0c481ba..7ea6d11 100644 --- a/src/ui/uri.ts +++ b/src/ui/uri.ts @@ -1,14 +1,31 @@ -import { SCHEME, URI_AUTHORITIES } from '../constants'; +import { + REV_KINDS, + SCHEME, + URI_AUTHORITIES, + URI_PARSE_ERROR_KINDS, +} from '../constants'; import { type Result, ok, err } from '../result'; import type { DiffyAddressableRev, Sha } from '../git/types'; export type DiffyUriParseError = - | { readonly kind: 'invalidScheme'; readonly got: string } - | { readonly kind: 'invalidAuthority'; readonly got: string } - | { readonly kind: 'missingSha' } - | { readonly kind: 'emptyPath' } - | { readonly kind: 'badEncoding'; readonly raw: string } - | { readonly kind: 'malformed'; readonly reason: string }; + | { + readonly kind: typeof URI_PARSE_ERROR_KINDS.invalidScheme; + readonly got: string; + } + | { + readonly kind: typeof URI_PARSE_ERROR_KINDS.invalidAuthority; + readonly got: string; + } + | { readonly kind: typeof URI_PARSE_ERROR_KINDS.missingSha } + | { readonly kind: typeof URI_PARSE_ERROR_KINDS.emptyPath } + | { + readonly kind: typeof URI_PARSE_ERROR_KINDS.badEncoding; + readonly raw: string; + } + | { + readonly kind: typeof URI_PARSE_ERROR_KINDS.malformed; + readonly reason: string; + }; export interface DiffyUriComponents { readonly rev: DiffyAddressableRev; @@ -25,34 +42,48 @@ const decodePath = (encoded: string): Result => { try { return ok(encoded.split(PATH_SEP).map(decodeURIComponent).join(PATH_SEP)); } catch { - return err({ kind: 'badEncoding', raw: encoded }); + return err({ kind: URI_PARSE_ERROR_KINDS.badEncoding, raw: encoded }); } }; export const buildDiffyUri = (rev: DiffyAddressableRev, path: string): string => { const encoded = encodePath(path); - if (rev.kind === 'commit') { + if (rev.kind === REV_KINDS.commit) { return `${SCHEME}${SCHEME_SEP}${URI_AUTHORITIES.commit}/${rev.sha}/${encoded}`; } return `${SCHEME}${SCHEME_SEP}${URI_AUTHORITIES.index}/${encoded}`; }; -const parseCommitUri = (rest: string): Result => { +const parseCommitUri = ( + rest: string, +): Result => { const slash = rest.indexOf(PATH_SEP); - if (slash <= 0) {return err({ kind: 'missingSha' });} + if (slash <= 0) { + return err({ kind: URI_PARSE_ERROR_KINDS.missingSha }); + } const sha: Sha = rest.slice(0, slash); const encodedPath = rest.slice(slash + 1); - if (encodedPath === '') {return err({ kind: 'emptyPath' });} + if (encodedPath === '') { + return err({ kind: URI_PARSE_ERROR_KINDS.emptyPath }); + } const decoded = decodePath(encodedPath); - if (!decoded.ok) {return decoded;} - return ok({ rev: { kind: 'commit', sha }, path: decoded.value }); + if (!decoded.ok) { + return decoded; + } + return ok({ rev: { kind: REV_KINDS.commit, sha }, path: decoded.value }); }; -const parseIndexUri = (rest: string): Result => { - if (rest === '') {return err({ kind: 'emptyPath' });} +const parseIndexUri = ( + rest: string, +): Result => { + if (rest === '') { + return err({ kind: URI_PARSE_ERROR_KINDS.emptyPath }); + } const decoded = decodePath(rest); - if (!decoded.ok) {return decoded;} - return ok({ rev: { kind: 'index' }, path: decoded.value }); + if (!decoded.ok) { + return decoded; + } + return ok({ rev: { kind: REV_KINDS.index }, path: decoded.value }); }; interface UriSplit { @@ -63,11 +94,21 @@ interface UriSplit { const splitUri = (uri: string): Result => { const sep = uri.indexOf(SCHEME_SEP); - if (sep < 0) {return err({ kind: 'malformed', reason: 'no scheme separator' });} + if (sep < 0) { + return err({ + kind: URI_PARSE_ERROR_KINDS.malformed, + reason: 'no scheme separator', + }); + } const scheme = uri.slice(0, sep); const afterScheme = uri.slice(sep + SCHEME_SEP.length); const firstSlash = afterScheme.indexOf(PATH_SEP); - if (firstSlash < 0) {return err({ kind: 'malformed', reason: 'no authority/path separator' });} + if (firstSlash < 0) { + return err({ + kind: URI_PARSE_ERROR_KINDS.malformed, + reason: 'no authority/path separator', + }); + } return ok({ scheme, authority: afterScheme.slice(0, firstSlash), @@ -79,11 +120,23 @@ export const parseDiffyUri = ( uri: string, ): Result => { const split = splitUri(uri); - if (!split.ok) {return split;} + if (!split.ok) { + return split; + } if (split.value.scheme !== SCHEME) { - return err({ kind: 'invalidScheme', got: split.value.scheme }); + return err({ + kind: URI_PARSE_ERROR_KINDS.invalidScheme, + got: split.value.scheme, + }); } - if (split.value.authority === URI_AUTHORITIES.commit) {return parseCommitUri(split.value.rest);} - if (split.value.authority === URI_AUTHORITIES.index) {return parseIndexUri(split.value.rest);} - return err({ kind: 'invalidAuthority', got: split.value.authority }); + if (split.value.authority === URI_AUTHORITIES.commit) { + return parseCommitUri(split.value.rest); + } + if (split.value.authority === URI_AUTHORITIES.index) { + return parseIndexUri(split.value.rest); + } + return err({ + kind: URI_PARSE_ERROR_KINDS.invalidAuthority, + got: split.value.authority, + }); }; diff --git a/src/vscodeGitApi.ts b/src/vscodeGitApi.ts index 4f9652f..a533469 100644 --- a/src/vscodeGitApi.ts +++ b/src/vscodeGitApi.ts @@ -1,7 +1,10 @@ import * as vscode from 'vscode'; +import { VSCODE_GIT_EXTENSION_ID } from './constants'; + +const GIT_API_VERSION = 1; interface GitExtensionExports { - getAPI(version: 1): GitApi; + getAPI: (version: typeof GIT_API_VERSION) => GitApi; } export interface GitVsRepository { @@ -10,18 +13,20 @@ export interface GitVsRepository { export interface GitApi { readonly repositories: readonly GitVsRepository[]; - getRepository?(uri: vscode.Uri): GitVsRepository | null; + getRepository?: (uri: vscode.Uri) => GitVsRepository | null; } export const getGitApi = async (): Promise => { - const ext = vscode.extensions.getExtension('vscode.git'); + const ext = vscode.extensions.getExtension( + VSCODE_GIT_EXTENSION_ID, + ); if (ext === undefined) { return undefined; } if (!ext.isActive) { await ext.activate(); } - return ext.exports.getAPI(1); + return ext.exports.getAPI(GIT_API_VERSION); }; export { findRepoForUri } from './git/repoMatch'; From c50923c771d2d012a91fab6f9c7103054fee495d Mon Sep 17 00:00:00 2001 From: Christian Findlay <16697547+MelbourneDeveloper@users.noreply.github.com> Date: Sun, 24 May 2026 09:43:26 +1000 Subject: [PATCH 07/17] stuff --- docs/assets/diffy-logo-768.png | Bin 17199 -> 0 bytes docs/assets/diffy-logo.svg | 44 -------------- docs/assets/diffy-mark-128.png | Bin 5123 -> 0 bytes docs/assets/diffy-mark-256.png | Bin 11586 -> 0 bytes docs/assets/diffy-mark.svg | 24 -------- docs/design-system.md | 39 +++++++----- icon.png | Bin 5123 -> 6209 bytes src/commands/flow.ts | 29 +++++++-- src/git/GitRepo.ts | 10 +++- src/test/suite/commands.test.ts | 22 +++---- src/test/unit/refFilter.test.ts | 102 ++++++++++++++++++++++++++++++++ src/ui/RefPicker.ts | 13 ++-- src/ui/format/refFilter.ts | 22 +++++++ test-fixtures/repo-seed/seed.sh | 4 ++ 14 files changed, 204 insertions(+), 105 deletions(-) delete mode 100644 docs/assets/diffy-logo-768.png delete mode 100644 docs/assets/diffy-logo.svg delete mode 100644 docs/assets/diffy-mark-128.png delete mode 100644 docs/assets/diffy-mark-256.png delete mode 100644 docs/assets/diffy-mark.svg create mode 100644 src/test/unit/refFilter.test.ts create mode 100644 src/ui/format/refFilter.ts diff --git a/docs/assets/diffy-logo-768.png b/docs/assets/diffy-logo-768.png deleted file mode 100644 index c7c69da22d45e62c2e8156cb384f4767324278bc..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 17199 zcmcJ1g;!MF_xBxI8fj?}>6Q?XK><+#1pxtRkZ$Rm0cj*Gx_zWuy1S*jhVIT0hMD)` zcm4i^_p+!p_nx!&KKtxC`*Xw9UMrCh-XjD6faK*1g|`5J4gLv~65xS9YkuCb0Ds|` zDk~`fnA@+6#++{ezzV!nc&h1{vcKTvt2wcNceLqYS<JNuaJyp|0QZOQ2}7%4T9m zuTmixu1GLm_oe2{-u_y=WVXp>0>?QDp36t*;FjlRG*>rnU`wI)JvKJj?!(Z)O(Sd| zskg9KMTBi=ij2kb;K^m0C>z^Q?d7}+aR2}7m4#$yUx2G!K=(>z3Kl+Qj<** zUOmj9heu<_0_Wegn=MC#5Lb_9F?S%j7z1o^n}mAocWW~Rnf>R|&)a$C@U5g(D&&TV zjlbnxu|Q<2U#%qTRr|kXaSv7iFRIIxa%>^{u7RHrvuMKC6rIFK`<#pG^r8NM^=C~? z-1gDpn#xJQpQ4TVgMnr{kJF@*Jye~-qmWMd1t?FdsA_EvFZ?>qzWaRvVzv79Ar z1;G&SfR<*H74(`yqsLgbYJIx{IR$uiMo}CB0}0OhaZHWM)ZVpNiJ39RNHTAgy|(7d zehY|$g#DfVrkjhgjZy}s+ez3R9!LWs_Fd|yif1&r8zHoM^yp8_qB6^fYkSI!DZlH9 z>~&y3h?f&whb)Rm_nF}IqR|O9lrlii@i5Tp4TNAax`|=POaDs`u{|iF$-IA3h~eqK zp*!df2y>V|c03mUa!H5Ao(LJCK|_}6P2bDT+hVL~gJ#HY7xx}poJnE7;zG+%AG+)%BS^-0%5`jrsrtoE^wvYFuu{ zWJb7o&8*TiV9?ioCgQPS;1%+6KpGIW71de!T;ROl4U|M!CNkB!5DA; zZBbS2j|uWp!)`AGrrqn!dqp~5Wv{2)^6)OGFzPCb=cI6w#$Uu3$ZP4Yb|^p)Yx(Is z0iS^t)%Ij3dPwD#+%lRF_T)P``d}hQg*E)BJ@>|5Mucno_`+ z#emFvs!$xk&RY!^zKc#}AQhOl+~LDi-fFfwpW-$tT$mH-f2(kxi;lfvb4j7&AN<{9t|VkZl<39`{>b{F8tsAj$Ei4!_?l+E^5)mJnjVkcAMD$I#L^HlLXU>DkUkkcB>gvhqLmDeFMN~ZmO?u8 zwLSG0YqZ|KZBlkOic~Io6?Q9F6C_KimZl4;I_TfXol<8STb@J8NS|;xxG(Y3{5}W4 z0JTof=HT1Uxh%4r@r@b5O(54Tgu>^}+5>JyMe>_#9wKMj5RCZ*b7ylUT}GY}ySlmk%qbwMpPMW5xpdfDTnORUtIDdcpL1C$qXjjt z7gq3ZrgtHWkg(sN(2b}DpV?co=sz`_5BB@ie1^zxYBd6wrkxXH1n!XiU}Va5zOgG< z+{RGmX8rz+{YUjr?p!Tpy8jjT;?*@m-N1lpNySGW`9E_)9wiL`E1W240wCP`D75*M z}@rTk!Nk6@y;$Y+$^RH+@Od6_1@b10CA>fl8{BSYA<4 z&alPrOL4J@ajeoP*JO}dtF5e2NLa}cSy;X(s{0|5>E+yw+X+qrY`&p?WAl6PNAO#g@p z_AtRci7?S5f#~HY5v@j_DuwI5zJ2R7K7wdfSAyZYxk(@IjlEet?oNmQu`omCB=ijO zO6sW;YFJ@Ht*8rU~ZLvu@gbRn|fE@hVc7;73FVl9*WWz*UniV6L>BH0>ZPt7RvEtERefC9f26*!}tB3Ofu-{HQO_PaGNY_=v86}G&9+0 z<(XE-c`WpW@XG_aIkv4&S&A9btkdIa+SfT&zOTXU?@6&vD%TJM+Y|+E7JV2@k62n| zlD~Wj;AlqT4$ADR%sY28d18z(J4W#3bUbAz_P|hBydSacM{f8oo}Sk0*Mpuvg8CKK z?$;;b11o>kHPR2Y(;YKA6i#IU3OAtqHxXxKrnbDRK!#T_L-0Ym^AK766b47F0sv_a z3%lSN(ZO%we%;pmZS}Gi83gP0Q9le5G;H^lVZv9hk-ob)5f4J3K%Bd3!9!tQk8S`))Gnr?92m zpUt1}4~(Lc9c$?i8&4PEBE*=oFF&-MA{6fv4w$Gb(!xo@s^}vO?#Bu4ejJrpJ{|J; zX%JuC(b43m(sH+z?V2PzEv2DX``I}S@sOt!JE!9bHhhpFqz=@y=TTg)zzQGglJe6M zG{a^2_-eOgHR6|cR-Sdd&m6V6_UeG>kld?R`3SQtlV{KIit7*XWG<(gS1!!^WmKb1 zk66$=y~o`^Q%(dXS{Ii`2qLGt_UO@Etad4NgoM^nq1K+@7z2s~ThW<-e1!|uZJhfic8Ls-_Ly8A?JwB^Z9_&~-95oZ z)T@@&tWF_f)Y^ck^R9jMeeTEM%djC+Tqjl>4lEfHZDq69{;eT<;gs!qQ3Aw?*49Q4 zg6m8}T-k^@j z0P&{M{FJ7mQqjEado1bWGog9E%Y~l4!Gq>c%dj_Ji50&jbk#qu{bu;5pI6d9mvg?{ zTsa0H3-*H>D}E4rz=tCwTyBOpqLguQY9#$V^XZfD zF3f|ABq*McnAOwHv8Z#g7=@1x)cnjHoftt#gP}i!;*F zZFMRD3-{3-9Q&*!f*&}*>V5l1pqp!8@Wy97XoLC|BbB6)*YjddA`^_IMF@XUB-*OK zudj`%|FDGvv|!m?2C_c%=u4pxuHw>mZ;OlnURoaH7u}c&aatD?JM<+y+Wt^rkOm0# zJF>OnMNZS+pKo>rwzqvOv8sNyIfnaker>4#7q1>dB`F$ zEm|=?=|84pbjqZq3v`MC=%SV!c%g8B2E(q+V9c3-{M)bl#z7cI)q2S4cxwm@5He=I z_w-mb{9Vf6(HjK?vzOytJzZ~*p`1vi{fOqAXt^PvqW~37dAzCT*vdhRxtne6;b@6O zh;ql;%Nh0kNp*cMFTbDsNhZo(yFD5okRgu}pUJ!qz7}O~!9G8qt0w&=OIdDI1I^6( zXr}rnIzv7J++7!s)N8TH-z+0eMe4Ls*SHwHPh&6@JQsW>MX|2Q(A{sO!C$E@}+={4WfTbuksaI(D=U6JJiW zOy9oMNy+hN5kg9q-5P zihjAf5tHjTRlJjwISIz*k7he8IF^89eQ8r>bSg-vW90KAkFCAEwvecp^MdPL`#HQS z?3O7b```(iV7V_}Q-WlZN!*kDkdF81@cfVaBadUMG2z(;gQ0=+=+MxWo35**(>DIJ zA3wyN-hCS_o?%&FDvk&B+bBFRJZ*-2Cp$rM-6@BMkd1XXxxcFu+Bw*~jV27vw6RD_ zdn?T(i&12BJFE4Mv8^mlu6(oaXOBX!OLjfd4jeGG>+P2aY(U?8#)8r3Ojy!%av9l5 z27~)urUs^^8M?Y%X4dC0YzTG4uB}n&`|MZNT;&(gp;vFc7Xv&V(ja5r_uW=X%A2-e z$pbjSbFMR5-`s?%X=`7)x$3>VRYomWZ7jU!GAN%u66ep&LjzBKhwS)lu5PX? zQ4$D+^?i?}97_zbWx8IFk_2dA6o?2p*V#B-uf+Gx{S{Qa+w=8|pn5LH+(o z-dANmSI2PACQh%zp9|hhyh6n-F6I#JUVj@!oZUMyzR1I8Vm=cFrlOaLG2yg`$kfzm zddDp1&Sq%9NE4Cu8~ue6jwcHSrRV2(b{ZdF@$k{?!Y6ZnWgDYWrhJAK$j-(g2x zt2(S1^aV-AG${oJkpN^|d6~)ofvvAo`%VSP&}FDGx9PR?<9yH?-r!4E!dqOp17oij(0bFugkJ=s{$xS3~twDA0Oz7OimO6DZE06tB*s@V4gml2f`G_8p0 zomTzki<|FEsi`%ai`S(l?PkdR%I2;_=wl}C-j`N~ThCmS8TN$fUVrs8@Y@b_DD^s!Bzx=#~G1Tkj{$ z_m1eqMMPLujN^n*o(^h)wJ~`9nZ_8&@+7~YpkC!mB*jd;h=fG-MS@PHHJ_UFq|)0( zwf;Q$ydfNa#;3-^6Bzt^KA@_CVAVO?=_uE29C6#L$iFDKch0C@a8|!Oe7>EI=;t(Ba4fqK+h|dnDMp$TBkO<_tO7d z*bud2tVU~oU%3H#ZTuxI>b|7LRw@k4PmyF7@1uf>SxB>)r3;||`6O9Amf+OH!~|J3 zGq{e)S<{fPn4`l*`a!jHr@tiV%sIN96tu)8qRhPi-TYBE1EfWmkxP_`hTR)xA3BtR z8)mj3(JL>Ad8=WZdj z;~}P`lLzbV?Ug*qWj{KQJ`CXllTMh*7BAKvpC&J{GdUpn=R$N`++OtjeCp=kA`K^+ zjMH|RiG{2an~c=#%s+s!`dm)_9sgZXMP*zQvh>>LnoV~qG$v3sOV3@Nup-C~&+hhq zYA`$xS#aLk->)RL2#^G2(w;R839CLjB!l5?^RpjDj z6H&oL#Q66I4>&op>N$_v%)Ur22T{tq1E!SdnJ08S1qm!O6L-;&2AZYXSTc5BenPBz<{DFw)<-bnEHwoypf!550~l2 z_otKm?DBVk@oIU=9^o6=&3q}d8J{RC+t!CIeKy=4cwmW)F#_E)0wAd8%)Pe}(LgnP zz$ESdw4KHKR~7{cc6osqsva+k5iPHvKoN=NlVfK@KWR%G6!pd06vZjt@k^p8*ODi| z6i1QCR&?D6a0^3ThL3oqxs3jYMF&aaHAcqxxXb0Y3)owoc}JTiQlRbROj)zG<#0_r z=<19wo>2GJhKGg0t-uh21642YALfN>mt24^9a=KMTf1gnv_S#8#Rl9%*ioj>F0x`v zg~+Dted*zMKm5siyGKL*a^J0daDsm}XLQhgw*r&%pi4LHlt*qm<4!q;9H_rAR=6UkNo1B9~Yr=L+?%k1wcNWtw(;8g&cQd6V3bXQ|ct@{tKT;G< z&VPx`7z)yj3k%Cl&jdxshr*V7GujOKS&!&N;lXU_Jccx?fyqhq{wwQH`?yOr8(i_Z zfK)$lQ?sIA2%xwuL|FD`9;E08^+9!2dyL%Bq6Ppw0q&B@koO{>cS;q6M2X>ymfa?j z?a*t8IwiW484~(g;icI^$EM4P4FhB?NFAjKV3^9k_gTl5`irD}TnuIfD@!`xCm~Ms zf5GC({7!sjJGB|?DU>?kr}6RH(YJzZdWPamGoTH_hIiycM6Ub6LumGTv$jb! znROE9L8_;z!sbh5RWl?@W7`3cODJzP-hjYY@urH6Z0GXzbZ|K62nh*E4IuoSF7ued z&s#xKj8_r`))4Uo^LZio{-GvBm}8#l1Pl>L%iZms2b|jn_ezT{5$W>MfgaC&IXF0! z)RLB=gDQm0xxLbW`h$BHphiZVe>cYcYVR%5R!=%CE-QBIXfg?^%Okn(QUEcA;?(Bi zGaHJZ&QBb=`EJ?+uVHvg`_;!9Ca@Ug`87U1K8ApKPmGmB*lsXkY*_7@hH9>zn>RLN zEFeiv=M+P9)@wgkGtHPB_gVKL&ayz4 z8(|h|=@GL$*I^W(D*P@;G4K$Z&Cu9lN3 zouZ4&M?wp6@k!sMdUjDLhMenRXfg30lG<&-Og2;{;^I*!$FJ)4j&;9x6+^t-(q?}m zA_B;4NVN56;ujyp`axPOz7;SxVZ<*NGHN4V@pg8M%JNEW2)y_1s{7Huc;rHuSY1;{ zknI&6QC9to5+YOWptz1f(xfzRMaZiN(?AmL^mqnWJxmU_EP3zdG7xh9hhMkuh3dSm za3Pmr!&$uTp`N=@;m$n=B3iVKub?;KxXpv4Y+abKiG{qEmlvPK%Wp&ib>+jQ(fIdIZl z>;m0RrCC|`BMAiD;$BgH2?p?7fe7WeV)cnya!5w;ooxJ|l3EUPs8}Wg@Su)|1~7Q% zbCCUT{gPdAL62*F6yAtv@#HTcdUN!Fr3Nj>#ghT=C zQf#Idiq0>$eSk@hX^{in#yT11kki!Sd<_WeQ#`yR-CIBU#to@w zb2%}MW!TC;d9NgRoo#2x0X z@Q>1V8{6JU)5fO~huJdSp=|NyD#k6Ht=ISRs%0ClUn0Z=#!z!4{Lp zMk?-M1O#-Y9838H1qCG{Xyef@Nlvl3Hn5K*aQ3VKYQMO+HxbIkGVY~r6eOLohky`@ zEbhYRye`kPnO1V}enR0+-GHc^`)eA1_k-Gl%O`)oeMv|lc&Qrf_L1##Vd2xtZ09Kf zjQwYGB;p%dN(mlu^DNx>LPyK%aoj$Azk+#VgduHt#?JBM$B$PT7#J3`?TLtpH0pf* zP`2e}`)eEgvHj$@p2pT1J$7|h$VkP`==aB&Z%x;@k_G`|KnwY)7r;X5!wxyS_cDib zrhc=)WKYM#sFSkk+Ar;Ijmv&SA1^A^Qp4?(4X;BCuQ}Tzg|WIU>wZ@&f^A|;f7Uv? zF(?14&9<+V^=uk{-2qb${%I2YZMF_&1w_jo|*oGMtJ zQCb%tocyDQR|T3shxLt(XpN^j+FkR+rP$=`BqS4hA5R`O67_gIqf&))9ODCNAMLmH zE7J*jTFQK7aXhKu>DJ9%aT5i+dH^5GwtG-{*-rL5zyoKtWY~A=kW-69@^i0HRJhPQ z#<)pBA3Q_J&(FUC=AruMwS|T^o2i|V%0?l-zdeYZ0RlY$zyn`SbIR*bxq?o+jS}5l z9_D=YiPzzJv|eR|BmT4Uo@xV2^r?>KK*Y((3GIC^fsC3D+cM{*n1~#{J6#-PiyCF^ z_46nWu(K2WJ{r9OH99LvUWPW& zhfh5dg9ftCcI7-kwIlqPBks0(b3JndfKKb99Uy_g@q$I{&y$bGC6ASh`^tVLG95Ge zM?^&MO%61Q+W%Rd6L;W?=@X^Hs;u)b2@8Oy8<5m%rNnzFh!vKotE+pNzH+y9NVh)w z+<~3y5~u&eEZ|9I9qZ5K%9E3)ZtoS{ik_uF8AKRb90SKCn6hukH}&*ltgi;Xb69#A z_Dzs2PC-%FEnW2oHb&(idWWP3#wmd{Y!N?OxBdX2>4<-KXgl60El8ktM1w3%RaDAS zdqG3YczRu4tA8=E`oFVln-zH_gK1vtYA<>?6u;nSZf?)luLhFBGkaFd1D`rLf5hu? zPV5cR#dNUEegD5+fQ6#L!G#Q}A3uKSyuTLNuixECX0@Oki1x&BW`Ni%v@L!up~-w#&xvhvY9 zU(IlI+G|(MlvU>1dF~9F5Iy}{h)?e82jt9lb)~XjmfC**W1#OYq6y;|rZk@YM(NRR zsQ>oLLoDjVt-!&jvgW@tQXmH#4&n2+Qe5XPHjAW*$P{K1nNI!bL~NSzIyrKW)b&bg9LJJqy0H zyWz`igB|_3r*K8vcbZM^%fay$bCi^eZZy3vwD0D%_22FgxZby&T+}`et>ySdMsV~* zPGg}{VUSONdtpb$@;Zt7M`-=UkN9{7qjwTU59IC)pD@*vgg~T0BU|PE@@B-!E6r*~Le@;| zyi_oJf{BszAIdZCiepv^PF0fk2?1!g1AmZ7Fo)kqqvNbmGFI>IS?d1Dz~*J8AODNx z>v_l2TA_i_a8tRh%e{MPMfJi{GuT*-#(bq5U9QENg$ly=i)+?BMw%%d;5~~3AS1@2a9+ZmORy5$9V;~ zZ}>G!@L3Hpq;X++c$|vJP8_;VHPrAY zJ8{)Y;Ni2f^r6Fi=Rv62(`4J#K-~4-#HKPT{CRENuTwBPZwW@1zWAfNS=lEbd1LYE)<+nqFUHH&S=agQr67^wz7kGNZ2c z6&3B@y;rzGb3*$$L%QW>6>6Ar{nD*I-RwUXkA#f#S8xcTA!`XBMd=sO@kFbCUo)T6 z{$i6mrbIIdcAoYb9mTMQMO^70Ydd=gw2@(&_K7OkoRA2QKB^R-!N&*o{Pv#`MSGgx z$o{2cr$EoJgsHguFAJMWqz7L9Xg@+VHu)cF{( z2WTxA=N_{W@8#qx0vRpwA$^pCLo=)BqdP^-0d?&dZSuyEQU;dULW}b0bw+0y!4Gmy zm%))GzWoUD1U4SX8Gvg@)x)bMN)c_g?Yf6KikvV>t&IPw0Vsz8|)R8Sl@o+c%lxD>quu+_ln4CD=UW zYrd#1$<37+F}3v~Y{?u{RlTocj+GwyN*w+e&U}YeOLK}!kY}OE-FzLFRi=nDn>+qwJ3_ zZ$0a!&88uokuf)Q_Z^?!9l%AoSW@1Y3IYexi*$8`g@ls4Gk2#F6+e&pVWIUvJEsab z0I^p2Ub66Ac59Cp(V@EilYfcJKu1$gKWq!gX%5K;F#&pdSEu3I{Pn;G&|RamcuBpi z*7wuB;wh4a=gwzvJx?-29k!32#AxwJ%ub|`7|Xz^xEz!soLxXmNlSH-WHOD|w(h-} zB2PbbLfDIBrjsqp0Pv2pN#12bOoYArnB<60W-S;%MFzy4aCjj1sI7p1t)8p zQRdkf;W_R9eMCTf%=Pzf)J{=fM_H0u#+>FUj(A&5=hp}^gfi1pb!M5DH5f3lJdus; z^kt9>z@i<}ds}C!seyD))K~K$bgxK#&yY>Zc}B^!Y4$H;6pN= zZ#qe}RyWy)WvV;ePnCf3R7cw==3P?+zV1{>K8o|h(!>@8`$2u+Ea&K2y$_fKK#L93 zxk10*2YTfZ5)6QImD#+>d)MxY(=()ao!Y?FtJpEo7>wTzsk<+sfLv$7lZ;qT3yiSI z6$APc{$c&TasUcPtz?t@0ev9m?#P^GR5NFG^(boS+4w$=(#HS1t?Kg_8@K)q3|y7Srd6la2GXE z3&p~6p4Xz=ID!qIVIS8c40orqBSNy@P5;?44_Qy z)G0~H(zRS*4KD)hO}N}xqebT9(VleI+yJ&eeT+g1MG!ZzIt<>v_lDO2=L934Q5JU zJsPL7Xe8f~0b{O)8QTYM@Yf`cpudMCaR)^d(r5J-PG*B?WeS|tX9h3qAgKB3cyf@s^!VrOV!5dTrPXXCpfw}PytnP@FPGfT%c%7p`uoyEh zm4;?ya*|DcsPDsw7jM7Hj4c${0}q3^-jkbxMP6&W!YHeaxEc;aCSB3B@dyl_(~B^X zvXzBpQe-7U$4$c)EKtw@_?=BJTN7g$Qg#&x;FPKwivj}NMUW#J9LGdcFX8zaLp*t~ z>(r?PP~A!Sgb;eL5*fW)5O>q)slF{vkPRdVTlQ*=8Kid#4W#FtUY2p6yvXHNO0^Z8 zCs^vLjVzv3GWOTbL{PNG3}deMaq11y4N5am(-Q^#@`Gs?!}8UxRPo>4ffv|-FyG^& z9i)QFz%!3Qp<~5~zLwv2@_IOn8_c|+|Jj;;<;;K4a=0p{^`mm|kQ&OwZ00y5$o|*C z`DGpkVV2G06s_~z6H~0t7eVCOUC%$(O<~fVV*xB`1X(#){|F!oB`aXQ?5Iohz7fZ$^Q3Xr+@c{Naec_akNO0s=n7=4r zbrE0|@>>q3|AptwKsAC$eIMvhLQV+zi`{+VF-6mf)>6~{mK+q?Ol!4s3WHQR-G9UoUj~~#KK<|Hg!hJIrsim+yw^RMS#;pf>Bd2BZh|NlH4hK3 za;w7v%rl9Kc@sOy@iE4@_bbPp)QfQq>RhM6@o!ebXC8pR$k+YP9@+9|*z{!)0OZFn}V@dGpc8h8u5O@I6tLpD#2IfDSDrKKWKe zv)NpWuHO}OephU~+piN*Aew>BCW~77jWeC}E%nnJuY>%$eKjk;7MzLI*Y(UQIi-iZ+b8tZCzE~?((Sm zbLzBuK-{b1>Xj1LluNr*_yy2{FJvP#(Ki#?}%yL5gv=QL+YYsL?3-q#TsQK=$9 zqA$$l{7I}HW+amPkqsg~$I2<(LqTPgN4T2B1EtOzkS%s2k1@3BMGw`DHuBy9a@P^BVeLxu|xjeJ$G4&(I_@D zbz*C}*xAcM(w3cx^@Wv6f&^e43=?ApbT!>4Z>w0`V6jzDxN)7I%OA`5XpAer#_|>U zmdO|9McW4$61kjH9DJ2x2WBDr4b<2Ei{Pz4pB&7nz#LN=EOr#;f#23(UBVU}n6|7R zP6sH29y#q0lt1k8mdv4|eo7BFWAa(MG1fl!QCa1$LCa=#59#SwJyM%l`N|tZJrR6B z;_mf${-UU)u95<4NHaIW2>jfe1BDX`R_T>NS3?49NJ=DzSl0+HN)})E3_Z-QrlvKL zHGUh=JC|Id)#65*pIu#aAvPsT;e`j^wKUn-*yqQ#2ul9FmcO0lZO}4QVXDi7Q(W@Y zq(1TOLCHLrToXkozXGY(1dH6ocU)y71m&Ci?m8~B6fa8^FU#G)F7C7Jth)F%M|UiJ z+Z)fjUTw&lNqBN?`9fJ$-cnn_eOxT7L~!c?p1Si?sz-~(X;J7h?E5zMn#ouxV!Xm- zGnO_rbE^Q1BQ3!b<|*KUsbiUCe^!g~x31pvS8gU2imlOctBTH5E@NIHTVB~Shlm+z zCT&G?wbO9k(oR3|iE^(4%vca!kADzm87fATzz+{o41iY)OyeYQ>@Ft3>Ovnd77QME7I8e-ps38dPvh%Qrc6qlxci>%9>6;47>&O5 z)2B2)v~qLVkwj?ChMKeb{Hq|ywq_4l6XPHhhS)1*Ou+3gL@IS;$yr~rPSJoV6+xhu zX|)|~`+zw0X{OVH$+#3E%UcyHQ`A;fx&{`w4p|5*T$sD9Sn=_k*uM2uScIn2u8gPud{%>cC@0`Kk8gs z0JJ)``O8IjmJVAoNmwE@N`A!8UA=c7pxG)Ba!TrWuBGoHXVOJ2pC?bEi<|4R^Zezz z&Izo>sfwk-B_;2(ifjbdlAvMo>yTGJa*IP(B!F@pZ|)2uvR_3brN_nfvs!1HPKN96 zmiQ?Aa@tk)OF17t!a5O38FRwYF>2Jd=rIvGeuWp}S8p;BOV)Q;aZtnDf(;G_-$}iH zQWw^QJ<*-YyCc=!jP4Ff`$Pb}GnQnNbepEFv3a0<9bvAY6oiJ+s zEiA3kqO%;I-CCQksNQkrG#hNtlp(#8bQKJY!M!@gCKTNWPU3~8oxlIeeCm1t042qF z`Gq8I^IcI4`=ICroCe=g9%Y3CB!v11=|xG?n+WqJ*rAEnQLD7aKG%TcONCd<^xcR3 zIBP6;M?b+JuS5=R6hc;%kBq02By34NO8V0MlG;;U(Z9h$F&Hu73+5S8$#3^8dDNl< z+IM7wxh)Iz)c<QQ{B|k3Tx_*$ zisaDhw68|d56NB^UQVhB-Z+MvB1;MuWbF#H?vhl4t)soB!t6VRcaDZau98;GCbCD2 z4SPq)QnX)Z2I}q}6rik1H{X|kcxj`g^wsUQe)Q?_t;5_koQU$7QQ1`(vnhdr2VIk` zRpyd>I%;Y2X7*_o!D|UZQX~b(6Y`hyZ=s|65u7fxar>4!PE;zQLqT6yO~rvYetfFe zo)SxT(x248fWL4S`R$$E{#*a%=Uy<jbuRr|Uw_eGvHA;PyEqX2wBLTI=@5y;hH%xbeRZB}`p=JeOm;*~nfHp%C@5(^ zY()bEezk+CHLE51Tdu7~W2E7c2NRULJ#fl@3|3a}c?igyo@vzVPwF*RefC9a1Lu3d zFf$*g<6B`?is;(ONQPb8!zk6r=ihJ3VQcT#dV~hXSn}UrL_21BahP**ory^vs7ahh z$G1gMQ)8FdP*RL!DB3dtu;=$tJ_=A|eGHR1yLOcW`tA?v#ozK>N=VMxF>WV!@R;|t zp5rL9*mVqTaL>2FvTh+&FaLv;U)8xwV%X0MriEvM(z1s+oJikjjYLFI|F~_!SEu<+ z+R_sY3ubSSu6izp^oghEu(7pKd-o1+%ynVn1CsL|=`DVRTw{>bU{oG{t@pmFGDzUD z)GvCb^?i#S707EytN_oH^dz1}?inUtEA#SAa>}-cohaCyqpHG;#XNI#D3lQDvwU7! zdX4ngMlo&1s(&?3zQ^GFcRl`RW8*??@N&A9PY>H;4G$5LO$^F z7qQv&Lw&3z0!%a5C1f?b?tKI$ZHM|`SA^bH<+c5MbH74r)0Pcqh{J=x(BxsLD)%y-qx>c3) zBb~;&a@-bX>9z1ME%^#R_7P;C-+(?06YeC=5YVc&W@xOJ4k0_E?61mLDdKP z4FonL8_8nR(?L_K6TP>G_PDv^;p^yyUVTBlp;xt;J&xy0SFpC`_VzZ|brnKUc0#?Z zwEgUZG=f3+9?4>CD9~w(yrf%EV=37rkQt&uvJjr#P`>FXC}|3#O@mRWs;VZ)-pK7q zyGgN@rHbYL`h|scxx_qtkn}JZNrw(!NqxqwT)q>mS7o;26a-g{aTn_hl8h@+H9qMp3P#glhbV6d z_z)4MsT~ITvCw8BHEq2}5(eg0BlHPviI!iZFet6eB4AN{%cUwU$nqcjX&VphY46O% z7W%ksbUnfJa|x>QxT?OMGTrw~m>G8Rvf=t-?&+)O!K0xAAnG01RcdexiV;(|OPxdt zn7(3R-G{qQe{OR8RdP6&ot<6v#etWC;!DYGldL2PJIwDWW`PM_HTlkNrkq}|=}5-V z>o4`$j2&CFF}O`37Y65*Z$7)S--5~5!TwfH0?bijH{!Zx3kPG>0n<`c@{vwTN-A}+ zLV2rJe4U$|4RFDy&$Tleepb`{8h4TG5 zIyC6`9*=eKKSXT!EBuzA+QFNUWdw{L>HMxTsu3vd8YDPn!y{+LI8-6 z{hc3Jk0cj`LCR@SzcBnirBJr1je^@3vB6+lDF_oqd;nV|J-<;_AY{y)I&fPy^@>!Hg*BO<^N3t8+#)ZUDl{G542wiE?|0=~6@y~|9neC92q%@!ENu8b zXe@%Wf|j9jwq5VQnQ6hUYd`7}uw4>tn5@l}#a=3^G@oj|#eV~mgFe#j&4_q(^&VuI zrR7ikj%|FX%&o-2LKFwUWg4CxMVflQyO4zs;LxGFjgdxEIz}$mI{3E$=aJH|UUF8! zq~yo`cc@_I$jj?dOxzPT6o|4YOs@do^e1w+lim{-ZUkR}P+3`Nu{$6y@n3ZX^|KFg zKKLNh{65c+p@r6V36E$!n+4YbZrAZqq3I$70PN;0swy8nNli=qhPUJig3MS)mT!+HVI#YpK8r$hj|l*# zeGaX!^8zM-*T%?8J>!aCj0B&l(LWSS&!=04trVhO;J0X9E?&P*cFafX!ev5W2-TnW zQ4&wjB%<6ZFN57~7mp&UX4wmm6+Rhfz+!2xqGQswg|+4caD({uRT&iaG;U|qMblcX zYSbKtVi0o65EaYw6H+;7t*FIkWrst(tl?n5Lb6H(h0hDcTE>Xtz6+o6F_M>;_sMPh zcRLJxH^EYoaeCfw;vRjuHxMVThDorsnao+iwY~Yy5~Y@xy5^MFqdjB~4Et8?tSZ9# zN#WIjf!2?o2c~hgw7PFS1+wqN;ws{0tO5^0r0_x9KR+SCFp*U~7C&74PU%ZF48@P3 tqIai%K#TYsL&AP5r~l7aL^K9}e{&4af^7*6z5xSxsrXu<_?dCw{{sp`Mq~g0 diff --git a/docs/assets/diffy-logo.svg b/docs/assets/diffy-logo.svg deleted file mode 100644 index f28d32b..0000000 --- a/docs/assets/diffy-logo.svg +++ /dev/null @@ -1,44 +0,0 @@ - - Diffy logo - Diffy wordmark with the split D-shaped diff mark. - - - - - - - - - - - - - - - - - - - Diffy - - - git diffs in VS Code - - diff --git a/docs/assets/diffy-mark-128.png b/docs/assets/diffy-mark-128.png deleted file mode 100644 index bd787c1077a236305ca0355e008aef07b4ae8893..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 5123 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 - Diffy mark - A split D-shaped diff mark with addition and deletion indicators. - - - - - - - - - - - - - - - - - - diff --git a/docs/design-system.md b/docs/design-system.md index 7207d4a..bbc0faa 100644 --- a/docs/design-system.md +++ b/docs/design-system.md @@ -58,32 +58,43 @@ Example body copy: The Diffy mark should communicate comparison, pairing, and code review without looking like a separate IDE. -Source assets: - -- [Mark SVG](assets/diffy-mark.svg) -- [Logo SVG](assets/diffy-logo.svg) -- [128px mark PNG](assets/diffy-mark-128.png) -- [256px mark PNG](assets/diffy-mark-256.png) -- [768px logo PNG](assets/diffy-logo-768.png) -- Root extension icon: `icon.png` +The current logo set is raster-only. There is no SVG source for these assets. Each icon is built from flat geometric shapes, hard color boundaries, and no text, so a future vector conversion can trace the shapes cleanly. + +Current assets: + +| Asset | Format | Use | +| ---------------------------------------------------------- | ------ | ---------------------------- | +| [Contact sheet](assets/diffy-icon-contact-sheet.png) | PNG | Compare all current concepts | +| [Contact sheet WebP](assets/diffy-icon-contact-sheet.webp) | WebP | Lightweight concept preview | +| [Primary 128](assets/diffy-icon-primary-128.png) | PNG | Root extension icon source | +| [Primary 256](assets/diffy-icon-primary-256.png) | PNG | Marketplace and docs | +| [Primary 512](assets/diffy-icon-primary-512.png) | PNG | High-resolution export | +| [Primary WebP](assets/diffy-icon-primary.webp) | WebP | Website asset | +| [Brackets WebP](assets/diffy-icon-brackets.webp) | WebP | Alternate concept | +| [Panes WebP](assets/diffy-icon-panes.webp) | WebP | Alternate concept | +| [Delta WebP](assets/diffy-icon-delta.webp) | WebP | Alternate concept | +| [Gutter WebP](assets/diffy-icon-gutter.webp) | WebP | Alternate concept | +| Root extension icon: `icon.png` | PNG | VS Code package icon | Preferred concepts: -- Two adjacent file panes with a narrow comparison gutter. -- A compact `D` monogram split into left and right halves. -- A restrained angle-bracket or bracket motif that suggests code without becoming a generic developer logo. +- Opposing panes, brackets, or angled halves that imply Side A vs Side B. +- A clear central gutter or compare rail. +- Flat shapes with enough negative space to survive at 16px. +- One primary brand accent plus optional semantic diff accents. Do: -- Keep the icon readable at 16px, 32px, and 128px. -- Prefer a simple geometric shape with one accent color. +- Keep icons readable at 16px, 32px, 128px, and 256px. +- Use flat fills, crisp geometry, and limited color counts. - Test against light, dark, and marketplace backgrounds. +- Keep icon variants text-free. Do not: - Use VS Code product marks or GitHub marks as part of the logo. - Build the mark around a custom sidebar, panel, activity icon, or webview concept. -- Use a complex multi-color illustration for the extension icon. +- Use gradients, shadows, texture, tiny line art, or photo-like detail in extension icons. ## Color diff --git a/icon.png b/icon.png index bd787c1077a236305ca0355e008aef07b4ae8893..3b04f7219f64d88897d219b72406eecf87e84d36 100644 GIT binary patch literal 6209 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 literal 5123 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 { return UI_TEXT.pickRefPlaceholder; }; +const resolveExcludeBranchName = async ({ + repo, + output, +}: { + repo: GitRepo; + output: vscode.OutputChannel; +}): Promise => { + const r = await repo.currentBranch(); + if (!r.ok) { + // Best-effort: log and continue with no exclusion. A failed lookup must not + // block ref picking — the worst case is the user sees their own branch. + reportGitError({ output, op: GIT_OPS.currentBranch, e: r.error }); + return undefined; + } + return r.value; +}; + export const pickRefAsSha = async ({ repo, output, @@ -93,10 +111,13 @@ export const pickRefAsSha = async ({ reportGitError({ output, op: GIT_OPS.listRefs, e: refs.error }); return err(CANCELLED); } - const args = filter === undefined - ? { refs: refs.value, placeholder: placeholderForRefFilter() } - : { refs: refs.value, placeholder: placeholderForRefFilter(filter), filter }; - const picked = await pickRef(args); + const excludeBranchName = await resolveExcludeBranchName({ repo, output }); + const picked = await pickRef({ + refs: refs.value, + placeholder: placeholderForRefFilter(filter), + filter, + excludeBranchName, + }); if (!picked.ok) { return err(CANCELLED); } diff --git a/src/git/GitRepo.ts b/src/git/GitRepo.ts index fc931c7..0d3f315 100644 --- a/src/git/GitRepo.ts +++ b/src/git/GitRepo.ts @@ -1,5 +1,5 @@ import { DEFAULT_LOG_LIMIT, GIT_LOG_FORMAT, REFS_FORMAT } from '../constants'; -import { type Result, ok, err, andThen } from '../result'; +import { type Result, ok, err, andThen, map } from '../result'; import type { GitRunner } from './GitRunner'; import { parseLog, @@ -41,6 +41,7 @@ export interface GitRepo { show: (args: ShowArgs) => Promise>; refs: () => Promise>; revParse: (name: string) => Promise>; + currentBranch: () => Promise>; } const buildLogArgs = (params: { limit: number; ref?: string }): readonly string[] => { @@ -99,4 +100,11 @@ export const createGitRepo = ({ const r = await runner.run({ args: ['rev-parse', '--verify', name], cwd }); return andThen(r, trimSha); }, + currentBranch: async () => { + const r = await runner.run({ args: ['branch', '--show-current'], cwd }); + return map(r, (stdout) => { + const name = stdout.trim(); + return name.length === 0 ? undefined : name; + }); + }, }); diff --git a/src/test/suite/commands.test.ts b/src/test/suite/commands.test.ts index a4e922b..ce060e4 100644 --- a/src/test/suite/commands.test.ts +++ b/src/test/suite/commands.test.ts @@ -205,8 +205,8 @@ describe('Diffy commands — end-to-end through real QuickPick UI', () => { // pickRef is item 4 → moveNext × 3 await moveAndAccept(3); - // RefPicker: alphabetical — refs/heads/main first, refs/tags/v0.1.0 second - // Pick the v0.1.0 tag (item 2) → moveNext × 1 + // RefPicker (current branch `main` excluded): refs/heads/feature first, + // refs/tags/v0.1.0 second. Pick v0.1.0 → moveNext × 1. await moveAndAccept(1); // FilePicker @@ -377,13 +377,14 @@ describe('Diffy commands — end-to-end through real QuickPick UI', () => { await flow; }); - it('compareWithBranch(historyItem={id:sha1}) → branch-filtered RefPicker → diff vs main(=sha3)', async () => { + it('compareWithBranch(historyItem={id:sha1}) → branch-filtered RefPicker hides current branch `main` → diff vs feature(=sha2)', async () => { const shas = readSeedShas(); const flow = vscode.commands.executeCommand(COMMAND_IDS.compareWithBranch, { id: shas.first, }); - // RefPicker with branch filter shows only "main" → accept top + // RefPicker with branch filter: current branch `main` is hidden, leaving + // only `feature` (at sha2). Accept top. await accept(); // FilePicker top await accept(); @@ -393,10 +394,10 @@ describe('Diffy commands — end-to-end through real QuickPick UI', () => { assert.equal(uris.left.scheme, 'diffy'); assert.equal(uris.right.scheme, 'diffy'); assert.match(uris.left.toString(), new RegExp(`diffy://commit/${shas.first}/`)); - assert.match(uris.right.toString(), new RegExp(`diffy://commit/${shas.third}/`)); + assert.match(uris.right.toString(), new RegExp(`diffy://commit/${shas.second}/`)); assert.match( labelStrings(diffTab), - new RegExp(`^${shas.first.slice(0, 7)} ↔ ${shas.third.slice(0, 7)} — `), + new RegExp(`^${shas.first.slice(0, 7)} ↔ ${shas.second.slice(0, 7)} — `), ); await dismissQuickPick(); @@ -443,23 +444,24 @@ describe('Diffy commands — end-to-end through real QuickPick UI', () => { assert.equal(allDiffTabs().length, before); }); - it('compareFileWithBranch(uri:a.txt) → RefPicker shows only branches → diff opens for branch HEAD vs working copy', async () => { + it('compareFileWithBranch(uri:a.txt) → branch RefPicker hides current branch → diff opens for feature(=sha2) vs working copy', async () => { const shas = readSeedShas(); const aTxt = vscode.Uri.file(`${workspaceRoot()}/a.txt`); const flow = vscode.commands.executeCommand(COMMAND_IDS.compareFileWithBranch, aTxt); - // RefPicker with branch filter shows only "main" → accept + // RefPicker with branch filter hides the current branch `main`, leaving + // only `feature` (at sha2). Accept. await accept(); const diffTab = await waitForDiffTab(); const uris = tabInputUris(diffTab); assert.equal(uris.left.scheme, 'diffy'); - assert.match(uris.left.toString(), new RegExp(`diffy://commit/${shas.third}/a\\.txt$`)); + assert.match(uris.left.toString(), new RegExp(`diffy://commit/${shas.second}/a\\.txt$`)); assert.equal(uris.right.scheme, 'file'); assert.match(uris.right.fsPath, /a\.txt$/); assert.match( labelStrings(diffTab), - new RegExp(`^${shas.third.slice(0, 7)} ↔ Working Copy — a\\.txt$`), + new RegExp(`^${shas.second.slice(0, 7)} ↔ Working Copy — a\\.txt$`), ); await flow; diff --git a/src/test/unit/refFilter.test.ts b/src/test/unit/refFilter.test.ts new file mode 100644 index 0000000..66162b4 --- /dev/null +++ b/src/test/unit/refFilter.test.ts @@ -0,0 +1,102 @@ +import { strict as assert } from 'node:assert'; +import { REF_TYPES } from '../../constants'; +import { filterRefs } from '../../ui/format/refFilter'; +import type { Ref } from '../../git/types'; + +const branch = (name: string): Ref => ({ + name, + fullName: `refs/heads/${name}`, + sha: `sha-${name}`, + type: REF_TYPES.branch, +}); + +const tag = (name: string): Ref => ({ + name, + fullName: `refs/tags/${name}`, + sha: `sha-${name}`, + type: REF_TYPES.tag, +}); + +const refs: readonly Ref[] = [ + branch('main'), + branch('agentpmo'), + branch('feature'), + tag('v0.1.0'), + tag('agentpmo'), +]; + +describe('filterRefs', () => { + it('returns every ref untouched when no filters supplied', () => { + const r = filterRefs({ refs }); + assert.equal(r.length, refs.length); + assert.deepEqual( + r.map((x) => x.fullName), + refs.map((x) => x.fullName), + ); + }); + + it('keeps only branches when type=branch', () => { + const r = filterRefs({ refs, type: REF_TYPES.branch }); + assert.deepEqual( + r.map((x) => x.name), + ['main', 'agentpmo', 'feature'], + ); + }); + + it('keeps only tags when type=tag', () => { + const r = filterRefs({ refs, type: REF_TYPES.tag }); + assert.deepEqual( + r.map((x) => x.name), + ['v0.1.0', 'agentpmo'], + ); + }); + + it('excludes the named branch (current HEAD) from the unfiltered list', () => { + const r = filterRefs({ refs, excludeBranchName: 'agentpmo' }); + assert.equal( + r.find((x) => x.type === REF_TYPES.branch && x.name === 'agentpmo'), + undefined, + 'branch named agentpmo must be excluded', + ); + assert.ok( + r.find((x) => x.type === REF_TYPES.tag && x.name === 'agentpmo'), + 'tag named agentpmo must NOT be excluded — name collision is fine across ref types', + ); + assert.deepEqual( + r.map((x) => x.fullName), + ['refs/heads/main', 'refs/heads/feature', 'refs/tags/v0.1.0', 'refs/tags/agentpmo'], + ); + }); + + it('excludes the named branch under a branch-type filter (the picker case the user reported)', () => { + const r = filterRefs({ + refs, + type: REF_TYPES.branch, + excludeBranchName: 'agentpmo', + }); + assert.deepEqual( + r.map((x) => x.name), + ['main', 'feature'], + ); + }); + + it('excludeBranchName=undefined is a no-op (detached HEAD case)', () => { + const r = filterRefs({ refs, type: REF_TYPES.branch, excludeBranchName: undefined }); + assert.deepEqual( + r.map((x) => x.name), + ['main', 'agentpmo', 'feature'], + ); + }); + + it('excludeBranchName never removes tags even when the tag name matches', () => { + const r = filterRefs({ + refs, + type: REF_TYPES.tag, + excludeBranchName: 'agentpmo', + }); + assert.deepEqual( + r.map((x) => x.name), + ['v0.1.0', 'agentpmo'], + ); + }); +}); diff --git a/src/ui/RefPicker.ts b/src/ui/RefPicker.ts index 6efe0b7..38459bf 100644 --- a/src/ui/RefPicker.ts +++ b/src/ui/RefPicker.ts @@ -5,6 +5,7 @@ import type { Ref, RefType } from '../git/types'; import { showSinglePick } from './runQuickPick'; import type { Cancelled } from './cancelled'; import { refTypeLabel } from './format/refLabel'; +import { filterRefs } from './format/refFilter'; interface RefPickItem extends vscode.QuickPickItem { readonly ref: Ref; @@ -17,22 +18,18 @@ const toItem = (ref: Ref): RefPickItem => ({ ref, }); -export const filterRefs = ( - refs: readonly Ref[], - filter?: RefType, -): readonly Ref[] => - filter === undefined ? refs : refs.filter((r) => r.type === filter); - export const pickRef = async ({ refs, placeholder, filter, + excludeBranchName, }: { refs: readonly Ref[]; placeholder?: string; - filter?: RefType; + filter?: RefType | undefined; + excludeBranchName?: string | undefined; }): Promise> => { - const items = filterRefs(refs, filter).map(toItem); + const items = filterRefs({ refs, type: filter, excludeBranchName }).map(toItem); const r = await showSinglePick({ items, placeholder: placeholder ?? UI_TEXT.pickRefPlaceholder, diff --git a/src/ui/format/refFilter.ts b/src/ui/format/refFilter.ts new file mode 100644 index 0000000..32d3600 --- /dev/null +++ b/src/ui/format/refFilter.ts @@ -0,0 +1,22 @@ +import { REF_TYPES } from '../../constants'; +import type { Ref, RefType } from '../../git/types'; + +export interface FilterRefsArgs { + readonly refs: readonly Ref[]; + readonly type?: RefType | undefined; + readonly excludeBranchName?: string | undefined; +} + +export const filterRefs = ({ + refs, + type, + excludeBranchName, +}: FilterRefsArgs): readonly Ref[] => { + const byType = type === undefined ? refs : refs.filter((r) => r.type === type); + if (excludeBranchName === undefined) { + return byType; + } + return byType.filter( + (r) => !(r.type === REF_TYPES.branch && r.name === excludeBranchName), + ); +}; diff --git a/test-fixtures/repo-seed/seed.sh b/test-fixtures/repo-seed/seed.sh index 1fcc910..30582a4 100755 --- a/test-fixtures/repo-seed/seed.sh +++ b/test-fixtures/repo-seed/seed.sh @@ -50,6 +50,10 @@ git commit -q -m "third: rename b -> b2 and extend" # Tag commit 2 so RefPicker has at least one tag to pick from git tag v0.1.0 HEAD~1 +# Second branch at commit 2 so RefPicker has a non-current branch to pick +# (current branch is excluded — comparing main↔main makes no sense). +git branch feature HEAD~1 + # Leave a working-copy edit on a.txt so 'compare with Working Copy' has content printf 'a.txt v2 edited\nsecond line v1\nthird line added\nworking copy uncommitted\n' > a.txt From d69514e57901fedb24b21c98a77a17cdd0a8d890 Mon Sep 17 00:00:00 2001 From: Christian Findlay <16697547+MelbourneDeveloper@users.noreply.github.com> Date: Sun, 24 May 2026 16:18:23 +1000 Subject: [PATCH 08/17] icon --- icon.png | Bin 6209 -> 7494 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/icon.png b/icon.png index 3b04f7219f64d88897d219b72406eecf87e84d36..56b8bb6e01cfaf60747a66d7629f995715cfb195 100644 GIT binary patch literal 7494 zcmV-M9l7F(P)0x00qs` zOZnq{*xcN_pw9PQ%wyVPjP395ADG}{ccv%HOaZ_|*AC)8ELpT@6+3tULtTsfB{C$++%yXmoDvXHeP0X;Yqw*36_gdJyy1NH{7k!kkBBtlw({&*LPR#hW zMT_1_4z6Y9%Z;J5ybp&jC7*M2-}tB!M)iF>A6x;2431^CV19tve7mc!@0WXz^;BuP z7$CaJZ)s_1cVNE^@W%|1iID8)iLffeOQ%@(D;T0b-RX1i#h#u$uSFn;jm1gRR2dR8 z)h=1Q=zR?SjsdmXaJt((##d=-B(t5ONwwQDKG)sb{~yt3W-2OSD$E(Zaa(iKx5&`7 z0!B4zLWxkN=_egqLnNxVO7jerD$NXu zbPbFl*IN6erNmX;-Q7DUD+wk80R2vTdwWgbW$tqfz1>>&03;@Httw5PJVTUVwi^OU zTrgP>m`N~S#0^<5bGt)T=FcG|EVD;sjwSYg*4EZmHyJ~_TmT63{kG=DZyH0F%L7zr z{@jDWAzH&h_Lj&FP+v=_R1R9y&}d!MaHVmapD=UP*sCyg5U~ApSI@5Rlw%Z9Ig+BS zt*(WG|`9|5lE3sFX2Lk5hX~!$}tAx#;2*<=|1~5@4byvi#;FCiy)75R%W;RgD9J z0wQwQ+IM&M?)rHts-RSd4F^o0cZ_D?FT@{2eLuP#)Ki%xIo$kYF!!) zK4b#MhzAV%KrT8rX-LmYm^?sW|0X7U34j2g=&KZCq^>JlTN_r8H81B`{Bs6?c=qN3 zV8~--SW6Az$R>jm788zZHaKjd!O}XQK1o=R91A}Ud%&I?W9KliWw*t~UdD?(7O(bM zbPk7ORhJ;7oPDLoenggt41v$;=}4t!RO2R54QH1=Ynfi$5!ObBBsk|>&`AmCwO=YYlCn*-ds(Z{+@i?mCq zON?E6KOi;G8N9 zfnOI=1BI4y`dL8UnG%MKB2hG(vwm0&JK)nNC-B4v()im`5?J5@!7xLVkucpcQ)7O< zdXYn9Trk3F>|Arai$~v|#`R|;bTIbh!spsg)Bs!HoapxnN2uuQa9~csERzg!?+D|F z2Eu*sN#VxxlUS4jd|^C?On;MTpGKkxR6+uTS_k;-nMpjdHjOn)47!I|fimaIpu$9< zU>xNgPX+)g70EynIQ2)&FvU8#dxY`+m97r@=?6O@NOW$yrbr@L4{#ay*9(*Q&bdkTJBU3{p&+0zDT@}bXlhzm=){65K=SsP(Htdo(u^}d z!}!+ONqp&?B!X->>tk^|)lAE&R#}+n?L`{=^{ENm{LUn@0s>pHAdn$#@DqvVv0wM4 zg2P+mc?t0^V}?m)_du5M?Q@d&_?mUv?i9I~Ew z(ge4LU=kr?1~QCYBaDF&R;E(|`ne z)!=Q*3=VFLL`{wr2x8%v41yuXM^`8C`Yy&dp7*f0mQ=dzPc+f%s^{pChK8n0!fYm> zX}X<2GGoNMx1xdY>$PdrCjkmd7yB}somBX!k8TZc%LX4${3*bWp)fw+8d89X^Jerr zk@)v4>x;D35>8+0;Df6iT(-i|lg0MXP$!ihgqJV8>nB+}zTJj~>)bnwMtSA1IeJH! zA24pd;G6+K23RcVhu@RJDF+(_nbHit(6|LCMR@j)0dD+_hhJ^8%2WyDB=dBA?DEaO zXB;|~KYLL?NosITUuy7~Q(atigsalPmT24w*hJlI?Pr{IM@DS|btqx~0KlC1wPa?> zTKex!aB<3V2Y!jn7p9QtZ3A5Ycn%l;IE#n21nR4AN{_i})yRv2IQc7pSl`D+phKb- z2n{LV*$#`V?#bbk4`#7Pyo{0mzZhvKA_pvAZ19CO2@K@QQ)6>Yv;L?8OA-A?G#LED zngnc#1E$NZ6wo!yxa`MSeDzrmDK})GGIkQd8qPByC2YJf3H;YfJ}$f~gH3&`24yKA zkmt7<#>d{`;`Aj(UB;Da6wWySWUvNvjB8JJ(YSy#phG3he9`x>^)fE{Q5KJF571Ui zqszYOpdh-I*aaJU8Sng22G9I4C9E3IBL%BD6880N9sI6wg1VSwgxlI9q)7 z$2zuakg@jXS!~+HXigKn8M3xL3snhyS>Wn>vv{rB>MEm{&mfip?^xmB>}3x2WJ_OB z%qswh(e>d~4r=QNfmruX?iso3WG|RDODM^Z2#;be`%o+fM*@GVAyn_vvHW}6Zg>J4+nH2E+)&SpJ z=WE{Ic+A(O-83HgB=?ilvs+&FaqlZWoK#_ZK*MeURJy(EFb6|^<*x{4_3=b1UV4}d z7JXSnoW{KP+H;;tj=lA%`1XxM2z-X`P0tbdp;XG3O!Ay5L;4(EuDH}v->VI!K+*|U zD3wgJ<_eb7ZJAAyz)3RDRDAY7xy9n)ZIX@^isYD#A>=X^UN%5uTSBex>F~VR0O;@Z zAo-%{ItMlgy^7+ce4)$Yr<($-J>G>s62>dWBMoBtz=?|qhb|;dFcs$%&u0Lr@#h1_ zw-63)APw^tTqj4-=kIOs;ROtn9GgkP{E?vm?JLswz!#Qb<*D^>6XB9@Dnx2?Ft%qIRLHAmGTg{68CftX<_4wC=;}0Cly5x3rMP3DT*Ga1YNd0HkQ+gce5= zF73#~x>3f-ae>|ei^sNG&Fzo%u893?CS?Bg_Z)@xL(>=@n#Rs)46L~{g=5ZGfUmsg zRlNG#AZqFy1nOo6p6{}FwVQEND`|qrL?G6st3x=s)!@!eLD&V1=dlf-@6I9t!Z>fC zu~23w2v6*=*fD64l;v0SnGB?4>&na*=J$2wrcM9*a`=paZXb0G4nF*qgO!O)x;>vlCJE#tTr2Z;)|17-&R3>}C=8;r`rqFyorp6?98_{V4ih?(>7MZbvS|Y6rykcB5yLIDrCS0>%Z~RFuwNw*YMo^ z{it0S`U3WTwo;dzaSlQy)2umyv~T=0%mQJEtj}eM_NB{SLHmjn5-Af|>yy0!tIiis z;LbO)$c+T3YY2S+)6&CWJ_8`Gpcx4mLmto|CE644Dz=^%CP@=GJBJW11NY*CFhK+0muiREea*JY_~5Kzl!%cq1Fr;j_te78L86@)o|cf?&Qof+P=MFgKQ`Jki(* zFC|^OElSLo=sfY-2_@5$C?F_6EcF88%zRyjGSHk3)6WzD6I~-ZKYr&5$Pt`d8$bY% z2@rPSC^8tzS65QECHhx_&md0CE#u$Zqm;LK4FFBM4;Wi^ zhsAV-BzFAK$*s=BT@CcX2@Vl_W_V>Ba=v_5j&evPfMuN2YII5$1b}`4_|ssX3cx&8 z0rC59=oP=cTX5(#M&L8fUS>3TL=s5%9zcP{FbD8#fL6B#NitHzS~R;zo+Mz4Eyd2#U9DYE*uJ0zkv_5HxkZR zYH-hHt43F>mWBCytdEtcdVI0%1e{jWj1x7YVrCa^>|Bp$M|#m<5=B7} zrm_JiwGxhRHR`A;77-Lxv#u+EQ_0hRwg6xwPv9$kjLzMRwuNCxxR4uc zJ6g#hY#!kNN2cm=-(lyWEmlWjRHy*_^!@VjAYyl92HVor~M<+Bi zT__(wNLI)9|XkfyTWu3*dofa%5Shh%pZI*H23I}H{b+9|iu+?fJ zYjJ(s3CjGeKcY*(a*v#!MXgEV>q|~lJAzAC!5rOW@BztWE>g#!lF9`(^e{FLu+}bA zsPv!xq)-`%`?myP|B@o-hs#02=T3E%X=+GXi}plX#XegPNeT_cau2l`tcT;%jW{w{ zj|>MB8ih`j$^4nst|oa%)=)u36FEK}+#0AKpwd&rXAJ-{A%BjI0_09>GS9eDTQ4nB0etIty`$x}Y}GOVrUYKr6y z%qsxMI7kQ1?SmFS*&N6;EUFtN@FS&T|9o~*4Y1*yLATA|rOYmnap4tOxECP#9vKI( zX9w|Gb`WWD_OSu9DWwnXI4Uh+;=Y1=zf=CV#8XH~!XsM)Z0NP9jd~PRGR=Bcuyo+O zS#td((pKc|u{AAW~3QHSFEq{?1@(yu=YTS7w*8R$B%lPm0zAiH>TKUfv0EC&6TX*-S047zup@Iy0 zkOPivF}Us0B$lOJJTlOW_icR?zZvR6Cfa?p*jUHs;>Blu&9>>r3>duJh@7xH6LuaElA3|U0>V?qGU5^r2)wh!({C;PEAHEXc=?;sXQ8S9xT3~gngZCce;2Xd7 zD!FG6DxyJz%ra@X__Z?=xbDobHB2cTIJKrfDc!_?&pqbh)|Y))3i|_;nr|R4kRnO# z7O!CFlrniusHIcLP3I8k4GD0I=|CbNs@coy4bx(_p5N|Z0*Ns4=BEK^B8>nmm z=z|fk#{<^185QeJm}Xbf4Pxes4I$-Pl6!ya1|RFXY}lcLLf5l^LEFmaW#@>^A^v#@ z?bas<$F&$-bF_m$Tjio&dJbeFWf1{Hic_T<$yc8Bu&kc6txN?lc>f1LzAE_CNiP27 z!j!gNi^XawG}_fG6$a}!bXz>K%_$m702@6tVom`hBjprZUXmH|UgYyqDSS``L zWYi@ruQ6rZmtbQT{1I?A^WF#5G7Sp=9*sfB`RT3N7M3-^mkK_uT%9hhdL3!81-jJdkM4 zH+hzUmR|hVJs$SiyZjipEJ+}Y9i6pa=s1eozcZlow!cBoVZX+Az`fJPi1i(4q)Po#+^cY7|+NDUP7|A)Z<;|ss zI=JF!2mT($l6u0=uB=QDRM4YL?C23+!3*6MSKph%ML*8shwFXyA_@>BHXvZId-n() z2N>h_4Ccs{n+&ffg}=YpFpOEm>1m_A3nI6IFjiXG z9^Y*6pBE-ajZleZF0QLg5X`A3upCk=KH9oX^7D^qGPrbwgNqM!)T_5p*ZZ+&DB1{c zykLNGCGUa^jEsa^$UeL^z&)D-Oj`^?fomVl;r5q(y^U-r85?#Y zIVAl{8d_d*sH4nptABHRBMxMB4>2zPX;w3&7o}&c>E-_63Z4wbe3NgdNR+ za}P4O_0kj?B?rK=b|=5>jOF^!5UX7v*OsN?LfOq!j78BQ1^NNZ5(o}_0OXVE@K^&x`dyE`HilBS zZ{BXBCOTiW#RQgQB9a86yYId-1c8?W5+YgR@TC@%MoA+jjLfEmeNF1iEI|g zQ{-@)8t&OPFtEGewTpd5piR+zssNZ{Ng%tFFQ^5+b#}5??}4aSCA}ePAmU|N5+LtN z{<_IO_WUJp20$RX-)4YwmK&V5#Ar^#1X4h(cQN7D72|Uu0ECZfohPM?D%?G{N^=bY z&2LDMc2dnw+;r23^>2X3qu+Tl0MMj@uKxaw4BkkgC0M~N59bdEz`%+|6DG8b`zefN z?x_v%RP;AHSpdka4~XgR2xwIRREl9Zu>lwuB2fVk{h_~qAmZVqBtV`Tzz-}-X=UMy zRcUrWAl%)$+fLMrC=~w`e7}@yl>z|M+26mO!H-ncz}$l%+?$y3)DGEHbDxT(KzL-? zZtUpZ{mZ;=JQH89gnKH;i1~)nAG1njfgn*D2;>|oV0mGO#S^-%?mn&PL^u#LZj6?H zMJ0AA0MH9{_6|H6F#pt$6lYc4!&9L*C7Rxy4Ly5Bs-QKK*bS3WuX`dFRy6H(3K6*h z+vmD_cik58wx}DwRIhT_Gr_Uf12m#xuISuhw$54 zn*P@iU1M1~=pt|X&MHmT%`3irYk@WG24^iZIK07VoxvM>ES}zB@zi#!wTCkEgK?N2 zSbn^#xBu;V1t2^V0MNpy_V)Ia^+p~xWX`C9V0tKC*Nb~s3RB}m4BfQaA=D?vs-W}8 z7roDxUuP$GR#(?hS1Fc%K20XsLlM^2R@X{F;#WlG*eVF7k48(@qTgc>jMFPAWIppw z!{*$M-riTr5&fh0o5V8;x}46=p{{@u7g*+J3{fJoIID)?6v<9t1nxzih`yr}SGRfGoOgLu#b->QsclPydoGkM(4f%ef z6{oE&i>@W`SBa>GL$|R|HG()jq(&X74@O(x-q6+4|HW7uPi4|gN5W^^b0cVNZ9IY4 z+(5u3@&SjtI>!kK)ePkFNPC@V)jN^eh?ebB0r0t=o?TDm^*fnTfNnZ7en*kmx~Snw zGR}2GI5YGZX(NSr_ul*sysCtKoH=nio{`P5!YXHe5kxn2cK6*JojavjpHI`xgm{)K zDwni0p2INL0DKV=J7CdkQm zMzpoH)v@oNW{A&X@W~850-!bm7Ungqo^KL?qyNRoA0S2#i2uN#7Yy*kh-aT37#P@< zcmA|89gEurZG^W#Na(r#xK5xl0UQm;x-faT|1LNrPc>GHC?3GKiHS1zS zF_v)ErQ-M$8_Tf)IOBS9uEcJBkHsos%rvSFMwO~mr7BgaN>!>-l`zxv{|kIEp}+XU QLI3~&07*qoM6N<$g8H&NVgLXD literal 6209 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 From fb8c02f741042df7332b853ac69b0776879c1955 Mon Sep 17 00:00:00 2001 From: Christian Findlay <16697547+MelbourneDeveloper@users.noreply.github.com> Date: Sun, 24 May 2026 16:21:03 +1000 Subject: [PATCH 09/17] icons --- docs/assets/diffy-icon-primary-1024.png | Bin 0 -> 558365 bytes docs/assets/diffy-icon-primary-128.png | Bin 0 -> 12969 bytes docs/assets/diffy-icon-primary-256.png | Bin 0 -> 48640 bytes docs/assets/diffy-icon-primary-512.png | Bin 0 -> 176558 bytes docs/assets/diffy-icon-primary.webp | Bin 0 -> 4714 bytes icon.png | Bin 7494 -> 12969 bytes 6 files changed, 0 insertions(+), 0 deletions(-) create mode 100644 docs/assets/diffy-icon-primary-1024.png create mode 100644 docs/assets/diffy-icon-primary-128.png create mode 100644 docs/assets/diffy-icon-primary-256.png create mode 100644 docs/assets/diffy-icon-primary-512.png create mode 100644 docs/assets/diffy-icon-primary.webp diff --git a/docs/assets/diffy-icon-primary-1024.png b/docs/assets/diffy-icon-primary-1024.png new file mode 100644 index 0000000000000000000000000000000000000000..f180ebfac6f95d290c5d05fb5d2b9f0ad4022c16 GIT binary patch literal 558365 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 z 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

SCrm5v{>%4GIq?3-A1K99*VDTx+ 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%0D3HCQai<9 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 z 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

AJozs&ya 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 z'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%0A%WR$@7!*4}H!$ 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

J!sV}=J!#Wt=1`{p9X+=oK0 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 A$^ZZW literal 0 HcmV?d00001 diff --git a/docs/assets/diffy-icon-primary-128.png b/docs/assets/diffy-icon-primary-128.png new file mode 100644 index 0000000000000000000000000000000000000000..6ed9db4147aeb3e8adb7b107443ea0e574b3f3f8 GIT binary patch literal 12969 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 literal 0 HcmV?d00001 diff --git a/docs/assets/diffy-icon-primary-256.png b/docs/assets/diffy-icon-primary-256.png new file mode 100644 index 0000000000000000000000000000000000000000..265ceb8df4428949a69a77568d16a6b96c4fd77c GIT binary patch literal 48640 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 literal 7494 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 QLI3~&07*qoM6N<$g8H&NVgLXD From 8bef18a074043a01354bf82819a645448edab028 Mon Sep 17 00:00:00 2001 From: Christian Findlay <16697547+MelbourneDeveloper@users.noreply.github.com> Date: Sun, 24 May 2026 16:28:33 +1000 Subject: [PATCH 10/17] docs: prune stale icon references in design-system The Logo Direction table linked to concept assets that were removed when the primary icon was finalized (contact-sheet, brackets, panes, delta, gutter). Replace with the actual shipping set, add the new 1024 px export, and note that the root icon.png matches the 128 px asset byte-for-byte. Co-Authored-By: Claude Opus 4.7 (1M context) --- docs/design-system.md | 23 ++++++++++------------- 1 file changed, 10 insertions(+), 13 deletions(-) diff --git a/docs/design-system.md b/docs/design-system.md index bbc0faa..a99612a 100644 --- a/docs/design-system.md +++ b/docs/design-system.md @@ -60,21 +60,18 @@ The Diffy mark should communicate comparison, pairing, and code review without l The current logo set is raster-only. There is no SVG source for these assets. Each icon is built from flat geometric shapes, hard color boundaries, and no text, so a future vector conversion can trace the shapes cleanly. +The whole set is rendered from the same primary mark — there are no alternate concepts in the current shipping set. Pick the size closest to the target rendering surface; the root `icon.png` is byte-identical to the 128 px export. + Current assets: -| Asset | Format | Use | -| ---------------------------------------------------------- | ------ | ---------------------------- | -| [Contact sheet](assets/diffy-icon-contact-sheet.png) | PNG | Compare all current concepts | -| [Contact sheet WebP](assets/diffy-icon-contact-sheet.webp) | WebP | Lightweight concept preview | -| [Primary 128](assets/diffy-icon-primary-128.png) | PNG | Root extension icon source | -| [Primary 256](assets/diffy-icon-primary-256.png) | PNG | Marketplace and docs | -| [Primary 512](assets/diffy-icon-primary-512.png) | PNG | High-resolution export | -| [Primary WebP](assets/diffy-icon-primary.webp) | WebP | Website asset | -| [Brackets WebP](assets/diffy-icon-brackets.webp) | WebP | Alternate concept | -| [Panes WebP](assets/diffy-icon-panes.webp) | WebP | Alternate concept | -| [Delta WebP](assets/diffy-icon-delta.webp) | WebP | Alternate concept | -| [Gutter WebP](assets/diffy-icon-gutter.webp) | WebP | Alternate concept | -| Root extension icon: `icon.png` | PNG | VS Code package icon | +| Asset | Format | Use | +| -------------------------------------------------- | ------ | ---------------------------- | +| [Primary 128](assets/diffy-icon-primary-128.png) | PNG | Root extension icon source | +| [Primary 256](assets/diffy-icon-primary-256.png) | PNG | Marketplace and docs | +| [Primary 512](assets/diffy-icon-primary-512.png) | PNG | High-resolution export | +| [Primary 1024](assets/diffy-icon-primary-1024.png) | PNG | Print, hero, and zoom assets | +| [Primary WebP](assets/diffy-icon-primary.webp) | WebP | Compressed website asset | +| Root extension icon: `icon.png` | PNG | VS Code package icon | Preferred concepts: From 32994714b7049c5202b7ecf55520a0ee1f2ec9b5 Mon Sep 17 00:00:00 2001 From: Christian Findlay <16697547+MelbourneDeveloper@users.noreply.github.com> Date: Tue, 26 May 2026 18:53:35 +1000 Subject: [PATCH 11/17] ci: fix formatting, lint, and coverage threshold - Run prettier across all source and test files - Remove unnecessary string concat in parsers.test.ts - Cover expectOk/expectErr negative paths and parseIndexUri bad encoding to push line coverage from 94.8% to 95.06% (threshold: 95%) --- AGENTS.md | 1 + CLAUDE.md | 12 +- README.md | 32 +-- shipwright.json | 9 +- src/commands/compareFileWithCommit.ts | 4 +- src/commands/compareFileWithRev.ts | 49 ++--- src/commands/compareTwoCommits.ts | 16 +- src/commands/compareWith.ts | 37 ++-- src/commands/compareWithPrevious.ts | 39 ++-- src/commands/compareWithRef.ts | 34 ++- src/commands/compareWithWorkingCopy.ts | 33 ++- src/commands/flow.ts | 70 +++--- src/commands/historyItem.ts | 11 +- src/commands/reopenLast.ts | 23 +- src/commands/shared.ts | 68 ++---- src/constants.ts | 202 ++++++++--------- src/extension.ts | 121 ++++------ src/git/GitRepo.ts | 62 +++--- src/git/GitRunner.ts | 53 ++--- src/git/parsers.ts | 155 +++++++------ src/git/repoMatch.ts | 7 +- src/git/types.ts | 21 +- src/logger.ts | 10 +- src/menus.ts | 39 +--- src/providers/DiffyContentProvider.ts | 23 +- src/result.ts | 21 +- src/state.ts | 16 +- src/test/runTests.ts | 41 ++-- src/test/suite/activation.test.ts | 30 ++- src/test/suite/commands.test.ts | 154 ++++++------- src/test/suite/contentProvider.test.ts | 34 ++- src/test/suite/helpers.ts | 62 +++--- src/test/suite/index.ts | 12 +- src/test/unit/format.test.ts | 166 +++++++------- src/test/unit/historyItem.test.ts | 36 +-- src/test/unit/menus.test.ts | 100 ++++----- src/test/unit/parsers.test.ts | 292 +++++++++++-------------- src/test/unit/refFilter.test.ts | 60 +++-- src/test/unit/repoMatch.test.ts | 77 +++---- src/test/unit/result.test.ts | 117 +++++----- src/test/unit/state.test.ts | 54 ++--- src/test/unit/uri.test.ts | 120 +++++----- src/ui/CommitPicker.ts | 14 +- src/ui/FilePicker.ts | 27 ++- src/ui/RefPicker.ts | 16 +- src/ui/SideBPicker.ts | 10 +- src/ui/format/fileItem.ts | 6 +- src/ui/format/refFilter.ts | 14 +- src/ui/format/refLabel.ts | 4 +- src/ui/format/relativeTime.ts | 2 +- src/ui/runQuickPick.ts | 14 +- src/ui/uri.ts | 38 ++-- src/vscodeGitApi.ts | 10 +- 53 files changed, 1188 insertions(+), 1490 deletions(-) diff --git a/AGENTS.md b/AGENTS.md index 48aaceb..34b118b 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -1,2 +1,3 @@ + @CLAUDE.md diff --git a/CLAUDE.md b/CLAUDE.md index a95d44e..4ea4630 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -19,7 +19,7 @@ Full design + execution plan: [spec.md](spec.md). **Diffy** is a VSCode extension that does exactly one thing: **pick two things and diff them** against a git repository. Side A is a commit; Side B is another commit, the working copy, the index, or a branch/tag (resolved to a commit). It shells out to `git`, hands two URIs to VSCode's built-in `vscode.diff`, and uses a multi-step QuickPick for browsing many changed files. No custom renderer, no custom view. -**Primary language:** TypeScript (pure — Rust LSP was considered and rejected; LSP is for *language* semantics, not diffing) +**Primary language:** TypeScript (pure — Rust LSP was considered and rejected; LSP is for _language_ semantics, not diffing) **Build command:** `make ci` **Test command:** `make test` **Lint command:** `make lint` @@ -45,7 +45,7 @@ context-menu / palette command ## Hard Rules (no exceptions, NON-NEGOTIABLE) -- **NO git commands from the agent.** No `git add`, `commit`, `push`, `checkout`, `merge`, `rebase`. CI and GitHub Actions handle git. (Diffy itself shells out to `git` at runtime — that's the product. The *agent* doesn't drive git in the dev loop.) +- **NO git commands from the agent.** No `git add`, `commit`, `push`, `checkout`, `merge`, `rebase`. CI and GitHub Actions handle git. (Diffy itself shells out to `git` at runtime — that's the product. The _agent_ doesn't drive git in the dev loop.) - **NO new views, sidebars, activity-bar icons, tree providers, or webviews.** Context menus + palette commands only. Browsing many files is a QuickPick, not a panel. - **NO THROWING EXCEPTIONS for control flow.** Return `Result` via a discriminated union. Panics are bugs. - **NO REGEX on structured data.** Git porcelain output is parsed via NUL-delimited splits (`-z` flag everywhere). Never regex over JSON, YAML, source code, or git output. @@ -102,15 +102,17 @@ context-menu / palette command Do not write assertions that guard against AI / taxonomy strings. Assert on **positive, human-readable values** that the user would actually see. ⛔️ BAD + ```typescript assert.doesNotMatch(label, /\[diffy-internal-tag\]/); ``` ✅ GOOD + ```typescript -const tabs = vscode.window.tabGroups.all.flatMap(g => g.tabs); -const diffTab = tabs.find(t => t.input instanceof vscode.TabInputTextDiff); -assert.ok(diffTab, 'a diff tab opened'); +const tabs = vscode.window.tabGroups.all.flatMap((g) => g.tabs); +const diffTab = tabs.find((t) => t.input instanceof vscode.TabInputTextDiff); +assert.ok(diffTab, "a diff tab opened"); assert.match(diffTab.label, /a1b2c3 ↔ Working Copy/); ``` diff --git a/README.md b/README.md index eb89023..80ca8c0 100644 --- a/README.md +++ b/README.md @@ -14,11 +14,11 @@ There are **seven** commands. Three are reachable from the Command Palette, four Open the **Source Control** view (Ctrl/Cmd+Shift+G), expand a repository's **History** section, and **right-click any commit**. Three Diffy entries appear: -| Menu label | Command ID | What it does | -| --- | --- | --- | -| **Diffy: Compare with…** | `diffy.compareWith` | The right-clicked commit is Side A. A QuickPick asks what Side B is: **Working Copy**, **Index**, **Pick a commit…**, or **Pick a branch or tag…**. Then a file QuickPick lists every changed file; selecting one opens `vscode.diff`. The picker stays open so you can open many files in a row. | -| **Diffy: Compare with Working Copy** | `diffy.compareWithWorkingCopy` | Same as above, but Side B is hardcoded to your on-disk working copy. Skips the Side B prompt. | -| **Diffy: Compare with Previous** | `diffy.compareWithPrevious` | Side A is the right-clicked commit, Side B is its first parent (`^1`). Use this for a classic "what changed in this commit?" view. | +| Menu label | Command ID | What it does | +| ------------------------------------ | ------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| **Diffy: Compare with…** | `diffy.compareWith` | The right-clicked commit is Side A. A QuickPick asks what Side B is: **Working Copy**, **Index**, **Pick a commit…**, or **Pick a branch or tag…**. Then a file QuickPick lists every changed file; selecting one opens `vscode.diff`. The picker stays open so you can open many files in a row. | +| **Diffy: Compare with Working Copy** | `diffy.compareWithWorkingCopy` | Same as above, but Side B is hardcoded to your on-disk working copy. Skips the Side B prompt. | +| **Diffy: Compare with Previous** | `diffy.compareWithPrevious` | Side A is the right-clicked commit, Side B is its first parent (`^1`). Use this for a classic "what changed in this commit?" view. | > These three are intentionally **hidden from the Command Palette** — they only make sense when invoked against a specific commit, which the right-click target provides. @@ -26,35 +26,35 @@ Open the **Source Control** view (Ctrl/Cmd+Shift+G), expand a repository's **His In the Source Control view, **right-click any file** under **Changes**, **Staged Changes**, or **Merge Changes**: -| Menu label | Command ID | What it does | -| --- | --- | --- | +| Menu label | Command ID | What it does | +| ------------------------------- | ----------------------------- | ----------------------------------------------------------------------------------------------------------------- | | **Diffy: Compare with Commit…** | `diffy.compareFileWithCommit` | Pick a commit from a log QuickPick. Opens a single-file diff: that file at the chosen commit ↔ your working copy. | ### From the **editor tab title** (right-click the file's tab) **Right-click the tab** of any open text file: -| Menu label | Command ID | What it does | -| --- | --- | --- | +| Menu label | Command ID | What it does | +| ------------------------------- | ----------------------------- | -------------------------------------------------------------------------- | | **Diffy: Compare with Commit…** | `diffy.compareFileWithCommit` | Same as above, but the target file is the one whose tab you right-clicked. | ### From the **File Explorer** (right-click a file in the tree) **Right-click any file** (not a folder) in the Explorer: -| Menu label | Command ID | What it does | -| --- | --- | --- | +| Menu label | Command ID | What it does | +| ------------------------------- | ----------------------------- | ------------------------------------------------------------ | | **Diffy: Compare with Commit…** | `diffy.compareFileWithCommit` | Same again — the target is the file you clicked in the tree. | ### From the **Command Palette** (Ctrl/Cmd+Shift+P) Type `Diffy:` to filter. Three entries are listed: -| Palette label | Command ID | What it does | -| --- | --- | --- | -| **Diffy: Compare Two Commits** | `diffy.compareTwoCommits` | No target needed. QuickPick chain: pick repo → pick Side A commit → pick Side B (working copy / index / commit / ref) → pick files. | -| **Diffy: Compare with Commit…** | `diffy.compareFileWithCommit` | Uses the **currently focused editor's file** as the target. Same flow as the right-click version. If no editor is focused, Diffy will tell you to open a file first. | -| **Diffy: Reopen Last Comparison** | `diffy.reopenLast` | Reopens the file picker for the last A↔B comparison you made (stored per-workspace). Handy after closing the picker mid-review. | +| Palette label | Command ID | What it does | +| --------------------------------- | ----------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| **Diffy: Compare Two Commits** | `diffy.compareTwoCommits` | No target needed. QuickPick chain: pick repo → pick Side A commit → pick Side B (working copy / index / commit / ref) → pick files. | +| **Diffy: Compare with Commit…** | `diffy.compareFileWithCommit` | Uses the **currently focused editor's file** as the target. Same flow as the right-click version. If no editor is focused, Diffy will tell you to open a file first. | +| **Diffy: Reopen Last Comparison** | `diffy.reopenLast` | Reopens the file picker for the last A↔B comparison you made (stored per-workspace). Handy after closing the picker mid-review. | > **`Diffy: Show Logs`** (`diffy.showLogs`) exists but is hidden from the palette; it's reserved for the extension to surface its OutputChannel programmatically. Open it manually via **View → Output → "Diffy"**. diff --git a/shipwright.json b/shipwright.json index 630cc62..f570463 100644 --- a/shipwright.json +++ b/shipwright.json @@ -13,14 +13,7 @@ "kind": "extension-vscode", "language": "typescript", "expectedVersion": "${PRODUCT_VERSION}", - "platforms": [ - "darwin-arm64", - "darwin-x64", - "linux-arm64", - "linux-x64", - "win32-arm64", - "win32-x64" - ] + "platforms": ["darwin-arm64", "darwin-x64", "linux-arm64", "linux-x64", "win32-arm64", "win32-x64"] } ], "hosts": { diff --git a/src/commands/compareFileWithCommit.ts b/src/commands/compareFileWithCommit.ts index 9e0109b..736bb54 100644 --- a/src/commands/compareFileWithCommit.ts +++ b/src/commands/compareFileWithCommit.ts @@ -1,5 +1,5 @@ -import type { CommandDeps } from './shared'; -import { FILE_REV_SOURCES, makeCompareFileWithRev } from './compareFileWithRev'; +import type { CommandDeps } from "./shared"; +import { FILE_REV_SOURCES, makeCompareFileWithRev } from "./compareFileWithRev"; export const makeCompareFileWithCommit = (deps: CommandDeps) => makeCompareFileWithRev({ deps, source: FILE_REV_SOURCES.commits }); diff --git a/src/commands/compareFileWithRev.ts b/src/commands/compareFileWithRev.ts index 9830e8b..78e759b 100644 --- a/src/commands/compareFileWithRev.ts +++ b/src/commands/compareFileWithRev.ts @@ -1,31 +1,27 @@ -import * as vscode from 'vscode'; -import { REV_KINDS, TITLE_PREFIX } from '../constants'; -import type { GitRepo } from '../git/GitRepo'; -import type { Sha } from '../git/types'; -import type { Result } from '../result'; -import { err, ok } from '../result'; -import { CANCELLED, type Cancelled } from '../ui/cancelled'; -import { pickCommit } from '../ui/CommitPicker'; -import { findRepoForUri } from '../vscodeGitApi'; -import { - type CommandDeps, - buildRepo, - openDiff, -} from './shared'; -import { pickRefAsSha, reportGitError, sideAFromSha } from './flow'; +import * as vscode from "vscode"; +import { REV_KINDS, TITLE_PREFIX } from "../constants"; +import type { GitRepo } from "../git/GitRepo"; +import type { Sha } from "../git/types"; +import type { Result } from "../result"; +import { err, ok } from "../result"; +import { CANCELLED, type Cancelled } from "../ui/cancelled"; +import { pickCommit } from "../ui/CommitPicker"; +import { findRepoForUri } from "../vscodeGitApi"; +import { type CommandDeps, buildRepo, openDiff } from "./shared"; +import { pickRefAsSha, reportGitError, sideAFromSha } from "./flow"; export const FILE_REV_SOURCES = { - commits: 'commits', - branch: 'branch', - tag: 'tag', - other: 'other', + commits: "commits", + branch: "branch", + tag: "tag", + other: "other", } as const; export type FileRevSource = (typeof FILE_REV_SOURCES)[keyof typeof FILE_REV_SOURCES]; const NO_EDITOR = `${TITLE_PREFIX} open a file first.`; const NOT_IN_REPO = `${TITLE_PREFIX} file is not in a git repository.`; -const LOG_OP = 'log'; +const LOG_OP = "log"; const pickShaForFile = async ({ repo, @@ -86,11 +82,8 @@ const handler = async ({ }); }; -export const makeCompareFileWithRev = ({ - deps, - source, -}: { - deps: CommandDeps; - source: FileRevSource; -}) => - async (uri?: vscode.Uri): Promise => { await handler({ deps, uri, source }); }; +export const makeCompareFileWithRev = + ({ deps, source }: { deps: CommandDeps; source: FileRevSource }) => + async (uri?: vscode.Uri): Promise => { + await handler({ deps, uri, source }); + }; diff --git a/src/commands/compareTwoCommits.ts b/src/commands/compareTwoCommits.ts index b00d2e8..25bf43f 100644 --- a/src/commands/compareTwoCommits.ts +++ b/src/commands/compareTwoCommits.ts @@ -1,15 +1,9 @@ -import type { MementoStore } from '../state'; -import type { CommandDeps } from './shared'; -import { - drillIntoFiles, - pickRepoAndCommit, - pickSideBAndResolve, -} from './flow'; +import type { MementoStore } from "../state"; +import type { CommandDeps } from "./shared"; +import { drillIntoFiles, pickRepoAndCommit, pickSideBAndResolve } from "./flow"; -export const makeCompareTwoCommits = ( - deps: CommandDeps & { readonly state: MementoStore }, -) => - async (): Promise => { +export const makeCompareTwoCommits = + (deps: CommandDeps & { readonly state: MementoStore }) => async (): Promise => { const start = await pickRepoAndCommit({ runner: deps.runner, gitApi: deps.gitApi, diff --git a/src/commands/compareWith.ts b/src/commands/compareWith.ts index 8e9dd82..bc52649 100644 --- a/src/commands/compareWith.ts +++ b/src/commands/compareWith.ts @@ -1,25 +1,13 @@ -import * as vscode from 'vscode'; -import { TITLE_PREFIX } from '../constants'; -import type { MementoStore } from '../state'; -import { extractHistoryItemSha } from './historyItem'; -import { - type CommandDeps, - buildRepo, - pickRepoFrom, -} from './shared'; -import { - drillIntoFiles, - pickSideBAndResolve, - sideAFromSha, -} from './flow'; +import * as vscode from "vscode"; +import { TITLE_PREFIX } from "../constants"; +import type { MementoStore } from "../state"; +import { extractHistoryItemSha } from "./historyItem"; +import { type CommandDeps, buildRepo, pickRepoFrom } from "./shared"; +import { drillIntoFiles, pickSideBAndResolve, sideAFromSha } from "./flow"; -const NOT_FROM_HISTORY = - `${TITLE_PREFIX} this command must be invoked from the SCM history view.`; +const NOT_FROM_HISTORY = `${TITLE_PREFIX} this command must be invoked from the SCM history view.`; -const handler = async ( - deps: CommandDeps & { readonly state: MementoStore }, - arg: unknown, -): Promise => { +const handler = async (deps: CommandDeps & { readonly state: MementoStore }, arg: unknown): Promise => { const sha = extractHistoryItemSha(arg); if (sha === undefined) { void vscode.window.showWarningMessage(NOT_FROM_HISTORY); @@ -44,7 +32,8 @@ const handler = async ( }); }; -export const makeCompareWith = ( - deps: CommandDeps & { readonly state: MementoStore }, -) => - async (arg: unknown): Promise => { await handler(deps, arg); }; +export const makeCompareWith = + (deps: CommandDeps & { readonly state: MementoStore }) => + async (arg: unknown): Promise => { + await handler(deps, arg); + }; diff --git a/src/commands/compareWithPrevious.ts b/src/commands/compareWithPrevious.ts index ba5f608..4b5eb65 100644 --- a/src/commands/compareWithPrevious.ts +++ b/src/commands/compareWithPrevious.ts @@ -1,26 +1,14 @@ -import * as vscode from 'vscode'; -import { REV_KINDS, TITLE_PREFIX } from '../constants'; -import type { MementoStore } from '../state'; -import { extractHistoryItemSha } from './historyItem'; -import { - type CommandDeps, - buildRepo, - pickRepoFrom, -} from './shared'; -import { - drillIntoFiles, - reportGitError, - sideAFromSha, -} from './flow'; +import * as vscode from "vscode"; +import { REV_KINDS, TITLE_PREFIX } from "../constants"; +import type { MementoStore } from "../state"; +import { extractHistoryItemSha } from "./historyItem"; +import { type CommandDeps, buildRepo, pickRepoFrom } from "./shared"; +import { drillIntoFiles, reportGitError, sideAFromSha } from "./flow"; -const REV_PARSE_PARENT_OP = 'rev-parse parent'; -const HISTORY_VIEW_WARNING = - `${TITLE_PREFIX} this command must be invoked from the SCM history view.`; +const REV_PARSE_PARENT_OP = "rev-parse parent"; +const HISTORY_VIEW_WARNING = `${TITLE_PREFIX} this command must be invoked from the SCM history view.`; -const handler = async ( - deps: CommandDeps & { readonly state: MementoStore }, - arg: unknown, -): Promise => { +const handler = async (deps: CommandDeps & { readonly state: MementoStore }, arg: unknown): Promise => { const sha = extractHistoryItemSha(arg); if (sha === undefined) { void vscode.window.showWarningMessage(HISTORY_VIEW_WARNING); @@ -50,7 +38,8 @@ const handler = async ( }); }; -export const makeCompareWithPrevious = ( - deps: CommandDeps & { readonly state: MementoStore }, -) => - async (arg: unknown): Promise => { await handler(deps, arg); }; +export const makeCompareWithPrevious = + (deps: CommandDeps & { readonly state: MementoStore }) => + async (arg: unknown): Promise => { + await handler(deps, arg); + }; diff --git a/src/commands/compareWithRef.ts b/src/commands/compareWithRef.ts index b04e649..4ed15c2 100644 --- a/src/commands/compareWithRef.ts +++ b/src/commands/compareWithRef.ts @@ -1,17 +1,12 @@ -import * as vscode from 'vscode'; -import { REV_KINDS, TITLE_PREFIX } from '../constants'; -import type { RefType } from '../git/types'; -import type { MementoStore } from '../state'; -import { extractHistoryItemSha } from './historyItem'; -import { - type CommandDeps, - buildRepo, - pickRepoFrom, -} from './shared'; -import { drillIntoFiles, pickRefAsSha, sideAFromSha } from './flow'; +import * as vscode from "vscode"; +import { REV_KINDS, TITLE_PREFIX } from "../constants"; +import type { RefType } from "../git/types"; +import type { MementoStore } from "../state"; +import { extractHistoryItemSha } from "./historyItem"; +import { type CommandDeps, buildRepo, pickRepoFrom } from "./shared"; +import { drillIntoFiles, pickRefAsSha, sideAFromSha } from "./flow"; -const NOT_FROM_HISTORY = - `${TITLE_PREFIX} this command must be invoked from the SCM history view.`; +const NOT_FROM_HISTORY = `${TITLE_PREFIX} this command must be invoked from the SCM history view.`; const handler = async ({ deps, @@ -46,11 +41,8 @@ const handler = async ({ }); }; -export const makeCompareWithRef = ({ - deps, - filter, -}: { - deps: CommandDeps & { readonly state: MementoStore }; - filter: RefType; -}) => - async (arg: unknown): Promise => { await handler({ deps, arg, filter }); }; +export const makeCompareWithRef = + ({ deps, filter }: { deps: CommandDeps & { readonly state: MementoStore }; filter: RefType }) => + async (arg: unknown): Promise => { + await handler({ deps, arg, filter }); + }; diff --git a/src/commands/compareWithWorkingCopy.ts b/src/commands/compareWithWorkingCopy.ts index b0edd89..b55c9b9 100644 --- a/src/commands/compareWithWorkingCopy.ts +++ b/src/commands/compareWithWorkingCopy.ts @@ -1,21 +1,13 @@ -import * as vscode from 'vscode'; -import { REV_KINDS, TITLE_PREFIX } from '../constants'; -import type { MementoStore } from '../state'; -import { extractHistoryItemSha } from './historyItem'; -import { - type CommandDeps, - buildRepo, - pickRepoFrom, -} from './shared'; -import { drillIntoFiles, sideAFromSha } from './flow'; +import * as vscode from "vscode"; +import { REV_KINDS, TITLE_PREFIX } from "../constants"; +import type { MementoStore } from "../state"; +import { extractHistoryItemSha } from "./historyItem"; +import { type CommandDeps, buildRepo, pickRepoFrom } from "./shared"; +import { drillIntoFiles, sideAFromSha } from "./flow"; -const NOT_FROM_HISTORY = - `${TITLE_PREFIX} this command must be invoked from the SCM history view.`; +const NOT_FROM_HISTORY = `${TITLE_PREFIX} this command must be invoked from the SCM history view.`; -const handler = async ( - deps: CommandDeps & { readonly state: MementoStore }, - arg: unknown, -): Promise => { +const handler = async (deps: CommandDeps & { readonly state: MementoStore }, arg: unknown): Promise => { const sha = extractHistoryItemSha(arg); if (sha === undefined) { void vscode.window.showWarningMessage(NOT_FROM_HISTORY); @@ -36,7 +28,8 @@ const handler = async ( }); }; -export const makeCompareWithWorkingCopy = ( - deps: CommandDeps & { readonly state: MementoStore }, -) => - async (arg: unknown): Promise => { await handler(deps, arg); }; +export const makeCompareWithWorkingCopy = + (deps: CommandDeps & { readonly state: MementoStore }) => + async (arg: unknown): Promise => { + await handler(deps, arg); + }; diff --git a/src/commands/flow.ts b/src/commands/flow.ts index 583e360..bd25a61 100644 --- a/src/commands/flow.ts +++ b/src/commands/flow.ts @@ -1,52 +1,35 @@ -import * as vscode from 'vscode'; -import { - LOG_EVENTS, - REF_TYPES, - REV_KINDS, - SIDE_B_KINDS, - TITLE_PREFIX, - UI_TEXT, -} from '../constants'; -import type { GitRepo } from '../git/GitRepo'; -import type { GitRunner } from '../git/GitRunner'; -import type { GitApi } from '../vscodeGitApi'; -import type { GitError, CommitRev, RefType, RevSpec, Sha } from '../git/types'; -import { logger } from '../logger'; -import { type Result, err, ok } from '../result'; -import { CANCELLED, type Cancelled } from '../ui/cancelled'; -import { pickCommit } from '../ui/CommitPicker'; -import { pickRef } from '../ui/RefPicker'; -import { pickSideBChoice, type SideBChoice } from '../ui/SideBPicker'; -import { mergeChangedFilesWithStats, pickFiles } from '../ui/FilePicker'; -import type { MementoStore } from '../state'; -import { buildRepo, openDiff, pickRepoFrom } from './shared'; +import * as vscode from "vscode"; +import { LOG_EVENTS, REF_TYPES, REV_KINDS, SIDE_B_KINDS, TITLE_PREFIX, UI_TEXT } from "../constants"; +import type { GitRepo } from "../git/GitRepo"; +import type { GitRunner } from "../git/GitRunner"; +import type { GitApi } from "../vscodeGitApi"; +import type { GitError, CommitRev, RefType, RevSpec, Sha } from "../git/types"; +import { logger } from "../logger"; +import { type Result, err, ok } from "../result"; +import { CANCELLED, type Cancelled } from "../ui/cancelled"; +import { pickCommit } from "../ui/CommitPicker"; +import { pickRef } from "../ui/RefPicker"; +import { pickSideBChoice, type SideBChoice } from "../ui/SideBPicker"; +import { mergeChangedFilesWithStats, pickFiles } from "../ui/FilePicker"; +import type { MementoStore } from "../state"; +import { buildRepo, openDiff, pickRepoFrom } from "./shared"; const GIT_OPS = { - listRefs: 'list refs', - revParse: 'rev-parse', - log: 'log', - diffNameStatus: 'diff --name-status', - diffNumstat: 'diff --numstat', - currentBranch: 'current branch', + listRefs: "list refs", + revParse: "rev-parse", + log: "log", + diffNameStatus: "diff --name-status", + diffNumstat: "diff --numstat", + currentBranch: "current branch", } as const; -export const reportGitError = ({ - output, - op, - e, -}: { - output: vscode.OutputChannel; - op: string; - e: GitError; -}): void => { +export const reportGitError = ({ output, op, e }: { output: vscode.OutputChannel; op: string; e: GitError }): void => { logger.error({ op, kind: e.kind }, LOG_EVENTS.gitError); output.appendLine(`${TITLE_PREFIX} ${op} failed ${UI_TEXT.pathDash} ${e.message}`); - if (e.stderr !== undefined && e.stderr !== '') { + if (e.stderr !== undefined && e.stderr !== "") { output.appendLine(e.stderr); } - void vscode.window.showErrorMessage( - `${TITLE_PREFIX} ${op} failed (see Output → Diffy).`, - ); + void vscode.window.showErrorMessage(`${TITLE_PREFIX} ${op} failed (see Output → Diffy).`); }; export const resolveSideB = async ({ @@ -202,8 +185,9 @@ export const drillIntoFiles = async ({ await state.setLastComparison({ revA, revB, repoRoot }); await pickFiles({ entries, - onPick: async (entry) => - { await openDiff({ revA, revB, repoRoot, relPath: entry.file.path }); }, + onPick: async (entry) => { + await openDiff({ revA, revB, repoRoot, relPath: entry.file.path }); + }, }); }; diff --git a/src/commands/historyItem.ts b/src/commands/historyItem.ts index f6601d7..1d27302 100644 --- a/src/commands/historyItem.ts +++ b/src/commands/historyItem.ts @@ -1,8 +1,7 @@ -const isNonEmptyString = (v: unknown): v is string => - typeof v === 'string' && v !== ''; +const isNonEmptyString = (v: unknown): v is string => typeof v === "string" && v !== ""; const stringIdOf = (obj: object): string | undefined => { - if (!('id' in obj)) { + if (!("id" in obj)) { return undefined; } return isNonEmptyString(obj.id) ? obj.id : undefined; @@ -12,18 +11,18 @@ export const extractHistoryItemSha = (arg: unknown): string | undefined => { if (isNonEmptyString(arg)) { return arg; } - if (typeof arg !== 'object' || arg === null) { + if (typeof arg !== "object" || arg === null) { return undefined; } const direct = stringIdOf(arg); if (direct !== undefined) { return direct; } - if (!('historyItem' in arg)) { + if (!("historyItem" in arg)) { return undefined; } const inner = arg.historyItem; - if (typeof inner !== 'object' || inner === null) { + if (typeof inner !== "object" || inner === null) { return undefined; } return stringIdOf(inner); diff --git a/src/commands/reopenLast.ts b/src/commands/reopenLast.ts index c9411d2..8ea43ee 100644 --- a/src/commands/reopenLast.ts +++ b/src/commands/reopenLast.ts @@ -1,16 +1,14 @@ -import * as vscode from 'vscode'; -import { TITLE_PREFIX } from '../constants'; -import { findRepoForUri } from '../vscodeGitApi'; -import type { MementoStore } from '../state'; -import { type CommandDeps, buildRepo } from './shared'; -import { drillIntoFiles } from './flow'; +import * as vscode from "vscode"; +import { TITLE_PREFIX } from "../constants"; +import { findRepoForUri } from "../vscodeGitApi"; +import type { MementoStore } from "../state"; +import { type CommandDeps, buildRepo } from "./shared"; +import { drillIntoFiles } from "./flow"; const NO_PREVIOUS = `${TITLE_PREFIX} no previous comparison to reopen.`; const REPO_GONE = `${TITLE_PREFIX} previous repository is no longer open.`; -const handler = async ( - deps: CommandDeps & { readonly state: MementoStore }, -): Promise => { +const handler = async (deps: CommandDeps & { readonly state: MementoStore }): Promise => { const last = deps.state.getLastComparison(); if (last === undefined) { void vscode.window.showInformationMessage(NO_PREVIOUS); @@ -32,7 +30,6 @@ const handler = async ( }); }; -export const makeReopenLast = ( - deps: CommandDeps & { readonly state: MementoStore }, -) => - async (): Promise => { await handler(deps); }; +export const makeReopenLast = (deps: CommandDeps & { readonly state: MementoStore }) => async (): Promise => { + await handler(deps); +}; diff --git a/src/commands/shared.ts b/src/commands/shared.ts index 9ec1a8e..df6694c 100644 --- a/src/commands/shared.ts +++ b/src/commands/shared.ts @@ -1,25 +1,15 @@ -import * as path from 'node:path'; -import * as vscode from 'vscode'; -import { - BUILT_IN_COMMANDS, - LOG_EVENTS, - REV_KINDS, - SHORT_SHA_LEN, - UI_TEXT, -} from '../constants'; -import type { GitRepo } from '../git/GitRepo'; -import type { GitRunner } from '../git/GitRunner'; -import { createGitRepo } from '../git/GitRepo'; -import { - type GitApi, - type GitVsRepository, - findRepoForUri, -} from '../vscodeGitApi'; -import type { CommitRev, DiffyAddressableRev, RevSpec, Sha } from '../git/types'; -import { logger } from '../logger'; -import { type Result, err, ok } from '../result'; -import { CANCELLED, type Cancelled } from '../ui/cancelled'; -import { buildDiffyUri } from '../ui/uri'; +import * as path from "node:path"; +import * as vscode from "vscode"; +import { BUILT_IN_COMMANDS, LOG_EVENTS, REV_KINDS, SHORT_SHA_LEN, UI_TEXT } from "../constants"; +import type { GitRepo } from "../git/GitRepo"; +import type { GitRunner } from "../git/GitRunner"; +import { createGitRepo } from "../git/GitRepo"; +import { type GitApi, type GitVsRepository, findRepoForUri } from "../vscodeGitApi"; +import type { CommitRev, DiffyAddressableRev, RevSpec, Sha } from "../git/types"; +import { logger } from "../logger"; +import { type Result, err, ok } from "../result"; +import { CANCELLED, type Cancelled } from "../ui/cancelled"; +import { buildDiffyUri } from "../ui/uri"; export interface CommandDeps { readonly runner: GitRunner; @@ -47,8 +37,7 @@ export const formatDiffTitle = ({ revA: CommitRev; revB: RevSpec; basename: string; -}): string => - `${labelForRev(revA)} ${UI_TEXT.pathArrow} ${labelForRev(revB)} ${UI_TEXT.pathDash} ${basename}`; +}): string => `${labelForRev(revA)} ${UI_TEXT.pathArrow} ${labelForRev(revB)} ${UI_TEXT.pathDash} ${basename}`; export const uriForRev = ({ rev, @@ -63,9 +52,7 @@ export const uriForRev = ({ return vscode.Uri.file(path.join(repoRoot, relPath)); } const addressable: DiffyAddressableRev = - rev.kind === REV_KINDS.commit - ? { kind: REV_KINDS.commit, sha: rev.sha } - : { kind: REV_KINDS.index }; + rev.kind === REV_KINDS.commit ? { kind: REV_KINDS.commit, sha: rev.sha } : { kind: REV_KINDS.index }; return vscode.Uri.parse(buildDiffyUri(addressable, relPath)); }; @@ -87,26 +74,13 @@ export const openDiff = async ({ revB, basename: path.basename(relPath), }); - logger.info( - { shaA: shortSha(revA.sha), revBKind: revB.kind }, - LOG_EVENTS.diffOpen, - ); - await vscode.commands.executeCommand( - BUILT_IN_COMMANDS.diff, - left, - right, - title, - ); + logger.info({ shaA: shortSha(revA.sha), revBKind: revB.kind }, LOG_EVENTS.diffOpen); + await vscode.commands.executeCommand(BUILT_IN_COMMANDS.diff, left, right, title); }; -export const repoForUri = ( - api: GitApi, - uri: vscode.Uri, -): GitVsRepository | undefined => findRepoForUri(api, uri); +export const repoForUri = (api: GitApi, uri: vscode.Uri): GitVsRepository | undefined => findRepoForUri(api, uri); -export const pickRepoFrom = async ( - api: GitApi, -): Promise> => { +export const pickRepoFrom = async (api: GitApi): Promise> => { if (api.repositories.length === 0) { return err(CANCELLED); } @@ -124,7 +98,5 @@ export const pickRepoFrom = async ( return picked === undefined ? err(CANCELLED) : ok(picked.repo); }; -export const buildRepo = ( - runner: GitRunner, - vsRepo: GitVsRepository, -): GitRepo => createGitRepo({ runner, cwd: vsRepo.rootUri.fsPath }); +export const buildRepo = (runner: GitRunner, vsRepo: GitVsRepository): GitRepo => + createGitRepo({ runner, cwd: vsRepo.rootUri.fsPath }); diff --git a/src/constants.ts b/src/constants.ts index 2bb8469..3a662ca 100644 --- a/src/constants.ts +++ b/src/constants.ts @@ -1,166 +1,166 @@ -export const SCHEME = 'diffy'; +export const SCHEME = "diffy"; -export const OUTPUT_CHANNEL_NAME = 'Diffy'; +export const OUTPUT_CHANNEL_NAME = "Diffy"; export const COMMAND_IDS = { - compareWith: 'diffy.compareWith', - compareWithWorkingCopy: 'diffy.compareWithWorkingCopy', - compareWithPrevious: 'diffy.compareWithPrevious', - compareWithBranch: 'diffy.compareWithBranch', - compareWithTag: 'diffy.compareWithTag', - compareTwoCommits: 'diffy.compareTwoCommits', - compareFileWithCommit: 'diffy.compareFileWithCommit', - compareFileWithBranch: 'diffy.compareFileWithBranch', - compareFileWithTag: 'diffy.compareFileWithTag', - reopenLast: 'diffy.reopenLast', - showLogs: 'diffy.showLogs', + compareWith: "diffy.compareWith", + compareWithWorkingCopy: "diffy.compareWithWorkingCopy", + compareWithPrevious: "diffy.compareWithPrevious", + compareWithBranch: "diffy.compareWithBranch", + compareWithTag: "diffy.compareWithTag", + compareTwoCommits: "diffy.compareTwoCommits", + compareFileWithCommit: "diffy.compareFileWithCommit", + compareFileWithBranch: "diffy.compareFileWithBranch", + compareFileWithTag: "diffy.compareFileWithTag", + reopenLast: "diffy.reopenLast", + showLogs: "diffy.showLogs", } as const; export const BUILT_IN_COMMANDS = { - diff: 'vscode.diff', - setContext: 'setContext', + diff: "vscode.diff", + setContext: "setContext", } as const; export const CONTEXT_KEYS = { - gitAvailable: 'diffy.gitAvailable', + gitAvailable: "diffy.gitAvailable", } as const; export const MEMENTO_KEYS = { - lastComparison: 'diffy.lastComparison', + lastComparison: "diffy.lastComparison", } as const; export const URI_AUTHORITIES = { - commit: 'commit', - index: 'index', + commit: "commit", + index: "index", } as const; export const DEFAULT_LOG_LIMIT = 200; export const SHORT_SHA_LEN = 7; -export const GIT_BINARY = 'git'; +export const GIT_BINARY = "git"; -export const NUL = '\x00'; -export const TAB = '\t'; -export const LF = '\n'; +export const NUL = "\x00"; +export const TAB = "\t"; +export const LF = "\n"; -export const GIT_LOG_FORMAT = '%H%x00%h%x00%an%x00%at%x00%s'; +export const GIT_LOG_FORMAT = "%H%x00%h%x00%an%x00%at%x00%s"; -export const REFS_FORMAT = '%(refname)%00%(refname:short)%00%(objectname)'; +export const REFS_FORMAT = "%(refname)%00%(refname:short)%00%(objectname)"; -export const REF_PREFIX_HEADS = 'refs/heads/'; -export const REF_PREFIX_TAGS = 'refs/tags/'; +export const REF_PREFIX_HEADS = "refs/heads/"; +export const REF_PREFIX_TAGS = "refs/tags/"; export const REV_KINDS = { - commit: 'commit', - workingCopy: 'workingCopy', - index: 'index', + commit: "commit", + workingCopy: "workingCopy", + index: "index", } as const; export const REF_TYPES = { - branch: 'branch', - tag: 'tag', - other: 'other', + branch: "branch", + tag: "tag", + other: "other", } as const; export const SIDE_B_KINDS = { workingCopy: REV_KINDS.workingCopy, index: REV_KINDS.index, - pickRef: 'pickRef', - pickCommit: 'pickCommit', + pickRef: "pickRef", + pickCommit: "pickCommit", } as const; export const URI_PARSE_ERROR_KINDS = { - invalidScheme: 'invalidScheme', - invalidAuthority: 'invalidAuthority', - missingSha: 'missingSha', - emptyPath: 'emptyPath', - badEncoding: 'badEncoding', - malformed: 'malformed', + invalidScheme: "invalidScheme", + invalidAuthority: "invalidAuthority", + missingSha: "missingSha", + emptyPath: "emptyPath", + badEncoding: "badEncoding", + malformed: "malformed", } as const; export const GIT_ERROR_KINDS = { - spawnFailed: 'spawnFailed', - nonZeroExit: 'nonZeroExit', - parseError: 'parseError', - notARepo: 'notARepo', - notFound: 'notFound', + spawnFailed: "spawnFailed", + nonZeroExit: "nonZeroExit", + parseError: "parseError", + notARepo: "notARepo", + notFound: "notFound", } as const; export const CHANGED_FILE_STATUSES = { - added: 'A', - modified: 'M', - deleted: 'D', - renamed: 'R', - copied: 'C', + added: "A", + modified: "M", + deleted: "D", + renamed: "R", + copied: "C", } as const; export const MENU_IDS = { - scmHistoryItem: 'scm/historyItem/context', - scmResourceState: 'scm/resourceState/context', - editorTitleContext: 'editor/title/context', - explorerContext: 'explorer/context', - commandPalette: 'commandPalette', + scmHistoryItem: "scm/historyItem/context", + scmResourceState: "scm/resourceState/context", + editorTitleContext: "editor/title/context", + explorerContext: "explorer/context", + commandPalette: "commandPalette", } as const; export const MENU_WHEN = { - scmGit: 'scmProvider == git', - resourceFile: 'resourceScheme == file', - resourceFileNotFolder: 'resourceScheme == file && !explorerResourceIsFolder', - never: 'false', + scmGit: "scmProvider == git", + resourceFile: "resourceScheme == file", + resourceFileNotFolder: "resourceScheme == file && !explorerResourceIsFolder", + never: "false", } as const; -export const MENU_GROUP_PREFIX = 'diffy'; +export const MENU_GROUP_PREFIX = "diffy"; -export const TITLE_PREFIX = 'Diffy:'; +export const TITLE_PREFIX = "Diffy:"; export const UI_TEXT = { - workingCopy: 'Working Copy', - workingCopyDescription: 'On-disk files in this repository', - indexLabel: 'Index', - indexDescription: 'The git staging area', - pickCommitLabel: 'Pick a commit…', - pickCommitDescription: 'Choose from recent log entries', - pickRefLabel: 'Pick a branch or tag…', - pickRefDescription: 'Choose from refs in this repository', - pickCommitPlaceholder: 'Pick a commit', - pickRefPlaceholder: 'Pick a branch or tag', - pickBranchPlaceholder: 'Pick a branch', - pickTagPlaceholder: 'Pick a tag', - pickRepoPlaceholder: 'Pick a git repository', - compareAgainstPlaceholder: 'Compare against…', - branchLabel: 'Branch', - tagLabel: 'Tag', - refLabel: 'Ref', - binaryStat: 'binary', - justNow: 'just now', - noChanges: 'Diffy: no changes between selected sides.', - pathArrow: '↔', - pathDash: '—', - bulletDot: '•', + workingCopy: "Working Copy", + workingCopyDescription: "On-disk files in this repository", + indexLabel: "Index", + indexDescription: "The git staging area", + pickCommitLabel: "Pick a commit…", + pickCommitDescription: "Choose from recent log entries", + pickRefLabel: "Pick a branch or tag…", + pickRefDescription: "Choose from refs in this repository", + pickCommitPlaceholder: "Pick a commit", + pickRefPlaceholder: "Pick a branch or tag", + pickBranchPlaceholder: "Pick a branch", + pickTagPlaceholder: "Pick a tag", + pickRepoPlaceholder: "Pick a git repository", + compareAgainstPlaceholder: "Compare against…", + branchLabel: "Branch", + tagLabel: "Tag", + refLabel: "Ref", + binaryStat: "binary", + justNow: "just now", + noChanges: "Diffy: no changes between selected sides.", + pathArrow: "↔", + pathDash: "—", + bulletDot: "•", } as const; export const LOG_EVENTS = { - extensionActivated: 'extension.activated', - extensionDeactivated: 'extension.deactivated', - gitRunStart: 'git.run.start', - gitRunEnd: 'git.run.end', - gitRunSpawnFailed: 'git.run.spawnFailed', - gitError: 'git.error', - diffOpen: 'diff.open', - providerParseFailed: 'provider.parseFailed', - providerRepoUnresolved: 'provider.repoUnresolved', - providerShowFailed: 'provider.showFailed', + extensionActivated: "extension.activated", + extensionDeactivated: "extension.deactivated", + gitRunStart: "git.run.start", + gitRunEnd: "git.run.end", + gitRunSpawnFailed: "git.run.spawnFailed", + gitError: "git.error", + diffOpen: "diff.open", + providerParseFailed: "provider.parseFailed", + providerRepoUnresolved: "provider.repoUnresolved", + providerShowFailed: "provider.showFailed", } as const; -export const VSCODE_GIT_EXTENSION_ID = 'vscode.git'; +export const VSCODE_GIT_EXTENSION_ID = "vscode.git"; export const LOG_LEVELS = { - trace: 'trace', - debug: 'debug', - info: 'info', - warn: 'warn', - error: 'error', + trace: "trace", + debug: "debug", + info: "info", + warn: "warn", + error: "error", } as const; export type LogLevel = (typeof LOG_LEVELS)[keyof typeof LOG_LEVELS]; diff --git a/src/extension.ts b/src/extension.ts index a922897..26dc02a 100644 --- a/src/extension.ts +++ b/src/extension.ts @@ -1,4 +1,4 @@ -import * as vscode from 'vscode'; +import * as vscode from "vscode"; import { BUILT_IN_COMMANDS, COMMAND_IDS, @@ -9,42 +9,34 @@ import { REF_TYPES, TITLE_PREFIX, UI_TEXT, -} from './constants'; -import { createGitRunner } from './git/GitRunner'; -import { type GitApi, getGitApi } from './vscodeGitApi'; -import { addLogStream, logger } from './logger'; -import { makeCompareFileWithCommit } from './commands/compareFileWithCommit'; -import { - FILE_REV_SOURCES, - makeCompareFileWithRev, -} from './commands/compareFileWithRev'; -import { makeCompareTwoCommits } from './commands/compareTwoCommits'; -import { makeCompareWith } from './commands/compareWith'; -import { makeCompareWithPrevious } from './commands/compareWithPrevious'; -import { makeCompareWithRef } from './commands/compareWithRef'; -import { makeCompareWithWorkingCopy } from './commands/compareWithWorkingCopy'; -import { makeReopenLast } from './commands/reopenLast'; -import { registerDiffyContentProvider } from './providers/DiffyContentProvider'; -import type { CommandDeps } from './commands/shared'; -import { buildRepo } from './commands/shared'; -import { createMementoStore } from './state'; -import type { GitRunner } from './git/GitRunner'; +} from "./constants"; +import { createGitRunner } from "./git/GitRunner"; +import { type GitApi, getGitApi } from "./vscodeGitApi"; +import { addLogStream, logger } from "./logger"; +import { makeCompareFileWithCommit } from "./commands/compareFileWithCommit"; +import { FILE_REV_SOURCES, makeCompareFileWithRev } from "./commands/compareFileWithRev"; +import { makeCompareTwoCommits } from "./commands/compareTwoCommits"; +import { makeCompareWith } from "./commands/compareWith"; +import { makeCompareWithPrevious } from "./commands/compareWithPrevious"; +import { makeCompareWithRef } from "./commands/compareWithRef"; +import { makeCompareWithWorkingCopy } from "./commands/compareWithWorkingCopy"; +import { makeReopenLast } from "./commands/reopenLast"; +import { registerDiffyContentProvider } from "./providers/DiffyContentProvider"; +import type { CommandDeps } from "./commands/shared"; +import { buildRepo } from "./commands/shared"; +import { createMementoStore } from "./state"; +import type { GitRunner } from "./git/GitRunner"; -const GIT_VERSION_ARG = '--version'; -const GIT_MISSING_MESSAGE = - `${TITLE_PREFIX} git binary not found on PATH ${UI_TEXT.pathDash} commands disabled.`; -const GIT_API_MISSING_MESSAGE = - `${TITLE_PREFIX} built-in git extension API unavailable.`; +const GIT_VERSION_ARG = "--version"; +const GIT_MISSING_MESSAGE = `${TITLE_PREFIX} git binary not found on PATH ${UI_TEXT.pathDash} commands disabled.`; +const GIT_API_MISSING_MESSAGE = `${TITLE_PREFIX} built-in git extension API unavailable.`; const TRAILING_NEWLINE = /\n$/; -const channelStream = ( - channel: vscode.OutputChannel, -): NodeJS.WritableStream => { +const channelStream = (channel: vscode.OutputChannel): NodeJS.WritableStream => { const stream = { write(chunk: string | Uint8Array): boolean { - const text = - typeof chunk === 'string' ? chunk : Buffer.from(chunk).toString('utf8'); - channel.appendLine(text.replace(TRAILING_NEWLINE, '')); + const text = typeof chunk === "string" ? chunk : Buffer.from(chunk).toString("utf8"); + channel.appendLine(text.replace(TRAILING_NEWLINE, "")); return true; }, }; @@ -57,17 +49,10 @@ const probeGit = async (runner: GitRunner): Promise => { }; const setGitAvailable = async (available: boolean): Promise => { - await vscode.commands.executeCommand( - BUILT_IN_COMMANDS.setContext, - CONTEXT_KEYS.gitAvailable, - available, - ); + await vscode.commands.executeCommand(BUILT_IN_COMMANDS.setContext, CONTEXT_KEYS.gitAvailable, available); }; -const makeRepoResolver = ( - api: GitApi, - runner: GitRunner, -) => () => { +const makeRepoResolver = (api: GitApi, runner: GitRunner) => () => { const first = api.repositories[0]; if (first === undefined) { return undefined; @@ -77,58 +62,35 @@ const makeRepoResolver = ( const registerAll = ( context: vscode.ExtensionContext, - deps: CommandDeps & { readonly state: ReturnType }, + deps: CommandDeps & { readonly state: ReturnType } ): void => { context.subscriptions.push( - vscode.commands.registerCommand( - COMMAND_IDS.compareWith, - makeCompareWith(deps), - ), - vscode.commands.registerCommand( - COMMAND_IDS.compareWithWorkingCopy, - makeCompareWithWorkingCopy(deps), - ), - vscode.commands.registerCommand( - COMMAND_IDS.compareWithPrevious, - makeCompareWithPrevious(deps), - ), + vscode.commands.registerCommand(COMMAND_IDS.compareWith, makeCompareWith(deps)), + vscode.commands.registerCommand(COMMAND_IDS.compareWithWorkingCopy, makeCompareWithWorkingCopy(deps)), + vscode.commands.registerCommand(COMMAND_IDS.compareWithPrevious, makeCompareWithPrevious(deps)), vscode.commands.registerCommand( COMMAND_IDS.compareWithBranch, - makeCompareWithRef({ deps, filter: REF_TYPES.branch }), - ), - vscode.commands.registerCommand( - COMMAND_IDS.compareWithTag, - makeCompareWithRef({ deps, filter: REF_TYPES.tag }), - ), - vscode.commands.registerCommand( - COMMAND_IDS.compareTwoCommits, - makeCompareTwoCommits(deps), - ), - vscode.commands.registerCommand( - COMMAND_IDS.compareFileWithCommit, - makeCompareFileWithCommit(deps), + makeCompareWithRef({ deps, filter: REF_TYPES.branch }) ), + vscode.commands.registerCommand(COMMAND_IDS.compareWithTag, makeCompareWithRef({ deps, filter: REF_TYPES.tag })), + vscode.commands.registerCommand(COMMAND_IDS.compareTwoCommits, makeCompareTwoCommits(deps)), + vscode.commands.registerCommand(COMMAND_IDS.compareFileWithCommit, makeCompareFileWithCommit(deps)), vscode.commands.registerCommand( COMMAND_IDS.compareFileWithBranch, - makeCompareFileWithRev({ deps, source: FILE_REV_SOURCES.branch }), + makeCompareFileWithRev({ deps, source: FILE_REV_SOURCES.branch }) ), vscode.commands.registerCommand( COMMAND_IDS.compareFileWithTag, - makeCompareFileWithRev({ deps, source: FILE_REV_SOURCES.tag }), - ), - vscode.commands.registerCommand( - COMMAND_IDS.reopenLast, - makeReopenLast(deps), + makeCompareFileWithRev({ deps, source: FILE_REV_SOURCES.tag }) ), + vscode.commands.registerCommand(COMMAND_IDS.reopenLast, makeReopenLast(deps)), vscode.commands.registerCommand(COMMAND_IDS.showLogs, () => { deps.output.show(true); - }), + }) ); }; -export const activate = async ( - context: vscode.ExtensionContext, -): Promise => { +export const activate = async (context: vscode.ExtensionContext): Promise => { const output = vscode.window.createOutputChannel(OUTPUT_CHANNEL_NAME); context.subscriptions.push(output); addLogStream({ stream: channelStream(output), level: LOG_LEVELS.info }); @@ -150,10 +112,7 @@ export const activate = async ( const deps = { runner, gitApi: api, output, state } as const; registerDiffyContentProvider(context, makeRepoResolver(api, runner)); registerAll(context, deps); - logger.info( - { repos: api.repositories.length }, - LOG_EVENTS.extensionActivated, - ); + logger.info({ repos: api.repositories.length }, LOG_EVENTS.extensionActivated); }; export const deactivate = (): void => { diff --git a/src/git/GitRepo.ts b/src/git/GitRepo.ts index 0d3f315..0993a50 100644 --- a/src/git/GitRepo.ts +++ b/src/git/GitRepo.ts @@ -1,12 +1,7 @@ -import { DEFAULT_LOG_LIMIT, GIT_LOG_FORMAT, REFS_FORMAT } from '../constants'; -import { type Result, ok, err, andThen, map } from '../result'; -import type { GitRunner } from './GitRunner'; -import { - parseLog, - parseNameStatus, - parseNumstat, - parseRefs, -} from './parsers'; +import { DEFAULT_LOG_LIMIT, GIT_LOG_FORMAT, REFS_FORMAT } from "../constants"; +import { type Result, ok, err, andThen, map } from "../result"; +import type { GitRunner } from "./GitRunner"; +import { parseLog, parseNameStatus, parseNumstat, parseRefs } from "./parsers"; import type { ChangedFile, Commit, @@ -17,7 +12,7 @@ import type { Ref, RevSpec, Sha, -} from './types'; +} from "./types"; export interface DiffSides { readonly from: CommitRev; @@ -45,63 +40,60 @@ export interface GitRepo { } const buildLogArgs = (params: { limit: number; ref?: string }): readonly string[] => { - const base = ['log', `--max-count=${params.limit.toString()}`, `--format=${GIT_LOG_FORMAT}`, '-z']; + const base = ["log", `--max-count=${params.limit.toString()}`, `--format=${GIT_LOG_FORMAT}`, "-z"]; return params.ref === undefined ? base : [...base, params.ref]; }; -const buildDiffArgs = ({ from, to }: DiffSides, subcommand: 'name-status' | 'numstat'): readonly string[] => { - const head = ['diff', `--${subcommand}`, '-z', '--find-renames', '--find-copies']; - if (to.kind === 'commit') {return [...head, from.sha, to.sha];} - if (to.kind === 'workingCopy') {return [...head, from.sha];} - return [...head, from.sha, '--cached']; +const buildDiffArgs = ({ from, to }: DiffSides, subcommand: "name-status" | "numstat"): readonly string[] => { + const head = ["diff", `--${subcommand}`, "-z", "--find-renames", "--find-copies"]; + if (to.kind === "commit") { + return [...head, from.sha, to.sha]; + } + if (to.kind === "workingCopy") { + return [...head, from.sha]; + } + return [...head, from.sha, "--cached"]; }; -const showSpec = ({ rev, path }: ShowArgs): string => - rev.kind === 'commit' ? `${rev.sha}:${path}` : `:${path}`; +const showSpec = ({ rev, path }: ShowArgs): string => (rev.kind === "commit" ? `${rev.sha}:${path}` : `:${path}`); const trimSha = (stdout: string): Result => { const sha = stdout.trim(); - if (sha.length === 0) {return err({ kind: 'parseError', message: 'revParse: empty output' });} + if (sha.length === 0) { + return err({ kind: "parseError", message: "revParse: empty output" }); + } return ok(sha); }; -export const createGitRepo = ({ - runner, - cwd, -}: { - runner: GitRunner; - cwd: string; -}): GitRepo => ({ +export const createGitRepo = ({ runner, cwd }: { runner: GitRunner; cwd: string }): GitRepo => ({ log: async (args = {}) => { const limit = args.limit ?? DEFAULT_LOG_LIMIT; - const logArgs = args.ref === undefined - ? buildLogArgs({ limit }) - : buildLogArgs({ limit, ref: args.ref }); + const logArgs = args.ref === undefined ? buildLogArgs({ limit }) : buildLogArgs({ limit, ref: args.ref }); const r = await runner.run({ args: logArgs, cwd }); return andThen(r, parseLog); }, nameStatus: async (args) => { - const r = await runner.run({ args: buildDiffArgs(args, 'name-status'), cwd }); + const r = await runner.run({ args: buildDiffArgs(args, "name-status"), cwd }); return andThen(r, parseNameStatus); }, numstat: async (args) => { - const r = await runner.run({ args: buildDiffArgs(args, 'numstat'), cwd }); + const r = await runner.run({ args: buildDiffArgs(args, "numstat"), cwd }); return andThen(r, parseNumstat); }, - show: async (args) => await runner.run({ args: ['show', showSpec(args)], cwd }), + show: async (args) => await runner.run({ args: ["show", showSpec(args)], cwd }), refs: async () => { const r = await runner.run({ - args: ['for-each-ref', `--format=${REFS_FORMAT}`], + args: ["for-each-ref", `--format=${REFS_FORMAT}`], cwd, }); return andThen(r, parseRefs); }, revParse: async (name) => { - const r = await runner.run({ args: ['rev-parse', '--verify', name], cwd }); + const r = await runner.run({ args: ["rev-parse", "--verify", name], cwd }); return andThen(r, trimSha); }, currentBranch: async () => { - const r = await runner.run({ args: ['branch', '--show-current'], cwd }); + const r = await runner.run({ args: ["branch", "--show-current"], cwd }); return map(r, (stdout) => { const name = stdout.trim(); return name.length === 0 ? undefined : name; diff --git a/src/git/GitRunner.ts b/src/git/GitRunner.ts index 5d267c4..873c6d9 100644 --- a/src/git/GitRunner.ts +++ b/src/git/GitRunner.ts @@ -1,10 +1,10 @@ -import { spawn, type ChildProcessWithoutNullStreams } from 'node:child_process'; -import { GIT_BINARY, GIT_ERROR_KINDS, LOG_EVENTS } from '../constants'; -import { type Result, ok, err } from '../result'; -import { logger as defaultLogger, type Logger } from '../logger'; -import type { GitError } from './types'; +import { spawn, type ChildProcessWithoutNullStreams } from "node:child_process"; +import { GIT_BINARY, GIT_ERROR_KINDS, LOG_EVENTS } from "../constants"; +import { type Result, ok, err } from "../result"; +import { logger as defaultLogger, type Logger } from "../logger"; +import type { GitError } from "./types"; -const UNKNOWN_EXIT_LABEL = '?'; +const UNKNOWN_EXIT_LABEL = "?"; const UNKNOWN_EXIT_CODE = -1; export interface GitRunArgs { @@ -44,55 +44,40 @@ const finishRun = (params: { }); }; -const wireSubprocess = ({ - child, - argCount, - subcommand, - logger, - resolve, -}: WireArgs): void => { - let stdout = ''; - let stderr = ''; - child.stdout.on('data', (chunk: Buffer) => { - stdout += chunk.toString('utf8'); +const wireSubprocess = ({ child, argCount, subcommand, logger, resolve }: WireArgs): void => { + let stdout = ""; + let stderr = ""; + child.stdout.on("data", (chunk: Buffer) => { + stdout += chunk.toString("utf8"); }); - child.stderr.on('data', (chunk: Buffer) => { - stderr += chunk.toString('utf8'); + child.stderr.on("data", (chunk: Buffer) => { + stderr += chunk.toString("utf8"); }); - child.on('error', (e: Error) => { + child.on("error", (e: Error) => { logger.warn({ argCount }, LOG_EVENTS.gitRunSpawnFailed); resolve(err({ kind: GIT_ERROR_KINDS.spawnFailed, message: e.message })); }); - child.on('close', (code: number | null) => { - logger.debug( - { exitCode: code, stdoutLen: stdout.length }, - LOG_EVENTS.gitRunEnd, - ); + child.on("close", (code: number | null) => { + logger.debug({ exitCode: code, stdoutLen: stdout.length }, LOG_EVENTS.gitRunEnd); resolve(finishRun({ code, subcommand, stdout, stderr })); }); }; -const runGit = async ({ - args, - cwd, - logger, -}: GitRunArgs & { logger: Logger }): Promise> => { +const runGit = async ({ args, cwd, logger }: GitRunArgs & { logger: Logger }): Promise> => { logger.debug({ argCount: args.length }, LOG_EVENTS.gitRunStart); return await new Promise>((resolve) => { const child = spawn(GIT_BINARY, [...args], { cwd }); wireSubprocess({ child, argCount: args.length, - subcommand: args[0] ?? '', + subcommand: args[0] ?? "", logger, resolve, }); }); }; -export const createGitRunner = ( - deps: { logger?: Logger } = {}, -): GitRunner => { +export const createGitRunner = (deps: { logger?: Logger } = {}): GitRunner => { const logger = deps.logger ?? defaultLogger; return { run: async (a) => await runGit({ ...a, logger }) }; }; diff --git a/src/git/parsers.ts b/src/git/parsers.ts index 3801ed0..3ad9609 100644 --- a/src/git/parsers.ts +++ b/src/git/parsers.ts @@ -7,17 +7,9 @@ import { REF_PREFIX_TAGS, REF_TYPES, TAB, -} from '../constants'; -import { type Result, ok, err } from '../result'; -import type { - ChangedFile, - ChangedFileStatus, - Commit, - DiffStat, - GitError, - Ref, - RefType, -} from './types'; +} from "../constants"; +import { type Result, ok, err } from "../result"; +import type { ChangedFile, ChangedFileStatus, Commit, DiffStat, GitError, Ref, RefType } from "./types"; const LOG_FIELDS_PER_RECORD = 5; const REF_FIELDS_PER_RECORD = 3; @@ -25,20 +17,25 @@ const NUMSTAT_TAB_FIELDS = 3; const CHAR_CODE_DIGIT_LO = 48; const CHAR_CODE_DIGIT_HI = 57; -const errParse = (message: string): Result => - err({ kind: GIT_ERROR_KINDS.parseError, message }); +const errParse = (message: string): Result => err({ kind: GIT_ERROR_KINDS.parseError, message }); const stripTrailingEmpty = (arr: readonly string[]): readonly string[] => { - if (arr.length === 0) {return arr;} + if (arr.length === 0) { + return arr; + } const last = arr[arr.length - 1]; - return last === '' ? arr.slice(0, -1) : arr; + return last === "" ? arr.slice(0, -1) : arr; }; const isAllDigits = (s: string): boolean => { - if (s.length === 0) {return false;} + if (s.length === 0) { + return false; + } for (let i = 0; i < s.length; i++) { const c = s.charCodeAt(i); - if (c < CHAR_CODE_DIGIT_LO || c > CHAR_CODE_DIGIT_HI) {return false;} + if (c < CHAR_CODE_DIGIT_LO || c > CHAR_CODE_DIGIT_HI) { + return false; + } } return true; }; @@ -62,24 +59,30 @@ const parseLogRecord = (fields: readonly string[]): Result => atStr === undefined || subject === undefined ) { - return errParse('parseLog: missing field'); + return errParse("parseLog: missing field"); + } + if (!isAllDigits(atStr)) { + return errParse("parseLog: invalid timestamp"); } - if (!isAllDigits(atStr)) {return errParse('parseLog: invalid timestamp');} const authorTime = Number.parseInt(atStr, 10); return ok({ sha, shortSha, author, authorTime, subject }); }; export const parseLog = (stdout: string): Result => { - if (stdout.length === 0) {return ok([]);} + if (stdout.length === 0) { + return ok([]); + } const tokens = stripTrailingEmpty(stdout.split(NUL)); if (tokens.length % LOG_FIELDS_PER_RECORD !== 0) { - return errParse('parseLog: field count not multiple of 5'); + return errParse("parseLog: field count not multiple of 5"); } const commits: Commit[] = []; for (let i = 0; i < tokens.length; i += LOG_FIELDS_PER_RECORD) { const slice = tokens.slice(i, i + LOG_FIELDS_PER_RECORD); const r = parseLogRecord(slice); - if (!r.ok) {return r;} + if (!r.ok) { + return r; + } commits.push(r.value); } return ok(commits); @@ -92,22 +95,25 @@ interface ParsedStatus { } const parseStatusToken = (raw: string): Result => { - if (raw.length === 0) {return errParse('parseNameStatus: empty status token');} + if (raw.length === 0) { + return errParse("parseNameStatus: empty status token"); + } const first = raw.charAt(0); if ( first === CHANGED_FILE_STATUSES.added || first === CHANGED_FILE_STATUSES.modified || first === CHANGED_FILE_STATUSES.deleted ) { - if (raw.length !== 1) {return errParse('parseNameStatus: extra chars on status');} + if (raw.length !== 1) { + return errParse("parseNameStatus: extra chars on status"); + } return ok({ status: first, isRename: false }); } - if ( - first === CHANGED_FILE_STATUSES.renamed || - first === CHANGED_FILE_STATUSES.copied - ) { + if (first === CHANGED_FILE_STATUSES.renamed || first === CHANGED_FILE_STATUSES.copied) { const digits = raw.slice(1); - if (!isAllDigits(digits)) {return errParse('parseNameStatus: bad similarity');} + if (!isAllDigits(digits)) { + return errParse("parseNameStatus: bad similarity"); + } return ok({ status: first, similarity: Number.parseInt(digits, 10), isRename: true }); } return errParse(`parseNameStatus: unknown status '${first}'`); @@ -116,25 +122,29 @@ const parseStatusToken = (raw: string): Result => { const buildSimple = ( parsed: ParsedStatus, tokens: readonly string[], - i: number, + i: number ): Result<{ file: ChangedFile; next: number }, GitError> => { const path = tokens[i + 1]; - if (path === undefined) {return errParse('parseNameStatus: missing path');} + if (path === undefined) { + return errParse("parseNameStatus: missing path"); + } return ok({ file: { status: parsed.status, path }, next: i + 2 }); }; const buildRenameOrCopy = ( parsed: ParsedStatus, tokens: readonly string[], - i: number, + i: number ): Result<{ file: ChangedFile; next: number }, GitError> => { const oldPath = tokens[i + 1]; const newPath = tokens[i + 2]; if (oldPath === undefined || newPath === undefined) { - return errParse('parseNameStatus: rename missing paths'); + return errParse("parseNameStatus: rename missing paths"); } const similarity = parsed.similarity; - if (similarity === undefined) {return errParse('parseNameStatus: rename missing similarity');} + if (similarity === undefined) { + return errParse("parseNameStatus: rename missing similarity"); + } return ok({ file: { status: parsed.status, path: newPath, oldPath, similarity }, next: i + 3, @@ -143,27 +153,31 @@ const buildRenameOrCopy = ( const readOneNameStatus = ( tokens: readonly string[], - i: number, + i: number ): Result<{ file: ChangedFile; next: number }, GitError> => { const head = tokens[i]; - if (head === undefined) {return errParse('parseNameStatus: missing status');} + if (head === undefined) { + return errParse("parseNameStatus: missing status"); + } const parsed = parseStatusToken(head); - if (!parsed.ok) {return parsed;} - return parsed.value.isRename - ? buildRenameOrCopy(parsed.value, tokens, i) - : buildSimple(parsed.value, tokens, i); + if (!parsed.ok) { + return parsed; + } + return parsed.value.isRename ? buildRenameOrCopy(parsed.value, tokens, i) : buildSimple(parsed.value, tokens, i); }; -export const parseNameStatus = ( - stdout: string, -): Result => { - if (stdout.length === 0) {return ok([]);} +export const parseNameStatus = (stdout: string): Result => { + if (stdout.length === 0) { + return ok([]); + } const tokens = stripTrailingEmpty(stdout.split(NUL)); const out: ChangedFile[] = []; let i = 0; while (i < tokens.length) { const r = readOneNameStatus(tokens, i); - if (!r.ok) {return r;} + if (!r.ok) { + return r; + } out.push(r.value.file); i = r.value.next; } @@ -180,15 +194,17 @@ interface NumstatHead { const splitNumstatHead = (head: string): Result => { const parts = head.split(TAB); if (parts.length !== NUMSTAT_TAB_FIELDS) { - return errParse('parseNumstat: expected 3 tab-fields'); + return errParse("parseNumstat: expected 3 tab-fields"); } const [a, d, p] = parts; if (a === undefined || d === undefined || p === undefined) { - return errParse('parseNumstat: missing tab fields'); + return errParse("parseNumstat: missing tab fields"); + } + if (a === "-" && d === "-") { + return ok({ added: 0, deleted: 0, binary: true, pathField: p }); } - if (a === '-' && d === '-') {return ok({ added: 0, deleted: 0, binary: true, pathField: p });} if (!isAllDigits(a) || !isAllDigits(d)) { - return errParse('parseNumstat: non-numeric counts'); + return errParse("parseNumstat: non-numeric counts"); } return ok({ added: Number.parseInt(a, 10), @@ -198,22 +214,23 @@ const splitNumstatHead = (head: string): Result => { }); }; -const readOneNumstat = ( - tokens: readonly string[], - i: number, -): Result<{ stat: DiffStat; next: number }, GitError> => { +const readOneNumstat = (tokens: readonly string[], i: number): Result<{ stat: DiffStat; next: number }, GitError> => { const head = tokens[i]; - if (head === undefined) {return errParse('parseNumstat: missing record');} + if (head === undefined) { + return errParse("parseNumstat: missing record"); + } const fields = splitNumstatHead(head); - if (!fields.ok) {return fields;} + if (!fields.ok) { + return fields; + } const { added, deleted, binary, pathField } = fields.value; - if (pathField !== '') { + if (pathField !== "") { return ok({ stat: { path: pathField, added, deleted, binary }, next: i + 1 }); } const oldPath = tokens[i + 1]; const newPath = tokens[i + 2]; if (oldPath === undefined || newPath === undefined) { - return errParse('parseNumstat: rename missing paths'); + return errParse("parseNumstat: rename missing paths"); } return ok({ stat: { path: newPath, oldPath, added, deleted, binary }, @@ -222,13 +239,17 @@ const readOneNumstat = ( }; export const parseNumstat = (stdout: string): Result => { - if (stdout.length === 0) {return ok([]);} + if (stdout.length === 0) { + return ok([]); + } const tokens = stripTrailingEmpty(stdout.split(NUL)); const out: DiffStat[] = []; let i = 0; while (i < tokens.length) { const r = readOneNumstat(tokens, i); - if (!r.ok) {return r;} + if (!r.ok) { + return r; + } out.push(r.value.stat); i = r.value.next; } @@ -238,25 +259,29 @@ export const parseNumstat = (stdout: string): Result => { const [fullName, shortName, sha] = fields; if (fullName === undefined || shortName === undefined || sha === undefined) { - return errParse('parseRefs: missing field'); + return errParse("parseRefs: missing field"); } - if (fullName === '' || shortName === '' || sha === '') { - return errParse('parseRefs: empty field'); + if (fullName === "" || shortName === "" || sha === "") { + return errParse("parseRefs: empty field"); } return ok({ name: shortName, fullName, sha, type: refTypeFromName(fullName) }); }; export const parseRefs = (stdout: string): Result => { - if (stdout.length === 0) {return ok([]);} + if (stdout.length === 0) { + return ok([]); + } const lines = stdout.split(LF).filter((l) => l.length > 0); const refs: Ref[] = []; for (const line of lines) { const fields = line.split(NUL); if (fields.length !== REF_FIELDS_PER_RECORD) { - return errParse('parseRefs: expected 3 NUL-separated fields per line'); + return errParse("parseRefs: expected 3 NUL-separated fields per line"); } const r = parseRefRecord(fields); - if (!r.ok) {return r;} + if (!r.ok) { + return r; + } refs.push(r.value); } return ok(refs); diff --git a/src/git/repoMatch.ts b/src/git/repoMatch.ts index 3445ef7..11f4e04 100644 --- a/src/git/repoMatch.ts +++ b/src/git/repoMatch.ts @@ -13,7 +13,7 @@ interface ApiLike> { export const matchRepoByFsPath = >( repositories: readonly R[], - targetFsPath: string, + targetFsPath: string ): R | undefined => { let best: R | undefined; for (const r of repositories) { @@ -28,10 +28,7 @@ export const matchRepoByFsPath = >( return best; }; -export const findRepoForUri = >( - api: ApiLike, - uri: U, -): R | undefined => { +export const findRepoForUri = >(api: ApiLike, uri: U): R | undefined => { if (api.getRepository !== undefined) { return api.getRepository(uri) ?? undefined; } diff --git a/src/git/types.ts b/src/git/types.ts index fddb31e..a30e957 100644 --- a/src/git/types.ts +++ b/src/git/types.ts @@ -1,14 +1,8 @@ -import type { - CHANGED_FILE_STATUSES, - GIT_ERROR_KINDS, - REF_TYPES, - REV_KINDS, -} from '../constants'; +import type { CHANGED_FILE_STATUSES, GIT_ERROR_KINDS, REF_TYPES, REV_KINDS } from "../constants"; export type Sha = string; -export type ChangedFileStatus = - (typeof CHANGED_FILE_STATUSES)[keyof typeof CHANGED_FILE_STATUSES]; +export type ChangedFileStatus = (typeof CHANGED_FILE_STATUSES)[keyof typeof CHANGED_FILE_STATUSES]; export interface ChangedFile { readonly status: ChangedFileStatus; @@ -37,8 +31,12 @@ export interface CommitRev { readonly kind: typeof REV_KINDS.commit; readonly sha: Sha; } -export interface WorkingCopyRev { readonly kind: typeof REV_KINDS.workingCopy } -export interface IndexRev { readonly kind: typeof REV_KINDS.index } +export interface WorkingCopyRev { + readonly kind: typeof REV_KINDS.workingCopy; +} +export interface IndexRev { + readonly kind: typeof REV_KINDS.index; +} export type RevSpec = CommitRev | WorkingCopyRev | IndexRev; @@ -53,8 +51,7 @@ export interface Ref { readonly type: RefType; } -export type GitErrorKind = - (typeof GIT_ERROR_KINDS)[keyof typeof GIT_ERROR_KINDS]; +export type GitErrorKind = (typeof GIT_ERROR_KINDS)[keyof typeof GIT_ERROR_KINDS]; export interface GitError { readonly kind: GitErrorKind; diff --git a/src/logger.ts b/src/logger.ts index 63dcc2c..50e1e9b 100644 --- a/src/logger.ts +++ b/src/logger.ts @@ -1,7 +1,7 @@ -import pino, { type Logger as PinoLogger, type Level, multistream } from 'pino'; -import { LOG_LEVELS } from './constants'; +import pino, { type Logger as PinoLogger, type Level, multistream } from "pino"; +import { LOG_LEVELS } from "./constants"; -const ENV_LOG_LEVEL_VAR = 'DIFFY_LOG_LEVEL'; +const ENV_LOG_LEVEL_VAR = "DIFFY_LOG_LEVEL"; export interface Logger { trace: (fields: object, msg?: string) => void; @@ -27,9 +27,7 @@ const buildPino = (): PinoLogger => base: null, timestamp: pino.stdTimeFunctions.isoTime, }, - multistream( - streams.map((s) => ({ stream: s.stream, level: s.level ?? LOG_LEVELS.trace })), - ), + multistream(streams.map((s) => ({ stream: s.stream, level: s.level ?? LOG_LEVELS.trace }))) ); let underlying: PinoLogger = buildPino(); diff --git a/src/menus.ts b/src/menus.ts index da7b5ec..25bc807 100644 --- a/src/menus.ts +++ b/src/menus.ts @@ -1,11 +1,4 @@ -import { - COMMAND_IDS, - MENU_GROUP_PREFIX, - MENU_IDS, - MENU_WHEN, - TITLE_PREFIX, - UI_TEXT, -} from './constants'; +import { COMMAND_IDS, MENU_GROUP_PREFIX, MENU_IDS, MENU_WHEN, TITLE_PREFIX, UI_TEXT } from "./constants"; export interface MenuEntry { readonly command: string; @@ -37,11 +30,7 @@ const fileLevelCommands = [ COMMAND_IDS.compareFileWithTag, ] as const; -const groupedEntries = ( - commands: readonly string[], - when: string, - groupPrefix: string, -): readonly MenuEntry[] => +const groupedEntries = (commands: readonly string[], when: string, groupPrefix: string): readonly MenuEntry[] => commands.map((command, i) => ({ command, when, @@ -53,26 +42,10 @@ const hideFromPalette = (commands: readonly string[]) => export const buildMenuManifest = (): MenuManifest => ({ menus: { - [MENU_IDS.scmHistoryItem]: groupedEntries( - commitLevelCommands, - MENU_WHEN.scmGit, - MENU_GROUP_PREFIX, - ), - [MENU_IDS.scmResourceState]: groupedEntries( - fileLevelCommands, - MENU_WHEN.scmGit, - MENU_GROUP_PREFIX, - ), - [MENU_IDS.editorTitleContext]: groupedEntries( - fileLevelCommands, - MENU_WHEN.resourceFile, - MENU_GROUP_PREFIX, - ), - [MENU_IDS.explorerContext]: groupedEntries( - fileLevelCommands, - MENU_WHEN.resourceFileNotFolder, - MENU_GROUP_PREFIX, - ), + [MENU_IDS.scmHistoryItem]: groupedEntries(commitLevelCommands, MENU_WHEN.scmGit, MENU_GROUP_PREFIX), + [MENU_IDS.scmResourceState]: groupedEntries(fileLevelCommands, MENU_WHEN.scmGit, MENU_GROUP_PREFIX), + [MENU_IDS.editorTitleContext]: groupedEntries(fileLevelCommands, MENU_WHEN.resourceFile, MENU_GROUP_PREFIX), + [MENU_IDS.explorerContext]: groupedEntries(fileLevelCommands, MENU_WHEN.resourceFileNotFolder, MENU_GROUP_PREFIX), }, commandPalette: hideFromPalette([ COMMAND_IDS.compareWith, diff --git a/src/providers/DiffyContentProvider.ts b/src/providers/DiffyContentProvider.ts index df178fd..9b77844 100644 --- a/src/providers/DiffyContentProvider.ts +++ b/src/providers/DiffyContentProvider.ts @@ -1,8 +1,8 @@ -import * as vscode from 'vscode'; -import { LOG_EVENTS, SCHEME } from '../constants'; -import type { GitRepo } from '../git/GitRepo'; -import { logger } from '../logger'; -import { parseDiffyUri } from '../ui/uri'; +import * as vscode from "vscode"; +import { LOG_EVENTS, SCHEME } from "../constants"; +import type { GitRepo } from "../git/GitRepo"; +import { logger } from "../logger"; +import { parseDiffyUri } from "../ui/uri"; export class DiffyContentProvider implements vscode.TextDocumentContentProvider { private readonly emitter = new vscode.EventEmitter(); @@ -15,17 +15,17 @@ export class DiffyContentProvider implements vscode.TextDocumentContentProvider const parsed = parseDiffyUri(uri.toString()); if (!parsed.ok) { logger.warn({ kind: parsed.error.kind }, LOG_EVENTS.providerParseFailed); - return ''; + return ""; } const repo = this.resolveRepo(uri); if (repo === undefined) { logger.warn({}, LOG_EVENTS.providerRepoUnresolved); - return ''; + return ""; } const r = await repo.show({ rev: parsed.value.rev, path: parsed.value.path }); if (!r.ok) { logger.debug({ kind: r.error.kind }, LOG_EVENTS.providerShowFailed); - return ''; + return ""; } return r.value; } @@ -37,12 +37,9 @@ export class DiffyContentProvider implements vscode.TextDocumentContentProvider export const registerDiffyContentProvider = ( context: vscode.ExtensionContext, - resolver: (uri: vscode.Uri) => GitRepo | undefined, + resolver: (uri: vscode.Uri) => GitRepo | undefined ): DiffyContentProvider => { const provider = new DiffyContentProvider(resolver); - context.subscriptions.push( - vscode.workspace.registerTextDocumentContentProvider(SCHEME, provider), - provider, - ); + context.subscriptions.push(vscode.workspace.registerTextDocumentContentProvider(SCHEME, provider), provider); return provider; }; diff --git a/src/result.ts b/src/result.ts index 3aa96b4..ec7c3b4 100644 --- a/src/result.ts +++ b/src/result.ts @@ -1,5 +1,11 @@ -export interface Ok { readonly ok: true; readonly value: T } -export interface Err { readonly ok: false; readonly error: E } +export interface Ok { + readonly ok: true; + readonly value: T; +} +export interface Err { + readonly ok: false; + readonly error: E; +} export type Result = Ok | Err; export const ok = (value: T): Ok => ({ ok: true, value }); @@ -10,16 +16,11 @@ export const isOk = (r: Result): r is Ok => r.ok; export const isErr = (r: Result): r is Err => !r.ok; -export const map = (r: Result, f: (t: T) => U): Result => - r.ok ? ok(f(r.value)) : r; +export const map = (r: Result, f: (t: T) => U): Result => (r.ok ? ok(f(r.value)) : r); -export const andThen = ( - r: Result, - f: (t: T) => Result, -): Result => (r.ok ? f(r.value) : r); +export const andThen = (r: Result, f: (t: T) => Result): Result => (r.ok ? f(r.value) : r); -export const unwrapOr = (r: Result, fallback: T): T => - r.ok ? r.value : fallback; +export const unwrapOr = (r: Result, fallback: T): T => (r.ok ? r.value : fallback); export function expectOk(r: Result): asserts r is Ok { if (!r.ok) { diff --git a/src/state.ts b/src/state.ts index 35c2986..3100f45 100644 --- a/src/state.ts +++ b/src/state.ts @@ -1,7 +1,7 @@ -import type * as vscode from 'vscode'; -import { MEMENTO_KEYS } from './constants'; -import type { REV_KINDS } from './constants'; -import type { RevSpec, Sha } from './git/types'; +import type * as vscode from "vscode"; +import { MEMENTO_KEYS } from "./constants"; +import type { REV_KINDS } from "./constants"; +import type { RevSpec, Sha } from "./git/types"; export interface LastComparison { readonly revA: { readonly kind: typeof REV_KINDS.commit; readonly sha: Sha }; @@ -16,16 +16,16 @@ export interface MementoStore { } export const isLastComparison = (raw: unknown): raw is LastComparison => { - if (typeof raw !== 'object' || raw === null) { + if (typeof raw !== "object" || raw === null) { return false; } - if (!('repoRoot' in raw) || typeof raw.repoRoot !== 'string') { + if (!("repoRoot" in raw) || typeof raw.repoRoot !== "string") { return false; } - if (!('revA' in raw) || typeof raw.revA !== 'object' || raw.revA === null) { + if (!("revA" in raw) || typeof raw.revA !== "object" || raw.revA === null) { return false; } - if (!('revB' in raw) || typeof raw.revB !== 'object' || raw.revB === null) { + if (!("revB" in raw) || typeof raw.revB !== "object" || raw.revB === null) { return false; } return true; diff --git a/src/test/runTests.ts b/src/test/runTests.ts index debc148..9ba7969 100644 --- a/src/test/runTests.ts +++ b/src/test/runTests.ts @@ -1,21 +1,21 @@ -import * as path from 'node:path'; -import { spawnSync } from 'node:child_process'; -import { existsSync } from 'node:fs'; -import { runTests } from '@vscode/test-electron'; +import * as path from "node:path"; +import { spawnSync } from "node:child_process"; +import { existsSync } from "node:fs"; +import { runTests } from "@vscode/test-electron"; -const repoRoot = path.resolve(__dirname, '..', '..'); +const repoRoot = path.resolve(__dirname, "..", ".."); const extensionDevelopmentPath = repoRoot; -const extensionTestsPath = path.resolve(__dirname, 'suite', 'index'); -const workspacePath = path.resolve(repoRoot, 'test-fixtures', 'repo-seed', 'workspace'); -const seedScript = path.resolve(repoRoot, 'test-fixtures', 'repo-seed', 'seed.sh'); +const extensionTestsPath = path.resolve(__dirname, "suite", "index"); +const workspacePath = path.resolve(repoRoot, "test-fixtures", "repo-seed", "workspace"); +const seedScript = path.resolve(repoRoot, "test-fixtures", "repo-seed", "seed.sh"); const ensureSeedRepo = (): void => { - if (existsSync(path.join(workspacePath, '.git'))) { + if (existsSync(path.join(workspacePath, ".git"))) { return; } - const r = spawnSync('bash', [seedScript], { stdio: 'inherit' }); + const r = spawnSync("bash", [seedScript], { stdio: "inherit" }); if (r.status !== 0) { - const exitLabel = r.status === null ? '?' : r.status.toString(); + const exitLabel = r.status === null ? "?" : r.status.toString(); throw new Error(`seed.sh failed with exit ${exitLabel}`); } }; @@ -25,26 +25,21 @@ const main = async (): Promise => { // Claude Code's host extension sets ELECTRON_RUN_AS_NODE=1 in our shell. // That env var, if inherited by the spawned Electron in @vscode/test-electron, // makes Electron behave as Node and refuse to launch VS Code. Drop it. - if ('ELECTRON_RUN_AS_NODE' in process.env) { - delete process.env['ELECTRON_RUN_AS_NODE']; + if ("ELECTRON_RUN_AS_NODE" in process.env) { + delete process.env["ELECTRON_RUN_AS_NODE"]; } - const coverageDir = process.env['NODE_V8_COVERAGE']; + const coverageDir = process.env["NODE_V8_COVERAGE"]; const extensionTestsEnv: Record = { - DIFFY_E2E: '1', + DIFFY_E2E: "1", }; - if (coverageDir !== undefined && coverageDir !== '') { - extensionTestsEnv['NODE_V8_COVERAGE'] = coverageDir; + if (coverageDir !== undefined && coverageDir !== "") { + extensionTestsEnv["NODE_V8_COVERAGE"] = coverageDir; } await runTests({ extensionDevelopmentPath, extensionTestsPath, extensionTestsEnv, - launchArgs: [ - workspacePath, - '--disable-telemetry', - '--enable-proposed-api', - 'nimblesite.diffy', - ], + launchArgs: [workspacePath, "--disable-telemetry", "--enable-proposed-api", "nimblesite.diffy"], }); }; diff --git a/src/test/suite/activation.test.ts b/src/test/suite/activation.test.ts index e60d0f5..d33546a 100644 --- a/src/test/suite/activation.test.ts +++ b/src/test/suite/activation.test.ts @@ -1,49 +1,45 @@ -import { strict as assert } from 'node:assert'; -import * as vscode from 'vscode'; -import { COMMAND_IDS, OUTPUT_CHANNEL_NAME } from '../../constants'; -import { tick } from './helpers'; +import { strict as assert } from "node:assert"; +import * as vscode from "vscode"; +import { COMMAND_IDS, OUTPUT_CHANNEL_NAME } from "../../constants"; +import { tick } from "./helpers"; -const EXTENSION_ID = 'nimblesite.diffy'; +const EXTENSION_ID = "nimblesite.diffy"; const TICK_MS = 20; const ALL_COMMAND_IDS: readonly string[] = Object.values(COMMAND_IDS); -describe('activation', () => { - it('extension is present, activates, and every command id is registered', async () => { +describe("activation", () => { + it("extension is present, activates, and every command id is registered", async () => { const ext = vscode.extensions.getExtension(EXTENSION_ID); assert.ok(ext, `extension ${EXTENSION_ID} should be present`); if (!ext.isActive) { await ext.activate(); } - assert.equal( - ext.isActive, - true, - 'extension should be active after activate()', - ); + assert.equal(ext.isActive, true, "extension should be active after activate()"); await tick(TICK_MS); const registered = await vscode.commands.getCommands(true); for (const id of ALL_COMMAND_IDS) { assert.ok( registered.includes(id), - `command ${id} should be registered (was: ${registered.filter((c) => c.startsWith('diffy.')).join(', ')})`, + `command ${id} should be registered (was: ${registered.filter((c) => c.startsWith("diffy.")).join(", ")})` ); } }); - it('exposes a workspace folder containing the seeded git repo', () => { + it("exposes a workspace folder containing the seeded git repo", () => { const folders = vscode.workspace.workspaceFolders; - assert.ok(folders, 'workspace folders should be set'); + assert.ok(folders, "workspace folders should be set"); assert.equal(folders.length, 1); const folder = folders[0]; assert.ok(folder); assert.match(folder.uri.fsPath, /repo-seed\/workspace$/); }); - it('shows logs command opens the Diffy OutputChannel without errors', async () => { + it("shows logs command opens the Diffy OutputChannel without errors", async () => { await vscode.commands.executeCommand(COMMAND_IDS.showLogs); // The channel's "visible" state isn't observable from the test host, but // the command must have a registered handler and complete without throwing. - assert.equal(typeof OUTPUT_CHANNEL_NAME, 'string'); + assert.equal(typeof OUTPUT_CHANNEL_NAME, "string"); assert.ok(OUTPUT_CHANNEL_NAME.length > 0); }); }); diff --git a/src/test/suite/commands.test.ts b/src/test/suite/commands.test.ts index ce060e4..729b36f 100644 --- a/src/test/suite/commands.test.ts +++ b/src/test/suite/commands.test.ts @@ -1,6 +1,6 @@ -import { strict as assert } from 'node:assert'; -import * as vscode from 'vscode'; -import { COMMAND_IDS } from '../../constants'; +import { strict as assert } from "node:assert"; +import * as vscode from "vscode"; +import { COMMAND_IDS } from "../../constants"; import { accept, allDiffTabs, @@ -14,9 +14,9 @@ import { waitForDiffTab, waitForRepoReady, workspaceRoot, -} from './helpers'; +} from "./helpers"; -const EXTENSION_ID = 'nimblesite.diffy'; +const EXTENSION_ID = "nimblesite.diffy"; const ensureActivated = async (): Promise => { const ext = vscode.extensions.getExtension(EXTENSION_ID); @@ -36,7 +36,7 @@ const moveAndAccept = async (steps: number): Promise => { await accept(); }; -describe('Diffy commands — end-to-end through real QuickPick UI', () => { +describe("Diffy commands — end-to-end through real QuickPick UI", () => { before(async () => { await ensureActivated(); }); @@ -53,16 +53,16 @@ describe('Diffy commands — end-to-end through real QuickPick UI', () => { await tick(5); }); - it('reopenLast with no prior comparison shows an info toast and opens no diff', async () => { + it("reopenLast with no prior comparison shows an info toast and opens no diff", async () => { // This test must run BEFORE any compareXxx test that sets the memento. const before = allDiffTabs().length; await vscode.commands.executeCommand(COMMAND_IDS.reopenLast); await tick(40); const after = allDiffTabs().length; - assert.equal(after, before, 'no diff should open when memento is empty'); + assert.equal(after, before, "no diff should open when memento is empty"); }); - it('compareFileWithCommit with no uri and no active editor → warning, no diff', async () => { + it("compareFileWithCommit with no uri and no active editor → warning, no diff", async () => { await closeAllEditors(); await tick(20); const before = allDiffTabs().length; @@ -71,29 +71,29 @@ describe('Diffy commands — end-to-end through real QuickPick UI', () => { assert.equal(allDiffTabs().length, before); }); - it('compareFileWithCommit with a file outside any repo → warning, no diff', async () => { - const outside = vscode.Uri.file('/tmp/diffy-not-in-any-repo.txt'); + it("compareFileWithCommit with a file outside any repo → warning, no diff", async () => { + const outside = vscode.Uri.file("/tmp/diffy-not-in-any-repo.txt"); const before = allDiffTabs().length; await vscode.commands.executeCommand(COMMAND_IDS.compareFileWithCommit, outside); await tick(40); assert.equal(allDiffTabs().length, before); }); - it('compareWithWorkingCopy without a historyItem → warning, no diff', async () => { + it("compareWithWorkingCopy without a historyItem → warning, no diff", async () => { const before = allDiffTabs().length; await vscode.commands.executeCommand(COMMAND_IDS.compareWithWorkingCopy); await tick(40); assert.equal(allDiffTabs().length, before); }); - it('compareWithPrevious without a historyItem → warning, no diff', async () => { + it("compareWithPrevious without a historyItem → warning, no diff", async () => { const before = allDiffTabs().length; await vscode.commands.executeCommand(COMMAND_IDS.compareWithPrevious); await tick(40); assert.equal(allDiffTabs().length, before); }); - it('compareWithPrevious({id:initial-commit}) → revParse fails → reports error, no diff', async () => { + it("compareWithPrevious({id:initial-commit}) → revParse fails → reports error, no diff", async () => { const shas = readSeedShas(); const before = allDiffTabs().length; // commit 1 has no parent, so revParse(`${commit1}^1`) fails. @@ -104,7 +104,7 @@ describe('Diffy commands — end-to-end through real QuickPick UI', () => { assert.equal(allDiffTabs().length, before); }); - it('compareTwoCommits: top commit (sha3) vs Working Copy → diff tab opens for a.txt', async () => { + it("compareTwoCommits: top commit (sha3) vs Working Copy → diff tab opens for a.txt", async () => { const shas = readSeedShas(); const flow = vscode.commands.executeCommand(COMMAND_IDS.compareTwoCommits); @@ -117,8 +117,8 @@ describe('Diffy commands — end-to-end through real QuickPick UI', () => { const diffTab = await waitForDiffTab(); const uris = tabInputUris(diffTab); - assert.equal(uris.left.scheme, 'diffy'); - assert.equal(uris.right.scheme, 'file'); + assert.equal(uris.left.scheme, "diffy"); + assert.equal(uris.right.scheme, "file"); assert.match(uris.left.toString(), new RegExp(`diffy://commit/${shas.third}/`)); assert.match(uris.right.fsPath, /a\.txt$/); assert.match(labelStrings(diffTab), /↔ Working Copy — a\.txt$/); @@ -128,7 +128,7 @@ describe('Diffy commands — end-to-end through real QuickPick UI', () => { await flow; }); - it('compareTwoCommits: pick oldest commit (sha1) vs Working Copy → diff for top changed file', async () => { + it("compareTwoCommits: pick oldest commit (sha1) vs Working Copy → diff for top changed file", async () => { const shas = readSeedShas(); const flow = vscode.commands.executeCommand(COMMAND_IDS.compareTwoCommits); @@ -141,7 +141,7 @@ describe('Diffy commands — end-to-end through real QuickPick UI', () => { const diffTab = await waitForDiffTab(); const uris = tabInputUris(diffTab); - assert.equal(uris.left.scheme, 'diffy'); + assert.equal(uris.left.scheme, "diffy"); assert.match(uris.left.toString(), new RegExp(`diffy://commit/${shas.first}/`)); assert.match(labelStrings(diffTab), new RegExp(`^${shas.first.slice(0, 7)} ↔ Working Copy — `)); @@ -149,7 +149,7 @@ describe('Diffy commands — end-to-end through real QuickPick UI', () => { await flow; }); - it('compareWith(historyItem={id:sha1}) → SideB=Working Copy → diff opens', async () => { + it("compareWith(historyItem={id:sha1}) → SideB=Working Copy → diff opens", async () => { const shas = readSeedShas(); const flow = vscode.commands.executeCommand(COMMAND_IDS.compareWith, { id: shas.first, @@ -162,16 +162,16 @@ describe('Diffy commands — end-to-end through real QuickPick UI', () => { const diffTab = await waitForDiffTab(); const uris = tabInputUris(diffTab); - assert.equal(uris.left.scheme, 'diffy'); + assert.equal(uris.left.scheme, "diffy"); assert.match(uris.left.toString(), new RegExp(`diffy://commit/${shas.first}/`)); - assert.equal(uris.right.scheme, 'file'); + assert.equal(uris.right.scheme, "file"); assert.match(labelStrings(diffTab), new RegExp(`^${shas.first.slice(0, 7)} ↔ Working Copy — `)); await dismissQuickPick(); await flow; }); - it('compareWith(historyItem) → SideB=Index → diff has diffy://index/ on the right', async () => { + it("compareWith(historyItem) → SideB=Index → diff has diffy://index/ on the right", async () => { const shas = readSeedShas(); const flow = vscode.commands.executeCommand(COMMAND_IDS.compareWith, { historyItem: { id: shas.first }, @@ -185,8 +185,8 @@ describe('Diffy commands — end-to-end through real QuickPick UI', () => { const diffTab = await waitForDiffTab(); const uris = tabInputUris(diffTab); - assert.equal(uris.left.scheme, 'diffy'); - assert.equal(uris.right.scheme, 'diffy'); + assert.equal(uris.left.scheme, "diffy"); + assert.equal(uris.right.scheme, "diffy"); assert.match(uris.left.toString(), new RegExp(`diffy://commit/${shas.first}/`)); assert.match(uris.right.toString(), /^diffy:\/\/index\//); assert.match(labelStrings(diffTab), new RegExp(`^${shas.first.slice(0, 7)} ↔ Index — `)); @@ -195,7 +195,7 @@ describe('Diffy commands — end-to-end through real QuickPick UI', () => { await flow; }); - it('compareWith → SideB=pickRef → user picks the v0.1.0 tag → diff against that ref', async () => { + it("compareWith → SideB=pickRef → user picks the v0.1.0 tag → diff against that ref", async () => { const shas = readSeedShas(); const flow = vscode.commands.executeCommand(COMMAND_IDS.compareWith, { id: shas.third, @@ -213,20 +213,17 @@ describe('Diffy commands — end-to-end through real QuickPick UI', () => { await accept(); const diffTab = await waitForDiffTab(); const uris = tabInputUris(diffTab); - assert.equal(uris.left.scheme, 'diffy'); - assert.equal(uris.right.scheme, 'diffy'); + assert.equal(uris.left.scheme, "diffy"); + assert.equal(uris.right.scheme, "diffy"); assert.match(uris.left.toString(), new RegExp(`diffy://commit/${shas.third}/`)); assert.match(uris.right.toString(), new RegExp(`diffy://commit/${shas.second}/`)); - assert.match( - labelStrings(diffTab), - new RegExp(`^${shas.third.slice(0, 7)} ↔ ${shas.second.slice(0, 7)} — `), - ); + assert.match(labelStrings(diffTab), new RegExp(`^${shas.third.slice(0, 7)} ↔ ${shas.second.slice(0, 7)} — `)); await dismissQuickPick(); await flow; }); - it('compareWith → SideB=pickCommit → user picks commit 1 → diff against commit 1', async () => { + it("compareWith → SideB=pickCommit → user picks commit 1 → diff against commit 1", async () => { const shas = readSeedShas(); const flow = vscode.commands.executeCommand(COMMAND_IDS.compareWith, { id: shas.third, @@ -250,7 +247,7 @@ describe('Diffy commands — end-to-end through real QuickPick UI', () => { await flow; }); - it('compareWithWorkingCopy(historyItem={id:sha2}) → no SideB picker, straight to FilePicker', async () => { + it("compareWithWorkingCopy(historyItem={id:sha2}) → no SideB picker, straight to FilePicker", async () => { const shas = readSeedShas(); const flow = vscode.commands.executeCommand(COMMAND_IDS.compareWithWorkingCopy, { id: shas.second, @@ -261,8 +258,8 @@ describe('Diffy commands — end-to-end through real QuickPick UI', () => { const diffTab = await waitForDiffTab(); const uris = tabInputUris(diffTab); - assert.equal(uris.left.scheme, 'diffy'); - assert.equal(uris.right.scheme, 'file'); + assert.equal(uris.left.scheme, "diffy"); + assert.equal(uris.right.scheme, "file"); assert.match(uris.left.toString(), new RegExp(`diffy://commit/${shas.second}/`)); assert.match(labelStrings(diffTab), new RegExp(`^${shas.second.slice(0, 7)} ↔ Working Copy — `)); @@ -270,7 +267,7 @@ describe('Diffy commands — end-to-end through real QuickPick UI', () => { await flow; }); - it('compareWithPrevious(historyItem={id:sha3}) → diffs against parent (commit 2)', async () => { + it("compareWithPrevious(historyItem={id:sha3}) → diffs against parent (commit 2)", async () => { const shas = readSeedShas(); const flow = vscode.commands.executeCommand(COMMAND_IDS.compareWithPrevious, { id: shas.third, @@ -280,20 +277,17 @@ describe('Diffy commands — end-to-end through real QuickPick UI', () => { await accept(); const diffTab = await waitForDiffTab(); const uris = tabInputUris(diffTab); - assert.equal(uris.left.scheme, 'diffy'); - assert.equal(uris.right.scheme, 'diffy'); + assert.equal(uris.left.scheme, "diffy"); + assert.equal(uris.right.scheme, "diffy"); assert.match(uris.left.toString(), new RegExp(`diffy://commit/${shas.third}/`)); assert.match(uris.right.toString(), new RegExp(`diffy://commit/${shas.second}/`)); - assert.match( - labelStrings(diffTab), - new RegExp(`^${shas.third.slice(0, 7)} ↔ ${shas.second.slice(0, 7)} — `), - ); + assert.match(labelStrings(diffTab), new RegExp(`^${shas.third.slice(0, 7)} ↔ ${shas.second.slice(0, 7)} — `)); await dismissQuickPick(); await flow; }); - it('compareFileWithCommit (with explicit uri arg) → CommitPicker top → diff opens for commit 3', async () => { + it("compareFileWithCommit (with explicit uri arg) → CommitPicker top → diff opens for commit 3", async () => { const aTxt = vscode.Uri.file(`${workspaceRoot()}/a.txt`); const shas = readSeedShas(); const flow = vscode.commands.executeCommand(COMMAND_IDS.compareFileWithCommit, aTxt); @@ -303,18 +297,18 @@ describe('Diffy commands — end-to-end through real QuickPick UI', () => { const diffTab = await waitForDiffTab(); const uris = tabInputUris(diffTab); - assert.equal(uris.left.scheme, 'diffy'); + assert.equal(uris.left.scheme, "diffy"); assert.match(uris.left.toString(), new RegExp(`diffy://commit/${shas.third}/a\\.txt$`)); - assert.equal(uris.right.scheme, 'file'); + assert.equal(uris.right.scheme, "file"); assert.match(uris.right.fsPath, /a\.txt$/); assert.match(labelStrings(diffTab), new RegExp(`^${shas.third.slice(0, 7)} ↔ Working Copy — a\\.txt$`)); await flow; }); - it('compareFileWithCommit (no uri arg, falls back to active editor) → moveNext to commit 1 → diff', async () => { + it("compareFileWithCommit (no uri arg, falls back to active editor) → moveNext to commit 1 → diff", async () => { const shas = readSeedShas(); - await openFileInEditor('dir/c.txt'); + await openFileInEditor("dir/c.txt"); await tick(20); const flow = vscode.commands.executeCommand(COMMAND_IDS.compareFileWithCommit); @@ -329,7 +323,7 @@ describe('Diffy commands — end-to-end through real QuickPick UI', () => { await flow; }); - it('reopenLast: runs compareWithWorkingCopy first, then re-invokes reopenLast and gets the same FilePicker', async () => { + it("reopenLast: runs compareWithWorkingCopy first, then re-invokes reopenLast and gets the same FilePicker", async () => { const shas = readSeedShas(); // Setup: produce a known last-comparison entry by running compareWithWorkingCopy. const setup = vscode.commands.executeCommand(COMMAND_IDS.compareWithWorkingCopy, { @@ -349,21 +343,21 @@ describe('Diffy commands — end-to-end through real QuickPick UI', () => { const reDiff = await waitForDiffTab(); const reUris = tabInputUris(reDiff); assert.match(reUris.left.toString(), new RegExp(`diffy://commit/${shas.first}/`)); - assert.equal(reUris.right.scheme, 'file'); + assert.equal(reUris.right.scheme, "file"); await dismissQuickPick(); await reflow; }); - it('compareWith without a historyItem id shows a warning and opens no diff', async () => { + it("compareWith without a historyItem id shows a warning and opens no diff", async () => { const before = allDiffTabs().length; await vscode.commands.executeCommand(COMMAND_IDS.compareWith, undefined); await tick(40); const after = allDiffTabs().length; - assert.equal(after, before, 'no new diff tab should appear'); + assert.equal(after, before, "no new diff tab should appear"); }); - it('compareWithPrevious(historyItem) with the wrapped { historyItem: {...} } shape still works', async () => { + it("compareWithPrevious(historyItem) with the wrapped { historyItem: {...} } shape still works", async () => { const shas = readSeedShas(); const flow = vscode.commands.executeCommand(COMMAND_IDS.compareWithPrevious, { historyItem: { id: shas.second }, @@ -377,7 +371,7 @@ describe('Diffy commands — end-to-end through real QuickPick UI', () => { await flow; }); - it('compareWithBranch(historyItem={id:sha1}) → branch-filtered RefPicker hides current branch `main` → diff vs feature(=sha2)', async () => { + it("compareWithBranch(historyItem={id:sha1}) → branch-filtered RefPicker hides current branch `main` → diff vs feature(=sha2)", async () => { const shas = readSeedShas(); const flow = vscode.commands.executeCommand(COMMAND_IDS.compareWithBranch, { id: shas.first, @@ -391,27 +385,24 @@ describe('Diffy commands — end-to-end through real QuickPick UI', () => { const diffTab = await waitForDiffTab(); const uris = tabInputUris(diffTab); - assert.equal(uris.left.scheme, 'diffy'); - assert.equal(uris.right.scheme, 'diffy'); + assert.equal(uris.left.scheme, "diffy"); + assert.equal(uris.right.scheme, "diffy"); assert.match(uris.left.toString(), new RegExp(`diffy://commit/${shas.first}/`)); assert.match(uris.right.toString(), new RegExp(`diffy://commit/${shas.second}/`)); - assert.match( - labelStrings(diffTab), - new RegExp(`^${shas.first.slice(0, 7)} ↔ ${shas.second.slice(0, 7)} — `), - ); + assert.match(labelStrings(diffTab), new RegExp(`^${shas.first.slice(0, 7)} ↔ ${shas.second.slice(0, 7)} — `)); await dismissQuickPick(); await flow; }); - it('compareWithBranch without a historyItem → warning, no diff', async () => { + it("compareWithBranch without a historyItem → warning, no diff", async () => { const before = allDiffTabs().length; await vscode.commands.executeCommand(COMMAND_IDS.compareWithBranch); await tick(40); assert.equal(allDiffTabs().length, before); }); - it('compareWithTag(historyItem={id:sha1}) → tag-filtered RefPicker → diff vs v0.1.0(=sha2)', async () => { + it("compareWithTag(historyItem={id:sha1}) → tag-filtered RefPicker → diff vs v0.1.0(=sha2)", async () => { const shas = readSeedShas(); const flow = vscode.commands.executeCommand(COMMAND_IDS.compareWithTag, { historyItem: { id: shas.first }, @@ -424,27 +415,24 @@ describe('Diffy commands — end-to-end through real QuickPick UI', () => { const diffTab = await waitForDiffTab(); const uris = tabInputUris(diffTab); - assert.equal(uris.left.scheme, 'diffy'); - assert.equal(uris.right.scheme, 'diffy'); + assert.equal(uris.left.scheme, "diffy"); + assert.equal(uris.right.scheme, "diffy"); assert.match(uris.left.toString(), new RegExp(`diffy://commit/${shas.first}/`)); assert.match(uris.right.toString(), new RegExp(`diffy://commit/${shas.second}/`)); - assert.match( - labelStrings(diffTab), - new RegExp(`^${shas.first.slice(0, 7)} ↔ ${shas.second.slice(0, 7)} — `), - ); + assert.match(labelStrings(diffTab), new RegExp(`^${shas.first.slice(0, 7)} ↔ ${shas.second.slice(0, 7)} — `)); await dismissQuickPick(); await flow; }); - it('compareWithTag without a historyItem → warning, no diff', async () => { + it("compareWithTag without a historyItem → warning, no diff", async () => { const before = allDiffTabs().length; await vscode.commands.executeCommand(COMMAND_IDS.compareWithTag); await tick(40); assert.equal(allDiffTabs().length, before); }); - it('compareFileWithBranch(uri:a.txt) → branch RefPicker hides current branch → diff opens for feature(=sha2) vs working copy', async () => { + it("compareFileWithBranch(uri:a.txt) → branch RefPicker hides current branch → diff opens for feature(=sha2) vs working copy", async () => { const shas = readSeedShas(); const aTxt = vscode.Uri.file(`${workspaceRoot()}/a.txt`); const flow = vscode.commands.executeCommand(COMMAND_IDS.compareFileWithBranch, aTxt); @@ -455,19 +443,16 @@ describe('Diffy commands — end-to-end through real QuickPick UI', () => { const diffTab = await waitForDiffTab(); const uris = tabInputUris(diffTab); - assert.equal(uris.left.scheme, 'diffy'); + assert.equal(uris.left.scheme, "diffy"); assert.match(uris.left.toString(), new RegExp(`diffy://commit/${shas.second}/a\\.txt$`)); - assert.equal(uris.right.scheme, 'file'); + assert.equal(uris.right.scheme, "file"); assert.match(uris.right.fsPath, /a\.txt$/); - assert.match( - labelStrings(diffTab), - new RegExp(`^${shas.second.slice(0, 7)} ↔ Working Copy — a\\.txt$`), - ); + assert.match(labelStrings(diffTab), new RegExp(`^${shas.second.slice(0, 7)} ↔ Working Copy — a\\.txt$`)); await flow; }); - it('compareFileWithTag(uri:a.txt) → RefPicker shows only tags → diff opens for v0.1.0(=sha2) vs working copy', async () => { + it("compareFileWithTag(uri:a.txt) → RefPicker shows only tags → diff opens for v0.1.0(=sha2) vs working copy", async () => { const shas = readSeedShas(); const aTxt = vscode.Uri.file(`${workspaceRoot()}/a.txt`); const flow = vscode.commands.executeCommand(COMMAND_IDS.compareFileWithTag, aTxt); @@ -477,19 +462,16 @@ describe('Diffy commands — end-to-end through real QuickPick UI', () => { const diffTab = await waitForDiffTab(); const uris = tabInputUris(diffTab); - assert.equal(uris.left.scheme, 'diffy'); + assert.equal(uris.left.scheme, "diffy"); assert.match(uris.left.toString(), new RegExp(`diffy://commit/${shas.second}/a\\.txt$`)); - assert.equal(uris.right.scheme, 'file'); + assert.equal(uris.right.scheme, "file"); assert.match(uris.right.fsPath, /a\.txt$/); - assert.match( - labelStrings(diffTab), - new RegExp(`^${shas.second.slice(0, 7)} ↔ Working Copy — a\\.txt$`), - ); + assert.match(labelStrings(diffTab), new RegExp(`^${shas.second.slice(0, 7)} ↔ Working Copy — a\\.txt$`)); await flow; }); - it('compareFileWithBranch with no uri and no active editor → warning, no diff', async () => { + it("compareFileWithBranch with no uri and no active editor → warning, no diff", async () => { await closeAllEditors(); await tick(20); const before = allDiffTabs().length; @@ -498,8 +480,8 @@ describe('Diffy commands — end-to-end through real QuickPick UI', () => { assert.equal(allDiffTabs().length, before); }); - it('compareFileWithTag with a file outside any repo → warning, no diff', async () => { - const outside = vscode.Uri.file('/tmp/diffy-not-in-any-repo.txt'); + it("compareFileWithTag with a file outside any repo → warning, no diff", async () => { + const outside = vscode.Uri.file("/tmp/diffy-not-in-any-repo.txt"); const before = allDiffTabs().length; await vscode.commands.executeCommand(COMMAND_IDS.compareFileWithTag, outside); await tick(40); diff --git a/src/test/suite/contentProvider.test.ts b/src/test/suite/contentProvider.test.ts index 11c3af1..b4db715 100644 --- a/src/test/suite/contentProvider.test.ts +++ b/src/test/suite/contentProvider.test.ts @@ -1,15 +1,15 @@ -import { strict as assert } from 'node:assert'; -import * as vscode from 'vscode'; -import { readSeedShas, waitForRepoReady } from './helpers'; +import { strict as assert } from "node:assert"; +import * as vscode from "vscode"; +import { readSeedShas, waitForRepoReady } from "./helpers"; -const EXTENSION_ID = 'nimblesite.diffy'; +const EXTENSION_ID = "nimblesite.diffy"; const readDiffy = async (uriString: string): Promise => { const doc = await vscode.workspace.openTextDocument(vscode.Uri.parse(uriString)); return doc.getText(); }; -describe('DiffyContentProvider (real diffy:// URIs against the seeded repo)', () => { +describe("DiffyContentProvider (real diffy:// URIs against the seeded repo)", () => { before(async () => { const ext = vscode.extensions.getExtension(EXTENSION_ID); if (ext !== undefined && !ext.isActive) { @@ -18,7 +18,7 @@ describe('DiffyContentProvider (real diffy:// URIs against the seeded repo)', () await waitForRepoReady(); }); - it('resolves a.txt at every committed version with the expected content', async () => { + it("resolves a.txt at every committed version with the expected content", async () => { const shas = readSeedShas(); const a1 = await readDiffy(`diffy://commit/${shas.first}/a.txt`); @@ -32,10 +32,10 @@ describe('DiffyContentProvider (real diffy:// URIs against the seeded repo)', () assert.doesNotMatch(a2, /working copy uncommitted/); const a3 = await readDiffy(`diffy://commit/${shas.third}/a.txt`); - assert.equal(a3, a2, 'a.txt is unchanged from commit 2 to commit 3'); + assert.equal(a3, a2, "a.txt is unchanged from commit 2 to commit 3"); }); - it('resolves dir/c.txt at v1 and v2 with the expected text', async () => { + it("resolves dir/c.txt at v1 and v2 with the expected text", async () => { const shas = readSeedShas(); const c1 = await readDiffy(`diffy://commit/${shas.first}/dir/c.txt`); assert.match(c1, /c\.txt v1/); @@ -44,28 +44,24 @@ describe('DiffyContentProvider (real diffy:// URIs against the seeded repo)', () assert.notEqual(c1, c2); }); - it('returns the renamed file b2.txt at the third commit but not before', async () => { + it("returns the renamed file b2.txt at the third commit but not before", async () => { const shas = readSeedShas(); const b2 = await readDiffy(`diffy://commit/${shas.third}/b2.txt`); assert.match(b2, /renamed and extended/); const missing = await readDiffy(`diffy://commit/${shas.first}/b2.txt`); - assert.equal( - missing, - '', - 'b2.txt does not exist at commit 1 and should resolve to empty', - ); + assert.equal(missing, "", "b2.txt does not exist at commit 1 and should resolve to empty"); }); - it('returns empty string for a deleted path (d.txt at commit 2)', async () => { + it("returns empty string for a deleted path (d.txt at commit 2)", async () => { const shas = readSeedShas(); const deletedAtV2 = await readDiffy(`diffy://commit/${shas.second}/d.txt`); - assert.equal(deletedAtV2, ''); + assert.equal(deletedAtV2, ""); const aliveAtV1 = await readDiffy(`diffy://commit/${shas.first}/d.txt`); assert.match(aliveAtV1, /d\.txt v1/); }); - it('returns empty string for a malformed diffy URI', async () => { - const bad = await readDiffy('diffy://nonsense/whatever'); - assert.equal(bad, ''); + it("returns empty string for a malformed diffy URI", async () => { + const bad = await readDiffy("diffy://nonsense/whatever"); + assert.equal(bad, ""); }); }); diff --git a/src/test/suite/helpers.ts b/src/test/suite/helpers.ts index 56962dd..0501a33 100644 --- a/src/test/suite/helpers.ts +++ b/src/test/suite/helpers.ts @@ -1,22 +1,22 @@ -import { spawnSync } from 'node:child_process'; -import * as path from 'node:path'; -import * as vscode from 'vscode'; +import { spawnSync } from "node:child_process"; +import * as path from "node:path"; +import * as vscode from "vscode"; export const workspaceRoot = (): string => { const folder = vscode.workspace.workspaceFolders?.[0]; if (folder === undefined) { - throw new Error('No workspace folder open'); + throw new Error("No workspace folder open"); } return folder.uri.fsPath; }; export const gitOut = (args: readonly string[]): string => { - const r = spawnSync('git', [...args], { + const r = spawnSync("git", [...args], { cwd: workspaceRoot(), - encoding: 'utf8', + encoding: "utf8", }); if (r.status !== 0) { - throw new Error(`git ${args.join(' ')} failed: ${r.stderr}`); + throw new Error(`git ${args.join(" ")} failed: ${r.stderr}`); } return r.stdout.trim(); }; @@ -28,20 +28,16 @@ export interface SeedShas { } export const readSeedShas = (): SeedShas => { - const lines = gitOut(['log', '--format=%H', '--reverse']).split('\n'); + const lines = gitOut(["log", "--format=%H", "--reverse"]).split("\n"); const [first, second, third] = lines; - if ( - first === undefined || - second === undefined || - third === undefined - ) { + if (first === undefined || second === undefined || third === undefined) { throw new Error(`Expected 3 commits, found ${lines.length.toString()}`); } return { first, second, third }; }; -export const tick = async (count = 30): Promise => - { await new Promise((resolve) => { +export const tick = async (count = 30): Promise => { + await new Promise((resolve) => { let i = 0; const step = (): void => { i++; @@ -52,39 +48,39 @@ export const tick = async (count = 30): Promise => setImmediate(step); }; setImmediate(step); - }); }; + }); +}; -const wait = async (ms: number): Promise => - { await new Promise((resolve) => { +const wait = async (ms: number): Promise => { + await new Promise((resolve) => { setTimeout(resolve, ms); - }); }; + }); +}; const PICKER_RENDER_MS = 900; export const accept = async (): Promise => { await wait(PICKER_RENDER_MS); - await vscode.commands.executeCommand('workbench.action.acceptSelectedQuickOpenItem'); + await vscode.commands.executeCommand("workbench.action.acceptSelectedQuickOpenItem"); }; export const typeText = async (text: string): Promise => { await wait(PICKER_RENDER_MS); - await vscode.commands.executeCommand('type', { text }); + await vscode.commands.executeCommand("type", { text }); }; export const moveNext = async (): Promise => { await wait(PICKER_RENDER_MS); - await vscode.commands.executeCommand('workbench.action.quickOpenSelectNext'); + await vscode.commands.executeCommand("workbench.action.quickOpenSelectNext"); }; export const dismissQuickPick = async (): Promise => { await wait(50); - await vscode.commands.executeCommand('workbench.action.closeQuickOpen'); + await vscode.commands.executeCommand("workbench.action.closeQuickOpen"); }; export const allDiffTabs = (): vscode.Tab[] => - vscode.window.tabGroups.all - .flatMap((g) => g.tabs) - .filter((t) => t.input instanceof vscode.TabInputTextDiff); + vscode.window.tabGroups.all.flatMap((g) => g.tabs).filter((t) => t.input instanceof vscode.TabInputTextDiff); export const waitForDiffTab = async ({ timeoutMs = 8000, @@ -99,7 +95,7 @@ export const waitForDiffTab = async ({ } const timer = setTimeout(() => { sub.dispose(); - reject(new Error('Timed out waiting for diff tab')); + reject(new Error("Timed out waiting for diff tab")); }, timeoutMs); const sub = vscode.window.tabGroups.onDidChangeTabs(() => { const t = allDiffTabs()[0]; @@ -113,7 +109,7 @@ export const waitForDiffTab = async ({ }; export const closeAllEditors = async (): Promise => { - await vscode.commands.executeCommand('workbench.action.closeAllEditors'); + await vscode.commands.executeCommand("workbench.action.closeAllEditors"); await tick(5); }; @@ -123,9 +119,7 @@ export const openFileInEditor = async (relPath: string): Promise { +export const tabInputUris = (tab: vscode.Tab): { left: vscode.Uri; right: vscode.Uri } => { if (!(tab.input instanceof vscode.TabInputTextDiff)) { throw new Error(`Tab is not a TabInputTextDiff: ${tab.label}`); } @@ -142,9 +136,9 @@ interface GitExtensionShape { } export const waitForRepoReady = async (timeoutMs = 15000): Promise => { - const ext = vscode.extensions.getExtension('vscode.git'); + const ext = vscode.extensions.getExtension("vscode.git"); if (ext === undefined) { - throw new Error('vscode.git extension not present'); + throw new Error("vscode.git extension not present"); } if (!ext.isActive) { await ext.activate(); @@ -156,7 +150,7 @@ export const waitForRepoReady = async (timeoutMs = 15000): Promise => { await new Promise((resolve, reject) => { const timer = setTimeout(() => { sub.dispose(); - reject(new Error('vscode.git never opened the seeded repo')); + reject(new Error("vscode.git never opened the seeded repo")); }, timeoutMs); const sub = api.onDidOpenRepository(() => { clearTimeout(timer); diff --git a/src/test/suite/index.ts b/src/test/suite/index.ts index d8d8308..479d42c 100644 --- a/src/test/suite/index.ts +++ b/src/test/suite/index.ts @@ -1,16 +1,16 @@ -import * as path from 'node:path'; -import Mocha from 'mocha'; -import { glob } from 'glob'; +import * as path from "node:path"; +import Mocha from "mocha"; +import { glob } from "glob"; export const run = async (): Promise => { const mocha = new Mocha({ - ui: 'bdd', + ui: "bdd", color: true, timeout: 60000, - reporter: 'spec', + reporter: "spec", }); const testsRoot = __dirname; - const files = await glob('**/*.test.js', { cwd: testsRoot }); + const files = await glob("**/*.test.js", { cwd: testsRoot }); for (const f of files) { mocha.addFile(path.resolve(testsRoot, f)); } diff --git a/src/test/unit/format.test.ts b/src/test/unit/format.test.ts index 8077c24..b86ae15 100644 --- a/src/test/unit/format.test.ts +++ b/src/test/unit/format.test.ts @@ -1,143 +1,139 @@ -import { strict as assert } from 'node:assert'; -import { formatRelative } from '../../ui/format/relativeTime'; -import { - formatCounts, - mergeChangedFilesWithStats, - statusBadge, -} from '../../ui/format/fileItem'; -import { refTypeLabel } from '../../ui/format/refLabel'; -import type { ChangedFile, DiffStat, Ref } from '../../git/types'; +import { strict as assert } from "node:assert"; +import { formatRelative } from "../../ui/format/relativeTime"; +import { formatCounts, mergeChangedFilesWithStats, statusBadge } from "../../ui/format/fileItem"; +import { refTypeLabel } from "../../ui/format/refLabel"; +import type { ChangedFile, DiffStat, Ref } from "../../git/types"; const HOUR = 60 * 60; const DAY = 24 * HOUR; -describe('formatRelative', () => { +describe("formatRelative", () => { it('returns "just now" for < 1 minute', () => { - assert.equal(formatRelative(100, 100), 'just now'); - assert.equal(formatRelative(100, 130), 'just now'); - assert.equal(formatRelative(100, 159), 'just now'); + assert.equal(formatRelative(100, 100), "just now"); + assert.equal(formatRelative(100, 130), "just now"); + assert.equal(formatRelative(100, 159), "just now"); }); it('clamps negative deltas (future timestamps) to "just now"', () => { - assert.equal(formatRelative(200, 100), 'just now'); + assert.equal(formatRelative(200, 100), "just now"); }); - it('formats minutes when < 60 min', () => { - assert.equal(formatRelative(0, 60), '1m ago'); - assert.equal(formatRelative(0, 30 * 60), '30m ago'); - assert.equal(formatRelative(0, 59 * 60), '59m ago'); + it("formats minutes when < 60 min", () => { + assert.equal(formatRelative(0, 60), "1m ago"); + assert.equal(formatRelative(0, 30 * 60), "30m ago"); + assert.equal(formatRelative(0, 59 * 60), "59m ago"); }); - it('formats hours when < 24 h', () => { - assert.equal(formatRelative(0, HOUR), '1h ago'); - assert.equal(formatRelative(0, 12 * HOUR), '12h ago'); - assert.equal(formatRelative(0, 23 * HOUR), '23h ago'); + it("formats hours when < 24 h", () => { + assert.equal(formatRelative(0, HOUR), "1h ago"); + assert.equal(formatRelative(0, 12 * HOUR), "12h ago"); + assert.equal(formatRelative(0, 23 * HOUR), "23h ago"); }); - it('formats days for older timestamps', () => { - assert.equal(formatRelative(0, DAY), '1d ago'); - assert.equal(formatRelative(0, 7 * DAY), '7d ago'); - assert.equal(formatRelative(0, 365 * DAY), '365d ago'); + it("formats days for older timestamps", () => { + assert.equal(formatRelative(0, DAY), "1d ago"); + assert.equal(formatRelative(0, 7 * DAY), "7d ago"); + assert.equal(formatRelative(0, 365 * DAY), "365d ago"); }); }); -describe('statusBadge', () => { - it('returns single letter for A/M/D', () => { - assert.equal(statusBadge({ status: 'A', path: 'x' }), 'A'); - assert.equal(statusBadge({ status: 'M', path: 'x' }), 'M'); - assert.equal(statusBadge({ status: 'D', path: 'x' }), 'D'); +describe("statusBadge", () => { + it("returns single letter for A/M/D", () => { + assert.equal(statusBadge({ status: "A", path: "x" }), "A"); + assert.equal(statusBadge({ status: "M", path: "x" }), "M"); + assert.equal(statusBadge({ status: "D", path: "x" }), "D"); }); - it('appends similarity for R', () => { + it("appends similarity for R", () => { const r: ChangedFile = { - status: 'R', - path: 'new', - oldPath: 'old', + status: "R", + path: "new", + oldPath: "old", similarity: 87, }; - assert.equal(statusBadge(r), 'R87'); + assert.equal(statusBadge(r), "R87"); }); - it('appends similarity for C', () => { + it("appends similarity for C", () => { const c: ChangedFile = { - status: 'C', - path: 'dst', - oldPath: 'src', + status: "C", + path: "dst", + oldPath: "src", similarity: 100, }; - assert.equal(statusBadge(c), 'C100'); + assert.equal(statusBadge(c), "C100"); }); - it('falls back to 0 when similarity is missing on R/C', () => { - const r = { status: 'R', path: 'new', oldPath: 'old' } as ChangedFile; - assert.equal(statusBadge(r), 'R0'); - const c = { status: 'C', path: 'dst', oldPath: 'src' } as ChangedFile; - assert.equal(statusBadge(c), 'C0'); + it("falls back to 0 when similarity is missing on R/C", () => { + const r = { status: "R", path: "new", oldPath: "old" } as ChangedFile; + assert.equal(statusBadge(r), "R0"); + const c = { status: "C", path: "dst", oldPath: "src" } as ChangedFile; + assert.equal(statusBadge(c), "C0"); }); }); -describe('formatCounts', () => { - it('formats numeric +N -M for non-binary stats', () => { - const s: DiffStat = { path: 'x', added: 12, deleted: 3, binary: false }; - assert.equal(formatCounts(s), '+12 -3'); +describe("formatCounts", () => { + it("formats numeric +N -M for non-binary stats", () => { + const s: DiffStat = { path: "x", added: 12, deleted: 3, binary: false }; + assert.equal(formatCounts(s), "+12 -3"); }); it('reports "binary" for binary stats', () => { - const s: DiffStat = { path: 'x', added: 0, deleted: 0, binary: true }; - assert.equal(formatCounts(s), 'binary'); + const s: DiffStat = { path: "x", added: 0, deleted: 0, binary: true }; + assert.equal(formatCounts(s), "binary"); }); - it('reports 0/0 cleanly when there are no changes', () => { - const s: DiffStat = { path: 'x', added: 0, deleted: 0, binary: false }; - assert.equal(formatCounts(s), '+0 -0'); + it("reports 0/0 cleanly when there are no changes", () => { + const s: DiffStat = { path: "x", added: 0, deleted: 0, binary: false }; + assert.equal(formatCounts(s), "+0 -0"); }); }); -describe('mergeChangedFilesWithStats', () => { - it('joins by path', () => { - const files: ChangedFile[] = [{ status: 'M', path: 'a.txt' }]; - const stats: DiffStat[] = [{ path: 'a.txt', added: 2, deleted: 1, binary: false }]; +describe("mergeChangedFilesWithStats", () => { + it("joins by path", () => { + const files: ChangedFile[] = [{ status: "M", path: "a.txt" }]; + const stats: DiffStat[] = [{ path: "a.txt", added: 2, deleted: 1, binary: false }]; const r = mergeChangedFilesWithStats(files, stats); assert.equal(r.length, 1); assert.deepEqual(r[0], { file: files[0], stat: stats[0] }); }); - it('falls back to zero stats when no matching numstat row exists', () => { - const files: ChangedFile[] = [{ status: 'A', path: 'new.txt' }]; + it("falls back to zero stats when no matching numstat row exists", () => { + const files: ChangedFile[] = [{ status: "A", path: "new.txt" }]; const r = mergeChangedFilesWithStats(files, []); assert.equal(r.length, 1); assert.deepEqual(r[0]?.stat, { - path: 'new.txt', + path: "new.txt", added: 0, deleted: 0, binary: false, }); }); - it('preserves order from the name-status list', () => { + it("preserves order from the name-status list", () => { const files: ChangedFile[] = [ - { status: 'M', path: 'b' }, - { status: 'M', path: 'a' }, + { status: "M", path: "b" }, + { status: "M", path: "a" }, ]; const stats: DiffStat[] = [ - { path: 'a', added: 1, deleted: 1, binary: false }, - { path: 'b', added: 2, deleted: 2, binary: false }, + { path: "a", added: 1, deleted: 1, binary: false }, + { path: "b", added: 2, deleted: 2, binary: false }, ]; const r = mergeChangedFilesWithStats(files, stats); - assert.equal(r[0]?.file.path, 'b'); - assert.equal(r[1]?.file.path, 'a'); + assert.equal(r[0]?.file.path, "b"); + assert.equal(r[1]?.file.path, "a"); }); - it('handles rename entries (new path differs from old)', () => { + it("handles rename entries (new path differs from old)", () => { const file: ChangedFile = { - status: 'R', - path: 'b2.txt', - oldPath: 'b.txt', + status: "R", + path: "b2.txt", + oldPath: "b.txt", similarity: 100, }; const stat: DiffStat = { - path: 'b2.txt', - oldPath: 'b.txt', + path: "b2.txt", + oldPath: "b.txt", added: 1, deleted: 0, binary: false, @@ -147,24 +143,24 @@ describe('mergeChangedFilesWithStats', () => { }); }); -describe('refTypeLabel', () => { +describe("refTypeLabel", () => { it('returns "Branch" for branches', () => { - const r: Ref = { name: 'main', fullName: 'refs/heads/main', sha: 'a', type: 'branch' }; - assert.equal(refTypeLabel(r), 'Branch'); + const r: Ref = { name: "main", fullName: "refs/heads/main", sha: "a", type: "branch" }; + assert.equal(refTypeLabel(r), "Branch"); }); it('returns "Tag" for tags', () => { - const r: Ref = { name: 'v1', fullName: 'refs/tags/v1', sha: 'a', type: 'tag' }; - assert.equal(refTypeLabel(r), 'Tag'); + const r: Ref = { name: "v1", fullName: "refs/tags/v1", sha: "a", type: "tag" }; + assert.equal(refTypeLabel(r), "Tag"); }); it('returns "Ref" for other ref kinds', () => { const r: Ref = { - name: 'origin/main', - fullName: 'refs/remotes/origin/main', - sha: 'a', - type: 'other', + name: "origin/main", + fullName: "refs/remotes/origin/main", + sha: "a", + type: "other", }; - assert.equal(refTypeLabel(r), 'Ref'); + assert.equal(refTypeLabel(r), "Ref"); }); }); diff --git a/src/test/unit/historyItem.test.ts b/src/test/unit/historyItem.test.ts index 37654e1..770174b 100644 --- a/src/test/unit/historyItem.test.ts +++ b/src/test/unit/historyItem.test.ts @@ -1,45 +1,45 @@ -import { strict as assert } from 'node:assert'; -import { extractHistoryItemSha } from '../../commands/historyItem'; +import { strict as assert } from "node:assert"; +import { extractHistoryItemSha } from "../../commands/historyItem"; -const FULL_SHA = '02920d872f11ed22afacf3966cb1da30b72dfe94'; +const FULL_SHA = "02920d872f11ed22afacf3966cb1da30b72dfe94"; -describe('extractHistoryItemSha', () => { - it('returns the value when arg is a non-empty string', () => { +describe("extractHistoryItemSha", () => { + it("returns the value when arg is a non-empty string", () => { assert.equal(extractHistoryItemSha(FULL_SHA), FULL_SHA); - assert.equal(extractHistoryItemSha('main'), 'main'); + assert.equal(extractHistoryItemSha("main"), "main"); }); - it('returns undefined for an empty string', () => { - assert.equal(extractHistoryItemSha(''), undefined); + it("returns undefined for an empty string", () => { + assert.equal(extractHistoryItemSha(""), undefined); }); - it('returns undefined for null / undefined / numbers / booleans', () => { + it("returns undefined for null / undefined / numbers / booleans", () => { assert.equal(extractHistoryItemSha(null), undefined); assert.equal(extractHistoryItemSha(undefined), undefined); assert.equal(extractHistoryItemSha(42), undefined); assert.equal(extractHistoryItemSha(true), undefined); }); - it('extracts id directly from a SourceControlHistoryItem-shaped object', () => { + it("extracts id directly from a SourceControlHistoryItem-shaped object", () => { assert.equal(extractHistoryItemSha({ id: FULL_SHA }), FULL_SHA); - assert.equal(extractHistoryItemSha({ id: 'main', message: 'x' }), 'main'); + assert.equal(extractHistoryItemSha({ id: "main", message: "x" }), "main"); }); - it('unwraps a { historyItem: { id } } shape', () => { + it("unwraps a { historyItem: { id } } shape", () => { assert.equal(extractHistoryItemSha({ historyItem: { id: FULL_SHA } }), FULL_SHA); }); - it('returns undefined when wrapper.historyItem is missing or not an object', () => { - assert.equal(extractHistoryItemSha({ something: 'else' }), undefined); + it("returns undefined when wrapper.historyItem is missing or not an object", () => { + assert.equal(extractHistoryItemSha({ something: "else" }), undefined); assert.equal(extractHistoryItemSha({ historyItem: null }), undefined); - assert.equal(extractHistoryItemSha({ historyItem: 'not-an-object' }), undefined); + assert.equal(extractHistoryItemSha({ historyItem: "not-an-object" }), undefined); }); - it('returns undefined when historyItem.id is empty / missing / wrong type', () => { - assert.equal(extractHistoryItemSha({ id: '' }), undefined); + it("returns undefined when historyItem.id is empty / missing / wrong type", () => { + assert.equal(extractHistoryItemSha({ id: "" }), undefined); assert.equal(extractHistoryItemSha({ id: 42 }), undefined); assert.equal(extractHistoryItemSha({ historyItem: {} }), undefined); - assert.equal(extractHistoryItemSha({ historyItem: { id: '' } }), undefined); + assert.equal(extractHistoryItemSha({ historyItem: { id: "" } }), undefined); assert.equal(extractHistoryItemSha({ historyItem: { id: 0 } }), undefined); }); }); diff --git a/src/test/unit/menus.test.ts b/src/test/unit/menus.test.ts index ed22a9f..6b8caf3 100644 --- a/src/test/unit/menus.test.ts +++ b/src/test/unit/menus.test.ts @@ -1,59 +1,43 @@ -import { strict as assert } from 'node:assert'; -import { readFileSync } from 'node:fs'; -import { resolve } from 'node:path'; -import { - COMMAND_IDS, - MENU_IDS, - MENU_WHEN, - TITLE_PREFIX, -} from '../../constants'; -import { COMMAND_TITLES, buildMenuManifest } from '../../menus'; - -const PROPOSED_API_HISTORY_ITEM_MENU = 'contribSourceControlHistoryItemMenu'; - -const repoRoot = resolve(__dirname, '..', '..', '..'); +import { strict as assert } from "node:assert"; +import { readFileSync } from "node:fs"; +import { resolve } from "node:path"; +import { COMMAND_IDS, MENU_IDS, MENU_WHEN, TITLE_PREFIX } from "../../constants"; +import { COMMAND_TITLES, buildMenuManifest } from "../../menus"; + +const PROPOSED_API_HISTORY_ITEM_MENU = "contribSourceControlHistoryItemMenu"; + +const repoRoot = resolve(__dirname, "..", "..", ".."); const readPackageJson = (): Record => - JSON.parse(readFileSync(resolve(repoRoot, 'package.json'), 'utf8')) as Record< - string, - unknown - >; + JSON.parse(readFileSync(resolve(repoRoot, "package.json"), "utf8")) as Record; const getContributes = (pkg: Record): Record => { - const c = pkg['contributes']; - if (typeof c !== 'object' || c === null) { - throw new Error('package.json: contributes missing'); + const c = pkg["contributes"]; + if (typeof c !== "object" || c === null) { + throw new Error("package.json: contributes missing"); } return c as Record; }; -const getMenus = ( - contributes: Record, -): Record => { - const m = contributes['menus']; - if (typeof m !== 'object' || m === null) { - throw new Error('package.json: contributes.menus missing'); +const getMenus = (contributes: Record): Record => { + const m = contributes["menus"]; + if (typeof m !== "object" || m === null) { + throw new Error("package.json: contributes.menus missing"); } return m as Record; }; -const titlePrefixPattern = new RegExp( - `^${TITLE_PREFIX.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')} `, -); +const titlePrefixPattern = new RegExp(`^${TITLE_PREFIX.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")} `); -describe('menu manifest — single source of truth', () => { - it('every COMMAND_ID has a human title in COMMAND_TITLES', () => { +describe("menu manifest — single source of truth", () => { + it("every COMMAND_ID has a human title in COMMAND_TITLES", () => { for (const id of Object.values(COMMAND_IDS)) { const title = COMMAND_TITLES[id]; assert.ok(title !== undefined, `missing title for command ${id}`); - assert.match( - title, - titlePrefixPattern, - `title for ${id} must start with "${TITLE_PREFIX} "`, - ); + assert.match(title, titlePrefixPattern, `title for ${id} must start with "${TITLE_PREFIX} "`); } }); - it('manifest contributes commands to the four user-discoverable menus', () => { + it("manifest contributes commands to the four user-discoverable menus", () => { const manifest = buildMenuManifest(); const menuIds = Object.keys(manifest.menus); assert.deepEqual( @@ -64,11 +48,11 @@ describe('menu manifest — single source of truth', () => { MENU_IDS.scmHistoryItem, MENU_IDS.scmResourceState, ].sort(), - 'menu IDs must match VSCode contribution points (note: singular scm/historyItem/context)', + "menu IDs must match VSCode contribution points (note: singular scm/historyItem/context)" ); }); - it('SCM history item menu contains all five commit-level Diffy commands', () => { + it("SCM history item menu contains all five commit-level Diffy commands", () => { const entries = buildMenuManifest().menus[MENU_IDS.scmHistoryItem]; assert.ok(entries !== undefined); const cmds = entries.map((e) => e.command); @@ -85,29 +69,25 @@ describe('menu manifest — single source of truth', () => { } }); - it('editor/title/context and explorer/context and scm/resourceState/context all expose the three file-level Diffy commands', () => { + it("editor/title/context and explorer/context and scm/resourceState/context all expose the three file-level Diffy commands", () => { const m = buildMenuManifest().menus; const fileLevel = [ COMMAND_IDS.compareFileWithCommit, COMMAND_IDS.compareFileWithBranch, COMMAND_IDS.compareFileWithTag, ]; - for (const menuId of [ - MENU_IDS.editorTitleContext, - MENU_IDS.explorerContext, - MENU_IDS.scmResourceState, - ]) { + for (const menuId of [MENU_IDS.editorTitleContext, MENU_IDS.explorerContext, MENU_IDS.scmResourceState]) { const entries = m[menuId]; assert.ok(entries !== undefined, `missing ${menuId}`); assert.deepEqual( entries.map((e) => e.command), fileLevel, - `${menuId} must list the three file-level commands in order`, + `${menuId} must list the three file-level commands in order` ); } }); - it('commandPalette block hides commit-level entries (they need a history-item arg)', () => { + it("commandPalette block hides commit-level entries (they need a history-item arg)", () => { const palette = buildMenuManifest().commandPalette; const hidden = palette.map((e) => e.command); for (const cmd of [ @@ -125,7 +105,7 @@ describe('menu manifest — single source of truth', () => { } }); - it('package.json contributes.menus is byte-equal to what the manifest would write', () => { + it("package.json contributes.menus is byte-equal to what the manifest would write", () => { const manifest = buildMenuManifest(); const pkg = readPackageJson(); const contributes = getContributes(pkg); @@ -140,21 +120,21 @@ describe('menu manifest — single source of truth', () => { when: e.when, group: e.group, })), - `package.json ${menuId} drifted from src/menus.ts — run 'npm run sync:menus'`, + `package.json ${menuId} drifted from src/menus.ts — run 'npm run sync:menus'` ); } assert.deepEqual( pkgMenus[MENU_IDS.commandPalette], manifest.commandPalette.map((e) => ({ command: e.command, when: e.when })), - 'package.json commandPalette block drifted from src/menus.ts', + "package.json commandPalette block drifted from src/menus.ts" ); }); - it('package.json contributes.commands lists every COMMAND_TITLES entry exactly', () => { + it("package.json contributes.commands lists every COMMAND_TITLES entry exactly", () => { const pkg = readPackageJson(); const contributes = getContributes(pkg); - const cmds = contributes['commands'] as readonly { + const cmds = contributes["commands"] as readonly { readonly command: string; readonly title: string; }[]; @@ -164,23 +144,19 @@ describe('menu manifest — single source of truth', () => { command, title, })); - assert.deepEqual( - cmds, - expected, - 'package.json commands drifted — run npm run sync:menus', - ); + assert.deepEqual(cmds, expected, "package.json commands drifted — run npm run sync:menus"); }); - it('package.json declares the contribSourceControlHistoryItemMenu proposed API (required for the commit-row context menu)', () => { + it("package.json declares the contribSourceControlHistoryItemMenu proposed API (required for the commit-row context menu)", () => { const pkg = readPackageJson(); - const proposals = pkg['enabledApiProposals']; + const proposals = pkg["enabledApiProposals"]; assert.ok( Array.isArray(proposals), - 'package.json must declare enabledApiProposals so SCM Graph commit menus actually appear', + "package.json must declare enabledApiProposals so SCM Graph commit menus actually appear" ); assert.ok( (proposals as readonly string[]).includes(PROPOSED_API_HISTORY_ITEM_MENU), - `enabledApiProposals must include ${PROPOSED_API_HISTORY_ITEM_MENU}`, + `enabledApiProposals must include ${PROPOSED_API_HISTORY_ITEM_MENU}` ); }); }); diff --git a/src/test/unit/parsers.test.ts b/src/test/unit/parsers.test.ts index 39968de..15f0edd 100644 --- a/src/test/unit/parsers.test.ts +++ b/src/test/unit/parsers.test.ts @@ -1,191 +1,162 @@ -import { strict as assert } from 'node:assert'; -import { GIT_ERROR_KINDS, REF_TYPES } from '../../constants'; -import { - parseLog, - parseNameStatus, - parseNumstat, - parseRefs, -} from '../../git/parsers'; -import { expectErr, expectOk } from '../../result'; - -const NUL = '\x00'; -const TAB = '\t'; - -const logRecord = ( - sha: string, - short: string, - author: string, - at: string, - subject: string, -): string => [sha, short, author, at, subject].join(NUL); - -describe('parseLog', () => { - it('returns empty array for empty input', () => { - const r = parseLog(''); +import { strict as assert } from "node:assert"; +import { GIT_ERROR_KINDS, REF_TYPES } from "../../constants"; +import { parseLog, parseNameStatus, parseNumstat, parseRefs } from "../../git/parsers"; +import { expectErr, expectOk } from "../../result"; + +const NUL = "\x00"; +const TAB = "\t"; + +const logRecord = (sha: string, short: string, author: string, at: string, subject: string): string => + [sha, short, author, at, subject].join(NUL); + +describe("parseLog", () => { + it("returns empty array for empty input", () => { + const r = parseLog(""); expectOk(r); assert.deepEqual(r.value, []); }); - it('parses a single commit with trailing NUL', () => { - const stdout = - logRecord('abc123def456', 'abc123d', 'Alice', '1700000000', 'init') + NUL; + it("parses a single commit with trailing NUL", () => { + const stdout = logRecord("abc123def456", "abc123d", "Alice", "1700000000", "init") + NUL; const r = parseLog(stdout); expectOk(r); assert.deepEqual(r.value, [ { - sha: 'abc123def456', - shortSha: 'abc123d', - author: 'Alice', + sha: "abc123def456", + shortSha: "abc123d", + author: "Alice", authorTime: 1700000000, - subject: 'init', + subject: "init", }, ]); }); - it('parses two commits separated by NUL', () => { - const a = logRecord('aaaa1111', 'aaaa111', 'Alice', '1700000000', 'first'); - const b = logRecord( - 'bbbb2222', - 'bbbb222', - 'Bob', - '1700000100', - 'second commit', - ); + it("parses two commits separated by NUL", () => { + const a = logRecord("aaaa1111", "aaaa111", "Alice", "1700000000", "first"); + const b = logRecord("bbbb2222", "bbbb222", "Bob", "1700000100", "second commit"); const r = parseLog(`${a}${NUL}${b}${NUL}`); expectOk(r); assert.deepEqual(r.value, [ { - sha: 'aaaa1111', - shortSha: 'aaaa111', - author: 'Alice', + sha: "aaaa1111", + shortSha: "aaaa111", + author: "Alice", authorTime: 1700000000, - subject: 'first', + subject: "first", }, { - sha: 'bbbb2222', - shortSha: 'bbbb222', - author: 'Bob', + sha: "bbbb2222", + shortSha: "bbbb222", + author: "Bob", authorTime: 1700000100, - subject: 'second commit', + subject: "second commit", }, ]); }); - it('parses without trailing NUL when field count matches', () => { - const stdout = logRecord('abcd1234', 'abcd123', 'Alice', '1700000000', 'init'); + it("parses without trailing NUL when field count matches", () => { + const stdout = logRecord("abcd1234", "abcd123", "Alice", "1700000000", "init"); const r = parseLog(stdout); expectOk(r); assert.equal(r.value.length, 1); }); - it('preserves an empty subject', () => { - const stdout = - logRecord('abcd1234', 'abcd123', 'Alice', '1700000000', '') + NUL; + it("preserves an empty subject", () => { + const stdout = logRecord("abcd1234", "abcd123", "Alice", "1700000000", "") + NUL; const r = parseLog(stdout); expectOk(r); assert.deepEqual(r.value, [ { - sha: 'abcd1234', - shortSha: 'abcd123', - author: 'Alice', + sha: "abcd1234", + shortSha: "abcd123", + author: "Alice", authorTime: 1700000000, - subject: '', + subject: "", }, ]); }); - it('preserves unicode and spaces in author/subject', () => { - const stdout = - logRecord( - 'abcd1234', - 'abcd123', - 'Élise Müller', - '1700000000', - 'feat: 日本語 fix ⚡', - ) + NUL; + it("preserves unicode and spaces in author/subject", () => { + const stdout = logRecord("abcd1234", "abcd123", "Élise Müller", "1700000000", "feat: 日本語 fix ⚡") + NUL; const r = parseLog(stdout); expectOk(r); assert.deepEqual(r.value, [ { - sha: 'abcd1234', - shortSha: 'abcd123', - author: 'Élise Müller', + sha: "abcd1234", + shortSha: "abcd123", + author: "Élise Müller", authorTime: 1700000000, - subject: 'feat: 日本語 fix ⚡', + subject: "feat: 日本語 fix ⚡", }, ]); }); - it('errors when field count is not a multiple of 5', () => { + it("errors when field count is not a multiple of 5", () => { const r = parseLog(`a${NUL}b${NUL}c${NUL}d${NUL}`); expectErr(r); assert.equal(r.error.kind, GIT_ERROR_KINDS.parseError); assert.match(r.error.message, /multiple of 5/); }); - it('errors when timestamp is not numeric', () => { - const stdout = logRecord('a', 'a', 'Alice', 'NOT_A_NUMBER', 'x') + NUL; + it("errors when timestamp is not numeric", () => { + const stdout = logRecord("a", "a", "Alice", "NOT_A_NUMBER", "x") + NUL; const r = parseLog(stdout); expectErr(r); assert.match(r.error.message, /timestamp/); }); - it('errors when timestamp is empty', () => { - const stdout = logRecord('a', 'a', 'Alice', '', 'x') + NUL; + it("errors when timestamp is empty", () => { + const stdout = logRecord("a", "a", "Alice", "", "x") + NUL; const r = parseLog(stdout); expectErr(r); assert.match(r.error.message, /timestamp/); }); - it('errors when timestamp has leading/trailing whitespace', () => { - const stdout = logRecord('a', 'a', 'Alice', ' 1700000000', 'x') + NUL; + it("errors when timestamp has leading/trailing whitespace", () => { + const stdout = logRecord("a", "a", "Alice", " 1700000000", "x") + NUL; const r = parseLog(stdout); expectErr(r); }); }); -describe('parseNameStatus', () => { - it('returns empty array for empty input', () => { - const r = parseNameStatus(''); +describe("parseNameStatus", () => { + it("returns empty array for empty input", () => { + const r = parseNameStatus(""); expectOk(r); assert.deepEqual(r.value, []); }); - it('parses an Added file', () => { + it("parses an Added file", () => { const r = parseNameStatus(`A${NUL}new.txt${NUL}`); expectOk(r); - assert.deepEqual(r.value, [{ status: 'A', path: 'new.txt' }]); + assert.deepEqual(r.value, [{ status: "A", path: "new.txt" }]); }); - it('parses a Modified file', () => { + it("parses a Modified file", () => { const r = parseNameStatus(`M${NUL}a.txt${NUL}`); expectOk(r); - assert.deepEqual(r.value, [{ status: 'M', path: 'a.txt' }]); + assert.deepEqual(r.value, [{ status: "M", path: "a.txt" }]); }); - it('parses a Deleted file', () => { + it("parses a Deleted file", () => { const r = parseNameStatus(`D${NUL}gone.txt${NUL}`); expectOk(r); - assert.deepEqual(r.value, [{ status: 'D', path: 'gone.txt' }]); + assert.deepEqual(r.value, [{ status: "D", path: "gone.txt" }]); }); - it('parses a Rename with similarity', () => { + it("parses a Rename with similarity", () => { const r = parseNameStatus(`R100${NUL}old.txt${NUL}new.txt${NUL}`); expectOk(r); - assert.deepEqual(r.value, [ - { status: 'R', path: 'new.txt', oldPath: 'old.txt', similarity: 100 }, - ]); + assert.deepEqual(r.value, [{ status: "R", path: "new.txt", oldPath: "old.txt", similarity: 100 }]); }); - it('parses a Copy with similarity', () => { + it("parses a Copy with similarity", () => { const r = parseNameStatus(`C75${NUL}src.txt${NUL}dst.txt${NUL}`); expectOk(r); - assert.deepEqual(r.value, [ - { status: 'C', path: 'dst.txt', oldPath: 'src.txt', similarity: 75 }, - ]); + assert.deepEqual(r.value, [{ status: "C", path: "dst.txt", oldPath: "src.txt", similarity: 75 }]); }); - it('parses a mixed batch', () => { + it("parses a mixed batch", () => { const stdout = `A${NUL}added.txt${NUL}` + `M${NUL}modified.txt${NUL}` + @@ -195,89 +166,83 @@ describe('parseNameStatus', () => { const r = parseNameStatus(stdout); expectOk(r); assert.deepEqual(r.value, [ - { status: 'A', path: 'added.txt' }, - { status: 'M', path: 'modified.txt' }, - { status: 'D', path: 'deleted.txt' }, - { status: 'R', path: 'now.txt', oldPath: 'was.txt', similarity: 98 }, - { status: 'C', path: 'dst.txt', oldPath: 'src.txt', similarity: 50 }, + { status: "A", path: "added.txt" }, + { status: "M", path: "modified.txt" }, + { status: "D", path: "deleted.txt" }, + { status: "R", path: "now.txt", oldPath: "was.txt", similarity: 98 }, + { status: "C", path: "dst.txt", oldPath: "src.txt", similarity: 50 }, ]); }); - it('preserves spaces, unicode, and quotes in paths', () => { + it("preserves spaces, unicode, and quotes in paths", () => { const stdout = `M${NUL}path with spaces/日本語 "name".txt${NUL}`; const r = parseNameStatus(stdout); expectOk(r); - assert.deepEqual(r.value, [ - { status: 'M', path: 'path with spaces/日本語 "name".txt' }, - ]); + assert.deepEqual(r.value, [{ status: "M", path: 'path with spaces/日本語 "name".txt' }]); }); - it('errors on unknown status letter', () => { + it("errors on unknown status letter", () => { const r = parseNameStatus(`X${NUL}a.txt${NUL}`); expectErr(r); assert.match(r.error.message, /unknown status/); }); - it('errors when A/M/D has trailing chars', () => { + it("errors when A/M/D has trailing chars", () => { const r = parseNameStatus(`A100${NUL}a.txt${NUL}`); expectErr(r); assert.match(r.error.message, /extra chars/); }); - it('errors on rename without similarity digits', () => { + it("errors on rename without similarity digits", () => { const r = parseNameStatus(`R${NUL}old${NUL}new${NUL}`); expectErr(r); assert.match(r.error.message, /similarity/); }); - it('errors on rename with non-digit similarity', () => { + it("errors on rename with non-digit similarity", () => { const r = parseNameStatus(`R1x0${NUL}old${NUL}new${NUL}`); expectErr(r); assert.match(r.error.message, /similarity/); }); - it('errors on truncated rename (missing new path)', () => { + it("errors on truncated rename (missing new path)", () => { const r = parseNameStatus(`R100${NUL}old.txt${NUL}`); expectErr(r); assert.match(r.error.message, /missing paths/); }); - it('errors when simple status is missing its path', () => { + it("errors when simple status is missing its path", () => { const r = parseNameStatus(`M${NUL}`); expectErr(r); }); }); -describe('parseNumstat', () => { - it('returns empty array for empty input', () => { - const r = parseNumstat(''); +describe("parseNumstat", () => { + it("returns empty array for empty input", () => { + const r = parseNumstat(""); expectOk(r); assert.deepEqual(r.value, []); }); - it('parses a regular numstat record', () => { + it("parses a regular numstat record", () => { const r = parseNumstat(`12${TAB}3${TAB}hello.txt${NUL}`); expectOk(r); - assert.deepEqual(r.value, [ - { path: 'hello.txt', added: 12, deleted: 3, binary: false }, - ]); + assert.deepEqual(r.value, [{ path: "hello.txt", added: 12, deleted: 3, binary: false }]); }); it('parses a binary marker ("-" / "-")', () => { const r = parseNumstat(`-${TAB}-${TAB}image.png${NUL}`); expectOk(r); - assert.deepEqual(r.value, [ - { path: 'image.png', added: 0, deleted: 0, binary: true }, - ]); + assert.deepEqual(r.value, [{ path: "image.png", added: 0, deleted: 0, binary: true }]); }); - it('parses a rename record', () => { + it("parses a rename record", () => { const r = parseNumstat(`3${TAB}1${TAB}${NUL}old.txt${NUL}new.txt${NUL}`); expectOk(r); assert.deepEqual(r.value, [ { - path: 'new.txt', - oldPath: 'old.txt', + path: "new.txt", + oldPath: "old.txt", added: 3, deleted: 1, binary: false, @@ -285,19 +250,16 @@ describe('parseNumstat', () => { ]); }); - it('parses a mixed batch with regular, binary, and rename', () => { - const stdout = - `2${TAB}1${TAB}a.txt${NUL}` + - `-${TAB}-${TAB}logo.png${NUL}` + - `5${TAB}0${TAB}${NUL}old.txt${NUL}new.txt${NUL}`; + it("parses a mixed batch with regular, binary, and rename", () => { + const stdout = `2${TAB}1${TAB}a.txt${NUL}-${TAB}-${TAB}logo.png${NUL}5${TAB}0${TAB}${NUL}old.txt${NUL}new.txt${NUL}`; const r = parseNumstat(stdout); expectOk(r); assert.deepEqual(r.value, [ - { path: 'a.txt', added: 2, deleted: 1, binary: false }, - { path: 'logo.png', added: 0, deleted: 0, binary: true }, + { path: "a.txt", added: 2, deleted: 1, binary: false }, + { path: "logo.png", added: 0, deleted: 0, binary: true }, { - path: 'new.txt', - oldPath: 'old.txt', + path: "new.txt", + oldPath: "old.txt", added: 5, deleted: 0, binary: false, @@ -305,12 +267,12 @@ describe('parseNumstat', () => { ]); }); - it('preserves spaces and unicode in paths', () => { + it("preserves spaces and unicode in paths", () => { const r = parseNumstat(`1${TAB}1${TAB}dir with spaces/日本語.txt${NUL}`); expectOk(r); assert.deepEqual(r.value, [ { - path: 'dir with spaces/日本語.txt', + path: "dir with spaces/日本語.txt", added: 1, deleted: 1, binary: false, @@ -318,72 +280,72 @@ describe('parseNumstat', () => { ]); }); - it('errors when tab count is wrong', () => { + it("errors when tab count is wrong", () => { const r = parseNumstat(`1${TAB}2${NUL}`); expectErr(r); assert.match(r.error.message, /3 tab-fields/); }); - it('errors when counts are non-numeric (and not binary marker)', () => { + it("errors when counts are non-numeric (and not binary marker)", () => { const r = parseNumstat(`abc${TAB}def${TAB}p${NUL}`); expectErr(r); assert.match(r.error.message, /non-numeric/); }); - it('errors on truncated rename', () => { + it("errors on truncated rename", () => { const r = parseNumstat(`3${TAB}1${TAB}${NUL}old.txt${NUL}`); expectErr(r); assert.match(r.error.message, /rename missing paths/); }); }); -describe('parseRefs', () => { - it('returns empty array for empty input', () => { - const r = parseRefs(''); +describe("parseRefs", () => { + it("returns empty array for empty input", () => { + const r = parseRefs(""); expectOk(r); assert.deepEqual(r.value, []); }); - it('parses a branch ref (newline-terminated record)', () => { + it("parses a branch ref (newline-terminated record)", () => { const r = parseRefs(`refs/heads/main${NUL}main${NUL}abc1234\n`); expectOk(r); assert.deepEqual(r.value, [ { - name: 'main', - fullName: 'refs/heads/main', - sha: 'abc1234', + name: "main", + fullName: "refs/heads/main", + sha: "abc1234", type: REF_TYPES.branch, }, ]); }); - it('parses a tag ref', () => { + it("parses a tag ref", () => { const r = parseRefs(`refs/tags/v1.0${NUL}v1.0${NUL}def5678\n`); expectOk(r); assert.deepEqual(r.value, [ { - name: 'v1.0', - fullName: 'refs/tags/v1.0', - sha: 'def5678', + name: "v1.0", + fullName: "refs/tags/v1.0", + sha: "def5678", type: REF_TYPES.tag, }, ]); }); - it('classifies a non-branch, non-tag ref as other', () => { + it("classifies a non-branch, non-tag ref as other", () => { const r = parseRefs(`refs/remotes/origin/main${NUL}origin/main${NUL}abc1234\n`); expectOk(r); assert.deepEqual(r.value, [ { - name: 'origin/main', - fullName: 'refs/remotes/origin/main', - sha: 'abc1234', + name: "origin/main", + fullName: "refs/remotes/origin/main", + sha: "abc1234", type: REF_TYPES.other, }, ]); }); - it('parses a mixed batch of branches and tags', () => { + it("parses a mixed batch of branches and tags", () => { const stdout = `refs/heads/main${NUL}main${NUL}aaa\n` + `refs/heads/dev${NUL}dev${NUL}bbb\n` + @@ -392,39 +354,39 @@ describe('parseRefs', () => { expectOk(r); assert.deepEqual(r.value, [ { - name: 'main', - fullName: 'refs/heads/main', - sha: 'aaa', + name: "main", + fullName: "refs/heads/main", + sha: "aaa", type: REF_TYPES.branch, }, { - name: 'dev', - fullName: 'refs/heads/dev', - sha: 'bbb', + name: "dev", + fullName: "refs/heads/dev", + sha: "bbb", type: REF_TYPES.branch, }, { - name: 'v1.0', - fullName: 'refs/tags/v1.0', - sha: 'ccc', + name: "v1.0", + fullName: "refs/tags/v1.0", + sha: "ccc", type: REF_TYPES.tag, }, ]); }); - it('errors when a line has fewer than 3 NUL-separated fields', () => { + it("errors when a line has fewer than 3 NUL-separated fields", () => { const r = parseRefs(`refs/heads/main${NUL}main\n`); expectErr(r); assert.match(r.error.message, /3 NUL-separated/); }); - it('errors when a ref field is empty', () => { + it("errors when a ref field is empty", () => { const r = parseRefs(`refs/heads/main${NUL}${NUL}abc\n`); expectErr(r); assert.match(r.error.message, /empty field/); }); - it('ignores trailing blank lines', () => { + it("ignores trailing blank lines", () => { const r = parseRefs(`refs/heads/main${NUL}main${NUL}abc\n\n\n`); expectOk(r); assert.equal(r.value.length, 1); diff --git a/src/test/unit/refFilter.test.ts b/src/test/unit/refFilter.test.ts index 66162b4..7391aeb 100644 --- a/src/test/unit/refFilter.test.ts +++ b/src/test/unit/refFilter.test.ts @@ -1,7 +1,7 @@ -import { strict as assert } from 'node:assert'; -import { REF_TYPES } from '../../constants'; -import { filterRefs } from '../../ui/format/refFilter'; -import type { Ref } from '../../git/types'; +import { strict as assert } from "node:assert"; +import { REF_TYPES } from "../../constants"; +import { filterRefs } from "../../ui/format/refFilter"; +import type { Ref } from "../../git/types"; const branch = (name: string): Ref => ({ name, @@ -17,86 +17,80 @@ const tag = (name: string): Ref => ({ type: REF_TYPES.tag, }); -const refs: readonly Ref[] = [ - branch('main'), - branch('agentpmo'), - branch('feature'), - tag('v0.1.0'), - tag('agentpmo'), -]; +const refs: readonly Ref[] = [branch("main"), branch("agentpmo"), branch("feature"), tag("v0.1.0"), tag("agentpmo")]; -describe('filterRefs', () => { - it('returns every ref untouched when no filters supplied', () => { +describe("filterRefs", () => { + it("returns every ref untouched when no filters supplied", () => { const r = filterRefs({ refs }); assert.equal(r.length, refs.length); assert.deepEqual( r.map((x) => x.fullName), - refs.map((x) => x.fullName), + refs.map((x) => x.fullName) ); }); - it('keeps only branches when type=branch', () => { + it("keeps only branches when type=branch", () => { const r = filterRefs({ refs, type: REF_TYPES.branch }); assert.deepEqual( r.map((x) => x.name), - ['main', 'agentpmo', 'feature'], + ["main", "agentpmo", "feature"] ); }); - it('keeps only tags when type=tag', () => { + it("keeps only tags when type=tag", () => { const r = filterRefs({ refs, type: REF_TYPES.tag }); assert.deepEqual( r.map((x) => x.name), - ['v0.1.0', 'agentpmo'], + ["v0.1.0", "agentpmo"] ); }); - it('excludes the named branch (current HEAD) from the unfiltered list', () => { - const r = filterRefs({ refs, excludeBranchName: 'agentpmo' }); + it("excludes the named branch (current HEAD) from the unfiltered list", () => { + const r = filterRefs({ refs, excludeBranchName: "agentpmo" }); assert.equal( - r.find((x) => x.type === REF_TYPES.branch && x.name === 'agentpmo'), + r.find((x) => x.type === REF_TYPES.branch && x.name === "agentpmo"), undefined, - 'branch named agentpmo must be excluded', + "branch named agentpmo must be excluded" ); assert.ok( - r.find((x) => x.type === REF_TYPES.tag && x.name === 'agentpmo'), - 'tag named agentpmo must NOT be excluded — name collision is fine across ref types', + r.find((x) => x.type === REF_TYPES.tag && x.name === "agentpmo"), + "tag named agentpmo must NOT be excluded — name collision is fine across ref types" ); assert.deepEqual( r.map((x) => x.fullName), - ['refs/heads/main', 'refs/heads/feature', 'refs/tags/v0.1.0', 'refs/tags/agentpmo'], + ["refs/heads/main", "refs/heads/feature", "refs/tags/v0.1.0", "refs/tags/agentpmo"] ); }); - it('excludes the named branch under a branch-type filter (the picker case the user reported)', () => { + it("excludes the named branch under a branch-type filter (the picker case the user reported)", () => { const r = filterRefs({ refs, type: REF_TYPES.branch, - excludeBranchName: 'agentpmo', + excludeBranchName: "agentpmo", }); assert.deepEqual( r.map((x) => x.name), - ['main', 'feature'], + ["main", "feature"] ); }); - it('excludeBranchName=undefined is a no-op (detached HEAD case)', () => { + it("excludeBranchName=undefined is a no-op (detached HEAD case)", () => { const r = filterRefs({ refs, type: REF_TYPES.branch, excludeBranchName: undefined }); assert.deepEqual( r.map((x) => x.name), - ['main', 'agentpmo', 'feature'], + ["main", "agentpmo", "feature"] ); }); - it('excludeBranchName never removes tags even when the tag name matches', () => { + it("excludeBranchName never removes tags even when the tag name matches", () => { const r = filterRefs({ refs, type: REF_TYPES.tag, - excludeBranchName: 'agentpmo', + excludeBranchName: "agentpmo", }); assert.deepEqual( r.map((x) => x.name), - ['v0.1.0', 'agentpmo'], + ["v0.1.0", "agentpmo"] ); }); }); diff --git a/src/test/unit/repoMatch.test.ts b/src/test/unit/repoMatch.test.ts index 2509779..ad6845a 100644 --- a/src/test/unit/repoMatch.test.ts +++ b/src/test/unit/repoMatch.test.ts @@ -1,5 +1,5 @@ -import { strict as assert } from 'node:assert'; -import { findRepoForUri, matchRepoByFsPath } from '../../git/repoMatch'; +import { strict as assert } from "node:assert"; +import { findRepoForUri, matchRepoByFsPath } from "../../git/repoMatch"; interface UriLike { readonly fsPath: string; @@ -10,47 +10,41 @@ interface RepoLike { const repoAt = (fsPath: string): RepoLike => ({ rootUri: { fsPath } }); -describe('matchRepoByFsPath', () => { - it('returns undefined for an empty repository list', () => { - const result = matchRepoByFsPath([], '/anywhere'); +describe("matchRepoByFsPath", () => { + it("returns undefined for an empty repository list", () => { + const result = matchRepoByFsPath([], "/anywhere"); assert.equal(result, undefined); }); - it('returns the only repository when the target is inside its root', () => { - const r = repoAt('/Users/me/repo'); - assert.strictEqual(matchRepoByFsPath([r], '/Users/me/repo/file.txt'), r); + it("returns the only repository when the target is inside its root", () => { + const r = repoAt("/Users/me/repo"); + assert.strictEqual(matchRepoByFsPath([r], "/Users/me/repo/file.txt"), r); }); - it('returns undefined when the target is outside every repository', () => { - const r = repoAt('/Users/me/repo'); - assert.equal(matchRepoByFsPath([r], '/Users/elsewhere/file.txt'), undefined); + it("returns undefined when the target is outside every repository", () => { + const r = repoAt("/Users/me/repo"); + assert.equal(matchRepoByFsPath([r], "/Users/elsewhere/file.txt"), undefined); }); - it('prefers the longest matching root for nested repositories', () => { - const outer = repoAt('/Users/me/repo'); - const inner = repoAt('/Users/me/repo/sub/inner'); - const result = matchRepoByFsPath( - [outer, inner], - '/Users/me/repo/sub/inner/file.txt', - ); + it("prefers the longest matching root for nested repositories", () => { + const outer = repoAt("/Users/me/repo"); + const inner = repoAt("/Users/me/repo/sub/inner"); + const result = matchRepoByFsPath([outer, inner], "/Users/me/repo/sub/inner/file.txt"); assert.strictEqual(result, inner); }); - it('order of the list does not affect the longest-match result', () => { - const outer = repoAt('/Users/me/repo'); - const inner = repoAt('/Users/me/repo/sub/inner'); - const result = matchRepoByFsPath( - [inner, outer], - '/Users/me/repo/sub/inner/file.txt', - ); + it("order of the list does not affect the longest-match result", () => { + const outer = repoAt("/Users/me/repo"); + const inner = repoAt("/Users/me/repo/sub/inner"); + const result = matchRepoByFsPath([inner, outer], "/Users/me/repo/sub/inner/file.txt"); assert.strictEqual(result, inner); }); }); -describe('findRepoForUri', () => { - it('delegates to api.getRepository when available', () => { - const repo = repoAt('/Users/me/repo'); - const uri = { fsPath: '/Users/me/repo/file.txt' }; +describe("findRepoForUri", () => { + it("delegates to api.getRepository when available", () => { + const repo = repoAt("/Users/me/repo"); + const uri = { fsPath: "/Users/me/repo/file.txt" }; const api = { repositories: [] as readonly RepoLike[], getRepository: (_u: UriLike) => repo, @@ -59,16 +53,16 @@ describe('findRepoForUri', () => { assert.strictEqual(result, repo); }); - it('falls back to fsPath matching when getRepository is absent', () => { - const repo = repoAt('/Users/me/repo'); - const uri = { fsPath: '/Users/me/repo/file.txt' }; + it("falls back to fsPath matching when getRepository is absent", () => { + const repo = repoAt("/Users/me/repo"); + const uri = { fsPath: "/Users/me/repo/file.txt" }; const api = { repositories: [repo] }; const result = findRepoForUri(api, uri); assert.strictEqual(result, repo); }); - it('returns undefined when getRepository returns null', () => { - const uri = { fsPath: '/Users/me/repo/file.txt' }; + it("returns undefined when getRepository returns null", () => { + const uri = { fsPath: "/Users/me/repo/file.txt" }; const api = { repositories: [] as readonly RepoLike[], getRepository: (_u: UriLike) => null, @@ -77,15 +71,12 @@ describe('findRepoForUri', () => { assert.equal(result, undefined); }); - it('falls back to longest-prefix matching across multiple roots', () => { - const a = repoAt('/Users/me/repo-a'); - const b = repoAt('/Users/me/repo-b'); + it("falls back to longest-prefix matching across multiple roots", () => { + const a = repoAt("/Users/me/repo-a"); + const b = repoAt("/Users/me/repo-b"); const api = { repositories: [a, b] }; - assert.strictEqual(findRepoForUri(api, { fsPath: '/Users/me/repo-b/x' }), b); - assert.strictEqual(findRepoForUri(api, { fsPath: '/Users/me/repo-a/y' }), a); - assert.equal( - findRepoForUri(api, { fsPath: '/Users/me/somewhere-else' }), - undefined, - ); + assert.strictEqual(findRepoForUri(api, { fsPath: "/Users/me/repo-b/x" }), b); + assert.strictEqual(findRepoForUri(api, { fsPath: "/Users/me/repo-a/y" }), a); + assert.equal(findRepoForUri(api, { fsPath: "/Users/me/somewhere-else" }), undefined); }); }); diff --git a/src/test/unit/result.test.ts b/src/test/unit/result.test.ts index 1afe30f..afaab54 100644 --- a/src/test/unit/result.test.ts +++ b/src/test/unit/result.test.ts @@ -1,132 +1,143 @@ -import { strict as assert } from 'node:assert'; -import { - ok, - err, - isOk, - isErr, - map, - andThen, - unwrapOr, - expectOk, - expectErr, - type Result, -} from '../../result'; - -describe('Result', () => { - describe('ok / err constructors', () => { - it('ok wraps the value and discriminates as ok=true', () => { +import { strict as assert } from "node:assert"; +import { ok, err, isOk, isErr, map, andThen, unwrapOr, expectOk, expectErr, type Result } from "../../result"; + +describe("Result", () => { + describe("ok / err constructors", () => { + it("ok wraps the value and discriminates as ok=true", () => { const r = ok(42); expectOk(r); assert.equal(r.value, 42); }); - it('err wraps the error and discriminates as ok=false', () => { - const r = err('boom'); + it("err wraps the error and discriminates as ok=false", () => { + const r = err("boom"); expectErr(r); - assert.equal(r.error, 'boom'); + assert.equal(r.error, "boom"); }); - it('ok preserves reference identity of the wrapped value', () => { + it("ok preserves reference identity of the wrapped value", () => { const obj = { x: 1 }; const r = ok(obj); expectOk(r); assert.strictEqual(r.value, obj); }); - it('err preserves reference identity of the wrapped error', () => { - const e = new Error('x'); + it("err preserves reference identity of the wrapped error", () => { + const e = new Error("x"); const r = err(e); expectErr(r); assert.strictEqual(r.error, e); }); }); - describe('isOk / isErr', () => { - it('isOk returns true on Ok and false on Err', () => { + describe("isOk / isErr", () => { + it("isOk returns true on Ok and false on Err", () => { assert.equal(isOk(ok(1)), true); - assert.equal(isOk(err('x')), false); + assert.equal(isOk(err("x")), false); }); - it('isErr returns true on Err and false on Ok', () => { - assert.equal(isErr(err('x')), true); + it("isErr returns true on Err and false on Ok", () => { + assert.equal(isErr(err("x")), true); assert.equal(isErr(ok(1)), false); }); - it('isOk narrows the union for the compiler (value is accessible)', () => { + it("isOk narrows the union for the compiler (value is accessible)", () => { const r: Result = ok(7); if (isOk(r)) { assert.equal(r.value + 1, 8); } else { - assert.fail('expected ok branch'); + assert.fail("expected ok branch"); } }); - it('isErr narrows the union for the compiler (error is accessible)', () => { - const r: Result = err('nope'); + it("isErr narrows the union for the compiler (error is accessible)", () => { + const r: Result = err("nope"); if (isErr(r)) { - assert.equal(r.error.toUpperCase(), 'NOPE'); + assert.equal(r.error.toUpperCase(), "NOPE"); } else { - assert.fail('expected err branch'); + assert.fail("expected err branch"); } }); }); - describe('map', () => { - it('applies the function on Ok', () => { + describe("map", () => { + it("applies the function on Ok", () => { const r = map(ok(2), (n) => n * 3); expectOk(r); assert.equal(r.value, 6); }); - it('passes Err through unchanged', () => { - const original: Result = err('bad'); + it("passes Err through unchanged", () => { + const original: Result = err("bad"); const r = map(original, (n) => n * 3); expectErr(r); - assert.equal(r.error, 'bad'); + assert.equal(r.error, "bad"); }); - it('supports type-changing maps', () => { + it("supports type-changing maps", () => { const r = map(ok(5), (n) => `n=${n.toString()}`); expectOk(r); - assert.equal(r.value, 'n=5'); + assert.equal(r.value, "n=5"); }); }); - describe('andThen', () => { - it('chains Ok into another Ok', () => { + describe("andThen", () => { + it("chains Ok into another Ok", () => { const r = andThen(ok(2), (n) => ok(n + 1)); expectOk(r); assert.equal(r.value, 3); }); - it('chains Ok into an Err and propagates it', () => { + it("chains Ok into an Err and propagates it", () => { const start: Result = ok(2); - const r = andThen(start, (_n): Result => err('downstream')); + const r = andThen(start, (_n): Result => err("downstream")); expectErr(r); - assert.equal(r.error, 'downstream'); + assert.equal(r.error, "downstream"); }); - it('short-circuits on Err without invoking the function', () => { + it("short-circuits on Err without invoking the function", () => { let called = false; - const start: Result = err('upstream'); + const start: Result = err("upstream"); const r = andThen(start, (n) => { called = true; return ok(n); }); assert.equal(called, false); expectErr(r); - assert.equal(r.error, 'upstream'); + assert.equal(r.error, "upstream"); }); }); - describe('unwrapOr', () => { - it('returns the Ok value', () => { + describe("unwrapOr", () => { + it("returns the Ok value", () => { assert.equal(unwrapOr(ok(10), 99), 10); }); - it('returns the fallback on Err', () => { - const r: Result = err('x'); + it("returns the fallback on Err", () => { + const r: Result = err("x"); assert.equal(unwrapOr(r, 99), 99); }); }); + + describe("expectOk / expectErr negative paths", () => { + it("expectOk throws with the JSON-serialized error when given an Err", () => { + const r: Result = err({ kind: "explode" }); + assert.throws( + () => { + expectOk(r); + }, + /expected Ok, got Err: \{"kind":"explode"\}/, + ); + }); + + it("expectErr throws with the JSON-serialized value when given an Ok", () => { + const r: Result<{ n: number }, string> = ok({ n: 7 }); + assert.throws( + () => { + expectErr(r); + }, + /expected Err, got Ok: \{"n":7\}/, + ); + }); + }); }); diff --git a/src/test/unit/state.test.ts b/src/test/unit/state.test.ts index 882cbc8..e1cd72b 100644 --- a/src/test/unit/state.test.ts +++ b/src/test/unit/state.test.ts @@ -1,70 +1,70 @@ -import { strict as assert } from 'node:assert'; -import { isLastComparison } from '../../state'; +import { strict as assert } from "node:assert"; +import { isLastComparison } from "../../state"; -const validRevA = { kind: 'commit', sha: 'abcdef0' }; -const validRevB = { kind: 'workingCopy' }; +const validRevA = { kind: "commit", sha: "abcdef0" }; +const validRevB = { kind: "workingCopy" }; -describe('isLastComparison', () => { - it('accepts a valid record with commit revA + workingCopy revB', () => { - const value = { revA: validRevA, revB: validRevB, repoRoot: '/path/to/repo' }; +describe("isLastComparison", () => { + it("accepts a valid record with commit revA + workingCopy revB", () => { + const value = { revA: validRevA, revB: validRevB, repoRoot: "/path/to/repo" }; assert.equal(isLastComparison(value), true); }); - it('accepts a valid record with two commit revs', () => { + it("accepts a valid record with two commit revs", () => { const value = { - revA: { kind: 'commit', sha: 'a' }, - revB: { kind: 'commit', sha: 'b' }, - repoRoot: '/path', + revA: { kind: "commit", sha: "a" }, + revB: { kind: "commit", sha: "b" }, + repoRoot: "/path", }; assert.equal(isLastComparison(value), true); }); - it('rejects null / undefined / non-object', () => { + it("rejects null / undefined / non-object", () => { assert.equal(isLastComparison(null), false); assert.equal(isLastComparison(undefined), false); - assert.equal(isLastComparison('string'), false); + assert.equal(isLastComparison("string"), false); assert.equal(isLastComparison(42), false); assert.equal(isLastComparison(true), false); assert.equal(isLastComparison([]), false); // arrays are objects but missing fields }); - it('rejects a record missing repoRoot', () => { + it("rejects a record missing repoRoot", () => { const value = { revA: validRevA, revB: validRevB }; assert.equal(isLastComparison(value), false); }); - it('rejects a record where repoRoot is not a string', () => { + it("rejects a record where repoRoot is not a string", () => { const value = { revA: validRevA, revB: validRevB, repoRoot: 42 }; assert.equal(isLastComparison(value), false); }); - it('rejects a record missing revA', () => { - const value = { revB: validRevB, repoRoot: '/p' }; + it("rejects a record missing revA", () => { + const value = { revB: validRevB, repoRoot: "/p" }; assert.equal(isLastComparison(value), false); }); - it('rejects a record where revA is null', () => { - const value = { revA: null, revB: validRevB, repoRoot: '/p' }; + it("rejects a record where revA is null", () => { + const value = { revA: null, revB: validRevB, repoRoot: "/p" }; assert.equal(isLastComparison(value), false); }); - it('rejects a record where revA is not an object', () => { - const value = { revA: 'commit', revB: validRevB, repoRoot: '/p' }; + it("rejects a record where revA is not an object", () => { + const value = { revA: "commit", revB: validRevB, repoRoot: "/p" }; assert.equal(isLastComparison(value), false); }); - it('rejects a record missing revB', () => { - const value = { revA: validRevA, repoRoot: '/p' }; + it("rejects a record missing revB", () => { + const value = { revA: validRevA, repoRoot: "/p" }; assert.equal(isLastComparison(value), false); }); - it('rejects a record where revB is null', () => { - const value = { revA: validRevA, revB: null, repoRoot: '/p' }; + it("rejects a record where revB is null", () => { + const value = { revA: validRevA, revB: null, repoRoot: "/p" }; assert.equal(isLastComparison(value), false); }); - it('rejects a record where revB is not an object', () => { - const value = { revA: validRevA, revB: 'workingCopy', repoRoot: '/p' }; + it("rejects a record where revB is not an object", () => { + const value = { revA: validRevA, revB: "workingCopy", repoRoot: "/p" }; assert.equal(isLastComparison(value), false); }); }); diff --git a/src/test/unit/uri.test.ts b/src/test/unit/uri.test.ts index 188edbb..4511cdd 100644 --- a/src/test/unit/uri.test.ts +++ b/src/test/unit/uri.test.ts @@ -1,10 +1,10 @@ -import { strict as assert } from 'node:assert'; -import { REV_KINDS, URI_PARSE_ERROR_KINDS } from '../../constants'; -import { buildDiffyUri, parseDiffyUri } from '../../ui/uri'; -import { expectErr, expectOk } from '../../result'; -import type { DiffyAddressableRev } from '../../git/types'; +import { strict as assert } from "node:assert"; +import { REV_KINDS, URI_PARSE_ERROR_KINDS } from "../../constants"; +import { buildDiffyUri, parseDiffyUri } from "../../ui/uri"; +import { expectErr, expectOk } from "../../result"; +import type { DiffyAddressableRev } from "../../git/types"; -const SHA = 'abc1234def567890fedcba0987654321aaaaaaaa'; +const SHA = "abc1234def567890fedcba0987654321aaaaaaaa"; const commit = (sha: string): DiffyAddressableRev => ({ kind: REV_KINDS.commit, @@ -20,132 +20,138 @@ const roundTrip = (rev: DiffyAddressableRev, path: string): void => { assert.equal(parsed.value.path, path); }; -describe('buildDiffyUri', () => { - it('builds a commit URI with sha and path', () => { - const uri = buildDiffyUri(commit(SHA), 'src/file.ts'); +describe("buildDiffyUri", () => { + it("builds a commit URI with sha and path", () => { + const uri = buildDiffyUri(commit(SHA), "src/file.ts"); assert.equal(uri, `diffy://commit/${SHA}/src/file.ts`); }); - it('builds an index URI without sha', () => { - const uri = buildDiffyUri(index(), 'src/file.ts'); - assert.equal(uri, 'diffy://index/src/file.ts'); + it("builds an index URI without sha", () => { + const uri = buildDiffyUri(index(), "src/file.ts"); + assert.equal(uri, "diffy://index/src/file.ts"); }); - it('percent-encodes spaces in path segments', () => { - const uri = buildDiffyUri(commit(SHA), 'a folder/b file.ts'); + it("percent-encodes spaces in path segments", () => { + const uri = buildDiffyUri(commit(SHA), "a folder/b file.ts"); assert.equal(uri, `diffy://commit/${SHA}/a%20folder/b%20file.ts`); }); - it('percent-encodes ? and # so they are not parsed as query/fragment', () => { - const uri = buildDiffyUri(commit(SHA), 'weird?name#here.ts'); + it("percent-encodes ? and # so they are not parsed as query/fragment", () => { + const uri = buildDiffyUri(commit(SHA), "weird?name#here.ts"); assert.equal(uri, `diffy://commit/${SHA}/weird%3Fname%23here.ts`); }); - it('preserves forward slashes as segment separators', () => { - const uri = buildDiffyUri(commit(SHA), 'a/b/c/d.ts'); + it("preserves forward slashes as segment separators", () => { + const uri = buildDiffyUri(commit(SHA), "a/b/c/d.ts"); assert.equal(uri, `diffy://commit/${SHA}/a/b/c/d.ts`); }); - it('encodes unicode characters in path segments', () => { - const uri = buildDiffyUri(commit(SHA), 'src/日本語.ts'); - const encoded = encodeURIComponent('日本語'); + it("encodes unicode characters in path segments", () => { + const uri = buildDiffyUri(commit(SHA), "src/日本語.ts"); + const encoded = encodeURIComponent("日本語"); assert.equal(uri, `diffy://commit/${SHA}/src/${encoded}.ts`); }); }); -describe('parseDiffyUri', () => { - it('parses a commit URI and decodes the path', () => { +describe("parseDiffyUri", () => { + it("parses a commit URI and decodes the path", () => { const r = parseDiffyUri(`diffy://commit/${SHA}/src/file.ts`); expectOk(r); assert.deepEqual(r.value.rev, { kind: REV_KINDS.commit, sha: SHA }); - assert.equal(r.value.path, 'src/file.ts'); + assert.equal(r.value.path, "src/file.ts"); }); - it('parses an index URI and decodes the path', () => { - const r = parseDiffyUri('diffy://index/src/file.ts'); + it("parses an index URI and decodes the path", () => { + const r = parseDiffyUri("diffy://index/src/file.ts"); expectOk(r); assert.deepEqual(r.value.rev, { kind: REV_KINDS.index }); - assert.equal(r.value.path, 'src/file.ts'); + assert.equal(r.value.path, "src/file.ts"); }); - it('rejects a non-diffy scheme', () => { - const r = parseDiffyUri('file:///some/path.ts'); + it("rejects a non-diffy scheme", () => { + const r = parseDiffyUri("file:///some/path.ts"); expectErr(r); assert.equal(r.error.kind, URI_PARSE_ERROR_KINDS.invalidScheme); }); - it('rejects an unknown authority', () => { + it("rejects an unknown authority", () => { const r = parseDiffyUri(`diffy://stash/${SHA}/x.ts`); expectErr(r); assert.equal(r.error.kind, URI_PARSE_ERROR_KINDS.invalidAuthority); }); - it('rejects a commit URI with no path after the sha', () => { + it("rejects a commit URI with no path after the sha", () => { const r = parseDiffyUri(`diffy://commit/${SHA}/`); expectErr(r); assert.equal(r.error.kind, URI_PARSE_ERROR_KINDS.emptyPath); }); - it('rejects a commit URI with no sha', () => { - const r = parseDiffyUri('diffy://commit//file.ts'); + it("rejects a commit URI with no sha", () => { + const r = parseDiffyUri("diffy://commit//file.ts"); expectErr(r); assert.equal(r.error.kind, URI_PARSE_ERROR_KINDS.missingSha); }); - it('rejects an index URI with no path', () => { - const r = parseDiffyUri('diffy://index/'); + it("rejects an index URI with no path", () => { + const r = parseDiffyUri("diffy://index/"); expectErr(r); assert.equal(r.error.kind, URI_PARSE_ERROR_KINDS.emptyPath); }); - it('rejects a malformed URI with no scheme separator', () => { - const r = parseDiffyUri('not-a-uri'); + it("rejects a malformed URI with no scheme separator", () => { + const r = parseDiffyUri("not-a-uri"); expectErr(r); assert.equal(r.error.kind, URI_PARSE_ERROR_KINDS.malformed); }); - it('rejects a URI missing the authority/path slash', () => { - const r = parseDiffyUri('diffy://commit'); + it("rejects a URI missing the authority/path slash", () => { + const r = parseDiffyUri("diffy://commit"); expectErr(r); assert.equal(r.error.kind, URI_PARSE_ERROR_KINDS.malformed); }); - it('rejects a URI with malformed percent encoding', () => { + it("rejects a URI with malformed percent encoding", () => { const r = parseDiffyUri(`diffy://commit/${SHA}/bad%ZZpath.ts`); expectErr(r); assert.equal(r.error.kind, URI_PARSE_ERROR_KINDS.badEncoding); }); + + it("rejects an index URI with malformed percent encoding", () => { + const r = parseDiffyUri("diffy://index/bad%ZZpath.ts"); + expectErr(r); + assert.equal(r.error.kind, URI_PARSE_ERROR_KINDS.badEncoding); + }); }); -describe('buildDiffyUri ↔ parseDiffyUri round-trip', () => { - it('round-trips a simple commit URI', () => { - roundTrip(commit(SHA), 'src/file.ts'); +describe("buildDiffyUri ↔ parseDiffyUri round-trip", () => { + it("round-trips a simple commit URI", () => { + roundTrip(commit(SHA), "src/file.ts"); }); - it('round-trips a simple index URI', () => { - roundTrip(index(), 'src/file.ts'); + it("round-trips a simple index URI", () => { + roundTrip(index(), "src/file.ts"); }); - it('round-trips paths with spaces', () => { - roundTrip(commit(SHA), 'a folder/b file.ts'); - roundTrip(index(), 'a folder/b file.ts'); + it("round-trips paths with spaces", () => { + roundTrip(commit(SHA), "a folder/b file.ts"); + roundTrip(index(), "a folder/b file.ts"); }); - it('round-trips paths with unicode', () => { - roundTrip(commit(SHA), 'src/日本語/файл.ts'); - roundTrip(index(), 'src/日本語/файл.ts'); + it("round-trips paths with unicode", () => { + roundTrip(commit(SHA), "src/日本語/файл.ts"); + roundTrip(index(), "src/日本語/файл.ts"); }); - it('round-trips paths containing # and ?', () => { - roundTrip(commit(SHA), 'weird?name#here.ts'); - roundTrip(index(), 'q=1&r=2#frag.ts'); + it("round-trips paths containing # and ?", () => { + roundTrip(commit(SHA), "weird?name#here.ts"); + roundTrip(index(), "q=1&r=2#frag.ts"); }); - it('round-trips paths with quotes and brackets', () => { + it("round-trips paths with quotes and brackets", () => { roundTrip(commit(SHA), 'a "b" [c] (d).ts'); }); - it('round-trips deeply nested paths', () => { - roundTrip(commit(SHA), 'a/b/c/d/e/f/g/h/i/j.ts'); + it("round-trips deeply nested paths", () => { + roundTrip(commit(SHA), "a/b/c/d/e/f/g/h/i/j.ts"); }); }); diff --git a/src/ui/CommitPicker.ts b/src/ui/CommitPicker.ts index 9b2b582..7c28d3b 100644 --- a/src/ui/CommitPicker.ts +++ b/src/ui/CommitPicker.ts @@ -1,10 +1,10 @@ -import type * as vscode from 'vscode'; -import { UI_TEXT } from '../constants'; -import { type Result, map } from '../result'; -import type { Commit } from '../git/types'; -import { showSinglePick } from './runQuickPick'; -import type { Cancelled } from './cancelled'; -import { formatRelative } from './format/relativeTime'; +import type * as vscode from "vscode"; +import { UI_TEXT } from "../constants"; +import { type Result, map } from "../result"; +import type { Commit } from "../git/types"; +import { showSinglePick } from "./runQuickPick"; +import type { Cancelled } from "./cancelled"; +import { formatRelative } from "./format/relativeTime"; interface CommitPickItem extends vscode.QuickPickItem { readonly commit: Commit; diff --git a/src/ui/FilePicker.ts b/src/ui/FilePicker.ts index 34171f0..01e62bf 100644 --- a/src/ui/FilePicker.ts +++ b/src/ui/FilePicker.ts @@ -1,13 +1,9 @@ -import type * as vscode from 'vscode'; -import { showStayOpenPick } from './runQuickPick'; -import { - type FileEntry, - formatCounts, - statusBadge, -} from './format/fileItem'; +import type * as vscode from "vscode"; +import { showStayOpenPick } from "./runQuickPick"; +import { type FileEntry, formatCounts, statusBadge } from "./format/fileItem"; -export { mergeChangedFilesWithStats } from './format/fileItem'; -export type { FileEntry } from './format/fileItem'; +export { mergeChangedFilesWithStats } from "./format/fileItem"; +export type { FileEntry } from "./format/fileItem"; interface FileItem extends vscode.QuickPickItem { readonly entry: FileEntry; @@ -28,13 +24,16 @@ export const pickFiles = async ({ entries: readonly FileEntry[]; onPick: (entry: FileEntry) => void | Promise; placeholder?: string; -}): Promise => - { await showStayOpenPick( +}): Promise => { + await showStayOpenPick( { items: entries.map(toItem), - placeholder: placeholder ?? 'Pick a file to diff (Esc to close)', + placeholder: placeholder ?? "Pick a file to diff (Esc to close)", matchOnDescription: true, matchOnDetail: true, }, - async (item) => { await onPick(item.entry); }, - ); }; + async (item) => { + await onPick(item.entry); + } + ); +}; diff --git a/src/ui/RefPicker.ts b/src/ui/RefPicker.ts index 38459bf..b7701d5 100644 --- a/src/ui/RefPicker.ts +++ b/src/ui/RefPicker.ts @@ -1,11 +1,11 @@ -import type * as vscode from 'vscode'; -import { SHORT_SHA_LEN, UI_TEXT } from '../constants'; -import { type Result, map } from '../result'; -import type { Ref, RefType } from '../git/types'; -import { showSinglePick } from './runQuickPick'; -import type { Cancelled } from './cancelled'; -import { refTypeLabel } from './format/refLabel'; -import { filterRefs } from './format/refFilter'; +import type * as vscode from "vscode"; +import { SHORT_SHA_LEN, UI_TEXT } from "../constants"; +import { type Result, map } from "../result"; +import type { Ref, RefType } from "../git/types"; +import { showSinglePick } from "./runQuickPick"; +import type { Cancelled } from "./cancelled"; +import { refTypeLabel } from "./format/refLabel"; +import { filterRefs } from "./format/refFilter"; interface RefPickItem extends vscode.QuickPickItem { readonly ref: Ref; diff --git a/src/ui/SideBPicker.ts b/src/ui/SideBPicker.ts index d593f9f..1483e87 100644 --- a/src/ui/SideBPicker.ts +++ b/src/ui/SideBPicker.ts @@ -1,8 +1,8 @@ -import type * as vscode from 'vscode'; -import { SIDE_B_KINDS, UI_TEXT } from '../constants'; -import { type Result, map } from '../result'; -import { showSinglePick } from './runQuickPick'; -import type { Cancelled } from './cancelled'; +import type * as vscode from "vscode"; +import { SIDE_B_KINDS, UI_TEXT } from "../constants"; +import { type Result, map } from "../result"; +import { showSinglePick } from "./runQuickPick"; +import type { Cancelled } from "./cancelled"; export type SideBChoice = | { readonly kind: typeof SIDE_B_KINDS.workingCopy } diff --git a/src/ui/format/fileItem.ts b/src/ui/format/fileItem.ts index 1da55eb..d5442b8 100644 --- a/src/ui/format/fileItem.ts +++ b/src/ui/format/fileItem.ts @@ -1,5 +1,5 @@ -import { CHANGED_FILE_STATUSES, UI_TEXT } from '../../constants'; -import type { ChangedFile, DiffStat } from '../../git/types'; +import { CHANGED_FILE_STATUSES, UI_TEXT } from "../../constants"; +import type { ChangedFile, DiffStat } from "../../git/types"; export const statusBadge = (file: ChangedFile): string => { if (file.status === CHANGED_FILE_STATUSES.renamed) { @@ -25,7 +25,7 @@ export interface FileEntry { export const mergeChangedFilesWithStats = ( files: readonly ChangedFile[], - stats: readonly DiffStat[], + stats: readonly DiffStat[] ): readonly FileEntry[] => { const byPath = new Map(); for (const s of stats) { diff --git a/src/ui/format/refFilter.ts b/src/ui/format/refFilter.ts index 32d3600..1b22f07 100644 --- a/src/ui/format/refFilter.ts +++ b/src/ui/format/refFilter.ts @@ -1,5 +1,5 @@ -import { REF_TYPES } from '../../constants'; -import type { Ref, RefType } from '../../git/types'; +import { REF_TYPES } from "../../constants"; +import type { Ref, RefType } from "../../git/types"; export interface FilterRefsArgs { readonly refs: readonly Ref[]; @@ -7,16 +7,10 @@ export interface FilterRefsArgs { readonly excludeBranchName?: string | undefined; } -export const filterRefs = ({ - refs, - type, - excludeBranchName, -}: FilterRefsArgs): readonly Ref[] => { +export const filterRefs = ({ refs, type, excludeBranchName }: FilterRefsArgs): readonly Ref[] => { const byType = type === undefined ? refs : refs.filter((r) => r.type === type); if (excludeBranchName === undefined) { return byType; } - return byType.filter( - (r) => !(r.type === REF_TYPES.branch && r.name === excludeBranchName), - ); + return byType.filter((r) => !(r.type === REF_TYPES.branch && r.name === excludeBranchName)); }; diff --git a/src/ui/format/refLabel.ts b/src/ui/format/refLabel.ts index ee34aea..b29acbc 100644 --- a/src/ui/format/refLabel.ts +++ b/src/ui/format/refLabel.ts @@ -1,5 +1,5 @@ -import { REF_TYPES, UI_TEXT } from '../../constants'; -import type { Ref } from '../../git/types'; +import { REF_TYPES, UI_TEXT } from "../../constants"; +import type { Ref } from "../../git/types"; export const refTypeLabel = (ref: Ref): string => { if (ref.type === REF_TYPES.branch) { diff --git a/src/ui/format/relativeTime.ts b/src/ui/format/relativeTime.ts index efce351..9dfd8b6 100644 --- a/src/ui/format/relativeTime.ts +++ b/src/ui/format/relativeTime.ts @@ -1,4 +1,4 @@ -import { UI_TEXT } from '../../constants'; +import { UI_TEXT } from "../../constants"; const SECONDS_PER_MINUTE = 60; const MINUTES_PER_HOUR = 60; diff --git a/src/ui/runQuickPick.ts b/src/ui/runQuickPick.ts index 0c83329..ef4e0a8 100644 --- a/src/ui/runQuickPick.ts +++ b/src/ui/runQuickPick.ts @@ -1,6 +1,6 @@ -import * as vscode from 'vscode'; -import { type Result, err, ok } from '../result'; -import { CANCELLED, type Cancelled } from './cancelled'; +import * as vscode from "vscode"; +import { type Result, err, ok } from "../result"; +import { CANCELLED, type Cancelled } from "./cancelled"; export interface QuickPickConfig { readonly items: readonly T[]; @@ -10,9 +10,7 @@ export interface QuickPickConfig { readonly ignoreFocusOut?: boolean; } -const createConfiguredPicker = ( - config: QuickPickConfig, -): vscode.QuickPick => { +const createConfiguredPicker = (config: QuickPickConfig): vscode.QuickPick => { const qp = vscode.window.createQuickPick(); qp.placeholder = config.placeholder; qp.matchOnDescription = config.matchOnDescription ?? false; @@ -23,7 +21,7 @@ const createConfiguredPicker = ( }; export const showSinglePick = async ( - config: QuickPickConfig, + config: QuickPickConfig ): Promise> => await new Promise>((resolve) => { const qp = createConfiguredPicker(config); @@ -48,7 +46,7 @@ export const showSinglePick = async ( export const showStayOpenPick = async ( config: QuickPickConfig, - onPick: (item: T) => void | Promise, + onPick: (item: T) => void | Promise ): Promise => { await new Promise((resolve) => { const qp = createConfiguredPicker({ ...config, ignoreFocusOut: true }); diff --git a/src/ui/uri.ts b/src/ui/uri.ts index 7ea6d11..04c1454 100644 --- a/src/ui/uri.ts +++ b/src/ui/uri.ts @@ -1,11 +1,6 @@ -import { - REV_KINDS, - SCHEME, - URI_AUTHORITIES, - URI_PARSE_ERROR_KINDS, -} from '../constants'; -import { type Result, ok, err } from '../result'; -import type { DiffyAddressableRev, Sha } from '../git/types'; +import { REV_KINDS, SCHEME, URI_AUTHORITIES, URI_PARSE_ERROR_KINDS } from "../constants"; +import { type Result, ok, err } from "../result"; +import type { DiffyAddressableRev, Sha } from "../git/types"; export type DiffyUriParseError = | { @@ -32,11 +27,10 @@ export interface DiffyUriComponents { readonly path: string; } -const SCHEME_SEP = '://'; -const PATH_SEP = '/'; +const SCHEME_SEP = "://"; +const PATH_SEP = "/"; -const encodePath = (path: string): string => - path.split(PATH_SEP).map(encodeURIComponent).join(PATH_SEP); +const encodePath = (path: string): string => path.split(PATH_SEP).map(encodeURIComponent).join(PATH_SEP); const decodePath = (encoded: string): Result => { try { @@ -54,16 +48,14 @@ export const buildDiffyUri = (rev: DiffyAddressableRev, path: string): string => return `${SCHEME}${SCHEME_SEP}${URI_AUTHORITIES.index}/${encoded}`; }; -const parseCommitUri = ( - rest: string, -): Result => { +const parseCommitUri = (rest: string): Result => { const slash = rest.indexOf(PATH_SEP); if (slash <= 0) { return err({ kind: URI_PARSE_ERROR_KINDS.missingSha }); } const sha: Sha = rest.slice(0, slash); const encodedPath = rest.slice(slash + 1); - if (encodedPath === '') { + if (encodedPath === "") { return err({ kind: URI_PARSE_ERROR_KINDS.emptyPath }); } const decoded = decodePath(encodedPath); @@ -73,10 +65,8 @@ const parseCommitUri = ( return ok({ rev: { kind: REV_KINDS.commit, sha }, path: decoded.value }); }; -const parseIndexUri = ( - rest: string, -): Result => { - if (rest === '') { +const parseIndexUri = (rest: string): Result => { + if (rest === "") { return err({ kind: URI_PARSE_ERROR_KINDS.emptyPath }); } const decoded = decodePath(rest); @@ -97,7 +87,7 @@ const splitUri = (uri: string): Result => { if (sep < 0) { return err({ kind: URI_PARSE_ERROR_KINDS.malformed, - reason: 'no scheme separator', + reason: "no scheme separator", }); } const scheme = uri.slice(0, sep); @@ -106,7 +96,7 @@ const splitUri = (uri: string): Result => { if (firstSlash < 0) { return err({ kind: URI_PARSE_ERROR_KINDS.malformed, - reason: 'no authority/path separator', + reason: "no authority/path separator", }); } return ok({ @@ -116,9 +106,7 @@ const splitUri = (uri: string): Result => { }); }; -export const parseDiffyUri = ( - uri: string, -): Result => { +export const parseDiffyUri = (uri: string): Result => { const split = splitUri(uri); if (!split.ok) { return split; diff --git a/src/vscodeGitApi.ts b/src/vscodeGitApi.ts index a533469..9054648 100644 --- a/src/vscodeGitApi.ts +++ b/src/vscodeGitApi.ts @@ -1,5 +1,5 @@ -import * as vscode from 'vscode'; -import { VSCODE_GIT_EXTENSION_ID } from './constants'; +import * as vscode from "vscode"; +import { VSCODE_GIT_EXTENSION_ID } from "./constants"; const GIT_API_VERSION = 1; @@ -17,9 +17,7 @@ export interface GitApi { } export const getGitApi = async (): Promise => { - const ext = vscode.extensions.getExtension( - VSCODE_GIT_EXTENSION_ID, - ); + const ext = vscode.extensions.getExtension(VSCODE_GIT_EXTENSION_ID); if (ext === undefined) { return undefined; } @@ -29,4 +27,4 @@ export const getGitApi = async (): Promise => { return ext.exports.getAPI(GIT_API_VERSION); }; -export { findRepoForUri } from './git/repoMatch'; +export { findRepoForUri } from "./git/repoMatch"; From 0894019af81a102d99ee5782b5886874b4a2b245 Mon Sep 17 00:00:00 2001 From: Christian Findlay <16697547+MelbourneDeveloper@users.noreply.github.com> Date: Tue, 26 May 2026 18:56:30 +1000 Subject: [PATCH 12/17] ci: enforce LF line endings on Windows checkouts Windows CI was failing prettier --check because git core.autocrlf=true was converting LF to CRLF on checkout, then prettier (configured with endOfLine: lf) flagged every file. Pin all text files to LF via .gitattributes so the working tree matches what prettier expects on every platform. Also re-format result.test.ts after the previous round of edits. --- .gitattributes | 8 ++++++++ src/test/unit/result.test.ts | 18 ++++++------------ 2 files changed, 14 insertions(+), 12 deletions(-) create mode 100644 .gitattributes diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..40a8c4c --- /dev/null +++ b/.gitattributes @@ -0,0 +1,8 @@ +* text=auto eol=lf + +*.png binary +*.jpg binary +*.jpeg binary +*.gif binary +*.ico binary +*.vsix binary diff --git a/src/test/unit/result.test.ts b/src/test/unit/result.test.ts index afaab54..f04a63b 100644 --- a/src/test/unit/result.test.ts +++ b/src/test/unit/result.test.ts @@ -122,22 +122,16 @@ describe("Result", () => { describe("expectOk / expectErr negative paths", () => { it("expectOk throws with the JSON-serialized error when given an Err", () => { const r: Result = err({ kind: "explode" }); - assert.throws( - () => { - expectOk(r); - }, - /expected Ok, got Err: \{"kind":"explode"\}/, - ); + assert.throws(() => { + expectOk(r); + }, /expected Ok, got Err: \{"kind":"explode"\}/); }); it("expectErr throws with the JSON-serialized value when given an Ok", () => { const r: Result<{ n: number }, string> = ok({ n: 7 }); - assert.throws( - () => { - expectErr(r); - }, - /expected Err, got Ok: \{"n":7\}/, - ); + assert.throws(() => { + expectErr(r); + }, /expected Err, got Ok: \{"n":7\}/); }); }); }); From d71bac51af2ded7108ad19dd0886d202894c9ec5 Mon Sep 17 00:00:00 2001 From: Christian Findlay <16697547+MelbourneDeveloper@users.noreply.github.com> Date: Tue, 26 May 2026 19:00:24 +1000 Subject: [PATCH 13/17] ci: spawn npm via shell on Windows in run-coverage.mjs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit spawnSync without shell:true can't resolve npm/npx on Windows (they're .cmd shims, not real executables). The CI test step was exiting before any test could run because spawnSync returned status=null. Opt into shell on win32 only — POSIX platforms keep direct execution. --- scripts/run-coverage.mjs | 1 + 1 file changed, 1 insertion(+) diff --git a/scripts/run-coverage.mjs b/scripts/run-coverage.mjs index 2726276..efa5eca 100644 --- a/scripts/run-coverage.mjs +++ b/scripts/run-coverage.mjs @@ -20,6 +20,7 @@ const run = (cmd, args, env) => { cwd: repoRoot, stdio: 'inherit', env: { ...process.env, ...env }, + shell: process.platform === 'win32', }); if (r.status !== 0) { process.stderr.write(`\n!! ${cmd} ${args.join(' ')} exited ${r.status ?? '?'}\n`); From a4606dee77ca96d661ec8069161e632afff69950 Mon Sep 17 00:00:00 2001 From: Christian Findlay <16697547+MelbourneDeveloper@users.noreply.github.com> Date: Tue, 26 May 2026 19:06:00 +1000 Subject: [PATCH 14/17] test: normalize fsPath separators before regex match on Windows MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit vscode.Uri.fsPath returns native separators — backslashes on Windows. Tests that asserted on multi-segment trailing paths (`dir/c.txt`, `repo-seed/workspace`) failed CI on Windows because the regex used forward slashes. Replace backslashes with slashes before matching so the assertions stay readable and work cross-platform. Single-segment matches like `/a\.txt$/` are unaffected. --- src/test/suite/activation.test.ts | 2 +- src/test/suite/commands.test.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/test/suite/activation.test.ts b/src/test/suite/activation.test.ts index d33546a..99f9e1a 100644 --- a/src/test/suite/activation.test.ts +++ b/src/test/suite/activation.test.ts @@ -32,7 +32,7 @@ describe("activation", () => { assert.equal(folders.length, 1); const folder = folders[0]; assert.ok(folder); - assert.match(folder.uri.fsPath, /repo-seed\/workspace$/); + assert.match(folder.uri.fsPath.replace(/\\/g, "/"), /repo-seed\/workspace$/); }); it("shows logs command opens the Diffy OutputChannel without errors", async () => { diff --git a/src/test/suite/commands.test.ts b/src/test/suite/commands.test.ts index 729b36f..7114b13 100644 --- a/src/test/suite/commands.test.ts +++ b/src/test/suite/commands.test.ts @@ -318,7 +318,7 @@ describe("Diffy commands — end-to-end through real QuickPick UI", () => { const diffTab = await waitForDiffTab(); const uris = tabInputUris(diffTab); assert.match(uris.left.toString(), new RegExp(`diffy://commit/${shas.first}/dir/c\\.txt$`)); - assert.match(uris.right.fsPath, /dir\/c\.txt$/); + assert.match(uris.right.fsPath.replace(/\\/g, "/"), /dir\/c\.txt$/); await flow; }); From 87da1a170eab343ee721be3532d9e04b001ba7f4 Mon Sep 17 00:00:00 2001 From: Christian Findlay <16697547+MelbourneDeveloper@users.noreply.github.com> Date: Tue, 26 May 2026 19:14:14 +1000 Subject: [PATCH 15/17] ci: make coverage threshold check cross-platform, fix Windows c8 path dup MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Replace the bash inline in the _coverage_check Makefile recipe with scripts/check-coverage-threshold.mjs. Windows runners invoked the recipe under PowerShell, which can't parse $$(...) substitutions or backslash line continuations. - Flip c8.all from true to false in package.json. On Windows, c8's filesystem walker registered files via one path string while V8's per-process coverage records used a slightly different normalization, so every source file appeared twice in the report — once at 0%, once with real coverage — halving the global total. Turning off the all-files enumeration relies on V8 records alone, which match each other consistently. --- Makefile | 3 +-- package.json | 2 +- scripts/check-coverage-threshold.mjs | 26 ++++++++++++++++++++++++++ 3 files changed, 28 insertions(+), 3 deletions(-) create mode 100644 scripts/check-coverage-threshold.mjs diff --git a/Makefile b/Makefile index ee027d8..5c52545 100644 --- a/Makefile +++ b/Makefile @@ -78,8 +78,7 @@ setup: # --------------------------------------------------------------------------- _coverage_check: - @THRESHOLD=$$(jq -r '.default_threshold' "$(COVERAGE_THRESHOLDS_FILE)"); \ - npx c8 check-coverage --lines $$THRESHOLD + @node scripts/check-coverage-threshold.mjs ## help: List all available targets help: diff --git a/package.json b/package.json index c87aa33..35fbf66 100644 --- a/package.json +++ b/package.json @@ -230,7 +230,7 @@ "out/git/types.js", "**/*.d.ts" ], - "all": true, + "all": false, "reporter": [ "text", "text-summary", diff --git a/scripts/check-coverage-threshold.mjs b/scripts/check-coverage-threshold.mjs new file mode 100644 index 0000000..3029ad8 --- /dev/null +++ b/scripts/check-coverage-threshold.mjs @@ -0,0 +1,26 @@ +#!/usr/bin/env node +// Reads the global line-coverage threshold from coverage-thresholds.json +// and delegates to `c8 check-coverage`. Written in Node so the recipe +// works under bash, cmd.exe, and PowerShell on Windows runners. + +import { readFileSync } from 'node:fs'; +import { spawnSync } from 'node:child_process'; +import { resolve } from 'node:path'; + +const repoRoot = resolve(import.meta.dirname, '..'); +const thresholdsPath = resolve(repoRoot, 'coverage-thresholds.json'); +const thresholds = JSON.parse(readFileSync(thresholdsPath, 'utf8')); +const threshold = thresholds.default_threshold; + +if (typeof threshold !== 'number') { + process.stderr.write(`coverage-thresholds.json: default_threshold must be a number, got ${typeof threshold}\n`); + process.exit(1); +} + +const r = spawnSync('npx', ['c8', 'check-coverage', '--lines', String(threshold)], { + cwd: repoRoot, + stdio: 'inherit', + shell: process.platform === 'win32', +}); + +process.exit(r.status ?? 1); From 2212badc3497a7c8459763a73ac49ba67ce15823 Mon Sep 17 00:00:00 2001 From: Christian Findlay <16697547+MelbourneDeveloper@users.noreply.github.com> Date: Tue, 26 May 2026 19:20:18 +1000 Subject: [PATCH 16/17] ci: normalize V8 coverage URLs to fix Windows path duplication MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Even with c8.all=false, the Mocha (Node) and Electron extension-host runs were emitting V8 coverage URLs in two different shapes on Windows — backslash-separated paths with mixed drive-letter case from one, forward-slash paths from the other. c8 keys files by URL, so the same file was counted twice and per-process coverage couldn't merge. Walk $NODE_V8_COVERAGE after both test runs and rewrite every `url` field to a canonical lowercase-drive forward-slash form. POSIX records are already canonical, so this is a no-op outside Windows. --- scripts/run-coverage.mjs | 37 ++++++++++++++++++++++++++++++++++++- 1 file changed, 36 insertions(+), 1 deletion(-) diff --git a/scripts/run-coverage.mjs b/scripts/run-coverage.mjs index efa5eca..2747ab1 100644 --- a/scripts/run-coverage.mjs +++ b/scripts/run-coverage.mjs @@ -8,12 +8,45 @@ // merges the lot. import { spawnSync } from 'node:child_process'; -import { rmSync, mkdirSync } from 'node:fs'; +import { rmSync, mkdirSync, readdirSync, readFileSync, writeFileSync } from 'node:fs'; import { resolve } from 'node:path'; const repoRoot = resolve(import.meta.dirname, '..'); const tempDir = resolve(repoRoot, 'coverage', 'tmp'); +// On Windows the mocha (Node) and electron-host (Electron) processes write V8 +// coverage records with slightly different URL normalizations — backslashes vs +// forward slashes, mixed drive-letter case. c8 keys files by URL, so the same +// physical file ends up reported twice with halved coverage. Rewrite every +// `url` field to a canonical lowercase forward-slash form before c8 merges. +const canonicalUrl = (url) => { + if (typeof url !== 'string' || !url.startsWith('file://')) { + return url; + } + return url.replace(/\\/g, '/').replace(/^file:\/\/\/?[A-Za-z]:/, (m) => m.toLowerCase()); +}; + +const normalizeCoverageDir = (dir) => { + for (const name of readdirSync(dir)) { + if (!name.endsWith('.json')) continue; + const path = resolve(dir, name); + const raw = readFileSync(path, 'utf8'); + const data = JSON.parse(raw); + if (!Array.isArray(data.result)) continue; + let changed = false; + for (const entry of data.result) { + const next = canonicalUrl(entry.url); + if (next !== entry.url) { + entry.url = next; + changed = true; + } + } + if (changed) { + writeFileSync(path, JSON.stringify(data)); + } + } +}; + const run = (cmd, args, env) => { process.stdout.write(`\n==> ${cmd} ${args.join(' ')}\n`); const r = spawnSync(cmd, args, { @@ -37,4 +70,6 @@ const covEnv = { NODE_V8_COVERAGE: tempDir }; run('npm', ['run', 'test:unit'], covEnv); run('npm', ['run', 'test:e2e'], covEnv); +normalizeCoverageDir(tempDir); + run('npx', ['c8', 'report'], {}); From efa7f36c2b6354191b90a3db7e619b6eb88f84db Mon Sep 17 00:00:00 2001 From: Christian Findlay <16697547+MelbourneDeveloper@users.noreply.github.com> Date: Wed, 27 May 2026 19:59:16 +1000 Subject: [PATCH 17/17] ci: drop the macOS/Windows matrix, run a single Ubuntu job MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The branch ruleset required a status check literally named "CI", but the matrix only ever produced "CI (ubuntu-latest)", "CI (macos-latest)", and "CI (windows-latest)" — the bare "CI" context was never reported, so PRs sat in "Expected — Waiting for status to be reported" forever. Collapse the workflow to one Ubuntu job named "CI". The job emits the exact context the ruleset wants, all steps run sequentially in a single runner, and the Windows-specific workarounds added earlier (gitattributes, shell:win32 spawn, fsPath separator normalization, V8 URL canonicalization) stay in the codebase so contributors developing on Windows still have a working local loop. --- .github/workflows/ci.yml | 27 ++++++++------------------- 1 file changed, 8 insertions(+), 19 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index b9a8ad3..2e7bd9d 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -14,12 +14,8 @@ concurrency: jobs: ci: name: CI - runs-on: ${{ matrix.os }} + runs-on: ubuntu-latest timeout-minutes: 10 - strategy: - fail-fast: false - matrix: - os: [ubuntu-latest, macos-latest, windows-latest] steps: - uses: actions/checkout@v4 @@ -30,7 +26,6 @@ jobs: - run: npm ci - # E2E suite runs under xvfb on Linux so @vscode/test-electron can launch. - name: Format check run: make fmt CHECK=1 @@ -38,34 +33,28 @@ jobs: run: make lint - name: Dry-run release stamp (proves the script still runs) - shell: bash run: node scripts/stamp-release-version.mjs 0.0.0 --dry - - name: Test (Linux, xvfb) - if: runner.os == 'Linux' + # E2E suite needs xvfb so @vscode/test-electron can launch a headless Electron. + - name: Test run: xvfb-run -a make test - - name: Test (macOS / Windows) - if: runner.os != 'Linux' - run: make test + - name: Build + run: make build - name: Upload coverage uses: actions/upload-artifact@v4 if: always() with: - name: coverage-report-${{ matrix.os }} + name: coverage-report path: | coverage/ lcov.info retention-days: 7 - - name: Build - run: make build - - name: Upload build artifacts uses: actions/upload-artifact@v4 with: - name: build-${{ matrix.os }} - path: | - out/ + name: build + path: out/ retention-days: 7

1t69syKWFjdd~0q#``{R9@N?U}A*XGuMzdTT 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�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