diff --git a/research-sentry/.env.example b/research-sentry/.env.example new file mode 100644 index 00000000..2039caea --- /dev/null +++ b/research-sentry/.env.example @@ -0,0 +1,5 @@ +# TinyFish API key — https://agent.tinyfish.ai/api-keys +TINYFISH_API_KEY=your-tinyfish-api-key + +# OpenAI API key (LLM + Whisper transcription) — https://platform.openai.com/api-keys +OPENAI_API_KEY=your-openai-api-key diff --git a/research-sentry/.env.local.example b/research-sentry/.env.local.example deleted file mode 100644 index e201a97e..00000000 --- a/research-sentry/.env.local.example +++ /dev/null @@ -1,7 +0,0 @@ -# Rename to .env.local and add your keys - -# TinyFish API key for web agent automation (https://tinyfish.ai) -TINYFISH_API_KEY=your-tinyfish-key-here - -# OpenAI API key for GPT-4o intent parsing and Whisper transcription -OPENAI_API_KEY=sk-your-key-here diff --git a/research-sentry/.gitignore b/research-sentry/.gitignore index 5f52bd74..73bdd33d 100644 --- a/research-sentry/.gitignore +++ b/research-sentry/.gitignore @@ -1,5 +1,7 @@ node_modules .next -.env*.local +.env +.env.local .vercel *.tsbuildinfo +next-env.d.ts diff --git a/research-sentry/README.md b/research-sentry/README.md index bdae958a..1a886ffa 100644 --- a/research-sentry/README.md +++ b/research-sentry/README.md @@ -1,176 +1,182 @@ # Research Sentry +**Live: https://cookbook-research-sentry.vercel.app/** -**A voice-first academic research co-pilot** that scans live portals (ArXiv, PubMed, Semantic Scholar, IEEE Xplore, Google Scholar, SSRN, CORE, DOAJ) to assemble verified paper metadata and summaries. It uses the **TinyFish Web Agent** to automate multi-step portal navigation and extract structured results in real time. +**Voice-first academic research co-pilot — AI agents scrape 8 live research portals in parallel and assemble verified paper metadata in real time.** -Live: https://cookbook-research-sentry.vercel.app/ +Speak or type a research query. Research Sentry parses your intent, dispatches one TinyFish browser agent per academic portal simultaneously, aggregates and deduplicates the results, and streams them back as each portal completes. Then ask follow-up questions, compare papers side-by-side, track citations, or export BibTeX. -Demo video: https://cookbook-research-sentry.vercel.app/ - ---- +## Architecture -## How It Works +``` +┌─────────────────────────────────────────────────────────────┐ +│ Browser (Client) │ +│ │ +│ VoiceRecorder / SearchInterface → ResultsGrid │ +│ ConversationInterface → PaperComparison → CitationTracker │ +│ TinyFishAgentTerminal (live agent log) │ +└──────────────────────────┬──────────────────────────────────┘ + │ + ┌────────────┼─────────────┐ + ▼ ▼ ▼ + /api/search/text /api/search/voice /api/compare + /api/summarize /api/conversation /api/citations/track + /api/export/bibtex + │ + ▼ +┌─────────────────────────────────────────────────────────────┐ +│ lib/tinyfish.ts │ +│ │ +│ runTinyFishAutomation(url, goal, stealth?) │ +│ Throws TinyFishError with typed codes: │ +│ MISSING_API_KEY | RUN_FAILED | TIMEOUT | │ +│ STREAM_ERROR | NO_RESULT │ +│ │ +│ client.agent.stream({ url, goal, browser_profile }) │ +│ onComplete → RunStatus.COMPLETED → return result │ +│ → RunStatus.FAILED → throw RUN_FAILED │ +└──────────────────────────┬──────────────────────────────────┘ + │ Promise.allSettled (x8 parallel) + ┌─────────┬───────┼────────┬─────────┐ + ▼ ▼ ▼ ▼ ▼ + ArXiv PubMed Semantic Google IEEE + Scholar Scholar Xplore + + SSRN + CORE + DOAJ + +Google Scholar + IEEE Xplore use browser_profile: 'stealth' +``` -1. **Voice / text input** -- speak or type your research query. -2. **GPT-4o parses intent** -- OpenAI extracts topic, keywords, and target sources from your query. -3. **TinyFish agents scrape 8 academic portals in parallel** -- each portal gets its own headless browser session via the TinyFish API. -4. **Results aggregated & deduplicated** -- papers from every source are merged, normalized, and ranked by citation count. -5. **Summarize, compare, export** -- ask follow-up questions, compare papers side-by-side, track citations, or export BibTeX. +### OpenAI usage ---- +``` +lib/intent-parser.ts → parse topic, keywords, sources from query +lib/summarizer.ts → summarize individual papers +lib/comparator.ts → structured methodology/results comparison +lib/conversation.ts → conversational follow-up answers +lib/citation-tracker.ts → citation velocity and impact prediction +lib/whisper.ts → speech-to-text via OpenAI's Whisper endpoint +``` ## Key Features -- **Voice input** -- record a question and Whisper transcribes it into a search query. -- **Multi-source search** -- scrapes ArXiv, PubMed, Semantic Scholar, Google Scholar, IEEE Xplore, SSRN, CORE, and DOAJ simultaneously. -- **Paper comparison** -- select papers and get a structured methodology/results comparison via GPT-4o. -- **Citation tracking** -- monitor a paper's citation velocity and predicted impact. -- **BibTeX export** -- download selected papers as a `.bib` file. -- **Conversational follow-ups** -- ask the AI assistant questions about your results. - ---- - -## TinyFish API Usage - -The core integration lives in `lib/tinyfish.ts`. Here is the SSE call that drives every search: - -```ts -const res = await fetch("https://agent.tinyfish.ai/v1/automation/run-sse", { - method: "POST", - headers: { - "X-API-Key": process.env.TINYFISH_API_KEY!, - "Content-Type": "application/json", - }, - body: JSON.stringify({ - url, - goal, - browser_profile: stealth ? "stealth" : "lite", - }), -}); - -// Parse the SSE stream -const reader = res.body!.getReader(); -const decoder = new TextDecoder(); -let buffer = ""; -let result = null; - -while (true) { - const { done, value } = await reader.read(); - if (done) break; - buffer += decoder.decode(value, { stream: true }); - const lines = buffer.split("\n"); - buffer = lines.pop() || ""; - for (const line of lines) { - if (line.startsWith("data: ")) { - const event = JSON.parse(line.slice(6)); - if (event.type === "COMPLETE") result = event.resultJson; - } - } -} -``` +- **Voice input** — record a question, OpenAI Whisper transcribes it into a search query +- **Multi-source search** — 8 portals scraped simultaneously: ArXiv, PubMed, Semantic Scholar, Google Scholar, IEEE Xplore, SSRN, CORE, DOAJ +- **Paper comparison** — structured methodology/results comparison across selected papers +- **Citation tracking** — monitor citation velocity and predicted impact +- **BibTeX export** — download selected papers as a `.bib` file +- **Conversational follow-ups** — ask the AI assistant questions about your results +- **Live agent terminal** — watch each TinyFish agent's progress in real time ---- +## Scraping Flow -## Tech Stack +1. User speaks or types a research query +2. OpenAI (`intent-parser.ts`) extracts topic, keywords, and target sources +3. One TinyFish agent fires per portal — all in parallel via `Promise.allSettled` +4. Each agent navigates the portal's live DOM with a tight, focused goal prompt +5. Results stream back to the aggregator as each agent completes +6. `aggregator.ts` deduplicates and ranks by citation count +7. Results appear in the UI as portals finish — no waiting for the slowest one -| Layer | Technology | -|-------|-----------| -| Framework | Next.js 14 (App Router) | -| Web scraping | TinyFish API (SSE) | -| LLM | OpenAI GPT-4o | -| Speech-to-text | OpenAI Whisper | -| Styling | Tailwind CSS | -| Icons | Lucide React | +## Setup ---- +### Prerequisites -## Setup +- Node.js 18+ +- TinyFish API key +- OpenAI API key + +### Environment Variables ```bash -# 1. Install dependencies -npm install +cp .env.example .env.local +``` -# 2. Create your env file -cp .env.local.example .env.local +Then fill in: -# 3. Add your API keys to .env.local -# TINYFISH_API_KEY -- get one at https://tinyfish.ai -# OPENAI_API_KEY -- get one at https://platform.openai.com +```env +# TinyFish (required) — https://agent.tinyfish.ai/api-keys +TINYFISH_API_KEY=your-tinyfish-key-here -# 4. Start the dev server -npm run dev +# OpenAI (required) — https://platform.openai.com/api-keys +OPENAI_API_KEY=your-openai-api-key ``` -Open http://localhost:3000 to use the app. +### Install & Run + +```bash +npm install +npm run dev +``` ---- +Open http://localhost:3000 -## Folder Structure +## Project Structure ``` research-sentry/ ├── app/ -│ ├── api/ -│ │ ├── citations/track/route.ts # Citation velocity analysis -│ │ ├── compare/route.ts # Paper comparison endpoint -│ │ ├── conversation/route.ts # Conversational follow-ups -│ │ ├── emails/extract/route.ts # Author email extraction -│ │ ├── export/bibtex/route.ts # BibTeX export -│ │ ├── health/route.ts # Health check -│ │ ├── search/text/route.ts # Text search endpoint -│ │ ├── search/voice/route.ts # Voice search endpoint -│ │ └── summarize/route.ts # Paper summarization -│ ├── globals.css │ ├── layout.tsx -│ └── page.tsx # Main UI +│ ├── page.tsx # Main UI +│ ├── globals.css +│ └── api/ +│ ├── citations/track/route.ts # Citation velocity analysis +│ ├── compare/route.ts # Paper comparison +│ ├── conversation/route.ts # Conversational follow-ups +│ ├── emails/extract/route.ts # Author email extraction +│ ├── export/bibtex/route.ts # BibTeX export +│ ├── health/route.ts # Health check +│ ├── search/text/route.ts # Text search +│ ├── search/voice/route.ts # Voice search +│ └── summarize/route.ts # Paper summarization ├── components/ +│ ├── SearchInterface.tsx +│ ├── VoiceRecorder.tsx +│ ├── ResultsGrid.tsx +│ ├── PaperCard.tsx +│ ├── PaperComparison.tsx +│ ├── PaperSummary.tsx │ ├── CitationTracker.tsx │ ├── ConversationInterface.tsx │ ├── CoPilotMode.tsx +│ ├── WorkflowSelector.tsx +│ ├── TinyFishAgentTerminal.tsx # Live agent log display │ ├── ErrorMessage.tsx -│ ├── LoadingSpinner.tsx -│ ├── PaperCard.tsx -│ ├── PaperComparison.tsx -│ ├── PaperSummary.tsx -│ ├── ResultsGrid.tsx -│ ├── SearchInterface.tsx -│ ├── TinyFishAgentTerminal.tsx # Live agent log display -│ ├── VoiceRecorder.tsx -│ └── WorkflowSelector.tsx +│ └── LoadingSpinner.tsx ├── hooks/ │ └── useVoiceCommands.ts ├── lib/ -│ ├── aggregator.ts # Deduplication & ranking +│ ├── tinyfish.ts # TinyFish agent client (typed errors) +│ ├── intent-parser.ts # OpenAI — query intent parsing +│ ├── summarizer.ts # OpenAI — paper summarization +│ ├── comparator.ts # OpenAI — paper comparison +│ ├── conversation.ts # OpenAI — conversational follow-ups +│ ├── citation-tracker.ts # OpenAI — citation velocity +│ ├── whisper.ts # OpenAI Whisper — speech-to-text +│ ├── aggregator.ts # Deduplication & ranking +│ ├── search.ts # Multi-source search orchestration +│ ├── workflows.ts │ ├── audio-utils.ts -│ ├── citation-tracker.ts -│ ├── comparator.ts -│ ├── conversation.ts │ ├── email-utils.ts -│ ├── intent-parser.ts # GPT-4o query parsing │ ├── pdf-utils.ts -│ ├── search.ts # Multi-source search engine -│ ├── summarizer.ts -│ ├── tinyfish.ts # TinyFish SSE client -│ ├── types.ts -│ └── workflows.ts -└── .env.local.example +│ └── types.ts +├── .env.example +└── package.json ``` ---- +## Constraint Checklist -## Architecture +| Constraint | Status | +|---|---| +| External database used? | NO (pure in-memory) | +| Scraping parallel? | YES (`Promise.allSettled` across 8 portals) | +| Bot-protected sites handled? | YES (Google Scholar + IEEE use `browser_profile: 'stealth'`) | +| SDK errors surfaced? | YES (typed `TinyFishError` with code — no silent `null` returns) | +| Voice input? | YES (OpenAI Whisper transcription) | +| BibTeX export? | YES | -```mermaid -graph TD - User((User)) -->|Voice/Text| UI[Search Interface] - UI -->|Intent| Parser[Intent Parser GPT-4o] - Parser -->|Plan| Engine[Search Engine] - Engine -->|Dispatch| Agent1[TinyFish Agent: ArXiv] - Engine -->|Dispatch| Agent2[TinyFish Agent: PubMed] - Engine -->|Dispatch| Agent3[TinyFish Agent: Scholar] - Agent1 -->|Scraping| Web[Live Web DOM] - Agent2 -->|Scraping| Web - Agent3 -->|Scraping| Web - Web -->|Result| Aggregator[Synthesis & Deduplication] - Aggregator -->|JSON Payload| UI - UI -->|Visuals| Terminal[Live Log Terminal] -``` +## Tech Stack + +- **Framework:** Next.js (App Router), TypeScript, Tailwind CSS +- **Browser Agents:** TinyFish SDK (`client.agent.stream`) +- **LLM:** OpenAI (gpt-4o-mini) + Speech-to-text: OpenAI Whisper +- **Icons:** Lucide React +- **Deployment:** Vercel diff --git a/research-sentry/app/api/citations/track/route.ts b/research-sentry/app/api/citations/track/route.ts index 4586046e..75580335 100644 --- a/research-sentry/app/api/citations/track/route.ts +++ b/research-sentry/app/api/citations/track/route.ts @@ -1,5 +1,5 @@ import { NextRequest, NextResponse } from 'next/server'; -import { analyzeCitationTrend } from '@/lib/citation-tracker'; +import { analyzeCitationNetwork } from '@/lib/citation-tracker'; export async function POST(req: NextRequest) { try { @@ -9,7 +9,7 @@ export async function POST(req: NextRequest) { return NextResponse.json({ error: 'Paper data required' }, { status: 400 }); } - const trackedData = await analyzeCitationTrend(paper); + const trackedData = await analyzeCitationNetwork(paper); // In a real app, we would save this to a database here diff --git a/research-sentry/app/api/conversation/route.ts b/research-sentry/app/api/conversation/route.ts index f5dbe506..67bdb9df 100644 --- a/research-sentry/app/api/conversation/route.ts +++ b/research-sentry/app/api/conversation/route.ts @@ -1,5 +1,5 @@ import { NextRequest, NextResponse } from 'next/server'; -import { generateConversationResponse } from '@/lib/conversation'; +import { continueConversation, Message } from '@/lib/conversation'; export const maxDuration = 60; @@ -11,9 +11,16 @@ export async function POST(req: NextRequest) { return NextResponse.json({ error: 'Invalid history format' }, { status: 400 }); } - const response = await generateConversationResponse(history, context); + // Build messages array — prepend context as system message if provided + const messages: Message[] = []; + if (context) { + messages.push({ role: 'system', content: `Research context: ${JSON.stringify(context)}` }); + } + messages.push(...(history as Message[])); + + const response = await continueConversation(messages); - return NextResponse.json(response); + return NextResponse.json({ response }); } catch (error) { console.error('Conversation API Error:', error); return NextResponse.json({ error: 'Failed to generate response' }, { status: 500 }); diff --git a/research-sentry/app/api/search/text/route.ts b/research-sentry/app/api/search/text/route.ts index 2a34e21f..f5ea0bfa 100644 --- a/research-sentry/app/api/search/text/route.ts +++ b/research-sentry/app/api/search/text/route.ts @@ -1,13 +1,76 @@ -import { NextRequest, NextResponse } from 'next/server'; +import { NextRequest } from 'next/server'; import { parseSearchIntent } from '@/lib/intent-parser'; -import { searchResearchPapers } from '@/lib/search'; +import { scrapeSourceStreaming } from '@/lib/search'; +import type { SearchCriteria, SourceType } from '@/lib/types'; export const maxDuration = 300; +const DEFAULT_SOURCES: SourceType[] = ['arxiv', 'pubmed', 'semantic_scholar']; + export async function POST(req: NextRequest) { const { query, sources } = await req.json(); - const criteria = await parseSearchIntent(query); - if (sources) criteria.sources = sources; - const results = await searchResearchPapers(criteria); - return NextResponse.json(results); + + // Parse intent — returns { keywords, topics, authors, yearFrom, yearTo, intent } + const parsed = await parseSearchIntent(query); + + // Build a proper SearchCriteria — topic is the original query or first keyword/topic + const topic: string = + query || + (Array.isArray(parsed.topics) && parsed.topics[0]) || + (Array.isArray(parsed.keywords) && parsed.keywords[0]) || + query; + + const criteria: SearchCriteria = { + topic, + keywords: Array.isArray(parsed.keywords) ? parsed.keywords : [query], + sources: (sources as SourceType[]) || DEFAULT_SOURCES, + maxResults: 20, + fullPrompt: query, + }; + + const encoder = new TextEncoder(); + const sseData = (payload: unknown) => encoder.encode(`data: ${JSON.stringify(payload)}\n\n`); + + const stream = new ReadableStream({ + async start(controller) { + let closed = false; + const safeEnqueue = (payload: unknown) => { + if (!closed) controller.enqueue(sseData(payload)); + }; + const safeClose = () => { + if (!closed) { closed = true; controller.close(); } + }; + + safeEnqueue({ type: 'SEARCH_STARTED', total: criteria.sources.length, query: topic }); + + const perSourceTimeoutMs = 40_000; + let totalFound = 0; + + const tasks = criteria.sources.map((source: SourceType) => + scrapeSourceStreaming(source, criteria, perSourceTimeoutMs) + .then(papers => { + totalFound += papers.length; + safeEnqueue({ type: 'SOURCE_COMPLETE', source, papers, count: papers.length }); + }) + .catch(err => { + console.error(`[Search/${source}] Failed:`, err?.message); + safeEnqueue({ type: 'SOURCE_ERROR', source, error: err?.message }); + }) + ); + + await Promise.allSettled(tasks); + + safeEnqueue({ type: 'SEARCH_COMPLETE', totalFound }); + safeClose(); + }, + }); + + return new Response(stream, { + headers: { + 'Content-Type': 'text/event-stream', + 'Cache-Control': 'no-cache, no-transform', + 'Connection': 'keep-alive', + 'X-Accel-Buffering': 'no', + }, + }); } diff --git a/research-sentry/app/api/search/voice/route.ts b/research-sentry/app/api/search/voice/route.ts index cee48ebd..ce8dff2a 100644 --- a/research-sentry/app/api/search/voice/route.ts +++ b/research-sentry/app/api/search/voice/route.ts @@ -2,9 +2,12 @@ import { NextRequest, NextResponse } from 'next/server'; import { transcribeAudio } from '@/lib/whisper'; import { parseSearchIntent } from '@/lib/intent-parser'; import { searchResearchPapers } from '@/lib/search'; +import { SearchCriteria, SourceType } from '@/lib/types'; export const maxDuration = 300; +const DEFAULT_SOURCES: SourceType[] = ['arxiv', 'pubmed', 'semantic_scholar', 'google_scholar', 'ieee']; + export async function POST(req: NextRequest) { const form = await req.formData(); const audio = form.get('audio'); @@ -13,7 +16,21 @@ export async function POST(req: NextRequest) { } const buffer = Buffer.from(await audio.arrayBuffer()); const transcript = await transcribeAudio(buffer); - const criteria = await parseSearchIntent(transcript); + const intent = await parseSearchIntent(transcript); + + // Map intent parser output to SearchCriteria + const criteria: SearchCriteria = { + topic: intent.topics?.[0] || intent.keywords?.[0] || transcript, + keywords: intent.keywords || [], + sources: DEFAULT_SOURCES, + maxResults: 20, + fullPrompt: transcript, + dateRange: (intent.yearFrom || intent.yearTo) ? { + from: intent.yearFrom ? String(intent.yearFrom) : undefined, + to: intent.yearTo ? String(intent.yearTo) : undefined, + } : undefined, + }; + const results = await searchResearchPapers(criteria); return NextResponse.json({ ...results, transcript }); } diff --git a/research-sentry/app/page.tsx b/research-sentry/app/page.tsx index 9f2227e7..3c41bb64 100644 --- a/research-sentry/app/page.tsx +++ b/research-sentry/app/page.tsx @@ -45,10 +45,14 @@ export default function Home() { setSearchSources(sources); setAgentLogs([]); setAgentComplete(false); + setResults(null); addAgentLog('TinyFish Agent initialized. Connecting to browser instance...', 'info'); addAgentLog(`Targeting [${sources.join(', ')}] for discovery.`, 'info'); + // Accumulate papers across sources as they stream in + const allPapers: import('@/lib/types').ResearchPaper[] = []; + try { const response = await fetch('/api/search/text', { method: 'POST', @@ -56,15 +60,45 @@ export default function Home() { body: JSON.stringify({ query, sources }), }); - if (!response.ok) { - addAgentLog(`Search request failed: ${response.statusText}`, 'error'); - throw new Error(`Search failed: ${response.statusText}`); + if (!response.ok) throw new Error(`Search failed: ${response.statusText}`); + if (!response.body) throw new Error('No response body'); + + const reader = response.body.getReader(); + const decoder = new TextDecoder(); + let buffer = ''; + + while (true) { + const { done, value } = await reader.read(); + if (done) break; + + buffer += decoder.decode(value, { stream: true }); + const lines = buffer.split('\n'); + buffer = lines.pop() ?? ''; + + for (const line of lines) { + if (!line.startsWith('data: ')) continue; + let event: Record; + try { event = JSON.parse(line.slice(6)); } catch { continue; } + + if (event.type === 'SEARCH_STARTED') { + addAgentLog(`Searching ${event.total} sources in parallel...`, 'info'); + } else if (event.type === 'SOURCE_COMPLETE') { + const papers = (event.papers as import('@/lib/types').ResearchPaper[]) ?? []; + addAgentLog(`${event.source}: found ${papers.length} papers.`, papers.length > 0 ? 'success' : 'info'); + if (papers.length > 0) { + allPapers.push(...papers); + // Show results immediately as each source completes + setResults({ query, papers: [...allPapers], totalFound: allPapers.length }); + } + } else if (event.type === 'SOURCE_ERROR') { + addAgentLog(`${event.source}: failed — ${event.error}`, 'error'); + } else if (event.type === 'SEARCH_COMPLETE') { + addAgentLog(`Discovery complete. Found ${allPapers.length} papers total.`, 'success'); + setAgentComplete(true); + setLoading(false); + } + } } - - const data = await response.json(); - addAgentLog(`Discovery complete. Found ${data.totalFound ?? 0} papers.`, 'success'); - setAgentComplete(true); - setResults(data); } catch (err) { addAgentLog(err instanceof Error ? err.message : 'An error occurred', 'error'); setAgentComplete(true); @@ -183,7 +217,7 @@ export default function Home() { Your AI Research Co-Pilot

- Search academic papers using your voice or text. Powered by OpenAI, GPT-4, and TinyFish Web Agent. + Search academic papers using your voice or text. Powered by OpenAI and TinyFish Web Agent.

{/* Features badges */} @@ -289,7 +323,7 @@ export default function Home() { /> - {/* Loading State */} + {/* Loading State — terminal visible while searching */} {loading && (
@@ -303,10 +337,29 @@ export default function Home() { -
- -

Compiling findings from 8 research nodes

-
+ {/* Show results as they arrive while other agents run */} + {results && results.papers.length > 0 ? ( +
+
+
+

+ Live results — {results.papers.length} papers found so far, more agents still running… +

+
+ setTrackingPaperId(id)} + /> +
+ ) : ( +
+ +

Compiling findings from research nodes…

+
+ )}
)} @@ -350,7 +403,7 @@ export default function Home() {
)} - {/* Results */} + {/* Final Results — shown after all agents complete */} {!loading && results && (
(null); const [authorsCopied, setAuthorsCopied] = useState(false); + const [authorModalOpen, setAuthorModalOpen] = useState(false); const formatDate = (dateStr: string) => { try { @@ -34,6 +35,8 @@ export default function PaperCard({ paper, onSelect, selected, onTrack }: PaperC }; const extractAuthors = async () => { + setAuthorModalOpen(true); + if (authors.length > 0) return; // already extracted, just reopen setIsExtractingAuthors(true); setAuthorError(null); setAuthorsCopied(false); @@ -209,75 +212,105 @@ export default function PaperCard({ paper, onSelect, selected, onTrack }: PaperC
- {/* Author information (PDF extraction) */} + {/* Author information — compact trigger + modal */} {(paper.pdfUrl || paper.url) && ( -
-
-
+ <> +
+
-

+

Author information

-

- Extract author names and emails from the paper PDF -

+

Extract author names and emails

+ +
+
+ + {/* Author Modal */} + {authorModalOpen && ( +
{ if (e.target === e.currentTarget) setAuthorModalOpen(false); }} + > +
+ {/* Header */} +
+
+ + Author Information +
+
+ {authors.length > 0 && ( + + )} + +
+
+ + {/* Paper title */} +
+

{paper.title}

+
+ + {/* Content */} +
+ {isExtractingAuthors && ( +
+
+ Extracting author information… +
+ )} + {authorError &&

{authorError}

} + {authors.length > 0 && ( +
+ {authors.map((author, idx) => { + const name = [author.firstName, author.lastName].filter(Boolean).join(' ').trim(); + return ( +
+ {name &&
{name}
} + {author.email} +
+ ); + })} +
+ )} + {!isExtractingAuthors && !authorError && authors.length === 0 && ( +

No author information found.

+ )} +
-
- {authors.length > 0 && ( + {/* Footer */} +
- )} - +
- - {authorError &&
{authorError}
} - - {authors.length > 0 ? ( -
- {authors.map((author, idx) => { - const name = [author.firstName, author.lastName].filter(Boolean).join(' ').trim(); - return ( -
- {name && ( -
- {name} -
- )} - - {author.email} - -
- ); - })} -
- ) : ( - !isExtractingAuthors && - !authorError &&
No author information found.
- )} -
-
+ )} + )}
diff --git a/research-sentry/components/PaperComparison.tsx b/research-sentry/components/PaperComparison.tsx index 4fe1a9a7..a910b619 100644 --- a/research-sentry/components/PaperComparison.tsx +++ b/research-sentry/components/PaperComparison.tsx @@ -103,7 +103,7 @@ export default function PaperComparison({ papers, onClose }: PaperComparisonProp - {comparison.points.map((point, i) => ( + {comparison.points?.map((point: { metric: string; papers: Record; insight: string }, i: number) => ( {point.metric} diff --git a/research-sentry/components/PaperSummary.tsx b/research-sentry/components/PaperSummary.tsx index 9288f11c..7236e8ec 100644 --- a/research-sentry/components/PaperSummary.tsx +++ b/research-sentry/components/PaperSummary.tsx @@ -1,7 +1,7 @@ 'use client'; import { useState } from 'react'; -import { Copy, Loader2, Sparkles } from 'lucide-react'; +import { Copy, Loader2, Sparkles, X } from 'lucide-react'; import { ResearchPaper } from '@/lib/types'; type SummaryLength = 'short' | 'medium' | 'long'; @@ -17,8 +17,11 @@ export default function PaperSummary({ paper, length = 'medium', title = 'AI Sum const [isLoading, setIsLoading] = useState(false); const [error, setError] = useState(null); const [copied, setCopied] = useState(false); + const [open, setOpen] = useState(false); const generate = async () => { + setOpen(true); + if (summary) return; // already generated, just reopen setIsLoading(true); setError(null); setCopied(false); @@ -29,10 +32,8 @@ export default function PaperSummary({ paper, length = 'medium', title = 'AI Sum headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ paper, length }), }); - if (!res.ok) throw new Error('Failed to generate summary'); const data = (await res.json()) as { summary?: string }; - const s = (data.summary || '').trim(); setSummary(s); if (!s) setError('No summary returned'); @@ -48,54 +49,95 @@ export default function PaperSummary({ paper, length = 'medium', title = 'AI Sum await navigator.clipboard.writeText(summary); setCopied(true); window.setTimeout(() => setCopied(false), 1200); - } catch { - // ignore - } + } catch { } }; return ( -
-
+ <> + {/* Trigger button — sits inline in the card */} +
-

+

{title}

-

- Brief, written summary (copyable) -

-
- -
- {summary && ( - - )} - +

Brief, written summary (copyable)

+
- {error &&
{error}
} + {/* Modal popup */} + {open && ( +
{ if (e.target === e.currentTarget) setOpen(false); }} + > +
+ {/* Header */} +
+
+ + {title} +
+
+ {summary && ( + + )} + +
+
+ + {/* Paper title */} +
+

{paper.title}

+
- {summary && ( -
- {summary} + {/* Content */} +
+ {isLoading && ( +
+ + Generating summary… +
+ )} + {error &&

{error}

} + {summary && ( +

+ {summary} +

+ )} +
+ + {/* Footer */} +
+ +
+
)} -
+ ); } - diff --git a/research-sentry/image.png b/research-sentry/image.png deleted file mode 100644 index 87edb79a..00000000 Binary files a/research-sentry/image.png and /dev/null differ diff --git a/research-sentry/lib/citation-tracker.ts b/research-sentry/lib/citation-tracker.ts index 72c6cb4b..9d5f3de0 100644 --- a/research-sentry/lib/citation-tracker.ts +++ b/research-sentry/lib/citation-tracker.ts @@ -1,76 +1,72 @@ import OpenAI from 'openai'; import { ResearchPaper } from './types'; -// Mock database for tracked papers in this demo -// In a real app, this would be a database model +function getOpenAI() { + return new OpenAI({ apiKey: process.env.OPENAI_API_KEY! }); +} + export interface TrackedPaper { - id: string; // Paper ID - paperTitle: string; - originalCitationCount: number; + paperId: string; + title: string; currentCitationCount: number; - velocity: number; // Citations per month - lastChecked: number; - trend: 'up' | 'stable' | 'down'; - impactProjections: { - nextYear: number; - fiveYear: number; - }; + trend: 'up' | 'down' | 'stable'; + velocity: number; + impactProjections: { nextYear: number; fiveYear: number }; + topicScore: number; + lastUpdated: string; } -const getOpenAI = () => { - const apiKey = process.env.OPENAI_API_KEY; - if (!apiKey) { - throw new Error('OPENAI_API_KEY is not configured'); - } - return new OpenAI({ apiKey }); -}; +export async function analyzeCitationNetwork(paper: ResearchPaper): Promise { + const currentCitations = paper.citations ?? 0; + const prompt = `Analyze this academic paper and predict its citation trajectory. -export async function analyzeCitationTrend(paper: ResearchPaper): Promise { - const openai = getOpenAI(); - // Simulating citation analysis with AI since we don't have historical data access in this demo - const prompt = `Analyze the potential citation impact of this research paper: - Title: "${paper.title}" - Current Citations: ${paper.citations || 0} - Published: ${paper.publishedDate} - Source: ${paper.source} - - Estimate the "Citation Velocity" (citations/month) and predict impact. - Return JSON: - { - "velocity": number, - "trend": "up" | "stable" | "down", - "impactProjections": { "nextYear": number, "fiveYear": number } - } - `; +Paper: "${paper.title}" +Authors: ${Array.isArray(paper.authors) ? paper.authors.join(', ') : paper.authors} +Year: ${paper.publishedDate ? new Date(paper.publishedDate).getFullYear() : 'unknown'} +Current citations: ${currentCitations} +Source: ${paper.source} - const response = await openai.chat.completions.create({ - model: 'gpt-4o', - messages: [{ role: 'user', content: prompt }], - response_format: { type: 'json_object' }, - }); +Return ONLY valid JSON, no markdown: +{ "trend": "up", "velocity": 5, "nextYear": 150, "fiveYear": 400, "topicScore": 75 }`; - const choice = response.choices?.[0]; - if (!choice) { - throw new Error('OpenAI returned no choices'); - } - if (choice.finish_reason === 'length') { - throw new Error('OpenAI response was truncated'); - } - let analysis: any; try { - analysis = JSON.parse(choice.message.content ?? '{}'); - } catch (error) { - throw new Error('OpenAI returned invalid JSON'); + const response = await getOpenAI().chat.completions.create({ + model: 'gpt-4o-mini', + messages: [{ role: 'user', content: prompt }], + max_tokens: 200, + }); + const content = response.choices[0]?.message?.content ?? '{}'; + let parsed: Record; + try { + parsed = JSON.parse(content.replace(/```json\n?|```/g, '').trim()); + } catch { + parsed = {}; + } + const trend = (['up', 'down', 'stable'].includes(parsed.trend as string) + ? parsed.trend + : currentCitations > 50 ? 'up' : 'stable') as 'up' | 'down' | 'stable'; + const velocity = Number(parsed.velocity ?? Math.max(1, Math.round(currentCitations / 12))); + const nextYear = Number(parsed.nextYear ?? currentCitations + velocity * 12); + const fiveYear = Number(parsed.fiveYear ?? currentCitations + velocity * 60); + return { + paperId: paper.id ?? paper.url ?? paper.title, + title: paper.title, + currentCitationCount: currentCitations, + trend, velocity, + impactProjections: { nextYear, fiveYear }, + topicScore: Number(parsed.topicScore ?? 60), + lastUpdated: new Date().toISOString(), + }; + } catch { + const velocity = Math.max(1, Math.round(currentCitations / 12)); + return { + paperId: paper.id ?? paper.title, + title: paper.title, + currentCitationCount: currentCitations, + trend: 'stable', velocity, + impactProjections: { nextYear: currentCitations + velocity * 12, fiveYear: currentCitations + velocity * 60 }, + topicScore: 60, + lastUpdated: new Date().toISOString(), + }; } - - return { - id: paper.id, - paperTitle: paper.title, - originalCitationCount: paper.citations || 0, - currentCitationCount: paper.citations || 0, // In real app, this updates - lastChecked: Date.now(), - velocity: analysis.velocity, - trend: analysis.trend, - impactProjections: analysis.impactProjections - }; } diff --git a/research-sentry/lib/comparator.ts b/research-sentry/lib/comparator.ts index ecceffdc..d54394d3 100644 --- a/research-sentry/lib/comparator.ts +++ b/research-sentry/lib/comparator.ts @@ -5,40 +5,37 @@ function getOpenAI() { return new OpenAI({ apiKey: process.env.OPENAI_API_KEY! }); } -export interface ComparisonPoint { - metric: string; - papers: { [paperId: string]: string }; - insight: string; -} - export interface ComparisonResult { - points: ComparisonPoint[]; - summary: string; + similarities: string[]; + differences: string[]; + methodology: { paper: number; method: string }[]; + strengths: { paper: number; strengths: string[] }[]; + weaknesses: { paper: number; weaknesses: string[] }[]; + recommendation: string; + points?: { metric: string; papers: Record; insight: string }[]; + summary?: string; } export async function comparePapers(papers: ResearchPaper[]): Promise { - const prompt = `Compare the following ${papers.length} research papers. - - Papers: - ${papers.map((p, i) => `[ID: ${p.id}] Title: ${p.title}\nAbstract: ${p.abstract}\n`).join('\n')} - - Generate a structured comparison focussing on: - 1. Methodology - 2. Dataset/Sample Size - 3. Key Results/Accuracy - 4. Limitations - - Return a JSON object with: - - points: Array of objects { metric: string, papers: { [id]: string value }, insight: string } - - summary: string (High-level synthesis of differences) - `; + const paperSummaries = papers.map((p, i) => { + const year = p.publishedDate ? new Date(p.publishedDate).getFullYear() : 'N/A'; + return `Paper ${i + 1}: "${p.title}" by ${p.authors.join(', ')} (${year})\nAbstract: ${p.abstract?.substring(0, 400) || 'N/A'}`; + }).join('\n\n'); - const response = await getOpenAI().chat.completions.create({ - model: 'gpt-4o', - messages: [{ role: 'user', content: prompt }], - response_format: { type: 'json_object' }, - }); + const prompt = `Compare these ${papers.length} research papers:\n\n${paperSummaries}\n\nReturn a JSON object with:\n- similarities: string[]\n- differences: string[]\n- methodology: {paper: number, method: string}[]\n- strengths: {paper: number, strengths: string[]}[]\n- weaknesses: {paper: number, weaknesses: string[]}[]\n- recommendation: string\n\nReturn ONLY valid JSON, no markdown.`; - const content = JSON.parse(response.choices[0].message.content!); - return content; + try { + const response = await getOpenAI().chat.completions.create({ + model: 'gpt-4o-mini', + messages: [ + { role: 'system', content: 'You are a research paper comparison expert. Compare papers objectively and return structured JSON only.' }, + { role: 'user', content: prompt } + ], + max_tokens: 1000, + }); + const content = response.choices[0]?.message?.content ?? '{}'; + return JSON.parse(content.replace(/```json\n?|```/g, '').trim()); + } catch { + return { similarities: [], differences: [], methodology: [], strengths: [], weaknesses: [], recommendation: 'Unable to compare papers.' }; + } } diff --git a/research-sentry/lib/conversation.ts b/research-sentry/lib/conversation.ts index 9ab8bac5..18ddc900 100644 --- a/research-sentry/lib/conversation.ts +++ b/research-sentry/lib/conversation.ts @@ -1,55 +1,21 @@ import OpenAI from 'openai'; -import { Message, ResearchPaper } from './types'; function getOpenAI() { return new OpenAI({ apiKey: process.env.OPENAI_API_KEY! }); } -export async function generateConversationResponse( - history: Message[], - context?: { papers?: ResearchPaper[], query?: string } -): Promise<{ content: string, relatedPapers?: ResearchPaper[] }> { - - const systemPrompt = `You are a sophisticated Research Assistant AI. - Your goal is to help users explore academic literature, understand paper details, and find connections. - - Context: - ${context?.query ? `Current Search Query: "${context.query}"` : ''} - ${context?.papers ? `Found Papers: ${context.papers.map(p => `- ${p.title} (${p.publishedDate})`).join('\n')}` : ''} - - Guidelines: - 1. Be concise but insightful. - 2. If discussing specific papers, cite them clearly. - 3. Can answer questions about methodology, results, and implications based on standard academic knowledge. - 4. If the user asks for a comparison, structure it clearly. - 5. Provide a conversational, professional tone. - `; +export interface Message { role: 'user' | 'assistant' | 'system'; content: string; } - const messages = [ - { role: 'system', content: systemPrompt }, - ...history.map(m => ({ role: m.role as 'user' | 'assistant' | 'system', content: m.content })) - ]; - - let response; - try { - response = await getOpenAI().chat.completions.create({ - model: 'gpt-4o', - messages: messages as any, - temperature: 0.7, - max_tokens: 500, - }); - } catch (err) { - console.error('OpenAI conversation error', err); - return { - content: "I couldn't generate a response.", - relatedPapers: context?.papers, - }; - } - - const content = response?.choices?.[0]?.message?.content; - return { - content: content || "I couldn't generate a response.", - // in a real implementation, we might extract new paper references here - relatedPapers: context?.papers // maintain context - }; +export async function continueConversation(messages: Message[]): Promise { + try { + const response = await getOpenAI().chat.completions.create({ + model: 'gpt-4o-mini', + messages, + max_tokens: 1000, + }); + return response.choices[0]?.message?.content ?? ''; + } catch (err) { + console.error('OpenAI conversation error', err); + return ''; + } } diff --git a/research-sentry/lib/intent-parser.ts b/research-sentry/lib/intent-parser.ts index facfea5f..33c0f615 100644 --- a/research-sentry/lib/intent-parser.ts +++ b/research-sentry/lib/intent-parser.ts @@ -1,31 +1,22 @@ import OpenAI from 'openai'; -import { SearchCriteria } from './types'; function getOpenAI() { return new OpenAI({ apiKey: process.env.OPENAI_API_KEY! }); } -export async function parseSearchIntent(query: string): Promise { - const res = await getOpenAI().chat.completions.create({ - model: 'gpt-4o', +export async function parseSearchIntent(query: string): Promise { + const response = await getOpenAI().chat.completions.create({ + model: 'gpt-4o-mini', messages: [ - { - role: 'system', - content: `Parse research query into JSON: {searchKeywords, sources[], refinedAgenticGoal}. - - searchKeywords: Short, high-quality search terms for API search (e.g., "LLM hallucinations"). - - refinedAgenticGoal: Detailed instructions for an AI agent (e.g., "Look for papers on X and focus on their methodology sections"). - Allowed sources: 'arxiv', 'pubmed', 'semantic_scholar', 'google_scholar', 'ieee', 'ssrn', 'core', 'doaj'.` - }, - { role: 'user', content: query } + { role: 'system', content: 'You are a research search intent parser. Extract structured search parameters from natural language queries and return JSON only.' }, + { role: 'user', content: `Parse this research search query into structured parameters:\n"${query}"\n\nReturn JSON with:\n- keywords: string[]\n- authors: string[]\n- yearFrom: number | null\n- yearTo: number | null\n- topics: string[]\n- intent: "find_papers" | "compare" | "summarize" | "cite"\n\nReturn ONLY valid JSON, no markdown.` } ], - response_format: { type: 'json_object' }, + max_tokens: 300, }); - const parsed = JSON.parse(res.choices[0].message.content!); - return { - topic: parsed.searchKeywords || query, - keywords: [], - sources: parsed.sources || ['arxiv', 'semantic_scholar'], - maxResults: 20, - fullPrompt: parsed.refinedAgenticGoal || query - }; + const content = response.choices[0]?.message?.content ?? '{}'; + try { + return JSON.parse(content.replace(/```json\n?|```/g, '').trim()); + } catch { + return { keywords: [query], authors: [], yearFrom: null, yearTo: null, topics: [], intent: 'find_papers' }; + } } diff --git a/research-sentry/lib/search.ts b/research-sentry/lib/search.ts index d9a2e3da..3d832b21 100644 --- a/research-sentry/lib/search.ts +++ b/research-sentry/lib/search.ts @@ -2,67 +2,26 @@ import { SearchCriteria, SearchResult, SourceType, ResearchPaper } from './types import { aggregateAndDeduplicate } from './aggregator'; import { runTinyFishAutomation } from './tinyfish'; -/** - * HYBRID SEARCH ENGINE - * Primary: TinyFish Web Agent (Real-time, Deep Scraping) - * Fallback: Direct API calls (Reliability) - */ +// ── JSON parsing ────────────────────────────────────────────────────────────── -// ============================================ -// UTILITIES (Robustness layer) -// ============================================ - -/** - * Hyper-robust JSON parser that handles markdown blocks and recursive scanning - */ -function parseTinyFishResponse(rawResponse: any): any[] { - // If it's already an array, just return it - if (Array.isArray(rawResponse)) return rawResponse; - - // If it's a string, it might be stringified JSON or markdown - if (typeof rawResponse === 'string') { +function parseTinyFishResponse(raw: any): any[] { + if (Array.isArray(raw)) return raw; + if (typeof raw === 'string') { try { - // Remove markdown code blocks if present - const cleanJson = rawResponse.replace(/```json\n?|```/g, '').trim(); - const parsed = JSON.parse(cleanJson); + const parsed = JSON.parse(raw.replace(/```json\n?|```/g, '').trim()); return findPapersArray(parsed); - } catch (e) { - console.error('[TinyFish-Parser] Failed to parse stringified response:', e); - return []; - } + } catch { return []; } } - - // If it's an object, find the array within it - if (rawResponse && typeof rawResponse === 'object') { - return findPapersArray(rawResponse); - } - + if (raw && typeof raw === 'object') return findPapersArray(raw); return []; } -/** - * Deep-scans an object for any array containing paper-like objects - */ function findPapersArray(obj: any): any[] { if (Array.isArray(obj)) return obj; if (!obj || typeof obj !== 'object') return []; - - // Check common keys first for speed - const fastKeys = ['papers', 'results', 'data', 'articles', 'items', 'result']; - for (const key of fastKeys) { + for (const key of ['papers', 'results', 'data', 'articles', 'items', 'result']) { if (Array.isArray(obj[key])) return obj[key]; - - // Sometimes the key contains stringified JSON - if (typeof obj[key] === 'string' && (obj[key].includes('[') || obj[key].includes('{'))) { - try { - const inner = JSON.parse(obj[key].replace(/```json\n?|```/g, '').trim()); - const innerArray = findPapersArray(inner); - if (innerArray.length > 0) return innerArray; - } catch (e) { } - } } - - // Recursive search for any array for (const key in obj) { if (Array.isArray(obj[key])) return obj[key]; if (typeof obj[key] === 'object') { @@ -73,29 +32,18 @@ function findPapersArray(obj: any): any[] { return []; } -// ============================================ -// FALLBACKS (Reliability layer) -// ============================================ +// ── Fallbacks ───────────────────────────────────────────────────────────────── async function fallbackArxiv(topic: string): Promise { try { - const query = encodeURIComponent(topic); - const res = await fetch(`https://export.arxiv.org/api/query?search_query=all:${query}&start=0&max_results=10`); + const res = await fetch(`https://export.arxiv.org/api/query?search_query=all:${encodeURIComponent(topic)}&start=0&max_results=10`); const xml = await res.text(); - const entries = xml.split('').slice(1); - return entries.map(entry => { - const get = (tag: string) => { - const m = entry.match(new RegExp(`<${tag}[^>]*>([\\s\\S]*?)`)); - return m ? m[1].trim().replace(/\s+/g, ' ') : ''; - }; + return xml.split('').slice(1).map(entry => { + const get = (tag: string) => { const m = entry.match(new RegExp(`<${tag}[^>]*>([\\s\\S]*?)`)); return m ? m[1].trim().replace(/\s+/g, ' ') : ''; }; const id = get('id').split('/abs/').pop() || ''; - return { - id, title: get('title'), authors: (entry.match(/([^<]+)<\/name>/g) || []).map(a => a.replace(/<\/?name>/g, '')), - abstract: get('summary'), publishedDate: get('published').split('T')[0], - source: 'arxiv' as SourceType, url: `https://arxiv.org/abs/${id}`, pdfUrl: `https://arxiv.org/pdf/${id}.pdf`, citations: 0 - }; + return { id, title: get('title'), authors: (entry.match(/([^<]+)<\/name>/g) || []).map(a => a.replace(/<\/?name>/g, '')), abstract: get('summary'), publishedDate: get('published').split('T')[0], source: 'arxiv' as SourceType, url: `https://arxiv.org/abs/${id}`, pdfUrl: `https://arxiv.org/pdf/${id}.pdf`, citations: 0 }; }); - } catch (e) { return []; } + } catch { return []; } } async function fallbackSemanticScholar(topic: string): Promise { @@ -103,84 +51,59 @@ async function fallbackSemanticScholar(topic: string): Promise const res = await fetch(`https://api.semanticscholar.org/graph/v1/paper/search?query=${encodeURIComponent(topic)}&limit=10&fields=paperId,title,abstract,authors,year,citationCount,openAccessPdf`); if (!res.ok) return []; const data = await res.json(); - return (data.data || []).map((p: any) => ({ - id: p.paperId, title: p.title || 'Untitled', authors: p.authors?.map((a: any) => a.name) || ['Unknown'], - abstract: p.abstract || 'No abstract', publishedDate: p.year ? `${p.year}-01-01` : new Date().toISOString().split('T')[0], - source: 'semantic_scholar' as SourceType, - url: `https://semanticscholar.org/paper/${p.paperId}`, - pdfUrl: p.openAccessPdf?.url, - citations: p.citationCount || 0 - })); - } catch (e) { return []; } + return (data.data || []).map((p: any) => ({ id: p.paperId, title: p.title || 'Untitled', authors: p.authors?.map((a: any) => a.name) || ['Unknown'], abstract: p.abstract || 'No abstract', publishedDate: p.year ? `${p.year}-01-01` : new Date().toISOString().split('T')[0], source: 'semantic_scholar' as SourceType, url: `https://semanticscholar.org/paper/${p.paperId}`, pdfUrl: p.openAccessPdf?.url, citations: p.citationCount || 0 })); + } catch { return []; } } -// ============================================ -// CORE TINYFISH ENGINE -// ============================================ +// ── Tight goal prompts ──────────────────────────────────────────────────────── -async function scrapeWithTinyFish( - url: string, - goal: string, - source: SourceType, - stealth = false, - timeoutMs?: number -): Promise { - const rawResult = await runTinyFishAutomation(url, goal, stealth, timeoutMs ? { timeoutMs } : undefined); +const GOALS: Record string> = { + arxiv: (t) => `Go to https://arxiv.org/search/?query=${encodeURIComponent(t)}&searchtype=all — you are already on the results page. DO NOT navigate elsewhere. Extract the first 5 papers visible on screen RIGHT NOW. For each paper return: title, authors (array), abstract, arxivId, publishedDate, url, pdfUrl. Return ONLY a JSON array. No explanations.`, - let result = parseTinyFishResponse(rawResult); + pubmed: (t) => `Go to https://pubmed.ncbi.nlm.nih.gov/?term=${encodeURIComponent(t)} — you are on the results page. DO NOT click any links or navigate away. Extract the first 5 articles visible. For each: title, authors (array), abstract, pmid, url. Return ONLY a JSON array. No explanations.`, - if (result.length === 0) { - console.warn(`[TinyFish] ${source} return 0 papers. RAW STRUCTURE:`, JSON.stringify(rawResult).slice(0, 300)); - return []; - } + semantic_scholar: (t) => `Go to https://api.semanticscholar.org/graph/v1/paper/search?query=${encodeURIComponent(t)}&limit=5&fields=paperId,title,abstract,authors,year,citationCount,openAccessPdf — this is a JSON API. Parse the response and return the papers array as-is. Return ONLY valid JSON. No explanations.`, - console.log(`[TinyFish] ${source} found ${result.length} papers via web automation`); + google_scholar: (t) => `Go to https://scholar.google.com/scholar?q=${encodeURIComponent(t)} — you are on the results page. DO NOT click links. Extract the first 5 results visible: title, authors (array), snippet (as abstract), citations count, link (url). Return ONLY a JSON array. No explanations.`, - return result - .filter((p: any) => p && typeof p === 'object') - .map((p: any) => { - // Case-insensitive key lookup helper - const getV = (keys: string[]) => { - const lowerKeys = keys.map(k => k.toLowerCase()); - for (const actualKey in p) { - if (lowerKeys.includes(actualKey.toLowerCase())) return p[actualKey]; - } - return null; - }; + ieee: (t) => `Go to https://ieeexplore.ieee.org/search/searchresult.jsp?queryText=${encodeURIComponent(t)} — you are on the results page. DO NOT navigate away. Extract the first 5 papers: title, authors (array), abstract, doi, url. Return ONLY a JSON array. No explanations.`, + + ssrn: (t) => `Go to https://www.ssrn.com/index.cfm/en/ssrn-search-results/?query=${encodeURIComponent(t)} — you are on the results page. DO NOT navigate away. Extract the first 5 papers: title, authors (array), abstract, url. Return ONLY a JSON array. No explanations.`, + + core: (t) => `Go to https://core.ac.uk/search?q=${encodeURIComponent(t)} — you are on the results page. DO NOT navigate away. Extract the first 5 results: title, authors (array), abstract, url. Return ONLY a JSON array. No explanations.`, + doaj: (t) => `Go to https://doaj.org/search/articles?source=%7B%22query%22%3A%7B%22query_string%22%3A%7B%22query%22%3A%22${encodeURIComponent(t)}%22%7D%7D%7D — you are on the results page. DO NOT navigate away. Extract the first 5 articles: title, authors (array), abstract, url. Return ONLY a JSON array. No explanations.`, +}; + +// ── Core scraper ────────────────────────────────────────────────────────────── + +async function scrapeWithTinyFish(url: string, goal: string, source: SourceType, stealth = false, timeoutMs?: number): Promise { + const rawResult = await runTinyFishAutomation(url, goal, stealth, timeoutMs ? { timeoutMs } : undefined); + const result = parseTinyFishResponse(rawResult); + if (result.length === 0) return []; + + return result.filter((p: any) => p && typeof p === 'object').map((p: any) => { + const getV = (keys: string[]) => { for (const k of keys) { for (const ak in p) { if (ak.toLowerCase() === k.toLowerCase()) return p[ak]; } } return null; }; const paperId = getV(['paperId', 'id', 'paper_id']); const arxivId = getV(['arxivId', 'arxiv_id', 'arxiv']); const pmid = getV(['pmid', 'pubmed_id']); const doi = getV(['doi']); - const id = paperId || arxivId || pmid || doi || `${source}-${Date.now()}-${Math.random()}`; - // Synthesize URLs if missing but ID is present - let url = getV(['url', 'link', 'href', 'paperUrl', 'paperLink']) || '#'; + let paperUrl = getV(['url', 'link', 'href', 'paperUrl', 'paperLink']) || '#'; let pdfUrl = getV(['pdfUrl', 'pdfLink', 'pdf', 'fullText', 'pdf_url']); - if (url === '#' || !url) { - if (source === 'arxiv' && arxivId) url = `https://arxiv.org/abs/${arxivId}`; - else if (source === 'pubmed' && pmid) url = `https://pubmed.ncbi.nlm.nih.gov/${pmid}/`; - else if (source === 'semantic_scholar' && paperId) url = `https://www.semanticscholar.org/paper/${paperId}`; - else if (source === 'google_scholar' && p.title) url = `https://scholar.google.com/scholar?q=${encodeURIComponent(p.title)}`; + if (paperUrl === '#' || !paperUrl) { + if (source === 'arxiv' && arxivId) paperUrl = `https://arxiv.org/abs/${arxivId}`; + else if (source === 'pubmed' && pmid) paperUrl = `https://pubmed.ncbi.nlm.nih.gov/${pmid}/`; + else if (source === 'semantic_scholar' && paperId) paperUrl = `https://www.semanticscholar.org/paper/${paperId}`; + else if (p.title) paperUrl = `https://scholar.google.com/scholar?q=${encodeURIComponent(p.title)}`; } - - if (!pdfUrl) { - if (source === 'arxiv' && arxivId) pdfUrl = `https://arxiv.org/pdf/${arxivId}.pdf`; + if (!pdfUrl && source === 'arxiv' && arxivId) pdfUrl = `https://arxiv.org/pdf/${arxivId}.pdf`; + if (source === 'arxiv' && typeof pdfUrl === 'string' && pdfUrl.includes('arxiv.org/abs/')) { + const idPart = pdfUrl.split('arxiv.org/abs/')[1]?.split(/[?#]/)[0]; + if (idPart) pdfUrl = `https://arxiv.org/pdf/${idPart}.pdf`; } - // Normalize arXiv pdf URLs (TinyFish often returns without ".pdf") - if (source === 'arxiv' && typeof pdfUrl === 'string') { - if (pdfUrl.includes('arxiv.org/abs/')) { - const idPart = pdfUrl.split('arxiv.org/abs/')[1]?.split(/[?#]/)[0]; - if (idPart) pdfUrl = `https://arxiv.org/pdf/${idPart}.pdf`; - } else if (pdfUrl.includes('arxiv.org/pdf/') && !pdfUrl.endsWith('.pdf')) { - pdfUrl = `${pdfUrl.split(/[?#]/)[0]}.pdf`; - } - } - - // Trace logging for debugging - console.log(`[Link-Trace] ${source}:${p.title?.substring(0, 15)} | Link: ${url !== '#'} | PDF: ${!!pdfUrl}`); return { id, @@ -188,110 +111,51 @@ async function scrapeWithTinyFish( authors: Array.isArray(p.authors) ? p.authors : (p.authors ? [p.authors] : ['Unknown']), abstract: p.abstract || p.snippet || p.summary || 'No abstract available', publishedDate: p.publishedDate || p.publicationDate || p.date || (p.year ? `${p.year}-01-01` : new Date().toISOString().split('T')[0]), - source: source, - url, + source, + url: paperUrl, pdfUrl: pdfUrl || undefined, citations: p.citations || p.citationCount || p.downloads || 0, - doi: doi + doi, }; - }); + }); } -// Scraper Wrappers +// ── Source scrapers ─────────────────────────────────────────────────────────── -async function scrapeArxiv(criteria: SearchCriteria, timeoutMs?: number): Promise { - const goal = `Search ArXiv for "${criteria.topic}". Extract top 5 papers. For each paper, MUST extract: title, authors array, abstract, publishedDate, arxivId, url, and pdfUrl. Return JSON array. ${criteria.fullPrompt ? `Instruction: ${criteria.fullPrompt}` : ''}`; - const tinyFishResults = await scrapeWithTinyFish('https://arxiv.org/search', goal, 'arxiv', false, timeoutMs); - if (tinyFishResults.length > 0) return tinyFishResults; - console.log(`[TinyFish-Search] ArXiv zero results. Triggering fallback API...`); - return fallbackArxiv(criteria.topic); -} +export async function scrapeSourceStreaming(source: string, criteria: SearchCriteria, timeoutMs?: number): Promise { + const s = source.toLowerCase().replace(/[\s_]+/g, ''); + const topic = criteria.topic; -async function scrapePubmed(criteria: SearchCriteria, timeoutMs?: number): Promise { - const goal = `Search PubMed for "${criteria.topic}". Extract top 5 papers. MUST extract: title, authors array, abstract, pmid, and link (url). Return JSON array.`; - const tinyFishResults = await scrapeWithTinyFish('https://pubmed.ncbi.nlm.nih.gov/', goal, 'pubmed', false, timeoutMs); - if (tinyFishResults.length > 0) return tinyFishResults; - return fallbackSemanticScholar(`${criteria.topic} pubmed`); -} + // Map source key to goal function + const goalKey = s === 'semanticscholar' ? 'semantic_scholar' : s; + const goalFn = GOALS[goalKey]; + if (!goalFn) return []; -async function scrapeSemanticScholar(criteria: SearchCriteria, timeoutMs?: number): Promise { - const goal = `Search Semantic Scholar for "${criteria.topic}". Extract top 5 papers. MUST extract: title, authors array, abstract, year, paperId, url, and pdfUrl. Return JSON array. ${criteria.fullPrompt ? `Instruction: ${criteria.fullPrompt}` : ''}`; - const tinyFishResults = await scrapeWithTinyFish('https://www.semanticscholar.org/', goal, 'semantic_scholar', false, timeoutMs); - if (tinyFishResults.length > 0) return tinyFishResults; - return fallbackSemanticScholar(criteria.topic); -} + const goal = goalFn(topic); + const stealth = s === 'googlescholar' || s === 'ieee'; -async function scrapeGoogleScholar(criteria: SearchCriteria, timeoutMs?: number): Promise { - const searchUrl = `https://scholar.google.com/scholar?q=${encodeURIComponent(criteria.topic)}`; - const goal = `Extract papers from this Google Scholar page: title, authors, snippet (abstract), citations count, link. Return JSON array.`; - return scrapeWithTinyFish(searchUrl, goal, 'google_scholar', true, timeoutMs); -} + // Extract the real target URL from the goal so the agent starts there directly + const urlMatch = goal.match(/Go to (https?:\/\/[^\s—]+)/); + const startUrl = urlMatch ? urlMatch[1] : 'https://www.google.com'; -async function scrapeIEEE(criteria: SearchCriteria, timeoutMs?: number): Promise { - const goal = `Search IEEE Xplore for "${criteria.topic}". Extract first 5 papers: title, authors, abstract, doi. Return JSON array.`; - const tinyFishResults = await scrapeWithTinyFish('https://ieeexplore.ieee.org/', goal, 'ieee', true, timeoutMs); + const tinyFishResults = await scrapeWithTinyFish(startUrl, goal, s as SourceType, stealth, timeoutMs); if (tinyFishResults.length > 0) return tinyFishResults; - return fallbackSemanticScholar(`${criteria.topic} ieee`); -} - -async function scrapeSSRN(criteria: SearchCriteria, timeoutMs?: number): Promise { - const goal = `Search SSRN for "${criteria.topic}". Extract top 5 papers: title, authors, abstract. Return JSON.`; - return scrapeWithTinyFish('https://www.ssrn.com/', goal, 'ssrn', false, timeoutMs); -} -async function scrapeCORE(criteria: SearchCriteria, timeoutMs?: number): Promise { - const goal = `Search CORE for "${criteria.topic}". Extract 5 results with title, abstract, link. Return JSON.`; - return scrapeWithTinyFish('https://core.ac.uk/', goal, 'core', false, timeoutMs); -} - -async function scrapeDOAJ(criteria: SearchCriteria, timeoutMs?: number): Promise { - const goal = `Search DOAJ for "${criteria.topic}". Extract 5 articles with title, abstract, link. Return JSON.`; - return scrapeWithTinyFish('https://doaj.org/', goal, 'doaj', false, timeoutMs); + // Fallback + if (s === 'arxiv') return fallbackArxiv(topic); + if (s === 'pubmed' || s === 'semanticscholar' || s === 'ieee') return fallbackSemanticScholar(topic); + return []; } -async function scrapeSource(source: string, criteria: SearchCriteria, timeoutMs?: number): Promise { - const s = source.toLowerCase().replace(/[\s_]+/g, ''); - switch (s) { - case 'arxiv': return scrapeArxiv(criteria, timeoutMs); - case 'pubmed': return scrapePubmed(criteria, timeoutMs); - case 'semanticscholar': return scrapeSemanticScholar(criteria, timeoutMs); - case 'googlescholar': return scrapeGoogleScholar(criteria, timeoutMs); - case 'ieee': case 'ieeexplore': return scrapeIEEE(criteria, timeoutMs); - case 'ssrn': return scrapeSSRN(criteria, timeoutMs); - case 'core': return scrapeCORE(criteria, timeoutMs); - case 'doaj': return scrapeDOAJ(criteria, timeoutMs); - default: return []; - } -} +// ── Legacy sync export (for other routes that call searchResearchPapers) ────── export async function searchResearchPapers(criteria: SearchCriteria): Promise { - console.log(`\n=================================================`); - console.log(`DISCOVERY: "${criteria.topic}"`); - console.log(`SOURCES: ${criteria.sources.join(', ')}`); - console.log(`=================================================`); - - // Prevent one slow portal from forcing a platform timeout. - // Each source gets a time budget for TinyFish automation. const perSourceTimeoutMs = 40_000; - const results = await Promise.all( - criteria.sources.map((s) => - scrapeSource(s, criteria, perSourceTimeoutMs).catch((e: any) => { - console.error(`[Search/${s}] Failed:`, e?.message); - return []; - }) + criteria.sources.map((s: string) => + scrapeSourceStreaming(s, criteria, perSourceTimeoutMs).catch(() => [] as ResearchPaper[]) ) ); - const papers = aggregateAndDeduplicate(results); - - console.log(`\n=================================================`); - console.log(`TOTAL DISCOVERY YIELD: ${papers.length}`); - console.log(`=================================================\n`); - - return { - query: criteria.topic, - papers: papers.slice(0, criteria.maxResults), - totalFound: papers.length, - }; + return { query: criteria.topic, papers: papers.slice(0, criteria.maxResults), totalFound: papers.length }; } diff --git a/research-sentry/lib/summarizer.ts b/research-sentry/lib/summarizer.ts index fcd16bf9..ea4cae1e 100644 --- a/research-sentry/lib/summarizer.ts +++ b/research-sentry/lib/summarizer.ts @@ -7,36 +7,27 @@ function getOpenAI() { export async function generatePaperSummary(paper: ResearchPaper, length: 'short' | 'medium' | 'long' = 'medium') { const words = length === 'short' ? 100 : length === 'medium' ? 300 : 600; - const prompt = `Write a brief, practical written summary of this academic paper for a researcher. - - Paper Title: ${paper.title} - Authors: ${paper.authors.join(', ')} - Abstract: ${paper.abstract} - - Output format (plain text, no markdown): - - 1–2 short paragraphs max - - Then 3–5 bullet points (use "-" bullets) covering: problem, method, main results, and "why it matters" - - Avoid filler and "spoken" phrasing. Do NOT start with "This paper titled..." - - Target length: ~${words} words. - Be concrete and professional. - `; - const response = await getOpenAI().chat.completions.create({ - model: 'gpt-4o', - messages: [{ role: 'user', content: prompt }], - }); +Paper Title: ${paper.title} +Authors: ${paper.authors.join(', ')} +Abstract: ${paper.abstract} - return response.choices[0].message.content!; -} +Output format (plain text, no markdown): +- 1-2 short paragraphs max +- Then 3-5 bullet points (use "-" bullets) covering: problem, method, main results, and "why it matters" +- Avoid filler. Do NOT start with "This paper titled..." -export async function synthesizeSpeech(text: string) { - const mp3 = await getOpenAI().audio.speech.create({ - model: 'tts-1', - voice: 'alloy', - input: text, - }); +Target length: ~${words} words.`; - return Buffer.from(await mp3.arrayBuffer()); + try { + const response = await getOpenAI().chat.completions.create({ + model: 'gpt-4o-mini', + messages: [{ role: 'user', content: prompt }], + max_tokens: 800, + }); + return response.choices[0]?.message?.content ?? ''; + } catch { + return ''; + } } diff --git a/research-sentry/lib/tinyfish.ts b/research-sentry/lib/tinyfish.ts index 86c3f2b4..c62ad46e 100644 --- a/research-sentry/lib/tinyfish.ts +++ b/research-sentry/lib/tinyfish.ts @@ -1,107 +1,100 @@ -// TinyFish Web Agent Client -// Endpoint: https://agent.tinyfish.ai/v1/automation/run-sse +// TinyFish Web Agent Client — using @tiny-fish/sdk +import { TinyFish, EventType, RunStatus } from '@tiny-fish/sdk'; + +export class TinyFishError extends Error { + constructor( + message: string, + public readonly code: 'MISSING_API_KEY' | 'RUN_FAILED' | 'TIMEOUT' | 'STREAM_ERROR' | 'NO_RESULT' + ) { + super(message); + this.name = 'TinyFishError'; + } +} export async function runTinyFishAutomation( url: string, goal: string, stealth = false, options?: { timeoutMs?: number } -): Promise { +): Promise { const apiKey = process.env.TINYFISH_API_KEY; if (!apiKey) { - console.error('[TinyFish] TINYFISH_API_KEY not set in environment'); - return null; + throw new TinyFishError('TINYFISH_API_KEY is not set in environment', 'MISSING_API_KEY'); } - console.log(`[TinyFish] Starting automation...`); - console.log(`[TinyFish] URL: ${url}`); + console.log(`[TinyFish] Starting automation for: ${url}`); console.log(`[TinyFish] Goal: ${goal.substring(0, 80)}...`); const controller = new AbortController(); const timeoutMs = options?.timeoutMs; - const timeout = timeoutMs ? setTimeout(() => controller.abort(), timeoutMs) : null; + const timeout = timeoutMs + ? setTimeout(() => controller.abort(), timeoutMs) + : null; try { - const res = await fetch('https://agent.tinyfish.ai/v1/automation/run-sse', { - method: 'POST', - headers: { - 'X-API-Key': apiKey, - 'Content-Type': 'application/json', - }, - body: JSON.stringify({ + const client = new TinyFish({ apiKey }); + let result: unknown = null; + let runFailed = false; + let failureMessage = 'Agent run failed'; + + const stream = await client.agent.stream( + { url, goal, - browser_profile: stealth ? 'stealth' : 'lite' - }), - signal: controller.signal, - }); - - if (!res.ok) { - const errorText = await res.text(); - console.error(`[TinyFish] HTTP Error ${res.status}: ${errorText}`); - return null; + browser_profile: stealth ? 'stealth' : 'lite', + }, + { + onComplete: (event) => { + if (event.status === RunStatus.COMPLETED) { + result = event.result ?? null; + console.log('[TinyFish] Automation complete!'); + } else if (event.status === RunStatus.FAILED) { + runFailed = true; + failureMessage = event.error?.message ?? 'Agent run failed'; + console.error('[TinyFish] Run failed:', failureMessage); + } + }, + } + ); + + // Drain the stream so the onComplete callback fires + for await (const event of stream) { + console.log(`[TinyFish] Event: ${event.type}`); + if (event.type === EventType.COMPLETE && !result && !runFailed) { + if (event.status === RunStatus.COMPLETED) { + result = event.result ?? null; + console.log('[TinyFish] Automation complete (fallback)!'); + } else if (event.status === RunStatus.FAILED) { + runFailed = true; + failureMessage = event.error?.message ?? 'Agent run failed'; + } + } } - if (!res.body) { - console.error('[TinyFish] No response body'); - return null; + if (runFailed) { + throw new TinyFishError(failureMessage, 'RUN_FAILED'); } - // Parse SSE stream - const reader = res.body.getReader(); - const decoder = new TextDecoder(); - let buffer = ''; - let result: any = null; - let lastEvent = ''; - - console.log('[TinyFish] Reading SSE stream...'); - - while (true) { - const { done, value } = await reader.read(); - if (done) break; - - buffer += decoder.decode(value, { stream: true }); - const lines = buffer.split('\n'); - buffer = lines.pop() || ''; - - for (const line of lines) { - if (line.startsWith('data: ')) { - try { - const eventData = JSON.parse(line.slice(6)); - lastEvent = eventData.status || eventData.type || 'unknown'; + if (!result) { + throw new TinyFishError('Stream ended without a COMPLETED result', 'NO_RESULT'); + } - console.log(`[TinyFish] Event: ${lastEvent}`); + console.log(`[TinyFish] Success! Got result:`, typeof result === 'object' + ? (Array.isArray(result) ? `Array with ${(result as unknown[]).length} items` : 'Object') + : typeof result); - if (eventData.status === 'COMPLETED' || eventData.type === 'COMPLETE' || eventData.type === 'complete') { - result = eventData.result_json || eventData.resultJson || eventData.result || eventData.data; - console.log('[TinyFish] Automation complete!'); - } + return result; - if (eventData.status === 'FAILED' || eventData.type === 'ERROR' || eventData.type === 'error') { - console.error('[TinyFish] Error event:', eventData.message || eventData); - return null; - } - } catch (parseErr) { - // Non-JSON event, skip - } - } - } - } + } catch (error: unknown) { + if (error instanceof TinyFishError) throw error; - if (result) { - console.log(`[TinyFish] Success! Got result:`, typeof result === 'object' ? - (Array.isArray(result) ? `Array with ${result.length} items` : 'Object') : typeof result); - return result; - } else { - console.log(`[TinyFish] Stream ended without COMPLETED status. Last event: ${lastEvent}`); - return null; + if ((error as { name?: string })?.name === 'AbortError') { + throw new TinyFishError(`Request timed out after ${timeoutMs}ms`, 'TIMEOUT'); } - } catch (error: any) { - const msg = error?.name === 'AbortError' ? 'Request timed out' : error?.message; - console.error(`[TinyFish] Error:`, msg); - return null; + const msg = (error as { message?: string })?.message ?? 'Unknown error'; + throw new TinyFishError(`Stream error: ${msg}`, 'STREAM_ERROR'); } finally { if (timeout) clearTimeout(timeout); } diff --git a/research-sentry/next-env.d.ts b/research-sentry/next-env.d.ts deleted file mode 100644 index 9edff1c7..00000000 --- a/research-sentry/next-env.d.ts +++ /dev/null @@ -1,6 +0,0 @@ -/// -/// -import "./.next/types/routes.d.ts"; - -// NOTE: This file should not be edited -// see https://nextjs.org/docs/app/api-reference/config/typescript for more information. diff --git a/research-sentry/package-lock.json b/research-sentry/package-lock.json deleted file mode 100644 index 15468527..00000000 --- a/research-sentry/package-lock.json +++ /dev/null @@ -1,2745 +0,0 @@ -{ - "name": "research-sentry", - "version": "1.0.0", - "lockfileVersion": 3, - "requires": true, - "packages": { - "": { - "name": "research-sentry", - "version": "1.0.0", - "dependencies": { - "lucide-react": "^0.344.0", - "next": "^16.2.1", - "openai": "^4.28.0", - "pdf-parse": "^2.4.5", - "react": "^18.2.0", - "react-dom": "^18.2.0" - }, - "devDependencies": { - "@types/node": "^20", - "@types/react": "^18", - "autoprefixer": "^10", - "postcss": "^8", - "tailwindcss": "^3.4.1", - "typescript": "^5" - } - }, - "node_modules/@alloc/quick-lru": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/@alloc/quick-lru/-/quick-lru-5.2.0.tgz", - "integrity": "sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/@emnapi/runtime": { - "version": "1.9.1", - "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.9.1.tgz", - "integrity": "sha512-VYi5+ZVLhpgK4hQ0TAjiQiZ6ol0oe4mBx7mVv7IflsiEp0OWoVsp/+f9Vc1hOhE0TtkORVrI1GvzyreqpgWtkA==", - "license": "MIT", - "optional": true, - "dependencies": { - "tslib": "^2.4.0" - } - }, - "node_modules/@img/colour": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@img/colour/-/colour-1.1.0.tgz", - "integrity": "sha512-Td76q7j57o/tLVdgS746cYARfSyxk8iEfRxewL9h4OMzYhbW4TAcppl0mT4eyqXddh6L/jwoM75mo7ixa/pCeQ==", - "license": "MIT", - "optional": true, - "engines": { - "node": ">=18" - } - }, - "node_modules/@img/sharp-darwin-arm64": { - "version": "0.34.5", - "resolved": "https://registry.npmjs.org/@img/sharp-darwin-arm64/-/sharp-darwin-arm64-0.34.5.tgz", - "integrity": "sha512-imtQ3WMJXbMY4fxb/Ndp6HBTNVtWCUI0WdobyheGf5+ad6xX8VIDO8u2xE4qc/fr08CKG/7dDseFtn6M6g/r3w==", - "cpu": [ - "arm64" - ], - "license": "Apache-2.0", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - }, - "optionalDependencies": { - "@img/sharp-libvips-darwin-arm64": "1.2.4" - } - }, - "node_modules/@img/sharp-darwin-x64": { - "version": "0.34.5", - "resolved": "https://registry.npmjs.org/@img/sharp-darwin-x64/-/sharp-darwin-x64-0.34.5.tgz", - "integrity": "sha512-YNEFAF/4KQ/PeW0N+r+aVVsoIY0/qxxikF2SWdp+NRkmMB7y9LBZAVqQ4yhGCm/H3H270OSykqmQMKLBhBJDEw==", - "cpu": [ - "x64" - ], - "license": "Apache-2.0", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - }, - "optionalDependencies": { - "@img/sharp-libvips-darwin-x64": "1.2.4" - } - }, - "node_modules/@img/sharp-libvips-darwin-arm64": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-arm64/-/sharp-libvips-darwin-arm64-1.2.4.tgz", - "integrity": "sha512-zqjjo7RatFfFoP0MkQ51jfuFZBnVE2pRiaydKJ1G/rHZvnsrHAOcQALIi9sA5co5xenQdTugCvtb1cuf78Vf4g==", - "cpu": [ - "arm64" - ], - "license": "LGPL-3.0-or-later", - "optional": true, - "os": [ - "darwin" - ], - "funding": { - "url": "https://opencollective.com/libvips" - } - }, - "node_modules/@img/sharp-libvips-darwin-x64": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-x64/-/sharp-libvips-darwin-x64-1.2.4.tgz", - "integrity": "sha512-1IOd5xfVhlGwX+zXv2N93k0yMONvUlANylbJw1eTah8K/Jtpi15KC+WSiaX/nBmbm2HxRM1gZ0nSdjSsrZbGKg==", - "cpu": [ - "x64" - ], - "license": "LGPL-3.0-or-later", - "optional": true, - "os": [ - "darwin" - ], - "funding": { - "url": "https://opencollective.com/libvips" - } - }, - "node_modules/@img/sharp-libvips-linux-arm": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm/-/sharp-libvips-linux-arm-1.2.4.tgz", - "integrity": "sha512-bFI7xcKFELdiNCVov8e44Ia4u2byA+l3XtsAj+Q8tfCwO6BQ8iDojYdvoPMqsKDkuoOo+X6HZA0s0q11ANMQ8A==", - "cpu": [ - "arm" - ], - "license": "LGPL-3.0-or-later", - "optional": true, - "os": [ - "linux" - ], - "funding": { - "url": "https://opencollective.com/libvips" - } - }, - "node_modules/@img/sharp-libvips-linux-arm64": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm64/-/sharp-libvips-linux-arm64-1.2.4.tgz", - "integrity": "sha512-excjX8DfsIcJ10x1Kzr4RcWe1edC9PquDRRPx3YVCvQv+U5p7Yin2s32ftzikXojb1PIFc/9Mt28/y+iRklkrw==", - "cpu": [ - "arm64" - ], - "license": "LGPL-3.0-or-later", - "optional": true, - "os": [ - "linux" - ], - "funding": { - "url": "https://opencollective.com/libvips" - } - }, - "node_modules/@img/sharp-libvips-linux-ppc64": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-ppc64/-/sharp-libvips-linux-ppc64-1.2.4.tgz", - "integrity": "sha512-FMuvGijLDYG6lW+b/UvyilUWu5Ayu+3r2d1S8notiGCIyYU/76eig1UfMmkZ7vwgOrzKzlQbFSuQfgm7GYUPpA==", - "cpu": [ - "ppc64" - ], - "license": "LGPL-3.0-or-later", - "optional": true, - "os": [ - "linux" - ], - "funding": { - "url": "https://opencollective.com/libvips" - } - }, - "node_modules/@img/sharp-libvips-linux-riscv64": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-riscv64/-/sharp-libvips-linux-riscv64-1.2.4.tgz", - "integrity": "sha512-oVDbcR4zUC0ce82teubSm+x6ETixtKZBh/qbREIOcI3cULzDyb18Sr/Wcyx7NRQeQzOiHTNbZFF1UwPS2scyGA==", - "cpu": [ - "riscv64" - ], - "license": "LGPL-3.0-or-later", - "optional": true, - "os": [ - "linux" - ], - "funding": { - "url": "https://opencollective.com/libvips" - } - }, - "node_modules/@img/sharp-libvips-linux-s390x": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-s390x/-/sharp-libvips-linux-s390x-1.2.4.tgz", - "integrity": "sha512-qmp9VrzgPgMoGZyPvrQHqk02uyjA0/QrTO26Tqk6l4ZV0MPWIW6LTkqOIov+J1yEu7MbFQaDpwdwJKhbJvuRxQ==", - "cpu": [ - "s390x" - ], - "license": "LGPL-3.0-or-later", - "optional": true, - "os": [ - "linux" - ], - "funding": { - "url": "https://opencollective.com/libvips" - } - }, - "node_modules/@img/sharp-libvips-linux-x64": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-x64/-/sharp-libvips-linux-x64-1.2.4.tgz", - "integrity": "sha512-tJxiiLsmHc9Ax1bz3oaOYBURTXGIRDODBqhveVHonrHJ9/+k89qbLl0bcJns+e4t4rvaNBxaEZsFtSfAdquPrw==", - "cpu": [ - "x64" - ], - "license": "LGPL-3.0-or-later", - "optional": true, - "os": [ - "linux" - ], - "funding": { - "url": "https://opencollective.com/libvips" - } - }, - "node_modules/@img/sharp-libvips-linuxmusl-arm64": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-arm64/-/sharp-libvips-linuxmusl-arm64-1.2.4.tgz", - "integrity": "sha512-FVQHuwx1IIuNow9QAbYUzJ+En8KcVm9Lk5+uGUQJHaZmMECZmOlix9HnH7n1TRkXMS0pGxIJokIVB9SuqZGGXw==", - "cpu": [ - "arm64" - ], - "license": "LGPL-3.0-or-later", - "optional": true, - "os": [ - "linux" - ], - "funding": { - "url": "https://opencollective.com/libvips" - } - }, - "node_modules/@img/sharp-libvips-linuxmusl-x64": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-x64/-/sharp-libvips-linuxmusl-x64-1.2.4.tgz", - "integrity": "sha512-+LpyBk7L44ZIXwz/VYfglaX/okxezESc6UxDSoyo2Ks6Jxc4Y7sGjpgU9s4PMgqgjj1gZCylTieNamqA1MF7Dg==", - "cpu": [ - "x64" - ], - "license": "LGPL-3.0-or-later", - "optional": true, - "os": [ - "linux" - ], - "funding": { - "url": "https://opencollective.com/libvips" - } - }, - "node_modules/@img/sharp-linux-arm": { - "version": "0.34.5", - "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm/-/sharp-linux-arm-0.34.5.tgz", - "integrity": "sha512-9dLqsvwtg1uuXBGZKsxem9595+ujv0sJ6Vi8wcTANSFpwV/GONat5eCkzQo/1O6zRIkh0m/8+5BjrRr7jDUSZw==", - "cpu": [ - "arm" - ], - "license": "Apache-2.0", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - }, - "optionalDependencies": { - "@img/sharp-libvips-linux-arm": "1.2.4" - } - }, - "node_modules/@img/sharp-linux-arm64": { - "version": "0.34.5", - "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm64/-/sharp-linux-arm64-0.34.5.tgz", - "integrity": "sha512-bKQzaJRY/bkPOXyKx5EVup7qkaojECG6NLYswgktOZjaXecSAeCWiZwwiFf3/Y+O1HrauiE3FVsGxFg8c24rZg==", - "cpu": [ - "arm64" - ], - "license": "Apache-2.0", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - }, - "optionalDependencies": { - "@img/sharp-libvips-linux-arm64": "1.2.4" - } - }, - "node_modules/@img/sharp-linux-ppc64": { - "version": "0.34.5", - "resolved": "https://registry.npmjs.org/@img/sharp-linux-ppc64/-/sharp-linux-ppc64-0.34.5.tgz", - "integrity": "sha512-7zznwNaqW6YtsfrGGDA6BRkISKAAE1Jo0QdpNYXNMHu2+0dTrPflTLNkpc8l7MUP5M16ZJcUvysVWWrMefZquA==", - "cpu": [ - "ppc64" - ], - "license": "Apache-2.0", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - }, - "optionalDependencies": { - "@img/sharp-libvips-linux-ppc64": "1.2.4" - } - }, - "node_modules/@img/sharp-linux-riscv64": { - "version": "0.34.5", - "resolved": "https://registry.npmjs.org/@img/sharp-linux-riscv64/-/sharp-linux-riscv64-0.34.5.tgz", - "integrity": "sha512-51gJuLPTKa7piYPaVs8GmByo7/U7/7TZOq+cnXJIHZKavIRHAP77e3N2HEl3dgiqdD/w0yUfiJnII77PuDDFdw==", - "cpu": [ - "riscv64" - ], - "license": "Apache-2.0", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - }, - "optionalDependencies": { - "@img/sharp-libvips-linux-riscv64": "1.2.4" - } - }, - "node_modules/@img/sharp-linux-s390x": { - "version": "0.34.5", - "resolved": "https://registry.npmjs.org/@img/sharp-linux-s390x/-/sharp-linux-s390x-0.34.5.tgz", - "integrity": "sha512-nQtCk0PdKfho3eC5MrbQoigJ2gd1CgddUMkabUj+rBevs8tZ2cULOx46E7oyX+04WGfABgIwmMC0VqieTiR4jg==", - "cpu": [ - "s390x" - ], - "license": "Apache-2.0", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - }, - "optionalDependencies": { - "@img/sharp-libvips-linux-s390x": "1.2.4" - } - }, - "node_modules/@img/sharp-linux-x64": { - "version": "0.34.5", - "resolved": "https://registry.npmjs.org/@img/sharp-linux-x64/-/sharp-linux-x64-0.34.5.tgz", - "integrity": "sha512-MEzd8HPKxVxVenwAa+JRPwEC7QFjoPWuS5NZnBt6B3pu7EG2Ge0id1oLHZpPJdn3OQK+BQDiw9zStiHBTJQQQQ==", - "cpu": [ - "x64" - ], - "license": "Apache-2.0", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - }, - "optionalDependencies": { - "@img/sharp-libvips-linux-x64": "1.2.4" - } - }, - "node_modules/@img/sharp-linuxmusl-arm64": { - "version": "0.34.5", - "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-arm64/-/sharp-linuxmusl-arm64-0.34.5.tgz", - "integrity": "sha512-fprJR6GtRsMt6Kyfq44IsChVZeGN97gTD331weR1ex1c1rypDEABN6Tm2xa1wE6lYb5DdEnk03NZPqA7Id21yg==", - "cpu": [ - "arm64" - ], - "license": "Apache-2.0", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - }, - "optionalDependencies": { - "@img/sharp-libvips-linuxmusl-arm64": "1.2.4" - } - }, - "node_modules/@img/sharp-linuxmusl-x64": { - "version": "0.34.5", - "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-x64/-/sharp-linuxmusl-x64-0.34.5.tgz", - "integrity": "sha512-Jg8wNT1MUzIvhBFxViqrEhWDGzqymo3sV7z7ZsaWbZNDLXRJZoRGrjulp60YYtV4wfY8VIKcWidjojlLcWrd8Q==", - "cpu": [ - "x64" - ], - "license": "Apache-2.0", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - }, - "optionalDependencies": { - "@img/sharp-libvips-linuxmusl-x64": "1.2.4" - } - }, - "node_modules/@img/sharp-wasm32": { - "version": "0.34.5", - "resolved": "https://registry.npmjs.org/@img/sharp-wasm32/-/sharp-wasm32-0.34.5.tgz", - "integrity": "sha512-OdWTEiVkY2PHwqkbBI8frFxQQFekHaSSkUIJkwzclWZe64O1X4UlUjqqqLaPbUpMOQk6FBu/HtlGXNblIs0huw==", - "cpu": [ - "wasm32" - ], - "license": "Apache-2.0 AND LGPL-3.0-or-later AND MIT", - "optional": true, - "dependencies": { - "@emnapi/runtime": "^1.7.0" - }, - "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - } - }, - "node_modules/@img/sharp-win32-arm64": { - "version": "0.34.5", - "resolved": "https://registry.npmjs.org/@img/sharp-win32-arm64/-/sharp-win32-arm64-0.34.5.tgz", - "integrity": "sha512-WQ3AgWCWYSb2yt+IG8mnC6Jdk9Whs7O0gxphblsLvdhSpSTtmu69ZG1Gkb6NuvxsNACwiPV6cNSZNzt0KPsw7g==", - "cpu": [ - "arm64" - ], - "license": "Apache-2.0 AND LGPL-3.0-or-later", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - } - }, - "node_modules/@img/sharp-win32-ia32": { - "version": "0.34.5", - "resolved": "https://registry.npmjs.org/@img/sharp-win32-ia32/-/sharp-win32-ia32-0.34.5.tgz", - "integrity": "sha512-FV9m/7NmeCmSHDD5j4+4pNI8Cp3aW+JvLoXcTUo0IqyjSfAZJ8dIUmijx1qaJsIiU+Hosw6xM5KijAWRJCSgNg==", - "cpu": [ - "ia32" - ], - "license": "Apache-2.0 AND LGPL-3.0-or-later", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - } - }, - "node_modules/@img/sharp-win32-x64": { - "version": "0.34.5", - "resolved": "https://registry.npmjs.org/@img/sharp-win32-x64/-/sharp-win32-x64-0.34.5.tgz", - "integrity": "sha512-+29YMsqY2/9eFEiW93eqWnuLcWcufowXewwSNIT6UwZdUUCrM3oFjMWH/Z6/TMmb4hlFenmfAVbpWeup2jryCw==", - "cpu": [ - "x64" - ], - "license": "Apache-2.0 AND LGPL-3.0-or-later", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - } - }, - "node_modules/@jridgewell/gen-mapping": { - "version": "0.3.13", - "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz", - "integrity": "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jridgewell/sourcemap-codec": "^1.5.0", - "@jridgewell/trace-mapping": "^0.3.24" - } - }, - "node_modules/@jridgewell/resolve-uri": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", - "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@jridgewell/sourcemap-codec": { - "version": "1.5.5", - "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz", - "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==", - "dev": true, - "license": "MIT" - }, - "node_modules/@jridgewell/trace-mapping": { - "version": "0.3.31", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz", - "integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jridgewell/resolve-uri": "^3.1.0", - "@jridgewell/sourcemap-codec": "^1.4.14" - } - }, - "node_modules/@napi-rs/canvas": { - "version": "0.1.80", - "resolved": "https://registry.npmjs.org/@napi-rs/canvas/-/canvas-0.1.80.tgz", - "integrity": "sha512-DxuT1ClnIPts1kQx8FBmkk4BQDTfI5kIzywAaMjQSXfNnra5UFU9PwurXrl+Je3bJ6BGsp/zmshVVFbCmyI+ww==", - "license": "MIT", - "workspaces": [ - "e2e/*" - ], - "engines": { - "node": ">= 10" - }, - "optionalDependencies": { - "@napi-rs/canvas-android-arm64": "0.1.80", - "@napi-rs/canvas-darwin-arm64": "0.1.80", - "@napi-rs/canvas-darwin-x64": "0.1.80", - "@napi-rs/canvas-linux-arm-gnueabihf": "0.1.80", - "@napi-rs/canvas-linux-arm64-gnu": "0.1.80", - "@napi-rs/canvas-linux-arm64-musl": "0.1.80", - "@napi-rs/canvas-linux-riscv64-gnu": "0.1.80", - "@napi-rs/canvas-linux-x64-gnu": "0.1.80", - "@napi-rs/canvas-linux-x64-musl": "0.1.80", - "@napi-rs/canvas-win32-x64-msvc": "0.1.80" - } - }, - "node_modules/@napi-rs/canvas-android-arm64": { - "version": "0.1.80", - "resolved": "https://registry.npmjs.org/@napi-rs/canvas-android-arm64/-/canvas-android-arm64-0.1.80.tgz", - "integrity": "sha512-sk7xhN/MoXeuExlggf91pNziBxLPVUqF2CAVnB57KLG/pz7+U5TKG8eXdc3pm0d7Od0WreB6ZKLj37sX9muGOQ==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@napi-rs/canvas-darwin-arm64": { - "version": "0.1.80", - "resolved": "https://registry.npmjs.org/@napi-rs/canvas-darwin-arm64/-/canvas-darwin-arm64-0.1.80.tgz", - "integrity": "sha512-O64APRTXRUiAz0P8gErkfEr3lipLJgM6pjATwavZ22ebhjYl/SUbpgM0xcWPQBNMP1n29afAC/Us5PX1vg+JNQ==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@napi-rs/canvas-darwin-x64": { - "version": "0.1.80", - "resolved": "https://registry.npmjs.org/@napi-rs/canvas-darwin-x64/-/canvas-darwin-x64-0.1.80.tgz", - "integrity": "sha512-FqqSU7qFce0Cp3pwnTjVkKjjOtxMqRe6lmINxpIZYaZNnVI0H5FtsaraZJ36SiTHNjZlUB69/HhxNDT1Aaa9vA==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@napi-rs/canvas-linux-arm-gnueabihf": { - "version": "0.1.80", - "resolved": "https://registry.npmjs.org/@napi-rs/canvas-linux-arm-gnueabihf/-/canvas-linux-arm-gnueabihf-0.1.80.tgz", - "integrity": "sha512-eyWz0ddBDQc7/JbAtY4OtZ5SpK8tR4JsCYEZjCE3dI8pqoWUC8oMwYSBGCYfsx2w47cQgQCgMVRVTFiiO38hHQ==", - "cpu": [ - "arm" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@napi-rs/canvas-linux-arm64-gnu": { - "version": "0.1.80", - "resolved": "https://registry.npmjs.org/@napi-rs/canvas-linux-arm64-gnu/-/canvas-linux-arm64-gnu-0.1.80.tgz", - "integrity": "sha512-qwA63t8A86bnxhuA/GwOkK3jvb+XTQaTiVML0vAWoHyoZYTjNs7BzoOONDgTnNtr8/yHrq64XXzUoLqDzU+Uuw==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@napi-rs/canvas-linux-arm64-musl": { - "version": "0.1.80", - "resolved": "https://registry.npmjs.org/@napi-rs/canvas-linux-arm64-musl/-/canvas-linux-arm64-musl-0.1.80.tgz", - "integrity": "sha512-1XbCOz/ymhj24lFaIXtWnwv/6eFHXDrjP0jYkc6iHQ9q8oXKzUX1Lc6bu+wuGiLhGh2GS/2JlfORC5ZcXimRcg==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@napi-rs/canvas-linux-riscv64-gnu": { - "version": "0.1.80", - "resolved": "https://registry.npmjs.org/@napi-rs/canvas-linux-riscv64-gnu/-/canvas-linux-riscv64-gnu-0.1.80.tgz", - "integrity": "sha512-XTzR125w5ZMs0lJcxRlS1K3P5RaZ9RmUsPtd1uGt+EfDyYMu4c6SEROYsxyatbbu/2+lPe7MPHOO/0a0x7L/gw==", - "cpu": [ - "riscv64" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@napi-rs/canvas-linux-x64-gnu": { - "version": "0.1.80", - "resolved": "https://registry.npmjs.org/@napi-rs/canvas-linux-x64-gnu/-/canvas-linux-x64-gnu-0.1.80.tgz", - "integrity": "sha512-BeXAmhKg1kX3UCrJsYbdQd3hIMDH/K6HnP/pG2LuITaXhXBiNdh//TVVVVCBbJzVQaV5gK/4ZOCMrQW9mvuTqA==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@napi-rs/canvas-linux-x64-musl": { - "version": "0.1.80", - "resolved": "https://registry.npmjs.org/@napi-rs/canvas-linux-x64-musl/-/canvas-linux-x64-musl-0.1.80.tgz", - "integrity": "sha512-x0XvZWdHbkgdgucJsRxprX/4o4sEed7qo9rCQA9ugiS9qE2QvP0RIiEugtZhfLH3cyI+jIRFJHV4Fuz+1BHHMg==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@napi-rs/canvas-win32-x64-msvc": { - "version": "0.1.80", - "resolved": "https://registry.npmjs.org/@napi-rs/canvas-win32-x64-msvc/-/canvas-win32-x64-msvc-0.1.80.tgz", - "integrity": "sha512-Z8jPsM6df5V8B1HrCHB05+bDiCxjE9QA//3YrkKIdVDEwn5RKaqOxCJDRJkl48cJbylcrJbW4HxZbTte8juuPg==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@next/env": { - "version": "16.2.1", - "resolved": "https://registry.npmjs.org/@next/env/-/env-16.2.1.tgz", - "integrity": "sha512-n8P/HCkIWW+gVal2Z8XqXJ6aB3J0tuM29OcHpCsobWlChH/SITBs1DFBk/HajgrwDkqqBXPbuUuzgDvUekREPg==", - "license": "MIT" - }, - "node_modules/@next/swc-darwin-arm64": { - "version": "16.2.1", - "resolved": "https://registry.npmjs.org/@next/swc-darwin-arm64/-/swc-darwin-arm64-16.2.1.tgz", - "integrity": "sha512-BwZ8w8YTaSEr2HIuXLMLxIdElNMPvY9fLqb20LX9A9OMGtJilhHLbCL3ggyd0TwjmMcTxi0XXt+ur1vWUoxj2Q==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@next/swc-darwin-x64": { - "version": "16.2.1", - "resolved": "https://registry.npmjs.org/@next/swc-darwin-x64/-/swc-darwin-x64-16.2.1.tgz", - "integrity": "sha512-/vrcE6iQSJq3uL3VGVHiXeaKbn8Es10DGTGRJnRZlkNQQk3kaNtAJg8Y6xuAlrx/6INKVjkfi5rY0iEXorZ6uA==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@next/swc-linux-arm64-gnu": { - "version": "16.2.1", - "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-16.2.1.tgz", - "integrity": "sha512-uLn+0BK+C31LTVbQ/QU+UaVrV0rRSJQ8RfniQAHPghDdgE+SlroYqcmFnO5iNjNfVWCyKZHYrs3Nl0mUzWxbBw==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@next/swc-linux-arm64-musl": { - "version": "16.2.1", - "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-16.2.1.tgz", - "integrity": "sha512-ssKq6iMRnHdnycGp9hCuGnXJZ0YPr4/wNwrfE5DbmvEcgl9+yv97/Kq3TPVDfYome1SW5geciLB9aiEqKXQjlQ==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@next/swc-linux-x64-gnu": { - "version": "16.2.1", - "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-16.2.1.tgz", - "integrity": "sha512-HQm7SrHRELJ30T1TSmT706IWovFFSRGxfgUkyWJZF/RKBMdbdRWJuFrcpDdE5vy9UXjFOx6L3mRdqH04Mmx0hg==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@next/swc-linux-x64-musl": { - "version": "16.2.1", - "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-16.2.1.tgz", - "integrity": "sha512-aV2iUaC/5HGEpbBkE+4B8aHIudoOy5DYekAKOMSHoIYQ66y/wIVeaRx8MS2ZMdxe/HIXlMho4ubdZs/J8441Tg==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@next/swc-win32-arm64-msvc": { - "version": "16.2.1", - "resolved": "https://registry.npmjs.org/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-16.2.1.tgz", - "integrity": "sha512-IXdNgiDHaSk0ZUJ+xp0OQTdTgnpx1RCfRTalhn3cjOP+IddTMINwA7DXZrwTmGDO8SUr5q2hdP/du4DcrB1GxA==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@next/swc-win32-x64-msvc": { - "version": "16.2.1", - "resolved": "https://registry.npmjs.org/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-16.2.1.tgz", - "integrity": "sha512-qvU+3a39Hay+ieIztkGSbF7+mccbbg1Tk25hc4JDylf8IHjYmY/Zm64Qq1602yPyQqvie+vf5T/uPwNxDNIoeg==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@nodelib/fs.scandir": { - "version": "2.1.5", - "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", - "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", - "dev": true, - "license": "MIT", - "dependencies": { - "@nodelib/fs.stat": "2.0.5", - "run-parallel": "^1.1.9" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/@nodelib/fs.stat": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", - "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 8" - } - }, - "node_modules/@nodelib/fs.walk": { - "version": "1.2.8", - "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", - "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@nodelib/fs.scandir": "2.1.5", - "fastq": "^1.6.0" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/@swc/helpers": { - "version": "0.5.15", - "resolved": "https://registry.npmjs.org/@swc/helpers/-/helpers-0.5.15.tgz", - "integrity": "sha512-JQ5TuMi45Owi4/BIMAJBoSQoOJu12oOk/gADqlcUL9JEdHB8vyjUSsxqeNXnmXHjYKMi2WcYtezGEEhqUI/E2g==", - "license": "Apache-2.0", - "dependencies": { - "tslib": "^2.8.0" - } - }, - "node_modules/@types/node": { - "version": "20.19.30", - "resolved": "https://registry.npmjs.org/@types/node/-/node-20.19.30.tgz", - "integrity": "sha512-WJtwWJu7UdlvzEAUm484QNg5eAoq5QR08KDNx7g45Usrs2NtOPiX8ugDqmKdXkyL03rBqU5dYNYVQetEpBHq2g==", - "license": "MIT", - "dependencies": { - "undici-types": "~6.21.0" - } - }, - "node_modules/@types/node-fetch": { - "version": "2.6.13", - "resolved": "https://registry.npmjs.org/@types/node-fetch/-/node-fetch-2.6.13.tgz", - "integrity": "sha512-QGpRVpzSaUs30JBSGPjOg4Uveu384erbHBoT1zeONvyCfwQxIkUshLAOqN/k9EjGviPRmWTTe6aH2qySWKTVSw==", - "license": "MIT", - "dependencies": { - "@types/node": "*", - "form-data": "^4.0.4" - } - }, - "node_modules/@types/prop-types": { - "version": "15.7.15", - "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.15.tgz", - "integrity": "sha512-F6bEyamV9jKGAFBEmlQnesRPGOQqS2+Uwi0Em15xenOxHaf2hv6L8YCVn3rPdPJOiJfPiCnLIRyvwVaqMY3MIw==", - "dev": true, - "license": "MIT" - }, - "node_modules/@types/react": { - "version": "18.3.27", - "resolved": "https://registry.npmjs.org/@types/react/-/react-18.3.27.tgz", - "integrity": "sha512-cisd7gxkzjBKU2GgdYrTdtQx1SORymWyaAFhaxQPK9bYO9ot3Y5OikQRvY0VYQtvwjeQnizCINJAenh/V7MK2w==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/prop-types": "*", - "csstype": "^3.2.2" - } - }, - "node_modules/abort-controller": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/abort-controller/-/abort-controller-3.0.0.tgz", - "integrity": "sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==", - "license": "MIT", - "dependencies": { - "event-target-shim": "^5.0.0" - }, - "engines": { - "node": ">=6.5" - } - }, - "node_modules/agentkeepalive": { - "version": "4.6.0", - "resolved": "https://registry.npmjs.org/agentkeepalive/-/agentkeepalive-4.6.0.tgz", - "integrity": "sha512-kja8j7PjmncONqaTsB8fQ+wE2mSU2DJ9D4XKoJ5PFWIdRMa6SLSN1ff4mOr4jCbfRSsxR4keIiySJU0N9T5hIQ==", - "license": "MIT", - "dependencies": { - "humanize-ms": "^1.2.1" - }, - "engines": { - "node": ">= 8.0.0" - } - }, - "node_modules/any-promise": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/any-promise/-/any-promise-1.3.0.tgz", - "integrity": "sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A==", - "dev": true, - "license": "MIT" - }, - "node_modules/anymatch": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", - "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", - "dev": true, - "license": "ISC", - "dependencies": { - "normalize-path": "^3.0.0", - "picomatch": "^2.0.4" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/arg": { - "version": "5.0.2", - "resolved": "https://registry.npmjs.org/arg/-/arg-5.0.2.tgz", - "integrity": "sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg==", - "dev": true, - "license": "MIT" - }, - "node_modules/asynckit": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", - "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==", - "license": "MIT" - }, - "node_modules/autoprefixer": { - "version": "10.4.23", - "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.23.tgz", - "integrity": "sha512-YYTXSFulfwytnjAPlw8QHncHJmlvFKtczb8InXaAx9Q0LbfDnfEYDE55omerIJKihhmU61Ft+cAOSzQVaBUmeA==", - "dev": true, - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/postcss/" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/autoprefixer" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "license": "MIT", - "dependencies": { - "browserslist": "^4.28.1", - "caniuse-lite": "^1.0.30001760", - "fraction.js": "^5.3.4", - "picocolors": "^1.1.1", - "postcss-value-parser": "^4.2.0" - }, - "bin": { - "autoprefixer": "bin/autoprefixer" - }, - "engines": { - "node": "^10 || ^12 || >=14" - }, - "peerDependencies": { - "postcss": "^8.1.0" - } - }, - "node_modules/baseline-browser-mapping": { - "version": "2.10.10", - "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.10.10.tgz", - "integrity": "sha512-sUoJ3IMxx4AyRqO4MLeHlnGDkyXRoUG0/AI9fjK+vS72ekpV0yWVY7O0BVjmBcRtkNcsAO2QDZ4tdKKGoI6YaQ==", - "license": "Apache-2.0", - "bin": { - "baseline-browser-mapping": "dist/cli.cjs" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/binary-extensions": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz", - "integrity": "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/braces": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", - "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", - "dev": true, - "license": "MIT", - "dependencies": { - "fill-range": "^7.1.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/browserslist": { - "version": "4.28.1", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.28.1.tgz", - "integrity": "sha512-ZC5Bd0LgJXgwGqUknZY/vkUQ04r8NXnJZ3yYi4vDmSiZmC/pdSN0NbNRPxZpbtO4uAfDUAFffO8IZoM3Gj8IkA==", - "dev": true, - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/browserslist" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/browserslist" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "license": "MIT", - "dependencies": { - "baseline-browser-mapping": "^2.9.0", - "caniuse-lite": "^1.0.30001759", - "electron-to-chromium": "^1.5.263", - "node-releases": "^2.0.27", - "update-browserslist-db": "^1.2.0" - }, - "bin": { - "browserslist": "cli.js" - }, - "engines": { - "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" - } - }, - "node_modules/call-bind-apply-helpers": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", - "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==", - "license": "MIT", - "dependencies": { - "es-errors": "^1.3.0", - "function-bind": "^1.1.2" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/camelcase-css": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/camelcase-css/-/camelcase-css-2.0.1.tgz", - "integrity": "sha512-QOSvevhslijgYwRx6Rv7zKdMF8lbRmx+uQGx2+vDc+KI/eBnsy9kit5aj23AgGu3pa4t9AgwbnXWqS+iOY+2aA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 6" - } - }, - "node_modules/caniuse-lite": { - "version": "1.0.30001766", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001766.tgz", - "integrity": "sha512-4C0lfJ0/YPjJQHagaE9x2Elb69CIqEPZeG0anQt9SIvIoOH4a4uaRl73IavyO+0qZh6MDLH//DrXThEYKHkmYA==", - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/browserslist" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/caniuse-lite" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "license": "CC-BY-4.0" - }, - "node_modules/chokidar": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz", - "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==", - "dev": true, - "license": "MIT", - "dependencies": { - "anymatch": "~3.1.2", - "braces": "~3.0.2", - "glob-parent": "~5.1.2", - "is-binary-path": "~2.1.0", - "is-glob": "~4.0.1", - "normalize-path": "~3.0.0", - "readdirp": "~3.6.0" - }, - "engines": { - "node": ">= 8.10.0" - }, - "funding": { - "url": "https://paulmillr.com/funding/" - }, - "optionalDependencies": { - "fsevents": "~2.3.2" - } - }, - "node_modules/chokidar/node_modules/glob-parent": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", - "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", - "dev": true, - "license": "ISC", - "dependencies": { - "is-glob": "^4.0.1" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/client-only": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/client-only/-/client-only-0.0.1.tgz", - "integrity": "sha512-IV3Ou0jSMzZrd3pZ48nLkT9DA7Ag1pnPzaiQhpW7c3RbcqqzvzzVu+L8gfqMp/8IM2MQtSiqaCxrrcfu8I8rMA==", - "license": "MIT" - }, - "node_modules/combined-stream": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", - "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", - "license": "MIT", - "dependencies": { - "delayed-stream": "~1.0.0" - }, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/commander": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/commander/-/commander-4.1.1.tgz", - "integrity": "sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 6" - } - }, - "node_modules/cssesc": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz", - "integrity": "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==", - "dev": true, - "license": "MIT", - "bin": { - "cssesc": "bin/cssesc" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/csstype": { - "version": "3.2.3", - "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.2.3.tgz", - "integrity": "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/delayed-stream": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", - "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", - "license": "MIT", - "engines": { - "node": ">=0.4.0" - } - }, - "node_modules/detect-libc": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz", - "integrity": "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==", - "license": "Apache-2.0", - "optional": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/didyoumean": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/didyoumean/-/didyoumean-1.2.2.tgz", - "integrity": "sha512-gxtyfqMg7GKyhQmb056K7M3xszy/myH8w+B4RT+QXBQsvAOdc3XymqDDPHx1BgPgsdAA5SIifona89YtRATDzw==", - "dev": true, - "license": "Apache-2.0" - }, - "node_modules/dlv": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/dlv/-/dlv-1.1.3.tgz", - "integrity": "sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA==", - "dev": true, - "license": "MIT" - }, - "node_modules/dunder-proto": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", - "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", - "license": "MIT", - "dependencies": { - "call-bind-apply-helpers": "^1.0.1", - "es-errors": "^1.3.0", - "gopd": "^1.2.0" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/electron-to-chromium": { - "version": "1.5.278", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.278.tgz", - "integrity": "sha512-dQ0tM1svDRQOwxnXxm+twlGTjr9Upvt8UFWAgmLsxEzFQxhbti4VwxmMjsDxVC51Zo84swW7FVCXEV+VAkhuPw==", - "dev": true, - "license": "ISC" - }, - "node_modules/es-define-property": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", - "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", - "license": "MIT", - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/es-errors": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", - "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", - "license": "MIT", - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/es-object-atoms": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz", - "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==", - "license": "MIT", - "dependencies": { - "es-errors": "^1.3.0" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/es-set-tostringtag": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz", - "integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==", - "license": "MIT", - "dependencies": { - "es-errors": "^1.3.0", - "get-intrinsic": "^1.2.6", - "has-tostringtag": "^1.0.2", - "hasown": "^2.0.2" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/escalade": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", - "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/event-target-shim": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/event-target-shim/-/event-target-shim-5.0.1.tgz", - "integrity": "sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==", - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/fast-glob": { - "version": "3.3.3", - "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.3.tgz", - "integrity": "sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@nodelib/fs.stat": "^2.0.2", - "@nodelib/fs.walk": "^1.2.3", - "glob-parent": "^5.1.2", - "merge2": "^1.3.0", - "micromatch": "^4.0.8" - }, - "engines": { - "node": ">=8.6.0" - } - }, - "node_modules/fast-glob/node_modules/glob-parent": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", - "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", - "dev": true, - "license": "ISC", - "dependencies": { - "is-glob": "^4.0.1" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/fastq": { - "version": "1.20.1", - "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.20.1.tgz", - "integrity": "sha512-GGToxJ/w1x32s/D2EKND7kTil4n8OVk/9mycTc4VDza13lOvpUZTGX3mFSCtV9ksdGBVzvsyAVLM6mHFThxXxw==", - "dev": true, - "license": "ISC", - "dependencies": { - "reusify": "^1.0.4" - } - }, - "node_modules/fill-range": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", - "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", - "dev": true, - "license": "MIT", - "dependencies": { - "to-regex-range": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/form-data": { - "version": "4.0.5", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.5.tgz", - "integrity": "sha512-8RipRLol37bNs2bhoV67fiTEvdTrbMUYcFTiy3+wuuOnUog2QBHCZWXDRijWQfAkhBj2Uf5UnVaiWwA5vdd82w==", - "license": "MIT", - "dependencies": { - "asynckit": "^0.4.0", - "combined-stream": "^1.0.8", - "es-set-tostringtag": "^2.1.0", - "hasown": "^2.0.2", - "mime-types": "^2.1.12" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/form-data-encoder": { - "version": "1.7.2", - "resolved": "https://registry.npmjs.org/form-data-encoder/-/form-data-encoder-1.7.2.tgz", - "integrity": "sha512-qfqtYan3rxrnCk1VYaA4H+Ms9xdpPqvLZa6xmMgFvhO32x7/3J/ExcTd6qpxM0vH2GdMI+poehyBZvqfMTto8A==", - "license": "MIT" - }, - "node_modules/formdata-node": { - "version": "4.4.1", - "resolved": "https://registry.npmjs.org/formdata-node/-/formdata-node-4.4.1.tgz", - "integrity": "sha512-0iirZp3uVDjVGt9p49aTaqjk84TrglENEDuqfdlZQ1roC9CWlPk6Avf8EEnZNcAqPonwkG35x4n3ww/1THYAeQ==", - "license": "MIT", - "dependencies": { - "node-domexception": "1.0.0", - "web-streams-polyfill": "4.0.0-beta.3" - }, - "engines": { - "node": ">= 12.20" - } - }, - "node_modules/fraction.js": { - "version": "5.3.4", - "resolved": "https://registry.npmjs.org/fraction.js/-/fraction.js-5.3.4.tgz", - "integrity": "sha512-1X1NTtiJphryn/uLQz3whtY6jK3fTqoE3ohKs0tT+Ujr1W59oopxmoEh7Lu5p6vBaPbgoM0bzveAW4Qi5RyWDQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": "*" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/rawify" - } - }, - "node_modules/fsevents": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", - "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", - "dev": true, - "hasInstallScript": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": "^8.16.0 || ^10.6.0 || >=11.0.0" - } - }, - "node_modules/function-bind": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", - "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", - "license": "MIT", - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/get-intrinsic": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", - "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==", - "license": "MIT", - "dependencies": { - "call-bind-apply-helpers": "^1.0.2", - "es-define-property": "^1.0.1", - "es-errors": "^1.3.0", - "es-object-atoms": "^1.1.1", - "function-bind": "^1.1.2", - "get-proto": "^1.0.1", - "gopd": "^1.2.0", - "has-symbols": "^1.1.0", - "hasown": "^2.0.2", - "math-intrinsics": "^1.1.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/get-proto": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", - "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", - "license": "MIT", - "dependencies": { - "dunder-proto": "^1.0.1", - "es-object-atoms": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/glob-parent": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", - "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", - "dev": true, - "license": "ISC", - "dependencies": { - "is-glob": "^4.0.3" - }, - "engines": { - "node": ">=10.13.0" - } - }, - "node_modules/gopd": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", - "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", - "license": "MIT", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/has-symbols": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", - "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", - "license": "MIT", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/has-tostringtag": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz", - "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==", - "license": "MIT", - "dependencies": { - "has-symbols": "^1.0.3" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/hasown": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", - "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", - "license": "MIT", - "dependencies": { - "function-bind": "^1.1.2" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/humanize-ms": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/humanize-ms/-/humanize-ms-1.2.1.tgz", - "integrity": "sha512-Fl70vYtsAFb/C06PTS9dZBo7ihau+Tu/DNCk/OyHhea07S+aeMWpFFkUaXRa8fI+ScZbEI8dfSxwY7gxZ9SAVQ==", - "license": "MIT", - "dependencies": { - "ms": "^2.0.0" - } - }, - "node_modules/is-binary-path": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", - "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", - "dev": true, - "license": "MIT", - "dependencies": { - "binary-extensions": "^2.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/is-core-module": { - "version": "2.16.1", - "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.16.1.tgz", - "integrity": "sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==", - "dev": true, - "license": "MIT", - "dependencies": { - "hasown": "^2.0.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-extglob": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", - "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-glob": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", - "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", - "dev": true, - "license": "MIT", - "dependencies": { - "is-extglob": "^2.1.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-number": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", - "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.12.0" - } - }, - "node_modules/jiti": { - "version": "1.21.7", - "resolved": "https://registry.npmjs.org/jiti/-/jiti-1.21.7.tgz", - "integrity": "sha512-/imKNG4EbWNrVjoNC/1H5/9GFy+tqjGBHCaSsN+P2RnPqjsLmv6UD3Ej+Kj8nBWaRAwyk7kK5ZUc+OEatnTR3A==", - "dev": true, - "license": "MIT", - "bin": { - "jiti": "bin/jiti.js" - } - }, - "node_modules/js-tokens": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", - "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", - "license": "MIT" - }, - "node_modules/lilconfig": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-3.1.3.tgz", - "integrity": "sha512-/vlFKAoH5Cgt3Ie+JLhRbwOsCQePABiU3tJ1egGvyQ+33R/vcwM2Zl2QR/LzjsBeItPt3oSVXapn+m4nQDvpzw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=14" - }, - "funding": { - "url": "https://github.com/sponsors/antonk52" - } - }, - "node_modules/lines-and-columns": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", - "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==", - "dev": true, - "license": "MIT" - }, - "node_modules/loose-envify": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", - "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", - "license": "MIT", - "dependencies": { - "js-tokens": "^3.0.0 || ^4.0.0" - }, - "bin": { - "loose-envify": "cli.js" - } - }, - "node_modules/lucide-react": { - "version": "0.344.0", - "resolved": "https://registry.npmjs.org/lucide-react/-/lucide-react-0.344.0.tgz", - "integrity": "sha512-6YyBnn91GB45VuVT96bYCOKElbJzUHqp65vX8cDcu55MQL9T969v4dhGClpljamuI/+KMO9P6w9Acq1CVQGvIQ==", - "license": "ISC", - "peerDependencies": { - "react": "^16.5.1 || ^17.0.0 || ^18.0.0" - } - }, - "node_modules/math-intrinsics": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", - "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", - "license": "MIT", - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/merge2": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", - "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 8" - } - }, - "node_modules/micromatch": { - "version": "4.0.8", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", - "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", - "dev": true, - "license": "MIT", - "dependencies": { - "braces": "^3.0.3", - "picomatch": "^2.3.1" - }, - "engines": { - "node": ">=8.6" - } - }, - "node_modules/mime-db": { - "version": "1.52.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", - "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/mime-types": { - "version": "2.1.35", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", - "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", - "license": "MIT", - "dependencies": { - "mime-db": "1.52.0" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", - "license": "MIT" - }, - "node_modules/mz": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/mz/-/mz-2.7.0.tgz", - "integrity": "sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "any-promise": "^1.0.0", - "object-assign": "^4.0.1", - "thenify-all": "^1.0.0" - } - }, - "node_modules/nanoid": { - "version": "3.3.11", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz", - "integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "license": "MIT", - "bin": { - "nanoid": "bin/nanoid.cjs" - }, - "engines": { - "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" - } - }, - "node_modules/next": { - "version": "16.2.1", - "resolved": "https://registry.npmjs.org/next/-/next-16.2.1.tgz", - "integrity": "sha512-VaChzNL7o9rbfdt60HUj8tev4m6d7iC1igAy157526+cJlXOQu5LzsBXNT+xaJnTP/k+utSX5vMv7m0G+zKH+Q==", - "license": "MIT", - "dependencies": { - "@next/env": "16.2.1", - "@swc/helpers": "0.5.15", - "baseline-browser-mapping": "^2.9.19", - "caniuse-lite": "^1.0.30001579", - "postcss": "8.4.31", - "styled-jsx": "5.1.6" - }, - "bin": { - "next": "dist/bin/next" - }, - "engines": { - "node": ">=20.9.0" - }, - "optionalDependencies": { - "@next/swc-darwin-arm64": "16.2.1", - "@next/swc-darwin-x64": "16.2.1", - "@next/swc-linux-arm64-gnu": "16.2.1", - "@next/swc-linux-arm64-musl": "16.2.1", - "@next/swc-linux-x64-gnu": "16.2.1", - "@next/swc-linux-x64-musl": "16.2.1", - "@next/swc-win32-arm64-msvc": "16.2.1", - "@next/swc-win32-x64-msvc": "16.2.1", - "sharp": "^0.34.5" - }, - "peerDependencies": { - "@opentelemetry/api": "^1.1.0", - "@playwright/test": "^1.51.1", - "babel-plugin-react-compiler": "*", - "react": "^18.2.0 || 19.0.0-rc-de68d2f4-20241204 || ^19.0.0", - "react-dom": "^18.2.0 || 19.0.0-rc-de68d2f4-20241204 || ^19.0.0", - "sass": "^1.3.0" - }, - "peerDependenciesMeta": { - "@opentelemetry/api": { - "optional": true - }, - "@playwright/test": { - "optional": true - }, - "babel-plugin-react-compiler": { - "optional": true - }, - "sass": { - "optional": true - } - } - }, - "node_modules/next/node_modules/postcss": { - "version": "8.4.31", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.31.tgz", - "integrity": "sha512-PS08Iboia9mts/2ygV3eLpY5ghnUcfLV/EXTOW1E2qYxJKGGBUtNjN76FYHnMs36RmARn41bC0AZmn+rR0OVpQ==", - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/postcss/" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/postcss" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "license": "MIT", - "dependencies": { - "nanoid": "^3.3.6", - "picocolors": "^1.0.0", - "source-map-js": "^1.0.2" - }, - "engines": { - "node": "^10 || ^12 || >=14" - } - }, - "node_modules/node-domexception": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/node-domexception/-/node-domexception-1.0.0.tgz", - "integrity": "sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ==", - "deprecated": "Use your platform's native DOMException instead", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/jimmywarting" - }, - { - "type": "github", - "url": "https://paypal.me/jimmywarting" - } - ], - "license": "MIT", - "engines": { - "node": ">=10.5.0" - } - }, - "node_modules/node-fetch": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz", - "integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==", - "license": "MIT", - "dependencies": { - "whatwg-url": "^5.0.0" - }, - "engines": { - "node": "4.x || >=6.0.0" - }, - "peerDependencies": { - "encoding": "^0.1.0" - }, - "peerDependenciesMeta": { - "encoding": { - "optional": true - } - } - }, - "node_modules/node-releases": { - "version": "2.0.27", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.27.tgz", - "integrity": "sha512-nmh3lCkYZ3grZvqcCH+fjmQ7X+H0OeZgP40OierEaAptX4XofMh5kwNbWh7lBduUzCcV/8kZ+NDLCwm2iorIlA==", - "dev": true, - "license": "MIT" - }, - "node_modules/normalize-path": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", - "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/object-assign": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", - "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/object-hash": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/object-hash/-/object-hash-3.0.0.tgz", - "integrity": "sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 6" - } - }, - "node_modules/openai": { - "version": "4.104.0", - "resolved": "https://registry.npmjs.org/openai/-/openai-4.104.0.tgz", - "integrity": "sha512-p99EFNsA/yX6UhVO93f5kJsDRLAg+CTA2RBqdHK4RtK8u5IJw32Hyb2dTGKbnnFmnuoBv5r7Z2CURI9sGZpSuA==", - "license": "Apache-2.0", - "dependencies": { - "@types/node": "^18.11.18", - "@types/node-fetch": "^2.6.4", - "abort-controller": "^3.0.0", - "agentkeepalive": "^4.2.1", - "form-data-encoder": "1.7.2", - "formdata-node": "^4.3.2", - "node-fetch": "^2.6.7" - }, - "bin": { - "openai": "bin/cli" - }, - "peerDependencies": { - "ws": "^8.18.0", - "zod": "^3.23.8" - }, - "peerDependenciesMeta": { - "ws": { - "optional": true - }, - "zod": { - "optional": true - } - } - }, - "node_modules/openai/node_modules/@types/node": { - "version": "18.19.130", - "resolved": "https://registry.npmjs.org/@types/node/-/node-18.19.130.tgz", - "integrity": "sha512-GRaXQx6jGfL8sKfaIDD6OupbIHBr9jv7Jnaml9tB7l4v068PAOXqfcujMMo5PhbIs6ggR1XODELqahT2R8v0fg==", - "license": "MIT", - "dependencies": { - "undici-types": "~5.26.4" - } - }, - "node_modules/openai/node_modules/undici-types": { - "version": "5.26.5", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", - "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==", - "license": "MIT" - }, - "node_modules/path-parse": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", - "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", - "dev": true, - "license": "MIT" - }, - "node_modules/pdf-parse": { - "version": "2.4.5", - "resolved": "https://registry.npmjs.org/pdf-parse/-/pdf-parse-2.4.5.tgz", - "integrity": "sha512-mHU89HGh7v+4u2ubfnevJ03lmPgQ5WU4CxAVmTSh/sxVTEDYd1er/dKS/A6vg77NX47KTEoihq8jZBLr8Cxuwg==", - "license": "Apache-2.0", - "dependencies": { - "@napi-rs/canvas": "0.1.80", - "pdfjs-dist": "5.4.296" - }, - "bin": { - "pdf-parse": "bin/cli.mjs" - }, - "engines": { - "node": ">=20.16.0 <21 || >=22.3.0" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/mehmet-kozan" - } - }, - "node_modules/pdfjs-dist": { - "version": "5.4.296", - "resolved": "https://registry.npmjs.org/pdfjs-dist/-/pdfjs-dist-5.4.296.tgz", - "integrity": "sha512-DlOzet0HO7OEnmUmB6wWGJrrdvbyJKftI1bhMitK7O2N8W2gc757yyYBbINy9IDafXAV9wmKr9t7xsTaNKRG5Q==", - "license": "Apache-2.0", - "engines": { - "node": ">=20.16.0 || >=22.3.0" - }, - "optionalDependencies": { - "@napi-rs/canvas": "^0.1.80" - } - }, - "node_modules/picocolors": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", - "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", - "license": "ISC" - }, - "node_modules/picomatch": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", - "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8.6" - }, - "funding": { - "url": "https://github.com/sponsors/jonschlinkert" - } - }, - "node_modules/pify": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", - "integrity": "sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/pirates": { - "version": "4.0.7", - "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.7.tgz", - "integrity": "sha512-TfySrs/5nm8fQJDcBDuUng3VOUKsd7S+zqvbOTiGXHfxX4wK31ard+hoNuvkicM/2YFzlpDgABOevKSsB4G/FA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 6" - } - }, - "node_modules/postcss": { - "version": "8.5.6", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.6.tgz", - "integrity": "sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==", - "dev": true, - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/postcss/" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/postcss" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "license": "MIT", - "dependencies": { - "nanoid": "^3.3.11", - "picocolors": "^1.1.1", - "source-map-js": "^1.2.1" - }, - "engines": { - "node": "^10 || ^12 || >=14" - } - }, - "node_modules/postcss-import": { - "version": "15.1.0", - "resolved": "https://registry.npmjs.org/postcss-import/-/postcss-import-15.1.0.tgz", - "integrity": "sha512-hpr+J05B2FVYUAXHeK1YyI267J/dDDhMU6B6civm8hSY1jYJnBXxzKDKDswzJmtLHryrjhnDjqqp/49t8FALew==", - "dev": true, - "license": "MIT", - "dependencies": { - "postcss-value-parser": "^4.0.0", - "read-cache": "^1.0.0", - "resolve": "^1.1.7" - }, - "engines": { - "node": ">=14.0.0" - }, - "peerDependencies": { - "postcss": "^8.0.0" - } - }, - "node_modules/postcss-js": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/postcss-js/-/postcss-js-4.1.0.tgz", - "integrity": "sha512-oIAOTqgIo7q2EOwbhb8UalYePMvYoIeRY2YKntdpFQXNosSu3vLrniGgmH9OKs/qAkfoj5oB3le/7mINW1LCfw==", - "dev": true, - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/postcss/" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "license": "MIT", - "dependencies": { - "camelcase-css": "^2.0.1" - }, - "engines": { - "node": "^12 || ^14 || >= 16" - }, - "peerDependencies": { - "postcss": "^8.4.21" - } - }, - "node_modules/postcss-load-config": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/postcss-load-config/-/postcss-load-config-6.0.1.tgz", - "integrity": "sha512-oPtTM4oerL+UXmx+93ytZVN82RrlY/wPUV8IeDxFrzIjXOLF1pN+EmKPLbubvKHT2HC20xXsCAH2Z+CKV6Oz/g==", - "dev": true, - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/postcss/" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "license": "MIT", - "dependencies": { - "lilconfig": "^3.1.1" - }, - "engines": { - "node": ">= 18" - }, - "peerDependencies": { - "jiti": ">=1.21.0", - "postcss": ">=8.0.9", - "tsx": "^4.8.1", - "yaml": "^2.4.2" - }, - "peerDependenciesMeta": { - "jiti": { - "optional": true - }, - "postcss": { - "optional": true - }, - "tsx": { - "optional": true - }, - "yaml": { - "optional": true - } - } - }, - "node_modules/postcss-nested": { - "version": "6.2.0", - "resolved": "https://registry.npmjs.org/postcss-nested/-/postcss-nested-6.2.0.tgz", - "integrity": "sha512-HQbt28KulC5AJzG+cZtj9kvKB93CFCdLvog1WFLf1D+xmMvPGlBstkpTEZfK5+AN9hfJocyBFCNiqyS48bpgzQ==", - "dev": true, - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/postcss/" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "license": "MIT", - "dependencies": { - "postcss-selector-parser": "^6.1.1" - }, - "engines": { - "node": ">=12.0" - }, - "peerDependencies": { - "postcss": "^8.2.14" - } - }, - "node_modules/postcss-selector-parser": { - "version": "6.1.2", - "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.1.2.tgz", - "integrity": "sha512-Q8qQfPiZ+THO/3ZrOrO0cJJKfpYCagtMUkXbnEfmgUjwXg6z/WBeOyS9APBBPCTSiDV+s4SwQGu8yFsiMRIudg==", - "dev": true, - "license": "MIT", - "dependencies": { - "cssesc": "^3.0.0", - "util-deprecate": "^1.0.2" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/postcss-value-parser": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz", - "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/queue-microtask": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", - "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "license": "MIT" - }, - "node_modules/react": { - "version": "18.3.1", - "resolved": "https://registry.npmjs.org/react/-/react-18.3.1.tgz", - "integrity": "sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ==", - "license": "MIT", - "dependencies": { - "loose-envify": "^1.1.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/react-dom": { - "version": "18.3.1", - "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.3.1.tgz", - "integrity": "sha512-5m4nQKp+rZRb09LNH59GM4BxTh9251/ylbKIbpe7TpGxfJ+9kv6BLkLBXIjjspbgbnIBNqlI23tRnTWT0snUIw==", - "license": "MIT", - "dependencies": { - "loose-envify": "^1.1.0", - "scheduler": "^0.23.2" - }, - "peerDependencies": { - "react": "^18.3.1" - } - }, - "node_modules/read-cache": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/read-cache/-/read-cache-1.0.0.tgz", - "integrity": "sha512-Owdv/Ft7IjOgm/i0xvNDZ1LrRANRfew4b2prF3OWMQLxLfu3bS8FVhCsrSCMK4lR56Y9ya+AThoTpDCTxCmpRA==", - "dev": true, - "license": "MIT", - "dependencies": { - "pify": "^2.3.0" - } - }, - "node_modules/readdirp": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", - "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", - "dev": true, - "license": "MIT", - "dependencies": { - "picomatch": "^2.2.1" - }, - "engines": { - "node": ">=8.10.0" - } - }, - "node_modules/resolve": { - "version": "1.22.11", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.11.tgz", - "integrity": "sha512-RfqAvLnMl313r7c9oclB1HhUEAezcpLjz95wFH4LVuhk9JF/r22qmVP9AMmOU4vMX7Q8pN8jwNg/CSpdFnMjTQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "is-core-module": "^2.16.1", - "path-parse": "^1.0.7", - "supports-preserve-symlinks-flag": "^1.0.0" - }, - "bin": { - "resolve": "bin/resolve" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/reusify": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.1.0.tgz", - "integrity": "sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==", - "dev": true, - "license": "MIT", - "engines": { - "iojs": ">=1.0.0", - "node": ">=0.10.0" - } - }, - "node_modules/run-parallel": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", - "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "license": "MIT", - "dependencies": { - "queue-microtask": "^1.2.2" - } - }, - "node_modules/scheduler": { - "version": "0.23.2", - "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.23.2.tgz", - "integrity": "sha512-UOShsPwz7NrMUqhR6t0hWjFduvOzbtv7toDH1/hIrfRNIDBnnBWd0CwJTGvTpngVlmwGCdP9/Zl/tVrDqcuYzQ==", - "license": "MIT", - "dependencies": { - "loose-envify": "^1.1.0" - } - }, - "node_modules/semver": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.4.tgz", - "integrity": "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==", - "license": "ISC", - "optional": true, - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/sharp": { - "version": "0.34.5", - "resolved": "https://registry.npmjs.org/sharp/-/sharp-0.34.5.tgz", - "integrity": "sha512-Ou9I5Ft9WNcCbXrU9cMgPBcCK8LiwLqcbywW3t4oDV37n1pzpuNLsYiAV8eODnjbtQlSDwZ2cUEeQz4E54Hltg==", - "hasInstallScript": true, - "license": "Apache-2.0", - "optional": true, - "dependencies": { - "@img/colour": "^1.0.0", - "detect-libc": "^2.1.2", - "semver": "^7.7.3" - }, - "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - }, - "optionalDependencies": { - "@img/sharp-darwin-arm64": "0.34.5", - "@img/sharp-darwin-x64": "0.34.5", - "@img/sharp-libvips-darwin-arm64": "1.2.4", - "@img/sharp-libvips-darwin-x64": "1.2.4", - "@img/sharp-libvips-linux-arm": "1.2.4", - "@img/sharp-libvips-linux-arm64": "1.2.4", - "@img/sharp-libvips-linux-ppc64": "1.2.4", - "@img/sharp-libvips-linux-riscv64": "1.2.4", - "@img/sharp-libvips-linux-s390x": "1.2.4", - "@img/sharp-libvips-linux-x64": "1.2.4", - "@img/sharp-libvips-linuxmusl-arm64": "1.2.4", - "@img/sharp-libvips-linuxmusl-x64": "1.2.4", - "@img/sharp-linux-arm": "0.34.5", - "@img/sharp-linux-arm64": "0.34.5", - "@img/sharp-linux-ppc64": "0.34.5", - "@img/sharp-linux-riscv64": "0.34.5", - "@img/sharp-linux-s390x": "0.34.5", - "@img/sharp-linux-x64": "0.34.5", - "@img/sharp-linuxmusl-arm64": "0.34.5", - "@img/sharp-linuxmusl-x64": "0.34.5", - "@img/sharp-wasm32": "0.34.5", - "@img/sharp-win32-arm64": "0.34.5", - "@img/sharp-win32-ia32": "0.34.5", - "@img/sharp-win32-x64": "0.34.5" - } - }, - "node_modules/source-map-js": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", - "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", - "license": "BSD-3-Clause", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/styled-jsx": { - "version": "5.1.6", - "resolved": "https://registry.npmjs.org/styled-jsx/-/styled-jsx-5.1.6.tgz", - "integrity": "sha512-qSVyDTeMotdvQYoHWLNGwRFJHC+i+ZvdBRYosOFgC+Wg1vx4frN2/RG/NA7SYqqvKNLf39P2LSRA2pu6n0XYZA==", - "license": "MIT", - "dependencies": { - "client-only": "0.0.1" - }, - "engines": { - "node": ">= 12.0.0" - }, - "peerDependencies": { - "react": ">= 16.8.0 || 17.x.x || ^18.0.0-0 || ^19.0.0-0" - }, - "peerDependenciesMeta": { - "@babel/core": { - "optional": true - }, - "babel-plugin-macros": { - "optional": true - } - } - }, - "node_modules/sucrase": { - "version": "3.35.1", - "resolved": "https://registry.npmjs.org/sucrase/-/sucrase-3.35.1.tgz", - "integrity": "sha512-DhuTmvZWux4H1UOnWMB3sk0sbaCVOoQZjv8u1rDoTV0HTdGem9hkAZtl4JZy8P2z4Bg0nT+YMeOFyVr4zcG5Tw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jridgewell/gen-mapping": "^0.3.2", - "commander": "^4.0.0", - "lines-and-columns": "^1.1.6", - "mz": "^2.7.0", - "pirates": "^4.0.1", - "tinyglobby": "^0.2.11", - "ts-interface-checker": "^0.1.9" - }, - "bin": { - "sucrase": "bin/sucrase", - "sucrase-node": "bin/sucrase-node" - }, - "engines": { - "node": ">=16 || 14 >=14.17" - } - }, - "node_modules/supports-preserve-symlinks-flag": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", - "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/tailwindcss": { - "version": "3.4.19", - "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-3.4.19.tgz", - "integrity": "sha512-3ofp+LL8E+pK/JuPLPggVAIaEuhvIz4qNcf3nA1Xn2o/7fb7s/TYpHhwGDv1ZU3PkBluUVaF8PyCHcm48cKLWQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@alloc/quick-lru": "^5.2.0", - "arg": "^5.0.2", - "chokidar": "^3.6.0", - "didyoumean": "^1.2.2", - "dlv": "^1.1.3", - "fast-glob": "^3.3.2", - "glob-parent": "^6.0.2", - "is-glob": "^4.0.3", - "jiti": "^1.21.7", - "lilconfig": "^3.1.3", - "micromatch": "^4.0.8", - "normalize-path": "^3.0.0", - "object-hash": "^3.0.0", - "picocolors": "^1.1.1", - "postcss": "^8.4.47", - "postcss-import": "^15.1.0", - "postcss-js": "^4.0.1", - "postcss-load-config": "^4.0.2 || ^5.0 || ^6.0", - "postcss-nested": "^6.2.0", - "postcss-selector-parser": "^6.1.2", - "resolve": "^1.22.8", - "sucrase": "^3.35.0" - }, - "bin": { - "tailwind": "lib/cli.js", - "tailwindcss": "lib/cli.js" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/thenify": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/thenify/-/thenify-3.3.1.tgz", - "integrity": "sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw==", - "dev": true, - "license": "MIT", - "dependencies": { - "any-promise": "^1.0.0" - } - }, - "node_modules/thenify-all": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/thenify-all/-/thenify-all-1.6.0.tgz", - "integrity": "sha512-RNxQH/qI8/t3thXJDwcstUO4zeqo64+Uy/+sNVRBx4Xn2OX+OZ9oP+iJnNFqplFra2ZUVeKCSa2oVWi3T4uVmA==", - "dev": true, - "license": "MIT", - "dependencies": { - "thenify": ">= 3.1.0 < 4" - }, - "engines": { - "node": ">=0.8" - } - }, - "node_modules/tinyglobby": { - "version": "0.2.15", - "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.15.tgz", - "integrity": "sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "fdir": "^6.5.0", - "picomatch": "^4.0.3" - }, - "engines": { - "node": ">=12.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/SuperchupuDev" - } - }, - "node_modules/tinyglobby/node_modules/fdir": { - "version": "6.5.0", - "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", - "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=12.0.0" - }, - "peerDependencies": { - "picomatch": "^3 || ^4" - }, - "peerDependenciesMeta": { - "picomatch": { - "optional": true - } - } - }, - "node_modules/tinyglobby/node_modules/picomatch": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", - "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/jonschlinkert" - } - }, - "node_modules/to-regex-range": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", - "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "is-number": "^7.0.0" - }, - "engines": { - "node": ">=8.0" - } - }, - "node_modules/tr46": { - "version": "0.0.3", - "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", - "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==", - "license": "MIT" - }, - "node_modules/ts-interface-checker": { - "version": "0.1.13", - "resolved": "https://registry.npmjs.org/ts-interface-checker/-/ts-interface-checker-0.1.13.tgz", - "integrity": "sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA==", - "dev": true, - "license": "Apache-2.0" - }, - "node_modules/tslib": { - "version": "2.8.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", - "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", - "license": "0BSD" - }, - "node_modules/typescript": { - "version": "5.9.3", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz", - "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", - "dev": true, - "license": "Apache-2.0", - "bin": { - "tsc": "bin/tsc", - "tsserver": "bin/tsserver" - }, - "engines": { - "node": ">=14.17" - } - }, - "node_modules/undici-types": { - "version": "6.21.0", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz", - "integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==", - "license": "MIT" - }, - "node_modules/update-browserslist-db": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.2.3.tgz", - "integrity": "sha512-Js0m9cx+qOgDxo0eMiFGEueWztz+d4+M3rGlmKPT+T4IS/jP4ylw3Nwpu6cpTTP8R1MAC1kF4VbdLt3ARf209w==", - "dev": true, - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/browserslist" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/browserslist" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "license": "MIT", - "dependencies": { - "escalade": "^3.2.0", - "picocolors": "^1.1.1" - }, - "bin": { - "update-browserslist-db": "cli.js" - }, - "peerDependencies": { - "browserslist": ">= 4.21.0" - } - }, - "node_modules/util-deprecate": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", - "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", - "dev": true, - "license": "MIT" - }, - "node_modules/web-streams-polyfill": { - "version": "4.0.0-beta.3", - "resolved": "https://registry.npmjs.org/web-streams-polyfill/-/web-streams-polyfill-4.0.0-beta.3.tgz", - "integrity": "sha512-QW95TCTaHmsYfHDybGMwO5IJIM93I/6vTRk+daHTWFPhwh+C8Cg7j7XyKrwrj8Ib6vYXe0ocYNrmzY4xAAN6ug==", - "license": "MIT", - "engines": { - "node": ">= 14" - } - }, - "node_modules/webidl-conversions": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", - "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==", - "license": "BSD-2-Clause" - }, - "node_modules/whatwg-url": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", - "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", - "license": "MIT", - "dependencies": { - "tr46": "~0.0.3", - "webidl-conversions": "^3.0.0" - } - } - } -} diff --git a/research-sentry/package.json b/research-sentry/package.json index 37e8416c..5f8539e5 100644 --- a/research-sentry/package.json +++ b/research-sentry/package.json @@ -10,10 +10,11 @@ "dependencies": { "lucide-react": "^0.344.0", "next": "^16.2.1", - "openai": "^4.28.0", "pdf-parse": "^2.4.5", "react": "^18.2.0", - "react-dom": "^18.2.0" + "react-dom": "^18.2.0", + "@tiny-fish/sdk": "latest", + "openai": "latest" }, "devDependencies": { "@types/node": "^20", @@ -23,4 +24,4 @@ "tailwindcss": "^3.4.1", "typescript": "^5" } -} +} \ No newline at end of file