Skip to content
8 changes: 7 additions & 1 deletion app/pages/package/[...package].vue
Original file line number Diff line number Diff line change
Expand Up @@ -393,6 +393,9 @@ const canonicalUrl = computed(() => {
return requestedVersion.value ? `${base}/v/${requestedVersion.value}` : base
})

// Markdown alternate URL for AI/LLM consumption
const markdownUrl = computed(() => `${canonicalUrl.value}.md`)

//atproto
// TODO: Maybe set this where it's not loaded here every load?
const { user } = useAtproto()
Expand Down Expand Up @@ -451,7 +454,10 @@ const likeAction = async () => {
}

useHead({
link: [{ rel: 'canonical', href: canonicalUrl }],
link: [
{ rel: 'canonical', href: canonicalUrl },
{ rel: 'alternate', type: 'text/markdown', href: markdownUrl },
],
})

useSeoMeta({
Expand Down
5 changes: 5 additions & 0 deletions codecov.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,11 @@ coverage:
default:
informational: true

# Ignore files that are covered by browser tests (Playwright) rather than unit tests
ignore:
- 'app/pages/**/*'
- 'app/layouts/**/*'

comment:
layout: 'reach,diff,flags,tree,components,tests,build'
behavior: default
Expand Down
1 change: 1 addition & 0 deletions nuxt.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,7 @@ export default defineNuxtConfig({
'/**': { isr: getISRConfig(60, true) },
'/api/**': { isr: 60 },
'/200.html': { prerender: true },
'/raw/**': { isr: 60 },
'/package/**': { isr: getISRConfig(60, true) },
'/:pkg/.well-known/skills/**': { isr: 3600 },
'/:scope/:pkg/.well-known/skills/**': { isr: 3600 },
Expand Down
226 changes: 226 additions & 0 deletions server/routes/raw/[...slug].md.get.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,226 @@
import { generatePackageMarkdown } from '../../utils/markdown'
import * as v from 'valibot'
import { PackageRouteParamsSchema } from '#shared/schemas/package'
import { NPM_MISSING_README_SENTINEL, ERROR_NPM_FETCH_FAILED } from '#shared/utils/constants'

// Cache TTL matches the ISR config for /raw/** routes (60 seconds)
const CACHE_MAX_AGE = 60

const NPM_API = 'https://api.npmjs.org'

const standardReadmeFilenames = [
'README.md',
'readme.md',
'Readme.md',
'README',
'readme',
'README.markdown',
'readme.markdown',
]

const standardReadmePattern = /^readme(\.md|\.markdown)?$/i

function encodePackageName(name: string): string {
if (name.startsWith('@')) {
return `@${encodeURIComponent(name.slice(1))}`
}
return encodeURIComponent(name)
}

async function fetchReadmeFromJsdelivr(
packageName: string,
readmeFilenames: string[],
version?: string,
): Promise<string | null> {
const versionSuffix = version ? `@${version}` : ''

for (const filename of readmeFilenames) {
try {
const url = `https://cdn.jsdelivr.net/npm/${packageName}${versionSuffix}/${filename}`
const response = await fetch(url)
if (response.ok) {
return await response.text()
}
} catch {
// Try next filename
}
}

return null
}

async function fetchWeeklyDownloads(packageName: string): Promise<{ downloads: number } | null> {
try {
const encodedName = encodePackageName(packageName)
return await $fetch<{ downloads: number }>(
`${NPM_API}/downloads/point/last-week/${encodedName}`,
)
} catch {
return null
}
}

async function fetchDownloadRange(
packageName: string,
weeks: number = 12,
): Promise<Array<{ day: string; downloads: number }> | null> {
try {
const encodedName = encodePackageName(packageName)
const today = new Date()
const end = new Date(
Date.UTC(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate() - 1),
)
const start = new Date(end)
start.setUTCDate(start.getUTCDate() - weeks * 7 + 1)

const startStr = start.toISOString().split('T')[0]
const endStr = end.toISOString().split('T')[0]

const response = await $fetch<{
downloads: Array<{ day: string; downloads: number }>
}>(`${NPM_API}/downloads/range/${startStr}:${endStr}/${encodedName}`)

return response.downloads
} catch {
return null
}
}

function isStandardReadme(filename: string | undefined): boolean {
return !!filename && standardReadmePattern.test(filename)
}

function parsePackageParamsFromSlug(slug: string): {
rawPackageName: string
rawVersion: string | undefined
} {
const segments = slug.split('/').filter(Boolean)

if (segments.length === 0) {
return { rawPackageName: '', rawVersion: undefined }
}

const vIndex = segments.indexOf('v')

if (vIndex !== -1 && vIndex < segments.length - 1) {
return {
rawPackageName: segments.slice(0, vIndex).join('/'),
rawVersion: segments.slice(vIndex + 1).join('/'),
}
}

const fullPath = segments.join('/')
const versionMatch = fullPath.match(/^(@[^/]+\/[^@]+|[^@]+)@(.+)$/)
if (versionMatch) {
const [, packageName, version] = versionMatch as [string, string, string]
return {
rawPackageName: packageName,
rawVersion: version,
}
}

return {
rawPackageName: fullPath,
rawVersion: undefined,
}
}

export default defineEventHandler(async event => {
// Get the slug parameter - Nitro captures it as "slug.md" due to the route pattern
const params = getRouterParams(event)
const slugParam = params['slug.md'] || params.slug

if (!slugParam) {
throw createError({
statusCode: 404,
statusMessage: 'Package not found',
})
}

// Remove .md suffix if present (it will be there from the route)
const slug = slugParam.endsWith('.md') ? slugParam.slice(0, -3) : slugParam

const { rawPackageName, rawVersion } = parsePackageParamsFromSlug(slug)

if (!rawPackageName) {
throw createError({
statusCode: 404,
statusMessage: 'Package not found',
})
}

const { packageName, version } = v.parse(PackageRouteParamsSchema, {
packageName: rawPackageName,
version: rawVersion,
})

let packageData
try {
packageData = await fetchNpmPackage(packageName)
} catch {
throw createError({
statusCode: 502,
statusMessage: ERROR_NPM_FETCH_FAILED,
})
}

let targetVersion = version
if (!targetVersion) {
targetVersion = packageData['dist-tags']?.latest
}

if (!targetVersion) {
throw createError({
statusCode: 404,
statusMessage: 'Package version not found',
})
}

const versionData = packageData.versions[targetVersion]
if (!versionData) {
throw createError({
statusCode: 404,
statusMessage: 'Package version not found',
})
}

let readmeContent: string | undefined

if (version) {
readmeContent = versionData.readme
} else {
readmeContent = packageData.readme
}

const readmeFilename = version ? versionData.readmeFilename : packageData.readmeFilename
const hasValidNpmReadme = readmeContent && readmeContent !== NPM_MISSING_README_SENTINEL

if (!hasValidNpmReadme || !isStandardReadme(readmeFilename)) {
const jsdelivrReadme = await fetchReadmeFromJsdelivr(
packageName,
standardReadmeFilenames,
targetVersion,
)
if (jsdelivrReadme) {
readmeContent = jsdelivrReadme
}
}

const [weeklyDownloadsData, dailyDownloads] = await Promise.all([
fetchWeeklyDownloads(packageName),
fetchDownloadRange(packageName, 12),
])

const markdown = generatePackageMarkdown({
pkg: packageData,
version: versionData,
readme: readmeContent && readmeContent !== NPM_MISSING_README_SENTINEL ? readmeContent : null,
weeklyDownloads: weeklyDownloadsData?.downloads,
dailyDownloads: dailyDownloads ?? undefined,
})

setHeader(event, 'Content-Type', 'text/markdown; charset=utf-8')
setHeader(event, 'Cache-Control', `public, max-age=${CACHE_MAX_AGE}, stale-while-revalidate`)

return markdown
})
Loading
Loading