[READ-ONLY] a fast, modern browser for the npm registry
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

fix: prevent 404 on code viewer direct URL access (#630)

authored by

Max and committed by
GitHub
d6e2e1aa 2317d7c4

+69 -104
+69
server/middleware/well-known-skills.ts
··· 1 + import type { H3Event } from 'h3' 2 + import * as v from 'valibot' 3 + import { PackageRouteParamsSchema } from '#shared/schemas/package' 4 + import { SkillNameSchema } from '#shared/schemas/skills' 5 + import { CACHE_MAX_AGE_ONE_HOUR, CACHE_MAX_AGE_ONE_YEAR } from '#shared/utils/constants' 6 + 7 + /** 8 + * Serves /.well-known/skills endpoints for `npx skills add` CLI. 9 + * Middleware pattern allows non-matching paths to pass through to Nuxt. 10 + */ 11 + export default defineEventHandler(event => { 12 + const url = getRequestURL(event) 13 + const match = url.pathname.match(/^\/(.+?)\/\.well-known\/skills\/(.*)$/) 14 + if (!match) return 15 + 16 + return cachedHandler(event) 17 + }) 18 + 19 + const cachedHandler = defineCachedEventHandler( 20 + async (event: H3Event) => { 21 + const url = getRequestURL(event) 22 + const match = url.pathname.match(/^\/(.+?)\/\.well-known\/skills\/(.*)$/)! 23 + const [, pkgPath, skillsPath] = match 24 + 25 + const validated = v.parse(PackageRouteParamsSchema, { 26 + packageName: pkgPath, 27 + version: undefined, 28 + }) 29 + const packument = await fetchNpmPackage(validated.packageName) 30 + const version = packument['dist-tags']?.latest 31 + if (!version) throw createError({ statusCode: 404, message: 'No latest version found' }) 32 + 33 + if (skillsPath === 'index.json' || skillsPath === '') { 34 + const fileTree = await getPackageFileTree(validated.packageName, version) 35 + const skillDirs = findSkillDirs(fileTree.tree) 36 + const skills = skillDirs.length 37 + ? await fetchSkillsListForWellKnown( 38 + validated.packageName, 39 + version, 40 + skillDirs.map(s => s.name), 41 + ) 42 + : [] 43 + setHeader(event, 'Cache-Control', `public, max-age=${CACHE_MAX_AGE_ONE_HOUR}`) 44 + setHeader(event, 'Content-Type', 'application/json') 45 + return { skills } 46 + } 47 + 48 + const [skillName, ...rest] = skillsPath!.split('/') 49 + const fileName = rest.join('/') 50 + if (fileName === 'SKILL.md' || fileName === '') { 51 + const content = await fetchSkillFile( 52 + validated.packageName, 53 + version, 54 + `skills/${v.parse(SkillNameSchema, skillName)}/SKILL.md`, 55 + ) 56 + setHeader(event, 'Cache-Control', `public, max-age=${CACHE_MAX_AGE_ONE_YEAR}, immutable`) 57 + setHeader(event, 'Content-Type', 'text/markdown; charset=utf-8') 58 + return content 59 + } 60 + 61 + throw createError({ statusCode: 404, message: 'File not found' }) 62 + }, 63 + { 64 + maxAge: CACHE_MAX_AGE_ONE_HOUR, 65 + swr: true, 66 + getKey: (event: H3Event) => 67 + `well-known-skills:v1:${getRequestURL(event).pathname.replace(/\/+$/, '')}`, 68 + }, 69 + )
-104
server/routes/[...path].get.ts
··· 1 - import type { H3Event } from 'h3' 2 - import * as v from 'valibot' 3 - import { PackageRouteParamsSchema } from '#shared/schemas/package' 4 - import { SkillNameSchema } from '#shared/schemas/skills' 5 - import { CACHE_MAX_AGE_ONE_HOUR, CACHE_MAX_AGE_ONE_YEAR } from '#shared/utils/constants' 6 - 7 - const CACHE_VERSION = 1 8 - 9 - /** 10 - * Well-known skills endpoint for `npx skills add` CLI compatibility. 11 - * 12 - * URL patterns: 13 - * - /vue/.well-known/skills/index.json → skills index 14 - * - /vue/.well-known/skills/my-skill/SKILL.md → raw SKILL.md 15 - * - /@scope/pkg/.well-known/skills/... → scoped packages 16 - */ 17 - export default defineCachedEventHandler( 18 - async event => { 19 - const path = getRouterParam(event, 'path') || '' 20 - 21 - const match = path.match(/^(.+?)\/\.well-known\/skills\/(.*)$/) 22 - if (!match) { 23 - // Not a well-known skills request, return 404 to let other handlers deal with it 24 - throw createError({ statusCode: 404, message: 'Not found' }) 25 - } 26 - 27 - const [, pkgPath, skillsPath] = match 28 - const packageName = pkgPath! 29 - 30 - try { 31 - const validated = v.parse(PackageRouteParamsSchema, { packageName, version: undefined }) 32 - 33 - // Always resolve to latest for well-known endpoint 34 - const packument = await fetchNpmPackage(validated.packageName) 35 - const version = packument['dist-tags']?.latest 36 - if (!version) { 37 - throw createError({ statusCode: 404, message: 'No latest version found' }) 38 - } 39 - 40 - if (skillsPath === 'index.json' || skillsPath === '') { 41 - return await handleWellKnownIndex(event, validated.packageName, version) 42 - } 43 - 44 - const parts = skillsPath!.split('/') 45 - const skillName = v.parse(SkillNameSchema, parts[0]) 46 - const fileName = parts.slice(1).join('/') 47 - 48 - if (fileName === 'SKILL.md' || fileName === '') { 49 - return await handleWellKnownSkillMd(event, validated.packageName, version, skillName) 50 - } 51 - 52 - throw createError({ statusCode: 404, message: 'File not found' }) 53 - } catch (error) { 54 - if (error && typeof error === 'object' && 'statusCode' in error) throw error 55 - throw createError({ statusCode: 500, message: 'Failed to fetch skills' }) 56 - } 57 - }, 58 - { 59 - maxAge: CACHE_MAX_AGE_ONE_HOUR, 60 - swr: true, 61 - getKey: event => { 62 - const path = getRouterParam(event, 'path') ?? '' 63 - return `well-known-skills:v${CACHE_VERSION}:${path.replace(/\/+$/, '').trim()}` 64 - }, 65 - }, 66 - ) 67 - 68 - async function handleWellKnownIndex(event: H3Event, packageName: string, version: string) { 69 - const fileTree = await getPackageFileTree(packageName, version) 70 - const skillDirs = findSkillDirs(fileTree.tree) 71 - 72 - if (skillDirs.length === 0) { 73 - return { skills: [] } 74 - } 75 - 76 - const skillNames = skillDirs.map(s => s.name) 77 - const skills = await fetchSkillsListForWellKnown(packageName, version, skillNames) 78 - 79 - setHeader(event, 'Cache-Control', `public, max-age=${CACHE_MAX_AGE_ONE_HOUR}`) 80 - setHeader(event, 'Content-Type', 'application/json') 81 - 82 - return { skills } 83 - } 84 - 85 - async function handleWellKnownSkillMd( 86 - event: H3Event, 87 - packageName: string, 88 - version: string, 89 - skillName: string, 90 - ) { 91 - try { 92 - const content = await fetchSkillFile(packageName, version, `skills/${skillName}/SKILL.md`) 93 - 94 - setHeader(event, 'Cache-Control', `public, max-age=${CACHE_MAX_AGE_ONE_YEAR}, immutable`) 95 - setHeader(event, 'Content-Type', 'text/markdown; charset=utf-8') 96 - 97 - return content 98 - } catch (error) { 99 - if (error && typeof error === 'object' && 'statusCode' in error && error.statusCode === 404) { 100 - throw createError({ statusCode: 404, message: 'SKILL.md not found' }) 101 - } 102 - throw error 103 - } 104 - }