-
-
Notifications
You must be signed in to change notification settings - Fork 2
Comparing changes
Open a pull request
base repository: Brooooooklyn/simple-git
base: v1.0.0
head repository: Brooooooklyn/simple-git
compare: v1.1.0
- 10 commits
- 67 files changed
- 3 contributors
Commits on Jul 6, 2026
-
feat: simple-git.napi.rs website (landing + docs + Cloudflare deploy) (…
…#146) * docs: add simple-git website design spec Design spec for simple-git.napi.rs — a marketing + docs site modeled on image.napi.rs (void + Vite + React 19 + Tailwind v4 + Shiki, deployed to Cloudflare). Landing + Getting Started, teal-on-slate theme, animated benchmark + doc-site 'last updated' showcase (no WASM playground). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * docs: add website implementation plan (7 tasks) Executable task breakdown for subagent-driven-development, cloning the image.napi.rs void site for @napi-rs/simple-git. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * feat(website): scaffold void Yarn workspace with blank / and /docs routes Task 1 of the marketing+docs site: add a minimal void (VoidZero) workspace under website/ that installs, builds, and serves two placeholder routes. - website/package.json (@napi-rs/simple-git-website), void.json, vite.config.ts, tsconfig.json, pages/index.tsx (<h1>), pages/docs/index.md. - Root package.json: add "workspaces": ["website"] (rewrites yarn.lock). - .gitignore: website build artifacts (.void/.wrangler/test-results/etc). No theme/content, no playground, no WASM — those come in later tasks. The native library is untouched (still require()s cleanly). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(website): move build-artifact ignores into website/.gitignore (keep root .gitignore untouched) Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * docs(plan): website ignores go in website/.gitignore, not root Resolves the plan-internal contradiction Codex flagged in the Task 1 review. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * feat(website): design system + root layout (theme tokens, fonts, shiki, header/footer) Task 2 visual foundation for the @napi-rs/simple-git site (dark-only, teal-cyan): - app.css: Tailwind v4 @theme tokens (cool slate near-black + teal accent), fluid clamp() type-scale, film-grain overlay, @layer components helpers (.container-page/.site-header/.eyebrow/.reveal), CSS-only mobile-nav rules, and self-hosted @font-face for the three variable fonts. - lib/highlight.ts: workerd-safe Shiki JS-regex-engine singleton (github-dark). - public/favicon.svg: new git-branch mark, teal on #04211f. - public/fonts/*.woff2: latin-subset variable fonts sourced from @fontsource-variable, preloaded via void.json head.link (stable paths). - pages/layout.tsx + pages/_components/Footer.tsx: sticky blurred header with JetBrains-Mono wordmark + Docs/GitHub/npm nav, CSS-only mobile drawer, and footer (brand + tagline + links + "Built with napi-rs · MIT licensed"). No analytics. - tsconfig.json: include lib/. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * feat(website): content data, shared UI primitives, Shiki loader (task 3) Add the content + primitives layer the landing sections (tasks 4-5) consume: _data modules: - samples.ts — hero + 5 tab code samples, byte-verbatim from README.md - benchmarks.ts — 1.9 s vs 65 ms (1000×), ~29× derived - features.ts — the 9 feature cards - platforms.ts — 15 napi triples grouped by OS (cross-checked vs package.json), Node >= 10 - docSites.ts — Nextra/Docusaurus/Starlight/Fumadocs/Rspress (name + URL + text glyph) index.server.ts — loader pre-highlights every sample server-side (Shiki JS-regex engine, no runtime WASM) and returns HTML as props; head (title + description). _components — reusable UI: CodeBlock (server-rendered highlighted HTML) plus the _-prefixed interactive islands _CopyButton, _InstallSwitcher, _CountUp, _Reveal, _TabbedCodeBlock. Landing is a regular Void route (full hydration), so islands need no `with { island }` — matches the reference/Task 2 convention. No-JS safe (code + reveals visible without JS); no analytics; no WASM. index.tsx untouched — section wiring is Task 4. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * feat(website): landing sections A — Hero, Benchmark, Doc-site showcase Build the first three landing sections and wire them into pages/index.tsx (Task 4). Remaining sections land in Task 5. - Hero: eyebrow, "Git for Node, at native speed." (accent "native"), tagline, Get started/GitHub/npm buttons, InstallSwitcher, heroHtml CodeBlock, and three CountUp stat tiles (29× / 15 / 0 — all grounded in _data + package.json). - Benchmark: "~30× faster than shelling out"; IntersectionObserver bar animation (git CLI 1.9 s full bar vs simple-git 65 ms sliver, proportional to ms); big 29× CountUp; caption from _data/benchmarks.ts (README Performance · performance.mjs). - DocSiteShowcase: "Powers 'Last updated on' for your docs"; mock doc footer stamp "Last updated on Jul 6, 2026 by LongYinan"; getFilesLatestModified bulk CodeBlock (new docSiteSample, byte-verbatim from README, highlighted via the loader); row of the 5 generators from _data/docSites.ts (text-glyph monograms, no remote logos). Shared presentational components: SectionHeader, Button, Chip (un-prefixed, no client state). Interactive pieces reuse the Task 3 islands (_CountUp/_Reveal/ _InstallSwitcher) via the regular-route full-hydration model — no island markers. No-JS safe: all copy, code, benchmark labels/values and generator names are in the SSR HTML; bars render at final proportional widths and CountUp shows final values with JS off; reveals are gated behind html.js. No WASM, no analytics, no remote images. Verify: tsc --noEmit clean; void:prepare + vite build succeed (SSR + client); dev SSR renders all three sections with 0 console errors; reveals fire on scroll. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(website): source benchmark durations from data; align hero stat copy to spec Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(website): drop uppercase transform so hero stat labels match spec copy Renders '29× faster / 15 platforms / 0 dependencies' in the approved lower case (numbers still sourced from benchSpeedup / platformCount / 0). tsc + build pass. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * feat(website): landing sections B — Features, CodeSample, PlatformMatrix, CtaBand Append the remaining four landing sections after Hero/Benchmark/DocSiteShowcase so pages/index.tsx is complete end-to-end (Hero → Benchmark → DocSiteShowcase → Features → CodeSample → PlatformMatrix → CtaBand; Footer from layout). - Features: 3×3 responsive card grid from _data/features.ts (9 cards). - CodeSample: _TabbedCodeBlock over statusHtml/commitHtml/blameHtml/pushHtml/errorsHtml ([Status] [Stage & commit] [Blame] [Push] [Typed errors]); first tab visible with JS off. - PlatformMatrix: 15 triples grouped by 6 OS from _data/platforms.ts; count + Node engine derived. - CtaBand: centered InstallSwitcher + [Read the docs]->/docs and [GitHub] buttons. All reuse existing shared/island components + data modules; no island markers, no forbidden tech, no-JS safe (all content in SSR HTML). tsc/void:prepare/build clean. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * feat(website): Getting Started docs page (/docs) Replace the Task-1 placeholder with the full Getting Started content and add a centered prose layout for the /docs route. - docs/layout.tsx: single-column .void-md prose shell (header/footer come from the root layout). @void/md applies the prose theme; code is highlighted at build time by voidMarkdown() with Shiki's pure-JS regex engine — no runtime WASM, page reads fully with JS disabled. - docs/index.md: Install (npm/yarn/pnpm/bun), 15 prebuilt triples + Node >= 10, 60-second example, flagship getFilesLatestModified doc-site "last updated", async & AbortSignal, typed errors (isGitError/GitErrorCode), dispose()/free()/ using, and Full API links to the GitHub README + npm. In-page [[toc]]. All code/API grounded in README.md and index.d.ts. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * docs(website): add index.d.ts link to the Getting Started Full API section Full API now links GitHub README + type definitions (index.d.ts) + npm, matching the plan/spec requirement. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * docs(plan): Task 7 may add .github/workflows/void-deploy.yml at root Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * feat(website): Void deploy workflow, Playwright smoke test, inline-code feature cards Task 7 of the @napi-rs/simple-git website build. - .github/workflows/void-deploy.yml: mirror ../Image deploy workflow, only changing the project to napi-simple-git. Push to main on website/** changes, Node 24, corepack, root `yarn install --immutable`, `yarn void deploy --project napi-simple-git` in website/, GitHub OIDC (id-token: write). - website/playwright.config.ts + website/e2e/smoke.spec.ts: deterministic smoke test over the Vite/Void SSR dev server. Asserts `/` 200 + hero H1 "Git for Node, at native speed." + "01 — BENCHMARK" eyebrow, and `/docs` 200 + the "Getting Started" heading. 2 passed. - website/pages/_components/Features.tsx: render backtick-delimited API names in feature `desc` strings as styled inline <code> (split on backtick, wrap odd segments). No markdown lib, no data change; server-rendered and no-JS safe. Responsive pass verified empirically (Chromium 320/360/375/414 on / and /docs): no horizontal body scroll, wide content scrolls within its own overflow-x:auto container, mobile drawer + tap targets work. No CSS changes needed. tsc clean; void:prepare + build succeed. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(website): make tabbed code block no-JS safe (show all panels without JS) The API section's tabbed code block hid inactive panels with the HTML `hidden` attribute and required JS to switch tabs, so with JavaScript disabled only the first of five code samples was ever visible. Emit all five panels with no `hidden` attribute and drive show/hide from app.css, gated on the existing `html.js` class (set before first paint by the layout's inline script, the same mechanism `.reveal` uses). Without JS the tab bar is hidden and every panel shows stacked, each with a visible per-panel label; with JS only the active panel (data-active) shows and clicking a tab swaps instantly, with no flash of all five before hydration. ARIA tablist/tab/tabpanel wiring is preserved. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * docs: add SEO fixes implementation plan (6 tasks) Grounded in the multi-agent SEO audit findings (verified). Covers head/social meta, canonical, JSON-LD, robots/sitemap, prerender/CWV, and a11y — all inside website/. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * feat(website): SEO/social static assets (og image, raster icons, robots, sitemap) Add the static asset layer the SEO/meta tags will reference: - public/og.png (1200x630) on-brand social share card - public/apple-touch-icon.png (180x180) + public/favicon.png (32x32) from favicon.svg - public/robots.txt + public/sitemap.xml (/, /docs @ lastmod 2026-07-06) - scripts/gen-og.mjs: reproducible generator using the installed Playwright chromium (no new dependency), fonts embedded as data URIs Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(website): replace ungrounded "Zero-copy" OG chip with "15 platforms" Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * feat(website): SEO head meta layer — base social tags + home canonical/og Move route-invariant tags (charset, theme-color, og:type/site_name/image*, twitter:card/image, apple-touch-icon + PNG favicon fallback) into the void.json base head; add per-route canonical + og:url and benefit-copy og:title on home, and rewrite the description const. Delete the tags that moved to base so each renders exactly once (void appends links, overrides meta by name/property). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * feat(website): per-route head for /docs (canonical, og:url, trimmed description) SEO Task 3: give /docs its own canonical + og:url and a right-length description via a co-located server head, since void markdown frontmatter silently drops head/link/og keys. - Add website/pages/docs/index.server.ts head export: - re-declares title 'Getting Started' and the docs description (a server head REPLACES the frontmatter-derived head) - link canonical https://simple-git.napi.rs/docs (non-trailing-slash form) - meta description + og:title/og:description + og:url .../docs - Trim description to 146 chars (was 175, clipped in SERPs) by dropping the trailing "Powered by libgit2 and Rust." clause; keep index.md frontmatter in sync. Docs body unchanged. Void's head merge keeps every route-invariant base social tag (og:type, og:site_name, twitter:card, og:image, theme-color, charset) and wraps the title with the "%s | @napi-rs/simple-git" template. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * feat(website): server-render SoftwareApplication + WebSite JSON-LD SEO Task 4: emit two schema.org JSON-LD blocks from the root layout so both / and /docs are eligible for rich results. Fields grounded in package.json (name, version 1.0.0, MIT, repo, npm) and the 15 napi targets (OS list). Serialized via JSON.stringify; server-only, no client JS. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * perf(website): prerender / and /docs, make static responses edge-cacheable Both landing (/) and docs (/docs) carry no request- or time-varying data yet were re-rendered per request (prerender=false + routing.revalidate {"*":0}), re-running Shiki on every hit. Switch both to prerender=true so they render once per deploy and serve from the edge cache, and drop the revalidate:0 block that (per void's "TTL 0 is skipped" rule) would otherwise suppress prerender. Islands (CountUp/InstallSwitcher/tabs) still hydrate client-side; no-JS render and the /docs/ 308 redirect are unchanged. Worker asset precedence (run_worker_first: ["/**"]) is generated by void and NOT exposed via void.json (worker/routing are additionalProperties:false, no assets knob); left as-is per brief, to raise upstream. See task-5 report. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * feat(website): a11y + CWV micro-fixes (skip link, focus ring, nav labels, new-tab cues, hero LCP, font/grain) - Skip link (first fragment child) + <main id="main-content"> (WCAG A) - Zero-specificity :where() :focus-visible ring in app.css Base (WCAG 2.4.7) - aria-label="Primary" on desktop + drawer navs, "Footer" on footer nav - Centralized "opens in a new tab" sr-only cue: Button (target=_blank), layout NAV, Footer links, DocSiteShowcase used-by row; rel="noreferrer" kept - Hero above-fold code column: <Reveal> -> plain <div> so LCP isn't opacity:0 JS-gated; dropped unused Reveal import; below-fold reveals untouched - Removed mix-blend-mode: soft-light from body::after film grain - font-display: swap -> optional on all three @font-face blocks (no mid-view swap => no CLS) Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * docs(plan): add SEO Task 7 — agent-friendliness (llms.txt) Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * feat(website): add llms.txt, llms-full.txt, robots pointer (SEO task 7) Expose the site to LLM agents via the llmstxt.org convention: - website/public/llms.txt — curated map (summary, docs links, grounded Core API surface, install commands); every API name verified against index.d.ts, facts (1.0.0, MIT, 15 targets, Node >= 10) per package.json. - website/public/llms-full.txt — full Getting Started docs in one plain markdown fetch, derived from pages/docs/index.md (frontmatter + [[toc]] stripped, retitled, code blocks intact). - website/public/robots.txt — comment pointer to /llms.txt after Sitemap. All served at web root (dist/client). Build + Playwright e2e green; library untouched; only website/public/ changed. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(website): make llms.txt spec-conformant; correct typed-error claim (docs + llms) Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * ci(website): deploy on root lockfile/package-manager changes too The Void deploy filter only watched website/**, but the site's dependencies resolve from the shared root yarn.lock. A lockfile-only website dep update (Renovate in-range bump or monthly lockFileMaintenance) edits only root yarn.lock, so it matched neither website/** nor the workflow file and never deployed — leaving the live site on stale deps. Add the root package-manager files to the deploy paths. Root/native-only lockfile bumps now redeploy an unchanged site, which is a harmless no-op. Addresses codex review finding on PR #146 (void-deploy.yml path filter). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * ci: fold void-deploy.yml into CI.yml Consolidate the website (Void) deploy into the main CI workflow and drop the standalone void-deploy.yml. Behavior is preserved: - Deploy runs on push to main only (never on pull_request) and stays independent of the native build/test jobs (no `needs` on them), so a Rust CI failure can't block a website deploy — same as the old workflow. - A small `changes` gate job (dorny/paths-filter) scopes the deploy to commits that affect the site (website/** or the shared root package-manager files); GitHub can't path-filter one job of a multi-job workflow, so the former on.paths filter moves here. - OIDC (id-token: write) and the VOID_* env stay scoped to the deploy job, so the native jobs keep their default token permissions. - Workflow-level concurrency (cancel-in-progress) already gives the deploy "latest push wins", replacing the old per-workflow group. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(website): scope the typed-error feature card to Git-layer errors The "Typed, catchable errors" card claimed every failure carries a GitErrorCode narrowable via isGitError(), but an aborted *Async call rejects with napi's AbortError (code === 'Cancelled'), which isGitError does not match. Scope the claim to Git-layer errors and note the abort exception — consistent with the docs, llms.txt and index.d.ts. Addresses codex review finding on PR #146 (features.ts typed-error claim). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * docs(plan): merge website build + SEO plans into one Fold the two same-day website plans into a single 2026-07-06-simple-git-website-plan.md with a shared, de-duplicated Context + Global Constraints preamble and two phases — Phase 1 (build) and Phase 2 (SEO, a11y & agent-friendliness) — each keeping its Task 1–7 and done-criteria verbatim. Delete the standalone SEO plan. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * chore: ignore root .void codegen output `void prepare`/`void build` can drop a `.void/project.json` at the repo root; ignore it like the other build artifacts (target/, *.node). The website workspace's own .void is already covered by website/.gitignore. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * ci: deploy to the correct Void project (simple-git) The deploy job targeted VOID_PROJECT=napi-simple-git, but that project does not exist — the real Void project is `simple-git` (https://simple-git.void.app). CI deploys would have failed at `void deploy --project napi-simple-git`. Point the deploy job (and the spec/plan references) at `simple-git`. Verified by a manual `void deploy --project simple-git`, which deployed successfully. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * ci: let website markdown-only changes trigger the deploy After folding the deploy into CI.yml, the workflow-level `paths-ignore: **/*.md` skipped the entire run for a website-docs-only push (e.g. website/pages/docs/index.md), so `dorny/paths-filter` never ran and the site was not redeployed. Replace paths-ignore with a positive `paths` filter: `**` triggers, root docs/markdown are excluded, and `website/**` is re-included last so website markdown still deploys. Native CI still skips pure root-doc/README changes. Addresses codex review finding on PR #146 (CI.yml deploy path gate). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * docs(spec): API Reference page design (/docs/api) Hand-authored, comprehensive /docs/api reference (single page + sidebar), grounded verbatim against index.d.ts + the README GitErrorCode table. Approach approved: hand-authored curated markdown, single page. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * feat(website): point npm links at npmx.dev Swap the package host from www.npmjs.com to npmx.dev (same /package/@napi-rs/simple-git path; canonical form verified — the bare /@scope/name variant 301s to /package/...). Covers the top nav, hero, footer, JSON-LD downloadUrl, the docs "Full API" link, and llms.txt / llms-full.txt. Labels stay "npm". Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * docs(plan): API Reference implementation plan (7 tasks) SDD plan for /docs/api: sidebar layout + scaffold, Repository (A/B), git objects & handles (object model / operations), interfaces+enums+ functions, error handling + polish. Each task grounded-verified against index.d.ts and codex-gated. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * feat(website): docs sidebar layout + /docs/api scaffold Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * feat(website): API reference — Repository (part A) Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(website): tighten grounding of init/findTree/findCommit prose (API ref part A) Codex grounding gate flagged prose beyond the JSDoc for three thin-doc methods. init now states only what README ("init a git repository") + the signature support; findTree/findCommit keep the null-if-not-found note but tie it explicitly to their `T | null` return type (also grounded by the README's `findTree(oid)!` / `findCommit(oid)!` usage). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * feat(website): API reference — Repository (part B) Document the second half of the Repository class in website/pages/docs/api.md: state & inspection, config/signature, remotes, branches/checkout/references, tags, diffs, revwalk and resource cleanup. Signatures copied verbatim from index.d.ts; prose grounded in JSDoc/signatures/README. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * feat(website): API reference — git object handles (object model) Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(website): verbatim TreeIter decl + drop ungrounded rename reflog claim (API ref objects) Codex gate: (1) the TreeIter fence now shows the complete verbatim class declaration from index.d.ts (incl. the body and next()); (2) removed the "msg is recorded in the reflog" note on Reference.rename — true in libgit2 but not stated in the rename JSDoc or README, so ungrounded. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(website): byte-match TreeIter fence to index.d.ts (blank line) Final codex nit ([low]): index.d.ts emits a blank line after the TreeIter class opening brace; mirror it so the fenced declaration is byte-identical. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * feat(website): API reference — remote, index, config, diff & options classes Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(website): drop RevWalk's redundant 93-line class-body fence (API ref) The RevWalk entry dumped the entire class body (with all JSDoc) as one fence AND repeated every member in its own #### section — a Task-4 over-correction. The prose one-liner already states the `extends Iterator<string, void, void>` inheritance and each member is documented individually, so the giant fence was pure redundancy. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(website): correct Diff intro — no tree-to-tree diff exists (API ref) Codex gate: the Diff class intro claimed deltas "between two trees, or a tree and the working directory", but the only Diff-producing APIs are diffTreeToWorkdir and diffTreeToWorkdirWithIndex (both tree-to-workdir); there is no tree-to-tree diff method. Dropped the ungrounded "two trees". Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(website): drop undocumented constructor default-state claims (API ref) Codex gate [low]: the options-class constructors asserted default states (default clone/fetch/push settings, no proxy, no callbacks) that aren't in the constructor JSDoc/README. Reduced each to "Create a new X." — grounded in the bare constructor signature; the setters below document configuration. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * feat(website): API reference — options/result types, enums & functions Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(website): DiffFlags belongs to DiffDelta.flags(), not DiffFile (API ref) Codex gate: the DiffFlags enum note said "flags on a DiffFile", but the flags are exposed via DiffDelta.flags() (index.d.ts:349-356) and DiffFile has no flags() accessor. Corrected the lead to DiffDelta (the rest of the sentence already referenced DiffDelta.flags()). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * feat(website): API reference — error handling section + whole-page polish Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(website): error example used findRemote (returns null, never throws) The typed-error try/catch example branched on GitErrorCode.NotFound after calling repo.findRemote('does-not-exist'), but findRemote returns Remote | null (index.d.ts:1091) — it never throws for a missing remote, so the catch would not fire. Replace the call with a neutral placeholder (matching the README's error-handling example) in the new /docs/api Error handling section AND in the pre-existing Getting Started page + llms-full.txt where the same broken example had shipped. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * ci: build + e2e the website on PRs (pre-merge gate) The website's build/test:e2e were only ever exercised via `yarn void deploy`, which is gated to push-to-main — so a PR breaking the Vite/TS/ markdown build passed native CI and only failed at deploy. Add a `website` job (build + Playwright smoke of / , /docs, /docs/api) that runs whenever website-affecting paths change, on PRs and pushes alike. The `changes` gate now fires for all triggers (not just main pushes) so it can drive the PR gate; `deploy` gains an explicit push+main guard to stay main-only. The website has no dependency on the native .node binary (library imports appear only inside doc code fences), so the job needs no Rust build. Addresses codex review finding on PR #146 (website not built in PR CI). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * docs: merge API-reference spec+plan into the website spec+plan The API Reference was a follow-on sub-project of the website, tracked in its own spec/plan pair. Fold each into its website parent so there is one spec and one plan for the whole site: - spec: API-reference design becomes §11 of the website design spec; the superseded "no full API-reference" non-goal and the route list updated. - plan: API-reference tasks become Phase 3 (7 tasks + constraints + done criteria) of the website plan; intro now says three phases. Deletes the two standalone -api-reference-{design,plan}.md files. No content lost — merged verbatim, re-leveled to fit. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * ci: gate Void deploy on the website smoke job `deploy` only had `needs: changes`, the same as the `website` job — so on a push to main both started in parallel once change-detection finished, and `void deploy` could publish before the Playwright smoke (/ , /docs, /docs/api) even finished, let alone passed. A later e2e failure then left a broken site already live. Add `website` to deploy's `needs` so the build + smoke must succeed first. When website changed the website job always runs (needs satisfiable); when it didn't, deploy's existing push+main+website==true `if` skips it regardless, so no deadlock. Addresses codex review finding on PR #146 (deploy not gated on e2e). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Configuration menu - View commit details
-
Copy full SHA for 6881589 - Browse repository at this point
Copy the full SHA 6881589View commit details -
fix(deps): update dependency @void/md to v0.10.5 (#147)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Configuration menu - View commit details
-
Copy full SHA for 8300a3f - Browse repository at this point
Copy the full SHA 8300a3fView commit details -
fix(deps): update dependency @void/react to v0.10.5 (#148)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Configuration menu - View commit details
-
Copy full SHA for 638b632 - Browse repository at this point
Copy the full SHA 638b632View commit details
Commits on Jul 7, 2026
-
Configuration menu - View commit details
-
Copy full SHA for 4ca5860 - Browse repository at this point
Copy the full SHA 4ca5860View commit details -
Configuration menu - View commit details
-
Copy full SHA for 723f6b3 - Browse repository at this point
Copy the full SHA 723f6b3View commit details -
fix(deps): update dependency void to v0.10.5 (#151)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Configuration menu - View commit details
-
Copy full SHA for 9b5a48b - Browse repository at this point
Copy the full SHA 9b5a48bView commit details -
chore(deps): update dorny/paths-filter action to v4 (#150)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Configuration menu - View commit details
-
Copy full SHA for 3ca64c5 - Browse repository at this point
Copy the full SHA 3ca64c5View commit details -
feat: add
created(first-add commit) to FileModification (#152)* docs: plan for FileModification.created field * feat: add `created` (first-add commit) to single-file getFileLatestModified Add a `created: CommitInfo | null` field to `FileModification`, carrying the commit that FIRST added the file. Task 1 wires the single-file path end to end (sync method + async task); the bulk path is a later task. - New `#[napi(object)] CommitInfo` (8 commit-metadata fields). - `build_commit_info` factors the 8-field extraction; `build_modification` routes through it (created: None). - `get_file_creation`: separate oldest-first walk (Sort::TOPOLOGICAL | Sort::REVERSE, no Sort::TIME) resolving the exact repo-root-relative path via tree.get_path, first-hit early-exit, merges included, no rename-follow. - `get_file_modification_with_created`: merges creation into a Some record only; never-committed paths stay null. Shared by the sync method and async run() so both are identical. - Regenerated index.d.ts (CommitInfo interface + created?). index.js unchanged (object types have no JS runtime export). - Tests: new `created` test (shape + non-CI CLI parity vs `git log --diff-filter=A --reverse`); bulk-vs-single cross-checks scoped with a withoutCreated helper until the bulk path is enriched. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * feat: extend `created` (first-add commit) to bulk getFilesLatestModified Task 2: carry the `created` creation record through the BULK path (sync + async), matching the single-file path from Task 1. - file_modification.rs: add `get_files_creation` (oldest-first Sort::TOPOLOGICAL|REVERSE bulk walk, exact tree.get_path, merges included, first/oldest containing commit wins) and `get_files_modification_with_created` (bulk modification walk + creation walk over present paths only, merged into each record's `created`). - repo.rs: sync `get_files_latest_modified` and async `GitBulkModificationTask::run` both call the shared `_with_created` wrapper so results are byte-identical across the async boundary. - modification.spec.mjs: remove the temporary `withoutCreated` helper, restore full deepEqual in the 3 bulk-vs-single cross-checks, add explicit bulk/async `created` assertions and the __proto__-record-carries-created regression. Bindings regenerated (index.js/index.d.ts unchanged: no public-surface delta). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * test: multi-commit `created` semantics + document created on API surface Add hermetic multi-commit tests characterizing `created` (the first-adding commit) as distinct from the last modification, and document the semantics on the public method doc comments (regenerated into index.d.ts). Tests (__test__/modification.spec.mjs), via a new makeRepoWithHistory helper that builds a controlled history and returns each commit's OID: - created is the first-adding commit, distinct from the last modification (C1 adds f.txt, C2 modifies it; created==C1, commitId==C2) - created returns the ORIGINAL add across a delete-then-re-add (C1/C2/C3 -> C1) - created equals the modification commit for a root-only file - bulk created matches single-file created on a multi-commit repo Docs: document `created` on getFileLatestModified (adds delete/re-add, merges included, no rename-follow to the existing exact-path + glob caveat) and add the missing `created` paragraph to getFilesLatestModified. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * docs(file-modification): created is undefined (not null) when absent `FileModification.created` is an `Option<CommitInfo>` on a `#[napi(object)]` struct; `None` serializes by OMITTING the property, so JS sees `created === undefined` (never `null`), matching the sibling Option fields ("Undefined if absent"). The docs wrongly said "null" and mislabeled the no-history case. - file_modification.rs: rewrite the `created` field doc to say "Undefined", scope the undefined case to glob/directory input only (exact path always present), and clarify a no-history path yields NO record at all (whole FileModification is null), not a present record with the field missing. - repo.rs get_file_latest_modified: "created may be null" -> "left undefined". - add a regression test pinning created === undefined + no own property for a glob/pathspec input ("*.rs"). - regenerate index.d.ts from the Rust docs (no logic change). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(file-modification): created resolves an exact FILE (blob) only The two creation walks resolved `created` on ANY `tree.get_path()` hit, so a DIRECTORY (tree entry) or submodule (gitlink/Commit entry) input produced a BOGUS `created` record (the entry's own creation commit) instead of undefined. Guard both `get_file_creation` and `get_files_creation` to resolve only when `entry.kind() == Some(git2::ObjectType::Blob)`; a non-blob Ok entry keeps walking (never resolves for a pure directory -> stays None/undefined). Symlinks are blobs (correctly files); directories/submodules are excluded. The modification walks are untouched (their directory->root behavior is pre-existing and out of scope). Also corrects the `created` doc rationale on both the field and getFileLatestModified (a directory DOES match a tree entry -- the old "matches no tree entry" reason was wrong; a creation is an exact FILE/blob only) and the plan doc (CommitInfo | undefined; FIELD undefined vs bulk map VALUE null). Adds regression tests (RED on the pre-fix binary): a directory input ("src") and a hermetic nested file-vs-directory ("dir/a.txt" resolves, "dir" does not). makeRepo now creates parent dirs so nested paths work. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(file-modification): created is undefined for a dir/submodule AT HEAD The per-commit blob check rejected a pure directory but not a path whose TYPE changed across history: a path that was a FILE (blob) long ago but is a DIRECTORY (tree) or submodule (gitlink) AT HEAD still found the OLD blob in an ancestor tree and returned a bogus `created`, breaking the documented directory/submodule -> undefined contract. Add a HEAD-kind gate at the start of both creation walks (get_file_creation, get_files_creation): peel HEAD to its tree and return None / drop the path when its entry is a tree or gitlink AT HEAD. A path ABSENT at HEAD (a deleted file) is NOT gated -- its original blob creation still resolves. The per-commit blob check stays (directory->file transitions still resolve to the commit the path became a file). Docs (created field, get_file_latest_modified, plan semantics) reworded: the caveat is decided by the path's entry kind AT HEAD, and a deleted file still reports its original creation. Regenerated index.d.ts. Tests: file->dir (created undefined; RED before fix), deleted-file-stays-resolved (created == original add), dir->file (created == the commit it became a file); makeRepoWithHistory extended for type-transition steps. 167 tests (was 164). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * docs(repo): correct created-resolution note for dir/submodule at HEAD get_files_latest_modified doc no longer claims a resolved path always has a `created` commit. After the HEAD-kind gate, a present record's `created` is undefined when the exact path is a directory (tree) or submodule (gitlink) at HEAD; a deleted path still resolves its original creation. Regenerated index.d.ts. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * docs: reconcile bulk-method dir note + add created/CommitInfo to README Two doc-only fixes for the FileModification.created feature (no logic change): - get_files_latest_modified doc no longer claims a directory "will not match" (it can resolve to a present bulk record); instead states a directory (tree) or submodule (gitlink) path is not a file, whose `created` is `undefined` -- consistent with the `created` paragraph that follows. Regenerated index.d.ts. - README: add the `created?: CommitInfo` field to the FileModification snippet and a mirrored `CommitInfo` interface (its 8 creating-commit fields). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(file-modification): evil-merge-only files fall back to the creating merge commit A file introduced only by a merge commit (present at HEAD, in neither parent) returned null from getFileLatestModified: the modification walk skips merge commits, so no non-merge commit ever touches such a path and the None arm returned null without consulting the creation walk. Both _with_created wrappers now fall back to the file's creating (merge) commit for the WHOLE record when the modification walk finds nothing but the file still exists, so commitId === created.commitId. The bulk wrapper now runs the creation walk over ALL input paths (dropping the present-only optimization) so absent-but-existing merge-only paths can fall back; never-committed paths still resolve to null. Modification walks, creation walks, and the bare-date methods are unchanged; async inherits the fix via the shared wrappers. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(file-modification): gate merge-only fallback on present-at-HEAD The evil-merge fallback (build the record from the creating commit when the modification walk finds nothing) over-fired for a merge-only file that was LATER DELETED by a merge: both the add and the delete are merges the modification walk skips (-> None), yet get_file_creation still resolves the old add (it allows paths absent at HEAD, to support deleted files' `created`). So getFileLatestModified reported the stale add-merge instead of null. Gate the None-branch fallback on the path being a blob AT HEAD: only a file still PRESENT at HEAD may fall back to its creating commit; an absent path (deleted, even by a merge) stays null. The ATTACH path (modification walk returned Some) is unchanged, so a normally-deleted file's `created` still resolves to its original add. Bulk peels the HEAD tree lazily (once, only when a fallback candidate first appears) so empty / all-present / all-never-committed inputs never peel HEAD (which would throw on an unborn HEAD). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * refactor(file-modification): peel HEAD once in single-file merge-only fallback Extract the reverse blob-walk of `get_file_creation` into a private `oldest_blob_commit` helper (no HEAD gate). `get_file_creation` now peels HEAD, applies its HEAD-kind gate, then delegates to the helper. The single-file `None`-branch fallback in `get_file_modification_with_created` peels HEAD ONCE (reusing the tree for its blob gate; the oldest-blob walk pushes HEAD itself) instead of peeling twice via `get_file_creation` + `path_is_blob_at_head`. Removed the now-dead `path_is_blob_at_head`. Pure refactor -- no behavior change, tests unchanged (172/172), clippy clean. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * docs(file-modification): narrow committerTime↔date-method parity for merge-only files The merge-only fallback made getFileLatestModified resolve a file present only via merges, but the cheap date twins (getFileLastModifiedDate / getFileLatestModifiedDate + async) keep the raw merge-skipping walk and do NOT apply that fallback. So committerTime is NOT strictly "identical to getFileLastModifiedDate" for such a file — the record has the creating merge's time while the date methods return null / throw. Narrow the three parity claims (the committerTime field, getFileLastModifiedDate, getFileLatestModified) to state the merge-only exception, and note on both date methods that they keep raw merge-skipping semantics. Docs-only — no behavior change. Add a test pinning the intentional divergence so routing the date methods through the fallback would fail and force a docs update. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * docs(file-modification): correct getFileLatestModified null-condition for merge-only-absent paths The summaries said null happens "only when no commit ever added the path", but b293ebf's blob-at-HEAD gate makes a file added by a merge and deleted by a merge (added in history, yet absent at HEAD) return null — the deleted-by-merge test asserts exactly that. The summary contradicted its own merge-only detail paragraph. Correct the null-condition on all four rich accessors (getFileLatestModified + async, getFilesLatestModified + async): null ⟺ no non-merge commit recorded a change AND the path is absent at HEAD (never added, OR only merge commits touched it and it is absent at HEAD, e.g. deleted by a merge). A path present at HEAD always resolves. Docs-only — no behavior change. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * docs(file-modification): scope "deleted file resolves created" to ordinary deletes The created docs said "a DELETED file (absent at HEAD) still reports its ORIGINAL creation", but a merge-only-deleted file returns null (no record, no created). Only a file deleted by an ordinary (non-merge) commit still yields a record whose created resolves. Tighten the three spots (created field, single + bulk methods) to say "deleted by an ordinary (non-merge) commit" — matching the tested deletes (all non-merge). Docs-only. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * test(file-modification): pin getFilesLatestModified([]) on an unborn HEAD The existing empty-input test runs on a populated repo, so it never exercised the unborn-HEAD case the bulk lazy-peel guards: a no-op call must return {} without peeling HEAD (an unconditional peel would throw "unborn HEAD"). Add a no-commits repo assertion locking that the lazy peel stays lazy. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(file-modification): merge-only fallback flat fields track the LATEST change, not creation A path touched only by merge commits, where one evil merge ADDS it and a later evil merge CHANGES it (still present at HEAD), built the whole FileModification from oldest_blob_commit (the creation), so commitId/committerTime pointed at the original add instead of the merge that last changed the file. Same flaw in bulk. Add latest_blob_change_including_merges: newest-first walk INCLUDING merges that returns the first commit whose blob OID at the path differs from that path's blob OID in EVERY parent (a root/normal add, or an evil-merge change; an inherited blob is skipped). Rewire both fallbacks so flat fields come from it while created stays oldest_blob_commit / get_files_creation. Add blob_oid_at helper, reused for the blob-at-HEAD gates. commitId === created.commitId now holds only when a single merge both added and last-changed the file; docs updated accordingly. Tests: double-evil-merge fixture (M1 adds v1, M2 changes to v2) asserting commitId===M2, created===M1, distinct, single==bulk. Suite 174 -> 176. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(file-modification): merge-only latest-change walk is mode-aware ((oid,mode), not oid) latest_blob_change_including_merges compared only the blob OID at the path, so a merge that changed ONLY the file mode (100644 -> 100755, same blob OID) looked "inherited" and was skipped, wrongly falling back to the older add-merge for the flat fields. The non-merge diff walk DOES count a mode-only delta, so merge-only files diverged from ordinary diff semantics. Compare the full tree-entry identity (blob OID, filemode): rename blob_oid_at -> blob_entry_at returning (Oid, i32); a commit is a genuine change iff its (oid, mode) at the path differs from every parent's. The two fallback HEAD blob-gates use blob_entry_at(...).is_some(). Creation stays mode-agnostic (oldest_blob_commit / get_file_creation untouched). Add a mode-only evil-merge fixture + single/bulk tests (M1 adds evil.txt @100644, M2 chmods to 100755, same blob): commitId == M2, created == M1. Suite 176 -> 178. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>Configuration menu - View commit details
-
Copy full SHA for 659d5ca - Browse repository at this point
Copy the full SHA 659d5caView commit details -
fix(deps): update void to v0.10.6 (#153)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Configuration menu - View commit details
-
Copy full SHA for e580290 - Browse repository at this point
Copy the full SHA e580290View commit details -
Configuration menu - View commit details
-
Copy full SHA for 3b8e55c - Browse repository at this point
Copy the full SHA 3b8e55cView commit details
This comparison is taking too long to generate.
Unfortunately it looks like we can’t render this comparison for you right now. It might be too big, or there might be something weird with your repository.
You can try running this command locally to see the comparison on your machine:
git diff v1.0.0...v1.1.0