├── .gitignore ├── .vscode └── settings.json ├── LICENSE.md ├── README.md ├── app ├── [id] │ ├── layout.tsx │ └── page.tsx ├── api │ └── webhook │ │ └── route.ts ├── avatar.png ├── explanation.tsx ├── favicon.ico ├── icons.tsx ├── layout.tsx ├── not-found.tsx ├── page.tsx └── time-ago.tsx ├── lib └── github.ts ├── next-env.d.ts ├── next.config.ts ├── package.json ├── pnpm-lock.yaml ├── postcss.config.js ├── styles ├── Home.module.scss └── globals.css └── tsconfig.json /.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | /.pnp 6 | .pnp.js 7 | 8 | # testing 9 | /coverage 10 | 11 | # next.js 12 | /.next/ 13 | /out/ 14 | 15 | # production 16 | /build 17 | 18 | # misc 19 | .DS_Store 20 | *.pem 21 | next-env.d.ts 22 | 23 | # debug 24 | npm-debug.log* 25 | yarn-debug.log* 26 | yarn-error.log* 27 | 28 | # local env files 29 | .env.local 30 | .env.development.local 31 | .env.test.local 32 | .env.production.local 33 | 34 | # vercel 35 | .vercel 36 | .output 37 | .env*.local 38 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "typescript.tsdk": "node_modules/.pnpm/typescript@4.9.4/node_modules/typescript/lib", 3 | "typescript.enablePromptUseWorkspaceTsdk": true 4 | } -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2025 Vercel 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![Deploy with Vercel](https://vercel.com/button)](https://vercel.com/new/clone?repository-url=https%3A%2F%2Fgithub.com%2Fvercel%2Fon-demand-isr&env=GITHUB_WEBHOOK_SECRET,GITHUB_APP_ID,GITHUB_APP_PK_PEM&envDescription=API%20keys%20needed%20to%20connect%20to%20the%20GitHub%20Application.&envLink=https%3A%2F%2Fgithub.com%2Fvercel%2Fon-demand-isr&demo-title=On-Demand%20ISR&demo-description=Demo%20of%20on-demand%20ISR%20in%20Next.js%2012.1%20using%20GitHub%20Issues.&demo-url=https%3A%2F%2Fon-demand-isr.vercel.app) 2 | 3 | # On-Demand Incremental Static Regeneration 4 | 5 | Demo of On-demand ISR in [Next.js](https://nextjs.org/docs/app/building-your-application/data-fetching/fetching-caching-and-revalidating#revalidating-data) using GitHub Issues. When a new issue is created, a webhook from a GitHub App _pushes_ new changes to the deployed application to regenerate the static page. 6 | 7 | ## Setup 8 | 9 | 1. Create a new [GitHub App](https://github.com/settings/apps/new) 10 | 1. Provide the URL of your deployed application for Homepage URL 11 | 1. Ensure Webhook "Active" is checked 12 | 1. Add `/api/webhook` as the Webhook URL 13 | 1. Create a Webhook secret and add it to `.env.local` as `GITHUB_WEBHOOK_SECRET` 14 | 1. Give "Read Only" access to Issues 15 | 1. Subscribe to "Issues" events 16 | 1. Add the App ID to `.env.local` as `GITHUB_APP_ID` 17 | 1. Generate a private key and add it to `.env.local` as `GITHUB_APP_PK_PEM` 18 | 1. Install the newly created GitHub App for your repo 19 | 1. `https://github.com/settings/apps//installations` 20 | 21 | ## Running Locally 22 | 23 | ```bash 24 | $ bun dev 25 | ``` 26 | 27 | ## Learn More 28 | 29 | - [Read the documentation](https://nextjs.org/docs/app/building-your-application/data-fetching/fetching-caching-and-revalidating#revalidating-data) 30 | -------------------------------------------------------------------------------- /app/[id]/layout.tsx: -------------------------------------------------------------------------------- 1 | import styles from '../../styles/Home.module.scss'; 2 | import Link from 'next/link'; 3 | import { GitHubIcon, LinkIcon } from '../icons'; 4 | import Explanation from '../explanation'; 5 | 6 | export default async function IssueLayout({ 7 | children, 8 | params 9 | }: { 10 | children: React.ReactNode; 11 | params: Promise<{ id: string }>; 12 | }) { 13 | const id = (await params).id; 14 | 15 | return ( 16 |
17 | 18 | 19 |
20 |
21 | {' '} 22 | 27 | vercel 28 | {' '} 29 | / on-demand-isr /{' '} 30 | 35 | #{id} 36 | 37 |
38 | 47 |
48 | {children} 49 |
50 | ); 51 | } 52 | -------------------------------------------------------------------------------- /app/[id]/page.tsx: -------------------------------------------------------------------------------- 1 | import Image from "next/image"; 2 | import { Marked } from "marked"; 3 | import { markedHighlight } from "marked-highlight"; 4 | import hljs from "highlight.js"; 5 | import styles from "../../styles/Home.module.scss"; 6 | import { fetchIssuePageData } from "../../lib/github"; 7 | import avatar from "../avatar.png"; 8 | import { Time } from "../time-ago"; 9 | 10 | interface Comment { 11 | html_url: string; 12 | id: number; 13 | user: { 14 | login: string; 15 | avatar_url?: string; 16 | }; 17 | created_at: string; 18 | body: string; 19 | } 20 | 21 | export function generateStaticParams() { 22 | return []; 23 | } 24 | 25 | function markdownToHtml( 26 | markdown: string 27 | ) { 28 | if (!markdown) { 29 | return ""; 30 | } 31 | 32 | const marked = new Marked( 33 | markedHighlight({ 34 | langPrefix: "hljs language-", 35 | highlight(code, lang) { 36 | const language = 37 | hljs.getLanguage(lang) 38 | ? lang 39 | : "plaintext"; 40 | return hljs.highlight(code, { 41 | language, 42 | }).value; 43 | }, 44 | }) 45 | ); 46 | 47 | return marked.parse( 48 | markdown 49 | ) as string; 50 | } 51 | 52 | export default async function IssuePage({ 53 | params, 54 | }: { 55 | params: Promise<{ id: string }>; 56 | }) { 57 | const id = (await params).id; 58 | const { issue, comments } = 59 | await fetchIssuePageData(id); 60 | 61 | // Filter out comments that contain "bot" in the title 62 | const filteredComments = 63 | comments.filter( 64 | (comment) => 65 | !comment.user.login 66 | .toLowerCase() 67 | .includes("bot") 68 | ); 69 | 70 | return ( 71 |
72 | 79 |
80 | {issue.user.login} 90 |
91 |
94 |
99 | {issue.user.login}{" "} 100 | commented{" "} 101 |
105 |
No description provided.", 112 | }} 113 | className={ 114 | styles.comment_body 115 | } 116 | /> 117 |
118 |
119 | {filteredComments.map( 120 | (comment: Comment) => ( 121 | 128 |
131 | {comment.user.login} 144 |
145 |
150 |
155 | 156 | {comment.user.login} 157 | {" "} 158 | commented{" "} 159 |
165 |
176 |
177 |
178 | ) 179 | )} 180 |
181 | ); 182 | } 183 | -------------------------------------------------------------------------------- /app/api/webhook/route.ts: -------------------------------------------------------------------------------- 1 | import crypto from 'crypto'; 2 | import { revalidatePath } from 'next/cache'; 3 | 4 | export async function POST(request: Request) { 5 | try { 6 | const text = await request.text(); 7 | 8 | const signature = crypto 9 | .createHmac('sha256', process.env.GITHUB_WEBHOOK_SECRET || '') 10 | .update(text) 11 | .digest('hex'); 12 | 13 | const trusted = Buffer.from(`sha256=${signature}`, 'ascii'); 14 | const untrusted = Buffer.from( 15 | request.headers.get('x-hub-signature-256') || '', 16 | 'ascii' 17 | ); 18 | 19 | if (!crypto.timingSafeEqual(trusted, untrusted)) { 20 | console.log('[Next.js] Invalid signature.', { 21 | trusted: trusted.toString('hex'), 22 | untrusted: untrusted.toString('hex'), 23 | }); 24 | return new Response('Invalid signature.', { 25 | status: 400, 26 | }); 27 | } 28 | 29 | const payload = JSON.parse(text); 30 | const issueNumber = payload.issue?.number; 31 | 32 | console.log('[Next.js] Revalidating /'); 33 | revalidatePath('/'); 34 | 35 | if (issueNumber) { 36 | console.log(`[Next.js] Revalidating /${issueNumber}`); 37 | revalidatePath(`/${issueNumber}`); 38 | } 39 | } catch (error) { 40 | return new Response(`Webhook error: ${error.message}`, { 41 | status: 400, 42 | }); 43 | } 44 | 45 | return new Response('Success!', { 46 | status: 200, 47 | }); 48 | } 49 | -------------------------------------------------------------------------------- /app/avatar.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vercel/on-demand-isr/bb263abca6db23c96b090806ccc19da85e5f4db5/app/avatar.png -------------------------------------------------------------------------------- /app/explanation.tsx: -------------------------------------------------------------------------------- 1 | import styles from '../styles/Home.module.scss'; 2 | 3 | export default function Explanation() { 4 | return ( 5 |
6 |

7 | This app demonstrates *On-Demand ISR support* in Next.js ( 8 | 13 | view source 14 | 15 | ). 16 |

17 | 18 |
19 | 20 | How does this work? 21 | 22 |

23 | When issues on the ` 24 | 29 | on-demand-isr 30 | 31 | ` repo change (get created, commented on, deleted, etc), GitHub fires 32 | off a webhook and the impacted pages get re-rendered and pushed to the 33 | edge, on demand. The webhook on the Next.js app side executes a new{' '} 34 | 39 | `revalidatePath()` 40 | {' '} 41 | API call. 42 |

43 |
44 | 45 |
46 | 47 | Didn't this exist already? 48 | 49 | 50 |

51 | Unlike `revalidate`{' '} 52 | 57 | with a time interval 58 | 59 | , a Serverless Function is invoked{' '} 60 | _only when content changes_, making it faster for the user 61 | (they see changes immediately), and more cost-efficient for owners. 62 |

63 |
64 | 65 |

66 | 67 | _💡 Try{' '} 68 | 73 | creating a new issue 74 | {' '} 75 | or commenting, and refresh this page to see the regenerated one!_ 76 | {' '} 77 |
78 | 79 | Pages take about *300ms~* to fully propagate to the global 80 | Vercel Edge Network after the regeneration completes. 81 | 82 |

83 |
84 | ); 85 | } 86 | -------------------------------------------------------------------------------- /app/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vercel/on-demand-isr/bb263abca6db23c96b090806ccc19da85e5f4db5/app/favicon.ico -------------------------------------------------------------------------------- /app/icons.tsx: -------------------------------------------------------------------------------- 1 | import styles from '../styles/Home.module.scss'; 2 | 3 | export function StarIcon() { 4 | return ( 5 | 10 | 14 | 15 | ); 16 | } 17 | 18 | export function ForkIcon() { 19 | return ( 20 | 25 | 29 | 33 | 34 | 35 | ); 36 | } 37 | 38 | export function GitHubIcon() { 39 | return ( 40 | 45 | 46 | 47 | ); 48 | } 49 | 50 | export function IssueIcon() { 51 | return ( 52 | 66 | ); 67 | } 68 | 69 | export function CommentIcon() { 70 | return ( 71 | 76 | 80 | 81 | ); 82 | } 83 | 84 | export function LinkIcon() { 85 | return ( 86 | 91 | 92 | 93 | 94 | ); 95 | } 96 | -------------------------------------------------------------------------------- /app/layout.tsx: -------------------------------------------------------------------------------- 1 | import '../styles/globals.css'; 2 | 3 | export const metadata = { 4 | title: 'On-Demand Incremental Static Regeneration with Next.js', 5 | }; 6 | 7 | export default function RootLayout({ 8 | children, 9 | }: { 10 | children: React.ReactNode; 11 | }) { 12 | return ( 13 | 14 | {children} 15 | 16 | ); 17 | } 18 | -------------------------------------------------------------------------------- /app/not-found.tsx: -------------------------------------------------------------------------------- 1 | export default function NotFound() { 2 | return
404: Not Found
; 3 | } 4 | -------------------------------------------------------------------------------- /app/page.tsx: -------------------------------------------------------------------------------- 1 | import styles from '../styles/Home.module.scss'; 2 | import Link from 'next/link'; 3 | import { 4 | CommentIcon, 5 | IssueIcon, 6 | StarIcon, 7 | ForkIcon, 8 | GitHubIcon 9 | } from './icons'; 10 | import { fetchIssueAndRepoData } from '../lib/github'; 11 | import Explanation from './explanation'; 12 | import { Time } from './time-ago'; 13 | 14 | export default async function Page() { 15 | const { issues, forks_count, stargazers_count } = 16 | await fetchIssueAndRepoData(); 17 | 18 | return ( 19 |
20 | 21 |
22 |
23 | {' '} 24 | 29 | vercel 30 | {' '} 31 | / on-demand-isr 32 |
33 | 49 |
50 |
51 | {issues.map((issue: any) => ( 52 | 53 | ))} 54 |
55 |
56 | ); 57 | } 58 | 59 | function IssueLink({ issue }: { issue: any }) { 60 | return ( 61 | 62 |
63 | 64 |
65 |
{issue.title}
66 |
67 | {`#${issue.number} opened `} 68 | {` by ${issue.user.login}`} 69 |
70 |
71 |
72 |
73 |
78 | 79 | ); 80 | } 81 | -------------------------------------------------------------------------------- /app/time-ago.tsx: -------------------------------------------------------------------------------- 1 | 'use client'; 2 | 3 | import { useState, useEffect } from 'react'; 4 | 5 | function formatDate(date: string) { 6 | const currentDate = new Date().getTime(); 7 | const targetDate = new Date(date).getTime(); 8 | const timeDifference = currentDate - targetDate; 9 | 10 | const seconds = Math.floor(timeDifference / 1000); 11 | const minutes = Math.floor(seconds / 60); 12 | const hours = Math.floor(minutes / 60); 13 | const days = Math.floor(hours / 24); 14 | const weeks = Math.floor(days / 7); 15 | const months = Math.floor(days / 30); 16 | const years = Math.floor(days / 365); 17 | 18 | if (seconds < 2) { 19 | return '1 second ago'; 20 | } else if (seconds < 60) { 21 | return `${seconds} seconds ago`; 22 | } else if (minutes < 2) { 23 | return '1 minute ago'; 24 | } else if (minutes < 60) { 25 | return `${minutes} minutes ago`; 26 | } else if (hours < 2) { 27 | return '1 hour ago'; 28 | } else if (hours < 24) { 29 | return `${hours} hours ago`; 30 | } else if (days < 2) { 31 | return '1 day ago'; 32 | } else if (days < 7) { 33 | return `${days} days ago`; 34 | } else if (weeks < 2) { 35 | return '1 week ago'; 36 | } else if (weeks < 4) { 37 | return `${weeks} weeks ago`; 38 | } else if (months < 2) { 39 | return '1 month ago'; 40 | } else if (months < 12) { 41 | return `${months} months ago`; 42 | } else if (years < 2) { 43 | return '1 year ago'; 44 | } else { 45 | return `${years} years ago`; 46 | } 47 | } 48 | 49 | export function Time({ time }: { time: string }) { 50 | const [formattedDate, setFormattedDate] = useState(''); 51 | 52 | useEffect(() => { 53 | const updateTime = () => { 54 | setFormattedDate(formatDate(time)); 55 | }; 56 | 57 | updateTime(); 58 | const interval = setInterval(updateTime, 1000); 59 | 60 | return () => clearInterval(interval); 61 | }, [time]); 62 | 63 | return {formattedDate}; 64 | } 65 | -------------------------------------------------------------------------------- /lib/github.ts: -------------------------------------------------------------------------------- 1 | import 'server-only'; 2 | import jwt from 'jsonwebtoken'; 3 | import { notFound } from 'next/navigation'; 4 | 5 | let accessToken; 6 | 7 | async function getAccessToken(installationId: number, token: string) { 8 | const data = await fetchGitHub( 9 | `/app/installations/${installationId}/access_tokens`, 10 | token, 11 | { method: 'POST' } 12 | ); 13 | return data.token; 14 | } 15 | 16 | function getGitHubJWT() { 17 | if (!process.env.GITHUB_APP_ID || !process.env.GITHUB_APP_PK_PEM) { 18 | throw new Error( 19 | 'GITHUB_APP_ID and GITHUB_APP_PK_PEM must be defined in .env.local' 20 | ); 21 | } 22 | 23 | return jwt.sign( 24 | { 25 | iat: Math.floor(Date.now() / 1000) - 60, 26 | iss: process.env.GITHUB_APP_ID, 27 | exp: Math.floor(Date.now() / 1000) + 60 * 10 // 10 minutes is the max 28 | }, 29 | process.env.GITHUB_APP_PK_PEM, 30 | { 31 | algorithm: 'RS256' 32 | } 33 | ); 34 | } 35 | 36 | async function getInstallation(token: string) { 37 | const installations = await fetchGitHub('/app/installations', token); 38 | return installations.find((i: any) => i.account.login === 'vercel'); 39 | } 40 | 41 | function createGitHubRequest(path: string, token: string, opts: any = {}) { 42 | return fetch(`https://api.github.com${path}`, { 43 | ...opts, 44 | headers: { 45 | ...opts.headers, 46 | Authorization: `Bearer ${token}`, 47 | 'Content-Type': 'application/json', 48 | Accept: 'application/vnd.github.v3+json' 49 | } 50 | }); 51 | } 52 | 53 | export async function fetchGitHub(path: string, token: string, opts: any = {}) { 54 | let req = await createGitHubRequest(path, token, opts); 55 | 56 | if (req.status === 401) { 57 | // JWT has expired, cache a new token 58 | await setAccessToken(); 59 | // Retry request with new cached access token 60 | req = await createGitHubRequest(path, accessToken, opts); 61 | } 62 | 63 | return req.json(); 64 | } 65 | 66 | export async function readAccessToken() { 67 | // check if exists 68 | if (!accessToken) { 69 | await setAccessToken(); 70 | } 71 | 72 | return accessToken; 73 | } 74 | 75 | export async function setAccessToken() { 76 | const jwt = getGitHubJWT(); 77 | const installation = await getInstallation(jwt); 78 | accessToken = await getAccessToken(installation.id, jwt); 79 | 80 | return accessToken; 81 | } 82 | 83 | export async function fetchIssueAndRepoData() { 84 | 'use cache'; 85 | 86 | const [issues, repoDetails] = await Promise.all([ 87 | fetchGitHub('/repos/vercel/on-demand-isr/issues', accessToken), 88 | fetchGitHub('/repos/vercel/on-demand-isr', accessToken) 89 | ]); 90 | 91 | console.log('[Next.js] Fetching data for /'); 92 | console.log(`[Next.js] Issues: ${issues.length}`); 93 | 94 | return { 95 | issues, 96 | stargazers_count: repoDetails.stargazers_count, 97 | forks_count: repoDetails.forks_count 98 | }; 99 | } 100 | 101 | export async function fetchIssuePageData(id: string) { 102 | 'use cache'; 103 | 104 | const [issue, comments, repoDetails] = await Promise.all([ 105 | fetchGitHub(`/repos/vercel/on-demand-isr/issues/${id}`, accessToken), 106 | fetchGitHub( 107 | `/repos/vercel/on-demand-isr/issues/${id}/comments`, 108 | accessToken 109 | ), 110 | fetchGitHub('/repos/vercel/on-demand-isr', accessToken) 111 | ]); 112 | 113 | console.log(`[Next.js] Fetching data for /${id}`); 114 | console.log(`[Next.js] [${id}] Comments: ${comments.length}`); 115 | 116 | if (issue.message === 'Not Found') { 117 | notFound(); 118 | } 119 | 120 | return { 121 | issue, 122 | comments, 123 | stargazers_count: repoDetails.stargazers_count, 124 | forks_count: repoDetails.forks_count 125 | }; 126 | } 127 | -------------------------------------------------------------------------------- /next-env.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | /// 3 | 4 | // NOTE: This file should not be edited 5 | // see https://nextjs.org/docs/app/api-reference/config/typescript for more information. 6 | -------------------------------------------------------------------------------- /next.config.ts: -------------------------------------------------------------------------------- 1 | import type { NextConfig } from 'next'; 2 | 3 | const nextConfig: NextConfig = { 4 | experimental: { 5 | useCache: true, 6 | inlineCss: true, 7 | }, 8 | images: { 9 | remotePatterns: [ 10 | { 11 | protocol: 'https', 12 | hostname: 'avatars.githubusercontent.com', 13 | port: '', 14 | pathname: '/u/**' 15 | } 16 | ] 17 | }, 18 | logging: { 19 | fetches: { 20 | fullUrl: true 21 | } 22 | } 23 | }; 24 | 25 | export default nextConfig; 26 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "private": true, 3 | "scripts": { 4 | "dev": "next dev --turbopack", 5 | "build": "next build", 6 | "start": "next start" 7 | }, 8 | "dependencies": { 9 | "highlight.js": "^11.11.1", 10 | "jsonwebtoken": "^9.0.2", 11 | "marked": "^15.0.7", 12 | "marked-highlight": "^2.2.1", 13 | "next": "15.3.0-canary.15", 14 | "react": "^19.0.0", 15 | "react-dom": "^19.0.0", 16 | "sass": "^1.85.0", 17 | "server-only": "^0.0.1" 18 | }, 19 | "devDependencies": { 20 | "@types/jsonwebtoken": "^9.0.8", 21 | "@types/node": "^22.13.4", 22 | "@types/react": "^19.0.8", 23 | "postcss": "^8.5.2", 24 | "typescript": "^5.7.3" 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /pnpm-lock.yaml: -------------------------------------------------------------------------------- 1 | lockfileVersion: '9.0' 2 | 3 | settings: 4 | autoInstallPeers: true 5 | excludeLinksFromLockfile: false 6 | 7 | importers: 8 | 9 | .: 10 | dependencies: 11 | highlight.js: 12 | specifier: ^11.11.1 13 | version: 11.11.1 14 | jsonwebtoken: 15 | specifier: ^9.0.2 16 | version: 9.0.2 17 | marked: 18 | specifier: ^15.0.7 19 | version: 15.0.7 20 | marked-highlight: 21 | specifier: ^2.2.1 22 | version: 2.2.1(marked@15.0.7) 23 | next: 24 | specifier: 15.3.0-canary.15 25 | version: 15.3.0-canary.15(react-dom@19.0.0(react@19.0.0))(react@19.0.0)(sass@1.85.0) 26 | react: 27 | specifier: ^19.0.0 28 | version: 19.0.0 29 | react-dom: 30 | specifier: ^19.0.0 31 | version: 19.0.0(react@19.0.0) 32 | sass: 33 | specifier: ^1.85.0 34 | version: 1.85.0 35 | server-only: 36 | specifier: ^0.0.1 37 | version: 0.0.1 38 | devDependencies: 39 | '@types/jsonwebtoken': 40 | specifier: ^9.0.8 41 | version: 9.0.8 42 | '@types/node': 43 | specifier: ^22.13.4 44 | version: 22.13.4 45 | '@types/react': 46 | specifier: ^19.0.8 47 | version: 19.0.8 48 | postcss: 49 | specifier: ^8.5.2 50 | version: 8.5.2 51 | typescript: 52 | specifier: ^5.7.3 53 | version: 5.7.3 54 | 55 | packages: 56 | 57 | '@emnapi/runtime@1.3.1': 58 | resolution: {integrity: sha512-kEBmG8KyqtxJZv+ygbEim+KCGtIq1fC22Ms3S4ziXmYKm8uyoLX0MHONVKwp+9opg390VaKRNt4a7A9NwmpNhw==} 59 | 60 | '@img/sharp-darwin-arm64@0.33.5': 61 | resolution: {integrity: sha512-UT4p+iz/2H4twwAoLCqfA9UH5pI6DggwKEGuaPy7nCVQ8ZsiY5PIcrRvD1DzuY3qYL07NtIQcWnBSY/heikIFQ==} 62 | engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} 63 | cpu: [arm64] 64 | os: [darwin] 65 | 66 | '@img/sharp-darwin-x64@0.33.5': 67 | resolution: {integrity: sha512-fyHac4jIc1ANYGRDxtiqelIbdWkIuQaI84Mv45KvGRRxSAa7o7d1ZKAOBaYbnepLC1WqxfpimdeWfvqqSGwR2Q==} 68 | engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} 69 | cpu: [x64] 70 | os: [darwin] 71 | 72 | '@img/sharp-libvips-darwin-arm64@1.0.4': 73 | resolution: {integrity: sha512-XblONe153h0O2zuFfTAbQYAX2JhYmDHeWikp1LM9Hul9gVPjFY427k6dFEcOL72O01QxQsWi761svJ/ev9xEDg==} 74 | cpu: [arm64] 75 | os: [darwin] 76 | 77 | '@img/sharp-libvips-darwin-x64@1.0.4': 78 | resolution: {integrity: sha512-xnGR8YuZYfJGmWPvmlunFaWJsb9T/AO2ykoP3Fz/0X5XV2aoYBPkX6xqCQvUTKKiLddarLaxpzNe+b1hjeWHAQ==} 79 | cpu: [x64] 80 | os: [darwin] 81 | 82 | '@img/sharp-libvips-linux-arm64@1.0.4': 83 | resolution: {integrity: sha512-9B+taZ8DlyyqzZQnoeIvDVR/2F4EbMepXMc/NdVbkzsJbzkUjhXv/70GQJ7tdLA4YJgNP25zukcxpX2/SueNrA==} 84 | cpu: [arm64] 85 | os: [linux] 86 | 87 | '@img/sharp-libvips-linux-arm@1.0.5': 88 | resolution: {integrity: sha512-gvcC4ACAOPRNATg/ov8/MnbxFDJqf/pDePbBnuBDcjsI8PssmjoKMAz4LtLaVi+OnSb5FK/yIOamqDwGmXW32g==} 89 | cpu: [arm] 90 | os: [linux] 91 | 92 | '@img/sharp-libvips-linux-s390x@1.0.4': 93 | resolution: {integrity: sha512-u7Wz6ntiSSgGSGcjZ55im6uvTrOxSIS8/dgoVMoiGE9I6JAfU50yH5BoDlYA1tcuGS7g/QNtetJnxA6QEsCVTA==} 94 | cpu: [s390x] 95 | os: [linux] 96 | 97 | '@img/sharp-libvips-linux-x64@1.0.4': 98 | resolution: {integrity: sha512-MmWmQ3iPFZr0Iev+BAgVMb3ZyC4KeFc3jFxnNbEPas60e1cIfevbtuyf9nDGIzOaW9PdnDciJm+wFFaTlj5xYw==} 99 | cpu: [x64] 100 | os: [linux] 101 | 102 | '@img/sharp-libvips-linuxmusl-arm64@1.0.4': 103 | resolution: {integrity: sha512-9Ti+BbTYDcsbp4wfYib8Ctm1ilkugkA/uscUn6UXK1ldpC1JjiXbLfFZtRlBhjPZ5o1NCLiDbg8fhUPKStHoTA==} 104 | cpu: [arm64] 105 | os: [linux] 106 | 107 | '@img/sharp-libvips-linuxmusl-x64@1.0.4': 108 | resolution: {integrity: sha512-viYN1KX9m+/hGkJtvYYp+CCLgnJXwiQB39damAO7WMdKWlIhmYTfHjwSbQeUK/20vY154mwezd9HflVFM1wVSw==} 109 | cpu: [x64] 110 | os: [linux] 111 | 112 | '@img/sharp-linux-arm64@0.33.5': 113 | resolution: {integrity: sha512-JMVv+AMRyGOHtO1RFBiJy/MBsgz0x4AWrT6QoEVVTyh1E39TrCUpTRI7mx9VksGX4awWASxqCYLCV4wBZHAYxA==} 114 | engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} 115 | cpu: [arm64] 116 | os: [linux] 117 | 118 | '@img/sharp-linux-arm@0.33.5': 119 | resolution: {integrity: sha512-JTS1eldqZbJxjvKaAkxhZmBqPRGmxgu+qFKSInv8moZ2AmT5Yib3EQ1c6gp493HvrvV8QgdOXdyaIBrhvFhBMQ==} 120 | engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} 121 | cpu: [arm] 122 | os: [linux] 123 | 124 | '@img/sharp-linux-s390x@0.33.5': 125 | resolution: {integrity: sha512-y/5PCd+mP4CA/sPDKl2961b+C9d+vPAveS33s6Z3zfASk2j5upL6fXVPZi7ztePZ5CuH+1kW8JtvxgbuXHRa4Q==} 126 | engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} 127 | cpu: [s390x] 128 | os: [linux] 129 | 130 | '@img/sharp-linux-x64@0.33.5': 131 | resolution: {integrity: sha512-opC+Ok5pRNAzuvq1AG0ar+1owsu842/Ab+4qvU879ippJBHvyY5n2mxF1izXqkPYlGuP/M556uh53jRLJmzTWA==} 132 | engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} 133 | cpu: [x64] 134 | os: [linux] 135 | 136 | '@img/sharp-linuxmusl-arm64@0.33.5': 137 | resolution: {integrity: sha512-XrHMZwGQGvJg2V/oRSUfSAfjfPxO+4DkiRh6p2AFjLQztWUuY/o8Mq0eMQVIY7HJ1CDQUJlxGGZRw1a5bqmd1g==} 138 | engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} 139 | cpu: [arm64] 140 | os: [linux] 141 | 142 | '@img/sharp-linuxmusl-x64@0.33.5': 143 | resolution: {integrity: sha512-WT+d/cgqKkkKySYmqoZ8y3pxx7lx9vVejxW/W4DOFMYVSkErR+w7mf2u8m/y4+xHe7yY9DAXQMWQhpnMuFfScw==} 144 | engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} 145 | cpu: [x64] 146 | os: [linux] 147 | 148 | '@img/sharp-wasm32@0.33.5': 149 | resolution: {integrity: sha512-ykUW4LVGaMcU9lu9thv85CbRMAwfeadCJHRsg2GmeRa/cJxsVY9Rbd57JcMxBkKHag5U/x7TSBpScF4U8ElVzg==} 150 | engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} 151 | cpu: [wasm32] 152 | 153 | '@img/sharp-win32-ia32@0.33.5': 154 | resolution: {integrity: sha512-T36PblLaTwuVJ/zw/LaH0PdZkRz5rd3SmMHX8GSmR7vtNSP5Z6bQkExdSK7xGWyxLw4sUknBuugTelgw2faBbQ==} 155 | engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} 156 | cpu: [ia32] 157 | os: [win32] 158 | 159 | '@img/sharp-win32-x64@0.33.5': 160 | resolution: {integrity: sha512-MpY/o8/8kj+EcnxwvrP4aTJSWw/aZ7JIGR4aBeZkZw5B7/Jn+tY9/VNwtcoGmdT7GfggGIU4kygOMSbYnOrAbg==} 161 | engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} 162 | cpu: [x64] 163 | os: [win32] 164 | 165 | '@next/env@15.3.0-canary.15': 166 | resolution: {integrity: sha512-4++Yw9LviRI1jQwA64heWPnu6JZFXit+w4H6RhPO09eiXvGFvM3sQw0nGP3KK8sDe8HS7MnHXewu5VH4q5kq6Q==} 167 | 168 | '@next/swc-darwin-arm64@15.3.0-canary.15': 169 | resolution: {integrity: sha512-2XwY4L2KFE17AKUXZyyTBifxuqUWRtUhk2IUsjZ9XjLhS/oZ2QGoZid3Hp0CFxKD7/7QU7ent4cK5CuIcY7HYQ==} 170 | engines: {node: '>= 10'} 171 | cpu: [arm64] 172 | os: [darwin] 173 | 174 | '@next/swc-darwin-x64@15.3.0-canary.15': 175 | resolution: {integrity: sha512-GIxcQTVlfql+yF+5y6VZJzHas7Kco/uVOgWvXj2+c0K8TLZndYgF8klg4sYX7bNj0vv7oEdrnCksNm4ijdkrvw==} 176 | engines: {node: '>= 10'} 177 | cpu: [x64] 178 | os: [darwin] 179 | 180 | '@next/swc-linux-arm64-gnu@15.3.0-canary.15': 181 | resolution: {integrity: sha512-B//o+BQt0riRo6VVRpWLKy8I+dCHIhopmNpUBi6VqHlDeLaLd+TSDbuiPIs0q804bvX+aFltARlRQsnQ8cUiIg==} 182 | engines: {node: '>= 10'} 183 | cpu: [arm64] 184 | os: [linux] 185 | 186 | '@next/swc-linux-arm64-musl@15.3.0-canary.15': 187 | resolution: {integrity: sha512-826hx95qA3JO84owyCjkGu+qBc2kqSqlxK3NxYHHwwbVlgBedN+jsW1u/5zKBiJ/W+sTbci2HQPF+F2Fk5JVGg==} 188 | engines: {node: '>= 10'} 189 | cpu: [arm64] 190 | os: [linux] 191 | 192 | '@next/swc-linux-x64-gnu@15.3.0-canary.15': 193 | resolution: {integrity: sha512-iuprGTpx9ejqat7NywfTeBqX4ilyM1ZiChVFCUvtVXgXFl4npeVjdko28Cp4DqXsaid/AX+SUFwOSUaGmx0oFA==} 194 | engines: {node: '>= 10'} 195 | cpu: [x64] 196 | os: [linux] 197 | 198 | '@next/swc-linux-x64-musl@15.3.0-canary.15': 199 | resolution: {integrity: sha512-bysxd/stxsk2zS/wNBpRyX9gsutXTKR+WYWJqO+SWedb6Fexn76pUuTgz/UM42Una3K9bUA4B8O5hQYqmAWJbQ==} 200 | engines: {node: '>= 10'} 201 | cpu: [x64] 202 | os: [linux] 203 | 204 | '@next/swc-win32-arm64-msvc@15.3.0-canary.15': 205 | resolution: {integrity: sha512-5I7jL1aFTSwTnitdYn/dMTnrmnZSI41vvKcg4qcQwotRv/H4Ro9+qSISSqXAEimvGwTDUA9d0CN4RkvLsxJriA==} 206 | engines: {node: '>= 10'} 207 | cpu: [arm64] 208 | os: [win32] 209 | 210 | '@next/swc-win32-x64-msvc@15.3.0-canary.15': 211 | resolution: {integrity: sha512-t4gUHjefzDlevhPTk1/1s7fi4GiVXAgnF4/FhNQHND1eLLXoj5s8Uud2WLdifDprqSClRi6J3TASLf1fuwWXtA==} 212 | engines: {node: '>= 10'} 213 | cpu: [x64] 214 | os: [win32] 215 | 216 | '@parcel/watcher-android-arm64@2.5.1': 217 | resolution: {integrity: sha512-KF8+j9nNbUN8vzOFDpRMsaKBHZ/mcjEjMToVMJOhTozkDonQFFrRcfdLWn6yWKCmJKmdVxSgHiYvTCef4/qcBA==} 218 | engines: {node: '>= 10.0.0'} 219 | cpu: [arm64] 220 | os: [android] 221 | 222 | '@parcel/watcher-darwin-arm64@2.5.1': 223 | resolution: {integrity: sha512-eAzPv5osDmZyBhou8PoF4i6RQXAfeKL9tjb3QzYuccXFMQU0ruIc/POh30ePnaOyD1UXdlKguHBmsTs53tVoPw==} 224 | engines: {node: '>= 10.0.0'} 225 | cpu: [arm64] 226 | os: [darwin] 227 | 228 | '@parcel/watcher-darwin-x64@2.5.1': 229 | resolution: {integrity: sha512-1ZXDthrnNmwv10A0/3AJNZ9JGlzrF82i3gNQcWOzd7nJ8aj+ILyW1MTxVk35Db0u91oD5Nlk9MBiujMlwmeXZg==} 230 | engines: {node: '>= 10.0.0'} 231 | cpu: [x64] 232 | os: [darwin] 233 | 234 | '@parcel/watcher-freebsd-x64@2.5.1': 235 | resolution: {integrity: sha512-SI4eljM7Flp9yPuKi8W0ird8TI/JK6CSxju3NojVI6BjHsTyK7zxA9urjVjEKJ5MBYC+bLmMcbAWlZ+rFkLpJQ==} 236 | engines: {node: '>= 10.0.0'} 237 | cpu: [x64] 238 | os: [freebsd] 239 | 240 | '@parcel/watcher-linux-arm-glibc@2.5.1': 241 | resolution: {integrity: sha512-RCdZlEyTs8geyBkkcnPWvtXLY44BCeZKmGYRtSgtwwnHR4dxfHRG3gR99XdMEdQ7KeiDdasJwwvNSF5jKtDwdA==} 242 | engines: {node: '>= 10.0.0'} 243 | cpu: [arm] 244 | os: [linux] 245 | 246 | '@parcel/watcher-linux-arm-musl@2.5.1': 247 | resolution: {integrity: sha512-6E+m/Mm1t1yhB8X412stiKFG3XykmgdIOqhjWj+VL8oHkKABfu/gjFj8DvLrYVHSBNC+/u5PeNrujiSQ1zwd1Q==} 248 | engines: {node: '>= 10.0.0'} 249 | cpu: [arm] 250 | os: [linux] 251 | 252 | '@parcel/watcher-linux-arm64-glibc@2.5.1': 253 | resolution: {integrity: sha512-LrGp+f02yU3BN9A+DGuY3v3bmnFUggAITBGriZHUREfNEzZh/GO06FF5u2kx8x+GBEUYfyTGamol4j3m9ANe8w==} 254 | engines: {node: '>= 10.0.0'} 255 | cpu: [arm64] 256 | os: [linux] 257 | 258 | '@parcel/watcher-linux-arm64-musl@2.5.1': 259 | resolution: {integrity: sha512-cFOjABi92pMYRXS7AcQv9/M1YuKRw8SZniCDw0ssQb/noPkRzA+HBDkwmyOJYp5wXcsTrhxO0zq1U11cK9jsFg==} 260 | engines: {node: '>= 10.0.0'} 261 | cpu: [arm64] 262 | os: [linux] 263 | 264 | '@parcel/watcher-linux-x64-glibc@2.5.1': 265 | resolution: {integrity: sha512-GcESn8NZySmfwlTsIur+49yDqSny2IhPeZfXunQi48DMugKeZ7uy1FX83pO0X22sHntJ4Ub+9k34XQCX+oHt2A==} 266 | engines: {node: '>= 10.0.0'} 267 | cpu: [x64] 268 | os: [linux] 269 | 270 | '@parcel/watcher-linux-x64-musl@2.5.1': 271 | resolution: {integrity: sha512-n0E2EQbatQ3bXhcH2D1XIAANAcTZkQICBPVaxMeaCVBtOpBZpWJuf7LwyWPSBDITb7In8mqQgJ7gH8CILCURXg==} 272 | engines: {node: '>= 10.0.0'} 273 | cpu: [x64] 274 | os: [linux] 275 | 276 | '@parcel/watcher-win32-arm64@2.5.1': 277 | resolution: {integrity: sha512-RFzklRvmc3PkjKjry3hLF9wD7ppR4AKcWNzH7kXR7GUe0Igb3Nz8fyPwtZCSquGrhU5HhUNDr/mKBqj7tqA2Vw==} 278 | engines: {node: '>= 10.0.0'} 279 | cpu: [arm64] 280 | os: [win32] 281 | 282 | '@parcel/watcher-win32-ia32@2.5.1': 283 | resolution: {integrity: sha512-c2KkcVN+NJmuA7CGlaGD1qJh1cLfDnQsHjE89E60vUEMlqduHGCdCLJCID5geFVM0dOtA3ZiIO8BoEQmzQVfpQ==} 284 | engines: {node: '>= 10.0.0'} 285 | cpu: [ia32] 286 | os: [win32] 287 | 288 | '@parcel/watcher-win32-x64@2.5.1': 289 | resolution: {integrity: sha512-9lHBdJITeNR++EvSQVUcaZoWupyHfXe1jZvGZ06O/5MflPcuPLtEphScIBL+AiCWBO46tDSHzWyD0uDmmZqsgA==} 290 | engines: {node: '>= 10.0.0'} 291 | cpu: [x64] 292 | os: [win32] 293 | 294 | '@parcel/watcher@2.5.1': 295 | resolution: {integrity: sha512-dfUnCxiN9H4ap84DvD2ubjw+3vUNpstxa0TneY/Paat8a3R4uQZDLSvWjmznAY/DoahqTHl9V46HF/Zs3F29pg==} 296 | engines: {node: '>= 10.0.0'} 297 | 298 | '@swc/counter@0.1.3': 299 | resolution: {integrity: sha512-e2BR4lsJkkRlKZ/qCHPw9ZaSxc0MVUd7gtbtaB7aMvHeJVYe8sOB8DBZkP2DtISHGSku9sCK6T6cnY0CtXrOCQ==} 300 | 301 | '@swc/helpers@0.5.15': 302 | resolution: {integrity: sha512-JQ5TuMi45Owi4/BIMAJBoSQoOJu12oOk/gADqlcUL9JEdHB8vyjUSsxqeNXnmXHjYKMi2WcYtezGEEhqUI/E2g==} 303 | 304 | '@types/jsonwebtoken@9.0.8': 305 | resolution: {integrity: sha512-7fx54m60nLFUVYlxAB1xpe9CBWX2vSrk50Y6ogRJ1v5xxtba7qXTg5BgYDN5dq+yuQQ9HaVlHJyAAt1/mxryFg==} 306 | 307 | '@types/ms@2.1.0': 308 | resolution: {integrity: sha512-GsCCIZDE/p3i96vtEqx+7dBUGXrc7zeSK3wwPHIaRThS+9OhWIXRqzs4d6k1SVU8g91DrNRWxWUGhp5KXQb2VA==} 309 | 310 | '@types/node@22.13.4': 311 | resolution: {integrity: sha512-ywP2X0DYtX3y08eFVx5fNIw7/uIv8hYUKgXoK8oayJlLnKcRfEYCxWMVE1XagUdVtCJlZT1AU4LXEABW+L1Peg==} 312 | 313 | '@types/react@19.0.8': 314 | resolution: {integrity: sha512-9P/o1IGdfmQxrujGbIMDyYaaCykhLKc0NGCtYcECNUr9UAaDe4gwvV9bR6tvd5Br1SG0j+PBpbKr2UYY8CwqSw==} 315 | 316 | braces@3.0.3: 317 | resolution: {integrity: sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==} 318 | engines: {node: '>=8'} 319 | 320 | buffer-equal-constant-time@1.0.1: 321 | resolution: {integrity: sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA==} 322 | 323 | busboy@1.6.0: 324 | resolution: {integrity: sha512-8SFQbg/0hQ9xy3UNTB0YEnsNBbWfhf7RtnzpL7TkBiTBRfrQ9Fxcnz7VJsleJpyp6rVLvXiuORqjlHi5q+PYuA==} 325 | engines: {node: '>=10.16.0'} 326 | 327 | caniuse-lite@1.0.30001699: 328 | resolution: {integrity: sha512-b+uH5BakXZ9Do9iK+CkDmctUSEqZl+SP056vc5usa0PL+ev5OHw003rZXcnjNDv3L8P5j6rwT6C0BPKSikW08w==} 329 | 330 | chokidar@4.0.3: 331 | resolution: {integrity: sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==} 332 | engines: {node: '>= 14.16.0'} 333 | 334 | client-only@0.0.1: 335 | resolution: {integrity: sha512-IV3Ou0jSMzZrd3pZ48nLkT9DA7Ag1pnPzaiQhpW7c3RbcqqzvzzVu+L8gfqMp/8IM2MQtSiqaCxrrcfu8I8rMA==} 336 | 337 | color-convert@2.0.1: 338 | resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==} 339 | engines: {node: '>=7.0.0'} 340 | 341 | color-name@1.1.4: 342 | resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==} 343 | 344 | color-string@1.9.1: 345 | resolution: {integrity: sha512-shrVawQFojnZv6xM40anx4CkoDP+fZsw/ZerEMsW/pyzsRbElpsL/DBVW7q3ExxwusdNXI3lXpuhEZkzs8p5Eg==} 346 | 347 | color@4.2.3: 348 | resolution: {integrity: sha512-1rXeuUUiGGrykh+CeBdu5Ie7OJwinCgQY0bc7GCRxy5xVHy+moaqkpL/jqQq0MtQOeYcrqEz4abc5f0KtU7W4A==} 349 | engines: {node: '>=12.5.0'} 350 | 351 | csstype@3.1.3: 352 | resolution: {integrity: sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==} 353 | 354 | detect-libc@1.0.3: 355 | resolution: {integrity: sha512-pGjwhsmsp4kL2RTz08wcOlGN83otlqHeD/Z5T8GXZB+/YcpQ/dgo+lbU8ZsGxV0HIvqqxo9l7mqYwyYMD9bKDg==} 356 | engines: {node: '>=0.10'} 357 | hasBin: true 358 | 359 | detect-libc@2.0.3: 360 | resolution: {integrity: sha512-bwy0MGW55bG41VqxxypOsdSdGqLwXPI/focwgTYCFMbdUiBAxLg9CFzG08sz2aqzknwiX7Hkl0bQENjg8iLByw==} 361 | engines: {node: '>=8'} 362 | 363 | ecdsa-sig-formatter@1.0.11: 364 | resolution: {integrity: sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==} 365 | 366 | fill-range@7.1.1: 367 | resolution: {integrity: sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==} 368 | engines: {node: '>=8'} 369 | 370 | highlight.js@11.11.1: 371 | resolution: {integrity: sha512-Xwwo44whKBVCYoliBQwaPvtd/2tYFkRQtXDWj1nackaV2JPXx3L0+Jvd8/qCJ2p+ML0/XVkJ2q+Mr+UVdpJK5w==} 372 | engines: {node: '>=12.0.0'} 373 | 374 | immutable@5.0.3: 375 | resolution: {integrity: sha512-P8IdPQHq3lA1xVeBRi5VPqUm5HDgKnx0Ru51wZz5mjxHr5n3RWhjIpOFU7ybkUxfB+5IToy+OLaHYDBIWsv+uw==} 376 | 377 | is-arrayish@0.3.2: 378 | resolution: {integrity: sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ==} 379 | 380 | is-extglob@2.1.1: 381 | resolution: {integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==} 382 | engines: {node: '>=0.10.0'} 383 | 384 | is-glob@4.0.3: 385 | resolution: {integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==} 386 | engines: {node: '>=0.10.0'} 387 | 388 | is-number@7.0.0: 389 | resolution: {integrity: sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==} 390 | engines: {node: '>=0.12.0'} 391 | 392 | jsonwebtoken@9.0.2: 393 | resolution: {integrity: sha512-PRp66vJ865SSqOlgqS8hujT5U4AOgMfhrwYIuIhfKaoSCZcirrmASQr8CX7cUg+RMih+hgznrjp99o+W4pJLHQ==} 394 | engines: {node: '>=12', npm: '>=6'} 395 | 396 | jwa@1.4.1: 397 | resolution: {integrity: sha512-qiLX/xhEEFKUAJ6FiBMbes3w9ATzyk5W7Hvzpa/SLYdxNtng+gcurvrI7TbACjIXlsJyr05/S1oUhZrc63evQA==} 398 | 399 | jws@3.2.2: 400 | resolution: {integrity: sha512-YHlZCB6lMTllWDtSPHz/ZXTsi8S00usEV6v1tjq8tOUZzw7DpSDWVXjXDre6ed1w/pd495ODpHZYSdkRTsa0HA==} 401 | 402 | lodash.includes@4.3.0: 403 | resolution: {integrity: sha512-W3Bx6mdkRTGtlJISOvVD/lbqjTlPPUDTMnlXZFnVwi9NKJ6tiAk6LVdlhZMm17VZisqhKcgzpO5Wz91PCt5b0w==} 404 | 405 | lodash.isboolean@3.0.3: 406 | resolution: {integrity: sha512-Bz5mupy2SVbPHURB98VAcw+aHh4vRV5IPNhILUCsOzRmsTmSQ17jIuqopAentWoehktxGd9e/hbIXq980/1QJg==} 407 | 408 | lodash.isinteger@4.0.4: 409 | resolution: {integrity: sha512-DBwtEWN2caHQ9/imiNeEA5ys1JoRtRfY3d7V9wkqtbycnAmTvRRmbHKDV4a0EYc678/dia0jrte4tjYwVBaZUA==} 410 | 411 | lodash.isnumber@3.0.3: 412 | resolution: {integrity: sha512-QYqzpfwO3/CWf3XP+Z+tkQsfaLL/EnUlXWVkIk5FUPc4sBdTehEqZONuyRt2P67PXAk+NXmTBcc97zw9t1FQrw==} 413 | 414 | lodash.isplainobject@4.0.6: 415 | resolution: {integrity: sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA==} 416 | 417 | lodash.isstring@4.0.1: 418 | resolution: {integrity: sha512-0wJxfxH1wgO3GrbuP+dTTk7op+6L41QCXbGINEmD+ny/G/eCqGzxyCsh7159S+mgDDcoarnBw6PC1PS5+wUGgw==} 419 | 420 | lodash.once@4.1.1: 421 | resolution: {integrity: sha512-Sb487aTOCr9drQVL8pIxOzVhafOjZN9UU54hiN8PU3uAiSV7lx1yYNpbNmex2PK6dSJoNTSJUUswT651yww3Mg==} 422 | 423 | marked-highlight@2.2.1: 424 | resolution: {integrity: sha512-SiCIeEiQbs9TxGwle9/OwbOejHCZsohQRaNTY2u8euEXYt2rYUFoiImUirThU3Gd/o6Q1gHGtH9qloHlbJpNIA==} 425 | peerDependencies: 426 | marked: '>=4 <16' 427 | 428 | marked@15.0.7: 429 | resolution: {integrity: sha512-dgLIeKGLx5FwziAnsk4ONoGwHwGPJzselimvlVskE9XLN4Orv9u2VA3GWw/lYUqjfA0rUT/6fqKwfZJapP9BEg==} 430 | engines: {node: '>= 18'} 431 | hasBin: true 432 | 433 | micromatch@4.0.8: 434 | resolution: {integrity: sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==} 435 | engines: {node: '>=8.6'} 436 | 437 | ms@2.1.3: 438 | resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==} 439 | 440 | nanoid@3.3.8: 441 | resolution: {integrity: sha512-WNLf5Sd8oZxOm+TzppcYk8gVOgP+l58xNy58D0nbUnOxOWRWvlcCV4kUF7ltmI6PsrLl/BgKEyS4mqsGChFN0w==} 442 | engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} 443 | hasBin: true 444 | 445 | next@15.3.0-canary.15: 446 | resolution: {integrity: sha512-3RZoMEmMw93FdUSQbfgiYyNKorUDMp6OhAt8HJT2PMA/60goljOlYfEbBUukIJslGadJn5lTGvyLuCFjNDNJYQ==} 447 | engines: {node: ^18.18.0 || ^19.8.0 || >= 20.0.0} 448 | hasBin: true 449 | peerDependencies: 450 | '@opentelemetry/api': ^1.1.0 451 | '@playwright/test': ^1.41.2 452 | babel-plugin-react-compiler: '*' 453 | react: ^18.2.0 || 19.0.0-rc-de68d2f4-20241204 || ^19.0.0 454 | react-dom: ^18.2.0 || 19.0.0-rc-de68d2f4-20241204 || ^19.0.0 455 | sass: ^1.3.0 456 | peerDependenciesMeta: 457 | '@opentelemetry/api': 458 | optional: true 459 | '@playwright/test': 460 | optional: true 461 | babel-plugin-react-compiler: 462 | optional: true 463 | sass: 464 | optional: true 465 | 466 | node-addon-api@7.1.1: 467 | resolution: {integrity: sha512-5m3bsyrjFWE1xf7nz7YXdN4udnVtXK6/Yfgn5qnahL6bCkf2yKt4k3nuTKAtT4r3IG8JNR2ncsIMdZuAzJjHQQ==} 468 | 469 | picocolors@1.1.1: 470 | resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==} 471 | 472 | picomatch@2.3.1: 473 | resolution: {integrity: sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==} 474 | engines: {node: '>=8.6'} 475 | 476 | postcss@8.4.31: 477 | resolution: {integrity: sha512-PS08Iboia9mts/2ygV3eLpY5ghnUcfLV/EXTOW1E2qYxJKGGBUtNjN76FYHnMs36RmARn41bC0AZmn+rR0OVpQ==} 478 | engines: {node: ^10 || ^12 || >=14} 479 | 480 | postcss@8.5.2: 481 | resolution: {integrity: sha512-MjOadfU3Ys9KYoX0AdkBlFEF1Vx37uCCeN4ZHnmwm9FfpbsGWMZeBLMmmpY+6Ocqod7mkdZ0DT31OlbsFrLlkA==} 482 | engines: {node: ^10 || ^12 || >=14} 483 | 484 | react-dom@19.0.0: 485 | resolution: {integrity: sha512-4GV5sHFG0e/0AD4X+ySy6UJd3jVl1iNsNHdpad0qhABJ11twS3TTBnseqsKurKcsNqCEFeGL3uLpVChpIO3QfQ==} 486 | peerDependencies: 487 | react: ^19.0.0 488 | 489 | react@19.0.0: 490 | resolution: {integrity: sha512-V8AVnmPIICiWpGfm6GLzCR/W5FXLchHop40W4nXBmdlEceh16rCN8O8LNWm5bh5XUX91fh7KpA+W0TgMKmgTpQ==} 491 | engines: {node: '>=0.10.0'} 492 | 493 | readdirp@4.1.1: 494 | resolution: {integrity: sha512-h80JrZu/MHUZCyHu5ciuoI0+WxsCxzxJTILn6Fs8rxSnFPh+UVHYfeIxK1nVGugMqkfC4vJcBOYbkfkwYK0+gw==} 495 | engines: {node: '>= 14.18.0'} 496 | 497 | safe-buffer@5.2.1: 498 | resolution: {integrity: sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==} 499 | 500 | sass@1.85.0: 501 | resolution: {integrity: sha512-3ToiC1xZ1Y8aU7+CkgCI/tqyuPXEmYGJXO7H4uqp0xkLXUqp88rQQ4j1HmP37xSJLbCJPaIiv+cT1y+grssrww==} 502 | engines: {node: '>=14.0.0'} 503 | hasBin: true 504 | 505 | scheduler@0.25.0: 506 | resolution: {integrity: sha512-xFVuu11jh+xcO7JOAGJNOXld8/TcEHK/4CituBUeUb5hqxJLj9YuemAEuvm9gQ/+pgXYfbQuqAkiYu+u7YEsNA==} 507 | 508 | semver@7.7.1: 509 | resolution: {integrity: sha512-hlq8tAfn0m/61p4BVRcPzIGr6LKiMwo4VM6dGi6pt4qcRkmNzTcWq6eCEjEh+qXjkMDvPlOFFSGwQjoEa6gyMA==} 510 | engines: {node: '>=10'} 511 | hasBin: true 512 | 513 | server-only@0.0.1: 514 | resolution: {integrity: sha512-qepMx2JxAa5jjfzxG79yPPq+8BuFToHd1hm7kI+Z4zAq1ftQiP7HcxMhDDItrbtwVeLg/cY2JnKnrcFkmiswNA==} 515 | 516 | sharp@0.33.5: 517 | resolution: {integrity: sha512-haPVm1EkS9pgvHrQ/F3Xy+hgcuMV0Wm9vfIBSiwZ05k+xgb0PkBQpGsAA/oWdDobNaZTH5ppvHtzCFbnSEwHVw==} 518 | engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} 519 | 520 | simple-swizzle@0.2.2: 521 | resolution: {integrity: sha512-JA//kQgZtbuY83m+xT+tXJkmJncGMTFT+C+g2h2R9uxkYIrE2yy9sgmcLhCnw57/WSD+Eh3J97FPEDFnbXnDUg==} 522 | 523 | source-map-js@1.2.1: 524 | resolution: {integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==} 525 | engines: {node: '>=0.10.0'} 526 | 527 | streamsearch@1.1.0: 528 | resolution: {integrity: sha512-Mcc5wHehp9aXz1ax6bZUyY5afg9u2rv5cqQI3mRrYkGC8rW2hM02jWuwjtL++LS5qinSyhj2QfLyNsuc+VsExg==} 529 | engines: {node: '>=10.0.0'} 530 | 531 | styled-jsx@5.1.6: 532 | resolution: {integrity: sha512-qSVyDTeMotdvQYoHWLNGwRFJHC+i+ZvdBRYosOFgC+Wg1vx4frN2/RG/NA7SYqqvKNLf39P2LSRA2pu6n0XYZA==} 533 | engines: {node: '>= 12.0.0'} 534 | peerDependencies: 535 | '@babel/core': '*' 536 | babel-plugin-macros: '*' 537 | react: '>= 16.8.0 || 17.x.x || ^18.0.0-0 || ^19.0.0-0' 538 | peerDependenciesMeta: 539 | '@babel/core': 540 | optional: true 541 | babel-plugin-macros: 542 | optional: true 543 | 544 | to-regex-range@5.0.1: 545 | resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==} 546 | engines: {node: '>=8.0'} 547 | 548 | tslib@2.8.1: 549 | resolution: {integrity: sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==} 550 | 551 | typescript@5.7.3: 552 | resolution: {integrity: sha512-84MVSjMEHP+FQRPy3pX9sTVV/INIex71s9TL2Gm5FG/WG1SqXeKyZ0k7/blY/4FdOzI12CBy1vGc4og/eus0fw==} 553 | engines: {node: '>=14.17'} 554 | hasBin: true 555 | 556 | undici-types@6.20.0: 557 | resolution: {integrity: sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg==} 558 | 559 | snapshots: 560 | 561 | '@emnapi/runtime@1.3.1': 562 | dependencies: 563 | tslib: 2.8.1 564 | optional: true 565 | 566 | '@img/sharp-darwin-arm64@0.33.5': 567 | optionalDependencies: 568 | '@img/sharp-libvips-darwin-arm64': 1.0.4 569 | optional: true 570 | 571 | '@img/sharp-darwin-x64@0.33.5': 572 | optionalDependencies: 573 | '@img/sharp-libvips-darwin-x64': 1.0.4 574 | optional: true 575 | 576 | '@img/sharp-libvips-darwin-arm64@1.0.4': 577 | optional: true 578 | 579 | '@img/sharp-libvips-darwin-x64@1.0.4': 580 | optional: true 581 | 582 | '@img/sharp-libvips-linux-arm64@1.0.4': 583 | optional: true 584 | 585 | '@img/sharp-libvips-linux-arm@1.0.5': 586 | optional: true 587 | 588 | '@img/sharp-libvips-linux-s390x@1.0.4': 589 | optional: true 590 | 591 | '@img/sharp-libvips-linux-x64@1.0.4': 592 | optional: true 593 | 594 | '@img/sharp-libvips-linuxmusl-arm64@1.0.4': 595 | optional: true 596 | 597 | '@img/sharp-libvips-linuxmusl-x64@1.0.4': 598 | optional: true 599 | 600 | '@img/sharp-linux-arm64@0.33.5': 601 | optionalDependencies: 602 | '@img/sharp-libvips-linux-arm64': 1.0.4 603 | optional: true 604 | 605 | '@img/sharp-linux-arm@0.33.5': 606 | optionalDependencies: 607 | '@img/sharp-libvips-linux-arm': 1.0.5 608 | optional: true 609 | 610 | '@img/sharp-linux-s390x@0.33.5': 611 | optionalDependencies: 612 | '@img/sharp-libvips-linux-s390x': 1.0.4 613 | optional: true 614 | 615 | '@img/sharp-linux-x64@0.33.5': 616 | optionalDependencies: 617 | '@img/sharp-libvips-linux-x64': 1.0.4 618 | optional: true 619 | 620 | '@img/sharp-linuxmusl-arm64@0.33.5': 621 | optionalDependencies: 622 | '@img/sharp-libvips-linuxmusl-arm64': 1.0.4 623 | optional: true 624 | 625 | '@img/sharp-linuxmusl-x64@0.33.5': 626 | optionalDependencies: 627 | '@img/sharp-libvips-linuxmusl-x64': 1.0.4 628 | optional: true 629 | 630 | '@img/sharp-wasm32@0.33.5': 631 | dependencies: 632 | '@emnapi/runtime': 1.3.1 633 | optional: true 634 | 635 | '@img/sharp-win32-ia32@0.33.5': 636 | optional: true 637 | 638 | '@img/sharp-win32-x64@0.33.5': 639 | optional: true 640 | 641 | '@next/env@15.3.0-canary.15': {} 642 | 643 | '@next/swc-darwin-arm64@15.3.0-canary.15': 644 | optional: true 645 | 646 | '@next/swc-darwin-x64@15.3.0-canary.15': 647 | optional: true 648 | 649 | '@next/swc-linux-arm64-gnu@15.3.0-canary.15': 650 | optional: true 651 | 652 | '@next/swc-linux-arm64-musl@15.3.0-canary.15': 653 | optional: true 654 | 655 | '@next/swc-linux-x64-gnu@15.3.0-canary.15': 656 | optional: true 657 | 658 | '@next/swc-linux-x64-musl@15.3.0-canary.15': 659 | optional: true 660 | 661 | '@next/swc-win32-arm64-msvc@15.3.0-canary.15': 662 | optional: true 663 | 664 | '@next/swc-win32-x64-msvc@15.3.0-canary.15': 665 | optional: true 666 | 667 | '@parcel/watcher-android-arm64@2.5.1': 668 | optional: true 669 | 670 | '@parcel/watcher-darwin-arm64@2.5.1': 671 | optional: true 672 | 673 | '@parcel/watcher-darwin-x64@2.5.1': 674 | optional: true 675 | 676 | '@parcel/watcher-freebsd-x64@2.5.1': 677 | optional: true 678 | 679 | '@parcel/watcher-linux-arm-glibc@2.5.1': 680 | optional: true 681 | 682 | '@parcel/watcher-linux-arm-musl@2.5.1': 683 | optional: true 684 | 685 | '@parcel/watcher-linux-arm64-glibc@2.5.1': 686 | optional: true 687 | 688 | '@parcel/watcher-linux-arm64-musl@2.5.1': 689 | optional: true 690 | 691 | '@parcel/watcher-linux-x64-glibc@2.5.1': 692 | optional: true 693 | 694 | '@parcel/watcher-linux-x64-musl@2.5.1': 695 | optional: true 696 | 697 | '@parcel/watcher-win32-arm64@2.5.1': 698 | optional: true 699 | 700 | '@parcel/watcher-win32-ia32@2.5.1': 701 | optional: true 702 | 703 | '@parcel/watcher-win32-x64@2.5.1': 704 | optional: true 705 | 706 | '@parcel/watcher@2.5.1': 707 | dependencies: 708 | detect-libc: 1.0.3 709 | is-glob: 4.0.3 710 | micromatch: 4.0.8 711 | node-addon-api: 7.1.1 712 | optionalDependencies: 713 | '@parcel/watcher-android-arm64': 2.5.1 714 | '@parcel/watcher-darwin-arm64': 2.5.1 715 | '@parcel/watcher-darwin-x64': 2.5.1 716 | '@parcel/watcher-freebsd-x64': 2.5.1 717 | '@parcel/watcher-linux-arm-glibc': 2.5.1 718 | '@parcel/watcher-linux-arm-musl': 2.5.1 719 | '@parcel/watcher-linux-arm64-glibc': 2.5.1 720 | '@parcel/watcher-linux-arm64-musl': 2.5.1 721 | '@parcel/watcher-linux-x64-glibc': 2.5.1 722 | '@parcel/watcher-linux-x64-musl': 2.5.1 723 | '@parcel/watcher-win32-arm64': 2.5.1 724 | '@parcel/watcher-win32-ia32': 2.5.1 725 | '@parcel/watcher-win32-x64': 2.5.1 726 | optional: true 727 | 728 | '@swc/counter@0.1.3': {} 729 | 730 | '@swc/helpers@0.5.15': 731 | dependencies: 732 | tslib: 2.8.1 733 | 734 | '@types/jsonwebtoken@9.0.8': 735 | dependencies: 736 | '@types/ms': 2.1.0 737 | '@types/node': 22.13.4 738 | 739 | '@types/ms@2.1.0': {} 740 | 741 | '@types/node@22.13.4': 742 | dependencies: 743 | undici-types: 6.20.0 744 | 745 | '@types/react@19.0.8': 746 | dependencies: 747 | csstype: 3.1.3 748 | 749 | braces@3.0.3: 750 | dependencies: 751 | fill-range: 7.1.1 752 | optional: true 753 | 754 | buffer-equal-constant-time@1.0.1: {} 755 | 756 | busboy@1.6.0: 757 | dependencies: 758 | streamsearch: 1.1.0 759 | 760 | caniuse-lite@1.0.30001699: {} 761 | 762 | chokidar@4.0.3: 763 | dependencies: 764 | readdirp: 4.1.1 765 | 766 | client-only@0.0.1: {} 767 | 768 | color-convert@2.0.1: 769 | dependencies: 770 | color-name: 1.1.4 771 | optional: true 772 | 773 | color-name@1.1.4: 774 | optional: true 775 | 776 | color-string@1.9.1: 777 | dependencies: 778 | color-name: 1.1.4 779 | simple-swizzle: 0.2.2 780 | optional: true 781 | 782 | color@4.2.3: 783 | dependencies: 784 | color-convert: 2.0.1 785 | color-string: 1.9.1 786 | optional: true 787 | 788 | csstype@3.1.3: {} 789 | 790 | detect-libc@1.0.3: 791 | optional: true 792 | 793 | detect-libc@2.0.3: 794 | optional: true 795 | 796 | ecdsa-sig-formatter@1.0.11: 797 | dependencies: 798 | safe-buffer: 5.2.1 799 | 800 | fill-range@7.1.1: 801 | dependencies: 802 | to-regex-range: 5.0.1 803 | optional: true 804 | 805 | highlight.js@11.11.1: {} 806 | 807 | immutable@5.0.3: {} 808 | 809 | is-arrayish@0.3.2: 810 | optional: true 811 | 812 | is-extglob@2.1.1: 813 | optional: true 814 | 815 | is-glob@4.0.3: 816 | dependencies: 817 | is-extglob: 2.1.1 818 | optional: true 819 | 820 | is-number@7.0.0: 821 | optional: true 822 | 823 | jsonwebtoken@9.0.2: 824 | dependencies: 825 | jws: 3.2.2 826 | lodash.includes: 4.3.0 827 | lodash.isboolean: 3.0.3 828 | lodash.isinteger: 4.0.4 829 | lodash.isnumber: 3.0.3 830 | lodash.isplainobject: 4.0.6 831 | lodash.isstring: 4.0.1 832 | lodash.once: 4.1.1 833 | ms: 2.1.3 834 | semver: 7.7.1 835 | 836 | jwa@1.4.1: 837 | dependencies: 838 | buffer-equal-constant-time: 1.0.1 839 | ecdsa-sig-formatter: 1.0.11 840 | safe-buffer: 5.2.1 841 | 842 | jws@3.2.2: 843 | dependencies: 844 | jwa: 1.4.1 845 | safe-buffer: 5.2.1 846 | 847 | lodash.includes@4.3.0: {} 848 | 849 | lodash.isboolean@3.0.3: {} 850 | 851 | lodash.isinteger@4.0.4: {} 852 | 853 | lodash.isnumber@3.0.3: {} 854 | 855 | lodash.isplainobject@4.0.6: {} 856 | 857 | lodash.isstring@4.0.1: {} 858 | 859 | lodash.once@4.1.1: {} 860 | 861 | marked-highlight@2.2.1(marked@15.0.7): 862 | dependencies: 863 | marked: 15.0.7 864 | 865 | marked@15.0.7: {} 866 | 867 | micromatch@4.0.8: 868 | dependencies: 869 | braces: 3.0.3 870 | picomatch: 2.3.1 871 | optional: true 872 | 873 | ms@2.1.3: {} 874 | 875 | nanoid@3.3.8: {} 876 | 877 | next@15.3.0-canary.15(react-dom@19.0.0(react@19.0.0))(react@19.0.0)(sass@1.85.0): 878 | dependencies: 879 | '@next/env': 15.3.0-canary.15 880 | '@swc/counter': 0.1.3 881 | '@swc/helpers': 0.5.15 882 | busboy: 1.6.0 883 | caniuse-lite: 1.0.30001699 884 | postcss: 8.4.31 885 | react: 19.0.0 886 | react-dom: 19.0.0(react@19.0.0) 887 | styled-jsx: 5.1.6(react@19.0.0) 888 | optionalDependencies: 889 | '@next/swc-darwin-arm64': 15.3.0-canary.15 890 | '@next/swc-darwin-x64': 15.3.0-canary.15 891 | '@next/swc-linux-arm64-gnu': 15.3.0-canary.15 892 | '@next/swc-linux-arm64-musl': 15.3.0-canary.15 893 | '@next/swc-linux-x64-gnu': 15.3.0-canary.15 894 | '@next/swc-linux-x64-musl': 15.3.0-canary.15 895 | '@next/swc-win32-arm64-msvc': 15.3.0-canary.15 896 | '@next/swc-win32-x64-msvc': 15.3.0-canary.15 897 | sass: 1.85.0 898 | sharp: 0.33.5 899 | transitivePeerDependencies: 900 | - '@babel/core' 901 | - babel-plugin-macros 902 | 903 | node-addon-api@7.1.1: 904 | optional: true 905 | 906 | picocolors@1.1.1: {} 907 | 908 | picomatch@2.3.1: 909 | optional: true 910 | 911 | postcss@8.4.31: 912 | dependencies: 913 | nanoid: 3.3.8 914 | picocolors: 1.1.1 915 | source-map-js: 1.2.1 916 | 917 | postcss@8.5.2: 918 | dependencies: 919 | nanoid: 3.3.8 920 | picocolors: 1.1.1 921 | source-map-js: 1.2.1 922 | 923 | react-dom@19.0.0(react@19.0.0): 924 | dependencies: 925 | react: 19.0.0 926 | scheduler: 0.25.0 927 | 928 | react@19.0.0: {} 929 | 930 | readdirp@4.1.1: {} 931 | 932 | safe-buffer@5.2.1: {} 933 | 934 | sass@1.85.0: 935 | dependencies: 936 | chokidar: 4.0.3 937 | immutable: 5.0.3 938 | source-map-js: 1.2.1 939 | optionalDependencies: 940 | '@parcel/watcher': 2.5.1 941 | 942 | scheduler@0.25.0: {} 943 | 944 | semver@7.7.1: {} 945 | 946 | server-only@0.0.1: {} 947 | 948 | sharp@0.33.5: 949 | dependencies: 950 | color: 4.2.3 951 | detect-libc: 2.0.3 952 | semver: 7.7.1 953 | optionalDependencies: 954 | '@img/sharp-darwin-arm64': 0.33.5 955 | '@img/sharp-darwin-x64': 0.33.5 956 | '@img/sharp-libvips-darwin-arm64': 1.0.4 957 | '@img/sharp-libvips-darwin-x64': 1.0.4 958 | '@img/sharp-libvips-linux-arm': 1.0.5 959 | '@img/sharp-libvips-linux-arm64': 1.0.4 960 | '@img/sharp-libvips-linux-s390x': 1.0.4 961 | '@img/sharp-libvips-linux-x64': 1.0.4 962 | '@img/sharp-libvips-linuxmusl-arm64': 1.0.4 963 | '@img/sharp-libvips-linuxmusl-x64': 1.0.4 964 | '@img/sharp-linux-arm': 0.33.5 965 | '@img/sharp-linux-arm64': 0.33.5 966 | '@img/sharp-linux-s390x': 0.33.5 967 | '@img/sharp-linux-x64': 0.33.5 968 | '@img/sharp-linuxmusl-arm64': 0.33.5 969 | '@img/sharp-linuxmusl-x64': 0.33.5 970 | '@img/sharp-wasm32': 0.33.5 971 | '@img/sharp-win32-ia32': 0.33.5 972 | '@img/sharp-win32-x64': 0.33.5 973 | optional: true 974 | 975 | simple-swizzle@0.2.2: 976 | dependencies: 977 | is-arrayish: 0.3.2 978 | optional: true 979 | 980 | source-map-js@1.2.1: {} 981 | 982 | streamsearch@1.1.0: {} 983 | 984 | styled-jsx@5.1.6(react@19.0.0): 985 | dependencies: 986 | client-only: 0.0.1 987 | react: 19.0.0 988 | 989 | to-regex-range@5.0.1: 990 | dependencies: 991 | is-number: 7.0.0 992 | optional: true 993 | 994 | tslib@2.8.1: {} 995 | 996 | typescript@5.7.3: {} 997 | 998 | undici-types@6.20.0: {} 999 | -------------------------------------------------------------------------------- /postcss.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | plugins: {}, 3 | }; 4 | -------------------------------------------------------------------------------- /styles/Home.module.scss: -------------------------------------------------------------------------------- 1 | .main { 2 | display: flex; 3 | flex-direction: column; 4 | padding: 25px; 5 | max-width: 1000px; 6 | margin: 0 auto; 7 | } 8 | 9 | .repo { 10 | display: flex; 11 | flex-direction: column; 12 | justify-content: space-between; 13 | margin-bottom: 20px; 14 | } 15 | 16 | .repo_title { 17 | font-size: 20px; 18 | 19 | a { 20 | color: #66e6f4; 21 | } 22 | 23 | a:hover { 24 | border-bottom: 1px dotted #66e6f4; 25 | } 26 | } 27 | 28 | .forks_stars { 29 | display: flex; 30 | align-items: center; 31 | margin-top: 16px; 32 | 33 | a:hover { 34 | color: #66e6f4; 35 | } 36 | } 37 | 38 | .issue_comments { 39 | color: #888; 40 | display: flex; 41 | align-items: center; 42 | margin-top: 16px; 43 | 44 | a:hover { 45 | color: #66e6f4; 46 | 47 | path { 48 | color: #66e6f4; 49 | } 50 | } 51 | } 52 | 53 | @media (min-width: 500px) { 54 | .repo { 55 | flex-direction: row; 56 | } 57 | 58 | .forks_stars { 59 | margin-top: 0; 60 | } 61 | 62 | .issue_comments { 63 | margin-top: 0; 64 | } 65 | } 66 | 67 | .explanation { 68 | color: #ccc; 69 | font-size: 14px; 70 | border: 1px dashed #666; 71 | padding: 10px 20px; 72 | margin-bottom: 40px; 73 | font-family: Menlo, Monaco, Consolas, 'Courier New', monospace; 74 | line-height: 1.5; 75 | background-color: #161b22; 76 | 77 | p { 78 | margin-bottom: 10px; 79 | 80 | a { 81 | color: #fff; 82 | border-bottom: 1px solid #fff; 83 | } 84 | 85 | em { 86 | color: #f4e866; 87 | font-weight: normal; 88 | font-style: normal; 89 | 90 | a { 91 | color: #f4e866; 92 | border-bottom-color: #f4e866; 93 | } 94 | } 95 | } 96 | } 97 | 98 | .explanation_notes { 99 | color: #999; 100 | margin-top: 5px; 101 | display: block; 102 | font-size: 12px; 103 | } 104 | 105 | .left { 106 | display: flex; 107 | } 108 | 109 | .right { 110 | display: flex; 111 | gap: 8px; 112 | color: #888; 113 | } 114 | 115 | .issues { 116 | border: 1px solid #30363d; 117 | width: 100%; 118 | color: #c8d1d9; 119 | font-size: 13px; 120 | border-radius: 6px; 121 | } 122 | 123 | .issue { 124 | padding: 15px 20px; 125 | border-bottom: 1px solid #21262d; 126 | display: flex; 127 | align-items: center; 128 | justify-content: space-between; 129 | 130 | &:hover { 131 | background-color: #151b22; 132 | } 133 | 134 | .issue_title:hover { 135 | color: #66e6f4; 136 | } 137 | } 138 | 139 | .issue_title { 140 | font-size: 15px; 141 | font-weight: 600; 142 | margin-bottom: 10px; 143 | } 144 | 145 | @mixin icon { 146 | fill: currentColor; 147 | display: inline-block; 148 | overflow: visible; 149 | vertical-align: text-bottom; 150 | } 151 | 152 | .github_icon { 153 | @include icon; 154 | margin-right: 5px; 155 | width: 20px; 156 | height: 20px; 157 | 158 | path { 159 | color: #eee; 160 | } 161 | } 162 | 163 | .issue_icon { 164 | @include icon; 165 | margin-right: 12px; 166 | margin-top: 2px; 167 | 168 | path { 169 | color: #66e6f4; 170 | } 171 | } 172 | 173 | .fork_icon { 174 | @include icon; 175 | margin-right: 5px; 176 | width: 15px; 177 | height: 15px; 178 | 179 | path { 180 | color: #eee; 181 | } 182 | } 183 | 184 | .star_icon { 185 | @include icon; 186 | margin-left: 16px; 187 | margin-right: 5px; 188 | width: 15px; 189 | height: 15px; 190 | 191 | path { 192 | color: #eee; 193 | } 194 | } 195 | 196 | .comment_icon { 197 | @include icon; 198 | margin-right: 5px; 199 | width: 16px; 200 | height: 16px; 201 | 202 | path { 203 | color: #888; 204 | } 205 | } 206 | 207 | .comment_count { 208 | color: #888; 209 | display: flex; 210 | align-items: center; 211 | margin-left: auto; 212 | } 213 | 214 | .details { 215 | summary { 216 | cursor: pointer; 217 | 218 | span { 219 | padding-left: 5px; 220 | } 221 | 222 | &:hover { 223 | color: #fff; 224 | } 225 | } 226 | 227 | p { 228 | padding: 10px 20px; 229 | margin: 0; 230 | 231 | code { 232 | background: #222; 233 | padding: 3px; 234 | } 235 | 236 | em { 237 | color: #eee; 238 | font-style: oblique; 239 | } 240 | } 241 | 242 | &[open] { 243 | summary { 244 | font-weight: bold; 245 | color: #fff; 246 | } 247 | 248 | background: #333; 249 | } 250 | } 251 | 252 | .rounded { 253 | border-radius: 50%; 254 | object-fit: cover; 255 | } 256 | 257 | .image { 258 | margin-right: 10px; 259 | margin-top: 4px; 260 | margin-bottom: auto; 261 | } 262 | 263 | .comments { 264 | width: 100%; 265 | color: #c8d1d9; 266 | font-size: 13px; 267 | border-radius: 6px; 268 | } 269 | 270 | .comment { 271 | padding: 15px 0px; 272 | display: flex; 273 | } 274 | 275 | .comment_div { 276 | border: 1px solid #21262d; 277 | border-radius: 5px; 278 | width: 100%; 279 | overflow: hidden; 280 | position: relative; 281 | 282 | &:hover { 283 | background-color: #151b22; 284 | } 285 | } 286 | 287 | .comment_timestamp { 288 | font-size: 14px; 289 | margin-bottom: 10px; 290 | background-color: #313f50; 291 | width: 100%; 292 | padding: 10px 18px; 293 | min-height: 2.2rem; 294 | } 295 | 296 | .comment_body { 297 | font-size: 15px; 298 | padding: 0 18px; 299 | min-height: 2.6rem; 300 | line-height: 1.5rem; 301 | } 302 | 303 | .issue_desc { 304 | height: 15px; 305 | } 306 | -------------------------------------------------------------------------------- /styles/globals.css: -------------------------------------------------------------------------------- 1 | html, 2 | body { 3 | padding: 0; 4 | margin: 0; 5 | font-family: -apple-system, BlinkMacSystemFont, Segoe UI, Roboto, Oxygen, 6 | Ubuntu, Cantarell, Fira Sans, Droid Sans, Helvetica Neue, sans-serif; 7 | color: #8b949e; 8 | background: #0d1117; 9 | scrollbar-gutter: stable; 10 | } 11 | 12 | a { 13 | color: inherit; 14 | text-decoration: none; 15 | } 16 | 17 | * { 18 | box-sizing: border-box; 19 | } 20 | 21 | samp, 22 | code { 23 | font-family: 'Dank Mono', 'Source Code Pro', 'Courier Prime Code', 24 | 'Liberation Mono', Consolas, Menlo, monospace; 25 | } 26 | 27 | samp { 28 | font-weight: bold; 29 | color: #333; 30 | } 31 | 32 | pre { 33 | box-sizing: border-box; 34 | overflow-x: auto; 35 | width: 100%; 36 | margin-top: 1.27rem; 37 | border-radius: 6px; 38 | line-height: 1.5; 39 | background-color: #000000; 40 | white-space: pre; 41 | -webkit-overflow-scrolling: touch; 42 | } 43 | 44 | pre > code { 45 | display: block; 46 | padding: 1.2rem !important; 47 | } 48 | 49 | :not(pre) > code { 50 | display: inline; 51 | white-space: pre-wrap; 52 | padding: 0.2em 0.4em; 53 | margin: 0; 54 | font-size: 85%; 55 | background-color: #6e768166; 56 | border-radius: 6px; 57 | } 58 | 59 | .hljs { 60 | color: #c9d1d9; 61 | background: #0d1117; 62 | } 63 | 64 | .hljs-doctag, 65 | .hljs-keyword, 66 | .hljs-meta .hljs-keyword, 67 | .hljs-template-tag, 68 | .hljs-template-variable, 69 | .hljs-type, 70 | .hljs-variable.language_ { 71 | color: #ff7b72; 72 | } 73 | 74 | .hljs-title, 75 | .hljs-title.class_, 76 | .hljs-title.class_.inherited__, 77 | .hljs-title.function_ { 78 | color: #d2a8ff; 79 | } 80 | 81 | .hljs-attr, 82 | .hljs-attribute, 83 | .hljs-literal, 84 | .hljs-meta, 85 | .hljs-number, 86 | .hljs-operator, 87 | .hljs-variable, 88 | .hljs-selector-attr, 89 | .hljs-selector-class, 90 | .hljs-selector-id { 91 | color: #79c0ff; 92 | } 93 | 94 | .hljs-regexp, 95 | .hljs-string, 96 | .hljs-meta .hljs-string { 97 | color: #a5d6ff; 98 | } 99 | 100 | .hljs-built_in, 101 | .hljs-symbol { 102 | color: #ffa657; 103 | } 104 | 105 | .hljs-comment, 106 | .hljs-code, 107 | .hljs-formula { 108 | color: #8b949e; 109 | } 110 | 111 | .hljs-name, 112 | .hljs-quote, 113 | .hljs-selector-tag, 114 | .hljs-selector-pseudo { 115 | color: #7ee787; 116 | } 117 | 118 | .hljs-subst { 119 | color: #c9d1d9; 120 | } 121 | 122 | .hljs-section { 123 | color: #1f6feb; 124 | font-weight: bold; 125 | } 126 | 127 | .hljs-bullet { 128 | color: #f2cc60; 129 | } 130 | 131 | .hljs-emphasis { 132 | color: #c9d1d9; 133 | font-style: italic; 134 | } 135 | 136 | .hljs-strong { 137 | color: #c9d1d9; 138 | font-weight: bold; 139 | } 140 | 141 | .hljs-addition { 142 | color: #aff5b4; 143 | background-color: #033a16; 144 | } 145 | 146 | .hljs-deletion { 147 | color: #ffdcd7; 148 | background-color: #67060c; 149 | } 150 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es5", 4 | "lib": [ 5 | "dom", 6 | "dom.iterable", 7 | "esnext" 8 | ], 9 | "allowJs": true, 10 | "skipLibCheck": true, 11 | "strict": false, 12 | "forceConsistentCasingInFileNames": true, 13 | "noEmit": true, 14 | "incremental": true, 15 | "esModuleInterop": true, 16 | "module": "esnext", 17 | "moduleResolution": "node", 18 | "resolveJsonModule": true, 19 | "isolatedModules": true, 20 | "jsx": "preserve", 21 | "plugins": [ 22 | { 23 | "name": "next" 24 | } 25 | ], 26 | "strictNullChecks": true 27 | }, 28 | "include": [ 29 | "next-env.d.ts", 30 | "**/*.ts", 31 | "**/*.tsx", 32 | "lib/github.ts", 33 | ".next/types/**/*.ts" 34 | ], 35 | "exclude": [ 36 | "node_modules" 37 | ] 38 | } 39 | --------------------------------------------------------------------------------