[
  {
    "path": ".github/CODEOWNERS",
    "content": "* @eungyeole"
  },
  {
    "path": ".github/FUNDING.yml",
    "content": "github: eungyeole\n"
  },
  {
    "path": ".github/dependabot.yml",
    "content": "# To get started with Dependabot version updates, you'll need to specify which\n# package ecosystems to update and where the package manifests are located.\n# Please see the documentation for all configuration options:\n# https://docs.github.com/code-security/dependabot/dependabot-version-updates/configuration-options-for-the-dependabot.yml-file\n\nversion: 2\nupdates:\n  - package-ecosystem: \"npm\" # See documentation for possible values\n    directory: \"/\" # Location of package manifests\n    schedule:\n      interval: \"weekly\"\n"
  },
  {
    "path": ".gitignore",
    "content": ".vercel\nnode_modules\n"
  },
  {
    "path": ".nvmrc",
    "content": "v18.12.1"
  },
  {
    "path": ".vscode/settings.json",
    "content": "{\n  \"editor.tabSize\": 2,\n  \"editor.formatOnSave\": true,\n  \"editor.codeActionsOnSave\": {\n    \"source.fixAll.eslint\": \"explicit\"\n  }\n}\n"
  },
  {
    "path": "README.md",
    "content": "# velog-readme-stats란?\n\n> markdown에서 나의 velog의 정보를 가져올수 있는 도구입니다.\n\n# Velog 뱃지\n\n> ?name= 의 값을 변경하세요\n\n```\n[![Velog's GitHub stats](https://velog-readme-stats.vercel.app/api/badge?name=eungyeole)](https://velog.io/@eungyeole)\n```\n\n[![Velog's GitHub stats](https://velog-readme-stats.vercel.app/api/badge?name=eungyeole)](https://velog.io/@eungyeole)\n\n# 최신 글 가져오기\n\n> ?name= 의 값을 변경하세요\n\n```\n[![Velog's GitHub stats](https://velog-readme-stats.vercel.app/api?name=eungyeole)](https://github.com/eungyeole/velog-readme-stats)\n```\n\n[![Velog's GitHub stats](https://velog-readme-stats.vercel.app/api?name=eungyeole)](https://github.com/eungyeole/velog-readme-stats)\n\n## 특정태그를 가진 최신글 가져오기\n\n> Option : `&tag`\n\n```\n[![Velog's GitHub stats](https://velog-readme-stats.vercel.app/api?name=eungyeole&tag=github)](https://github.com/eungyeole/velog-readme-stats)\n```\n\n[![Velog's GitHub stats](https://velog-readme-stats.vercel.app/api?name=eungyeole&tag=github)](https://github.com/eungyeole/velog-readme-stats)\n\n## 특정 제목을 가진 글\n\n> Option : `&slug`\n\n```\n[![Velog's GitHub stats](https://velog-readme-stats.vercel.app/api?name=eungyeole&slug=Velog-포스트로-Github를-꾸며보자)](https://github.com/eungyeole/velog-readme-stats)\n```\n\n[![Velog's GitHub stats](https://velog-readme-stats.vercel.app/api?name=eungyeole&slug=Velog-포스트로-Github를-꾸며보자)](https://github.com/eungyeole/velog-readme-stats)\n\n## 상태카드와 최신글 연결하기\n\n> Option : name, tag\n> 카드클릭시 최신글로 리다이렉트 합니다.\n\n```\nhttps://velog-readme-stats.vercel.app/api/redirect?name=eungyeole&tag=github\n```\n\n[![Velog's GitHub stats](https://velog-readme-stats.vercel.app/api?name=eungyeole&tag=github)](https://velog-readme-stats.vercel.app/api/redirect?name=eungyeole&tag=github)\n\n# 최신 글 목록 가져오기 (Beta)\n\n> ?name= 의 값을 변경하세요\n\n```\n[![Velog's GitHub stats](https://velog-readme-stats.vercel.app/api/list?name=eungyeole)](https://velog.io/@eungyeole)\n```\n\n[![Velog's GitHub stats](https://velog-readme-stats.vercel.app/api/list?name=eungyeole)](https://velog.io/@eungyeole)\n"
  },
  {
    "path": "api/serverless.ts",
    "content": "import app from \"../src/app\";\n\nimport { VercelRequest, VercelResponse } from \"@vercel/node\";\n\nconst serverless = async (req: VercelRequest, res: VercelResponse) => {\n  await app.ready();\n\n  return app.server.emit(\"request\", req, res);\n};\n\nexport default serverless;\n"
  },
  {
    "path": "package.json",
    "content": "{\n  \"name\": \"velog-readme-stats\",\n  \"version\": \"1.0.0\",\n  \"description\": \"당신의 Velog 상태를 깃허브에서 확인하세요.\",\n  \"main\": \"./api/index.js\",\n  \"scripts\": {\n    \"dev\": \"ts-node-dev --respawn --transpile-only ./src/index.ts\",\n    \"dev:vercel\": \"vercel dev --debug\",\n    \"test\": \"echo \\\"Error: no test specified\\\" && exit 1\"\n  },\n  \"author\": \"\",\n  \"license\": \"MIT\",\n  \"dependencies\": {\n    \"@fastify/view\": \"^9.1.0\",\n    \"@napi-rs/canvas\": \"^0.1.44\",\n    \"ejs\": \"^3.1.10\",\n    \"fastify\": \"^4.28.1\",\n    \"graphql-request\": \"^6.1.0\"\n  },\n  \"devDependencies\": {\n    \"@types/ejs\": \"^3.1.5\",\n    \"@types/node\": \"^20.14.9\",\n    \"@vercel/node\": \"^3.2.1\",\n    \"ts-node-dev\": \"^2.0.0\",\n    \"typescript\": \"^5.5.3\",\n    \"vercel\": \"^34.3.0\"\n  }\n}\n"
  },
  {
    "path": "src/app.ts",
    "content": "import Fastify from \"fastify\";\nimport { router } from \"./router\";\nimport fastifyView from \"@fastify/view\";\nimport ejs from \"ejs\";\nimport path from \"path\";\nimport { cwd } from \"process\";\n\nconst app = Fastify({\n  logger: false,\n});\n\napp.register(router, {\n  prefix: \"/api\",\n});\n\napp.register(fastifyView, {\n  engine: {\n    ejs,\n  },\n  templates: path.resolve(cwd(), \"templates\"),\n});\n\nexport default app;\n"
  },
  {
    "path": "src/errors/ApiError.ts",
    "content": "export class ApiError extends Error {\n  statusCode: number;\n\n  constructor(message: string, statusCode: number) {\n    super(message);\n    this.statusCode = statusCode;\n    Error.captureStackTrace(this, this.constructor);\n  }\n}\n\nexport class NotFoundError extends ApiError {\n  constructor(message = \"Resource not found\") {\n    super(message, 404);\n  }\n}\n\nexport class InternalServerError extends ApiError {\n  constructor(message = \"Internal Server Error\") {\n    super(message, 500);\n  }\n}\n"
  },
  {
    "path": "src/index.ts",
    "content": "import app from \"./app\";\n\napp.listen(\n  {\n    port: 3000,\n  },\n  (err, address) => {\n    if (err) {\n      throw err;\n    }\n    console.log(`Server listening on ${address}`);\n  }\n);\n"
  },
  {
    "path": "src/router.ts",
    "content": "import { FastifyInstance } from \"fastify\";\nimport { ping } from \"./routes/ping\";\nimport { redirect } from \"./routes/redirect\";\nimport { postList } from \"./routes/post-list\";\nimport { post } from \"./routes/post\";\nimport { badge } from \"./routes/badge\";\n\nexport const router = async (fastify: FastifyInstance) => {\n  fastify.register(ping);\n  fastify.register(redirect);\n  fastify.register(postList);\n  fastify.register(post);\n  fastify.register(badge);\n};\n"
  },
  {
    "path": "src/routes/badge.ts",
    "content": "import { FastifyInstance, FastifyRequest } from \"fastify\";\nimport { NotFoundError } from \"../errors/ApiError\";\nimport { getTextWidth } from \"../utils/get-text-width\";\n\nexport const badge = async (fastify: FastifyInstance) => {\n  fastify.get(\n    \"/badge\",\n    async (\n      req: FastifyRequest<{\n        Querystring: { name: string };\n      }>,\n      res\n    ) => {\n      const { name } = req.query;\n\n      if (!name) {\n        throw new NotFoundError(\"name is required\");\n      }\n\n      return res.type(\"image/svg+xml\").view(\"badge.ejs\", {\n        name,\n        getTextWidth,\n      });\n    }\n  );\n};\n"
  },
  {
    "path": "src/routes/ping.ts",
    "content": "import { FastifyInstance } from \"fastify\";\n\nexport const ping = async (fastify: FastifyInstance) => {\n  fastify.get(\"/ping\", async (_, res) => {\n    res.send(\"pong\");\n  });\n};\n"
  },
  {
    "path": "src/routes/post-list.ts",
    "content": "import { FastifyInstance, FastifyRequest } from \"fastify\";\nimport { velogClient } from \"../utils/velog-client\";\n\nexport const postList = async (fastify: FastifyInstance) => {\n  fastify.get(\n    \"/list\",\n    async (\n      req: FastifyRequest<{\n        Querystring: { name: string };\n      }>,\n      res\n    ) => {\n      const { name } = req.query;\n\n      try {\n        const { posts } = await velogClient.getPosts({\n          username: name,\n          limit: 4,\n        });\n\n        const data = {\n          username: name,\n          posts: posts.map((post) => ({\n            user: { username: name },\n            url_slug: post.url_slug,\n            title: post.title,\n          })),\n        };\n\n        return res.type(\"image/svg+xml\").view(\"post-list.ejs\", data);\n      } catch (e) {\n        return res.send(e);\n      }\n    }\n  );\n};\n"
  },
  {
    "path": "src/routes/post.ts",
    "content": "import { FastifyInstance, FastifyRequest } from \"fastify\";\nimport { getTextWidth } from \"../utils/get-text-width\";\nimport { NotFoundError } from \"../errors/ApiError\";\nimport { getRecentlyPost } from \"../utils/get-recently-post\";\n\nexport const post = async (fastify: FastifyInstance) => {\n  fastify.get(\n    \"/\",\n    async (\n      req: FastifyRequest<{\n        Querystring: { name: string; tag?: string; slug?: string };\n      }>,\n      res\n    ) => {\n      const { name, slug, tag } = req.query;\n\n      if (!name) {\n        throw new NotFoundError(\"name is required\");\n      }\n\n      try {\n        const post = await getRecentlyPost(name, { tag, slug });\n\n        return res.type(\"image/svg+xml\").view(\"post.ejs\", {\n          ...post,\n          getTextWidth,\n        });\n      } catch (e) {\n        return res.send(e);\n      }\n    }\n  );\n};\n"
  },
  {
    "path": "src/routes/redirect.ts",
    "content": "import { FastifyInstance, FastifyRequest } from \"fastify\";\nimport { NotFoundError } from \"../errors/ApiError\";\nimport { velogClient } from \"../utils/velog-client\";\n\nexport const redirect = async (fastify: FastifyInstance) => {\n  fastify.get(\n    \"/redirect\",\n    async (\n      req: FastifyRequest<{\n        Querystring: { name: string; tag: string };\n      }>,\n      res\n    ) => {\n      const { name, tag } = req.query;\n\n      try {\n        const { posts } = await velogClient.getPosts({\n          username: name,\n          tag,\n          limit: 1,\n        });\n\n        const [post] = posts;\n\n        if (!post.url_slug) {\n          throw new NotFoundError(\"Post not found\");\n        }\n\n        const url = new URL(`https://velog.io/@${name}/${post.url_slug}`);\n\n        res.redirect(url.toString(), 301);\n      } catch (e) {\n        return res.send(e);\n      }\n    }\n  );\n};\n"
  },
  {
    "path": "src/utils/get-recently-post.ts",
    "content": "import { velogClient } from \"./velog-client\";\n\ninterface GetRecentlyPostOptions {\n  slug?: string;\n  tag?: string;\n}\n\ninterface RecentlyPost {\n  title: string | null;\n  likes: number | null;\n  short_description: string | null;\n  user: { username: string };\n  tags: Array<string>;\n}\n\nexport const getRecentlyPost = async (\n  username: string,\n  options?: GetRecentlyPostOptions\n) => {\n  const { slug, tag } = options || {};\n\n  if (slug) {\n    const { post } = await velogClient.getPostByUrlSlug({\n      username,\n      url_slug: slug,\n    });\n    return post as RecentlyPost;\n  }\n\n  const { posts } = await velogClient.getPosts({\n    username,\n    limit: 1,\n    tag,\n  });\n\n  return posts[0] as RecentlyPost;\n};\n"
  },
  {
    "path": "src/utils/get-text-width.ts",
    "content": "import canvas from \"@napi-rs/canvas\";\n\nexport function getTextWidth(text: string, font: number) {\n  const ctx = canvas.createCanvas(1, 1).getContext(\"2d\");\n  ctx.font = `${font}px -apple-system, BlinkMacSystemFont, Segoe UI, Helvetica, Arial, sans-serif, Apple Color Emoji, Segoe UI Emoji`;\n  const textMetrics = ctx.measureText(text);\n\n  return textMetrics.width;\n}\n"
  },
  {
    "path": "src/utils/velog-client/client.ts",
    "content": "import { GraphQLClient } from 'graphql-request'\n\nexport const client = new GraphQLClient('https://v3.velog.io/graphql')"
  },
  {
    "path": "src/utils/velog-client/gql/posts.ts",
    "content": "import { gql } from \"graphql-request\";\n\nexport const postsGql = gql`\n  query velogPosts($input: GetPostsInput!) {\n    posts(input: $input) {\n      id\n      title\n      short_description\n      thumbnail\n      user {\n        username\n        profile {\n          thumbnail\n        }\n      }\n      url_slug\n      released_at\n      updated_at\n      comments_count\n      tags\n      likes\n    }\n  }\n`;\n"
  },
  {
    "path": "src/utils/velog-client/gql/read-post.ts",
    "content": "import { gql } from \"graphql-request\";\n\nexport const readPostGql = gql`\n  query readPost($input: ReadPostInput!) {\n    post(input: $input) {\n      id\n      title\n      released_at\n      updated_at\n      body\n      short_description\n      is_markdown\n      is_private\n      is_temp\n      thumbnail\n      comments_count\n      url_slug\n      likes\n      is_liked\n      is_followed\n      tags\n      user {\n        id\n        username\n        profile {\n          id\n          display_name\n          thumbnail\n          short_bio\n          profile_links\n        }\n        velog_config {\n          title\n        }\n      }\n      comments {\n        id\n        user {\n          id\n          username\n          profile {\n            id\n            thumbnail\n          }\n        }\n        text\n        replies_count\n        level\n        created_at\n        level\n        deleted\n      }\n      series {\n        id\n        name\n        url_slug\n        series_posts {\n          id\n          post {\n            id\n            title\n            url_slug\n            user {\n              id\n              username\n            }\n          }\n        }\n      }\n      linked_posts {\n        previous {\n          id\n          title\n          url_slug\n          user {\n            id\n            username\n          }\n        }\n        next {\n          id\n          title\n          url_slug\n          user {\n            id\n            username\n          }\n        }\n      }\n    }\n  }\n`;\n"
  },
  {
    "path": "src/utils/velog-client/index.ts",
    "content": "export * from \"./velog-client\";\nexport * from \"./types\";\n"
  },
  {
    "path": "src/utils/velog-client/types.ts",
    "content": "export interface GetPostsParams {\n  cursor?: number;\n  limit?: number;\n  username: string;\n  tag?: string;\n}\n\nexport type GetPostsResponse = {\n  posts: Array<{\n    id: string;\n    title: string | null;\n    short_description: string | null;\n    thumbnail: string | null;\n    url_slug: string | null;\n    released_at: any | null;\n    updated_at: any;\n    comments_count: number | null;\n    tags: Array<string>;\n    is_private: boolean;\n    likes: number | null;\n    user: {\n      id: string;\n      username: string;\n      profile: { id: string; thumbnail: string | null; display_name: string };\n    } | null;\n  }>;\n};\n\nexport interface GetPostByUrlSlugParams {\n  username: string;\n  url_slug: string;\n}\n\nexport interface GetPostByUrlSlugResponse {\n  post: {\n    id: string;\n    title: string | null;\n    released_at: any | null;\n    updated_at: any;\n    body: string | null;\n    short_description: string | null;\n    is_markdown: boolean | null;\n    is_private: boolean;\n    is_temp: boolean | null;\n    thumbnail: string | null;\n    comments_count: number | null;\n    url_slug: string | null;\n    likes: number | null;\n    is_liked: boolean | null;\n    is_followed: boolean | null;\n    tags: Array<string>;\n    user: {\n      id: string;\n      username: string;\n      profile: {\n        id: string;\n        display_name: string;\n        thumbnail: string | null;\n        short_bio: string;\n        profile_links: Record<string, any>;\n      };\n      velog_config: { title: string | null } | null;\n    } | null;\n    comments: Array<{\n      id: string;\n      text: string | null;\n      replies_count: number | null;\n      level: number | null;\n      created_at: any | null;\n      deleted: boolean | null;\n      user: {\n        id: string;\n        username: string;\n        profile: { id: string; thumbnail: string | null };\n      } | null;\n    }>;\n    series: {\n      id: string;\n      name: string | null;\n      url_slug: string | null;\n      series_posts: Array<{\n        id: string;\n        post: {\n          id: string;\n          title: string | null;\n          url_slug: string | null;\n          user: { id: string; username: string } | null;\n        } | null;\n      }> | null;\n    } | null;\n    linked_posts: {\n      previous: {\n        id: string;\n        title: string | null;\n        url_slug: string | null;\n        user: { id: string; username: string } | null;\n      } | null;\n      next: {\n        id: string;\n        title: string | null;\n        url_slug: string | null;\n        user: { id: string; username: string } | null;\n      } | null;\n    } | null;\n  } | null;\n}\n"
  },
  {
    "path": "src/utils/velog-client/velog-client.ts",
    "content": "import { client } from \"./client\";\nimport { postsGql } from \"./gql/posts\";\nimport {\n  GetPostByUrlSlugParams,\n  GetPostByUrlSlugResponse,\n  GetPostsParams,\n  GetPostsResponse,\n} from \"./types\";\nimport { readPostGql } from \"./gql/read-post\";\n\nclass VelogClient {\n  constructor() {}\n\n  public async getPosts(params: GetPostsParams) {\n    return await client.request<GetPostsResponse>(postsGql, {\n      input: params,\n    });\n  }\n\n  public async getPostByUrlSlug(params: GetPostByUrlSlugParams) {\n    return await client.request<GetPostByUrlSlugResponse>(readPostGql, {\n      input: params,\n    });\n  }\n}\n\nexport const velogClient = new VelogClient();\n"
  },
  {
    "path": "templates/badge.ejs",
    "content": "<% const size = getTextWidth(name, 11); const rectWidth = size + 28; %>\n<svg\n  width=\"<%= rectWidth %>\"\n  height=\"20\"\n  fill=\"none\"\n  xmlns=\"http://www.w3.org/2000/svg\"\n  viewBox=\"0 0 <%= rectWidth %> 20\"\n>\n  <style>\n    .name {\n      fill: #ffffff;\n      font-weight: 500;\n      font-size: 11px;\n    }\n    .background {\n      width: 100%;\n      height: 100%;\n      fill: #20c997;\n      rx: 3;\n    }\n    .logo {\n      fill: #ffffff;\n      width: 14px;\n      height: 14px;\n      transform: translateY(-2px);\n    }\n  </style>\n  <rect class=\"background\" />\n\n  <g>\n    <path\n      class=\"logo\"\n      d=\"M18.6199 8.526V7.54163C17.9949 7.3385 17.2605 7.11975 16.4167 6.88538C15.573 6.63538 15.0027 6.51038 14.7058 6.51038C14.0496 6.51038 13.6589 6.82288 13.5339 7.44788L12.0105 16.0963C11.5261 15.4557 11.1277 14.9166 10.8152 14.4791C10.3308 13.7916 9.8855 13.0026 9.47925 12.1119C9.05737 11.2213 8.84644 10.4244 8.84644 9.72131C8.84644 9.29944 8.96362 8.9635 9.198 8.7135C9.41675 8.44788 9.83081 8.11194 10.4402 7.70569C9.81519 6.90881 9.03393 6.51038 8.09643 6.51038C7.59644 6.51038 7.18237 6.65881 6.85425 6.95569C6.5105 7.25256 6.33862 7.69006 6.33862 8.26819C6.33862 9.23694 6.74487 10.4479 7.55737 11.901C8.35425 13.3385 9.89331 15.5026 12.1746 18.3932L14.4949 18.5573L16.2761 8.526H18.6199Z\"\n    />\n\n    <text\n      textLength=\"<%= size %>\"\n      text-anchor=\"start\"\n      x=\"22\"\n      y=\"14\"\n      class=\"name\"\n    >\n      <%= name %>\n    </text>\n  </g>\n</svg>\n"
  },
  {
    "path": "templates/post-list.ejs",
    "content": "<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"450\" height=\"160\">\n  <style>\n    @keyframes fadeInAnimation {\n      from {\n        opacity: 0;\n      }\n      to {\n        opacity: 1;\n      }\n    }\n\n    svg {\n      font-family: -apple-system, BlinkMacSystemFont, Segoe UI, Helvetica, Arial,\n        sans-serif, Apple Color Emoji, Segoe UI Emoji;\n      animation: fadeInAnimation 0.8s ease-in-out forwards;\n    }\n    .background {\n      fill: #00000000;\n      width: calc(100% - 1px);\n      height: calc(100% - 1px);\n      stroke: #8b8b8b22;\n      stroke-opacity: 1;\n      rx: 4.5px;\n      x: 0.5px;\n      y: 0.5px;\n    }\n    .header {\n      font-size: 14px;\n      font-weight: 600;\n      fill: #8d96a0;\n    }\n    .log-title {\n      font-size: 14px;\n      font-weight: 400;\n      fill: #8d96a0;\n    }\n    .log-description {\n      font-size: 12px;\n      fill: #8d96a0;\n    }\n    .tag-item {\n      font-size: 12px;\n      fill: #0ca678;\n    }\n    .heart-count {\n      font-size: 12px;\n      fill: #8d96a0;\n    }\n    .log-title:hover {\n      fill: #0ca678;\n      text-decoration: underline;\n    }\n    .list-style {\n      font-size: 14px;\n      fill: #8d96a0;\n    }\n  </style>\n  <rect class=\"background\" />\n\n  <g transform=\"translate(25, 35)\">\n    <g transform=\"translate(0, 0)\">\n      <text x=\"0\" y=\"0\" class=\"header\"><%= username %>.log's latest posts</text>\n    </g>\n  </g>\n  <g transform=\"translate(0, 45)\">\n    <svg x=\"25\" width=\"400\" height=\"400\" viewBox=\"0 0 400 400\">\n      <g transform=\"translate(0, 0)\">\n        <% posts.forEach((post, index) => { %>\n        <text class=\"list-style\" x=\"5\" y=\"<%= 20 + index * 23 %>\">•</text>\n        <a\n          href=\"https://velog.io/@<%= post.user.username %>/<%= post.url_slug %>\"\n        >\n          <text x=\"20\" y=\"<%= 20 + index * 23 %>\" class=\"log-title\">\n            <%= post.title || \"-\" %>\n          </text>\n        </a>\n        <% }); %>\n      </g>\n    </svg>\n  </g>\n</svg>\n"
  },
  {
    "path": "templates/post.ejs",
    "content": "<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"450\" height=\"130\">\n  <style>\n    @keyframes fadeInAnimation {\n      from {\n        opacity: 0;\n      }\n      to {\n        opacity: 1;\n      }\n    }\n\n    svg {\n      font-family: -apple-system, BlinkMacSystemFont, Segoe UI, Helvetica, Arial,\n        sans-serif, Apple Color Emoji, Segoe UI Emoji;\n      animation: fadeInAnimation 0.8s ease-in-out forwards;\n    }\n    text {\n      fill: #8d96a0;\n    }\n    .background {\n      fill: #00000000;\n      width: calc(100% - 1px);\n      height: calc(100% - 1px);\n      stroke: #8b8b8b22;\n      stroke-opacity: 1;\n      rx: 4.5px;\n      x: 0.5px;\n      y: 0.5px;\n    }\n    .header {\n      font-weight: 600;\n      font-size: 14px;\n    }\n    .log-title {\n      font-weight: 700;\n      font-size: 14px;\n    }\n    .log-description {\n      font-size: 12px;\n      font-weight: 400;\n    }\n    .likes {\n      font-size: 12px;\n    }\n    .tag__text {\n      font-size: 12px;\n      font-weight: 400;\n      fill: #0ca678;\n      text-anchor: middle;\n    }\n    .tag {\n      fill: #00000000;\n      stroke: #8b8b8b22;\n      stroke-opacity: 1;\n    }\n  </style>\n  <rect class=\"background\" />\n  <g transform=\"translate(25, 35)\">\n    <g transform=\"translate(0, 0)\">\n      <text x=\"0\" y=\"0\" class=\"header\"><%= user.username %>.log</text>\n      <svg\n        width=\"30\"\n        x=\"390\"\n        y=\"-10\"\n        height=\"13\"\n        viewBox=\"0 0 30 13\"\n        fill=\"none\"\n        xmlns=\"http://www.w3.org/2000/svg\"\n      >\n        <path\n          d=\"M11.25 0L7.5 2.26044L3.75 0L0 2.82555V6.78133L7.5 12.4324L15 6.78133V2.82555L11.25 0Z\"\n          fill=\"#8d96a0\"\n        />\n      </svg>\n      <text x=\"<%= likes > 99 ? 365 : likes > 9 ? 370 : 380 %>\" class=\"likes\">\n        <%= likes %>\n      </text>\n    </g>\n  </g>\n  <g data-testid=\"main-card-body\" transform=\"translate(0, 45)\">\n    <svg x=\"25\" width=\"400\" height=\"40\" viewBox=\"0 0 400 40\">\n      <g transform=\"translate(0, 0)\">\n        <text x=\"2\" y=\"15\" class=\"log-title\"><%= title %></text>\n        <text x=\"2\" y=\"35\" class=\"log-description\">\n          <%= short_description %>\n        </text>\n      </g>\n    </svg>\n  </g>\n  <g transform=\"translate(0, 40)\">\n    <% let prev = 25; %> <% tags.forEach((tag, index) => { const size =\n    getTextWidth(tag, 12) + 20; const pos = prev; if (prev + size > 400) return;\n    prev += size + 6; %>\n    <svg x=\"<%= pos %>\" width=\"<%= size %>\" viewBox=\"0 0 <%= size %> 19\">\n      <g style=\"position: relative\">\n        <rect width=\"<%= size %>\" height=\"19.5367\" rx=\"9.76834\" class=\"tag\" />\n        <text x=\"<%= size / 2 %>\" y=\"13\" class=\"tag__text\"><%= tag %></text>\n      </g>\n    </svg>\n    <% }); %>\n  </g>\n</svg>\n"
  },
  {
    "path": "tsconfig.json",
    "content": "{\n  \"compilerOptions\": {\n    \"sourceMap\": true,\n    \"outDir\": \"./dist\",\n    \"noImplicitAny\": true,\n    \"strictNullChecks\": true,\n    \"strictFunctionTypes\": true,\n    \"noImplicitThis\": true,\n    \"noUnusedParameters\": true,\n    \"noImplicitReturns\": true,\n    \"allowSyntheticDefaultImports\": true,\n    \"esModuleInterop\": true\n  },\n  \"exclude\": [\"node_modules\"],\n  \"include\": [\"./src/**/*.ts\"]\n}\n"
  },
  {
    "path": "vercel.json",
    "content": "{\n  \"redirects\": [\n    {\n      \"source\": \"/\",\n      \"destination\": \"https://github.com/eungyeole/velog-readme-stats\"\n    }\n  ],\n  \"rewrites\": [\n    {\n      \"source\": \"/api\",\n      \"destination\": \"/api/serverless.ts\"\n    },\n    {\n      \"source\": \"/api/(.*)\",\n      \"destination\": \"/api/serverless.ts\"\n    }\n  ]\n}"
  }
]