fix(ui): support takumi v2 og images#2986
Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub.
1 Skipped Deployment
|
|
Important Review skippedDraft detected. Please check the settings in the CodeRabbit UI or the ⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: CHILL Plan: Pro Run ID: You can disable this status message by setting the Use the checkbox below for a quick retry:
📝 WalkthroughWalkthroughThis PR migrates Open Graph image generation from ChangesTakumi OG image migration
Sequence Diagram(s)sequenceDiagram
participant Page as Page component
participant DefineOgImage as defineOgImage
participant NuxtOgImage as nuxt-og-image module
participant Takumi as OgImage/*.takumi.vue
participant DataSources as Package/Repo/Downloads data
Page->>DefineOgImage: call defineOgImage('Template.takumi', props, { alt })
DefineOgImage->>NuxtOgImage: register OG image request
NuxtOgImage->>Takumi: render selected takumi component with props
Takumi->>DataSources: fetch package/repo/downloads/likes (server-side)
DataSources-->>Takumi: return metadata and stats
Takumi-->>NuxtOgImage: rendered image output
NuxtOgImage-->>Page: og:image meta tag
Possibly related PRs
Suggested reviewers: 🚥 Pre-merge checks | ✅ 4✅ Passed checks (4 passed)
✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
07aa320 to
5aa2e46
Compare
|
Review the following changes in direct dependencies. Learn more about Socket for GitHub.
|
|
Warning Review the following alerts detected in dependencies. According to your organization's Security Policy, it is recommended to resolve "Warn" alerts. Learn more about Socket for GitHub.
|
Codecov Report✅ All modified and coverable lines are covered by tests. 📢 Thoughts on this report? Let us know! |
📊 Dependency Size ChangesWarning This PR adds 8.5 MB of new dependencies, which exceeds the threshold of 200 kB.
Total size change: 8.5 MB |
There was a problem hiding this comment.
Actionable comments posted: 5
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
nuxt.config.ts (1)
78-78: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick winRestore a site-wide
twitterCardmeta tag innuxt.config.ts
defineOgImageonly covers OG images, and there’s no othertwitterCard/twitter:carddeclaration in the repo, so X previews would lose their card type without an explicituseSeoMeta({ twitterCard: 'summary_large_image' })or equivalent.🤖 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 `@nuxt.config.ts` at line 78, Restore a site-wide twitterCard meta declaration in nuxt.config.ts, since defineOgImage only handles OG images and there is no other twitter:card setting in the app. Add the missing SEO meta via the existing Nuxt config SEO setup (for example with useSeoMeta) so every page continues to advertise summary_large_image for X previews; locate the relevant global metadata block near defineOgImage and the current site-wide head/meta configuration.
🧹 Nitpick comments (6)
app/components/OgImage/BlogPost.takumi.vue (1)
48-56: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winAvoid unchecked author-name indexes.
allNames[0],[1], and[2]bypass the project’s strict indexed-access convention. Add explicit fallbacks before formatting.Proposed fix
const formattedAuthorNames = computed(() => { const allNames = authors.map(a => a.name) if (allNames.length === 0) return '' - if (allNames.length === 1) return allNames[0] - if (allNames.length === 2) return `${allNames[0]} and ${allNames[1]}` - if (allNames.length === 3) return `${allNames[0]}, ${allNames[1]}, and ${allNames[2]}` + const [first = '', second = '', third = ''] = allNames + if (allNames.length === 1) return first + if (allNames.length === 2) return `${first} and ${second}` + if (allNames.length === 3) return `${first}, ${second}, and ${third}` const shown = allNames.slice(0, MAX_VISIBLE_AUTHORS) const remaining = allNames.length - MAX_VISIBLE_AUTHORS return `${shown.join(', ')} and ${remaining} others` })As per coding guidelines, “Ensure you write strictly type-safe code, for example by ensuring you always check when accessing an array value by index.”
🤖 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 `@app/components/OgImage/BlogPost.takumi.vue` around lines 48 - 56, The formattedAuthorNames computed formatter is accessing allNames[0], allNames[1], and allNames[2] directly, which violates the strict indexed-access convention. Update the logic in BlogPost.takumi.vue to add explicit guards/fallbacks before composing the 1-, 2-, and 3-author strings, using named checks against allNames in the formattedAuthorNames function so each indexed value is only used after confirming it exists.Source: Coding guidelines
app/pages/search.vue (1)
555-591: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winDuplicated title/description logic between
useSeoMetaanddefineOgImage.The
titleanddescriptionclosures passed todefineOgImageare identical to those already computed foruseSeoMeta. Consider extracting sharedcomputedrefs to avoid the strings diverging later.♻️ Suggested refactor
+const pageTitle = computed( + () => + `${query.value ? $t('search.title_search', { search: query.value }) : $t('search.title_packages')} - npmx`, +) +const pageDescription = computed(() => + query.value + ? $t('search.meta_description', { search: query.value }) + : $t('search.meta_description_packages'), +) + useSeoMeta({ - title: () => - `${query.value ? $t('search.title_search', { search: query.value }) : $t('search.title_packages')} - npmx`, - ogTitle: () => - `${query.value ? $t('search.title_search', { search: query.value }) : $t('search.title_packages')} - npmx`, - twitterTitle: () => - `${query.value ? $t('search.title_search', { search: query.value }) : $t('search.title_packages')} - npmx`, - description: () => - query.value - ? $t('search.meta_description', { search: query.value }) - : $t('search.meta_description_packages'), - ogDescription: () => - query.value - ? $t('search.meta_description', { search: query.value }) - : $t('search.meta_description_packages'), - twitterDescription: () => - query.value - ? $t('search.meta_description', { search: query.value }) - : $t('search.meta_description_packages'), + title: pageTitle, + ogTitle: pageTitle, + twitterTitle: pageTitle, + description: pageDescription, + ogDescription: pageDescription, + twitterDescription: pageDescription, }) defineOgImage( 'Page.takumi', { - title: () => - `${query.value ? $t('search.title_search', { search: query.value }) : $t('search.title_packages')} - npmx`, - description: () => - query.value - ? $t('search.meta_description', { search: query.value }) - : $t('search.meta_description_packages'), + title: pageTitle, + description: pageDescription, }, { alt: () => query.value ? `Search results for "${query.value}" on npmx` : 'Search npm packages on npmx', }, )🤖 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 `@app/pages/search.vue` around lines 555 - 591, The search page repeats the same title and description logic in both useSeoMeta and defineOgImage, which can drift over time. Extract the shared title and description into reusable computed refs in search.vue, then reference those values from both useSeoMeta and defineOgImage (including the alt text branch as needed) so the metadata stays consistent in one place.app/pages/pds.vue (1)
13-20: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winHardcoded, untranslated strings in
defineOgImage.
title/description/altare hardcoded English literals here, while the page'suseSeoMetafields above use$t(...). Consider wiring these through i18n for consistency with the rest of the page (and similarly for other pages in this PR using literal alt/title strings, e.g.package-code,package-docs,package/[[org]]/[name].vue).Based on learnings,
$t()should be relied on consistently inuseSeoMetaand OG-image-defining composables across all pages rather than hardcoded strings.🤖 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 `@app/pages/pds.vue` around lines 13 - 20, The `defineOgImage` call in `pds.vue` still uses hardcoded English `title`, `description`, and `alt` strings instead of the page’s i18n flow. Update the `defineOgImage` arguments to use `$t(...)`-backed translations like the surrounding `useSeoMeta` fields, and apply the same pattern anywhere else in this PR that passes literal OG/title/alt text (for example in `package-code`, `package-docs`, and `package/[[org]]/[name].vue`) so the composables are consistent.Source: Learnings
pnpm-workspace.yaml (1)
20-27: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winAlign the Storybook versions
storybookis still on^10.3.1while the other Storybook packages are on^10.3.5. If there’s no reason to keep the core package behind, bump it to match.🤖 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 `@pnpm-workspace.yaml` around lines 20 - 27, The Storybook package set is version-skewed because the core storybook dependency is behind the other Storybook addons and utilities. Update the storybook entry in the storybook package group to match the same 10.3.5 range used by the other Storybook packages, keeping the existing package names unchanged.modules/runtime/server/cache.ts (1)
190-234: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚖️ Poor tradeoffDuplicated download-curve mock logic across
cache.tsandmock-routes.cjs.This new branch and the equivalent one in
test/fixtures/mock-routes.cjs(lines 184-210) both mock the npm/downloads/rangeendpoint but use different hashing/curve formulas (this one adds trend/wave-period/weekend-dip; the other is a simpler sine+noise curve). Any future adjustment to one will silently diverge from the other, causing inconsistent sparkline/chart shapes between component tests and e2e snapshot tests for the same package.Consider extracting the shared curve-generation logic into a small utility consumable from both an ESM/TS context and the CJS mock file (e.g. a plain
.mjs/.cjshelper with no TS-only syntax), or generating one from the other in a build step.🤖 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 `@modules/runtime/server/cache.ts` around lines 190 - 234, The npm downloads mock logic is duplicated and diverging between cache.ts and mock-routes.cjs, which will produce inconsistent sparkline shapes. Extract the shared `/downloads/range` curve generation from the cache branch in cache.ts into a reusable helper that both the cache handler and the mock-routes.cjs fixture can call, keeping the hashing, trend, wave, and weekend behavior identical in both places.test/e2e/og-image.spec.ts (1)
34-34: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick winSnapshot filename isn't sanitised for query-string characters.
For
path: '/compare?packages=vue,react,svelte', the name transform only replaces/and strips a leading-, leaving?,=, and,in the generated snapshot filename (og-image-compare?packages=vue,react,svelte.png). This is unusual for a snapshot artifact name and may cause portability issues with some tooling/filesystems.💡 Proposed fix to sanitise the slug
- name: `og-image-${path.replace(/\//g, '-').replace(/^-/, '') || 'home'}.png`, + name: `og-image-${ + path.replace(/[/?]/g, '-').replace(/[=,]/g, '_').replace(/^-/, '') || 'home' + }.png`,Also applies to: 66-67
🤖 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 `@test/e2e/og-image.spec.ts` at line 34, The snapshot name generation for the OG image tests is only partially sanitizing the `path` value, so query-string characters from entries like `compare` still leak into the filename. Update the slug/name transform in `og-image.spec.ts` where the snapshot path is derived to sanitize all unsafe characters from the `path` value, not just `/` and a leading `-`, so the generated artifact name stays filesystem-friendly for the `compare` cases and the other affected entries.
🤖 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 `@app/components/OgImage/Package.takumi.vue`:
- Around line 180-187: The optional likes lookup in Package.takumi.vue can
reject and cause the outer Promise.all flow to fail, which incorrectly 404s the
OG image. Update the fetchLikes logic in the Package component so the $fetch
branch handles failures locally and falls back to totalLikes.value = 0, keeping
likes as non-blocking metadata while preserving the deterministic
import.meta.test path.
In `@app/pages/compare.vue`:
- Around line 145-152: The OG image alt text in defineOgImage is hard-wired to
the empty-state copy and does not reflect when packages.value has selected
items. Update the defineOgImage call in compare.vue so the alt callback mirrors
the same packages.value conditional used by useSeoMeta, returning
comparison-related text when packages are present and the empty-state
translation only when none are selected. Keep the logic aligned with
packages.value and the existing meta helpers to ensure the alt description
matches the rendered image.
In `@nuxt.config.ts`:
- Around line 302-314: The ogImage security config is overly relaxed in all
environments and reuses the image-proxy secret. Update the ogImage security
block in nuxt.config.ts so restrictRuntimeImagesToOrigin is only disabled for
test/e2e or other non-production contexts, while production keeps the stricter
default. Also replace the shared NUXT_IMAGE_PROXY_SECRET usage with a dedicated
OG-image secret in the ogImage.security.secret field so the two signing domains
remain separate.
In `@patches/`@nuxt__test-utils.patch:
- Around line 1-13: The cloning logic in getVitestConfigFromNuxt is dropping
unset runtimeConfig keys because JSON.parse(JSON.stringify(...)) removes
undefined properties. Replace that clone with a method that preserves undefined
fields when building nuxtRuntimeConfig, so auth-related keys like oauthJwkOne
remain present even when unset.
In `@test/fixtures/jsdelivr/vue.json`:
- Around line 5-6: The jsDelivr vue fixture has an invalid default entrypoint
because vue.global.min.js is not present in the mocked dist contents. Update the
default field in vue.json to reference one of the existing files under dist,
such as vue.global.js or vue.global.prod.js, so the fixture matches the
available mock assets.
---
Outside diff comments:
In `@nuxt.config.ts`:
- Line 78: Restore a site-wide twitterCard meta declaration in nuxt.config.ts,
since defineOgImage only handles OG images and there is no other twitter:card
setting in the app. Add the missing SEO meta via the existing Nuxt config SEO
setup (for example with useSeoMeta) so every page continues to advertise
summary_large_image for X previews; locate the relevant global metadata block
near defineOgImage and the current site-wide head/meta configuration.
---
Nitpick comments:
In `@app/components/OgImage/BlogPost.takumi.vue`:
- Around line 48-56: The formattedAuthorNames computed formatter is accessing
allNames[0], allNames[1], and allNames[2] directly, which violates the strict
indexed-access convention. Update the logic in BlogPost.takumi.vue to add
explicit guards/fallbacks before composing the 1-, 2-, and 3-author strings,
using named checks against allNames in the formattedAuthorNames function so each
indexed value is only used after confirming it exists.
In `@app/pages/pds.vue`:
- Around line 13-20: The `defineOgImage` call in `pds.vue` still uses hardcoded
English `title`, `description`, and `alt` strings instead of the page’s i18n
flow. Update the `defineOgImage` arguments to use `$t(...)`-backed translations
like the surrounding `useSeoMeta` fields, and apply the same pattern anywhere
else in this PR that passes literal OG/title/alt text (for example in
`package-code`, `package-docs`, and `package/[[org]]/[name].vue`) so the
composables are consistent.
In `@app/pages/search.vue`:
- Around line 555-591: The search page repeats the same title and description
logic in both useSeoMeta and defineOgImage, which can drift over time. Extract
the shared title and description into reusable computed refs in search.vue, then
reference those values from both useSeoMeta and defineOgImage (including the alt
text branch as needed) so the metadata stays consistent in one place.
In `@modules/runtime/server/cache.ts`:
- Around line 190-234: The npm downloads mock logic is duplicated and diverging
between cache.ts and mock-routes.cjs, which will produce inconsistent sparkline
shapes. Extract the shared `/downloads/range` curve generation from the cache
branch in cache.ts into a reusable helper that both the cache handler and the
mock-routes.cjs fixture can call, keeping the hashing, trend, wave, and weekend
behavior identical in both places.
In `@pnpm-workspace.yaml`:
- Around line 20-27: The Storybook package set is version-skewed because the
core storybook dependency is behind the other Storybook addons and utilities.
Update the storybook entry in the storybook package group to match the same
10.3.5 range used by the other Storybook packages, keeping the existing package
names unchanged.
In `@test/e2e/og-image.spec.ts`:
- Line 34: The snapshot name generation for the OG image tests is only partially
sanitizing the `path` value, so query-string characters from entries like
`compare` still leak into the filename. Update the slug/name transform in
`og-image.spec.ts` where the snapshot path is derived to sanitize all unsafe
characters from the `path` value, not just `/` and a leading `-`, so the
generated artifact name stays filesystem-friendly for the `compare` cases and
the other affected entries.
🪄 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: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Run ID: 59831482-ee95-47a2-8248-bc6768eadbff
⛔ Files ignored due to path filters (15)
pnpm-lock.yamlis excluded by!**/pnpm-lock.yamltest/e2e/og-image.spec.ts-snapshots/og-image-accessibility.pngis excluded by!**/*.pngtest/e2e/og-image.spec.ts-snapshots/og-image-blog-alpha-release.pngis excluded by!**/*.pngtest/e2e/og-image.spec.ts-snapshots/og-image-compare-packages-vue-react-svelte.pngis excluded by!**/*.pngtest/e2e/og-image.spec.ts-snapshots/og-image-for--.pngis excluded by!**/*.pngtest/e2e/og-image.spec.ts-snapshots/og-image-for--package-nuxt-v-3-20-2.pngis excluded by!**/*.pngtest/e2e/og-image.spec.ts-snapshots/og-image-home.pngis excluded by!**/*.pngtest/e2e/og-image.spec.ts-snapshots/og-image-package--babel-plugin-transform-exponentiation-operator.pngis excluded by!**/*.pngtest/e2e/og-image.spec.ts-snapshots/og-image-package--nuxt-kit.pngis excluded by!**/*.pngtest/e2e/og-image.spec.ts-snapshots/og-image-package--tanstack-react-query.pngis excluded by!**/*.pngtest/e2e/og-image.spec.ts-snapshots/og-image-package-code-takumi-js-v-1-8-7.pngis excluded by!**/*.pngtest/e2e/og-image.spec.ts-snapshots/og-image-package-code-vue-v-3-5-27.pngis excluded by!**/*.pngtest/e2e/og-image.spec.ts-snapshots/og-image-package-docs-ufo-v-1-6-3.pngis excluded by!**/*.pngtest/e2e/og-image.spec.ts-snapshots/og-image-package-nuxt-v-4-3-1.pngis excluded by!**/*.pngtest/e2e/og-image.spec.ts-snapshots/og-image-package-vue.pngis excluded by!**/*.png
📒 Files selected for processing (63)
.env.example.storybook/preview.tsapp/app.vueapp/components/OgBrand.vueapp/components/OgImage/BlogPost.d.vue.tsapp/components/OgImage/BlogPost.takumi.vueapp/components/OgImage/BlogPost.vueapp/components/OgImage/Compare.takumi.vueapp/components/OgImage/Default.vueapp/components/OgImage/Package.d.vue.tsapp/components/OgImage/Package.takumi.vueapp/components/OgImage/Package.vueapp/components/OgImage/Page.takumi.vueapp/components/OgImage/Profile.takumi.vueapp/components/OgImage/Splash.takumi.vueapp/components/OgLayout.vueapp/components/global/BlogPostWrapper.vueapp/composables/useCharts.tsapp/pages/about.vueapp/pages/accessibility.vueapp/pages/brand.vueapp/pages/compare.vueapp/pages/index.vueapp/pages/org/[org].vueapp/pages/package-code/[[org]]/[packageName]/v/[version]/[...filePath].vueapp/pages/package-docs/[...path].vueapp/pages/package/[[org]]/[name].vueapp/pages/pds.vueapp/pages/privacy.vueapp/pages/profile/[identity]/index.vueapp/pages/recharging.vueapp/pages/search.vueapp/pages/settings.vueapp/pages/translation-status.vueapp/pages/~[username]/index.vueapp/pages/~[username]/orgs.vuedocs/nuxt.config.tsmodules/og-image.tsmodules/runtime/server/cache.tsnuxt.config.tspackage.jsonpatches/@nuxt__test-utils.patchpnpm-workspace.yamlpublic/robots.txtscripts/generate-fixtures.tstest/e2e/og-image.spec.tstest/fixtures/jsdelivr/takumi-js.jsontest/fixtures/jsdelivr/vue.jsontest/fixtures/mock-routes.cjstest/fixtures/npm-api/downloads/@anthropic-ai/claude-code.jsontest/fixtures/npm-api/downloads/@babel/plugin-transform-exponentiation-operator.jsontest/fixtures/npm-api/downloads/@tanstack/react-query.jsontest/fixtures/npm-api/downloads/react.jsontest/fixtures/npm-api/downloads/svelte.jsontest/fixtures/npm-registry/packuments/@anthropic-ai/claude-code.jsontest/fixtures/npm-registry/packuments/@babel/plugin-transform-exponentiation-operator.jsontest/fixtures/npm-registry/packuments/@tanstack/react-query.jsontest/fixtures/npm-registry/packuments/react.jsontest/fixtures/npm-registry/packuments/svelte.jsontest/fixtures/npm-registry/packuments/takumi-js.jsontest/nuxt/components/OgImagePackage.spec.tstest/unit/a11y-component-coverage.spec.tsuno.config.ts
💤 Files with no reviewable changes (6)
- app/components/OgImage/Package.d.vue.ts
- app/components/OgImage/BlogPost.d.vue.ts
- app/components/OgImage/Default.vue
- app/components/OgImage/BlogPost.vue
- app/components/OgImage/Package.vue
- modules/og-image.ts
There was a problem hiding this comment.
Caution
Inline review comments failed to post. This is likely due to GitHub's internal server error or limits when posting large numbers of comments. If you are seeing this consistently it is likely a permissions issue. Please check "Moderation" -> "Code review limits" under your organization settings.
Actionable comments posted: 5
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
nuxt.config.ts (1)
78-78: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick winRestore a site-wide
twitterCardmeta tag innuxt.config.ts
defineOgImageonly covers OG images, and there’s no othertwitterCard/twitter:carddeclaration in the repo, so X previews would lose their card type without an explicituseSeoMeta({ twitterCard: 'summary_large_image' })or equivalent.🤖 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 `@nuxt.config.ts` at line 78, Restore a site-wide twitterCard meta declaration in nuxt.config.ts, since defineOgImage only handles OG images and there is no other twitter:card setting in the app. Add the missing SEO meta via the existing Nuxt config SEO setup (for example with useSeoMeta) so every page continues to advertise summary_large_image for X previews; locate the relevant global metadata block near defineOgImage and the current site-wide head/meta configuration.
🧹 Nitpick comments (6)
app/components/OgImage/BlogPost.takumi.vue (1)
48-56: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winAvoid unchecked author-name indexes.
allNames[0],[1], and[2]bypass the project’s strict indexed-access convention. Add explicit fallbacks before formatting.Proposed fix
const formattedAuthorNames = computed(() => { const allNames = authors.map(a => a.name) if (allNames.length === 0) return '' - if (allNames.length === 1) return allNames[0] - if (allNames.length === 2) return `${allNames[0]} and ${allNames[1]}` - if (allNames.length === 3) return `${allNames[0]}, ${allNames[1]}, and ${allNames[2]}` + const [first = '', second = '', third = ''] = allNames + if (allNames.length === 1) return first + if (allNames.length === 2) return `${first} and ${second}` + if (allNames.length === 3) return `${first}, ${second}, and ${third}` const shown = allNames.slice(0, MAX_VISIBLE_AUTHORS) const remaining = allNames.length - MAX_VISIBLE_AUTHORS return `${shown.join(', ')} and ${remaining} others` })As per coding guidelines, “Ensure you write strictly type-safe code, for example by ensuring you always check when accessing an array value by index.”
🤖 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 `@app/components/OgImage/BlogPost.takumi.vue` around lines 48 - 56, The formattedAuthorNames computed formatter is accessing allNames[0], allNames[1], and allNames[2] directly, which violates the strict indexed-access convention. Update the logic in BlogPost.takumi.vue to add explicit guards/fallbacks before composing the 1-, 2-, and 3-author strings, using named checks against allNames in the formattedAuthorNames function so each indexed value is only used after confirming it exists.Source: Coding guidelines
app/pages/search.vue (1)
555-591: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winDuplicated title/description logic between
useSeoMetaanddefineOgImage.The
titleanddescriptionclosures passed todefineOgImageare identical to those already computed foruseSeoMeta. Consider extracting sharedcomputedrefs to avoid the strings diverging later.♻️ Suggested refactor
+const pageTitle = computed( + () => + `${query.value ? $t('search.title_search', { search: query.value }) : $t('search.title_packages')} - npmx`, +) +const pageDescription = computed(() => + query.value + ? $t('search.meta_description', { search: query.value }) + : $t('search.meta_description_packages'), +) + useSeoMeta({ - title: () => - `${query.value ? $t('search.title_search', { search: query.value }) : $t('search.title_packages')} - npmx`, - ogTitle: () => - `${query.value ? $t('search.title_search', { search: query.value }) : $t('search.title_packages')} - npmx`, - twitterTitle: () => - `${query.value ? $t('search.title_search', { search: query.value }) : $t('search.title_packages')} - npmx`, - description: () => - query.value - ? $t('search.meta_description', { search: query.value }) - : $t('search.meta_description_packages'), - ogDescription: () => - query.value - ? $t('search.meta_description', { search: query.value }) - : $t('search.meta_description_packages'), - twitterDescription: () => - query.value - ? $t('search.meta_description', { search: query.value }) - : $t('search.meta_description_packages'), + title: pageTitle, + ogTitle: pageTitle, + twitterTitle: pageTitle, + description: pageDescription, + ogDescription: pageDescription, + twitterDescription: pageDescription, }) defineOgImage( 'Page.takumi', { - title: () => - `${query.value ? $t('search.title_search', { search: query.value }) : $t('search.title_packages')} - npmx`, - description: () => - query.value - ? $t('search.meta_description', { search: query.value }) - : $t('search.meta_description_packages'), + title: pageTitle, + description: pageDescription, }, { alt: () => query.value ? `Search results for "${query.value}" on npmx` : 'Search npm packages on npmx', }, )🤖 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 `@app/pages/search.vue` around lines 555 - 591, The search page repeats the same title and description logic in both useSeoMeta and defineOgImage, which can drift over time. Extract the shared title and description into reusable computed refs in search.vue, then reference those values from both useSeoMeta and defineOgImage (including the alt text branch as needed) so the metadata stays consistent in one place.app/pages/pds.vue (1)
13-20: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winHardcoded, untranslated strings in
defineOgImage.
title/description/altare hardcoded English literals here, while the page'suseSeoMetafields above use$t(...). Consider wiring these through i18n for consistency with the rest of the page (and similarly for other pages in this PR using literal alt/title strings, e.g.package-code,package-docs,package/[[org]]/[name].vue).Based on learnings,
$t()should be relied on consistently inuseSeoMetaand OG-image-defining composables across all pages rather than hardcoded strings.🤖 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 `@app/pages/pds.vue` around lines 13 - 20, The `defineOgImage` call in `pds.vue` still uses hardcoded English `title`, `description`, and `alt` strings instead of the page’s i18n flow. Update the `defineOgImage` arguments to use `$t(...)`-backed translations like the surrounding `useSeoMeta` fields, and apply the same pattern anywhere else in this PR that passes literal OG/title/alt text (for example in `package-code`, `package-docs`, and `package/[[org]]/[name].vue`) so the composables are consistent.Source: Learnings
pnpm-workspace.yaml (1)
20-27: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winAlign the Storybook versions
storybookis still on^10.3.1while the other Storybook packages are on^10.3.5. If there’s no reason to keep the core package behind, bump it to match.🤖 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 `@pnpm-workspace.yaml` around lines 20 - 27, The Storybook package set is version-skewed because the core storybook dependency is behind the other Storybook addons and utilities. Update the storybook entry in the storybook package group to match the same 10.3.5 range used by the other Storybook packages, keeping the existing package names unchanged.modules/runtime/server/cache.ts (1)
190-234: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚖️ Poor tradeoffDuplicated download-curve mock logic across
cache.tsandmock-routes.cjs.This new branch and the equivalent one in
test/fixtures/mock-routes.cjs(lines 184-210) both mock the npm/downloads/rangeendpoint but use different hashing/curve formulas (this one adds trend/wave-period/weekend-dip; the other is a simpler sine+noise curve). Any future adjustment to one will silently diverge from the other, causing inconsistent sparkline/chart shapes between component tests and e2e snapshot tests for the same package.Consider extracting the shared curve-generation logic into a small utility consumable from both an ESM/TS context and the CJS mock file (e.g. a plain
.mjs/.cjshelper with no TS-only syntax), or generating one from the other in a build step.🤖 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 `@modules/runtime/server/cache.ts` around lines 190 - 234, The npm downloads mock logic is duplicated and diverging between cache.ts and mock-routes.cjs, which will produce inconsistent sparkline shapes. Extract the shared `/downloads/range` curve generation from the cache branch in cache.ts into a reusable helper that both the cache handler and the mock-routes.cjs fixture can call, keeping the hashing, trend, wave, and weekend behavior identical in both places.test/e2e/og-image.spec.ts (1)
34-34: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick winSnapshot filename isn't sanitised for query-string characters.
For
path: '/compare?packages=vue,react,svelte', the name transform only replaces/and strips a leading-, leaving?,=, and,in the generated snapshot filename (og-image-compare?packages=vue,react,svelte.png). This is unusual for a snapshot artifact name and may cause portability issues with some tooling/filesystems.💡 Proposed fix to sanitise the slug
- name: `og-image-${path.replace(/\//g, '-').replace(/^-/, '') || 'home'}.png`, + name: `og-image-${ + path.replace(/[/?]/g, '-').replace(/[=,]/g, '_').replace(/^-/, '') || 'home' + }.png`,Also applies to: 66-67
🤖 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 `@test/e2e/og-image.spec.ts` at line 34, The snapshot name generation for the OG image tests is only partially sanitizing the `path` value, so query-string characters from entries like `compare` still leak into the filename. Update the slug/name transform in `og-image.spec.ts` where the snapshot path is derived to sanitize all unsafe characters from the `path` value, not just `/` and a leading `-`, so the generated artifact name stays filesystem-friendly for the `compare` cases and the other affected entries.
🤖 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 `@app/components/OgImage/Package.takumi.vue`:
- Around line 180-187: The optional likes lookup in Package.takumi.vue can
reject and cause the outer Promise.all flow to fail, which incorrectly 404s the
OG image. Update the fetchLikes logic in the Package component so the $fetch
branch handles failures locally and falls back to totalLikes.value = 0, keeping
likes as non-blocking metadata while preserving the deterministic
import.meta.test path.
In `@app/pages/compare.vue`:
- Around line 145-152: The OG image alt text in defineOgImage is hard-wired to
the empty-state copy and does not reflect when packages.value has selected
items. Update the defineOgImage call in compare.vue so the alt callback mirrors
the same packages.value conditional used by useSeoMeta, returning
comparison-related text when packages are present and the empty-state
translation only when none are selected. Keep the logic aligned with
packages.value and the existing meta helpers to ensure the alt description
matches the rendered image.
In `@nuxt.config.ts`:
- Around line 302-314: The ogImage security config is overly relaxed in all
environments and reuses the image-proxy secret. Update the ogImage security
block in nuxt.config.ts so restrictRuntimeImagesToOrigin is only disabled for
test/e2e or other non-production contexts, while production keeps the stricter
default. Also replace the shared NUXT_IMAGE_PROXY_SECRET usage with a dedicated
OG-image secret in the ogImage.security.secret field so the two signing domains
remain separate.
In `@patches/`@nuxt__test-utils.patch:
- Around line 1-13: The cloning logic in getVitestConfigFromNuxt is dropping
unset runtimeConfig keys because JSON.parse(JSON.stringify(...)) removes
undefined properties. Replace that clone with a method that preserves undefined
fields when building nuxtRuntimeConfig, so auth-related keys like oauthJwkOne
remain present even when unset.
In `@test/fixtures/jsdelivr/vue.json`:
- Around line 5-6: The jsDelivr vue fixture has an invalid default entrypoint
because vue.global.min.js is not present in the mocked dist contents. Update the
default field in vue.json to reference one of the existing files under dist,
such as vue.global.js or vue.global.prod.js, so the fixture matches the
available mock assets.
---
Outside diff comments:
In `@nuxt.config.ts`:
- Line 78: Restore a site-wide twitterCard meta declaration in nuxt.config.ts,
since defineOgImage only handles OG images and there is no other twitter:card
setting in the app. Add the missing SEO meta via the existing Nuxt config SEO
setup (for example with useSeoMeta) so every page continues to advertise
summary_large_image for X previews; locate the relevant global metadata block
near defineOgImage and the current site-wide head/meta configuration.
---
Nitpick comments:
In `@app/components/OgImage/BlogPost.takumi.vue`:
- Around line 48-56: The formattedAuthorNames computed formatter is accessing
allNames[0], allNames[1], and allNames[2] directly, which violates the strict
indexed-access convention. Update the logic in BlogPost.takumi.vue to add
explicit guards/fallbacks before composing the 1-, 2-, and 3-author strings,
using named checks against allNames in the formattedAuthorNames function so each
indexed value is only used after confirming it exists.
In `@app/pages/pds.vue`:
- Around line 13-20: The `defineOgImage` call in `pds.vue` still uses hardcoded
English `title`, `description`, and `alt` strings instead of the page’s i18n
flow. Update the `defineOgImage` arguments to use `$t(...)`-backed translations
like the surrounding `useSeoMeta` fields, and apply the same pattern anywhere
else in this PR that passes literal OG/title/alt text (for example in
`package-code`, `package-docs`, and `package/[[org]]/[name].vue`) so the
composables are consistent.
In `@app/pages/search.vue`:
- Around line 555-591: The search page repeats the same title and description
logic in both useSeoMeta and defineOgImage, which can drift over time. Extract
the shared title and description into reusable computed refs in search.vue, then
reference those values from both useSeoMeta and defineOgImage (including the alt
text branch as needed) so the metadata stays consistent in one place.
In `@modules/runtime/server/cache.ts`:
- Around line 190-234: The npm downloads mock logic is duplicated and diverging
between cache.ts and mock-routes.cjs, which will produce inconsistent sparkline
shapes. Extract the shared `/downloads/range` curve generation from the cache
branch in cache.ts into a reusable helper that both the cache handler and the
mock-routes.cjs fixture can call, keeping the hashing, trend, wave, and weekend
behavior identical in both places.
In `@pnpm-workspace.yaml`:
- Around line 20-27: The Storybook package set is version-skewed because the
core storybook dependency is behind the other Storybook addons and utilities.
Update the storybook entry in the storybook package group to match the same
10.3.5 range used by the other Storybook packages, keeping the existing package
names unchanged.
In `@test/e2e/og-image.spec.ts`:
- Line 34: The snapshot name generation for the OG image tests is only partially
sanitizing the `path` value, so query-string characters from entries like
`compare` still leak into the filename. Update the slug/name transform in
`og-image.spec.ts` where the snapshot path is derived to sanitize all unsafe
characters from the `path` value, not just `/` and a leading `-`, so the
generated artifact name stays filesystem-friendly for the `compare` cases and
the other affected entries.
🪄 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: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Run ID: 59831482-ee95-47a2-8248-bc6768eadbff
⛔ Files ignored due to path filters (15)
pnpm-lock.yamlis excluded by!**/pnpm-lock.yamltest/e2e/og-image.spec.ts-snapshots/og-image-accessibility.pngis excluded by!**/*.pngtest/e2e/og-image.spec.ts-snapshots/og-image-blog-alpha-release.pngis excluded by!**/*.pngtest/e2e/og-image.spec.ts-snapshots/og-image-compare-packages-vue-react-svelte.pngis excluded by!**/*.pngtest/e2e/og-image.spec.ts-snapshots/og-image-for--.pngis excluded by!**/*.pngtest/e2e/og-image.spec.ts-snapshots/og-image-for--package-nuxt-v-3-20-2.pngis excluded by!**/*.pngtest/e2e/og-image.spec.ts-snapshots/og-image-home.pngis excluded by!**/*.pngtest/e2e/og-image.spec.ts-snapshots/og-image-package--babel-plugin-transform-exponentiation-operator.pngis excluded by!**/*.pngtest/e2e/og-image.spec.ts-snapshots/og-image-package--nuxt-kit.pngis excluded by!**/*.pngtest/e2e/og-image.spec.ts-snapshots/og-image-package--tanstack-react-query.pngis excluded by!**/*.pngtest/e2e/og-image.spec.ts-snapshots/og-image-package-code-takumi-js-v-1-8-7.pngis excluded by!**/*.pngtest/e2e/og-image.spec.ts-snapshots/og-image-package-code-vue-v-3-5-27.pngis excluded by!**/*.pngtest/e2e/og-image.spec.ts-snapshots/og-image-package-docs-ufo-v-1-6-3.pngis excluded by!**/*.pngtest/e2e/og-image.spec.ts-snapshots/og-image-package-nuxt-v-4-3-1.pngis excluded by!**/*.pngtest/e2e/og-image.spec.ts-snapshots/og-image-package-vue.pngis excluded by!**/*.png
📒 Files selected for processing (63)
.env.example.storybook/preview.tsapp/app.vueapp/components/OgBrand.vueapp/components/OgImage/BlogPost.d.vue.tsapp/components/OgImage/BlogPost.takumi.vueapp/components/OgImage/BlogPost.vueapp/components/OgImage/Compare.takumi.vueapp/components/OgImage/Default.vueapp/components/OgImage/Package.d.vue.tsapp/components/OgImage/Package.takumi.vueapp/components/OgImage/Package.vueapp/components/OgImage/Page.takumi.vueapp/components/OgImage/Profile.takumi.vueapp/components/OgImage/Splash.takumi.vueapp/components/OgLayout.vueapp/components/global/BlogPostWrapper.vueapp/composables/useCharts.tsapp/pages/about.vueapp/pages/accessibility.vueapp/pages/brand.vueapp/pages/compare.vueapp/pages/index.vueapp/pages/org/[org].vueapp/pages/package-code/[[org]]/[packageName]/v/[version]/[...filePath].vueapp/pages/package-docs/[...path].vueapp/pages/package/[[org]]/[name].vueapp/pages/pds.vueapp/pages/privacy.vueapp/pages/profile/[identity]/index.vueapp/pages/recharging.vueapp/pages/search.vueapp/pages/settings.vueapp/pages/translation-status.vueapp/pages/~[username]/index.vueapp/pages/~[username]/orgs.vuedocs/nuxt.config.tsmodules/og-image.tsmodules/runtime/server/cache.tsnuxt.config.tspackage.jsonpatches/@nuxt__test-utils.patchpnpm-workspace.yamlpublic/robots.txtscripts/generate-fixtures.tstest/e2e/og-image.spec.tstest/fixtures/jsdelivr/takumi-js.jsontest/fixtures/jsdelivr/vue.jsontest/fixtures/mock-routes.cjstest/fixtures/npm-api/downloads/@anthropic-ai/claude-code.jsontest/fixtures/npm-api/downloads/@babel/plugin-transform-exponentiation-operator.jsontest/fixtures/npm-api/downloads/@tanstack/react-query.jsontest/fixtures/npm-api/downloads/react.jsontest/fixtures/npm-api/downloads/svelte.jsontest/fixtures/npm-registry/packuments/@anthropic-ai/claude-code.jsontest/fixtures/npm-registry/packuments/@babel/plugin-transform-exponentiation-operator.jsontest/fixtures/npm-registry/packuments/@tanstack/react-query.jsontest/fixtures/npm-registry/packuments/react.jsontest/fixtures/npm-registry/packuments/svelte.jsontest/fixtures/npm-registry/packuments/takumi-js.jsontest/nuxt/components/OgImagePackage.spec.tstest/unit/a11y-component-coverage.spec.tsuno.config.ts
💤 Files with no reviewable changes (6)
- app/components/OgImage/Package.d.vue.ts
- app/components/OgImage/BlogPost.d.vue.ts
- app/components/OgImage/Default.vue
- app/components/OgImage/BlogPost.vue
- app/components/OgImage/Package.vue
- modules/og-image.ts
🛑 Comments failed to post (5)
app/components/OgImage/Package.takumi.vue (1)
180-187: 🩺 Stability & Availability | 🟠 Major | ⚡ Quick win
Don’t let optional likes failures 404 the OG image.
The non-test
$fetch('/api/social/likes/...')branch can reject, and the outerPromise.allcatch turns that into a 404. Treat likes like optional metadata and fall back to0.Proposed fix
const fetchLikes = import.meta.test ? // need deterministic likes for testing Promise.resolve().then(() => { totalLikes.value = 83 }) - : $fetch<{ totalLikes: number }>(`/api/social/likes/${name}`).then(d => { + : $fetch<{ totalLikes: number }>(`/api/social/likes/${name}`).then(d => { totalLikes.value = d?.totalLikes ?? 0 + }).catch(() => { + totalLikes.value = 0 })📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.const fetchLikes = import.meta.test ? // need deterministic likes for testing Promise.resolve().then(() => { totalLikes.value = 83 }) : $fetch<{ totalLikes: number }>(`/api/social/likes/${name}`).then(d => { totalLikes.value = d?.totalLikes ?? 0 }).catch(() => { totalLikes.value = 0 })🤖 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 `@app/components/OgImage/Package.takumi.vue` around lines 180 - 187, The optional likes lookup in Package.takumi.vue can reject and cause the outer Promise.all flow to fail, which incorrectly 404s the OG image. Update the fetchLikes logic in the Package component so the $fetch branch handles failures locally and falls back to totalLikes.value = 0, keeping likes as non-blocking metadata while preserving the deterministic import.meta.test path.app/pages/compare.vue (1)
145-152: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win
OG image
altdoesn't reflect selected packages.The
altis hard-wired to the empty-state translation regardless ofpackages.value, whileuseSeoMetabelow (Lines 238-263) conditionally switches title/description based onpackages.value.length > 0. When packages are selected, the image will show a real comparison but the alt text will still say something like "Compare npm packages side-by-side," misdescribing the actual content for screen-reader users.♿️ Proposed fix
defineOgImage( 'Compare.takumi', { packages: () => packages.value.toSorted((a, b) => a.localeCompare(b)), emptyDescription: () => $t('compare.packages.meta_description_empty'), }, - { alt: () => $t('compare.packages.meta_description_empty') }, + { + alt: () => + packages.value.length > 0 + ? $t('compare.packages.meta_description', { packages: packages.value.join(', ') }) + : $t('compare.packages.meta_description_empty'), + }, )📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.defineOgImage( 'Compare.takumi', { packages: () => packages.value.toSorted((a, b) => a.localeCompare(b)), emptyDescription: () => $t('compare.packages.meta_description_empty'), }, { alt: () => packages.value.length > 0 ? $t('compare.packages.meta_description', { packages: packages.value.join(', ') }) : $t('compare.packages.meta_description_empty'), }, )🤖 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 `@app/pages/compare.vue` around lines 145 - 152, The OG image alt text in defineOgImage is hard-wired to the empty-state copy and does not reflect when packages.value has selected items. Update the defineOgImage call in compare.vue so the alt callback mirrors the same packages.value conditional used by useSeoMeta, returning comparison-related text when packages are present and the empty-state translation only when none are selected. Keep the logic aligned with packages.value and the existing meta helpers to ensure the alt description matches the rendered image.nuxt.config.ts (1)
302-314: 🔒 Security & Privacy | 🟠 Major | ⚡ Quick win
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash set -euo pipefail printf '--- nuxt.config.ts around target lines ---\n' sed -n '288,328p' nuxt.config.ts | cat -n printf '\n--- search for NUXT_IMAGE_PROXY_SECRET ---\n' rg -n "NUXT_IMAGE_PROXY_SECRET|restrictRuntimeImagesToOrigin|ogImage:" -S . || trueRepository: npmx-dev/npmx.dev
Length of output: 246
🏁 Script executed:
#!/bin/bash set -euo pipefail echo '--- nuxt.config.ts around target lines ---' sed -n '288,328p' nuxt.config.ts | cat -n echo echo '--- search for NUXT_IMAGE_PROXY_SECRET / restrictRuntimeImagesToOrigin / ogImage ---' rg -n "NUXT_IMAGE_PROXY_SECRET|restrictRuntimeImagesToOrigin|ogImage:" -S . || trueRepository: npmx-dev/npmx.dev
Length of output: 2587
🏁 Script executed:
#!/bin/bash set -euo pipefail echo '--- nuxt.config.ts ogImage block ---' sed -n '330,355p' nuxt.config.ts | cat -n echo echo '--- modules/image-proxy.ts ---' sed -n '1,120p' modules/image-proxy.ts | cat -n echo echo '--- docs/nuxt.config.ts ogImage block ---' sed -n '1,40p' docs/nuxt.config.ts | cat -nRepository: npmx-dev/npmx.dev
Length of output: 3332
Limit the OG-image security relaxation to non-production
restrictRuntimeImagesToOrigin: falsedisables origin pinning everywhere, including production. Gate that override to test/e2e only so release builds keep the stricter default, and use a dedicated OG-image secret instead of reusingNUXT_IMAGE_PROXY_SECRETto keep the two signing domains separate.🤖 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 `@nuxt.config.ts` around lines 302 - 314, The ogImage security config is overly relaxed in all environments and reuses the image-proxy secret. Update the ogImage security block in nuxt.config.ts so restrictRuntimeImagesToOrigin is only disabled for test/e2e or other non-production contexts, while production keeps the stricter default. Also replace the shared NUXT_IMAGE_PROXY_SECRET usage with a dedicated OG-image secret in the ogImage.security.secret field so the two signing domains remain separate.patches/@nuxt__test-utils.patch (1)
1-13: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash # Inspect runtimeConfig shape to check for non-JSON-safe values rg -n "runtimeConfig" nuxt.config.ts -A5Repository: npmx-dev/npmx.dev
Length of output: 284
🏁 Script executed:
#!/bin/bash set -euo pipefail # Show the full runtimeConfig block and any obvious non-JSON-safe usages nearby. sed -n '34,70p' nuxt.config.ts printf '\n---\n' # Search for runtimeConfig declarations/usages that might introduce Dates, Maps, Sets, functions, or circular refs. rg -n "runtimeConfig|structuredClone|JSON\.parse|JSON\.stringify|Date\(|new Map|new Set|BigInt|Symbol|function\(" -S . \ --glob '!dist/**' --glob '!node_modules/**'Repository: npmx-dev/npmx.dev
Length of output: 50373
🏁 Script executed:
#!/bin/bash set -euo pipefail # Inspect the relevant patch hunk and the source of runtimeConfig values. git diff -- patches/@nuxt__test-utils.patch printf '\n--- nuxt.config.ts ---\n' sed -n '34,70p' nuxt.config.tsRepository: npmx-dev/npmx.dev
Length of output: 1175
🏁 Script executed:
#!/bin/bash set -euo pipefail # Find direct uses of the runtimeConfig keys. rg -n "oauthJwkOne|sessionPassword|imageProxySecret|upstash|orgToken|runtimeConfig\." -S . \ --glob '!dist/**' --glob '!node_modules/**' --glob '!patches/**'Repository: npmx-dev/npmx.dev
Length of output: 19649
🏁 Script executed:
#!/bin/bash set -euo pipefail # Look specifically for reads of the missing field and any presence checks on runtimeConfig. rg -n "oauthJwkOne|hasOwnProperty\(['\"]oauthJwkOne['\"]\)|['\"]oauthJwkOne['\"] in|runtimeConfig\.(sessionPassword|imageProxySecret|github|oauthJwkOne|upstash|public)" -S . \ --glob '!dist/**' --glob '!node_modules/**' --glob '!patches/**'Repository: npmx-dev/npmx.dev
Length of output: 594
🏁 Script executed:
#!/bin/bash set -euo pipefail node - <<'JS' const runtimeConfig = { sessionPassword: '', imageProxySecret: '', github: { orgToken: '' }, oauthJwkOne: undefined, upstash: { redisRestUrl: '', redisRestToken: '' }, public: { algolia: { appId: 'OFCNCOG2CU', apiKey: 'x', indexName: 'npm-search' } }, } const cloned = JSON.parse(JSON.stringify(runtimeConfig)) const structured = structuredClone(runtimeConfig) console.log('original keys:', Object.keys(runtimeConfig).join(', ')) console.log('json keys :', Object.keys(cloned).join(', ')) console.log('structured :', Object.keys(structured).join(', ')) console.log('has oauthJwkOne original/json/structured:', Object.prototype.hasOwnProperty.call(runtimeConfig, 'oauthJwkOne'), Object.prototype.hasOwnProperty.call(cloned, 'oauthJwkOne'), Object.prototype.hasOwnProperty.call(structured, 'oauthJwkOne'), ) JSRepository: npmx-dev/npmx.dev
Length of output: 461
Preserve unset runtimeConfig fields
JSON.parse(JSON.stringify(...))dropsoauthJwkOnewhen it is unset, and that key is read by the auth code. Use a clone method that keepsundefinedproperties.🤖 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 `@patches/`@nuxt__test-utils.patch around lines 1 - 13, The cloning logic in getVitestConfigFromNuxt is dropping unset runtimeConfig keys because JSON.parse(JSON.stringify(...)) removes undefined properties. Replace that clone with a method that preserves undefined fields when building nuxtRuntimeConfig, so auth-related keys like oauthJwkOne remain present even when unset.test/fixtures/jsdelivr/vue.json (1)
5-6: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash set -euo pipefail printf 'File contents:\n' cat -n test/fixtures/jsdelivr/vue.json | sed -n '1,120p' printf '\nSearch for referenced default path in fixture tree:\n' rg -n 'vue\.global\.min\.js|/dist/vue\.global\.min\.js' test/fixtures/jsdelivr -S || true printf '\nList nearby fixture files:\n' find test/fixtures/jsdelivr -maxdepth 2 -type f | sort | sed -n '1,200p'Repository: npmx-dev/npmx.dev
Length of output: 4635
🏁 Script executed:
#!/bin/bash set -euo pipefail cat -n test/fixtures/jsdelivr/vue.json | sed -n '1,120p'Repository: npmx-dev/npmx.dev
Length of output: 4308
Use an existing file for the default entrypoint (test/fixtures/jsdelivr/vue.json:5).
defaultpoints to/dist/vue.global.min.js, but the fixture only containsvue.global.jsandvue.global.prod.jsunderdist. Update it to a file that exists in the mock.🤖 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 `@test/fixtures/jsdelivr/vue.json` around lines 5 - 6, The jsDelivr vue fixture has an invalid default entrypoint because vue.global.min.js is not present in the mocked dist contents. Update the default field in vue.json to reference one of the existing files under dist, such as vue.global.js or vue.global.prod.js, so the fixture matches the available mock assets.
5aa2e46 to
a601475
Compare
🔗 Linked issue
N/A
🧭 Context
While upgrading
nuxt-og-imageand Takumi, a few OG image routes were visibly broken. The regressions showed up as shifted absolute overlays, black repo icons, and unstable package compare output.Examples that drove this fix:
/package-code/takumi-js/v/1.8.7had the nested file tree decoration misaligned./package/vueand/package/nuxt/v/4.3.1had provider icons rendering black instead of the muted icon color./compare/vue,react,sveltecould move between runs because the compare data was not fully fixture-backed.📚 Description
nuxt-og-imageto6.7.2and Takumi packages to2.0.0-rc.5.takumi-jsnested tree case and the compare-package snapshot, then refreshes the affected OG snapshots.Verified with
CI=1 pnpm install --frozen-lockfile, targetedvp lint,git diff --check, andpnpm test:types.