docs: add sponsors page#456
Conversation
📝 WalkthroughWalkthroughThis PR introduces a new Sponsors page in the documentation. The sponsors data is fetched from an external API, organized by tier, and displayed as sponsor cards with optional logos and notes. The page handles errors and loading states gracefully. Navigation is updated to include the new Sponsors link in the sidebar Resources section. ChangesSponsors Page Feature
Sequence DiagramsequenceDiagram
participant Page as Sponsors Page
participant API as en.dev API
participant Vue as Vue Renderer
Page->>API: fetch sponsors.json on mount
alt API Success
API-->>Page: sponsor data with tiers & infrastructure
Page->>Page: compute tiered sponsor lists
Page->>Vue: render tier sections
Vue-->>Page: sponsor cards (logo, name, note)
else API Error
API-->>Page: HTTP error
Page->>Vue: render error message
else Loading
Page->>Vue: render loading state
end
Estimated code review effort🎯 2 (Simple) | ⏱️ ~10 minutes Poem
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Comment |
Greptile SummaryAdds a
Confidence Score: 5/5Documentation-only change; no product runtime is affected and the fetch is purely client-side on a docs page. The two changed files are a trivial sidebar config update and a new static docs page. The fetch target is a first-party domain the project controls, loading/error states are handled, and nothing here touches app logic or sensitive data paths. No files require special attention. Important Files Changed
Reviews (2): Last reviewed commit: "docs: add sponsors page" | Re-trigger Greptile |
96aa4b1 to
67393b3
Compare
There was a problem hiding this comment.
Actionable comments posted: 1
🧹 Nitpick comments (1)
docs/sponsors.md (1)
13-21: ⚡ Quick winAdd request timeout/abort to avoid indefinite loading state.
On Line 15,
fetchhas no timeout. If the request hangs, Line 36 can show “Loading sponsors...” forever. AddAbortControllerwith a short timeout and surface fallback error state.Also applies to: 36-37
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@docs/sponsors.md` around lines 13 - 21, The fetch call inside onMounted should use an AbortController with a short timeout so the request can't hang indefinitely; create an AbortController, start a setTimeout that calls controller.abort() after the desired timeout, pass controller.signal into fetch("https://en.dev/sponsors.json", { signal }), and clear the timeout when the response arrives; in the catch block detect abort errors (or any error) and set error.value to a sensible fallback message (e.g., "Request timed out" for aborts or the caught err.message) and ensure feed.value is left empty/undefined so the UI exits the "Loading sponsors..." state.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@docs/sponsors.md`:
- Around line 41-43: The anchor uses sponsor.url directly (in the v-for
rendering of sponsor-card), which can allow unsafe schemes; add a URL
validation/sanitization step (e.g., implement isValidSponsorUrl or
sanitizeSponsorUrl used when binding :href) that parses sponsor.url and
allowlists only http: and https: (otherwise return a safe fallback like '#' or
null), then bind the sanitized value to :href for the anchor and ensure existing
rel/target attrs remain; apply the same sanitizer wherever sponsor.url is used
(lines rendering the other sponsor anchors).
---
Nitpick comments:
In `@docs/sponsors.md`:
- Around line 13-21: The fetch call inside onMounted should use an
AbortController with a short timeout so the request can't hang indefinitely;
create an AbortController, start a setTimeout that calls controller.abort()
after the desired timeout, pass controller.signal into
fetch("https://en.dev/sponsors.json", { signal }), and clear the timeout when
the response arrives; in the catch block detect abort errors (or any error) and
set error.value to a sensible fallback message (e.g., "Request timed out" for
aborts or the caught err.message) and ensure feed.value is left empty/undefined
so the UI exits the "Loading sponsors..." state.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro Plus
Run ID: aefa7465-56a0-44c5-a15f-5ec8347b0a64
📒 Files selected for processing (2)
docs/.vitepress/config.mtsdocs/sponsors.md
| <a v-for="sponsor in tier.sponsors" :key="sponsor.name" class="sponsor-card" :href="sponsor.url" target="_blank" rel="noopener noreferrer"> | ||
| <img v-if="sponsor.logo" :src="sponsor.logo" :alt="sponsor.name"> | ||
| <span>{{ sponsor.name }}</span> |
There was a problem hiding this comment.
Validate external sponsor URLs before binding to href.
On Line 41, Line 51, and Line 60, sponsor.url is used directly from remote JSON. If that feed is ever compromised, javascript:/unexpected schemes could be injected and executed on click. Please allowlist protocols (for example https: and http:) before rendering links.
Suggested fix
const infrastructureSponsors = computed(() => (feed.value?.infrastructure || []).filter((s) => s.name && s.url));
+
+function isSafeHttpUrl(value) {
+ try {
+ const u = new URL(value);
+ return u.protocol === "https:" || u.protocol === "http:";
+ } catch {
+ return false;
+ }
+}
+
+function sanitizeSponsors(list) {
+ return (list || []).filter((s) => s?.name && isSafeHttpUrl(s?.url));
+}
-const paidSponsors = computed(() => (feed.value?.paid || feed.value?.sponsors || []).filter((s) => s.name && s.url));
+const paidSponsors = computed(() => sanitizeSponsors(feed.value?.paid || feed.value?.sponsors));
const sponsorsByTier = computed(() => tiers.map(([id, label]) => ({ id, label, sponsors: paidSponsors.value.filter((s) => s.tier === id) })));
const otherSponsors = computed(() => paidSponsors.value.filter((s) => !tiers.some(([id]) => id === s.tier)));
-const infrastructureSponsors = computed(() => (feed.value?.infrastructure || []).filter((s) => s.name && s.url));
+const infrastructureSponsors = computed(() => sanitizeSponsors(feed.value?.infrastructure));Also applies to: 51-53, 60-62
🧰 Tools
🪛 markdownlint-cli2 (0.22.1)
[warning] 42-42: Images should have alternate text (alt text)
(MD045, no-alt-text)
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@docs/sponsors.md` around lines 41 - 43, The anchor uses sponsor.url directly
(in the v-for rendering of sponsor-card), which can allow unsafe schemes; add a
URL validation/sanitization step (e.g., implement isValidSponsorUrl or
sanitizeSponsorUrl used when binding :href) that parses sponsor.url and
allowlists only http: and https: (otherwise return a safe fallback like '#' or
null), then bind the sanitized value to :href for the anchor and ensure existing
rel/target attrs remain; apply the same sanitizer wherever sponsor.url is used
(lines rendering the other sponsor anchors).
Summary
Validation
Note
Low Risk
Documentation-only VitePress page with client-side fetch; no product runtime or security-sensitive code paths.
Overview
Adds a Sponsors docs page and links it from the Resources sidebar.
The new page loads sponsor data at runtime from
https://en.dev/sponsors.json, groups paid sponsors by tier (Anchor through Backer), shows uncategorized and infrastructure partners, and includes loading/error fallbacks plus a link to become a sponsor on en.dev.Reviewed by Cursor Bugbot for commit 67393b3. Bugbot is set up for automated code reviews on this repo. Configure here.
Summary by CodeRabbit
New Features
Documentation