diff --git a/.agent-state/decisions.ndjson b/.agent-state/decisions.ndjson new file mode 100644 index 00000000..d9abb0a0 --- /dev/null +++ b/.agent-state/decisions.ndjson @@ -0,0 +1 @@ +{"ts":"2026-06-09T23:13:09.916Z","sha":"2c9a43adaccebc7bda8227d07a70e0df990c4356","subject":"feat: DOCX-first resume pipeline with visual QC and recruiter-driven restructure","decision":"DOCX compiled from typed TS data via turbodocx, not pandoc or raw docx lib","why":"pandoc drops CSS; docx lib broke Apple Pages previously (fdcd220); turbodocx was the validated path and QC now guards it","resolves":["user directive to make DOCX the sole","properly-styled distributable"],"overrides":[]} diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 649241d7..f5b0347d 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -35,31 +35,24 @@ jobs: node-version: 22 cache: pnpm - - name: Setup pandoc - uses: pandoc/actions/setup@main - - name: Install dependencies run: pnpm install --frozen-lockfile - name: Install Playwright browsers run: pnpm exec playwright install --with-deps chromium + - name: Compile resume DOCX + run: pnpm resume:build + - name: Build site run: pnpm build - name: Generate og-image run: pnpm generate:og && cp public/og-image.png dist/og-image.png - - name: Generate resume DOCX with pandoc - run: pandoc dist/resume/index.html -f html -t docx -o dist/Jon_Bogaty_Resume.docx - - - name: Generate resume PDF via Playwright - run: pnpm exec tsx scripts/generate-resume-pdf-only.ts - - name: Verify generated artifacts run: | - ls -la dist/Jon_Bogaty_Resume.pdf dist/Jon_Bogaty_Resume.docx dist/og-image.png - test -s dist/Jon_Bogaty_Resume.pdf || { echo "PDF is empty"; exit 1; } + ls -la dist/Jon_Bogaty_Resume.docx dist/og-image.png test -s dist/Jon_Bogaty_Resume.docx || { echo "DOCX is empty"; exit 1; } test -s dist/og-image.png || { echo "og-image is empty"; exit 1; } @@ -84,5 +77,4 @@ jobs: cd dist && tar czf "../site-${TAG_NAME}.tar.gz" . && cd .. gh release upload "$TAG_NAME" \ "site-${TAG_NAME}.tar.gz" \ - "dist/Jon_Bogaty_Resume.pdf" \ "dist/Jon_Bogaty_Resume.docx" diff --git a/.github/workflows/resume.yml b/.github/workflows/resume.yml index f5c4e2cd..95bd5dbf 100644 --- a/.github/workflows/resume.yml +++ b/.github/workflows/resume.yml @@ -3,10 +3,9 @@ name: Resume Generator on: pull_request: paths: - - 'src/content/resume.json' - - 'scripts/generate-resume*.ts' + - 'src/content/resume.ts' + - 'scripts/resume/**' - 'src/lib/dates.ts' - - 'src/pages/resume.astro' permissions: contents: write @@ -38,41 +37,55 @@ jobs: node-version: 22 cache: pnpm - - name: Setup pandoc - uses: pandoc/actions/setup@main + # pull_request `paths` filters match the whole PR diff, so this job + # re-runs on its own regenerate commits. The guard + content-aware diff + # below break that loop (the DOCX zip is not byte-deterministic). + - name: Skip if triggered by own regenerate commit + id: guard + run: | + if [ "$(git log -1 --format='%an')" = "github-actions[bot]" ]; then + echo "skip=true" >> "$GITHUB_OUTPUT" + else + echo "skip=false" >> "$GITHUB_OUTPUT" + fi - name: Install dependencies + if: steps.guard.outputs.skip != 'true' run: pnpm install --frozen-lockfile - - name: Build site (includes resume page) - run: pnpm build - - - name: Generate DOCX with pandoc - run: pandoc dist/resume/index.html -f html -t docx -o public/Jon_Bogaty_Resume.docx - - - name: Copy PDF from build output - run: cp dist/Jon_Bogaty_Resume.pdf public/Jon_Bogaty_Resume.pdf 2>/dev/null || true + - name: Compile resume DOCX + if: steps.guard.outputs.skip != 'true' + run: pnpm resume:build - - name: Check for changes + - name: Check for content changes + if: steps.guard.outputs.skip != 'true' id: diff run: | - if git diff --quiet public/Jon_Bogaty_Resume.pdf public/Jon_Bogaty_Resume.docx; then + # Compare document content, not zip bytes (zip timestamps differ + # on every build even when the document is identical). + new_xml=$(mktemp); old_xml=$(mktemp) + unzip -p public/Jon_Bogaty_Resume.docx word/document.xml > "$new_xml" + git show HEAD:public/Jon_Bogaty_Resume.docx > /tmp/old.docx 2>/dev/null \ + && unzip -p /tmp/old.docx word/document.xml > "$old_xml" 2>/dev/null \ + || : > "$old_xml" + if cmp -s "$new_xml" "$old_xml"; then echo "changed=false" >> "$GITHUB_OUTPUT" + git checkout -- public/Jon_Bogaty_Resume.docx else echo "changed=true" >> "$GITHUB_OUTPUT" fi - name: Commit and push - if: steps.diff.outputs.changed == 'true' + if: steps.guard.outputs.skip != 'true' && steps.diff.outputs.changed == 'true' run: | git config user.name "github-actions[bot]" git config user.email "github-actions[bot]@users.noreply.github.com" - git add public/Jon_Bogaty_Resume.pdf public/Jon_Bogaty_Resume.docx - git commit -m "chore: regenerate resume PDF and DOCX" + git add public/Jon_Bogaty_Resume.docx + git commit -m "chore: regenerate resume DOCX" git push - name: Comment on PR - if: steps.diff.outputs.changed == 'true' + if: steps.guard.outputs.skip != 'true' && steps.diff.outputs.changed == 'true' uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 with: script: | @@ -80,5 +93,5 @@ jobs: owner: context.repo.owner, repo: context.repo.repo, issue_number: context.issue.number, - body: 'Resume PDF and DOCX have been automatically regenerated from updated source files.' + body: 'Resume DOCX has been automatically regenerated from updated source files.' }) diff --git a/.gitignore b/.gitignore index db34554d..f787f0e4 100644 --- a/.gitignore +++ b/.gitignore @@ -38,3 +38,17 @@ yarn-error.log* coverage playwright-report test-results + +# Resume QC render output +artifacts/ +.ui-design/reviews/*.png +.ui-design/reviews/snapshot.txt +.ui-design/reviews/review_state.json + +# Agent state cache (re-derivable) +.agent-state/cursor.md +.agent-state/digest.md +.agent-state/.indexed-sha +.agent-state/.last-stop-sha +.agent-state/.last-compaction +.agent-state/batch.json diff --git a/.ui-design/reviews/ai-look-diagnosis_20260609.md b/.ui-design/reviews/ai-look-diagnosis_20260609.md new file mode 100644 index 00000000..9404390a --- /dev/null +++ b/.ui-design/reviews/ai-look-diagnosis_20260609.md @@ -0,0 +1,138 @@ +# Design Review: Why the Site Reads as "AI-Generated" + +**Review ID:** ai-look-diagnosis_20260609 +**Target:** Entire site (Astro + React islands, shadcn/ui, Tailwind v4) +**Focus:** Comprehensive, with emphasis on diagnosing the "looks like an AI-generated website" feedback +**Evidence:** Live screenshots (desktop 1440px + mobile 390px, all tabs) in this directory; full source read; two independent reviewer passes (visual + copy) + +## Summary + +The feedback is accurate, and the cause is identifiable. The site stacks **nearly every signature element of the 2024–25 v0/Lovable/Claude-artifact template aesthetic simultaneously**, and the copy carries the strongest LLM writing tells ("battle-tested", "production-ready", "Track record of...", pipe-stacked headline). No single element is fatal — it's the *co-occurrence* of ~10 recognizable patterns that pattern-matches instantly for anyone who has seen AI-generated sites. The good news: the underlying design system (Instrument Serif + amber-on-black + JetBrains Mono) is genuinely distinctive and should be kept; the tells are mostly surface decoration and copy, all cheap to fix. + +**Issues found:** 14 — Critical: 4 · Major: 6 · Minor: 4 + +--- + +## The Diagnosis: Co-occurring AI Tells, Ranked by Contribution + +### Critical (these four carry ~70% of the impression) + +#### 1. Animated gradient text on the hero name +**Location:** `src/index.css:157-164` (`.hero-name`) +**Category:** Visual + +Amber→white→steel animated `background-clip: text` gradient, shimmering on an 8s loop, applied to your own name. This is the single most recognizable AI-portfolio tell — it's in the majority of v0/Lovable outputs. Human designers almost never animate gradient text, and never on the person's name. + +**Fix:** Static `var(--foreground)` (or static amber). Instrument Serif at 7xl is already the visual interest. ~10 min. + +#### 2. The full background-effect stack: mesh-gradient shader + dot grid (×2) + gradient orbs +**Location:** `HeroSection.tsx:24-66` (ShaderBg via `@paper-design/shaders-react` MeshGradient), `HeroSection.tsx:73-79` (hero dot grid), `index.css:105-114` (`body::before` page-wide dot grid), `index.css:117-127` (`body::after` corner orbs) +**Category:** Visual + +Four ambient background treatments running at once. The dot grid is v0's default template background; corner gradient orbs are the default dark-mode treatment in multiple AI starters; mesh-gradient shaders are the Paper-design/v0 signature. Stacked together this is "maximum AI vibes." + +**Fix:** Pick **one** treatment and delete the rest. The MeshGradient shader is the most distinctive of the four — if kept, delete both dot grids and `body::after`. Or delete everything and let the rich black breathe. ~15 min. + +#### 3. The glassmorphism hero stat-card row +**Location:** `HeroSection.tsx:121-142` +**Category:** Visual + +Four equal `rounded-lg border bg-card/40 backdrop-blur-sm` boxes — big number + mono-uppercase label ("15+ YEARS / $100K+/mo CLOUD SAVED / 5 PYPI PACKAGES / 5 OSS FRAMEWORKS"). This is the canonical AI "credibility metrics" module, and the `+` suffixes read as stat inflation. "5 PyPI packages" and "5 OSS frameworks" given equal billing with 15 years of career also reads as padding to fill the grid. + +**Fix:** Kill the cards. Either weave the stats into the tagline prose, or render as a borderless horizontal typographic band (number in serif, label in mono, thin rules between). Drop the `+` where the number is already real. ~45 min. + +#### 4. The copy — strongest tells of all +**Location:** `src/content/resume.ts` +**Category:** Content + +Technical readers smell LLM copy faster than LLM layout: +- **Headline:** `"Staff DevOps & Platform Engineer | Multi-Cloud Infrastructure Architect | Python/Go Open-Source Builder"` — pipe-stacked LinkedIn-SEO keyword format, rendered in tracked-out uppercase mono so it dominates the hero. +- **Tagline:** `"Founding-DevOps operator who ships OSS frameworks."` — noun-phrase mashup nobody says aloud. +- **Summary:** `"Track record of..."` (top-flagged AI resume phrase), semicolon-chained parallel triads (`"builder of...; architect of...;"`), `"before industry-wide adoption"`, `"comprehensive internal automation platform"`. +- **Project descriptions:** `"Battle-tested Python monorepo..."`, `"Production-ready framework..."`, `"Complete game framework..."` — the three marquee LLM adjectives, one per card, in a row. +- **Highlights:** `"Spearheaded SRE initiatives..."`, `"pioneering IaC before industry-wide adoption"`, `"cutting onboarding time significantly"` (weasel adverb in place of a number). +- **Skills:** `"Agentic AI Orchestration"`, `"Prompt Engineering"`, `"LLM-Driven Workflows"` as badge text. +- Pervasive `·` middot chains and uniform triadic sentence rhythm ("X, Y, and Z" in nearly every sentence). + +**Fix:** Rewrite in plain declarative voice using only facts already in resume.ts. One title, not three. Lead the summary with the concrete Flipside arc (sole DevOps engineer, the Python CLI, the cost reduction) instead of adjective stacks. Delete "battle-tested"/"production-ready"/"complete" outright — the feature lists after them already do the work. ⚠️ The reviewer-suggested rewrites in the appendix below contain *illustrative* numbers — verify every fact against resume.ts before adopting any of them; do not introduce new metrics. + +### Major + +#### 5. Pulsing status pill ("Available · Independent OSS") +**Location:** `HeroSection.tsx:88-97`, `index.css:167-173` +The rounded-full bordered pill with animated green pulse dot, centered above the name, is the most-copied single element from Framer/AI templates. Keep the information, lose the packaging: a quiet mono line without border or pulse. + +#### 6. Badge chip-cloud Skills tab +**Location:** `SkillGrid.tsx` (all of it) +~80 identical secondary-variant pill badges in flex-wrap clusters inside uniform cards. The "chip cloud" is the most-cloned AI portfolio section; every skill gets identical visual weight, which communicates that no human prioritized them. Fix: comma-separated prose lists for secondary categories; reserve badges (or any emphasis) for a deliberately curated short list. + +#### 7. Staggered fade-up entrance on every hero element +**Location:** `HeroSection.tsx` (delays 0 / 0.3 / 0.5 / 0.6 / 0.7) +The uniform sequential y-offset reveal is the default Motion demo choreography AI tools emit. One considered entrance (the name) beats five mechanical ones. + +#### 8. Hash-rotated accent bar on every project card +**Location:** `ProjectGrid.tsx:7-19, 32` +A 2px top color bar in one of 5 rotating brand colors assigned by string hash — color as arbitrary decoration rather than meaning. Either make color mean something (category) or drop it; a left border (as SkillGrid headline cards already use) reads as classification rather than template. + +#### 9. Tab-based information architecture with near-empty tabs +**Location:** `App.tsx` +Six tabs, each wrapped in the identical `max-w-6xl py-10` + `font-heading text-2xl` h2 shell. "Education" is a 21-line component; "Earlier Career" 20 lines. Uniform scaffolding around thin content is a structural AI tell (generated IA fills slots). It also hides your best material — the projects — behind a click. Most strong personal sites are an opinionated single scroll with varied section treatments. At minimum: collapse Education + Earlier Career into Work, and let section layouts differ from each other. + +#### 10. Everything is a shadcn Card with lucide icons +**Location:** ProjectGrid, SkillGrid, JobList, footer +Default-radius bordered cards + lucide icons at default sizes + `Badge variant="secondary"` everywhere is the unmistakable stock shadcn skin. The components are fine; the zero-customization defaults are the tell. The Work tab's sidebar/detail layout is the one section that escapes this — it reads designed. Use it as the quality bar. + +### Minor + +11. **`·` middot separator chains** in footer/status/about ("DevOps · SRE · Platform Engineering · AI") — LLM-formatting signature; use sentences or real layout. +12. **"All rights reserved" + version stamp footer** (`SiteFooter.tsx`) — boilerplate that adds genericness; the build-version stamp is a nice human touch though, keep that. +13. **Bottom gradient fade strip** on the hero (`HeroSection.tsx:205`) — template-y; unnecessary once the background stack is simplified. +14. **Footer "Connect/Resume" 3-column grid** — stock footer-template structure for what is four links and two buttons. + +--- + +## What's Genuinely Good — Keep It + +- **Instrument Serif at display size** — immediately differentiating; just don't animate it. +- **Amber #E8A849 on rich black #0B0D14** — a real palette decision, not a default. +- **JetBrains Mono micro-labels** — reads engineering-authentic. +- **Work tab sidebar/detail layout** — the most "designed by a human" section on the site. +- **Package sub-listings inside project cards** (`ralph (CLI)…`, `@jbcom/agentic-*`) — concrete, shipped-code detail an AI wouldn't fake. This is your best anti-AI signal; give it *more* room, not less. +- **`departureContext` italic notes, reduced-motion handling, a11y labels** — careful human touches. + +The deeper point: **specificity is the antidote.** AI sites are generic because they decorate instead of show. Real terminal output from radioactive-ralph, a real architecture sketch, actual download counts pulled from PyPI, a screenshot of Strata's terrain — any one concrete artifact does more to read "human" than all the gradient removal combined. + +--- + +## Prioritized Fix Plan + +1. **Copy rewrite pass on resume.ts** (headline, tagline, summary, the three project-description adjectives, "Spearheaded"/"Track record" highlights). Highest impact, zero layout risk. Facts only from existing data. +2. **De-stack the backgrounds:** keep at most the shader; delete `body::before`, `body::after`, hero dot grid. +3. **Un-animate `.hero-name`**; reduce hero motion to a single entrance. +4. **Replace stat cards** with a typographic stat band (or fold into the tagline); drop `+` inflation; de-pill the status badge. +5. **De-chip the Skills tab** (prose lists for secondary categories) and swap project-card top bars for left borders or nothing. +6. **Structural pass (larger):** collapse 6 tabs → fewer, differentiated sections; consider single-scroll. Add one piece of real, concrete artifact per project (output, screenshot, live stat). + +Items 1–5 are roughly an afternoon and remove the bulk of the impression. Item 6 is the difference between "not AI-looking" and "memorable." + +--- + +## Appendix: Reviewer Copy Suggestions (UNVERIFIED — check facts before use) + +Full agent transcripts produced rewrite candidates for every flagged string. Treat them as tone references, not drop-ins: several contain invented specifics (e.g. "~$150K to ~$45K", "first DevOps hire at two startups", "2015") that must be verified against real history or discarded. + +| Flagged | Direction | +|---|---| +| `X \| Y \| Z` headline | One title: "Staff DevOps & Platform Engineer" + short clause | +| "Founding-DevOps operator who ships OSS frameworks" | Plain facts: years, role shape, what you build now | +| "Track record of modernizing..." | Say what happened at Flipside, concretely | +| "Battle-tested Python monorepo" | "Python monorepo with 5 independently-installable packages: ..." | +| "Production-ready framework for building intelligent agent fleets" | Name the actual capabilities, drop the adjectives | +| "Complete game framework" / "advanced water" | "React Three Fiber framework for procedural 3D worlds. Covers ..." | +| "Spearheaded SRE initiatives for a high-traffic..." | "Led SRE work at GoHealth: monitoring, alerting, reliability..." | +| "cutting onboarding time significantly" | Real outcome or cut the clause | +| "$100K+/mo" stat | "$100K/mo" if that's the real number — kill the `+` | + +--- + +_Generated by design review, 2026-06-09. Screenshots: shot-desktop-hero.png, shot-desktop-projects.png, shot-desktop-work.png, shot-desktop-skills.png, shot-mobile-hero.png._ diff --git a/README.md b/README.md index d7d178d5..01cd24dd 100644 --- a/README.md +++ b/README.md @@ -1,130 +1,68 @@ # jbcom.github.io -> Jon Bogaty's professional portfolio and jbcom ecosystem showcase +> Jon Bogaty's professional portfolio — [jonbogaty.com](https://www.jonbogaty.com) -## 🎯 Purpose +## Purpose -This site serves as: -1. **Professional Portfolio** - Resume, skills, experience -2. **Ecosystem Directory** - All jbcom packages with links to their repos -3. **Static Site** - Fast, accessible, zero JavaScript required +1. **Professional portfolio** — career history, skills, open-source projects +2. **Resume distribution** — a compiled, QC'd DOCX as the single distributable +3. **Ecosystem showcase** — the jbcom open-source frameworks -## 🏗️ Architecture: Static-First +## Architecture -This is a **pure static site** built for GitHub Pages. No React, no build tools, just HTML/CSS. +Astro 6 static site with React islands, shadcn/ui, and Tailwind CSS v4. ``` -/ -├── content/ # Content as source (markdown/YAML) -│ ├── resume.md # Resume source -│ ├── about.md # About page content -│ ├── vision.md # Ecosystem vision -│ └── ecosystem.yml # 20+ packages with metadata -├── templates/ # Pandoc templates for resume generation -│ └── resume-pdf.html # PDF generation template -├── assets/ -│ └── css/ -│ └── style.css # Complete design system implementation -├── *.html # Static HTML pages -└── .github/workflows/ - └── deploy.yml # Build & deploy (generates PDF/DOCX) +src/ +├── content/resume.ts # CANONICAL resume data — single source of truth +├── pages/ +│ ├── index.astro # Portfolio SPA shell (React island) +│ └── resume.astro # Print-optimized HTML resume view +├── components/ # React components (hero, tabs, sections) +└── layouts/Layout.astro # Meta, JSON-LD, OG tags + +scripts/resume/ +├── template.ts # resume.ts → Word-semantics HTML +├── build-docx.ts # HTML → Jon_Bogaty_Resume.docx (turbodocx) +└── qc.ts # DOCX → LibreOffice → PNG pages for visual review ``` -### Build Process - -1. **Content** - All content stored as markdown or YAML -2. **Generation** - GitHub Actions generates PDF/DOCX from markdown via pandoc -3. **Deployment** - Static HTML/CSS deployed to GitHub Pages - -**No JavaScript required** for core functionality. Fast page loads (<1s). - -## 🎨 Design System - -### Colors -- **Background**: Deep slate (#020617) -- **Surface**: Slate panels with glassmorphism (#0f172a) -- **Primary**: Cyan/Teal (#0ea5e9) -- **Secondary**: Deep blue (#3170aa) -- **Accent**: Purple (#7c3aed) +### Resume pipeline (DOCX-first, no PDF) -### Typography -- **Headings**: Space Grotesk - bold, technical, modern -- **Body**: Inter - clean, readable, professional -- **Code**: JetBrains Mono - monospace, developer-friendly - -### Components -- Glassmorphic cards with backdrop blur -- Gradient accents on hover states -- Responsive grid layouts -- Mobile bottom navigation - -## 📱 Responsive Design - -| Breakpoint | Layout | -|------------|--------| -| xs (0-599px) | Bottom nav, single column | -| sm (600-899px) | Collapsible drawer, 2 columns | -| md (900-1199px) | Persistent sidebar, 2-3 columns | -| lg (1200px+) | Full sidebar, 3+ columns | - -## 🚀 Development +The DOCX is the resume. There is deliberately no PDF target. ```bash -# Install dependencies -npm install - -# Start dev server -npm run dev - -# Build for production -npm run build - -# Preview production build -npm run preview - -# Deploy to GitHub Pages -npm run deploy +pnpm resume:build # compile public/Jon_Bogaty_Resume.docx from resume.ts +pnpm resume:qc # render the actual DOCX to PNGs (artifacts/resume-qc/) + # — read them before shipping; requires LibreOffice + poppler ``` -## 📁 Structure +`src/content/resume.ts` is typed and commented; positioning decisions are +documented inline and in `docs/resume-review/`. Hard rule: **every fact must +be real** — no invented metrics, titles, or dates. Site-only content (extra +projects, site-flavor copy) is controlled with `onResume: false` and +`resumeDescription` fields rather than forked data. -``` -src/ -├── components/ -│ ├── Layout.tsx # Main layout with responsive sidebar -│ └── StrataBackground.tsx # The 3D layered background -├── data/ -│ └── ecosystem.ts # Package catalog -├── pages/ -│ ├── HomePage.tsx # Landing with hero -│ ├── AboutPage.tsx # Bio and skills -│ ├── EcosystemPage.tsx # Package directory -│ ├── ProjectPage.tsx # Individual package -│ └── DemosPage.tsx # Interactive strata demos -├── theme.ts # Material UI theme -├── main.tsx # Entry point -└── App.tsx # Router and layer composition -``` - -## 🐕 Dogfooding - -This site demonstrates what strata can do: -- The animated background uses strata components -- The demos page showcases interactive scenes -- All 3D is powered by React Three Fiber +### Commands -The best way to show what a library can do is to use it. +```bash +pnpm dev # dev server +pnpm build # static build (dist/) +pnpm test # vitest unit tests (includes DOCX structural QC) +pnpm test:e2e # Playwright +pnpm check # astro check + tsc +pnpm lint # biome +``` -## 📦 Tech Stack +### CI/CD -- **React 18** - UI framework -- **TypeScript** - Type safety -- **Vite** - Build tool -- **Material UI 5** - Component library -- **React Router 6** - Navigation -- **React Three Fiber** - 3D rendering -- **React Three Drei** - R3F helpers +- `ci.yml` — lint, typecheck, tests on PRs +- `resume.yml` — regenerates the DOCX on PRs touching resume sources +- `cd.yml` / `release.yml` — release-please + GitHub Pages deploy -## 📄 License +## Design system -MIT © Jon Bogaty +Always dark. Amber `#E8A849` on rich black `#0B0D14`, steel blue `#6B8BAD` +secondary. Instrument Serif headings, Inter body, JetBrains Mono code. +Print/DOCX accent is `#996B1D` (dark goldenrod — site amber is too light on +white). diff --git a/docs/resume-review/recruiter-review-2026-06-09.md b/docs/resume-review/recruiter-review-2026-06-09.md new file mode 100644 index 00000000..9eb290b4 --- /dev/null +++ b/docs/resume-review/recruiter-review-2026-06-09.md @@ -0,0 +1,194 @@ +# Resume Review: Recruiter + Copy Editor + Career Coach + +**Date:** 2026-06-09 +**Source reviewed:** `src/content/resume.ts` (canonical data — migrated from resume.json in PR #152) +**Reviewer stance:** senior technical recruiter screening Staff/Principal DevOps-Platform-SRE resumes; professional resume copy editor; career coach. Harsh-honest by request. No facts invented — `[needs real number]` marks missing data Jon must supply. + +--- + +## A. RECRUITER 6-SECOND SCAN + +**What a screener's eye hits, in order:** name → headline → current title/company → most recent dates → first 2 bullets of current role → years of experience stat. + +### The headline is the biggest immediate problem +``` +"Staff DevOps & Platform Engineer | Multi-Cloud Infrastructure Architect | Python/Go Open-Source Builder" +``` +This is the exact pipe-stacked, three-title pattern that reads as AI/self-promotional. Worse: it's **strategically incoherent**. A screener can't tell what you are. "Staff DevOps & Platform Engineer" is a real, searchable title. "Multi-Cloud Infrastructure Architect" and "Python/Go Open-Source Builder" dilute it. Recruiters boolean-search on *one* title bucket. Pick **one** primary identity. "Open-Source Builder" actively hurts for an employed-staff-role search — it signals hobbyist, not operator. + +The tagline compounds it: "Founding-DevOps operator" is strong and differentiated — that should be in the *headline*, not buried. "who ships OSS frameworks" is the wrong lead for a job seeker: it advertises the thing that looks like unemployment. + +### The "Independent / Open-Source" current role — the #1 risk +As written, it reads as **unemployment with a productivity story stapled on**: +1. Company name is literally `"Independent"` — the universal recruiter tell for "between jobs." +2. The summary opens with *"Full-time focus … following departure from Flipside Crypto"* — explaining the gap *inside the role* draws a circle around it. +3. Status "Available · Independent OSS" — "Available" + "Independent" together scream *looking, not working*. + +**This can be a strength, but only if reframed as deliberate.** A 15-year staff engineer who, after a layoff, spent months shipping a C/WASM supply-chain-secured password generator with SLSA L3 + Sigstore and a Go autonomous-dev orchestrator is demonstrating exactly the senior platform/security judgment the target roles want. The problem is 100% framing, not substance. Date math: Jan 2026–present = ~5 months as of June 2026 — short, explainable, recent. Don't let framing turn 5 months into a red flag. + +### The 5-year sole-engineer Flipside tenure — strongest asset, undersold +"First and only DevOps engineer," 5-year arc, $150K→$40-50K/mo, 10K-line tooling. **This is the story.** It's the antidote to both the job-hopping below it and the "hobbyist" risk. Flag: 8 dense bullets means the 6-second scan bounces off the wall of text. The sole-operator + $100K/mo savings + 146-module codegen must land in the first two bullets. + +### Job-hopping 2014–2020 — real pattern, currently unmitigated +- Magnetic: ~13mo · ClassPass: ~12mo · Qualia: ~11mo · Jump Ramp: **~6mo** · Symbiont: ~2.8yr ✓ · GoHealth: ~10mo + +**Five sub-14-month roles in ~6 years**, including a 6-month stint. A senior screener will read flight risk — the wrong signal for a Staff hire. Symbiont (2.8yr) and Flipside (5yr) partially rehabilitate it, but nothing currently frames it. Jump Ramp at 6 months is the weakest line item and an interview landmine. Fix: compress Magnetic + Jump Ramp (+ arguably Qualia) into `earlierCareer` so the visible history reads Flipside 5yr / GoHealth / Symbiont 2.8yr / ClassPass. + +### departureContext +"Role eliminated January 2026…" — correct instinct, **wrong placement**. A departure note on a resume draws attention to the ending, not the work, and contradicts the "deliberate independent period" story. **Delete `departureContext` from the resume**; handle layoff context in cover letter / recruiter screen. Only Flipside has it, which makes it stick out more. + +### Does the headline match search terms? +Partially. "Staff DevOps & Platform Engineer" — searchable. Missing from headline/summary lead: **SRE** (you have the title history), **Kubernetes**, **Terraform**, **Platform Engineering** as a phrase. The AI-infra angle is present but thin for an AI-infrastructure target — one genuine project. + +--- + +## B. ATS & STRUCTURE + +### Keyword coverage +**Well covered:** AWS, GCP, Azure, Terraform, Terragrunt, Kubernetes, EKS/GKE/AKS, Docker, Lambda, Python, Go, CI/CD, GitHub Actions, GitLab CI, Jenkins, ArgoCD, GitOps, Vault, secrets management, Prometheus, Grafana, Datadog, Snowflake, IAM, Zero-Trust, SLSA, Sigstore. + +**Gaps / under-weighted:** +- **"SRE" / "Site Reliability"** — only in one job title; not a named skill or summary competency. Add a reliability skill group (SLOs, error budgets, on-call, incident response). +- **"Platform Engineering"** as a literal skill keyword — implied everywhere, named nowhere. +- **Helm** — conspicuously absent for someone running EKS/GKE/AKS (add only if true). +- **OpenTelemetry, service mesh** — common Staff-Platform JD terms; add only if real. +- **FinOps / Cost Optimization** — you have the best cost story on the resume and the ATS keyword isn't present. + +### Section ordering & length +- The Independent role says "— see Projects," forcing a scroll past everything. Inline one-line project descriptors or move Projects directly under Work. +- **Strata Game Library is off-narrative** for DevOps/SRE/Platform — cut from the resume (keep on the site). +- The full document is almost certainly **3+ pages**. Target 2 for a 15-year senior IC. Cut: Strata, the weakest 2014–2017 one-bullet roles (fold to earlierCareer), departureContext, and merge 2-3 Flipside bullets. + +### Machine-parsing hazards +- Pipe characters in the headline — replace with a single title. +- `→` arrows and `$100K+/mo` shorthand can mangle in text extraction. Spell out: "from ~$150K to ~$40–50K per month." +- The hero stats block fragments ("15+ Years", "$100K+/mo Cloud saved") parse as noise if they reach the DOCX — ensure the generator renders them as a labeled line or drops them. +- Education at the bottom with an AAS — correct placement. Fine. + +--- + +## C. LINE-BY-LINE COPY EDIT + +### Summary +> "Track record of modernizing infrastructure as the sole DevOps engineer, authoring 10,000+ line Python tooling from scratch, and driving six-figure monthly cloud cost reductions…" + +"Track record of" is an LLM tell *and* a hedge; "six-figure monthly" launders a hard number into marketing-speak. +**Rewrite:** "As the sole DevOps engineer at Flipside Crypto, modernized AWS infrastructure, authored 10,000+ lines of Python tooling from scratch, and cut cloud spend by ~$100K/month." + +> "Early adopter of Docker and Terraform before industry-wide adoption" + +Unverifiable puffery, and **duplicated** verbatim at ClassPass. Keep once, at ClassPass, where the 2015 date anchor proves it. In summary: "Used Docker and Terraform in production from 2015." The date does the bragging. + +> "Building tools that bridge AI research and production engineering." + +Vague aspirational closer. If AI-infra is a real target: "Author of an AI agent orchestration framework (Agentic) spanning TypeScript, Python, and Rust." Else cut. + +> "Active open-source contributor and framework author publishing across…" + +"Active open-source contributor" + "Independent" role tips the reader toward "unemployed hobbyist," and undersells the truth. +**Rewrite:** "Author and maintainer of five published frameworks across Python, Go, TypeScript, and Rust, including a polyglot AI-agent orchestration toolkit and a 5-package data library." + +### Independent (current role) +> "Full-time focus on production-grade open-source infrastructure tooling following departure from Flipside Crypto." + +Explains the gap inside the role. **Rewrite:** "Independent open-source work focused on supply-chain security, reproducible builds, and autonomous agent orchestration — shipping production-grade tooling in Go, C/WASM, and Python." + +> "Authoring case studies on multi-cloud migration patterns…" + +"Authoring case studies" = blogging; reads as filler activity. Cut, or move published writing to a Writing/Talks section with links. + +> "— see Projects" + +Breaks the skim. Inline: "…paranoid-passwd, a C/WASM password generator with SLSA L3 provenance and Sigstore keyless signing." + +### Flipside Crypto +> "Joined as the first and only DevOps engineer; overhauled legacy AWS infrastructure … to 99% serverless…" + +Strong but a 40-word run-on with the best metric at the end. **Rewrite:** "First and only DevOps engineer; led a 5-year migration of legacy AWS infrastructure to ~99% serverless (Lambda + managed services)." `[verify: 99% — measured or estimate?]` + +> tm_cli bullet — your single best technical bullet. Tighten: "Built tm_cli — a 10,000+ line Python CLI/library that generates 146+ Terraform modules across 13 providers (AWS, GCP, GitHub, Vault, Slack) from annotated Python functions." + +> "$150K/month → $40-50K/month … $100K+/month in sustained savings" + +Nearly perfect — **move to bullet #1 or #2** (currently #5). ATS-safe rendering: "Cut AWS spend from ~$150K to ~$40–50K per month (~$100K/month sustained) via serverless migration, right-sizing, and autoscaling." + +> "Identified, placed, and mentored SRE engineers … while remaining the singular DevOps IC" + +Two ideas crammed; "Identified, placed" is HR-speak; and this is the **third repetition** of the sole-engineer point. Make the point once, powerfully. Keep: "Established company-wide CI/CD patterns, secrets-sharing workflows, and automation standards adopted across product and data teams." Mentoring only with a number: "Mentored [needs real number] engineers…" + +> "Administered all IT and security operations: Google Workspace, 1Password, DNS…" + +IT/Helpdesk-adjacent laundry list — down-levels a Staff Platform identity. +**Rewrite:** "Owned security and identity operations alongside the platform role: SSO/SCIM, HashiCorp Vault, Snowflake security, compliance audits, and incident response." + +> Fireblocks bullet — dense jargon, no outcome. **Rewrite:** "Led a cross-cloud Fireblocks co-signer migration (AWS Nitro Enclaves/Anjuna → Fireblocks-native on GCP) to [needs real outcome: reduce custody risk / cut cost / simplify compliance], spanning enclave architecture, GCP workload identity, and MDM device provisioning." + +### GoHealth +> "Spearheaded SRE initiatives … improved system scalability, availability, and operational visibility" + +Three abstractions, zero numbers — textbook unquantified SRE claim. +**Rewrite:** "Drove SRE reliability work for a high-traffic health-insurance marketplace, improving availability and observability during peak open-enrollment surges (uptime [needs real number]%, MTTR ↓ [needs real number])." + +### Symbiont — solid, keep. Optional: "…deployed for [needs real number] enterprise customers." + +### Jump Ramp +> "cutting onboarding time significantly" — canonical unquantified intensifier. Number or delete. And consider folding the whole 6-month role into earlierCareer. + +### ClassPass — "$20K/month Janitor Monkey" bullet is good, keep. The "pioneering IaC" claim lives here (with the 2015 date), not in the summary. + +### Magnetic — fold to earlierCareer, or: "Managed 300+ server fleet for a multinational ad-tech firm; led Chef cookbook overhaul and Rundeck+PAM automation." + +### Projects +- **radioactive-ralph:** drop the named-variants flavor (site-only). "Go autonomous dev orchestrator driving Claude Code across multiple repos with safety gates — Unix-socket IPC, SQLite event log, stream-json session control, launchd/systemd integration." +- **paranoid-passwd:** lead with the supply-chain stack, not the "LLM as adversary" gimmick: "C-core password generator compiled to <100KB WASM (Zig cross-compile, FIPS 180-4 SHA-256), with Wolfi/melange/apko supply chain, SLSA L3 provenance, Sigstore keyless signing, SBOM attestation." +- **Agentic:** kill "Production-ready"/"intelligent agent fleets": "Polyglot AI-agent orchestration framework (TypeScript, Python, Rust): fleet management, AI-powered triage, framework-agnostic crew orchestration, 4 GitHub Marketplace Actions." +- **Extended Data Library:** kill "Battle-tested": "Python monorepo of 5 independently published PyPI packages (serialization, structured logging, cloud connectors, Go secrets-sync) with strict typing and 75%+ test coverage." +- **Strata:** cut from resume entirely. + +--- + +## D. CAREER COACH POSITIONING + +### The one story this resume should tell +**"The engineer who runs your entire platform alone."** 5 years as the sole DevOps/platform/SRE/security function, a $100K/mo cost win, and a 10K-line codegen platform proving leverage. Companies pay Staff/Principal money for exactly this profile. Everything should ladder up to that thesis. + +**Secondary theme:** force-multiplication through tooling — tm_cli, terraform-pipeline, the OSS frameworks all evidence "I build the system that does the work." That's the Staff/Principal differentiator. + +**Do NOT lead with "AI-infra builder."** One genuine AI-infra project can't sustain it as a primary identity; recruiters for those roles will probe. "Platform engineer who also builds AI-agent orchestration tooling" is believable and differentiating. Revisit when Agentic has adoption numbers. + +### Cut entirely +1. Strata project (off-thesis) · 2. `departureContext` · 3. "Authoring case studies" bullet · 4. Pipe-stacked headline · 5. Google Workspace/1Password/DNS laundry · 6. Magnetic + Jump Ramp (+ Qualia) as full entries — fold to earlierCareer · 7. Duplicate "before industry-wide adoption" claim. + +### Missing — add (with real data only) +- **Scope numbers at Flipside:** engineers/teams served, # Lambda functions, # repos/environments. Scope = seniority signal. +- **Reliability metrics:** the resume has *zero* hard reliability numbers despite SRE positioning — uptime/SLA, MTTR, incident reduction at GoHealth and Flipside. Biggest credibility gap. +- **Independent-role reframe:** rename so it reads deliberate — "Independent (Self-Directed)" or the existing jonbogaty.com freelance entity so it reads consultancy, not gap. No mention of the Flipside departure in the body. +- **OSS adoption line** (only if non-trivial): "5 published frameworks · [stars] · [PyPI downloads/mo]". Real numbers convert hobby → adopted. If small, omit. +- **Certifications** if held (CKA, AWS, Terraform Associate). Don't fabricate. + +### One resume or variants? +**Two variants, one JSON source of truth** (the pipeline makes this cheap): +- **Variant A — "Platform/DevOps/SRE Operator"** (primary, ~90% of applications): headline "Staff Platform & DevOps Engineer," lead with sole-operator + cost + tooling, AI as supporting project, Strata cut. +- **Variant B — "Platform + Supply-Chain Security"**: elevate paranoid-passwd (SLSA/Sigstore/SBOM), Vault/secrets, Fireblocks enclave migration, and the security-ops scope. +- No separate AI-infra variant yet. + +--- + +## E. TOP 10 ACTIONS (ranked by impact) + +1. **Replace the pipe-stacked headline** with one searchable title: `"Staff Platform & DevOps Engineer — Sole-Operator Infrastructure at Scale"`. +2. **Reframe the Independent role**: rename to read deliberate; **delete "following departure from Flipside Crypto."** +3. **Delete `departureContext`** from Flipside. Cover-letter/verbal only. +4. **Reorder Flipside highlights**: $100K/mo cost cut and tm_cli codegen to #1 and #2. +5. **Cut Strata** from the resume (keep on site). +6. **De-slop the summary**: remove "Track record of," "before industry-wide adoption" (keep at ClassPass), "Active open-source contributor," "bridge AI research and production." +7. **Add reliability + scope numbers** (`[needs real number]` placeholders) at Flipside and GoHealth — zero hard reliability metrics is the biggest credibility gap for SRE-titled roles. +8. **Fold Magnetic + Jump Ramp (+ Qualia) into earlierCareer** — removes the 6-month landmine and breaks the job-hop optics. +9. **Compress the Flipside IT-admin bullet** to security & identity operations framing. +10. **Add literal skill keywords**: FinOps/Cost Optimization, SRE/SLOs/Error Budgets, Platform Engineering (+ Helm/OpenTelemetry only if true). + +--- + +## Bottom line + +The substance is genuinely strong — a 5-year sole-operator with a real $100K/mo win and a 10K-line internal platform is a Staff-grade profile most candidates can't match. The resume **undersells that and oversells the OSS-hobbyist angle**, with a headline that doesn't commit to an identity and a current-role framing that reads as unemployment. Fix the framing (1-3), lead with proof (4), cut off-thesis material (5, 8), and the same facts read as Staff/Principal instead of "talented generalist between jobs." The only place data is actually missing is reliability metrics (7) — get real numbers or the SRE positioning stays soft. diff --git a/package.json b/package.json index 0eed06c2..c267cdbb 100644 --- a/package.json +++ b/package.json @@ -9,7 +9,8 @@ "dev": "astro dev", "build": "astro build", "preview": "astro preview", - "generate:resume": "tsx scripts/generate-resume.ts", + "resume:build": "tsx scripts/resume/build-docx.ts", + "resume:qc": "tsx scripts/resume/qc.ts", "generate:og": "tsx scripts/generate-og-image.ts", "test": "vitest run", "test:watch": "vitest", @@ -42,9 +43,11 @@ "@biomejs/biome": "^2.4.5", "@playwright/test": "^1.58.2", "@tailwindcss/vite": "^4.2.1", + "@turbodocx/html-to-docx": "^1.21.0", "@types/node": "^25.0.3", "@types/react": "^19.2.14", "@types/react-dom": "^19.2.3", + "jszip": "^3.10.1", "playwright": "^1.58.2", "shadcn": "^3.8.5", "tailwindcss": "^4.2.1", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index c7a734e3..2b2d4a7e 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -10,7 +10,7 @@ importers: dependencies: '@astrojs/react': specifier: ^4.4.2 - version: 4.4.2(@types/node@25.0.3)(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(jiti@2.6.1)(lightningcss@1.31.1)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(tsx@4.21.0)(yaml@2.9.0) + version: 4.4.2(@types/node@25.0.3)(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(jiti@2.6.1)(lightningcss@1.31.1)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(terser@5.48.0)(tsx@4.21.0)(yaml@2.9.0) '@astrojs/sitemap': specifier: ^3.7.2 version: 3.7.2 @@ -28,7 +28,7 @@ importers: version: 0.0.71(@types/react@19.2.14)(react@19.2.4) astro: specifier: ^6.1.10 - version: 6.1.10(@types/node@25.0.3)(jiti@2.6.1)(lightningcss@1.31.1)(rollup@4.60.4)(tsx@4.21.0)(typescript@5.9.3)(yaml@2.9.0) + version: 6.1.10(@types/node@25.0.3)(jiti@2.6.1)(lightningcss@1.31.1)(rollup@4.60.4)(terser@5.48.0)(tsx@4.21.0)(typescript@5.9.3)(yaml@2.9.0) class-variance-authority: specifier: ^0.7.1 version: 0.7.1 @@ -65,7 +65,10 @@ importers: version: 1.58.2 '@tailwindcss/vite': specifier: ^4.2.1 - version: 4.2.1(vite@7.3.3(@types/node@25.0.3)(jiti@2.6.1)(lightningcss@1.31.1)(tsx@4.21.0)(yaml@2.9.0)) + version: 4.2.1(vite@7.3.3(@types/node@25.0.3)(jiti@2.6.1)(lightningcss@1.31.1)(terser@5.48.0)(tsx@4.21.0)(yaml@2.9.0)) + '@turbodocx/html-to-docx': + specifier: ^1.21.0 + version: 1.21.0 '@types/node': specifier: ^25.0.3 version: 25.0.3 @@ -75,6 +78,9 @@ importers: '@types/react-dom': specifier: ^19.2.3 version: 19.2.3(@types/react@19.2.14) + jszip: + specifier: ^3.10.1 + version: 3.10.1 playwright: specifier: ^1.58.2 version: 1.58.2 @@ -95,7 +101,7 @@ importers: version: 5.9.3 vitest: specifier: ^4.1.0 - version: 4.1.0(@types/node@25.0.3)(msw@2.12.10(@types/node@25.0.3)(typescript@5.9.3))(vite@7.3.3(@types/node@25.0.3)(jiti@2.6.1)(lightningcss@1.31.1)(tsx@4.21.0)(yaml@2.9.0)) + version: 4.1.0(@types/node@25.0.3)(msw@2.12.10(@types/node@25.0.3)(typescript@5.9.3))(vite@7.3.3(@types/node@25.0.3)(jiti@2.6.1)(lightningcss@1.31.1)(terser@5.48.0)(tsx@4.21.0)(yaml@2.9.0)) packages: @@ -1081,6 +1087,9 @@ packages: resolution: {integrity: sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==} engines: {node: '>=6.0.0'} + '@jridgewell/source-map@0.3.11': + resolution: {integrity: sha512-ZMp1V8ZFcPG5dIWnQLr3NSI1MiCU7UETdS/A0G8V/XWHvJv3ZsFqutJn1Y5RPmAPX6F3BiE397OqveU/9NCuIA==} + '@jridgewell/sourcemap-codec@1.5.5': resolution: {integrity: sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==} @@ -1125,6 +1134,38 @@ packages: resolution: {integrity: sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==} engines: {node: '>= 8'} + '@oozcitak/dom@1.15.5': + resolution: {integrity: sha512-L6v3Mwb0TaYBYgeYlIeBaHnc+2ZEaDSbFiRm5KmqZQSoBlbPlf+l6aIH/sD5GUf2MYwULw00LT7+dOnEuAEC0A==} + engines: {node: '>=8.0'} + + '@oozcitak/infra@1.0.3': + resolution: {integrity: sha512-9O2wxXGnRzy76O1XUxESxDGsXT5kzETJPvYbreO4mv6bqe1+YSuux2cZTagjJ/T4UfEwFJz5ixanOqB0QgYAag==} + engines: {node: '>=6.0'} + + '@oozcitak/infra@1.0.5': + resolution: {integrity: sha512-o+zZH7M6l5e3FaAWy3ojaPIVN5eusaYPrKm6MZQt0DKNdgXa2wDYExjpP0t/zx+GoQgQKzLu7cfD8rHCLt8JrQ==} + engines: {node: '>=6.0'} + + '@oozcitak/url@1.0.0': + resolution: {integrity: sha512-LGrMeSxeLzsdaitxq3ZmBRVOrlRRQIgNNci6L0VRnOKlJFuRIkNm4B+BObXPCJA6JT5bEJtrrwjn30jueHJYZQ==} + engines: {node: '>=8.0'} + + '@oozcitak/util@1.0.1': + resolution: {integrity: sha512-dFwFqcKrQnJ2SapOmRD1nQWEZUtbtIy9Y6TyJquzsalWNJsKIPxmTI0KG6Ypyl8j7v89L2wixH9fQDNrF78hKg==} + engines: {node: '>=6.0'} + + '@oozcitak/util@1.0.2': + resolution: {integrity: sha512-4n8B1cWlJleSOSba5gxsMcN4tO8KkkcvXhNWW+ADqvq9Xj+Lrl9uCa90GRpjekqQJyt84aUX015DG81LFpZYXA==} + engines: {node: '>=6.0'} + + '@oozcitak/util@8.0.0': + resolution: {integrity: sha512-+9Hq6yuoq/3TRV/n/xcpydGBq2qN2/DEDMqNTG7rm95K6ZE2/YY/sPyx62+1n8QsE9O26e5M1URlXsk+AnN9Jw==} + engines: {node: '>=6.0'} + + '@oozcitak/util@8.3.3': + resolution: {integrity: sha512-Ufpab7G5PfnEhQyy5kDg9C8ltWJjsVT1P/IYqacjstaqydG4Q21HAT2HUZQYBrC/a1ZLKCz87pfydlDvv8y97w==} + engines: {node: '>=6.0'} + '@open-draft/deferred-promise@2.2.0': resolution: {integrity: sha512-CecwLWx3rhxVQF6V4bAgPS5t+So2sTbPgAzafKkVizyi7tlwpcFpdFqq+wqF2OwNBmqFuu6tOyouTuxgpMfzmA==} @@ -2115,6 +2156,10 @@ packages: '@ts-morph/common@0.27.0': resolution: {integrity: sha512-Wf29UqxWDpc+i61k3oIOzcUfQt79PIT9y/MWfAGlrkjg6lBC1hwDECLXPVJAhWjiGbfBCxZd65F/LIZF3+jeJQ==} + '@turbodocx/html-to-docx@1.21.0': + resolution: {integrity: sha512-v5KmuqofkYYSBEB/bz7IiA7mnQ+/K3rN0w57/8UG6ipX2u4fz4Eijx9H75mI9DHEBFFfqkxXKcNnt3PpY3UbNg==} + engines: {node: '>=20.0.0'} + '@types/babel__core@7.20.5': resolution: {integrity: sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==} @@ -2248,6 +2293,15 @@ packages: resolution: {integrity: sha512-5cvg6CtKwfgdmVqY1WIiXKc3Q1bkRqGLi+2W/6ao+6Y7gu/RCwRuAhGEzh5B4KlszSuTLgZYuqFqo5bImjNKng==} engines: {node: '>= 0.6'} + acorn@8.16.0: + resolution: {integrity: sha512-UVJyE9MttOsBQIDKw1skb9nAwQuR5wuGD3+82K6JgJlm/Y+KI92oNsMNGZCYdDsVtRHSak0pcV5Dno5+4jh9sw==} + engines: {node: '>=0.4.0'} + hasBin: true + + agent-base@6.0.2: + resolution: {integrity: sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==} + engines: {node: '>= 6.0.0'} + agent-base@7.1.4: resolution: {integrity: sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ==} engines: {node: '>= 14'} @@ -2321,6 +2375,12 @@ packages: engines: {node: '>=22.12.0', npm: '>=9.6.5', pnpm: '>=7.1.0'} hasBin: true + asynckit@0.4.0: + resolution: {integrity: sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==} + + axios@1.17.0: + resolution: {integrity: sha512-J8SwNxprqqpbfenehxWYXE7CW+wM1BB4w3+N+g+/Wx40xM4rsLrfPmHHxSWIxJLYDgSY/HqlFPIYb2/S3rxafw==} + axobject-query@4.1.0: resolution: {integrity: sha512-qIj0G9wZbMGNLjLmg1PT6v2mE9AH2zlnADJD/2tC6E00hgmhUOfEB6greHPAfLRSufHqROIUTkw6E+M3lH0PTQ==} engines: {node: '>= 0.4'} @@ -2357,6 +2417,9 @@ packages: engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} hasBin: true + buffer-from@1.1.2: + resolution: {integrity: sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==} + bundle-name@4.1.0: resolution: {integrity: sha512-tjwM5exMg6BGRI+kNmTntNsvdZS1X8BFYS6tnJ2hdH0kVxM6/eVZ2xy+FqStSWvYmtfFMDLIxurorHwDKfDz5Q==} engines: {node: '>=18'} @@ -2377,6 +2440,9 @@ packages: resolution: {integrity: sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==} engines: {node: '>=6'} + camel-case@4.1.2: + resolution: {integrity: sha512-gxGWBrTT1JuMx6R+o5PTXMmUnhnVzLQ9SNutD4YqKtI6ap897t3tKECYla6gCWEkplXnlNybEkZg9GEGxKFCgw==} + caniuse-lite@1.0.30001774: resolution: {integrity: sha512-DDdwPGz99nmIEv216hKSgLD+D4ikHQHjBC/seF98N9CPqRX4M5mSxT9eTV6oyisnJcuzxtZy4n17yKKQYmYQOA==} @@ -2415,6 +2481,10 @@ packages: class-variance-authority@0.7.1: resolution: {integrity: sha512-Ka+9Trutv7G8M6WT6SeiRWz792K5qEqIGEGzXKhAE6xOWAY6pPH8U+9IY3oCMv6kqTmLsv7Xh/2w2RigkePMsg==} + clean-css@5.3.3: + resolution: {integrity: sha512-D5J+kHaVb/wKSFcyyV75uCn8fiY4sV38XJoe4CUyGQ+mOU/fMVYUdH1hJC+CJQ5uY3EnW27SbJYS4X8BiLrAFg==} + engines: {node: '>= 10.0'} + cli-cursor@5.0.0: resolution: {integrity: sha512-aCj4O5wKyszjMmDT4tZj93kxyydN/K5zPWSCe6/0AV/AA1pqe5ZBIw0a2ZfPQV7lL5/yb5HsUreJ6UFAF1tEQw==} engines: {node: '>=18'} @@ -2445,9 +2515,17 @@ packages: color-name@1.1.4: resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==} + combined-stream@1.0.8: + resolution: {integrity: sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==} + engines: {node: '>= 0.8'} + comma-separated-tokens@2.0.3: resolution: {integrity: sha512-Fu4hJdvzeylCfQPp9SGWidpzrMs7tTrlu6Vb8XGaRGck8QSNZJJp538Wrb60Lax4fPwR64ViY468OIUTbRlGZg==} + commander@10.0.1: + resolution: {integrity: sha512-y4Mg2tXshplEbSGzx7amzPwKKOCGuoSRP/CjEdwwk0FOGlUbq6lKuoyDZTNZkmxHdJtp54hdfY/JUrdL7Xfdug==} + engines: {node: '>=14'} + commander@11.1.0: resolution: {integrity: sha512-yPVavfyCcRhmorC7rWlkHn15b4wDVgVmBA7kV4QVBsF7kv/9TKJAbAXVTxvTnwP8HHKjRCJDClKbciiYS7p0DQ==} engines: {node: '>=16'} @@ -2456,6 +2534,9 @@ packages: resolution: {integrity: sha512-H+y0Jo/T1RZ9qPP4Eh1pkcQcLRglraJaSLoyOtHxu6AapkjWVCy2Sit1QQ4x3Dng8qDlSsZEet7g5Pq06MvTgw==} engines: {node: '>=20'} + commander@2.20.3: + resolution: {integrity: sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==} + common-ancestor-path@2.0.0: resolution: {integrity: sha512-dnN3ibLeoRf2HNC+OlCiNc5d2zxbLJXOtiZUudNFSXZrNSydxcCsSpRzXwfu7BBWCIfHPw+xTayeBvJCP/D8Ng==} engines: {node: '>= 18'} @@ -2486,6 +2567,9 @@ packages: resolution: {integrity: sha512-ei8Aos7ja0weRpFzJnEA9UHJ/7XQmqglbRwnf2ATjcB9Wq874VKH9kfjjirM6UhU2/E5fFYadylyhFldcqSidQ==} engines: {node: '>=18'} + core-util-is@1.0.3: + resolution: {integrity: sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==} + cors@2.8.6: resolution: {integrity: sha512-tJtZBBHA6vjIAaF6EnIaq6laBBP9aq/Y3ouVJjEfoHbRBcHBAHYcMh/w8LDrk2PvIMMq8gmopa5D4V8RmbrxGw==} engines: {node: '>= 0.10'} @@ -2576,6 +2660,10 @@ packages: defu@6.1.7: resolution: {integrity: sha512-7z22QmUWiQ/2d0KkdYmANbRUVABpZ9SNYyH5vx6PZ+nE5bcC0l7uFvEfHlyld/HcGBFTL536ClDt3DEcSlEJAQ==} + delayed-stream@1.0.0: + resolution: {integrity: sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==} + engines: {node: '>=0.4.0'} + depd@2.0.0: resolution: {integrity: sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==} engines: {node: '>= 0.8'} @@ -2624,6 +2712,9 @@ packages: domutils@3.2.2: resolution: {integrity: sha512-6kZKyUajlDuqlHKVX1w7gyslj9MPIXzIFiz/rGu35uC1wMi+kMhQwGhl4lt9unC9Vb9INnY9Z3/ZA3+FhASLaw==} + dot-case@3.0.4: + resolution: {integrity: sha512-Kv5nKlh6yRrdrGvxeJ2e5y2eRUpkUosIW4A2AS38zwSz27zu7ufDwQPi5Jhs3XAlGNetl3bmnGhQsMtkKJnj3w==} + dotenv@17.3.1: resolution: {integrity: sha512-IO8C/dzEb6O3F9/twg6ZLXz164a2fhTnEWb95H23Dm4OuN+92NmEAlTrupP9VW6Jm3sO26tQlqyvyi4CsnY9GA==} engines: {node: '>=12'} @@ -2671,6 +2762,10 @@ packages: resolution: {integrity: sha512-aN97NXWF6AWBTahfVOIrB/NShkzi5H7F9r1s9mD3cDj4Ko5f2qhhVoYMibXF7GlLveb/D2ioWay8lxI97Ven3g==} engines: {node: '>=0.12'} + entities@7.0.1: + resolution: {integrity: sha512-TWrgLOFUQTH994YUyl1yT4uyavY5nNB5muff+RtWaqNVCAK408b5ZnnbNAUEWLTCpum9w6arT70i1XdQ4UeOPA==} + engines: {node: '>=0.12'} + env-paths@2.2.1: resolution: {integrity: sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A==} engines: {node: '>=6'} @@ -2693,6 +2788,10 @@ packages: resolution: {integrity: sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==} engines: {node: '>= 0.4'} + es-set-tostringtag@2.1.0: + resolution: {integrity: sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==} + engines: {node: '>= 0.4'} + esbuild@0.25.12: resolution: {integrity: sha512-bbPBYYrtZbkt6Os6FiTLCTFxvq4tt3JKall1vRwshA3fdVztsLAatFaZobhkBC8/BrPetoa0oksYoKXoG4ryJg==} engines: {node: '>=18'} @@ -2821,6 +2920,15 @@ packages: resolution: {integrity: sha512-9UbaD6XdAL97+k/n+N7JwX46K/M6Zc6KcFYskrYL8wbBV/Uyk0CTAMY0VT+qiK5PM7AIc9aTWYtq65U7T+aCNQ==} engines: {node: '>=8'} + follow-redirects@1.16.0: + resolution: {integrity: sha512-y5rN/uOsadFT/JfYwhxRS5R7Qce+g3zG97+JrtFZlC9klX/W5hD7iiLzScI4nZqUS7DNUdhPgw4xI8W2LuXlUw==} + engines: {node: '>=4.0'} + peerDependencies: + debug: '*' + peerDependenciesMeta: + debug: + optional: true + fontace@0.4.1: resolution: {integrity: sha512-lDMvbAzSnHmbYMTEld5qdtvNH2/pWpICOqpean9IgC7vUbUJc3k+k5Dokp85CegamqQpFbXf0rAVkbzpyTA8aw==} @@ -2828,6 +2936,10 @@ packages: resolution: {integrity: sha512-Wp1zXWPVUPBmfoa3Cqc9ctaKuzKAV6uLstRqlR56kSjplf5uAce+qeyYym7F+PHbGTk+tCEdkCW6RD7DX/gBZw==} engines: {node: '>=20'} + form-data@4.0.5: + resolution: {integrity: sha512-8RipRLol37bNs2bhoV67fiTEvdTrbMUYcFTiy3+wuuOnUog2QBHCZWXDRijWQfAkhBj2Uf5UnVaiWwA5vdd82w==} + engines: {node: '>= 6'} + formdata-polyfill@4.0.10: resolution: {integrity: sha512-buewHzMvYL29jdeQTVILecSaZKnt/RJWjoZCF5OW60Z67/GmSLBkOFM7qh1PI3zFNtJbaZL5eQu1vLfazOwj4g==} engines: {node: '>=12.20.0'} @@ -2941,6 +3053,10 @@ packages: resolution: {integrity: sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==} engines: {node: '>= 0.4'} + has-tostringtag@1.0.2: + resolution: {integrity: sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==} + engines: {node: '>= 0.4'} + hasown@2.0.2: resolution: {integrity: sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==} engines: {node: '>= 0.4'} @@ -2982,12 +3098,23 @@ packages: resolution: {integrity: sha512-RWzP96k/yv0PQfyXnWjs6zot20TqfpfsNXhOnev8d1InAxubW93L11/oNUc3tQqn2G0bSdAOBpX+2uDFHV7kdQ==} engines: {node: '>=16.9.0'} + html-entities@2.6.0: + resolution: {integrity: sha512-kig+rMn/QOVRvr7c86gQ8lWXq+Hkv6CbAH1hLu+RG338StTpE8Z0b44SDVaqVu7HGKf27frdmUYEs9hTUX/cLQ==} + html-escaper@3.0.3: resolution: {integrity: sha512-RuMffC89BOWQoY0WKGpIhn5gX3iI54O6nRA0yC124NYVtzjmFWBIiFd8M0x+ZdX0P9R4lADg1mgP8C7PxGOWuQ==} + html-minifier-terser@7.2.0: + resolution: {integrity: sha512-tXgn3QfqPIpGl9o+K5tpcj3/MN4SfLtsx2GWwBC3SSd0tXQGyF3gsSqad8loJgKZGM3ZxbYDd5yhiBIdWpmvLA==} + engines: {node: ^14.13.1 || >=16.0.0} + hasBin: true + html-void-elements@3.0.0: resolution: {integrity: sha512-bEqo66MRXsUGxWHV5IP0PUiAWwoEjba4VCzg0LjFJBpchPaTfyfCKTG6bc5F8ucKec3q5y6qOdGyYTSBEvhCrg==} + htmlparser2@10.1.0: + resolution: {integrity: sha512-VTZkM9GWRAtEpveh7MSF6SjjrpNVNNVJfFup7xTY3UpFtm67foy9HDVXneLtFVt4pMz5kZtgNcvCniNFb1hlEQ==} + http-cache-semantics@4.2.0: resolution: {integrity: sha512-dTxcvPXqPvXBQpq5dUr6mEMJX4oIEFv6bwom3FDwKRDsuIjjJGANqhBuoAn9c1RQJIdAKav33ED65E2ys+87QQ==} @@ -2995,6 +3122,10 @@ packages: resolution: {integrity: sha512-4FbRdAX+bSdmo4AUFuS0WNiPz8NgFt+r8ThgNWmlrjQjt1Q7ZR9+zTlce2859x4KSXrwIsaeTqDoKQmtP8pLmQ==} engines: {node: '>= 0.8'} + https-proxy-agent@5.0.1: + resolution: {integrity: sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==} + engines: {node: '>= 6'} + https-proxy-agent@7.0.6: resolution: {integrity: sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==} engines: {node: '>= 14'} @@ -3015,6 +3146,14 @@ packages: resolution: {integrity: sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==} engines: {node: '>= 4'} + image-size@2.0.2: + resolution: {integrity: sha512-IRqXKlaXwgSMAMtpNzZa1ZAe8m+Sa1770Dhk8VkSsP9LS+iHD62Zd8FQKs8fbPiagBE7BzoFX23cxFnwshpV6w==} + engines: {node: '>=16.x'} + hasBin: true + + immediate@3.0.6: + resolution: {integrity: sha512-XXOFtyqDjNDAQxVfYxuF7g9Il/IbWmmlQg2MYKOH8ExIT1qg6xc4zyS3HaEEATgs1btfzxq15ciUiY7gjSXRGQ==} + import-fresh@3.3.1: resolution: {integrity: sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==} engines: {node: '>=6'} @@ -3113,6 +3252,9 @@ packages: resolution: {integrity: sha512-e6rvdUCiQCAuumZslxRJWR/Doq4VpPR82kqclvcS0efgt430SlGIk05vdCN58+VrzgtIcfNODjozVielycD4Sw==} engines: {node: '>=16'} + isarray@1.0.0: + resolution: {integrity: sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==} + isexe@2.0.0: resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==} @@ -3162,6 +3304,9 @@ packages: jsonfile@6.2.0: resolution: {integrity: sha512-FGuPw30AdOIUTRMC2OMRtQV+jkVj2cfPqSeWXv1NEAJ1qZ5zb1X6z1mFhbfOB/iy3ssJCD+3KuZ8r8C3uVFlAg==} + jszip@3.10.1: + resolution: {integrity: sha512-xXDvecyTpGLrqFrvkrUSoxxfJI5AH7U8zxxtVclpsUtMCq4JQ290LY8AW5c7Ggnr/Y/oK+bQMbqK2qmtk3pN4g==} + kleur@3.0.3: resolution: {integrity: sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==} engines: {node: '>=6'} @@ -3170,6 +3315,9 @@ packages: resolution: {integrity: sha512-o+NO+8WrRiQEE4/7nwRJhN1HWpVmJm511pBHUxPLtp0BUISzlBplORYSmTclCnJvQq2tKu/sgl3xVpkc7ZWuQQ==} engines: {node: '>=6'} + lie@3.3.0: + resolution: {integrity: sha512-UaiMJzeWRlEujzAuw5LokY1L5ecNQYZKfmyZ9L7wDHb/p5etKaxXhohBcrw0EYby+G/NA52vRSN4N39dxHAIwQ==} + lightningcss-android-arm64@1.31.1: resolution: {integrity: sha512-HXJF3x8w9nQ4jbXRiNppBCqeZPIAfUo8zE/kOEGbW5NZvGc/K7nMxbhIr+YlFlHW5mpbg/YFPdbnCh1wAXCKFg==} engines: {node: '>= 12.0.0'} @@ -3243,6 +3391,9 @@ packages: lines-and-columns@1.2.4: resolution: {integrity: sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==} + lodash@4.18.1: + resolution: {integrity: sha512-dMInicTPVE8d1e5otfwmmjlxkZoUpiVLwyeTdUsi/Caj/gfzzblBcCE5sRHV/AsjuCmxWrte2TNGSYuCeCq+0Q==} + log-symbols@6.0.0: resolution: {integrity: sha512-i24m8rpwhmPIS4zscNzK6MSEhk0DUWa/8iYQWxhffV8jkI4Phvs3F+quL5xvS0gdQR0FyTCMMH33Y78dDTzzIw==} engines: {node: '>=18'} @@ -3250,6 +3401,12 @@ packages: longest-streak@3.1.0: resolution: {integrity: sha512-9Ri+o0JYgehTaVBBDoMqIl8GXtbWg711O3srftcHhZ0dqnETqLaoIK0x17fUw9rFSlK/0NlsKe0Ahhyl5pXE2g==} + lower-case@2.0.2: + resolution: {integrity: sha512-7fm3l3NAF9WfN6W3JOmf5drwpVqX78JtoGJ3A6W0a6ZnldM41w2fV5D490psKFTpMds8TJse/eHLFFsNHHjHgg==} + + lru-cache@10.4.3: + resolution: {integrity: sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==} + lru-cache@11.3.6: resolution: {integrity: sha512-Gf/KoL3C/MlI7Bt0PGI9I+TeTC/I6r/csU58N4BSNc4lppLBeKsOdFYkK+dX0ABDUMJNfCHTyPpzwwO21Awd3A==} engines: {node: 20 || >=22} @@ -3423,10 +3580,18 @@ packages: resolution: {integrity: sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==} engines: {node: '>=8.6'} + mime-db@1.52.0: + resolution: {integrity: sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==} + engines: {node: '>= 0.6'} + mime-db@1.54.0: resolution: {integrity: sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ==} engines: {node: '>= 0.6'} + mime-types@2.1.35: + resolution: {integrity: sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==} + engines: {node: '>= 0.6'} + mime-types@3.0.2: resolution: {integrity: sha512-Lbgzdk0h4juoQ9fCKXW4by0UJqj+nOOrI9MJ1sSj4nI8aI2eo1qmvQEie4VD1glsS250n15LsWsYtCugiStS5A==} engines: {node: '>=18'} @@ -3506,6 +3671,9 @@ packages: nlcst-to-string@4.0.0: resolution: {integrity: sha512-YKLBCcUYKAg0FNlOBT6aI91qFmSiFKiluk655WzPF+DDMA02qIyy8uiRqI8QXtcFpEvll12LpL5MXqEmAZ+dcA==} + no-case@3.0.4: + resolution: {integrity: sha512-fgAN3jGAh+RoxUGZHTSOLJIqUc2wmoBwGR4tbpNAKmmovFoWq0OdRkb0VkldReO2a2iBT/OEulG9XSUc10r3zg==} + node-domexception@1.0.0: resolution: {integrity: sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ==} engines: {node: '>=10.5.0'} @@ -3607,6 +3775,12 @@ packages: package-manager-detector@1.6.0: resolution: {integrity: sha512-61A5ThoTiDG/C8s8UMZwSorAGwMJ0ERVGj2OjoW5pAalsNOg15+iQiPzrLJ4jhZ1HJzmC2PIHT2oEiH3R5fzNA==} + pako@1.0.11: + resolution: {integrity: sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==} + + param-case@3.0.4: + resolution: {integrity: sha512-RXlj7zCYokReqWpOPH9oYivUzLYZ5vAPIfEmCTNViosC78F8F0H9y7T7gG2M39ymgutxF5gcFEsyZQSph9Bp3A==} + parent-module@1.0.1: resolution: {integrity: sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==} engines: {node: '>=6'} @@ -3629,6 +3803,9 @@ packages: resolution: {integrity: sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==} engines: {node: '>= 0.8'} + pascal-case@3.1.2: + resolution: {integrity: sha512-uWlGT3YSnK9x3BQJaOdcZwrnV6hPpd8jFH1/ucpiLRPh/2zCVJKS19E4GvYHvaCcACn3foXZ0cLB9Wrx1KGe5g==} + path-browserify@1.0.1: resolution: {integrity: sha512-b7uo2UCUOYZcnF/3ID0lulOJi/bafxa1xPe7ZPsammBSpjSWQkjNxlt635YGS2MiR9GjvuXCtz2emr3jbsz98g==} @@ -3702,6 +3879,9 @@ packages: resolution: {integrity: sha512-DEvV2ZF2r2/63V+tK8hQvrR2ZGn10srHbXviTlcv7Kpzw8jWiNTqbVgjO3IY8RxrrOUF8VPMQQFysYYYv0YZxw==} engines: {node: '>=6'} + process-nextick-args@2.0.1: + resolution: {integrity: sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==} + prompts@2.4.2: resolution: {integrity: sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q==} engines: {node: '>= 6'} @@ -3713,6 +3893,10 @@ packages: resolution: {integrity: sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==} engines: {node: '>= 0.10'} + proxy-from-env@2.1.0: + resolution: {integrity: sha512-cJ+oHTW1VAEa8cJslgmUZrc+sjRKgAKl3Zyse6+PV38hZe/V6Z14TbCuXcan9F9ghlz4QrFr2c92TNF82UkYHA==} + engines: {node: '>=10'} + qs@6.15.0: resolution: {integrity: sha512-mAZTtNCeetKMH+pSjrb76NAM8V9a05I9aBZOHztWy/UqcJdQYNsf59vrRKWnojAT9Y+GbIvoTBC++CPHqpDBhQ==} engines: {node: '>=0.6'} @@ -3787,6 +3971,9 @@ packages: resolution: {integrity: sha512-9nfp2hYpCwOjAN+8TZFGhtWEwgvWHXqESH8qT89AT/lWklpLON22Lc8pEtnpsZz7VmawabSU0gCjnj8aC0euHQ==} engines: {node: '>=0.10.0'} + readable-stream@2.3.8: + resolution: {integrity: sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==} + readdirp@4.1.2: resolution: {integrity: sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg==} engines: {node: '>= 14.18.0'} @@ -3820,6 +4007,10 @@ packages: rehype@13.0.2: resolution: {integrity: sha512-j31mdaRFrwFRUIlxGeuPXXKWQxet52RBQRvCmzl5eCefn/KGbomK5GMHNMsOJf55fgo3qw5tST5neDuarDYR2A==} + relateurl@0.2.7: + resolution: {integrity: sha512-G08Dxvm4iDN3MLM0EsP62EDV9IuhXPR6blNz6Utcp7zyV3tr4HVNINt6MpaRWbxoOHT3Q7YN2P+jaHX8vUbgog==} + engines: {node: '>= 0.10'} + remark-gfm@4.0.1: resolution: {integrity: sha512-1quofZ2RQ9EWdeN34S79+KExV1764+wCUGop5CPL1WGdD0ocPpu91lzPGbwWMECpEpd42kJGQwzRfyov9j4yNg==} @@ -3896,6 +4087,9 @@ packages: run-parallel@1.2.0: resolution: {integrity: sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==} + safe-buffer@5.1.2: + resolution: {integrity: sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==} + safer-buffer@2.1.2: resolution: {integrity: sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==} @@ -3927,6 +4121,9 @@ packages: resolution: {integrity: sha512-xRXBn0pPqQTVQiC8wyQrKs2MOlX24zQ0POGaj0kultvoOCstBQM5yvOhAVSUwOMjQtTvsPWoNCHfPGwaaQJhTw==} engines: {node: '>= 18'} + setimmediate@1.0.5: + resolution: {integrity: sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA==} + setprototypeof@1.2.0: resolution: {integrity: sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==} @@ -3992,6 +4189,9 @@ packages: resolution: {integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==} engines: {node: '>=0.10.0'} + source-map-support@0.5.21: + resolution: {integrity: sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==} + source-map@0.6.1: resolution: {integrity: sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==} engines: {node: '>=0.10.0'} @@ -4027,6 +4227,9 @@ packages: resolution: {integrity: sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ==} engines: {node: '>=18'} + string_decoder@1.1.1: + resolution: {integrity: sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==} + stringify-entities@4.0.4: resolution: {integrity: sha512-IwfBptatlO+QCJUo19AqvrPNqlVMpW9YEL2LIVY+Rpv2qsjCGxaDLNRgeGsQWJhfItebuJhsGSLjaBbNSQ+ieg==} @@ -4077,6 +4280,11 @@ packages: resolution: {integrity: sha512-g9ljZiwki/LfxmQADO3dEY1CbpmXT5Hm2fJ+QaGKwSXUylMybePR7/67YW7jOrrvjEgL1Fmz5kzyAjWVWLlucg==} engines: {node: '>=6'} + terser@5.48.0: + resolution: {integrity: sha512-J/9An6vs9Us6wKRriSFXBWdRZapREHqFzdNUKk0pmu804EMR6dr6winwo7e5JDxN4xahxQsuysyYFwlwj4XN/Q==} + engines: {node: '>=10'} + hasBin: true + tiny-inflate@1.0.3: resolution: {integrity: sha512-pkY1fj1cKHb2seWDy0B16HeWyczlJA9/WW3u3c4z/NiWDsO3DOU5D7nhTLE9CF0yXv/QZFY7sEJmj24dK+Rrqw==} @@ -4611,6 +4819,10 @@ packages: resolution: {integrity: sha512-g/eziiSUNBSsdDJtCLB8bdYEUMj4jR7AGeUo96p/3dTafgjHhpF4RiCFPiRILwjQoDXx5MqkBr4fwWtR3Ky4Wg==} engines: {node: '>=20'} + xmlbuilder2@2.1.2: + resolution: {integrity: sha512-PI710tmtVlQ5VmwzbRTuhmVhKnj9pM8Si+iOZCV2g2SNo3gCrpzR2Ka9wNzZtqfD+mnP+xkrqoNy0sjKZqP4Dg==} + engines: {node: '>=8.0'} + xxhash-wasm@1.1.0: resolution: {integrity: sha512-147y/6YNh+tlp6nd/2pWq38i9h6mz/EuQ6njIrmW8D1BS5nCqs0P6DG+m6zTGnNz5I+uhZ0SHxBs9BsPrwcKDA==} @@ -4759,15 +4971,15 @@ snapshots: dependencies: prismjs: 1.30.0 - '@astrojs/react@4.4.2(@types/node@25.0.3)(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(jiti@2.6.1)(lightningcss@1.31.1)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(tsx@4.21.0)(yaml@2.9.0)': + '@astrojs/react@4.4.2(@types/node@25.0.3)(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(jiti@2.6.1)(lightningcss@1.31.1)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(terser@5.48.0)(tsx@4.21.0)(yaml@2.9.0)': dependencies: '@types/react': 19.2.14 '@types/react-dom': 19.2.3(@types/react@19.2.14) - '@vitejs/plugin-react': 4.7.0(vite@6.4.2(@types/node@25.0.3)(jiti@2.6.1)(lightningcss@1.31.1)(tsx@4.21.0)(yaml@2.9.0)) + '@vitejs/plugin-react': 4.7.0(vite@6.4.2(@types/node@25.0.3)(jiti@2.6.1)(lightningcss@1.31.1)(terser@5.48.0)(tsx@4.21.0)(yaml@2.9.0)) react: 19.2.4 react-dom: 19.2.4(react@19.2.4) ultrahtml: 1.6.0 - vite: 6.4.2(@types/node@25.0.3)(jiti@2.6.1)(lightningcss@1.31.1)(tsx@4.21.0)(yaml@2.9.0) + vite: 6.4.2(@types/node@25.0.3)(jiti@2.6.1)(lightningcss@1.31.1)(terser@5.48.0)(tsx@4.21.0)(yaml@2.9.0) transitivePeerDependencies: - '@types/node' - jiti @@ -5494,6 +5706,11 @@ snapshots: '@jridgewell/resolve-uri@3.1.2': {} + '@jridgewell/source-map@0.3.11': + dependencies: + '@jridgewell/gen-mapping': 0.3.13 + '@jridgewell/trace-mapping': 0.3.31 + '@jridgewell/sourcemap-codec@1.5.5': {} '@jridgewell/trace-mapping@0.3.31': @@ -5552,6 +5769,33 @@ snapshots: '@nodelib/fs.scandir': 2.1.5 fastq: 1.20.1 + '@oozcitak/dom@1.15.5': + dependencies: + '@oozcitak/infra': 1.0.5 + '@oozcitak/url': 1.0.0 + '@oozcitak/util': 8.0.0 + + '@oozcitak/infra@1.0.3': + dependencies: + '@oozcitak/util': 1.0.1 + + '@oozcitak/infra@1.0.5': + dependencies: + '@oozcitak/util': 8.0.0 + + '@oozcitak/url@1.0.0': + dependencies: + '@oozcitak/infra': 1.0.3 + '@oozcitak/util': 1.0.2 + + '@oozcitak/util@1.0.1': {} + + '@oozcitak/util@1.0.2': {} + + '@oozcitak/util@8.0.0': {} + + '@oozcitak/util@8.3.3': {} + '@open-draft/deferred-promise@2.2.0': {} '@open-draft/logger@0.3.0': @@ -6515,12 +6759,12 @@ snapshots: '@tailwindcss/oxide-win32-arm64-msvc': 4.2.1 '@tailwindcss/oxide-win32-x64-msvc': 4.2.1 - '@tailwindcss/vite@4.2.1(vite@7.3.3(@types/node@25.0.3)(jiti@2.6.1)(lightningcss@1.31.1)(tsx@4.21.0)(yaml@2.9.0))': + '@tailwindcss/vite@4.2.1(vite@7.3.3(@types/node@25.0.3)(jiti@2.6.1)(lightningcss@1.31.1)(terser@5.48.0)(tsx@4.21.0)(yaml@2.9.0))': dependencies: '@tailwindcss/node': 4.2.1 '@tailwindcss/oxide': 4.2.1 tailwindcss: 4.2.1 - vite: 7.3.3(@types/node@25.0.3)(jiti@2.6.1)(lightningcss@1.31.1)(tsx@4.21.0)(yaml@2.9.0) + vite: 7.3.3(@types/node@25.0.3)(jiti@2.6.1)(lightningcss@1.31.1)(terser@5.48.0)(tsx@4.21.0)(yaml@2.9.0) '@ts-morph/common@0.27.0': dependencies: @@ -6528,6 +6772,24 @@ snapshots: minimatch: 10.2.5 path-browserify: 1.0.1 + '@turbodocx/html-to-docx@1.21.0': + dependencies: + axios: 1.17.0 + color-name: 1.1.4 + html-entities: 2.6.0 + html-minifier-terser: 7.2.0 + htmlparser2: 10.1.0 + image-size: 2.0.2 + jszip: 3.10.1 + lodash: 4.18.1 + lru-cache: 10.4.3 + mime-types: 2.1.35 + nanoid: 3.3.11 + xmlbuilder2: 2.1.2 + transitivePeerDependencies: + - debug + - supports-color + '@types/babel__core@7.20.5': dependencies: '@babel/parser': 7.29.3 @@ -6606,7 +6868,7 @@ snapshots: '@ungap/structured-clone@1.3.1': {} - '@vitejs/plugin-react@4.7.0(vite@6.4.2(@types/node@25.0.3)(jiti@2.6.1)(lightningcss@1.31.1)(tsx@4.21.0)(yaml@2.9.0))': + '@vitejs/plugin-react@4.7.0(vite@6.4.2(@types/node@25.0.3)(jiti@2.6.1)(lightningcss@1.31.1)(terser@5.48.0)(tsx@4.21.0)(yaml@2.9.0))': dependencies: '@babel/core': 7.29.0 '@babel/plugin-transform-react-jsx-self': 7.27.1(@babel/core@7.29.0) @@ -6614,7 +6876,7 @@ snapshots: '@rolldown/pluginutils': 1.0.0-beta.27 '@types/babel__core': 7.20.5 react-refresh: 0.17.0 - vite: 6.4.2(@types/node@25.0.3)(jiti@2.6.1)(lightningcss@1.31.1)(tsx@4.21.0)(yaml@2.9.0) + vite: 6.4.2(@types/node@25.0.3)(jiti@2.6.1)(lightningcss@1.31.1)(terser@5.48.0)(tsx@4.21.0)(yaml@2.9.0) transitivePeerDependencies: - supports-color @@ -6627,14 +6889,14 @@ snapshots: chai: 6.2.2 tinyrainbow: 3.1.0 - '@vitest/mocker@4.1.0(msw@2.12.10(@types/node@25.0.3)(typescript@5.9.3))(vite@7.3.3(@types/node@25.0.3)(jiti@2.6.1)(lightningcss@1.31.1)(tsx@4.21.0)(yaml@2.9.0))': + '@vitest/mocker@4.1.0(msw@2.12.10(@types/node@25.0.3)(typescript@5.9.3))(vite@7.3.3(@types/node@25.0.3)(jiti@2.6.1)(lightningcss@1.31.1)(terser@5.48.0)(tsx@4.21.0)(yaml@2.9.0))': dependencies: '@vitest/spy': 4.1.0 estree-walker: 3.0.3 magic-string: 0.30.21 optionalDependencies: msw: 2.12.10(@types/node@25.0.3)(typescript@5.9.3) - vite: 7.3.3(@types/node@25.0.3)(jiti@2.6.1)(lightningcss@1.31.1)(tsx@4.21.0)(yaml@2.9.0) + vite: 7.3.3(@types/node@25.0.3)(jiti@2.6.1)(lightningcss@1.31.1)(terser@5.48.0)(tsx@4.21.0)(yaml@2.9.0) '@vitest/pretty-format@4.1.0': dependencies: @@ -6715,6 +6977,14 @@ snapshots: mime-types: 3.0.2 negotiator: 1.0.0 + acorn@8.16.0: {} + + agent-base@6.0.2: + dependencies: + debug: 4.4.3 + transitivePeerDependencies: + - supports-color + agent-base@7.1.4: {} ajv-draft-04@1.0.0(ajv@8.18.0): @@ -6765,7 +7035,7 @@ snapshots: dependencies: tslib: 2.8.1 - astro@6.1.10(@types/node@25.0.3)(jiti@2.6.1)(lightningcss@1.31.1)(rollup@4.60.4)(tsx@4.21.0)(typescript@5.9.3)(yaml@2.9.0): + astro@6.1.10(@types/node@25.0.3)(jiti@2.6.1)(lightningcss@1.31.1)(rollup@4.60.4)(terser@5.48.0)(tsx@4.21.0)(typescript@5.9.3)(yaml@2.9.0): dependencies: '@astrojs/compiler': 3.0.1 '@astrojs/internal-helpers': 0.9.0 @@ -6816,8 +7086,8 @@ snapshots: unist-util-visit: 5.1.0 unstorage: 1.17.5 vfile: 6.0.3 - vite: 7.3.3(@types/node@25.0.3)(jiti@2.6.1)(lightningcss@1.31.1)(tsx@4.21.0)(yaml@2.9.0) - vitefu: 1.1.3(vite@7.3.3(@types/node@25.0.3)(jiti@2.6.1)(lightningcss@1.31.1)(tsx@4.21.0)(yaml@2.9.0)) + vite: 7.3.3(@types/node@25.0.3)(jiti@2.6.1)(lightningcss@1.31.1)(terser@5.48.0)(tsx@4.21.0)(yaml@2.9.0) + vitefu: 1.1.3(vite@7.3.3(@types/node@25.0.3)(jiti@2.6.1)(lightningcss@1.31.1)(terser@5.48.0)(tsx@4.21.0)(yaml@2.9.0)) xxhash-wasm: 1.1.0 yargs-parser: 22.0.0 zod: 4.4.3 @@ -6858,6 +7128,18 @@ snapshots: - uploadthing - yaml + asynckit@0.4.0: {} + + axios@1.17.0: + dependencies: + follow-redirects: 1.16.0 + form-data: 4.0.5 + https-proxy-agent: 5.0.1 + proxy-from-env: 2.1.0 + transitivePeerDependencies: + - debug + - supports-color + axobject-query@4.1.0: {} bail@2.0.2: {} @@ -6898,6 +7180,8 @@ snapshots: node-releases: 2.0.27 update-browserslist-db: 1.2.3(browserslist@4.28.1) + buffer-from@1.1.2: {} + bundle-name@4.1.0: dependencies: run-applescript: 7.1.0 @@ -6916,6 +7200,11 @@ snapshots: callsites@3.1.0: {} + camel-case@4.1.2: + dependencies: + pascal-case: 3.1.2 + tslib: 2.8.1 + caniuse-lite@1.0.30001774: {} ccount@2.0.1: {} @@ -6944,6 +7233,10 @@ snapshots: dependencies: clsx: 2.1.1 + clean-css@5.3.3: + dependencies: + source-map: 0.6.1 + cli-cursor@5.0.0: dependencies: restore-cursor: 5.1.0 @@ -6968,12 +7261,20 @@ snapshots: color-name@1.1.4: {} + combined-stream@1.0.8: + dependencies: + delayed-stream: 1.0.0 + comma-separated-tokens@2.0.3: {} + commander@10.0.1: {} + commander@11.1.0: {} commander@14.0.3: {} + commander@2.20.3: {} + common-ancestor-path@2.0.0: {} content-disposition@1.0.1: {} @@ -6990,6 +7291,8 @@ snapshots: cookie@1.1.1: {} + core-util-is@1.0.3: {} + cors@2.8.6: dependencies: object-assign: 4.1.1 @@ -7067,6 +7370,8 @@ snapshots: defu@6.1.7: {} + delayed-stream@1.0.0: {} + depd@2.0.0: {} dequal@2.0.3: {} @@ -7107,6 +7412,11 @@ snapshots: domelementtype: 2.3.0 domhandler: 5.0.3 + dot-case@3.0.4: + dependencies: + no-case: 3.0.4 + tslib: 2.8.1 + dotenv@17.3.1: {} dset@3.1.4: {} @@ -7148,6 +7458,8 @@ snapshots: entities@6.0.1: {} + entities@7.0.1: {} + env-paths@2.2.1: {} error-ex@1.3.4: @@ -7164,6 +7476,13 @@ snapshots: dependencies: es-errors: 1.3.0 + es-set-tostringtag@2.1.0: + dependencies: + es-errors: 1.3.0 + get-intrinsic: 1.3.0 + has-tostringtag: 1.0.2 + hasown: 2.0.2 + esbuild@0.25.12: optionalDependencies: '@esbuild/aix-ppc64': 0.25.12 @@ -7400,6 +7719,8 @@ snapshots: flattie@1.1.1: {} + follow-redirects@1.16.0: {} + fontace@0.4.1: dependencies: fontkitten: 1.0.3 @@ -7408,6 +7729,14 @@ snapshots: dependencies: tiny-inflate: 1.0.3 + form-data@4.0.5: + dependencies: + asynckit: 0.4.0 + combined-stream: 1.0.8 + es-set-tostringtag: 2.1.0 + hasown: 2.0.2 + mime-types: 2.1.35 + formdata-polyfill@4.0.10: dependencies: fetch-blob: 3.2.0 @@ -7508,6 +7837,10 @@ snapshots: has-symbols@1.1.0: {} + has-tostringtag@1.0.2: + dependencies: + has-symbols: 1.1.0 + hasown@2.0.2: dependencies: function-bind: 1.1.2 @@ -7603,10 +7936,29 @@ snapshots: hono@4.12.18: {} + html-entities@2.6.0: {} + html-escaper@3.0.3: {} + html-minifier-terser@7.2.0: + dependencies: + camel-case: 4.1.2 + clean-css: 5.3.3 + commander: 10.0.1 + entities: 4.5.0 + param-case: 3.0.4 + relateurl: 0.2.7 + terser: 5.48.0 + html-void-elements@3.0.0: {} + htmlparser2@10.1.0: + dependencies: + domelementtype: 2.3.0 + domhandler: 5.0.3 + domutils: 3.2.2 + entities: 7.0.1 + http-cache-semantics@4.2.0: {} http-errors@2.0.1: @@ -7617,6 +7969,13 @@ snapshots: statuses: 2.0.2 toidentifier: 1.0.1 + https-proxy-agent@5.0.1: + dependencies: + agent-base: 6.0.2 + debug: 4.4.3 + transitivePeerDependencies: + - supports-color + https-proxy-agent@7.0.6: dependencies: agent-base: 7.1.4 @@ -7634,6 +7993,10 @@ snapshots: ignore@5.3.2: {} + image-size@2.0.2: {} + + immediate@3.0.6: {} + import-fresh@3.3.1: dependencies: parent-module: 1.0.1 @@ -7693,6 +8056,8 @@ snapshots: dependencies: is-inside-container: 1.0.0 + isarray@1.0.0: {} + isexe@2.0.0: {} isexe@3.1.5: {} @@ -7727,10 +8092,21 @@ snapshots: optionalDependencies: graceful-fs: 4.2.11 + jszip@3.10.1: + dependencies: + lie: 3.3.0 + pako: 1.0.11 + readable-stream: 2.3.8 + setimmediate: 1.0.5 + kleur@3.0.3: {} kleur@4.1.5: {} + lie@3.3.0: + dependencies: + immediate: 3.0.6 + lightningcss-android-arm64@1.31.1: optional: true @@ -7782,6 +8158,8 @@ snapshots: lines-and-columns@1.2.4: {} + lodash@4.18.1: {} + log-symbols@6.0.0: dependencies: chalk: 5.6.2 @@ -7789,6 +8167,12 @@ snapshots: longest-streak@3.1.0: {} + lower-case@2.0.2: + dependencies: + tslib: 2.8.1 + + lru-cache@10.4.3: {} + lru-cache@11.3.6: {} lru-cache@5.1.1: @@ -8141,8 +8525,14 @@ snapshots: braces: 3.0.3 picomatch: 2.3.2 + mime-db@1.52.0: {} + mime-db@1.54.0: {} + mime-types@2.1.35: + dependencies: + mime-db: 1.52.0 + mime-types@3.0.2: dependencies: mime-db: 1.54.0 @@ -8214,6 +8604,11 @@ snapshots: dependencies: '@types/nlcst': 2.0.3 + no-case@3.0.4: + dependencies: + lower-case: 2.0.2 + tslib: 2.8.1 + node-domexception@1.0.0: {} node-fetch-native@1.6.7: {} @@ -8319,6 +8714,13 @@ snapshots: package-manager-detector@1.6.0: {} + pako@1.0.11: {} + + param-case@3.0.4: + dependencies: + dot-case: 3.0.4 + tslib: 2.8.1 + parent-module@1.0.1: dependencies: callsites: 3.1.0 @@ -8347,6 +8749,11 @@ snapshots: parseurl@1.3.3: {} + pascal-case@3.1.2: + dependencies: + no-case: 3.0.4 + tslib: 2.8.1 + path-browserify@1.0.1: {} path-key@3.1.1: {} @@ -8398,6 +8805,8 @@ snapshots: prismjs@1.30.0: {} + process-nextick-args@2.0.1: {} + prompts@2.4.2: dependencies: kleur: 3.0.3 @@ -8410,6 +8819,8 @@ snapshots: forwarded: 0.2.0 ipaddr.js: 1.9.1 + proxy-from-env@2.1.0: {} + qs@6.15.0: dependencies: side-channel: 1.1.0 @@ -8526,6 +8937,16 @@ snapshots: react@19.2.4: {} + readable-stream@2.3.8: + dependencies: + core-util-is: 1.0.3 + inherits: 2.0.4 + isarray: 1.0.0 + process-nextick-args: 2.0.1 + safe-buffer: 5.1.2 + string_decoder: 1.1.1 + util-deprecate: 1.0.2 + readdirp@4.1.2: {} readdirp@5.0.0: {} @@ -8573,6 +8994,8 @@ snapshots: rehype-stringify: 10.0.1 unified: 11.0.5 + relateurl@0.2.7: {} + remark-gfm@4.0.1: dependencies: '@types/mdast': 4.0.4 @@ -8707,6 +9130,8 @@ snapshots: dependencies: queue-microtask: 1.2.3 + safe-buffer@5.1.2: {} + safer-buffer@2.1.2: {} sax@1.5.0: {} @@ -8744,6 +9169,8 @@ snapshots: transitivePeerDependencies: - supports-color + setimmediate@1.0.5: {} + setprototypeof@1.2.0: {} shadcn@3.8.5(@types/node@25.0.3)(typescript@5.9.3): @@ -8886,6 +9313,11 @@ snapshots: source-map-js@1.2.1: {} + source-map-support@0.5.21: + dependencies: + buffer-from: 1.1.2 + source-map: 0.6.1 + source-map@0.6.1: {} space-separated-tokens@2.0.2: {} @@ -8914,6 +9346,10 @@ snapshots: get-east-asian-width: 1.5.0 strip-ansi: 7.2.0 + string_decoder@1.1.1: + dependencies: + safe-buffer: 5.1.2 + stringify-entities@4.0.4: dependencies: character-entities-html4: 2.1.0 @@ -8961,6 +9397,13 @@ snapshots: tapable@2.3.0: {} + terser@5.48.0: + dependencies: + '@jridgewell/source-map': 0.3.11 + acorn: 8.16.0 + commander: 2.20.3 + source-map-support: 0.5.21 + tiny-inflate@1.0.3: {} tiny-invariant@1.3.3: {} @@ -9180,7 +9623,7 @@ snapshots: '@types/unist': 3.0.3 vfile-message: 4.0.3 - vite@6.4.2(@types/node@25.0.3)(jiti@2.6.1)(lightningcss@1.31.1)(tsx@4.21.0)(yaml@2.9.0): + vite@6.4.2(@types/node@25.0.3)(jiti@2.6.1)(lightningcss@1.31.1)(terser@5.48.0)(tsx@4.21.0)(yaml@2.9.0): dependencies: esbuild: 0.25.12 fdir: 6.5.0(picomatch@4.0.4) @@ -9193,10 +9636,11 @@ snapshots: fsevents: 2.3.3 jiti: 2.6.1 lightningcss: 1.31.1 + terser: 5.48.0 tsx: 4.21.0 yaml: 2.9.0 - vite@7.3.3(@types/node@25.0.3)(jiti@2.6.1)(lightningcss@1.31.1)(tsx@4.21.0)(yaml@2.9.0): + vite@7.3.3(@types/node@25.0.3)(jiti@2.6.1)(lightningcss@1.31.1)(terser@5.48.0)(tsx@4.21.0)(yaml@2.9.0): dependencies: esbuild: 0.27.7 fdir: 6.5.0(picomatch@4.0.4) @@ -9209,17 +9653,18 @@ snapshots: fsevents: 2.3.3 jiti: 2.6.1 lightningcss: 1.31.1 + terser: 5.48.0 tsx: 4.21.0 yaml: 2.9.0 - vitefu@1.1.3(vite@7.3.3(@types/node@25.0.3)(jiti@2.6.1)(lightningcss@1.31.1)(tsx@4.21.0)(yaml@2.9.0)): + vitefu@1.1.3(vite@7.3.3(@types/node@25.0.3)(jiti@2.6.1)(lightningcss@1.31.1)(terser@5.48.0)(tsx@4.21.0)(yaml@2.9.0)): optionalDependencies: - vite: 7.3.3(@types/node@25.0.3)(jiti@2.6.1)(lightningcss@1.31.1)(tsx@4.21.0)(yaml@2.9.0) + vite: 7.3.3(@types/node@25.0.3)(jiti@2.6.1)(lightningcss@1.31.1)(terser@5.48.0)(tsx@4.21.0)(yaml@2.9.0) - vitest@4.1.0(@types/node@25.0.3)(msw@2.12.10(@types/node@25.0.3)(typescript@5.9.3))(vite@7.3.3(@types/node@25.0.3)(jiti@2.6.1)(lightningcss@1.31.1)(tsx@4.21.0)(yaml@2.9.0)): + vitest@4.1.0(@types/node@25.0.3)(msw@2.12.10(@types/node@25.0.3)(typescript@5.9.3))(vite@7.3.3(@types/node@25.0.3)(jiti@2.6.1)(lightningcss@1.31.1)(terser@5.48.0)(tsx@4.21.0)(yaml@2.9.0)): dependencies: '@vitest/expect': 4.1.0 - '@vitest/mocker': 4.1.0(msw@2.12.10(@types/node@25.0.3)(typescript@5.9.3))(vite@7.3.3(@types/node@25.0.3)(jiti@2.6.1)(lightningcss@1.31.1)(tsx@4.21.0)(yaml@2.9.0)) + '@vitest/mocker': 4.1.0(msw@2.12.10(@types/node@25.0.3)(typescript@5.9.3))(vite@7.3.3(@types/node@25.0.3)(jiti@2.6.1)(lightningcss@1.31.1)(terser@5.48.0)(tsx@4.21.0)(yaml@2.9.0)) '@vitest/pretty-format': 4.1.0 '@vitest/runner': 4.1.0 '@vitest/snapshot': 4.1.0 @@ -9236,7 +9681,7 @@ snapshots: tinyexec: 1.2.4 tinyglobby: 0.2.17 tinyrainbow: 3.1.0 - vite: 7.3.3(@types/node@25.0.3)(jiti@2.6.1)(lightningcss@1.31.1)(tsx@4.21.0)(yaml@2.9.0) + vite: 7.3.3(@types/node@25.0.3)(jiti@2.6.1)(lightningcss@1.31.1)(terser@5.48.0)(tsx@4.21.0)(yaml@2.9.0) why-is-node-running: 2.3.0 optionalDependencies: '@types/node': 25.0.3 @@ -9378,6 +9823,12 @@ snapshots: is-wsl: 3.1.1 powershell-utils: 0.1.0 + xmlbuilder2@2.1.2: + dependencies: + '@oozcitak/dom': 1.15.5 + '@oozcitak/infra': 1.0.5 + '@oozcitak/util': 8.3.3 + xxhash-wasm@1.1.0: {} y18n@5.0.8: {} diff --git a/public/Jon_Bogaty_Resume.docx b/public/Jon_Bogaty_Resume.docx index c4c7f6c8..7a56c106 100644 Binary files a/public/Jon_Bogaty_Resume.docx and b/public/Jon_Bogaty_Resume.docx differ diff --git a/public/Jon_Bogaty_Resume.pdf b/public/Jon_Bogaty_Resume.pdf deleted file mode 100644 index 0ce0b645..00000000 Binary files a/public/Jon_Bogaty_Resume.pdf and /dev/null differ diff --git a/scripts/generate-og-image.ts b/scripts/generate-og-image.ts index 64c72a2e..59944eac 100644 --- a/scripts/generate-og-image.ts +++ b/scripts/generate-og-image.ts @@ -5,7 +5,7 @@ import { writeFileSync } from 'node:fs' import { resolve } from 'node:path' import { chromium } from 'playwright' -import resume from '../src/content/resume.json' with { type: 'json' } +import resume from '../src/content/resume.ts' const root = resolve(import.meta.dirname!, '..') const out = resolve(root, 'public/og-image.png') @@ -84,7 +84,7 @@ const html = `
|
+ ${escapeHtml(title.toUpperCase())} + |
+
${left}
${right}
${escapeHtml(about.name)}
+${escapeHtml(about.label)}
+${contactParts.join(' · ')}
` +} + +function summarySection(): string { + const paragraphs = resume.about.summary + return ( + sectionHeading('Professional Summary') + + paragraphs.map((p) => `${escapeHtml(p)}
`).join('\n') + ) +} + +function experienceSection(): string { + const jobs = resume.work + .filter((job) => job.onResume !== false) + .map((job) => { + const dates = formatDateRange(job.startDate, job.endDate) + const highlights = (job.highlights ?? []) + .map( + (h) => + `${escapeHtml(job.name)}
${escapeHtml(job.summary)}
` : ''} +${highlights ? `${escapeHtml(project.name)} — ${escapeHtml(project.tagline)}${project.domain ? ` · ${escapeHtml(project.domain)}` : ''}
` + ) + .join('\n') + return ( + sectionHeading('Open Source') + + `Five published frameworks across Python, Go, TypeScript, and Rust — full portfolio, packages, and write-ups at jonbogaty.com.
` + + projects + ) +} + +function earlierCareerSection(): string { + // Deliberately a single paragraph, not a position list: a decade of short + // early roles reads as a consulting-style arc in prose, but as job-hopping + // in a list. + return ( + sectionHeading('Earlier Career') + + `${escapeHtml(resume.earlierCareer.summary)}
` + ) +} + +function skillsSection(): string { + const categories = resume.skills + .map( + (cat) => + `${escapeHtml(cat.name)}: ${escapeHtml(cat.keywords.join(', '))}
` + ) + .join('\n') + return sectionHeading('Technical Skills') + categories +} + +function educationSection(): string { + const entries = resume.education + .map( + ( + edu + ) => `${escapeHtml(edu.studyType)} — ${escapeHtml(edu.area)}
+${escapeHtml(edu.institution)} · ${escapeHtml(edu.startDate)}–${escapeHtml(edu.endDate)}
+${edu.honors.length > 0 ? `${edu.honors.map((h) => escapeHtml(h)).join(' · ')}
` : ''}` + ) + .join('\n') + return sectionHeading('Education') + entries +} + +export function resumeDocxHtml(): string { + return ` + + + +