diff --git a/app/api/ask/route.ts b/app/api/ask/route.ts new file mode 100644 index 0000000..92e0e57 --- /dev/null +++ b/app/api/ask/route.ts @@ -0,0 +1,24 @@ +import { NextRequest, NextResponse } from "next/server"; +import { RAGPipeline } from "@/lib/rag/pipeline"; + +export async function POST(req: NextRequest) { + const body = await req.json(); + const question = (body.question || body.query || "").toString().trim(); + if (!question) { + return NextResponse.json({ error: "Question required" }, { status: 400 }); + } + + const pipeline = await RAGPipeline.getInstance(); + const { answer, context, results } = await pipeline.ask(question, 5); + return NextResponse.json({ + answer, + context, + sources: results.map((r) => ({ + id: r.chunkId, + docId: r.docId, + title: r.title, + text: r.text, + score: r.score, + })), + }); +} \ No newline at end of file diff --git a/app/api/documents/[id]/route.ts b/app/api/documents/[id]/route.ts new file mode 100644 index 0000000..c1ecbc5 --- /dev/null +++ b/app/api/documents/[id]/route.ts @@ -0,0 +1,14 @@ +import { NextResponse } from "next/server"; + +import { RAGPipeline } from "@/lib/rag/pipeline"; + +export async function GET(_: Request, { params }: { params: { id: string } }) { + const pipeline = await RAGPipeline.getInstance(); + const document = pipeline.getDocument(params.id); + + if (!document) { + return NextResponse.json({ error: "Not found" }, { status: 404 }); + } + + return NextResponse.json(document); +} \ No newline at end of file diff --git a/app/api/documents/route.ts b/app/api/documents/route.ts new file mode 100644 index 0000000..d572e93 --- /dev/null +++ b/app/api/documents/route.ts @@ -0,0 +1,10 @@ +import { NextResponse } from "next/server"; + +import { RAGPipeline } from "@/lib/rag/pipeline"; + +export async function GET() { + const pipeline = await RAGPipeline.getInstance(); + return NextResponse.json({ + documents: pipeline.listDocuments(), + }); +} \ No newline at end of file diff --git a/app/api/search/route.ts b/app/api/search/route.ts new file mode 100644 index 0000000..9fe2284 --- /dev/null +++ b/app/api/search/route.ts @@ -0,0 +1,21 @@ +import { NextRequest, NextResponse } from "next/server"; +import { RAGPipeline } from "@/lib/rag/pipeline"; + +export async function POST(req: NextRequest) { + const { query } = await req.json(); + if (!query) return NextResponse.json({ error: "Query required" }, { status: 400 }); + + const pipeline = await RAGPipeline.getInstance(); + const results = await pipeline.search(query, 5); + + return NextResponse.json({ + results: results.map((r) => ({ + id: r.chunkId, + docId: r.docId, + title: r.title, + text: r.text, + snippet: r.text.slice(0, 220), + score: r.score, + })), + }); +} \ No newline at end of file diff --git a/app/dashboard/chatbot/page.tsx b/app/dashboard/chatbot/page.tsx new file mode 100644 index 0000000..9da8c2b --- /dev/null +++ b/app/dashboard/chatbot/page.tsx @@ -0,0 +1,382 @@ +"use client"; + +import { useState, useCallback, useEffect, useRef } from "react"; +import Sidebar from "../../../components/Sidebar"; +import { supabase } from "../../../lib/supabase"; + +const API_BASE = process.env.NEXT_PUBLIC_API_BASE || "http://localhost:3000"; + +const getApiUrl = (endpoint: string) => { + const base = API_BASE.endsWith("/") ? API_BASE.slice(0, -1) : API_BASE; + const path = endpoint.startsWith("/") ? endpoint : `/${endpoint}`; + return `${base}${path}`; +}; + +interface Message { + role: "user" | "bot"; + text: string; +} + +interface DocItem { + id: string; + title: string; + uploadedAt: string; + contentLength: number; +} + +interface SearchResult { + id: string; + title: string; + snippet: string; +} + +interface SelectedDoc { + id: string; + title: string; + content: string; +} + +const topics = [ + "About KTPilot & fraternity", + "Events & agenda", + "Membership & eligibility", + "Project guidelines", +]; + +const GREEN = "#1E3D2F"; + +export default function ChatbotPage() { + const [query, setQuery] = useState(""); + const [messages, setMessages] = useState([ + { role: "bot", text: "Hi, I'm KTPilot. Ask me anything about KTP and your docs." }, + ]); + const [isThinking, setIsThinking] = useState(false); + const [documents, setDocuments] = useState([]); + const [searchQuery, setSearchQuery] = useState(""); + const [searchResults, setSearchResults] = useState([]); + const [selectedDoc, setSelectedDoc] = useState(null); + const chatEndRef = useRef(null); + const chatContainerRef = useRef(null); + + const getToken = async () => { + const { + data: { session }, + } = await supabase.auth.getSession(); + return session?.access_token || ""; + }; + + const loadDocuments = useCallback(async () => { + try { + const token = await getToken(); + const res = await fetch(getApiUrl("/api/documents"), { + headers: { Authorization: `Bearer ${token}` }, + }); + const data = await res.json(); + setDocuments(data.documents || []); + } catch (err) { + console.error("Failed to load documents:", err); + } + }, []); + + useEffect(() => { + loadDocuments(); + }, [loadDocuments]); + + useEffect(() => { + chatEndRef.current?.scrollIntoView({ behavior: "smooth" }); + }, [messages, isThinking]); + + useEffect(() => { + window.scrollTo(0, 0); + chatContainerRef.current?.scrollTo({ top: 0 }); + }, []); + + const handleAsk = async (e: React.FormEvent) => { + e.preventDefault(); + const text = query.trim(); + if (!text) return; + + setMessages((prev) => [...prev, { role: "user", text }]); + setQuery(""); + setIsThinking(true); + + try { + const token = await getToken(); + const res = await fetch(getApiUrl("/api/ask"), { + method: "POST", + headers: { + "Content-Type": "application/json", + Authorization: `Bearer ${token}`, + }, + body: JSON.stringify({ query: text }), + }); + const data = await res.json(); + setMessages((prev) => [...prev, { role: "bot", text: data.answer || "I couldn't generate an answer." }]); + } catch { + setMessages((prev) => [...prev, { role: "bot", text: "Server error. Please try again." }]); + } finally { + setIsThinking(false); + } + }; + + const handleSearch = async (e: React.FormEvent) => { + e.preventDefault(); + const text = searchQuery.trim(); + if (!text) return; + + try { + const token = await getToken(); + const res = await fetch(getApiUrl("/api/search"), { + method: "POST", + headers: { + "Content-Type": "application/json", + Authorization: `Bearer ${token}`, + }, + body: JSON.stringify({ query: text }), + }); + const data = await res.json(); + setSearchResults(data.results || []); + } catch { + setSearchResults([]); + } + }; + + const handleViewDocument = async (id: string) => { + try { + const token = await getToken(); + const res = await fetch(getApiUrl(`/api/documents/${id}`), { + headers: { Authorization: `Bearer ${token}` }, + }); + const data = await res.json(); + setSelectedDoc(data); + } catch (err) { + console.error("Failed to load document:", err); + } + }; + + const handleQuickTopic = (topic: string) => { + setQuery(topic); + document.getElementById("ktp-chat-input")?.focus(); + }; + + return ( +
+ + +
+
+
+

+ KTPilot +

+

+ Ask about chapter docs, rules, events, and membership. +

+
+
+ {documents.length} indexed docs +
+
+ +
+
+
+
+ KTP +
+
+
KTPilot Chat
+
Answering from your uploaded documents
+
+
+ + Online +
+
+ +
+
Suggested prompts
+
+ {topics.map((topic) => ( + + ))} +
+
+ +
+ {messages.map((message, index) => ( +
+ {message.role === "bot" && ( +
+ KTP +
+ )} +
+ {message.text} +
+
+ ))} + + {isThinking && ( +
+
+ KTP +
+
+ {[0, 1, 2].map((item) => ( + + ))} +
+
+ )} + +
+
+ +
+ setQuery(e.target.value)} + className="flex-1 bg-gray-50 border border-gray-200 rounded-full px-4 py-3 text-sm text-gray-800 outline-none focus:border-gray-400 placeholder:text-gray-400" + /> + +
+
+ + +
+
+
+ ); +} diff --git a/components/Sidebar.jsx b/components/Sidebar.jsx index ae376f7..6aa4674 100644 --- a/components/Sidebar.jsx +++ b/components/Sidebar.jsx @@ -14,6 +14,7 @@ export default function Sidebar() { { label: "MERCH", path: "/dashboard/merch" }, { label: "EVENTS AND RSVP", path: "/dashboard/rsvp" }, { label: "PROFILE", path: "/dashboard/profile" }, + { label: "KTPILOT", path: "/dashboard/chatbot" }, ]; const adminItems = useMemo( diff --git a/data/documents.json b/data/documents.json new file mode 100644 index 0000000..0d10bf1 --- /dev/null +++ b/data/documents.json @@ -0,0 +1,27 @@ +[ + { + "id": "absent-guidelines-aebcf6ef", + "title": "Absent Guidelines", + "content": "Guidelines for Absences:\nUnexcused: There is one unexcused absence per semester.\n● The first unexcused absence results in a warning and a $10 fine.\n● The second unexcused absence results in one strike and a $35 fine.\n● Any other unexcused absences result in immediate probationary status. Tardiness at a\nchapter meeting will be counted towards an unexcused absence.\nExcused: Two excused absences a semester. The specified member will be fined $15 on the\nthird excused absence. Any excused absence after that will follow this equation: Fine = 20 x\n(excused absences - 3). To acquire an excused absence, a member must inform the Vice\nPresident of Membership at least 24 hours in advance.\nAmple notice is defined as at least 24 hours before the event. Non-frequent occurrence is no\nmore than one recreational activity absence per semester.\nThe Vice President of Membership will record all absences and tardiness after each chapter\nmeeting. Members are responsible for verifying the accuracy of their attendance records and\naddressing any discrepancies within 24 hours of notification. Every Tardy and Absence will be\nnotified.\nGuidelines for Tardies:\n● Tardies apply to all members, regardless of activity status. A tardy is defined as arriving\nat a mandatory event at least five minutes after the assigned time.\n○ If a member is more than twenty minutes late to a mandatory event, they will\nreceive an unexcused absence.\n● For every two tardies, a member will be issued one unexcused absence.\nGuidelines for Fines:\nIf fines are not paid on time, they will increase by $5 daily. After 10 days, the specified member\nwill be put on probation.\n-- 1 of 2 --\nExamples of Absences\nExcused Absences:\n1. Family and Personal Emergencies\nExamples: Death, illness, or significant events in the family.\nNotification Requirement: As soon as possible.\n2. Mandatory Academic or Professional Commitments\nExamples: Exams, job interviews, or compulsory workshops.\nNotification Requirement: 24 hours in advance.\n3. Health-related Issues\nExamples: Sickness, doctor’s appointments, or mental health days.\nNotification Requirement: As soon as possible, with documentation if available.\n4. Religious or Cultural Observances\nExamples: Holidays, ceremonies, or cultural events.\nNotification Requirement: 48 hours in advance.\n5. Unforeseeable Urgent Situations\nExamples: Pet emergencies, sudden travel needs, or accidents.\nNotification Requirement: As soon as possible.\nUnexcused Absences:\n1. Recreational Activities\nExamples: Concerts, sports events, or outings.\nNote: May be excused with ample notice and non-frequent occurrence.\n2. Social and Personal Engagements\nExamples: Dates, parties, or non-urgent personal errands.\nNote: Fraternity obligations should take priority.\n3. Routine Non-Emergency Commitments\nExamples: Regular gym sessions, casual meet-ups, or hobby classes.\nNote: These should be scheduled around fraternity commitments.\n4. Avoidable Absences\nExamples: Oversleeping, lack of transport planning, Tardiness, or forgetfulness.\nNote: These demonstrate a need for prioritization and planning.\n-- 2 of 2 --" + }, + { + "id": "ktp-code-of-conduct-e6397f81", + "title": "KTP Code of Conduct", + "content": "Kappa Theta Pi - Mu Chapter will not tolerate harassment in any form. Violation of the Policy subjects an\noffending person to disciplinary action, including expulsion from the Fraternity. This policy applies to all\norganizational members.\nAll organizational members are protected from harassment, including, but not limited to, harassment\naccording to their race, ethnicity, age, gender, gender identity, disability, sexual orientation, and religion.\nHarassment is more than insensitivity or conduct that offends or creates an uncomfortable situation. Any\nwords or acts designed to disregard the safety or rights of another and which intimidate, degrade,\ndemean, threaten, and/or haze will not be tolerated on the basis of the standards of Kappa Theta Pi - Mu\nChapter. Such physical, psychological, verbal, electronic, and/or written acts directed toward an individual\nor group of individuals are prohibited.\nHarassment in any form is unacceptable because it is demeaning to another person and undermines the\nintegrity of the Fraternity. Organizational members should always treat others respectfully and with dignity\nin a manner that does not offend another person's sensibilities.\nPolicy 1. Discrimination\nDiscrimination against or harassment of any individual for the reason of race, ethnicity, creed, religion,\nsex, sexual orientation, gender identity, marital status, national origin, age, disability or veteran status is\nspecifically prohibited. Accordingly, equal access to employment opportunities, membership, educational\nprograms, financial assistance, and all other fraternal activities is extended to all persons.\nPolicy 2. Assault & Battery\nIn any activity or event sponsored or endorsed by Kappa Theta Pi - Mu Chapter, no chapter, member or\nguest will engage in or permit assault and battery as defined in the state statutes in which the activity or\nevent occurs.\nPhysical Violence: Physical violence or threats thereof will not be tolerated under any circumstances.\nPolicy 3. Sexual Misconduct\nNo organizational member or guest will engage in or permit sexist or sexually abusive behavior, sexual\nharassment, non-consensual sexual activity, sexual violence, domestic violence, dating violence, stalking,\nor sexual exploitation. For the purposes of this policy:\n● Consent is the affirmative, unambiguous, and voluntary agreement to engage in a specific sexual\nactivity during a sexual encounter.\n● Sexual harassment is unwelcome conduct of a sexual nature.\n● Sexual violence is any physical or sexual act perpetrated against a person’s will or where a\nperson is incapable of giving consent (e.g., due to an individual’s age or use of drugs or alcohol,\nor because of an intellectual or other disability prevents the individual from having the capacity to\ngive consent).\n● Domestic violence is an abusive behavior used by an intimate partner to gain or maintain power\nand control over the other intimate partner. Domestic violence can be physical, sexual, emotional,\neconomic, or psychological actions or threats of actions that influence another person.\n● Dating violence is defined as violence committed by a person who is or has been in a social\nrelationship of a romantic or intimate nature with the victim; and where the existence of such a\nrelationship will be determined based on a consideration of the following factors: the length of the\n-- 1 of 6 --\nrelationship, the type of relationship, and the frequency of interaction between the persons\ninvolved in the relationship.\n● Stalking is defined as a pattern of repeated and unwanted attention, harassment, contact, or any\nother course of conduct directed at a specific person that would cause a reasonable person to\nfeel fear.\n● Sexual exploitation occurs when a person takes non-consensual or abusive sexual advantage of\nanother for his/her own advantage or benefit, or to benefit or advantage anyone other than the\none being exploited. That behavior does not otherwise constitute one of other sexual misconduct\noffenses.\nPolicy 4. Sexual Harassment\nHarassment and Bullying: Harassment or any form of intimidation is unacceptable. However, it's\nrecognized that sometimes jokes among friends can be misconstrued. In such cases, intent and context\nwill be considered, and the matter may be subject to review.\nKappa Theta Pi - Mu Chapter will not tolerate sexual harassment in any form. Violation of this policy\nsubjects an offending person to disciplinary action, including expulsion from the Fraternity. This policy\napplies to all Fraternity collegiate members, pledges, alumni members, officers, directors, and employees.\n[\\]\nDefinition: Sexual harassment consists of unwelcome sexual advances, requests for sexual favors or\nother verbal or physical acts of a sexual nature where:\n- Submission to such conduct is made either explicitly or implicitly a term or condition of\nmembership in the Fraternity or\n- A Fraternity decision is based on an individual's acceptance or rejection of such conduct or\n- Such conduct interferes with an individual's membership in the Fraternity or work for the\nFraternity or creates an environment that is intimidating, hostile, or offensive to a reasonable\nperson of the same sex as the recipient.\nSexual harassment is a form of misconduct that is unacceptable because it is demeaning to another\nperson and undermines the integrity of the Fraternity. Fraternity members and pledges at all times should\ntreat others respectfully and with dignity in a manner that does not offend another person's sensibilities.\nActions taken by nonmembers relating to Fraternity business may also constitute sexual harassment and\nshould be promptly reported.\nA violation may include, but is not limited to:\n● Any action or statement of a sexual nature which is embarrassing, harassing, intimidating, or\nabusive;\n● Unwanted comments, communications, jokes, or requests of a sexual nature;\n● Unwelcome and persistent sexually explicit statements or stories;\n● Repeated use of sexually degrading words, gestures, or sounds to describe a person;\n● Recurring derogatory comments or questions about an individual's sexual orientation and/or\nbehavior;\n● Any kind of unwelcome sexual advances;\n● Repeated phone calls, messages, and/or other communications sexual in nature, even after\nstatements have there is no interest;\n-- 2 of 6 --\n● Threats of retaliation as a result of a sexual encounter;\n● Implied or stated threats of sexual violence;\n● Sexual harassment can occur with any gender, gender identities/expressions, and/or sexual\norientation.\nPolicy 8. Alcohol\nResponsibility: If alcohol is consumed at fraternity events, it should be done responsibly and within legal\nlimits.\nPolicy 9. Attendance\nMandatory Meetings: Members are expected to attend all mandatory meetings. If a member cannot\nattend due to an unavoidable conflict, they should email kappathetapiutd@gmail.com at least 24 hours\nin advance.\nEvent Participation: Active participation in fraternity events is mandatory. A consistent lack of\nparticipation without a valid reason will be subject to review.\nCommittee Commitment: Members who are a part of specific committees have a responsibility not only\nto the broader fraternity but also to their respective committees. Consistent participation and active\ninvolvement in committee events, tasks, and initiatives are essential.\nCommittee Meetings: Committee members should prioritize attendance at all scheduled committee\nmeetings. These sessions play a pivotal role in the planning, coordination, and execution of committee\ntasks, and every member's input is invaluable.\nNotification of Absence: If a committee member cannot attend a scheduled meeting due to unforeseen\ncircumstances, it's essential to notify the committee chair or the relevant point of contact as soon as\npossible, preferably 24 hours in advance.\nValue Contribution: It's not just about attendance; it's about contributing value. Members should come\nprepared to meetings, actively engage in discussions, and collaborate effectively with peers to ensure\nmeeting the committee's objectives.\nReview of Participation: A member's consistent absence or lack of active participation in committee\nmeetings and events without a genuine reason may lead to a review by the committee chair and, if\nnecessary, the fraternity leadership.\nPolicy 5. Open Dating Policy\nKappa Theta Pi - Mu Chapter is committed to fostering an environment where all members, regardless of\ngender, gender identity, sexual orientation, or relationship status, are treated with respect, dignity, and\nfairness. We emphasize the importance of clear communication, consent, and the availability of resources\nfor support. This policy applied to all fraternity members, pledges, alumni, and officers.\nDefinition: Open dating involves engaging in romantic or intimate relationships within the fraternity while\nadhering to consent, respect, and open communication principles.\nGuidelines and Expectations:\n1. Consent: All members engaging in romantic or intimate relationships within the fraternity\nmust ensure that consent is affirmative, unambiguous, and voluntary. Consent should be\ngiven explicitly for each sexual activity during a sexual encounter.\n-- 3 of 6 --\n2. Respectful Communication: Members engaging in relationships must communicate\nopenly, honestly, and respectfully with their partners. They should discuss boundaries,\nexpectations, and concerns to ensure a healthy and consensual relationship.\n3. Confidentiality and Compliance Reporting: Any member who experiences discomfort,\nharassment, or any form of misconduct within a relationship is encouraged to report the\nincident to the designated compliance head or any authority you feel comfortable with\nwithin the fraternity. All reports will be handled confidentially, and appropriate actions will\nbe taken to address the issue.\n4. No Retaliation: Retaliation against any member who reports an incident, raises concern,\nor seeks assistance regarding a relationship issue is strictly prohibited. The fraternity is\ncommitted to protecting the rights and well-being of those who come forward.\n5. Enforcement and Accountability: Violation of this policy may result in disciplinary\naction, which includes warning, education, suspension, or expulsion from the fraternity,\ndepending on the severity and recurrence of the violation.\nPolicy 6. Interaction with Pledges\nKappa Theta Pi - Mu Chapter recognizes the importance of fostering a positive, respectful environment\nfor all members, including pledges. The policy provides guidelines on how organizational members should\nconduct themselves when interacting with pledges, ensuring a supportive and educational experience\nduring the pledge process.\nGuidelines and Expectations:\n1. Respectful Treatment: All organizational members must treat pledges with respect,\nfairness, and professionalism. Hazing, discrimination, harassment, or intimidation\ntowards pledges is strictly prohibited.\n2. Guidance and Mentorship: Organizational members should offer guidance, mentorship,\nand support to pledges throughout the pledging process. Encourage their personal and\nprofessional growth while upholding the values and mission of Kappa Theta Pi - Mu\nChapter.\n3. Education on Fraternity Values: Pledges should be educated about the fraternity’s\nvalues, goals, and expectations. Members are encouraged to help pledges understand\nand align with the principles and standards of the fraternity.\n4. No Discrimination: Pledges should not be subjected to discrimination based on race,\nethnicity, creed, religion, sex, sexual orientation, gender identity, marital status, national\norigin, age, or disability.\n5. Maintaining Professionalism: All interactions with pledges should be conducted\nprofessionally and appropriately, Any person or romantic relationship with a pledge is\nstrictly prohibited during the pledging period.\nPolicy 7. Relationships with Pledges\nKappa Theta Pi - Mu Chapter emphasized the importance of maintaining appropriate boundaries and\nfostering a healthy, professional environment during pledging. This policy outlines the fraternity’s stance\non relationships with pledges.\nGuidelines and Expectations:\n-- 4 of 6 --\n1. No Romantic Relations During Pledging: Organizational members are not allowed to\ninitiate or engage in romantic relationships with pledges while in the pledging process.\nThis includes any form of dating, romantic involvement, or intimate relationships.\n2. Prior Relationships: If an organization member is already in a romantic relationship with\nan individual who subsequently becomes a pledge, the fraternity understands and\nrespects this relationship. However, both parties should act professionally and maintain\nappropriate boundaries during pledging.\n3. Transparency and Accountability: If a pre-existing relationship exists between an\norganizational member and a pledge, both parties should promptly inform the appropriate\nfraternity authorities to ensure transparency and adherence to fraternity policies.\n4. Post-Pledging Relationships: Once the pledging period is successfully completed, and\nthe pledge becomes a full member of the fraternity, any existing relationship can continue\nwithout restriction, provided it aligns with the values and policies of Kappa Theta Pi - Mu\nChapter.\n2. Representation and Public Behavior:\nRespectful Representation: Members should always represent the fraternity positively and respectfully,\nboth in person and online.\nPublic Criticism: Openly criticizing or denigrating the fraternity or its members in public forums, including\nsocial media, is strictly prohibited.\nConfidentiality: Internal matters, discussions, or confidential information should not be shared outside of\nthe fraternity.\n5. Financial Responsibility:\nDues: Members are expected to pay their dues on time. If a member faces financial difficulties, they\nshould approach the current executive board to discuss potential solutions.\nFraternity Property: Members should respect and take care of any property belonging to the fraternity.\n6. General Behavior:\nLeadership Respect: Members should respect the decisions and directives of fraternity leadership while\nfeeling empowered to voice their opinions in appropriate forums.\nEvery member of Kappa Theta Pi should strive to uphold these standards, not just for the reputation of\nour fraternity but also for personal growth and the betterment of our community. Violations of this Code of\nConduct may be subject to review and potential disciplinary action.\nKappa Theta Pi - Mu Chapter is deeply committed to ensuring that every member, particularly the\npledges, experiences a nurturing, respectful, and enriching environment that aligns with our fraternity\nvalues and ethos. This policy outlines the standards of behavior expected when interacting with pledges.\nGuidelines and Expectations:\nPositive Reinforcement: Encourage pledges through positive reinforcement rather than criticism.\nRecognize their efforts and contributions to foster a sense of belonging and motivation.\nClear Boundaries: Maintain clear and professional boundaries. Personal and romantic relationships\nbetween organizational members and pledges are strictly prohibited during the pledging period to avoid\nconflicts of interest or perceived favoritism.\n-- 5 of 6 --\nOpen Communication: Maintain transparent lines of communication. Pledges should feel comfortable\nseeking guidance, asking questions, or voicing concerns without fear of retribution.\nNurturing Environment: Avoid intimidating actions, words, or behaviors. Instead, create an environment\nwhere pledges can thrive, learn, and grow.\nZero Tolerance for Hazing: Hazing in any form is strictly prohibited. Any member engaging in or\npromoting hazing activities will face immediate disciplinary action.\nRole Modeling: As seasoned fraternity members, your actions, words, and behaviors are models for the\npledges. Uphold the highest standards of conduct and integrity and exemplify what it means to be a part\nof Kappa Theta Pi - Mu Chapter.\nConstructive Feedback: If feedback is required, ensure it is constructive, respectful, and provided in a\nmanner that aids the pledge's development rather than diminishes their confidence.\n-- 6 of 6 --" + }, + { + "id": "ktp-strike-system-framework-1f2ecc1d", + "title": "KTP Strike System Framework", + "content": "Strike System Framework:\nInfractions & Penalties:\nThe following is how a brother can acquire a strike:\n● Missing a meeting without prior notice.\n● Repeated Absences.\n● Repeated tardiness.\n● Neglecting fraternity duties.\n● Negative comments about the fraternity.\n● Inappropriate actions or statements.\n● Not paying fines on time.\nTiered disciplinary System\nThe fraternity's disciplinary system is structured in tiers, with each level of consequence building\nupon the last. Strikes are the highest degree of consequence, followed by fines and point\ndeductions.\n● Strikes: The most severe consequence, reserved for serious and/or repeated\ninfractions. All strikes are accompanied by a fine and a point deduction.\n● Fines: A monetary penalty for specific infractions. All fines are accompanied by a point\ndeduction.\n● Point Deductions: The lowest level of consequence. Point deductions can be issued\nalone for minor infractions or as a component of a strike or fine.\nStrikes outline:\n1 Strike: A formal written warning, a point deduction, and a fine. The fine will be determined by\nthe specific infraction that resulted in the strike, as outlined in the fraternity's constitution and\nbylaws. For instance, an unexcused absence that results in a strike will carry the associated fine\nfrom the constitution.\n2 Strikes: Brothers/Pledges who accumulate two strikes will be required to meet with the\nInternal Affairs Committee for a thorough review of their behavior. Additionally, they will receive\nanother fine and a point deduction. During this process, the leadership will determine\nappropriate remediation tasks tailored to the individual’s situation. The committee will evaluate\nthe individual's alignment with fraternity values and commitment, considering the nature and\nseverity of the infractions. Continued membership will be contingent upon successful completion\nof assigned remediation and a demonstrated willingness to grow and improve.\n3 Strikes: Immediate removal from the fraternity. The decision will be final, and the individual’s\nmembership will be terminated with no opportunity for remediation.\n-- 1 of 2 --\nIndividuals who accumulate two strikes will be temporarily inactive for a duration determined\nby the leadership, considering the nature and severity of the infractions. During this period, the\nmember may be required to complete remediation tasks determined by leadership. The\nleadership will also reassess the individual's membership in the Kappa Theta Pi - Mu\nChapter, questioning their alignment with fraternity values and commitment. The decision\nregarding continued membership will be made after a thorough evaluation, considering the\nmember’s past contributions, the nature of their infractions, the results of the remediation tasks,\nand their potential for growth and change.\nDuration: Strikes have a shelf life of one academic semester. At the start of each new\nsemester, all members will begin with a clean slate.\nRight to Appeal: All Active Members have the right to dispute a strike if they believe it was\nissued in error or is unjustified. Appeals must be submitted to the Internal Affairs Committee for\nreview.\nRemoval of Strikes: Members may reduce active strikes by demonstrating consistent positive\nbehavior, active engagement, and contribution to fraternity events. For example, if a member\nhas two strikes during the current semester, they may have one removed by the end of the\nsemester based on commendable conduct, as determined by the Executive Board\n-- 2 of 2 --" + }, + { + "id": "spring-26-ktp-contacts-s26-active-brothers-de901084", + "title": "Spring '26 KTP Contacts - S26 Active Brothers", + "content": "Name UTD Email Phone Number Instagram Major Class\nAadhav Manimurugan axm210260@utdallas.edu 469-349-4553 aadhav_04 Computer Science Gamma\nAaron Gheevarghese axg230205@utdallas.edu 469-386-0233 aaron_g_h Computer Science Delta\nAashay Vishwakarma axv210094@utdallas.edu 469-731-6208 aashayvishwakarma Computer Science Gamma\nAbhinav Atluri axa220182@utdallas.edu 860-987-8424 @abhiatluri6 Data Science Delta\nAjay Kumaran AXK210259@utdallas.edu 972-370-4234 ajlikesoj Computer Science Gamma\nAman Balam Axb230089@utdallas.edu 469-389-9898 a.man5696 Computer Science Gamma\nAnvi Siddabhattuni axs230396@utdallas.edu 469-353-0279 @anvisidda Computer Science Delta\nAriha Kothari Axk220306@utdallas.edu 469-999-5293 Ariha.kothari Software Engineering Delta\nArnav Jain axj220099@utdallas.edu 972-983-8163 arnxv_jain Computer Science Delta\nArya Thombare arya.thombare@utdallas.edu 480-619-2181 aryathombare Computer Science Beta\nAshrita Dara axd220116@utdallas.edu 9724397907 the_best_ashi Data Science Epsilon\nAyaan Khan ark220001@utdallas.edu 512-817-9280 ayaan077 Software Engineering Delta\nAyush Velhal adv220002@utdallas.edu 469-763-5767 ayush.velhal Computer Science Delta\nAyushi Deshmukh Ayushi.deshmukh@utdallas.edu 214-500-9315 ayushideshmukhh Software Engineering Gamma\nBhavaneeth Parnapalli Bxp230033@utdallas.edu 6822837303 Bhavaneeth_ Data Science Epsilon\nEthan Philipose dal198705@utdallas.edu 4695050300 esphilipose Business Administration Epsilon\nEthan Varghese ethan.varghese@utdallas.edu 469-986-8928 ethanv_17 Computer Science Beta\nHima Nagi Reddy hima.nagireddy@utdallas.edu 972-765-8884 hima.sn Computer Science Beta\nIshaan Dhandapani ixd210005@utdallas.edu 469-216-7243 ishaandhandapani Computer Science Gamma\nItbaan Alam Ixa210016@utdallas.edu 737-529-4882 itbaan ITS Gamma\nJashanpreet Singh jxs220111@utdallas.edu 2145319643 _jashanpreet_singh23Computer Science Epsilon\nJiya Khurana Dal279860@utdallas.edu 9452498574 jiyaxkhurana Computer Science Epsilon\nJoel Philipose jkp220004@utdallas.edu 469-336-0668 joelphilipose ITS Delta\nKavinram Senthil kks220005@utdallas.edu 972-214-4616 kavin1724 CIS Tech Gamma\nKrish Patel kxp220050@utdallas.edu 469-301-8622 krish_patel1204 Finance Delta\nLalith Keertipati LXK220030@utdallas.edu 4699808951 lalith220 Business Administration Epsilon\nMansi Cherukupally Mansi.Cherukupally@UTDallas.edu 832-759-8848 mansicherukupally Computer Science Beta\nMekha Mathew mam220020@utdallas.edu 512-688-9914 mekhamathew._ Computer Science Gamma\nMohamed Afsar Harsath Arif afsar.harsatharif@utdallas.edu 469-636-2699 techafsar Computer Engineering Beta\nMohammed Faadil Khan mohammedfaadil.khan@UTDallas.edu 945-400-4166 mohammedf_khan Computer Engineering Gamma\nNavmi Srithaj dal273651@utdallas.edu 9729041155 @navmisrithaj Computer Science Epsilon\nNihita Soma nxs220092@utdallas.edu 469-545-6393 nihita_soma Computer Science Delta\nNivedh Koya nivedh.koya@utdallas.edu 972-349-0908 nivedh_k1 Computer Science Beta\nNoel Emmanuel Nke220000@utdallas.edu 210-535-0802 noelemmanuel20 Computer Science Gamma\nOm Pansuriya dal651445@utdallas.edu 4692384058 0kayy0m Computer Science Epsilon\n-- 1 of 2 --\nName UTD Email Phone Number Instagram Major Class\nPranay Chintakunta dal853128@utdallas.edu 9035053595 pranaykc_7 Computer Science Epsilon\nPraneel Sreepada pss200001@utdallas.edu 469-514-5992 praneel_s7 Computer Science Delta\nPrateek Banda prb220003@utdallas.edu 4695888902 prateek_b07 Computer Science Epsilon\nRahil Islam rmi210001@utdallas.edu 512-971-9434 rahil.isl CIS Tech Delta\nRishi Ramesh Rxr230057@utdallas.edu 682-583-7587 _seyyon_ Computer science Delta\nRushil Patel rushil.patel2@utdallas.edu 469-616-6800 rushilpatell Computer Science Beta\nRuthvik Penmatsa Rvp220004@utdallas.edu 940-279-6348 Ruthvikpenmatsa Finance Delta\nSachin Selvakumar Sxs230344@utdallas.edu 469-404-2155 __sachins_ Computer Science Delta\nSahaj Dahal sxd210137@utdallas.edu 608-698-2182 sahaj_dahal10 Computer Science Delta\nSanjana Shangle sanjana.shangle@utdallas.edu 469-438-0491 sanjana.shangle Computer Science Alpha\nShraddha Gangaram sxg210196@utdallas.edu 9728060008 shraddha.gangaram Data Science Epsilon\nShreyas Ankolekar sxa210225@utdallas.edu 972-730-2458 s.ankolekar Computer Science Gamma\nSimon Beyene sbb240000@utdallas.edu 832-939-1555 simon_beyene Data Science Delta\nSiri Kishore Dola dal943756@utdallas.edu 4699200107 siri_k57 Computer Science Epsilon\nTanmayi Addanki tua220000@utdallas.edu 6083813262 tanvvayi Healthcare Management Epsilon\nVenkat Sai Eshwar Varma Sagi Vxs210103@utdallas.edu 903-505-1373 mr.venkatam Computer Science Gamma\nVignesh Selvam VAS220005@utdallas.edu 347-463-1769 vignesh_s9 CIS Tech Gamma\nViraaj Singh Vxs220100@utdallas.com 4692313364 _viraaj.singh_ Comp. Info. Syst. & Tech. Epsilon\nYeshwanth Vajinapalli Yeshwanth.Vajinapalli@UTDallas.edu 4694084162 yeshwanthvajinapalli Biochemistry Epsilon\n-- 2 of 2 --" + }, + { + "id": "spring-26-kappa-theta-pi-constitution-3279b6c7", + "title": "Spring 26 Kappa Theta Pi Constitution", + "content": "KAPPA THETA PI CONSTITUTION\nDate: 03/01/2026\nPREAMBLE\nWe, the active members of Kappa Theta Pi Professional Fraternity at the\nUniversity of Texas at Dallas establishes this Constitution to execute our purpose,\nfunctions, and progression.\nTable of Contents\n1. Preamble\n2. Article I: Organization\n○ Section 1: Name\n○ Section 2: Letters\n○ Section 3: Advisor\n○ Section 4: Funds\n3. Article II: Statement of Purpose\n4. Article III: Membership\n○ Section 1: Terminology\n○ Section 2: Eligibility\n○ Section 3: Rush\n○ Section 4: Pledging\n○ Section 5: Active Membership\n-- 1 of 32 --\n○ Section 6: Alumni\n5. Article IV: Membership Requirements\n○ Section 1: Assessments\n○ Section 2: Dues\n○ Section 3: Attendance\n○ Section 4: Professional Development\n○ Section 5: Committees and Individual Tasks\n○ Section 6: Inactive Status\n○ Section 7: Status Change\n○ Section 8: Probation\n○ Section 9: Disciplinary Strike System\n6. ARTICLE V: Bylaws\n○ Section 1: Governance\n○ Section 2: Chapter Meetings\n○ Section 3: Social Gatherings\n○ Section 4: Professional Development\n○ Section 5: Voting\n7. Article VI: Executive Board\n○ Section 1: Purpose\n○ Section 2: Management\n○ Section 3: Post-Election Transition Period\n○ Section 4: Executive Board Positions and Responsibilities\n-- 2 of 32 --\n○ Section 5: Elections\n○ Section 6: Accountability\n○ Section 7: Final Authority of the Executive Board\n○ Section 8: Director Appointments and Removal\n8. Article VII: History\n○ Section 1: Foundation\n○ Section 2: The Innovators at the University of Texas at Dallas\n○ Section 3: Meetings\n○ Section 4: Concept\n9. Article VIII: Amendments\n○ Section 1: Existence\n○ Section 2: Proposition\n○ Section 3: Voting\n10. Article IX: Ratification\n-- 3 of 32 --\nARTICLE I: Organization\nSection 1: Name\nThe name of this fraternity shall be “Kappa Theta Pi, Professional Technology\nFraternity,” henceforth referred to as “Kappa Theta Pi” or “KTP.”\nSection 2: Letters\nThe letters of this fraternity shall be ΚΘΠ, which signifies a “Love for Technology.”\nSection 3: Advisor\nOur advisor is Srimathi Srinivasan, a professor at UTD.\nSection 4: Funds\nFunds will be derived from membership dues, fundraisers, etc. The Executive\nBoard will manage funds and approve all expenses.\n-- 4 of 32 --\nARTICLE II: Statement of Purpose\n1. Build an active community of students with a shared interest in technology.\n2. Sponsor events and activities promoting intellectual, technical, professional,\nand social development.\n3. Provide career guidance to members.\n4. Foster relationships among members, students, alumni, faculty members,\nthe local community, and businesses.\n5. Provide service and philanthropy to the local community.\n6. Maintain lifelong cooperation and friendship among the members of this\nfraternity.\n7. Kappa Theta Pi is committed to a policy of equal opportunity for all persons\nand does not discriminate based on race, color, national origin, age, marital\nstatus, sex, sexual orientation, gender identity, gender expression, disability,\nreligion, height, weight, or veteran status in its membership or activities\nunless permitted by university policy for gender-specific organizations.\n-- 5 of 32 --\nARTICLE III: Membership\nSection 1: Terminology\n1. The Pledging process is the first semester of membership within the\nfraternity.\n2. A Pledge is a member undergoing the Pledging process.\n3. The Rush is the process by which the fraternity is introduced to Pledge\ncandidates and decides which candidates shall proceed into the Pledging\nprocess. A Pledge candidate undergoing the Rushing process may be called\na Rushee.\n4. An Active is a fraternity member who has successfully exited the Pledging\nprocess, is a current student at the University of Texas at Dallas (the\nUniversity), and is in otherwise good standing (see Article III) with the\nfraternity.\n5. An Inactive is a fraternity member who is not currently participating in\nfraternity requirements but will return to active status at the end of the\nsemester.\n6. An Alumnus is a former active student who has graduated from the\nuniversity and was in good standing with the fraternity in their last semester\nof enrollment.\n7. In our fraternity hierarchy, Executive Members hold the highest authority\nand are responsible for leadership, decision-making, and guiding the\nfraternity’s direction. Active Members come next, participating fully in\n-- 6 of 32 --\nevents and supporting executive decisions. Inactive Members follow as\nthose who, though temporarily not participating, retain their membership\nstatus and may re-engage. Alumni are valued for their past contributions\nand offer mentorship, while Pledges are at the introductory level, learning\nabout the fraternity and aspiring to join the active membership by\ndemonstrating commitment and respect for our values.\n8. It is crucial for pledges to respect the positions of each brother, as this\nrespect reflects their dedication to the fraternity’s values and commitment\nto becoming part of the brotherhood. Failure to show proper regard for\neach role disrupts the unity and order that define our fraternity. Any\ndisrespect towards a brother can lead to disciplinary action, as honoring\nthese roles is a fundamental expectation for pledges aspiring to join as full\nmembers.\nSection 2: Eligibility\nA Rushee who wishes to enter the Pledging process must reasonably expect to\nremain at the University for at least one more semester, excluding their Pledging\nsemester.\nSection 3: Rush\n1. At the beginning of the fall and spring semesters, the Vice President of\nMembership and Director of Rush will arrange advertising, informational\nsessions, events, and applications for potential members with assistance\nfrom involved members.\n2. Rush shall begin with informational sessions that provide an overview of\nthe fraternity to interested students.\n-- 7 of 32 --\n3. After attending an informational session, all students interested in\nbecoming members of Kappa Theta Pi must fill out an application to acquire\nclerical information and a summary of their interests.\n4. Events are for students interested in becoming active members. The Vice\nPresident of Membership and involved members will organize the rush\nevents. At each event, attendance, participation, and overall behavior will\nbe factors that determine eligibility for a bid.\n5. After the Rush process, the executive board will hold a technical and\nbehavioral interview. At least three Executive Board members will be\npresent at each interview.\n6. A selection meeting will be called within 7 days of the last interview,\ncomprising all eligible active members to determine the Pledge class. Active\nmembers must attend two Rush events to vote at the selection meeting. All\navailable members of the fraternity conduct the selection meeting, and the\nVice President of Membership or Director of Rush will present the pledges.\nVia discussions and voting, bids are assigned to potential members based\non talent and fraternity needs. The Executive Board holds the final decision\nfor all bids.\nSection 4: Pledging\n1. As Pledges of Kappa Theta Pi, they shall be required to participate in the\nevents and onboarding requirements decided by the Vice President of\nMembership to create bonds amongst other pledges and active members\nand foster commitment to KTP. Failure to complete all of the requirements,\n-- 8 of 32 --\nunless overridden by a 2/3 majority decision of the Executive Board, shall\nresult in loss of a Bid.\n2. Big and Little pairings shall be decided within 7 weeks after pledge\ninitiation. If interested, an active member shall be paired with one Pledge.\nThe Vice President of Membership shall determine secret gift offerings,\ninformal interviews, or other requirements. If the number of pledges\nexceeds the number of active members wanting a little, the actives can\nchoose to take more than one little. If there are more pledges than active\nmembers willing to take a Little, the Executive Board will determine\nalternative pairings or mentorship arrangements.\n3. Pledges are expected to interact with all active and executive members with\nthe utmost respect and professionalism. Any behavior that undermines the\ndignity of the fraternity, its members, or its leadership will be subject to\ndisciplinary action.\n4. Pledges shall be subject to a distinct disciplinary system, separate from the\n3-Strike System governing active members. This pledge-specific system, as\ndefined in the 'Pledge Code of Conduct,' is based on tracking infractions,\nand pledges do not accrue strikes as defined in Article IV, Section 9.\nDisciplinary actions are determined by the Executive Board and may include\nfines, additional requirements, or termination of the pledge process. (KTP\nCode of Conduct and Absent Guidelines).\n5. Until a Pledge becomes an Active, they shall be conducted under a strict\n'Dry' policy for the entirety of the pledge program. This Dry Pledge Rules\nprohibits any and all association with alcohol and controlled substances.\n-- 9 of 32 --\na. Prohibited activities include, but are not limited to:\ni. The consumption, possession, holding, or distribution of such\nsubstances.\nii. Knowingly being in the immediate vicinity where such\nsubstances are being consumed. * Appearing in person or in\nsocial media content (such as posts or photos) where such\nsubstances are present.\nb. Pledges are also prohibited from appearing to be under the influence\nof alcohol or controlled substances in public or on social media.\nAdditionally, pledges may not post, share, or engage with any content\ninvolving alcohol or substance use on social media platforms for the\nduration of the program.\nc. Any violation of this policy [dry pledging] will result in immediate\ndisciplinary action, which will include a formal infraction. Leading to\nan immediate meeting with the Executive Board and being exited\nfrom the fraternity.\ni. Any brother found hiding, protecting, or enabling a pledge to\nviolate the Dry Pledging Process, including but not limited to,\nthe use, possession, or appearance of alcohol or controlled\nsubstances, will be held equally accountable and subject to the\nsame disciplinary consequences deemed by the Vice President\nof Internal Affairs .\n-- 10 of 32 --\n6. At a Crossover event at the end of each semester, pledges who complete\nthe pledging process shall become active members.\n7. The pledging process is a probationary trial period intended to assess a\npledge’s character, trustworthiness, and commitment to the fraternity’s\nvalues and mission. As pledges are candidates for membership, they are not\nafforded the rights or privileges of active members as defined in this\nConstitution.\n8. The Executive Board reserves the right to revoke a pledge's bid and\nterminate their pledging process at any point, with or without cause, by a\n2/3 majority decision. This authority is separate from and in addition to the\nloss of a bid resulting from failure to complete specified requirements.\nSection 5: Active Membership\nActive members must meet all requirements stated in Article IV.\nSection 6: Alumni\n1. An Alumni Member shall be defined as an individual who has been duly\ninitiated into active membership of Kappa Theta Pi and has subsequently\ngraduated from the University of Texas at Dallas.\n2. Alumni status is granted after graduation but may be held if the individual\nwishes to remain active as a master’s student.\n3. At a Crossover event at the end of each semester, each graduating active\nmember, not on probation, shall become an honored alumni member.\n4. Alumni members may attend future fraternity events, provided there is\nsufficient space and available funding to accommodate them.\n-- 11 of 32 --\n5. An annual newsletter shall be sent to alumni members, highlighting the\nyear’s achievements and direction for the future.\n6. Prior to the Crossover event, all alumni must transfer their roles and\nresponsibilities to a successor they trust or to an individual approved by the\nExecutive Board.\n7. All alumni must cease active duties immediately following the Crossover\nevent.\n8. Alumni shall not participate in any fraternity activities or operations unless\nexplicitly approved by the Executive Board.\n9. Alumni shall not interfere with the internal affairs, decision-making\nprocesses, or operations of the fraternity unless given express permission\nby the Executive Board.\nARTICLE IV: Membership Requirements\n1. Membership shall be open to students of The University of Texas at Dallas\nregardless of sex (unless specifically exempt by law), race, color, religion,\nage, national origin, disability, or veteran status.\n2. Membership must be limited to UT Dallas students, faculty, and staff.\nSection 1: Assessments\n1. Active brothers must maintain a minimum cumulative grade point average\nof 3.0.\n-- 12 of 32 --\n2. Active members shall be able to name and identify the current Executive\nBoard.\n3. Active members shall not be a member of any other professional fraternity.\nSection 2: Dues\n1. Each semester, the Executive Board shall determine dues, and the funds\ncollected shall be used to reserve rooms for chapter meetings, fund social\nevents, pay fees, or any other expenditure for the fraternity.\n2. If there is reason to believe that funds are not being used for the benefit of\nKTP, members in charge of the finances will have an academic hearing and\nbe voted on by all members about the future of KTP.\n3. The Vice President of Finance shall collect the payments, or a different\nmember of the Executive Board voted for this role and deposit them into\nthe fraternity’s official bank account.\n4. The Executive Board may assess any additional fees at any time.\n5. If members cannot afford dues, they must contact the Executive Board in\nwriting to determine an alternative plan that the Executive Board sees fit.\nSection 3: Attendance\n1. Attendance at all chapter meetings and any event the Executive Board\ndetermines is mandatory shall be logged.\n2. Members must notify the Vice President of Membership at least 24 hours in\nadvance to receive an excused absence via the official KTP email.\n-- 13 of 32 --\n3. Excused Absences: Members are allowed two excused absences per\nsemester.\na. The specified member will be fined $15 on the third excused\nabsence.\nb. Any excused absence after that will follow this equation: Fine = 20 x\n(excused absences - 3).\nc. The Vice President of Membership will record all absences and\ntardiness after each chapter meeting. Members are responsible for\nverifying the accuracy of their attendance records and addressing any\ndiscrepancies within 24 hours of notification. Every Tardy and\nAbsence will be notified.\n4. Unexcused Absences\na. Failure to receive an excused absence will be recorded as an\nunexcused absence.\nb. There are two unexcused absences per semester.\ni. The first unexcused absence will result in a warning and a $10\nfine.\nii. The second unexcused absence is a strike and a $35 fine. Any\nother unexcused absences result in immediate probationary\nstatus. Tardiness at a chapter meeting will be counted towards\nan unexcused absence.\n5. Tardies\n-- 14 of 32 --\na. Tardiness applies to all members and is defined as arriving five\nminutes after the assigned event time.\nb. For every two tardies, a member will receive one unexcused absence.\nc. If a member is more than twenty minutes late, they will receive an\nunexcused absence.\nSection 4: Professional Development\n1. All active members shall be expected to participate in two professional\ndevelopment events per semester organized by the Vice President of\nProfessional Development.\nSection 5: Committees and Individual Tasks\n1. Vice Presidents may choose to maintain a committee throughout the\nsemester.\na. If so, the committees will have meetings and aid the Vice President.\n2. Vice Presidents who choose not to maintain a committee will be\nresponsible for actively requesting help to accomplish tasks.\na. Members should inform Vice Presidents that they are working on a\ntask.\n3. Committees may be added and dropped based on the needs of the\nfraternity.\nSection 6: Inactive\n-- 15 of 32 --\n1. Each member with active standing in the fraternity for one semester or\nmore is entitled to one semester of Inactive Status for study abroad,\ninternship, or personal reasons.\na. A member may only be inactive for more than one semester under\nspecial circumstances, which will be reviewed on a case-by-case basis\nby The Executive Board. An example of an approved exception would\nbe an extended study abroad program lasting more than one\nsemester.\n2. To acquire Inactive Status, the requesting member must fill out the Inactive\nStatus application form, located in the drive or available on request from\nThe Executive Board. The Executive Board shall determine whether or not\nto grant the status.\n3. Inactive Status requests are due by the end of the pledge bid process.\nSection 7: Status Change\n1. All activity statuses shall be finalized by the time bids are issued. Any\nmember seeking to change their activity status must initiate the process by\ncontacting the Vice President of Membership.\n2. Members are not allowed to go inactive the semester before the semester\nof their graduation. In addition members are also not allowed to go inactive\nthe semester of their graduation. Either members must stay active or must\nformally drop from the fraternity. Exceptions can be made in certain\ncircumstances with proper documentation.\n-- 16 of 32 --\n3. Once bids have been issued, the activity status of any member shall remain\nunchanged, except where the Executive Board, at its sole discretion,\ndetermines that a change is warranted.\n4. If a member needs to temporarily step back from active participation due to\na family emergency or other personal reasons, the member should reach\nout to the executive team to request a change in their status from active to\ninactive. This change is subject to the approval of the executive team, and\nsupporting documentation may be required.\n5. Similarly, if a member wishes to return to active status from inactive, they\nshould also contact the executive team for approval.\nSection 8: Probation\n1. Probation is when a member is forcibly put on inactive status and is not\nallowed participation of any capacity with the fraternity.\n2. A member can be put on probation at any period of time as deemed\nappropriate by the Executive Board. The probationary period is also\ndependent on the remediation tasks or the severity of the case.\n3. An exit interview shall be a meeting between a member on probation and\nat least two Executive Board members. The exit interview is a formal way to\ngive members an honorable way to drop out. The Executive Board can\ninitiate the exit interview process with members whenever they feel\nappropriate.\n4. A member on probation shall be unable to vote or attend any social\ngatherings unless the social event is taking place at the current residence of\nthe probated member.\n-- 17 of 32 --\n5. Members on probation are not eligible to run for any leadership positions\nSection 9: Disciplinary Strike System\n1. All Strikes will be specified in the KTP Strike System Framework.\n2. Kappa Theta Pi will operate under a 3-Strike System:\na. Strike 1: Formal warning and a $20 fine.\nb. Strike 2: Mandatory meeting with the Internal Affairs Committee to\nevaluate continued membership in Kappa Theta Pi.\nc. Strike 3: Immediate removal from the fraternity.\n3. All Active Members have the right to dispute a strike if they believe it was\nissued in error or is unjustified.\nARTICLE V: Bylaws\nSection 1: Governance\nThe laws governing Kappa Theta Pi are in this Constitution.\nSection 2: Chapter Meetings\nA weekly chapter meeting shall be held to discuss the current events and status of\nthe fraternity. Unless otherwise instructed, all active members and pledges must\nattend all chapter meetings. In order to be granted absence from a meeting,\n-- 18 of 32 --\nwritten consent by the Vice President of Membership is required by email at least\n24 hours in advance.\nSection 3: Social Gatherings\nA social gathering for members of this fraternity shall be held at least three times\nper semester. Funds from the fraternity's account shall be used to host these\nevents.\nSection 4: Professional Development\nAt least three professional development events shall occur per semester. These\nevents may include, but are not limited to, resume-building workshops,\nprofessional photo shoots, internship panels, career counseling, and corporate\nvisits.\nSection 5: Voting\nFor all voting cases, a majority vote of 2/3 of a quorum is necessary to pass the\nmotion, such as Executive Board elections and amendments to the Constitution.\nARTICLE VI: Executive Board\nSection 1: Purpose\nThe function of the Executive Board is to manage the fraternity and best serve its\nmembers.\nSection 2: Management\n1. The Executive Board shall schedule chapter meetings and events.\n-- 19 of 32 --\n2. The Executive Board may call special meetings by giving 48 hours notice to\nall active members unless circumstances deem otherwise.\n3. The President shall summon a weekly Executive Board meeting.\nSection 3: Post-Election Transition Period\n1. Following the conclusion of elections, the newly elected Executive Board\nmembers shall serve as officers-elect in a transitional training role. They will\nofficially assume the full duties and responsibilities of their respective\noffices at the conclusion of the current academic semester.\n2. Outgoing Executive Board members are required to provide shadowing\nopportunities and mentorship to their successors. This period shall include\nat least two (2) formal meetings and ongoing communication to ensure a\nsmooth transition of duties and responsibilities. The goal is to equip the\nincoming Executive Board with the necessary knowledge and skills to\ncontinue the fraternity's operations effectively.\n3. All duties, responsibilities, and official documents, including passwords,\nfinancial records, and other pertinent materials, must be fully transferred to\nthe newly elected Executive Board by the end of the transition period.\nFailure to complete the transfer may result in disciplinary action as deemed\nappropriate by the majority of members in the fraternity.\nSection 4: Executive Board Positions and Responsibilities\n1. President\na. Call all meetings to order and preside over them.\n-- 20 of 32 --\nb. Act as a liaison between this fraternity and all outside offices.\nc. Approve all outgoing correspondence.\nd. See that the Executive Board members faithfully complete their\nduties.\ne. Rule on points of order.\nf. Representing the fraternity in external affairs and maintaining\nrelationships with other committees and organizations\ng. Organize and delegate Pledge process and semester plans for\nBrothers\n2. Vice President of Finance\na. Keep accurate records and control of the fraternity’s finances.\nb. Provide financial reports when necessary.\nc. Assure that proper authorization is received before releasing any\nfunds.\nd. Collect dues and any other fees collected by the fraternity.\ne. Overseeing all philanthropic initiatives and fundraising activities\nf. Planning and budgeting for events, including those in partnerships\nwith entities\ng. Ensuring the financial stability and accountability of the fraternity\nh. Manage the Fundraising Committee (Optional).\n3. Vice President of Technology\na. Manage all web-based content, ensuring the fraternity’s online\npresence is up-to-date and secure.\n-- 21 of 32 --\nb. Routine maintenance and updates to the ktputd.com website.\nc. Providing technical advice and guidance to the fraternity.\nd. Leading or establishing a Technical Development Committee\n(optional).\ne. Work with the Vice President of Professional Development to host\ntechnology workshops and events.\nf. Oversee and plan all technical projects.\n4. Vice President of Professional Development\na. Organizing professional workshops to help members develop their\ncareers.\nb. Collaborating closely with the VP of Technology to maximize the use\nof technology in professional development efforts.\nc. Maintain relationships with alumni members, university faculty, the\nlocal community, and corporations.\nd. Arrange professional development events and guest speakers.\n5. Vice President of Membership\na. Organizing membership drives and representing the fraternity at fairs\nand SOC events.\nb. Collaborating with the VP of Internal Affairs to ensure adherence to\nregulations and standards.\n-- 22 of 32 --\nc. Facilitating the integration of pledges into the fraternity through\nrelationship building.\nd. Conducting one-on-one sessions to gather feedback and foster\ncommunication.\ne. Organizing brother dates to strengthen fraternal bonds.\nf. Holding Members accountable to their deadlines and responsibilities.\ng. Managing a comprehensive database of all fraternity information\nwith the Vice President of Internal Affairs.\nh. Organizing and executing 'Big and Little' activities to foster\nmentorship within the community.\ni. Coordinating with Vice President of Engagement to plan semesterly\nformal\nj. Record and distribute minutes of meetings.\nk. Managing the fraternity’s calendar and scheduling events\nl. Reserve spaces for chapter meetings and events\nm. Tracking attendance at fraternity events and meetings.\n6. Vice President of Social Engagement\na. Overseeing marketing strategies and campaigns.\nb. Managing and engaging with the community on various social media\nplatforms, including LinkedIn, TikTok, and Discord.\nc. Enhancing the organization's presence and engagement online.\nd. Overseeing Engagement Committee\ne. Coordinating with the Vice President of Tech and Vice President of\nProfessional Development for online promotions and activities.\n-- 23 of 32 --\nf. Coordinating with Vice President of Membership to plan semesterly\nformal\n7. Vice President of Internal Affairs\na. Overseeing the fraternity's public relations and human resources.\nb. Managing constitutional amendments and enforcing compliance.\nc. Administering the strike system and handling disciplinary procedures.\nd. Conducting one-on-one meetings ensures that members understand\ntheir responsibilities and the fraternity's expectations.\ne. Enforce strict observance of the laws and policies of this fraternity.\nf. Managing a comprehensive database of all fraternity information\nwith the Vice President of Membership.\ng. Using attendance data from the Vice President of Membership to\nenforce the disciplinary strike system.\n8. Vice President of External Affairs\na. Assume the duties of the President in their absence.\nb. Assist the President in the performance of their duties.\nc. Act as a liaison between the Chapter and all outside offices.\nd. Be an ideal representation of the values of a Member of Kappa Theta\nPi.\ne. Establish and maintain relationships with corporate sponsors,\nuniversity officials, and other professional organizations.\nf. Represent the chapter in external affairs, including recruitment fairs,\nconferences, and university events.\n-- 24 of 32 --\ng. Coordinate outreach efforts to increase chapter visibility and\nengagement within the broader community.\nh. Collaborate with the Vice President of Marketing to manage public\nrelations and social media presence.\n9. Vice President of Marketing\na. Organize and order apparel.\nb. Design and implement advertising plans.\nc. Manage the fraternity’s public image and social media presence, such\nas updating the Facebook page, Instagram account, and LinkedIn\nprofile.\nd. Ensure brand consistency across Chapter matters.\ne. Collect and address Member feedback to enhance Chapter activities\nand experiences.\nf. Keep a visual record of events, such as photos and videos.\ng. Collaborate with other Executive Board members to align marketing\nefforts with Chapter goals.\nh. Monitor and analyze social media engagement, using insights to\nimprove outreach strategies.\ni. Create and distribute promotional materials, such as flyers,\nnewsletters, and digital content.\nj. Train and oversee a marketing committee, if applicable, to assist with\npromotional efforts.\nk. Maintain an archive of past marketing materials and strategies for\nfuture reference and continuity.\n-- 25 of 32 --\nl. Innovate new ways to enhance the Chapter’s presence both on\ncampus and online.\nm. Collaborate with the Vice President of External Affairs to enhance\npublic relations and outreach efforts.\nn. Monitor engagement metrics on social media to assess the\neffectiveness of marketing strategies.\nSection 5: Elections\n1. An eligible voter in an Executive Board election must maintain Active status\non the election date.\n2. An eligible candidate for Vice President of Finance, Vice President of\nCommunication, Vice President of Technology, Vice President of\nEngagement, Vice President of Professional Development, or Vice President\nof Membership must maintain Active status on the election date.\na. If, for some circumstances, the majority of current active members\ncannot run for an executive position, newly crossed-over pledges can\nrun for an executive position.\n3. An eligible candidate for President must maintain Active status on the\nelection date and must have served on the executive board previously, in\naddition maintained Active status for at least two full semesters.\n4. A member with a probation record is not an eligible candidate for any\nExecutive Board position.\n-- 26 of 32 --\n5. An eligible voter who cannot attend the election may cast their vote by\npresenting a signed absentee ballot to the Executive Board before the start\nof the election.\n6. A majority vote of 2/3 of a quorum shall be necessary to attain an office. If a\nmajority is not reached, the candidate receiving the lowest number of votes\ncast is dropped from the ballot, and a re-vote is taken of the remaining\ncandidates until a majority is received.\n7. A petition of 1/3 of the total active members shall be necessary to raise the\nquestion of removing an officer or a member before the next chapter\nmeeting. A 2/3 majority vote is required to remove an officer from their\noffice. A 2/3 majority vote is needed to remove a member from the\nfraternity. This process is reserved for unforeseen or egregious\ncircumstances that warrant immediate removal, and it operates separately\nfrom the three-strike disciplinary system. After reading the charges, the\nimpeachment issue shall be discussed and voted upon at the meeting.\n8. An Executive Board appointment shall take place on an as-needed basis.\nThe option for voting will be available, but appointment will be the first\ncourse of action.\n9. The sitting Executive Board officers are responsible for transitioning their\nresponsibilities throughout the semester in which the election takes place.\nThe newly elected Executive Board officers shall take on their full\nresponsibilities at the end of the semester in which they are voted in.\n10.If KTP is dissolved, the remaining funds will be allocated to any current\nprojects that KTP’s brothers will carry on.\nSection 6: Accountability\n-- 27 of 32 --\nAll Executive Board actions and positions are subject to review and revision\nprovided a 2/3 majority active member quorum is in disagreement.\nSection 7: Final Authority of the Executive Board\nThe Executive Board shall hold the ultimate authority in all matters concerning the\ngovernance, operations, and decision-making of Kappa Theta Pi. All decisions\nmade by the Executive Board shall be deemed final and binding, subject to the\nprovisions of this Constitution. Any decision made by the Executive Board may be\nappealed by a petition signed by at least 25% of active members, prompting a\nvote by the full membership. The Executive Board reserves the right to review,\namend, or overrule decisions made by any committee, subcommittee, or\nindividual member of the organization.\nSection 8: Director Appointments and Removal\nThe Executive Board shall have the authority to appoint and remove directors at\nany point in the semester to address the evolving needs of the fraternity. The\nappointment of a director shall require a majority vote of the Executive Board. The\nremoval of a director shall require a 2/3 majority vote of the Executive Board.\nARTICLE VII: History\nSection 1: Foundation\nSeven Informatics students founded Kappa Theta Pi on January 10th, 2012, in Ann\nArbor at the University of Michigan. The founding class of KTP UT Dallas is listed\nbelow.\n-- 28 of 32 --\nSection 2:The Innovators* at the University of Texas at Dallas\n- Ethan Lobo: President\n- Laiba Piracha: VP of Membership\n- Rushil Patel: VP of Professional Development\n- Avani Mehrotra: VP of Social Engagement\n- Tariq Mahamid: VP of Technology\n- Arya Thombare: VP of Communications\n- Mohamed Afsar Harsath Arif: VP of Internal Affairs\n- Honorable Mention: Kairavi Pandya\n* These brothers have as much power as designated by their activity status. Meaning that if an innovator\nis an active member, then they will have active brother privileges. If an innovator is on the executive\nboard, then they will have executive privileges. Being an innovator does not grant any additional powers\nor privileges.\nSection 3: Meetings\nThe Executive Board will hold mandatory weekly meetings. General meetings will\nbe held once a week. The President may call meetings any time with a 48-hour\nnotice and emergency meetings as needed. Special meetings, meetings not\ncontained by the mandatory biweekly executive meetings and weekly general\nmeetings, will not be subject to compulsory attendance. However, attendance is\nencouraged as a quorum will not be needed to pass votes.\nSection 4: Concept\n-- 29 of 32 --\nKappa Theta Pi was initially intended to be a professional fraternity for Informatics\nstudents interested in Information Technology. The scope was soon broadened to\nencompass students of any major passionate about technology.\nARTICLE VIII: Amendments\nSection 1: Existence\nThis fraternity may occasionally pass rules and regulations regarding daily business\noperations as long as they do not contradict this Constitution and any current\nbylaws.\nSection 2: Proposition\nA 2/3 majority vote may amend bylaws. A change to a current bylaw shall be\nsubmitted in writing at an Executive Board meeting. Any active member may\npropose a change to the current bylaws.\nSection 3: Voting\nVoting on an amendment shall be done at the next chapter meeting or through an\nonline platform with a quorum in attendance.\nAll members, including the Executive Board, will hold a voting power of 1 vote.\nPledges will hold a voting power of 0 votes.\n-- 30 of 32 --\nARTICLE IX: Ratification\nAny loophole decision made by the Executive Board shall be communicated to all\nactive members within 48 hours. A petition signed by 25% of active members may\nchallenge the decision, prompting a full membership vote.\nEnabling clause: This Constitution shall go into effect on (03/01/2026) upon\napproval of all Executive board members. This constitution shall replace and\nrender null all previous constitutions, procedures, practices, and precedents for\nthis organization\nSignatures: The signatures below indicate that we, as Executive Officers, approve\nthis document.\nRole Signature Date\nPresident Mohamed Afsar Harsath Arif 3/01/2026\nVice President of External\nAffairs\nAjay Kumaran 3/01/2026\nVice President of Finance Mansi Cherukupally 3/01/2026\nVice President of Internal\nAffairs\nVignesh Selvam 3/01/2026\nVice President of\nMembership\nKavinram Senthil 3/01/2026\n-- 31 of 32 --\nVice President of Professional\nDevelopment\nAashay Vishwakarma 3/01/2026\nVice President of Social\nEngagement\nMekha Mathew 3/01/2026\nVice President of Technology Venkat Sai Eshwar Varma Sagi 3/01/2026\nVice President of Marketing Anvi Siddabhattuni 03/01/2026\n-- 32 of 32 --" + } +] diff --git a/data/pdfs/.gitkeep b/data/pdfs/.gitkeep new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/data/pdfs/.gitkeep @@ -0,0 +1 @@ + diff --git a/data/pdfs/Absent Guidelines.pdf b/data/pdfs/Absent Guidelines.pdf new file mode 100644 index 0000000..46fdd33 Binary files /dev/null and b/data/pdfs/Absent Guidelines.pdf differ diff --git a/data/pdfs/KTP Code of Conduct.pdf b/data/pdfs/KTP Code of Conduct.pdf new file mode 100644 index 0000000..dd0b818 Binary files /dev/null and b/data/pdfs/KTP Code of Conduct.pdf differ diff --git a/data/pdfs/KTP Strike System Framework.pdf b/data/pdfs/KTP Strike System Framework.pdf new file mode 100644 index 0000000..3b2702e Binary files /dev/null and b/data/pdfs/KTP Strike System Framework.pdf differ diff --git a/data/pdfs/Spring '26 KTP Contacts - S26 Active Brothers.pdf b/data/pdfs/Spring '26 KTP Contacts - S26 Active Brothers.pdf new file mode 100644 index 0000000..82687d6 Binary files /dev/null and b/data/pdfs/Spring '26 KTP Contacts - S26 Active Brothers.pdf differ diff --git a/data/pdfs/Spring 26 Kappa Theta Pi Constitution.pdf b/data/pdfs/Spring 26 Kappa Theta Pi Constitution.pdf new file mode 100644 index 0000000..8b0b51b Binary files /dev/null and b/data/pdfs/Spring 26 Kappa Theta Pi Constitution.pdf differ diff --git a/lib/rag/embeddings.ts b/lib/rag/embeddings.ts new file mode 100644 index 0000000..0a2b48e --- /dev/null +++ b/lib/rag/embeddings.ts @@ -0,0 +1,63 @@ +// /lib/rag/embedding.ts + +import { GoogleGenerativeAI } from "@google/generative-ai"; + +const EMBEDDING_MODEL = "models/gemini-embedding-exp-03-07"; +const MOCK_DIMENSION = 768; // Match Gemini's output dimension + +function normalize(vec: number[]): number[] { + const magnitude = Math.sqrt(vec.reduce((sum, v) => sum + v * v, 0)) || 1; + return vec.map((v) => v / magnitude); +} + +function mockEmbedding(text: string): number[] { + const vector = new Array(MOCK_DIMENSION).fill(0); + for (let i = 0; i < text.length; i += 1) { + const code = text.charCodeAt(i); + const slot = i % MOCK_DIMENSION; + vector[slot] += ((code % 97) + 1) / 100; + } + return normalize(vector); +} + +export class EmbeddingService { + private readonly genAI: GoogleGenerativeAI | null; + + constructor() { + const apiKey = process.env.GEMINI_API_KEY?.trim(); + this.genAI = apiKey ? new GoogleGenerativeAI(apiKey) : null; + } + + async embedText(text: string): Promise { + const clean = text.trim(); + if (!clean) return mockEmbedding("empty"); + if (!this.genAI) { + return mockEmbedding(clean); + } + try { + const model = this.genAI.getGenerativeModel({ model: EMBEDDING_MODEL }); + const result = await model.embedContent(clean); + return normalize(result.embedding.values); + } catch { + return mockEmbedding(clean); + } + } + + async embedBatch(texts: string[]): Promise { + if (texts.length === 0) return []; + if (!this.genAI) return texts.map((t) => mockEmbedding(t)); + + try { + const model = this.genAI.getGenerativeModel({ model: EMBEDDING_MODEL }); + + // Gemini has no native batch endpoint — run in parallel instead + const results = await Promise.all( + texts.map((text) => model.embedContent(text.trim() || "empty")) + ); + + return results.map((r) => normalize(r.embedding.values)); + } catch { + return texts.map((t) => mockEmbedding(t)); + } + } +} diff --git a/lib/rag/llm.ts b/lib/rag/llm.ts new file mode 100644 index 0000000..44768eb --- /dev/null +++ b/lib/rag/llm.ts @@ -0,0 +1,50 @@ +// /lib/rag/llm.ts + +import { GoogleGenerativeAI } from "@google/generative-ai"; + +const genAI = process.env.GEMINI_API_KEY + ? new GoogleGenerativeAI(process.env.GEMINI_API_KEY) + : null; + +export class LLMService { + async answer(question: string, context: string): Promise { + if (!genAI) { + return `No LLM configured. Context:\n${context}`; + } + + try { + const model = genAI.getGenerativeModel({ model: "gemini-2.5-flash" }); + + const prompt = ` +You are KTPilot, an assistant that answers questions using only the provided context. + +Rules: +- Be clear, direct, and accurate. +- Use only the provided context. Do not invent facts. +- If the context partially answers the question, give the partial answer and briefly say what is missing. +- Only reply with "I don't know based on the provided documents." when the context does not contain enough information to answer at all. +- Prefer concrete details such as names, dates, policies, and requirements when they are present. + +Context: +${context} + +Question: +${question} +`; + + const result = await model.generateContent(prompt); + return result.response.text() ?? "I don't know based on the provided documents."; + } catch (error: any) { + console.error("Gemini LLM error:", JSON.stringify(error, null, 2)); + if (error.status === 429) { + return "LLM service temporarily unavailable due to API quota limits. Please try again later."; + } + throw error; + } + } +} + +export async function generateAnswer(context: string, question: string) { + const llm = new LLMService(); + return llm.answer(question, context); +} diff --git a/lib/rag/pipeline.ts b/lib/rag/pipeline.ts new file mode 100644 index 0000000..85d8db5 --- /dev/null +++ b/lib/rag/pipeline.ts @@ -0,0 +1,129 @@ +import { readFile } from "node:fs/promises"; +import path from "node:path"; + +import { EmbeddingService } from "@/lib/rag/embeddings"; +import { LLMService } from "@/lib/rag/llm"; +import { InMemoryVectorStore, ScoredChunk, StoredChunk } from "@/lib/rag/vectorStore"; + +export interface DocumentItem { + id: string; + title: string; + content: string; +} + +interface ChunkedText { + chunkId: string; + text: string; + docId: string; + title: string; +} + +let singleton: RAGPipeline | null = null; +let initPromise: Promise | null = null; + +function cleanText(input: string): string { + return input.replace(/\r\n/g, "\n").replace(/\n{3,}/g, "\n\n").replace(/[ \t]+/g, " ").trim(); +} + +function chunkByChars(content: string, minSize = 500, maxSize = 1000): string[] { + const text = cleanText(content); + if (text.length <= maxSize) return [text]; + + const chunks: string[] = []; + let start = 0; + while (start < text.length) { + const targetEnd = Math.min(start + maxSize, text.length); + let split = text.lastIndexOf(" ", targetEnd); + if (split <= start + minSize) split = targetEnd; + const piece = text.slice(start, split).trim(); + if (piece) chunks.push(piece); + if (split >= text.length) break; + start = Math.max(0, split - 120); + } + return chunks; +} + +export class RAGPipeline { + private readonly embeddingService = new EmbeddingService(); + private readonly vectorStore = new InMemoryVectorStore(); + private readonly llm = new LLMService(); + private documents: DocumentItem[] = []; + + static async getInstance(): Promise { + if (singleton) return singleton; + if (!initPromise) { + initPromise = (async () => { + const p = new RAGPipeline(); + await p.initialize(); + singleton = p; + return p; + })(); + } + return initPromise; + } + + private async initialize(): Promise { + this.documents = await this.loadDocuments(); + const chunkObjects = this.chunkDocuments(this.documents); + const embeddings = await this.embeddingService.embedBatch(chunkObjects.map((c) => c.text)); + const storedChunks: StoredChunk[] = chunkObjects.map((chunk, index) => ({ + ...chunk, + embedding: embeddings[index], + })); + this.vectorStore.setChunks(storedChunks); + } + + listDocuments(): Array<{ id: string; title: string; length: number; contentLength: number }> { + return this.documents.map((doc) => ({ + id: doc.id, + title: doc.title, + length: doc.content.length, + contentLength: doc.content.length, + })); + } + + getDocument(id: string): DocumentItem | null { + return this.documents.find((doc) => doc.id === id) ?? null; + } + + async search(query: string, topK = 5): Promise { + const embedding = await this.embeddingService.embedText(query); + return this.vectorStore.hybridSearch(query, embedding, topK); + } + + async ask(question: string, topK = 5): Promise<{ answer: string; context: string; results: ScoredChunk[] }> { + const results = await this.search(question, Math.max(topK, 6)); + console.log("RAG results:", results.map(r => ({ score: r.score, title: r.title, preview: r.text.slice(0, 80) }))); + const context = this.buildContext(results); + const answer = await this.llm.answer(question, context); + return { answer, context, results: results.slice(0, topK) }; + } + + private buildContext(results: ScoredChunk[]): string { + if (results.length === 0) return "No matching context found."; + return results + .map((r) => `[${r.title} | ${r.chunkId}]\n${r.text}`) + .join("\n\n---\n\n"); + } + + private async loadDocuments(): Promise { + const jsonPath = path.join(process.cwd(), "data", "documents.json"); + const raw = await readFile(jsonPath, "utf-8"); + const parsed = JSON.parse(raw) as DocumentItem[]; + return parsed.map((doc) => ({ + ...doc, + content: cleanText(doc.content), + })); + } + + private chunkDocuments(documents: DocumentItem[]): ChunkedText[] { + return documents.flatMap((doc) => + chunkByChars(doc.content).map((text, index) => ({ + chunkId: `${doc.id}_chunk_${index}`, + text, + docId: doc.id, + title: doc.title, + })) + ); + } +} diff --git a/lib/rag/vectorStore.ts b/lib/rag/vectorStore.ts new file mode 100644 index 0000000..55e8ffb --- /dev/null +++ b/lib/rag/vectorStore.ts @@ -0,0 +1,90 @@ +export interface StoredChunk { + embedding: number[]; + text: string; + docId: string; + title: string; + chunkId: string; +} + +export interface ScoredChunk extends StoredChunk { + score: number; +} + +function clamp(value: number, min = 0, max = 1): number { + return Math.min(max, Math.max(min, value)); +} + +function tokenize(input: string): string[] { + return input + .toLowerCase() + .replace(/[^a-z0-9\s]/g, " ") + .split(/\s+/) + .filter((token) => token.length > 1); +} + +function uniqueOverlapCount(source: string[], target: string[]): number { + if (source.length === 0 || target.length === 0) return 0; + const targetSet = new Set(target); + return Array.from(new Set(source)).filter((token) => targetSet.has(token)).length; +} + +function cosineSimilarity(a: number[], b: number[]): number { + if (a.length !== b.length) return -1; + let dot = 0; + let normA = 0; + let normB = 0; + for (let i = 0; i < a.length; i += 1) { + dot += a[i] * b[i]; + normA += a[i] * a[i]; + normB += b[i] * b[i]; + } + const denom = Math.sqrt(normA) * Math.sqrt(normB); + if (!denom) return -1; + return dot / denom; +} + +export class InMemoryVectorStore { + private chunks: StoredChunk[] = []; + + setChunks(chunks: StoredChunk[]): void { + this.chunks = chunks; + } + + hybridSearch(queryText: string, queryEmbedding: number[], topK = 5): ScoredChunk[] { + const queryTokens = tokenize(queryText); + const loweredQuery = queryText.trim().toLowerCase(); + + return this.chunks + .map((chunk) => { + const semanticRaw = cosineSimilarity(queryEmbedding, chunk.embedding); + const semanticScore = semanticRaw < 0 ? 0 : (semanticRaw + 1) / 2; + + const titleCoverage = uniqueOverlapCount(queryTokens, tokenize(chunk.title)) / Math.max(queryTokens.length, 1); + const textCoverage = uniqueOverlapCount(queryTokens, tokenize(chunk.text)) / Math.max(queryTokens.length, 1); + const exactMatchBoost = + loweredQuery && (chunk.title.toLowerCase().includes(loweredQuery) || chunk.text.toLowerCase().includes(loweredQuery)) + ? 0.2 + : 0; + + const lexicalScore = clamp((titleCoverage * 0.45) + (textCoverage * 0.45) + exactMatchBoost); + const combinedScore = (semanticScore * 0.65) + (lexicalScore * 0.35); + + return { + ...chunk, + score: combinedScore, + }; + }) + .sort((a, b) => b.score - a.score) + .slice(0, topK); + } + + search(queryEmbedding: number[], topK = 5): ScoredChunk[] { + return this.chunks + .map((chunk) => ({ + ...chunk, + score: cosineSimilarity(queryEmbedding, chunk.embedding), + })) + .sort((a, b) => b.score - a.score) + .slice(0, topK); + } +} diff --git a/package-lock.json b/package-lock.json index b0f4ce0..e964526 100644 --- a/package-lock.json +++ b/package-lock.json @@ -10,13 +10,15 @@ "license": "ISC", "dependencies": { "@emailjs/browser": "^4.3.3", + "@google/generative-ai": "^0.24.1", "@nextui-org/navbar": "^2.0.27", "@nextui-org/react": "^2.2.10", "@nextui-org/system": "^2.0.15", "@nextui-org/theme": "^2.1.18", "@supabase/auth-helpers-nextjs": "^0.10.0", "@supabase/auth-helpers-react": "^0.5.0", - "@supabase/supabase-js": "^2.49.4", + "@supabase/ssr": "^0.9.0", + "@supabase/supabase-js": "^2.100.1", "@tailwindcss/aspect-ratio": "^0.4.2", "axios": "^1.9.0", "bcrypt": "^6.0.0", @@ -26,15 +28,17 @@ "cors": "^2.8.5", "d3": "^7.9.0", "d3-array": "^3.2.4", - "dotenv": "^16.5.0", + "dotenv": "^16.6.1", "express": "^4.21.2", "framer-motion": "^11.1.7", "js-cookie": "^3.0.5", "jsonwebtoken": "^9.0.2", + "mammoth": "^1.12.0", "next-auth": "^4.24.11", "ngrok": "^5.0.0-beta.2", - "nodemailer": "^6.10.1", + "nodemailer": "^7.0.13", "object-hash": "^3.0.0", + "pdf-parse": "^2.4.5", "react-chartjs-2": "^5.2.0", "react-icons": "^5.3.0", "react-photo-album": "^2.3.1", @@ -49,6 +53,7 @@ "devDependencies": { "@types/d3-array": "^3.2.1", "@types/node": "20.11.30", + "@types/pdf-parse": "^1.1.5", "@types/react": "18.2.73", "autoprefixer": "^10.0.1", "eslint": "^8", @@ -58,6 +63,7 @@ "react": "^18.3.1", "react-dom": "^18.3.1", "tailwindcss": "^3.4.13", + "tsx": "^4.21.0", "typescript": "5.4.5" } }, @@ -65,7 +71,6 @@ "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" @@ -126,6 +131,448 @@ "tslib": "^2.4.0" } }, + "node_modules/@esbuild/aix-ppc64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.27.7.tgz", + "integrity": "sha512-EKX3Qwmhz1eMdEJokhALr0YiD0lhQNwDqkPYyPhiSwKrh7/4KRjQc04sZ8db+5DVVnZ1LmbNDI1uAMPEUBnQPg==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.27.7.tgz", + "integrity": "sha512-jbPXvB4Yj2yBV7HUfE2KHe4GJX51QplCN1pGbYjvsyCZbQmies29EoJbkEc+vYuU5o45AfQn37vZlyXy4YJ8RQ==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.27.7.tgz", + "integrity": "sha512-62dPZHpIXzvChfvfLJow3q5dDtiNMkwiRzPylSCfriLvZeq0a1bWChrGx/BbUbPwOrsWKMn8idSllklzBy+dgQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-x64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.27.7.tgz", + "integrity": "sha512-x5VpMODneVDb70PYV2VQOmIUUiBtY3D3mPBG8NxVk5CogneYhkR7MmM3yR/uMdITLrC1ml/NV1rj4bMJuy9MCg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-arm64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.27.7.tgz", + "integrity": "sha512-5lckdqeuBPlKUwvoCXIgI2D9/ABmPq3Rdp7IfL70393YgaASt7tbju3Ac+ePVi3KDH6N2RqePfHnXkaDtY9fkw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-x64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.27.7.tgz", + "integrity": "sha512-rYnXrKcXuT7Z+WL5K980jVFdvVKhCHhUwid+dDYQpH+qu+TefcomiMAJpIiC2EM3Rjtq0sO3StMV/+3w3MyyqQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-arm64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.27.7.tgz", + "integrity": "sha512-B48PqeCsEgOtzME2GbNM2roU29AMTuOIN91dsMO30t+Ydis3z/3Ngoj5hhnsOSSwNzS+6JppqWsuhTp6E82l2w==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-x64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.27.7.tgz", + "integrity": "sha512-jOBDK5XEjA4m5IJK3bpAQF9/Lelu/Z9ZcdhTRLf4cajlB+8VEhFFRjWgfy3M1O4rO2GQ/b2dLwCUGpiF/eATNQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.27.7.tgz", + "integrity": "sha512-RkT/YXYBTSULo3+af8Ib0ykH8u2MBh57o7q/DAs3lTJlyVQkgQvlrPTnjIzzRPQyavxtPtfg0EopvDyIt0j1rA==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.27.7.tgz", + "integrity": "sha512-RZPHBoxXuNnPQO9rvjh5jdkRmVizktkT7TCDkDmQ0W2SwHInKCAV95GRuvdSvA7w4VMwfCjUiPwDi0ZO6Nfe9A==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ia32": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.27.7.tgz", + "integrity": "sha512-GA48aKNkyQDbd3KtkplYWT102C5sn/EZTY4XROkxONgruHPU72l+gW+FfF8tf2cFjeHaRbWpOYa/uRBz/Xq1Pg==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-loong64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.27.7.tgz", + "integrity": "sha512-a4POruNM2oWsD4WKvBSEKGIiWQF8fZOAsycHOt6JBpZ+JN2n2JH9WAv56SOyu9X5IqAjqSIPTaJkqN8F7XOQ5Q==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-mips64el": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.27.7.tgz", + "integrity": "sha512-KabT5I6StirGfIz0FMgl1I+R1H73Gp0ofL9A3nG3i/cYFJzKHhouBV5VWK1CSgKvVaG4q1RNpCTR2LuTVB3fIw==", + "cpu": [ + "mips64el" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ppc64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.27.7.tgz", + "integrity": "sha512-gRsL4x6wsGHGRqhtI+ifpN/vpOFTQtnbsupUF5R5YTAg+y/lKelYR1hXbnBdzDjGbMYjVJLJTd2OFmMewAgwlQ==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-riscv64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.27.7.tgz", + "integrity": "sha512-hL25LbxO1QOngGzu2U5xeXtxXcW+/GvMN3ejANqXkxZ/opySAZMrc+9LY/WyjAan41unrR3YrmtTsUpwT66InQ==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-s390x": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.27.7.tgz", + "integrity": "sha512-2k8go8Ycu1Kb46vEelhu1vqEP+UeRVj2zY1pSuPdgvbd5ykAw82Lrro28vXUrRmzEsUV0NzCf54yARIK8r0fdw==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-x64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.27.7.tgz", + "integrity": "sha512-hzznmADPt+OmsYzw1EE33ccA+HPdIqiCRq7cQeL1Jlq2gb1+OyWBkMCrYGBJ+sxVzve2ZJEVeePbLM2iEIZSxA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-arm64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.27.7.tgz", + "integrity": "sha512-b6pqtrQdigZBwZxAn1UpazEisvwaIDvdbMbmrly7cDTMFnw/+3lVxxCTGOrkPVnsYIosJJXAsILG9XcQS+Yu6w==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-x64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.27.7.tgz", + "integrity": "sha512-OfatkLojr6U+WN5EDYuoQhtM+1xco+/6FSzJJnuWiUw5eVcicbyK3dq5EeV/QHT1uy6GoDhGbFpprUiHUYggrw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-arm64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.27.7.tgz", + "integrity": "sha512-AFuojMQTxAz75Fo8idVcqoQWEHIXFRbOc1TrVcFSgCZtQfSdc1RXgB3tjOn/krRHENUB4j00bfGjyl2mJrU37A==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-x64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.27.7.tgz", + "integrity": "sha512-+A1NJmfM8WNDv5CLVQYJ5PshuRm/4cI6WMZRg1by1GwPIQPCTs1GLEUHwiiQGT5zDdyLiRM/l1G0Pv54gvtKIg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openharmony-arm64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.27.7.tgz", + "integrity": "sha512-+KrvYb/C8zA9CU/g0sR6w2RBw7IGc5J2BPnc3dYc5VJxHCSF1yNMxTV5LQ7GuKteQXZtspjFbiuW5/dOj7H4Yw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/sunos-x64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.27.7.tgz", + "integrity": "sha512-ikktIhFBzQNt/QDyOL580ti9+5mL/YZeUPKU2ivGtGjdTYoqz6jObj6nOMfhASpS4GU4Q/Clh1QtxWAvcYKamA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-arm64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.27.7.tgz", + "integrity": "sha512-7yRhbHvPqSpRUV7Q20VuDwbjW5kIMwTHpptuUzV+AA46kiPze5Z7qgt6CLCK3pWFrHeNfDd1VKgyP4O+ng17CA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-ia32": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.27.7.tgz", + "integrity": "sha512-SmwKXe6VHIyZYbBLJrhOoCJRB/Z1tckzmgTLfFYOfpMAx63BJEaL9ExI8x7v0oAO3Zh6D/Oi1gVxEYr5oUCFhw==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-x64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.27.7.tgz", + "integrity": "sha512-56hiAJPhwQ1R4i+21FVF7V8kSD5zZTdHcVuRFMW0hn753vVfQN8xlx4uOPT4xoGH0Z/oVATuR82AiqSTDIpaHg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, "node_modules/@eslint-community/eslint-utils": { "version": "4.7.0", "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.7.0.tgz", @@ -265,6 +712,15 @@ "tslib": "^2.8.0" } }, + "node_modules/@google/generative-ai": { + "version": "0.24.1", + "resolved": "https://registry.npmjs.org/@google/generative-ai/-/generative-ai-0.24.1.tgz", + "integrity": "sha512-MqO+MLfM6kjxcKoy0p1wRzG3b4ZZXtPI+z2IE26UogS2Cm/XHO+7gGRBh6gcJsOiIVoH93UwKvW4HdgiOZCy9Q==", + "license": "Apache-2.0", + "engines": { + "node": ">=18.0.0" + } + }, "node_modules/@humanwhocodes/config-array": { "version": "0.13.0", "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.13.0.tgz", @@ -344,7 +800,6 @@ "version": "8.0.2", "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", "integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==", - "dev": true, "license": "ISC", "dependencies": { "string-width": "^5.1.2", @@ -362,7 +817,6 @@ "version": "6.2.0", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.2.0.tgz", "integrity": "sha512-TKY5pyBkHyADOPYlRT9Lx6F544mPl0vS5Ew7BJ45hA08Q+t3GjbueLliBWN3sMICk6+y7HdyxSzC4bWS8baBdg==", - "dev": true, "license": "MIT", "engines": { "node": ">=12" @@ -375,7 +829,6 @@ "version": "7.1.0", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", - "dev": true, "license": "MIT", "dependencies": { "ansi-regex": "^6.0.1" @@ -391,7 +844,6 @@ "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", @@ -402,7 +854,6 @@ "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" @@ -412,14 +863,12 @@ "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.30", "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.30.tgz", "integrity": "sha512-GQ7Nw5G2lTu/BtHTKfXhKHok2WGetd4XYcVKGx00SjAk8GMwgJM3zr6zORiPGuOE+/vkc90KtTosSSvaCjKb2Q==", - "dev": true, "license": "MIT", "dependencies": { "@jridgewell/resolve-uri": "^3.1.0", @@ -432,6 +881,190 @@ "integrity": "sha512-M5UknZPHRu3DEDWoipU6sE8PdkZ6Z/S+v4dD+Ke8IaNlpdSQah50lz1KtcFBa2vsdOnwbbnxJwVM4wty6udA5w==", "license": "MIT" }, + "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/@napi-rs/wasm-runtime": { "version": "0.2.12", "resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-0.2.12.tgz", @@ -448,8 +1081,7 @@ "node_modules/@next/env": { "version": "14.2.35", "resolved": "https://registry.npmjs.org/@next/env/-/env-14.2.35.tgz", - "integrity": "sha512-DuhvCtj4t9Gwrx80dmz2F4t/zKQ4ktN8WrMwOuVzkJfBilwAwGr6v16M5eI8yCuZ63H9TTuEU09Iu2HqkzFPVQ==", - "dev": true + "integrity": "sha512-DuhvCtj4t9Gwrx80dmz2F4t/zKQ4ktN8WrMwOuVzkJfBilwAwGr6v16M5eI8yCuZ63H9TTuEU09Iu2HqkzFPVQ==" }, "node_modules/@next/eslint-plugin-next": { "version": "14.1.3", @@ -468,7 +1100,6 @@ "cpu": [ "arm64" ], - "dev": true, "optional": true, "os": [ "darwin" @@ -484,7 +1115,6 @@ "cpu": [ "x64" ], - "dev": true, "optional": true, "os": [ "darwin" @@ -500,7 +1130,6 @@ "cpu": [ "arm64" ], - "dev": true, "optional": true, "os": [ "linux" @@ -516,7 +1145,6 @@ "cpu": [ "arm64" ], - "dev": true, "optional": true, "os": [ "linux" @@ -532,7 +1160,6 @@ "cpu": [ "x64" ], - "dev": true, "optional": true, "os": [ "linux" @@ -548,7 +1175,6 @@ "cpu": [ "x64" ], - "dev": true, "optional": true, "os": [ "linux" @@ -564,7 +1190,6 @@ "cpu": [ "arm64" ], - "dev": true, "optional": true, "os": [ "win32" @@ -580,7 +1205,6 @@ "cpu": [ "ia32" ], - "dev": true, "optional": true, "os": [ "win32" @@ -596,7 +1220,6 @@ "cpu": [ "x64" ], - "dev": true, "optional": true, "os": [ "win32" @@ -2149,7 +2772,6 @@ "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", @@ -2163,7 +2785,6 @@ "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" @@ -2173,7 +2794,6 @@ "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", @@ -2206,7 +2826,6 @@ "version": "0.11.0", "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", "integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==", - "dev": true, "license": "MIT", "optional": true, "engines": { @@ -4277,84 +4896,120 @@ } }, "node_modules/@supabase/auth-js": { - "version": "2.71.1", - "resolved": "https://registry.npmjs.org/@supabase/auth-js/-/auth-js-2.71.1.tgz", - "integrity": "sha512-mMIQHBRc+SKpZFRB2qtupuzulaUhFYupNyxqDj5Jp/LyPvcWvjaJzZzObv6URtL/O6lPxkanASnotGtNpS3H2Q==", + "version": "2.100.1", + "resolved": "https://registry.npmjs.org/@supabase/auth-js/-/auth-js-2.100.1.tgz", + "integrity": "sha512-c5FB4nrG7cs1mLSzFGuIVl2iR2YO5XkSJ96uF4zubYm8YDn71XOi2emE9sBm/avfGCj61jaRBLOvxEAVnpys0Q==", "license": "MIT", "dependencies": { - "@supabase/node-fetch": "^2.6.14" + "tslib": "2.8.1" + }, + "engines": { + "node": ">=20.0.0" } }, "node_modules/@supabase/functions-js": { - "version": "2.4.5", - "resolved": "https://registry.npmjs.org/@supabase/functions-js/-/functions-js-2.4.5.tgz", - "integrity": "sha512-v5GSqb9zbosquTo6gBwIiq7W9eQ7rE5QazsK/ezNiQXdCbY+bH8D9qEaBIkhVvX4ZRW5rP03gEfw5yw9tiq4EQ==", - "license": "MIT", - "dependencies": { - "@supabase/node-fetch": "^2.6.14" - } - }, - "node_modules/@supabase/node-fetch": { - "version": "2.6.15", - "resolved": "https://registry.npmjs.org/@supabase/node-fetch/-/node-fetch-2.6.15.tgz", - "integrity": "sha512-1ibVeYUacxWYi9i0cf5efil6adJ9WRyZBLivgjs+AUpewx1F3xPi7gLgaASI2SmIQxPoCEjAsLAzKPgMJVgOUQ==", + "version": "2.100.1", + "resolved": "https://registry.npmjs.org/@supabase/functions-js/-/functions-js-2.100.1.tgz", + "integrity": "sha512-mo8QheoV4KR+wSubtyEWhZUxWnCM7YZ23TncccMAlbWAHb8YTDqRGRm9IalWCAswniKyud6buZCk9snRqI86KA==", "license": "MIT", "dependencies": { - "whatwg-url": "^5.0.0" + "tslib": "2.8.1" }, "engines": { - "node": "4.x || >=6.0.0" + "node": ">=20.0.0" } }, + "node_modules/@supabase/phoenix": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/@supabase/phoenix/-/phoenix-0.4.0.tgz", + "integrity": "sha512-RHSx8bHS02xwfHdAbX5Lpbo6PXbgyf7lTaXTlwtFDPwOIw64NnVRwFAXGojHhjtVYI+PEPNSWwkL90f4agN3bw==", + "license": "MIT" + }, "node_modules/@supabase/postgrest-js": { - "version": "1.19.4", - "resolved": "https://registry.npmjs.org/@supabase/postgrest-js/-/postgrest-js-1.19.4.tgz", - "integrity": "sha512-O4soKqKtZIW3olqmbXXbKugUtByD2jPa8kL2m2c1oozAO11uCcGrRhkZL0kVxjBLrXHE0mdSkFsMj7jDSfyNpw==", + "version": "2.100.1", + "resolved": "https://registry.npmjs.org/@supabase/postgrest-js/-/postgrest-js-2.100.1.tgz", + "integrity": "sha512-OIh4mOSo2LdqF2kox76OAPDtcSs+PwKABJOjc6plUV4/LXhFEsI2uwdEEIs7K7fd141qehWEVl/Y+Ts0fNvYsw==", "license": "MIT", "dependencies": { - "@supabase/node-fetch": "^2.6.14" + "tslib": "2.8.1" + }, + "engines": { + "node": ">=20.0.0" } }, "node_modules/@supabase/realtime-js": { - "version": "2.15.1", - "resolved": "https://registry.npmjs.org/@supabase/realtime-js/-/realtime-js-2.15.1.tgz", - "integrity": "sha512-edRFa2IrQw50kNntvUyS38hsL7t2d/psah6om6aNTLLcWem0R6bOUq7sk7DsGeSlNfuwEwWn57FdYSva6VddYw==", + "version": "2.100.1", + "resolved": "https://registry.npmjs.org/@supabase/realtime-js/-/realtime-js-2.100.1.tgz", + "integrity": "sha512-FHuRWPX4qZQ4x+0Q+ZrKaBZnOiVGiwsgiAUJM98pYRib1yeaE/fOM1lZ1ozd+4gA8Udw23OyaD8SxKS5mT5NYw==", "license": "MIT", "dependencies": { - "@supabase/node-fetch": "^2.6.13", - "@types/phoenix": "^1.6.6", + "@supabase/phoenix": "^0.4.0", "@types/ws": "^8.18.1", + "tslib": "2.8.1", "ws": "^8.18.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@supabase/ssr": { + "version": "0.9.0", + "resolved": "https://registry.npmjs.org/@supabase/ssr/-/ssr-0.9.0.tgz", + "integrity": "sha512-UFY6otYV3yqCgV+AyHj80vNkTvbf1Gas2LW4dpbQ4ap6p6v3eB2oaDfcI99jsuJzwVBCFU4BJI+oDYyhNk1z0Q==", + "license": "MIT", + "dependencies": { + "cookie": "^1.0.2" + }, + "peerDependencies": { + "@supabase/supabase-js": "^2.97.0" + } + }, + "node_modules/@supabase/ssr/node_modules/cookie": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-1.1.1.tgz", + "integrity": "sha512-ei8Aos7ja0weRpFzJnEA9UHJ/7XQmqglbRwnf2ATjcB9Wq874VKH9kfjjirM6UhU2/E5fFYadylyhFldcqSidQ==", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" } }, "node_modules/@supabase/storage-js": { - "version": "2.11.0", - "resolved": "https://registry.npmjs.org/@supabase/storage-js/-/storage-js-2.11.0.tgz", - "integrity": "sha512-Y+kx/wDgd4oasAgoAq0bsbQojwQ+ejIif8uczZ9qufRHWFLMU5cODT+ApHsSrDufqUcVKt+eyxtOXSkeh2v9ww==", + "version": "2.100.1", + "resolved": "https://registry.npmjs.org/@supabase/storage-js/-/storage-js-2.100.1.tgz", + "integrity": "sha512-x9xpEIoWM4xKiAlwfWTgHPSN6N4Y0aS4FVU4F6ZPbq7Gayw08SrtC6/YH/gOr8CjXQr0HxXYXDop2xGTSjubYA==", "license": "MIT", "dependencies": { - "@supabase/node-fetch": "^2.6.14" + "iceberg-js": "^0.8.1", + "tslib": "2.8.1" + }, + "engines": { + "node": ">=20.0.0" } }, "node_modules/@supabase/supabase-js": { - "version": "2.55.0", - "resolved": "https://registry.npmjs.org/@supabase/supabase-js/-/supabase-js-2.55.0.tgz", - "integrity": "sha512-Y1uV4nEMjQV1x83DGn7+Z9LOisVVRlY1geSARrUHbXWgbyKLZ6/08dvc0Us1r6AJ4tcKpwpCZWG9yDQYo1JgHg==", + "version": "2.100.1", + "resolved": "https://registry.npmjs.org/@supabase/supabase-js/-/supabase-js-2.100.1.tgz", + "integrity": "sha512-CAeFm5sfX8sbTzxoxRafhohreIzl9a7R6qHTck3MrgTqm5M5g/u0IHfEKYzI9w/17r8NINl8UZrw2i08wrO7Iw==", "license": "MIT", "dependencies": { - "@supabase/auth-js": "2.71.1", - "@supabase/functions-js": "2.4.5", - "@supabase/node-fetch": "2.6.15", - "@supabase/postgrest-js": "1.19.4", - "@supabase/realtime-js": "2.15.1", - "@supabase/storage-js": "^2.10.4" + "@supabase/auth-js": "2.100.1", + "@supabase/functions-js": "2.100.1", + "@supabase/postgrest-js": "2.100.1", + "@supabase/realtime-js": "2.100.1", + "@supabase/storage-js": "2.100.1" + }, + "engines": { + "node": ">=20.0.0" } }, "node_modules/@swc/counter": { "version": "0.1.3", "resolved": "https://registry.npmjs.org/@swc/counter/-/counter-0.1.3.tgz", "integrity": "sha512-e2BR4lsJkkRlKZ/qCHPw9ZaSxc0MVUd7gtbtaB7aMvHeJVYe8sOB8DBZkP2DtISHGSku9sCK6T6cnY0CtXrOCQ==", - "dev": true, "license": "Apache-2.0" }, "node_modules/@swc/helpers": { @@ -4742,24 +5397,28 @@ "undici-types": "~5.26.4" } }, - "node_modules/@types/phoenix": { - "version": "1.6.6", - "resolved": "https://registry.npmjs.org/@types/phoenix/-/phoenix-1.6.6.tgz", - "integrity": "sha512-PIzZZlEppgrpoT2QgbnDU+MMzuR6BbCjllj0bM70lWoejMeNJAxCchxnv7J3XFkI8MpygtRpzXrIlmWUBclP5A==", - "license": "MIT" + "node_modules/@types/pdf-parse": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/@types/pdf-parse/-/pdf-parse-1.1.5.tgz", + "integrity": "sha512-kBfrSXsloMnUJOKi25s3+hRmkycHfLK6A09eRGqF/N8BkQoPUmaCr+q8Cli5FnfohEz/rsv82zAiPz/LXtOGhA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*" + } }, "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, + "devOptional": true, "license": "MIT" }, "node_modules/@types/react": { "version": "18.2.73", "resolved": "https://registry.npmjs.org/@types/react/-/react-18.2.73.tgz", "integrity": "sha512-XcGdod0Jjv84HOC7N5ziY3x+qL0AfmubvKOZ9hJjJ2yd5EE+KYjWhdOjt387e9HPheHkdggF9atTifMRtyAaRA==", - "dev": true, + "devOptional": true, "license": "MIT", "dependencies": { "@types/prop-types": "*", @@ -5204,6 +5863,15 @@ "win32" ] }, + "node_modules/@xmldom/xmldom": { + "version": "0.8.12", + "resolved": "https://registry.npmjs.org/@xmldom/xmldom/-/xmldom-0.8.12.tgz", + "integrity": "sha512-9k/gHF6n/pAi/9tqr3m3aqkuiNosYTurLLUtc7xQ9sxB/wm7WPygCv8GYa6mS0fLJEHhqMC1ATYhz++U/lRHqg==", + "license": "MIT", + "engines": { + "node": ">=10.0.0" + } + }, "node_modules/accepts": { "version": "1.3.8", "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", @@ -5261,7 +5929,6 @@ "version": "5.0.1", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "dev": true, "license": "MIT", "engines": { "node": ">=8" @@ -5271,7 +5938,6 @@ "version": "4.3.0", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, "license": "MIT", "dependencies": { "color-convert": "^2.0.1" @@ -5287,14 +5953,12 @@ "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", @@ -5308,7 +5972,6 @@ "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" @@ -5321,7 +5984,6 @@ "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/argparse": { @@ -5627,7 +6289,26 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", - "dev": true, + "license": "MIT" + }, + "node_modules/base64-js": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", + "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", + "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/bcrypt": { @@ -5648,7 +6329,6 @@ "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" @@ -5657,6 +6337,12 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/bluebird": { + "version": "3.4.7", + "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.4.7.tgz", + "integrity": "sha512-iD3898SR7sWVRHbiQv+sHUtHnMvC1o3nW5rAcqnq3uOn07DSAppZYUkIGslDz6gXC7HfunPe7YVBgoEJASPcHA==", + "license": "MIT" + }, "node_modules/body-parser": { "version": "1.20.4", "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.4.tgz", @@ -5749,7 +6435,6 @@ "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" @@ -5810,7 +6495,6 @@ "version": "1.6.0", "resolved": "https://registry.npmjs.org/busboy/-/busboy-1.6.0.tgz", "integrity": "sha512-8SFQbg/0hQ9xy3UNTB0YEnsNBbWfhf7RtnzpL7TkBiTBRfrQ9Fxcnz7VJsleJpyp6rVLvXiuORqjlHi5q+PYuA==", - "dev": true, "dependencies": { "streamsearch": "^1.1.0" }, @@ -5915,17 +6599,15 @@ "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.30001735", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001735.tgz", - "integrity": "sha512-EV/laoX7Wq2J9TQlyIXRxTJqIw4sxfXS4OYgudGxBYRuTv0q7AM6yMEpU/Vo1I94thg9U6EZ2NfZx9GJq83u7w==", - "dev": true, + "version": "1.0.30001790", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001790.tgz", + "integrity": "sha512-bOoxfJPyYo+ds6W0YfptaCWbFnJYjh2Y1Eow5lRv+vI2u8ganPZqNm1JwNh0t2ELQCqIWg4B3dWEusgAmsoyOw==", "funding": [ { "type": "opencollective", @@ -5983,7 +6665,6 @@ "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", @@ -6008,7 +6689,6 @@ "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" @@ -6033,7 +6713,6 @@ "version": "0.0.1", "resolved": "https://registry.npmjs.org/client-only/-/client-only-0.0.1.tgz", "integrity": "sha512-IV3Ou0jSMzZrd3pZ48nLkT9DA7Ag1pnPzaiQhpW7c3RbcqqzvzzVu+L8gfqMp/8IM2MQtSiqaCxrrcfu8I8rMA==", - "dev": true, "license": "MIT" }, "node_modules/clone-response": { @@ -6186,6 +6865,12 @@ "integrity": "sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==", "license": "MIT" }, + "node_modules/core-util-is": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz", + "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==", + "license": "MIT" + }, "node_modules/cors": { "version": "2.8.5", "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.5.tgz", @@ -6203,7 +6888,6 @@ "version": "7.0.6", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", - "dev": true, "license": "MIT", "dependencies": { "path-key": "^3.1.0", @@ -6218,7 +6902,6 @@ "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" @@ -6852,9 +7535,14 @@ "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/dingbat-to-unicode": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/dingbat-to-unicode/-/dingbat-to-unicode-1.0.1.tgz", + "integrity": "sha512-98l0sW87ZT58pU4i61wa2OHwxbiYSbuxsCBozaVnYX2iCnr3bLM3fIes1/ej7h1YdOKuKt/MLs706TVnALA65w==", + "license": "BSD-2-Clause" + }, "node_modules/dir-glob": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", @@ -6872,7 +7560,6 @@ "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/doctrine": { @@ -6910,6 +7597,15 @@ "url": "https://dotenvx.com" } }, + "node_modules/duck": { + "version": "0.1.12", + "resolved": "https://registry.npmjs.org/duck/-/duck-0.1.12.tgz", + "integrity": "sha512-wkctla1O6VfP89gQ+J/yDesM0S7B7XLXjKGzXxMDVFg7uEn706niAtyYovKbyq1oT9YwDcly721/iUWoc8MVRg==", + "license": "BSD", + "dependencies": { + "underscore": "^1.13.1" + } + }, "node_modules/dunder-proto": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", @@ -6928,7 +7624,6 @@ "version": "0.2.0", "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==", - "dev": true, "license": "MIT" }, "node_modules/ecdsa-sig-formatter": { @@ -6957,7 +7652,6 @@ "version": "9.2.2", "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", - "dev": true, "license": "MIT" }, "node_modules/encodeurl": { @@ -7151,6 +7845,48 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/esbuild": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.27.7.tgz", + "integrity": "sha512-IxpibTjyVnmrIQo5aqNpCgoACA/dTKLTlhMHihVHhdkxKyPO1uBBthumT0rdHmcsk9uMonIWS0m4FljWzILh3w==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.27.7", + "@esbuild/android-arm": "0.27.7", + "@esbuild/android-arm64": "0.27.7", + "@esbuild/android-x64": "0.27.7", + "@esbuild/darwin-arm64": "0.27.7", + "@esbuild/darwin-x64": "0.27.7", + "@esbuild/freebsd-arm64": "0.27.7", + "@esbuild/freebsd-x64": "0.27.7", + "@esbuild/linux-arm": "0.27.7", + "@esbuild/linux-arm64": "0.27.7", + "@esbuild/linux-ia32": "0.27.7", + "@esbuild/linux-loong64": "0.27.7", + "@esbuild/linux-mips64el": "0.27.7", + "@esbuild/linux-ppc64": "0.27.7", + "@esbuild/linux-riscv64": "0.27.7", + "@esbuild/linux-s390x": "0.27.7", + "@esbuild/linux-x64": "0.27.7", + "@esbuild/netbsd-arm64": "0.27.7", + "@esbuild/netbsd-x64": "0.27.7", + "@esbuild/openbsd-arm64": "0.27.7", + "@esbuild/openbsd-x64": "0.27.7", + "@esbuild/openharmony-arm64": "0.27.7", + "@esbuild/sunos-x64": "0.27.7", + "@esbuild/win32-arm64": "0.27.7", + "@esbuild/win32-ia32": "0.27.7", + "@esbuild/win32-x64": "0.27.7" + } + }, "node_modules/escalade": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", @@ -7742,7 +8478,6 @@ "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", @@ -7759,7 +8494,6 @@ "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" @@ -7786,7 +8520,6 @@ "version": "1.19.1", "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.19.1.tgz", "integrity": "sha512-GwLTyxkCXjXbxqIhTsMI2Nui8huMPtnxg7krajPJAjnEG/iiOS7i+zCtWGZR9G0NBKbXKh6X9m9UIsYX/N6vvQ==", - "dev": true, "license": "ISC", "dependencies": { "reusify": "^1.0.4" @@ -7836,7 +8569,6 @@ "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" @@ -7966,7 +8698,6 @@ "version": "3.3.1", "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.1.tgz", "integrity": "sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==", - "dev": true, "license": "ISC", "dependencies": { "cross-spawn": "^7.0.6", @@ -8064,7 +8795,6 @@ "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, @@ -8202,7 +8932,6 @@ "version": "10.3.10", "resolved": "https://registry.npmjs.org/glob/-/glob-10.3.10.tgz", "integrity": "sha512-fa46+tv1Ak0UPK1TOy/pZrIybNNt4HCv7SDzwyfiOZkvZLEbjsZkJBPtDHVshZjbecAoAGSC20MjLDG/qr679g==", - "dev": true, "license": "ISC", "dependencies": { "foreground-child": "^3.1.0", @@ -8225,7 +8954,6 @@ "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" @@ -8238,7 +8966,6 @@ "version": "2.0.2", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", - "dev": true, "license": "MIT", "dependencies": { "balanced-match": "^1.0.0" @@ -8248,7 +8975,6 @@ "version": "9.0.5", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", - "dev": true, "license": "ISC", "dependencies": { "brace-expansion": "^2.0.1" @@ -8355,7 +9081,6 @@ "version": "4.2.11", "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", - "dev": true, "license": "ISC" }, "node_modules/graphemer": { @@ -8498,6 +9223,15 @@ "node": ">=10.19.0" } }, + "node_modules/iceberg-js": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/iceberg-js/-/iceberg-js-0.8.1.tgz", + "integrity": "sha512-1dhVQZXhcHje7798IVM+xoo/1ZdVfzOMIc8/rgVSijRK38EDqOJoGula9N/8ZI5RD8QTxNQtK/Gozpr+qUqRRA==", + "license": "MIT", + "engines": { + "node": ">=20.0.0" + } + }, "node_modules/iconv-lite": { "version": "0.6.3", "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", @@ -8520,6 +9254,12 @@ "node": ">= 4" } }, + "node_modules/immediate": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/immediate/-/immediate-3.0.6.tgz", + "integrity": "sha512-XXOFtyqDjNDAQxVfYxuF7g9Il/IbWmmlQg2MYKOH8ExIT1qg6xc4zyS3HaEEATgs1btfzxq15ciUiY7gjSXRGQ==", + "license": "MIT" + }, "node_modules/import-fresh": { "version": "3.3.1", "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.1.tgz", @@ -8684,7 +9424,6 @@ "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" @@ -8737,7 +9476,6 @@ "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" @@ -8788,7 +9526,6 @@ "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" @@ -8814,7 +9551,6 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", - "dev": true, "license": "MIT", "engines": { "node": ">=8" @@ -8843,7 +9579,6 @@ "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" @@ -8882,7 +9617,6 @@ "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" @@ -9071,7 +9805,6 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", - "dev": true, "license": "ISC" }, "node_modules/iterator.prototype": { @@ -9096,7 +9829,6 @@ "version": "2.3.6", "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-2.3.6.tgz", "integrity": "sha512-N3yCS/NegsOBokc8GAdM8UcmfsKiSS8cipheD/nivzr700H+nsMOxJjQnvwOcRYVuFkdH0wGUvW2WbXGmrZGbQ==", - "dev": true, "license": "BlueOak-1.0.0", "dependencies": { "@isaacs/cliui": "^8.0.2" @@ -9115,7 +9847,6 @@ "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" @@ -9228,6 +9959,18 @@ "node": ">=4.0" } }, + "node_modules/jszip": { + "version": "3.10.1", + "resolved": "https://registry.npmjs.org/jszip/-/jszip-3.10.1.tgz", + "integrity": "sha512-xXDvecyTpGLrqFrvkrUSoxxfJI5AH7U8zxxtVclpsUtMCq4JQ290LY8AW5c7Ggnr/Y/oK+bQMbqK2qmtk3pN4g==", + "license": "(MIT OR GPL-3.0-or-later)", + "dependencies": { + "lie": "~3.3.0", + "pako": "~1.0.2", + "readable-stream": "~2.3.6", + "setimmediate": "^1.0.5" + } + }, "node_modules/jwa": { "version": "1.4.2", "resolved": "https://registry.npmjs.org/jwa/-/jwa-1.4.2.tgz", @@ -9291,11 +10034,19 @@ "node": ">= 0.8.0" } }, + "node_modules/lie": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/lie/-/lie-3.3.0.tgz", + "integrity": "sha512-UaiMJzeWRlEujzAuw5LokY1L5ecNQYZKfmyZ9L7wDHb/p5etKaxXhohBcrw0EYby+G/NA52vRSN4N39dxHAIwQ==", + "license": "MIT", + "dependencies": { + "immediate": "~3.0.5" + } + }, "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" @@ -9308,7 +10059,6 @@ "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/locate-path": { @@ -9399,6 +10149,17 @@ "loose-envify": "cli.js" } }, + "node_modules/lop": { + "version": "0.4.2", + "resolved": "https://registry.npmjs.org/lop/-/lop-0.4.2.tgz", + "integrity": "sha512-RefILVDQ4DKoRZsJ4Pj22TxE3omDO47yFpkIBoDKzkqPRISs5U1cnAdg/5583YPkWPaLIYHOKRMQSvjFsO26cw==", + "license": "BSD-2-Clause", + "dependencies": { + "duck": "^0.1.12", + "option": "~0.2.1", + "underscore": "^1.13.1" + } + }, "node_modules/lowercase-keys": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-2.0.0.tgz", @@ -9420,6 +10181,39 @@ "node": ">=10" } }, + "node_modules/mammoth": { + "version": "1.12.0", + "resolved": "https://registry.npmjs.org/mammoth/-/mammoth-1.12.0.tgz", + "integrity": "sha512-cwnK1RIcRdDMi2HRx2EXGYlxqIEh0Oo3bLhorgnsVJi2UkbX1+jKxuBNR9PC5+JaX7EkmJxFPmo6mjLpqShI2w==", + "license": "BSD-2-Clause", + "dependencies": { + "@xmldom/xmldom": "^0.8.6", + "argparse": "~1.0.3", + "base64-js": "^1.5.1", + "bluebird": "~3.4.0", + "dingbat-to-unicode": "^1.0.1", + "jszip": "^3.7.1", + "lop": "^0.4.2", + "path-is-absolute": "^1.0.0", + "underscore": "^1.13.1", + "xmlbuilder": "^10.0.0" + }, + "bin": { + "mammoth": "bin/mammoth" + }, + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/mammoth/node_modules/argparse": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", + "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "license": "MIT", + "dependencies": { + "sprintf-js": "~1.0.2" + } + }, "node_modules/math-intrinsics": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", @@ -9451,7 +10245,6 @@ "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" @@ -9470,7 +10263,6 @@ "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", @@ -9484,7 +10276,6 @@ "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" @@ -9562,7 +10353,6 @@ "version": "7.1.2", "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz", "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==", - "dev": true, "license": "ISC", "engines": { "node": ">=16 || 14 >=14.17" @@ -9593,7 +10383,6 @@ "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", @@ -9605,7 +10394,6 @@ "version": "3.3.11", "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz", "integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==", - "dev": true, "funding": [ { "type": "github", @@ -9656,7 +10444,6 @@ "version": "14.2.35", "resolved": "https://registry.npmjs.org/next/-/next-14.2.35.tgz", "integrity": "sha512-KhYd2Hjt/O1/1aZVX3dCwGXM1QmOV4eNM2UTacK5gipDdPN/oHHK/4oVGy7X8GMfPMsUTUEmGlsy0EY1YGAkig==", - "dev": true, "dependencies": { "@next/env": "14.2.35", "@swc/helpers": "0.5.5", @@ -9737,7 +10524,6 @@ "version": "0.5.5", "resolved": "https://registry.npmjs.org/@swc/helpers/-/helpers-0.5.5.tgz", "integrity": "sha512-KGYxvIOXcceOAbEk4bi/dVLEK9z8sZ0uBB3Il5b1rhfClSpcX0yfRO0KmTkqR2cnQDymwLB+25ZyMzICg/cm/A==", - "dev": true, "license": "Apache-2.0", "dependencies": { "@swc/counter": "^0.1.3", @@ -9748,7 +10534,6 @@ "version": "8.4.31", "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.31.tgz", "integrity": "sha512-PS08Iboia9mts/2ygV3eLpY5ghnUcfLV/EXTOW1E2qYxJKGGBUtNjN76FYHnMs36RmARn41bC0AZmn+rR0OVpQ==", - "dev": true, "funding": [ { "type": "opencollective", @@ -9824,9 +10609,9 @@ "license": "MIT" }, "node_modules/nodemailer": { - "version": "6.10.1", - "resolved": "https://registry.npmjs.org/nodemailer/-/nodemailer-6.10.1.tgz", - "integrity": "sha512-Z+iLaBGVaSjbIzQ4pX6XV41HrooLsQ10ZWPUehGmuantvzWoDVBnmsdUcOIDM1t+yPor5pDhVlDESgOMEGxhHA==", + "version": "7.0.13", + "resolved": "https://registry.npmjs.org/nodemailer/-/nodemailer-7.0.13.tgz", + "integrity": "sha512-PNDFSJdP+KFgdsG3ZzMXCgquO7I6McjY2vlqILjtJd0hy8wEvtugS9xKRF2NWlPNGxvLCXlTNIae4serI7dinw==", "license": "MIT-0", "engines": { "node": ">=6.0.0" @@ -9836,7 +10621,6 @@ "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" @@ -10054,6 +10838,12 @@ "node": ">= 6" } }, + "node_modules/option": { + "version": "0.2.4", + "resolved": "https://registry.npmjs.org/option/-/option-0.2.4.tgz", + "integrity": "sha512-pkEqbDyl8ou5cpq+VsnQbe/WlEy5qS7xPzMS1U55OCG9KPvwFD46zDbxQIj3egJSFc3D+XhYOPUzz49zQAVy7A==", + "license": "BSD-2-Clause" + }, "node_modules/optionator": { "version": "0.9.4", "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz", @@ -10131,6 +10921,12 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/pako": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/pako/-/pako-1.0.11.tgz", + "integrity": "sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==", + "license": "(MIT AND Zlib)" + }, "node_modules/parent-module": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", @@ -10167,7 +10963,6 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", - "dev": true, "license": "MIT", "engines": { "node": ">=0.10.0" @@ -10177,7 +10972,6 @@ "version": "3.1.1", "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", - "dev": true, "license": "MIT", "engines": { "node": ">=8" @@ -10187,14 +10981,12 @@ "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/path-scurry": { "version": "1.11.1", "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.11.1.tgz", "integrity": "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==", - "dev": true, "license": "BlueOak-1.0.0", "dependencies": { "lru-cache": "^10.2.0", @@ -10211,7 +11003,6 @@ "version": "10.4.3", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", - "dev": true, "license": "ISC" }, "node_modules/path-to-regexp": { @@ -10230,6 +11021,38 @@ "node": ">=8" } }, + "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/pend": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/pend/-/pend-1.2.0.tgz", @@ -10240,7 +11063,6 @@ "version": "1.1.1", "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", - "dev": true, "license": "ISC" }, "node_modules/picomatch": { @@ -10260,7 +11082,6 @@ "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" @@ -10270,7 +11091,6 @@ "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" @@ -10290,7 +11110,6 @@ "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", @@ -10319,7 +11138,6 @@ "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", @@ -10337,7 +11155,6 @@ "version": "4.0.1", "resolved": "https://registry.npmjs.org/postcss-js/-/postcss-js-4.0.1.tgz", "integrity": "sha512-dDLF8pEO191hJMtlHFPRa8xsizHaM82MLfNkUHdUtVEV3tgTp5oj+8qbEqYM57SLfc74KSbw//4SeJma2LRVIw==", - "dev": true, "license": "MIT", "dependencies": { "camelcase-css": "^2.0.1" @@ -10357,7 +11174,6 @@ "version": "4.0.2", "resolved": "https://registry.npmjs.org/postcss-load-config/-/postcss-load-config-4.0.2.tgz", "integrity": "sha512-bSVhyJGL00wMVoPUzAVAnbEoWyqRxkjv64tUl427SKnPrENtq6hJwUojroMz2VB+Q1edmi4IfrAPpami5VVgMQ==", - "dev": true, "funding": [ { "type": "opencollective", @@ -10393,7 +11209,6 @@ "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", @@ -10419,7 +11234,6 @@ "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", @@ -10433,7 +11247,6 @@ "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/preact": { @@ -10473,6 +11286,12 @@ "integrity": "sha512-WuxUnVtlWL1OfZFQFuqvnvs6MiAGk9UNsBostyBOB0Is9wb5uRESevA6rnl/rkksXaGX3GzZhPup5d6Vp1nFew==", "license": "MIT" }, + "node_modules/process-nextick-args": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", + "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==", + "license": "MIT" + }, "node_modules/prop-types": { "version": "15.8.1", "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz", @@ -10552,7 +11371,6 @@ "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", @@ -10646,7 +11464,6 @@ "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==", - "dev": true, "license": "MIT", "dependencies": { "loose-envify": "^1.1.0" @@ -10669,7 +11486,6 @@ "version": "18.3.1", "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.3.1.tgz", "integrity": "sha512-5m4nQKp+rZRb09LNH59GM4BxTh9251/ylbKIbpe7TpGxfJ+9kv6BLkLBXIjjspbgbnIBNqlI23tRnTWT0snUIw==", - "dev": true, "license": "MIT", "dependencies": { "loose-envify": "^1.1.0", @@ -10854,17 +11670,42 @@ "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/readable-stream": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", + "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", + "license": "MIT", + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "node_modules/readable-stream/node_modules/isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==", + "license": "MIT" + }, + "node_modules/readable-stream/node_modules/safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "license": "MIT" + }, "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" @@ -10877,7 +11718,6 @@ "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" @@ -10981,7 +11821,6 @@ "version": "1.22.10", "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.10.tgz", "integrity": "sha512-NPRy+/ncIMeDlTAsuqwKIiferiawhefFJtkNSW0qZJEqMEb+qBt/77B/jGeeek+F0uOeN05CDa6HXbbIgtVX4w==", - "dev": true, "license": "MIT", "dependencies": { "is-core-module": "^2.16.0", @@ -11040,7 +11879,6 @@ "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", @@ -11096,7 +11934,6 @@ "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", @@ -11207,7 +12044,6 @@ "version": "0.23.2", "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.23.2.tgz", "integrity": "sha512-UOShsPwz7NrMUqhR6t0hWjFduvOzbtv7toDH1/hIrfRNIDBnnBWd0CwJTGvTpngVlmwGCdP9/Zl/tVrDqcuYzQ==", - "dev": true, "license": "MIT", "dependencies": { "loose-envify": "^1.1.0" @@ -11352,6 +12188,12 @@ "node": ">= 0.4" } }, + "node_modules/setimmediate": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz", + "integrity": "sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA==", + "license": "MIT" + }, "node_modules/setprototypeof": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", @@ -11362,7 +12204,6 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", - "dev": true, "license": "MIT", "dependencies": { "shebang-regex": "^3.0.0" @@ -11375,7 +12216,6 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", - "dev": true, "license": "MIT", "engines": { "node": ">=8" @@ -11457,7 +12297,6 @@ "version": "4.1.0", "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", - "dev": true, "license": "ISC", "engines": { "node": ">=14" @@ -11489,12 +12328,17 @@ "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==", - "dev": true, "license": "BSD-3-Clause", "engines": { "node": ">=0.10.0" } }, + "node_modules/sprintf-js": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", + "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==", + "license": "BSD-3-Clause" + }, "node_modules/stable-hash": { "version": "0.0.5", "resolved": "https://registry.npmjs.org/stable-hash/-/stable-hash-0.0.5.tgz", @@ -11529,16 +12373,29 @@ "version": "1.1.0", "resolved": "https://registry.npmjs.org/streamsearch/-/streamsearch-1.1.0.tgz", "integrity": "sha512-Mcc5wHehp9aXz1ax6bZUyY5afg9u2rv5cqQI3mRrYkGC8rW2hM02jWuwjtL++LS5qinSyhj2QfLyNsuc+VsExg==", - "dev": true, "engines": { "node": ">=10.0.0" } }, + "node_modules/string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "license": "MIT", + "dependencies": { + "safe-buffer": "~5.1.0" + } + }, + "node_modules/string_decoder/node_modules/safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "license": "MIT" + }, "node_modules/string-width": { "version": "5.1.2", "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", - "dev": true, "license": "MIT", "dependencies": { "eastasianwidth": "^0.2.0", @@ -11557,7 +12414,6 @@ "version": "4.2.3", "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", - "dev": true, "license": "MIT", "dependencies": { "emoji-regex": "^8.0.0", @@ -11572,14 +12428,12 @@ "version": "8.0.0", "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", - "dev": true, "license": "MIT" }, "node_modules/string-width/node_modules/ansi-regex": { "version": "6.2.0", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.2.0.tgz", "integrity": "sha512-TKY5pyBkHyADOPYlRT9Lx6F544mPl0vS5Ew7BJ45hA08Q+t3GjbueLliBWN3sMICk6+y7HdyxSzC4bWS8baBdg==", - "dev": true, "license": "MIT", "engines": { "node": ">=12" @@ -11592,7 +12446,6 @@ "version": "7.1.0", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", - "dev": true, "license": "MIT", "dependencies": { "ansi-regex": "^6.0.1" @@ -11721,7 +12574,6 @@ "version": "6.0.1", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "dev": true, "license": "MIT", "dependencies": { "ansi-regex": "^5.0.1" @@ -11735,7 +12587,6 @@ "version": "6.0.1", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "dev": true, "license": "MIT", "dependencies": { "ansi-regex": "^5.0.1" @@ -11771,7 +12622,6 @@ "version": "5.1.1", "resolved": "https://registry.npmjs.org/styled-jsx/-/styled-jsx-5.1.1.tgz", "integrity": "sha512-pW7uC1l4mBZ8ugbiZrcIsiIvVx1UmTfw7UkC3Um2tmfUq9Bhk8IiyEIPl6F8agHgjzku6j0xQEZbfA5uSgSaCw==", - "dev": true, "license": "MIT", "dependencies": { "client-only": "0.0.1" @@ -11795,7 +12645,6 @@ "version": "3.35.0", "resolved": "https://registry.npmjs.org/sucrase/-/sucrase-3.35.0.tgz", "integrity": "sha512-8EbVDiu9iN/nESwxeSxDKe0dunta1GOlHufmSSXxMD2z2/tMZpDMpvXQGsc+ajGo8y2uYUmixaSRUc/QPoQ0GA==", - "dev": true, "license": "MIT", "dependencies": { "@jridgewell/gen-mapping": "^0.3.2", @@ -11818,7 +12667,6 @@ "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" @@ -11841,7 +12689,6 @@ "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" @@ -11902,7 +12749,6 @@ "version": "3.4.17", "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-3.4.17.tgz", "integrity": "sha512-w33E2aCvSDP0tW9RZuNXadXlkHXqFzSkQew/aIa2i/Sj8fThxwovwlXHSPXTbAHwEIhBFXAedUhP2tueAKP8Og==", - "dev": true, "license": "MIT", "dependencies": { "@alloc/quick-lru": "^5.2.0", @@ -11947,7 +12793,6 @@ "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" @@ -11957,7 +12802,6 @@ "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" @@ -11993,7 +12837,6 @@ "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" @@ -12011,12 +12854,6 @@ "node": ">=0.6" } }, - "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-api-utils": { "version": "1.4.3", "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-1.4.3.tgz", @@ -12034,7 +12871,6 @@ "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/tsconfig-paths": { @@ -12056,6 +12892,26 @@ "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", "license": "0BSD" }, + "node_modules/tsx": { + "version": "4.21.0", + "resolved": "https://registry.npmjs.org/tsx/-/tsx-4.21.0.tgz", + "integrity": "sha512-5C1sg4USs1lfG0GFb2RLXsdpXqBSEhAaA/0kPL01wxzpMqLILNxIxIOKiILz+cdg/pLnOUxFYOR5yhHU666wbw==", + "dev": true, + "license": "MIT", + "dependencies": { + "esbuild": "~0.27.0", + "get-tsconfig": "^4.7.5" + }, + "bin": { + "tsx": "dist/cli.mjs" + }, + "engines": { + "node": ">=18.0.0" + }, + "optionalDependencies": { + "fsevents": "~2.3.3" + } + }, "node_modules/type-check": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", @@ -12206,6 +13062,12 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/underscore": { + "version": "1.13.8", + "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.13.8.tgz", + "integrity": "sha512-DXtD3ZtEQzc7M8m4cXotyHR+FAS18C64asBYY5vqZexfYryNNnDc02W4hKg3rdQuqOYas1jkseX0+nZXjTXnvQ==", + "license": "MIT" + }, "node_modules/undici-types": { "version": "5.26.5", "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", @@ -12355,7 +13217,6 @@ "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/utils-merge": { @@ -12407,27 +13268,10 @@ "d3-timer": "^3.0.1" } }, - "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" - } - }, "node_modules/which": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", - "dev": true, "license": "ISC", "dependencies": { "isexe": "^2.0.0" @@ -12542,7 +13386,6 @@ "version": "8.1.0", "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz", "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==", - "dev": true, "license": "MIT", "dependencies": { "ansi-styles": "^6.1.0", @@ -12561,7 +13404,6 @@ "version": "7.0.0", "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", - "dev": true, "license": "MIT", "dependencies": { "ansi-styles": "^4.0.0", @@ -12579,14 +13421,12 @@ "version": "8.0.0", "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", - "dev": true, "license": "MIT" }, "node_modules/wrap-ansi-cjs/node_modules/string-width": { "version": "4.2.3", "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", - "dev": true, "license": "MIT", "dependencies": { "emoji-regex": "^8.0.0", @@ -12601,7 +13441,6 @@ "version": "6.2.0", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.2.0.tgz", "integrity": "sha512-TKY5pyBkHyADOPYlRT9Lx6F544mPl0vS5Ew7BJ45hA08Q+t3GjbueLliBWN3sMICk6+y7HdyxSzC4bWS8baBdg==", - "dev": true, "license": "MIT", "engines": { "node": ">=12" @@ -12614,7 +13453,6 @@ "version": "6.2.1", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz", "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==", - "dev": true, "license": "MIT", "engines": { "node": ">=12" @@ -12627,7 +13465,6 @@ "version": "7.1.0", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", - "dev": true, "license": "MIT", "dependencies": { "ansi-regex": "^6.0.1" @@ -12646,9 +13483,9 @@ "license": "ISC" }, "node_modules/ws": { - "version": "8.18.3", - "resolved": "https://registry.npmjs.org/ws/-/ws-8.18.3.tgz", - "integrity": "sha512-PEIGCY5tSlUt50cqyMXfCzX+oOPqN0vuGqWzbcJ2xvnkzkq46oOpz7dQaTDBdfICb4N14+GARUDw2XV2N4tvzg==", + "version": "8.20.0", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.20.0.tgz", + "integrity": "sha512-sAt8BhgNbzCtgGbt2OxmpuryO63ZoDk/sqaB/znQm94T4fCEsy/yV+7CdC1kJhOU9lboAEU7R3kquuycDoibVA==", "license": "MIT", "engines": { "node": ">=10.0.0" @@ -12666,6 +13503,15 @@ } } }, + "node_modules/xmlbuilder": { + "version": "10.1.1", + "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-10.1.1.tgz", + "integrity": "sha512-OyzrcFLL/nb6fMGHbiRDuPup9ljBycsdCypwuyg5AAHvyWzGfChJpCXMG88AGTIMFhGZ9RccFN1e6lhg3hkwKg==", + "license": "MIT", + "engines": { + "node": ">=4.0" + } + }, "node_modules/yallist": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", diff --git a/package.json b/package.json index e726583..946dbbc 100644 --- a/package.json +++ b/package.json @@ -4,20 +4,23 @@ "private": true, "type": "module", "scripts": { - "dev": "CHOKIDAR_USEPOLLING=true next dev", + "dev": "next dev", "build": "next build", "start": "next start", - "lint": "next lint" + "lint": "next lint", + "convert:pdf": "tsx scripts/convertPdfToJson.ts" }, "dependencies": { "@emailjs/browser": "^4.3.3", + "@google/generative-ai": "^0.24.1", "@nextui-org/navbar": "^2.0.27", "@nextui-org/react": "^2.2.10", "@nextui-org/system": "^2.0.15", "@nextui-org/theme": "^2.1.18", "@supabase/auth-helpers-nextjs": "^0.10.0", "@supabase/auth-helpers-react": "^0.5.0", - "@supabase/supabase-js": "^2.49.4", + "@supabase/ssr": "^0.9.0", + "@supabase/supabase-js": "^2.100.1", "@tailwindcss/aspect-ratio": "^0.4.2", "axios": "^1.9.0", "bcrypt": "^6.0.0", @@ -27,15 +30,17 @@ "cors": "^2.8.5", "d3": "^7.9.0", "d3-array": "^3.2.4", - "dotenv": "^16.5.0", + "dotenv": "^16.6.1", "express": "^4.21.2", "framer-motion": "^11.1.7", "js-cookie": "^3.0.5", "jsonwebtoken": "^9.0.2", + "mammoth": "^1.12.0", "next-auth": "^4.24.11", "ngrok": "^5.0.0-beta.2", - "nodemailer": "^6.10.1", + "nodemailer": "^7.0.13", "object-hash": "^3.0.0", + "pdf-parse": "^2.4.5", "react-chartjs-2": "^5.2.0", "react-icons": "^5.3.0", "react-photo-album": "^2.3.1", @@ -50,6 +55,7 @@ "devDependencies": { "@types/d3-array": "^3.2.1", "@types/node": "20.11.30", + "@types/pdf-parse": "^1.1.5", "@types/react": "18.2.73", "autoprefixer": "^10.0.1", "eslint": "^8", @@ -59,6 +65,7 @@ "react": "^18.3.1", "react-dom": "^18.3.1", "tailwindcss": "^3.4.13", + "tsx": "^4.21.0", "typescript": "5.4.5" }, "description": "First, install all necessary dependencies:", diff --git a/scripts/convertPdfToJson.ts b/scripts/convertPdfToJson.ts new file mode 100644 index 0000000..42b92b4 --- /dev/null +++ b/scripts/convertPdfToJson.ts @@ -0,0 +1,73 @@ +import { readdir, readFile, stat, writeFile, mkdir } from "node:fs/promises"; +import path from "node:path"; +import crypto from "node:crypto"; + +type DocumentRecord = { + id: string; + title: string; + content: string; +}; + +function cleanText(input: string): string { + return input + .replace(/\r\n/g, "\n") + .replace(/[ \t]+/g, " ") + .replace(/\n{3,}/g, "\n\n") + .replace(/\s+\n/g, "\n") + .trim(); +} + +function makeId(fileName: string): string { + const stem = fileName.replace(/\.pdf$/i, ""); + const slug = stem.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, ""); + const hash = crypto.createHash("sha1").update(fileName).digest("hex").slice(0, 8); + return `${slug || "document"}-${hash}`; +} + +async function main(): Promise { + const pdfModule = await import("pdf-parse"); + const PDFParseCtor = (pdfModule as unknown as { PDFParse?: new (options: { data: Buffer }) => { getText: () => Promise<{ text: string }>; destroy: () => Promise } }) + .PDFParse; + if (!PDFParseCtor) { + throw new Error("pdf-parse PDFParse export is unavailable in this runtime."); + } + + const projectRoot = process.cwd(); + const dataDir = path.join(projectRoot, "data"); + const pdfDir = path.join(dataDir, "pdfs"); + const outputFile = path.join(dataDir, "documents.json"); + + await mkdir(pdfDir, { recursive: true }); + + const files = await readdir(pdfDir); + const pdfFiles = files.filter((file) => file.toLowerCase().endsWith(".pdf")); + + const documents: DocumentRecord[] = []; + + for (const file of pdfFiles) { + const fullPath = path.join(pdfDir, file); + const fileStats = await stat(fullPath); + if (!fileStats.isFile()) continue; + + const buffer = await readFile(fullPath); + const parser = new PDFParseCtor({ data: buffer }); + const parsed = await parser.getText(); + await parser.destroy(); + const content = cleanText(parsed.text || ""); + if (!content) continue; + + documents.push({ + id: makeId(file), + title: file.replace(/\.pdf$/i, ""), + content, + }); + } + + await writeFile(outputFile, `${JSON.stringify(documents, null, 2)}\n`, "utf-8"); + console.log(`Converted ${documents.length} PDF(s) -> ${path.relative(projectRoot, outputFile)}`); +} + +main().catch((error) => { + console.error("PDF conversion failed:", error); + process.exit(1); +}); diff --git a/tsconfig.json b/tsconfig.json index bbc41a0..ad177f0 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -13,6 +13,12 @@ "esModuleInterop": true, "module": "esnext", "moduleResolution": "node", + "baseUrl": ".", + "paths": { + "@/*": [ + "./*" + ] + }, "resolveJsonModule": true, "isolatedModules": true, "jsx": "preserve",