diff --git a/.github/workflows/closes_comment.yml b/.github/workflows/closes_comment.yml
new file mode 100644
index 00000000..a9846c91
--- /dev/null
+++ b/.github/workflows/closes_comment.yml
@@ -0,0 +1,37 @@
+name: Issue Closed Comment
+
+on:
+ issues:
+ types: [closed]
+
+permissions:
+ issues: write
+
+jobs:
+ comment-on-close:
+ runs-on: ubuntu-latest
+
+ steps:
+ - name: Post Closing Comment
+ uses: actions/github-script@v7
+
+ with:
+ github-token: ${{ secrets.GITHUB_TOKEN }}
+
+ script: |
+ const issue = context.payload.issue;
+ const creator = issue.user.login;
+ const repoName = context.repo.repo;
+
+ const commentBody = `### Issue Closed\n\n` +
+ `Thank you, @${creator}, for taking the time to report or engage with this issue in **${repoName}**! š”ļø\n\n` +
+ `Your input helps us manage our project roadmap effectively.\n\n` +
+ `If you discover new edge cases or have ideas for enhancements, feel free to open a new issue. Happy coding! š`;
+
+ await github.rest.issues.createComment({
+ owner: context.repo.owner,
+ repo: context.repo.repo,
+ issue_number: issue.number,
+ body: commentBody
+ });
+
diff --git a/.github/workflows/pr_raise.yml b/.github/workflows/pr_raise.yml
new file mode 100644
index 00000000..aac10364
--- /dev/null
+++ b/.github/workflows/pr_raise.yml
@@ -0,0 +1,35 @@
+name: Auto Comment on PR
+
+on:
+ pull_request_target:
+ types: [opened]
+
+permissions:
+ pull-requests: write
+
+jobs:
+ comment:
+ runs-on: ubuntu-latest
+
+ steps:
+ - name: Add Comment to Pull Request
+ uses: actions/github-script@v7
+
+ with:
+ github-token: ${{ secrets.GITHUB_TOKEN }}
+ script: |
+ const prNumber = context.payload.pull_request.number;
+ const contributor = context.payload.pull_request.user.login;
+ const commentBody = `Thank you for submitting your pull request, @${contributor}! š
+ We'll review it as soon as possible.
+ If there are any specific instructions or feedback regarding your PR, we'll provide them here.
+ Thanks again for your contribution to our project! š`;
+
+ await github.rest.issues.createComment({
+ owner: context.repo.owner,
+ repo: context.repo.repo,
+ issue_number: prNumber,
+ body: commentBody
+ });
+
+
diff --git a/backend/Input_validators/ValidateBookmark.js b/backend/Input_validators/ValidateBookmark.js
new file mode 100644
index 00000000..d5e4c803
--- /dev/null
+++ b/backend/Input_validators/ValidateBookmark.js
@@ -0,0 +1,53 @@
+const z = require("zod");
+
+const BOOKMARK_CATEGORIES = [
+ "DSA", "System Design", "Behavioral", "Aptitude", "Coding",
+ "OS", "DBMS", "Networking", "Other",
+];
+
+const validateCreateBookmark = (req, res, next) => {
+ const schema = z.object({
+ question: z.string().min(1, "Question is required").max(500),
+ answer: z.string().max(2000).optional(),
+ category: z.enum(BOOKMARK_CATEGORIES).default("DSA"),
+ difficulty: z.enum(["Easy", "Medium", "Hard"]).default("Medium"),
+ tags: z.array(z.string().max(30).toLowerCase()).max(5).optional(),
+ notes: z.string().max(500).optional(),
+ source: z.enum(["manual", "session", "sheet", "imported"]).optional(),
+ sourceRef: z.string().optional(),
+ });
+
+ const result = schema.safeParse(req.body);
+ if (!result.success) {
+ return res.status(400).json({
+ success: false,
+ message: result.error.errors.map((e) => e.message).join(", "),
+ });
+ }
+ req.validatedBody = result.data;
+ next();
+};
+
+const validateUpdateBookmark = (req, res, next) => {
+ const schema = z.object({
+ question: z.string().min(1).max(500).optional(),
+ answer: z.string().max(2000).optional(),
+ category: z.enum(BOOKMARK_CATEGORIES).optional(),
+ difficulty: z.enum(["Easy", "Medium", "Hard"]).optional(),
+ tags: z.array(z.string().max(30).toLowerCase()).max(5).optional(),
+ notes: z.string().max(500).optional(),
+ starred: z.boolean().optional(),
+ });
+
+ const result = schema.safeParse(req.body);
+ if (!result.success) {
+ return res.status(400).json({
+ success: false,
+ message: result.error.errors.map((e) => e.message).join(", "),
+ });
+ }
+ req.validatedBody = result.data;
+ next();
+};
+
+module.exports = { validateCreateBookmark, validateUpdateBookmark };
diff --git a/backend/Input_validators/ValidateNotesSummary.js b/backend/Input_validators/ValidateNotesSummary.js
index fc754d25..f72aed29 100644
--- a/backend/Input_validators/ValidateNotesSummary.js
+++ b/backend/Input_validators/ValidateNotesSummary.js
@@ -56,7 +56,7 @@ const aiOutputSchema = z.object({
const saveNotesSummarySchema = z.object({
- fileName: z.string().min(1).max(200),
+ fileName: z.string().min(1).max(200, "File name cannot exceed 200 characters"),
sourceType: z.enum(["upload", "platform"]),
sourceUrl: z.string().url().optional().nullable(),
pageCount: z.number().int().nonnegative().optional().default(0),
diff --git a/backend/constants/achievements.js b/backend/constants/achievements.js
index eabb8e6b..7da2b777 100644
--- a/backend/constants/achievements.js
+++ b/backend/constants/achievements.js
@@ -12,6 +12,7 @@ const VALID_ACHIEVEMENTS = new Set([
"DSA Master",
"3-Day Streak",
"7-Day Streak",
+ "14-Day Streak",
"30-Day Streak",
]);
diff --git a/backend/controllers/achievementController.js b/backend/controllers/achievementController.js
index 8cf01091..ca7af002 100644
--- a/backend/controllers/achievementController.js
+++ b/backend/controllers/achievementController.js
@@ -1,22 +1,14 @@
const User = require('../models/User');
const { VALID_ACHIEVEMENTS } = require('../constants/achievements');
+const { resetStreakIfMissed } = require('../utils/streakTracker');
exports.getAchievements = async (req, res) => {
try {
const user = await User.findById(req.user._id).select('unlockedAchievements lastPracticeDate currentStreak');
if (!user) return res.status(404).json({ success: false, error: 'User not found' });
- // Reset streak to 0 if one or more calendar days were missed
- if (user.lastPracticeDate && user.currentStreak > 0) {
- const now = new Date();
- const d1 = new Date(user.lastPracticeDate);
- const utc1 = Date.UTC(d1.getUTCFullYear(), d1.getUTCMonth(), d1.getUTCDate());
- const utc2 = Date.UTC(now.getUTCFullYear(), now.getUTCMonth(), now.getUTCDate());
- const diffDays = Math.floor((utc2 - utc1) / (1000 * 60 * 60 * 24));
- if (diffDays > 1) {
- user.currentStreak = 0;
- await user.save();
- }
+ if (resetStreakIfMissed(user)) {
+ await user.save();
}
res.json({ success: true, unlockedAchievements: user.unlockedAchievements });
@@ -35,7 +27,6 @@ exports.saveAchievements = async (req, res) => {
});
}
- // Reject any ID not in the server-side allowlist
const unknown = unlockedAchievements.filter((id) => !VALID_ACHIEVEMENTS.has(id));
if (unknown.length > 0) {
@@ -46,8 +37,6 @@ exports.saveAchievements = async (req, res) => {
}
try {
- // $addToSet is idempotent and additive-only ā it never removes
- // achievements the user already earned, and never duplicates.
await User.findByIdAndUpdate(
req.user._id,
{ $addToSet: { unlockedAchievements: { $each: unlockedAchievements } } },
diff --git a/backend/controllers/authController.js b/backend/controllers/authController.js
index 00000669..2f5885ba 100644
--- a/backend/controllers/authController.js
+++ b/backend/controllers/authController.js
@@ -4,6 +4,8 @@ const jwt = require("jsonwebtoken");
const crypto = require("crypto");
const { sendVerificationEmail } = require("../utils/sendEmail");
const { validatePassword } = require('../utils/passwordPolicy');
+const { isValidCountry } = require("../utils/nameCountry");
+const { resetStreakIfMissed } = require("../utils/streakTracker");
// Models for cascade deletion on account delete
const Session = require("../models/Session");
@@ -97,27 +99,13 @@ const registerUser = async (req, res) => {
const userExists = await User.findOne({ email: cleanEmail });
if (userExists) {
- if (!userExists.isEmailVerified) {
- const rawToken = crypto.randomBytes(32).toString("hex");
- userExists.emailVerificationToken = crypto.createHash("sha256").update(rawToken).digest("hex");
- userExists.emailVerificationExpires = new Date(Date.now() + 24 * 60 * 60 * 1000);
- await userExists.save();
- try {
- const verificationUrl = `${process.env.FRONTEND_URL}/verify-email?token=${rawToken}`;
- await sendVerificationEmail(userExists.email, verificationUrl);
- } catch (err) {
- console.error("Failed to resend verification email on re-registration:", err);
- }
- }
- return res.status(201).json({
- success: true,
- message: "If this email is not already registered, your account has been created. Please check your email to verify your account before logging in.",
+ // Always return the same ambiguous message to prevent email enumeration
+ return res.status(409).json({
+ success: false,
+ message: "An account with this email already exists. Please log in.",
});
}
- // Hash raw password with bcrypt before DB creation (#757)
- const hashedPassword = await bcrypt.hash(password, PASSWORD_SALT_ROUNDS);
-
// Split name into first and last names for defaults
const nameParts = cleanName.split(/\s+/);
const firstName = nameParts[0] || "";
@@ -126,14 +114,12 @@ const registerUser = async (req, res) => {
// Generate default unique PrepPilot ID
const defaultPrepPilotId = cleanEmail.split("@")[0] + Math.floor(1000 + Math.random() * 9000);
- const rawToken = crypto.randomBytes(32).toString("hex");
- const emailVerificationToken = crypto.createHash("sha256").update(rawToken).digest("hex");
- const emailVerificationExpires = new Date(Date.now() + 24 * 60 * 60 * 1000);
-
+ // Email verification is currently disabled ā accounts are active immediately on creation.
+ // To re-enable: set isEmailVerified to false, generate a token, and call sendVerificationEmail.
const user = await User.create({
name: cleanName,
email: cleanEmail,
- password: hashedPassword,
+ password,
profileImageUrl,
firstName,
lastName,
@@ -147,21 +133,29 @@ const registerUser = async (req, res) => {
socials: { github: "", linkedin: "", twitter: "", portfolio: "" }
},
platformPreferences: { theme: "light", notificationsEnabled: true },
- isEmailVerified: false,
- emailVerificationToken,
- emailVerificationExpires,
+ isEmailVerified: true, // verification disabled ā users can log in immediately
+ emailVerificationToken: null,
+ emailVerificationExpires: null,
});
- try {
- const verificationUrl = `${process.env.FRONTEND_URL}/verify-email?token=${rawToken}`;
- await sendVerificationEmail(user.email, verificationUrl);
- } catch (err) {
- console.error("Failed to send initial verification email:", err);
- }
+ // Issue tokens immediately so the user is logged in right after signup
+ const accessToken = generateAccessToken(user._id, user.tokenVersion);
+ const refreshToken = generateRefreshToken(user._id);
+
+ user.refreshTokenHash = await bcrypt.hash(refreshToken, REFRESH_TOKEN_SALT_ROUNDS);
+ user.refreshTokenExpiresAt = new Date(Date.now() + REFRESH_TOKEN_MAX_AGE_MS);
+ await user.save();
+
+ res.cookie("refreshToken", refreshToken, getRefreshCookieOptions());
return res.status(201).json({
success: true,
- message: "If this email is not already registered, your account has been created. Please check your email to verify your account before logging in.",
+ message: "Account created successfully.",
+ accessToken,
+ _id: user._id,
+ name: user.name,
+ email: user.email,
+ profileImageUrl: user.profileImageUrl,
});
} catch (error) {
console.error("Register error:", error);
@@ -192,14 +186,6 @@ const loginUser = async (req, res) => {
return res.status(401).json({ success: false, message: "Invalid email or password provided." });
}
- // Block login until email is verified
- if (!user.isEmailVerified) {
- return res.status(403).json({
- success: false,
- message: "Please verify your email before logging in. Check your inbox for the verification link.",
- });
- }
-
const accessToken = generateAccessToken(user._id, user.tokenVersion);
const refreshToken = generateRefreshToken(user._id);
@@ -413,17 +399,8 @@ const getUserProfile = async (req, res) => {
return res.status(404).json({ success: false, message: "Requested user profile not found" });
}
- // Reset streak to 0 if one or more calendar days were missed
- if (user.lastPracticeDate && user.currentStreak > 0) {
- const now = new Date();
- const d1 = new Date(user.lastPracticeDate);
- const utc1 = Date.UTC(d1.getUTCFullYear(), d1.getUTCMonth(), d1.getUTCDate());
- const utc2 = Date.UTC(now.getUTCFullYear(), now.getUTCMonth(), now.getUTCDate());
- const diffDays = Math.floor((utc2 - utc1) / (1000 * 60 * 60 * 24));
- if (diffDays > 1) {
- user.currentStreak = 0;
- await user.save();
- }
+ if (resetStreakIfMissed(user)) {
+ await user.save();
}
res.json(user);
@@ -433,6 +410,7 @@ const getUserProfile = async (req, res) => {
}
};
+
/**
* Update the user profile settings.
* @route PUT /api/auth/profile
@@ -459,13 +437,24 @@ const updateUserProfile = async (req, res) => {
}
// Update fields if they are sent in request
+ // Update fields if they are sent in request
if (firstName !== undefined) user.firstName = firstName;
if (lastName !== undefined) user.lastName = lastName;
if (bio !== undefined) user.bio = bio;
- if (country !== undefined) user.country = country;
+
+ if (country !== undefined) {
+ if (country.trim() !== "" && !isValidCountry(country)) {
+ return res.status(400).json({
+ success: false,
+ message: "Please enter a valid country name.",
+ });
+ }
+
+ user.country = country.trim();
+ }
+
if (profileImageUrl !== undefined) user.profileImageUrl = profileImageUrl;
if (visibility !== undefined) user.visibility = visibility;
-
// Sync name based on firstName and lastName
if (firstName !== undefined || lastName !== undefined) {
const fName = firstName !== undefined ? firstName : user.firstName;
@@ -558,8 +547,8 @@ const changePassword = async (req, res) => {
return res.status(400).json({ success: false, message: "Incorrect original password" });
}
- // Hash new password before saving (#757)
- user.password = await bcrypt.hash(newPassword, PASSWORD_SALT_ROUNDS);
+ // Set new password (User.js pre save hook hashes it automatically)
+ user.password = newPassword;
// Fix #759: Revoke active refresh tokens in database & increment tokenVersion for access tokens
user.refreshTokenHash = null;
diff --git a/backend/controllers/bookmarkController.js b/backend/controllers/bookmarkController.js
new file mode 100644
index 00000000..0ab4b7b3
--- /dev/null
+++ b/backend/controllers/bookmarkController.js
@@ -0,0 +1,192 @@
+const Bookmark = require("../models/Bookmark");
+
+const MAX_BOOKMARKS = Number(process.env.MAX_BOOKMARKS) || 200;
+
+exports.createBookmark = async (req, res) => {
+ try {
+ const userId = req.user._id;
+ const count = await Bookmark.countDocuments({ user: userId });
+ if (count >= MAX_BOOKMARKS) {
+ return res.status(400).json({
+ success: false,
+ message: `Bookmark limit of ${MAX_BOOKMARKS} reached.`,
+ });
+ }
+
+ const data = req.validatedBody;
+ const payload = {
+ user: userId,
+ question: data.question,
+ answer: data.answer || "",
+ category: data.category,
+ difficulty: data.difficulty,
+ tags: data.tags || [],
+ notes: data.notes || "",
+ source: data.source || "manual",
+ sourceRef: data.sourceRef || null,
+ };
+
+ const bookmark = await Bookmark.create(payload);
+ res.status(201).json({ success: true, bookmark });
+ } catch (error) {
+ console.error("Create bookmark error:", error);
+ res.status(500).json({ success: false, message: "Server error" });
+ }
+};
+
+exports.getBookmarks = async (req, res) => {
+ try {
+ const userId = req.user._id;
+ const { category, difficulty, starred, tag, search, page = 1, limit = 20 } = req.query;
+
+ const filter = { user: userId };
+ if (category) filter.category = category;
+ if (difficulty) filter.difficulty = difficulty;
+ if (starred === "true") filter.starred = true;
+ if (tag) filter.tags = { $in: [tag.toLowerCase()] };
+ if (search) {
+ filter.$or = [
+ { question: { $regex: search, $options: "i" } },
+ { notes: { $regex: search, $options: "i" } },
+ { answer: { $regex: search, $options: "i" } },
+ ];
+ }
+
+ const pageNum = Math.max(1, parseInt(page));
+ const limitNum = Math.min(50, Math.max(1, parseInt(limit)));
+ const skip = (pageNum - 1) * limitNum;
+
+ const [bookmarks, total] = await Promise.all([
+ Bookmark.find(filter).sort({ starred: -1, createdAt: -1 }).skip(skip).limit(limitNum),
+ Bookmark.countDocuments(filter),
+ ]);
+
+ res.json({
+ success: true,
+ bookmarks,
+ pagination: {
+ page: pageNum,
+ limit: limitNum,
+ total,
+ pages: Math.ceil(total / limitNum),
+ },
+ });
+ } catch (error) {
+ console.error("Get bookmarks error:", error);
+ res.status(500).json({ success: false, message: "Server error" });
+ }
+};
+
+exports.getBookmarkById = async (req, res) => {
+ try {
+ const bookmark = await Bookmark.findOne({
+ _id: req.params.id,
+ user: req.user._id,
+ });
+ if (!bookmark) {
+ return res.status(404).json({ success: false, message: "Bookmark not found" });
+ }
+ res.json({ success: true, bookmark });
+ } catch (error) {
+ console.error("Get bookmark error:", error);
+ res.status(500).json({ success: false, message: "Server error" });
+ }
+};
+
+exports.updateBookmark = async (req, res) => {
+ try {
+ const bookmark = await Bookmark.findOne({
+ _id: req.params.id,
+ user: req.user._id,
+ });
+ if (!bookmark) {
+ return res.status(404).json({ success: false, message: "Bookmark not found" });
+ }
+ Object.assign(bookmark, req.validatedBody);
+ await bookmark.save();
+ res.json({ success: true, bookmark });
+ } catch (error) {
+ console.error("Update bookmark error:", error);
+ res.status(500).json({ success: false, message: "Server error" });
+ }
+};
+
+exports.toggleStar = async (req, res) => {
+ try {
+ const bookmark = await Bookmark.findOne({
+ _id: req.params.id,
+ user: req.user._id,
+ });
+ if (!bookmark) {
+ return res.status(404).json({ success: false, message: "Bookmark not found" });
+ }
+ bookmark.starred = !bookmark.starred;
+ await bookmark.save();
+ res.json({ success: true, bookmark });
+ } catch (error) {
+ console.error("Toggle star error:", error);
+ res.status(500).json({ success: false, message: "Server error" });
+ }
+};
+
+exports.deleteBookmark = async (req, res) => {
+ try {
+ const bookmark = await Bookmark.findOneAndDelete({
+ _id: req.params.id,
+ user: req.user._id,
+ });
+ if (!bookmark) {
+ return res.status(404).json({ success: false, message: "Bookmark not found" });
+ }
+ res.json({ success: true, message: "Bookmark deleted" });
+ } catch (error) {
+ console.error("Delete bookmark error:", error);
+ res.status(500).json({ success: false, message: "Server error" });
+ }
+};
+
+exports.getTags = async (req, res) => {
+ try {
+ const tags = await Bookmark.distinct("tags", { user: req.user._id });
+ res.json({ success: true, tags: tags.sort() });
+ } catch (error) {
+ console.error("Get tags error:", error);
+ res.status(500).json({ success: false, message: "Server error" });
+ }
+};
+
+exports.getStats = async (req, res) => {
+ try {
+ const userId = req.user._id;
+ const [total, byCategory, byDifficulty, starredCount] = await Promise.all([
+ Bookmark.countDocuments({ user: userId }),
+ Bookmark.aggregate([
+ { $match: { user: userId } },
+ { $group: { _id: "$category", count: { $sum: 1 } } },
+ { $sort: { count: -1 } },
+ ]),
+ Bookmark.aggregate([
+ { $match: { user: userId } },
+ { $group: { _id: "$difficulty", count: { $sum: 1 } } },
+ ]),
+ Bookmark.countDocuments({ user: userId, starred: true }),
+ ]);
+
+ const categoryBreakdown = {};
+ for (const item of byCategory) {
+ categoryBreakdown[item._id] = item.count;
+ }
+ const difficultyBreakdown = {};
+ for (const item of byDifficulty) {
+ difficultyBreakdown[item._id] = item.count;
+ }
+
+ res.json({
+ success: true,
+ stats: { total, starredCount, categoryBreakdown, difficultyBreakdown },
+ });
+ } catch (error) {
+ console.error("Get stats error:", error);
+ res.status(500).json({ success: false, message: "Server error" });
+ }
+};
diff --git a/backend/controllers/flashcardController.js b/backend/controllers/flashcardController.js
index b346921b..e188e84f 100644
--- a/backend/controllers/flashcardController.js
+++ b/backend/controllers/flashcardController.js
@@ -1,10 +1,7 @@
const mongoose = require("mongoose");
const Flashcard = require("../models/Flashcard");
+const { recordActivity } = require("../utils/streakTracker");
-/**
- * SuperMemo SM-2 Algorithm helper
- * Returns updated { interval, repetition, efactor, dueDate }
- */
const calculateSM2 = ({ interval = 0, repetition = 0, efactor = 2.5 }, rating) => {
let score = 3;
if (rating === "again" || rating === "1") score = 1;
@@ -201,10 +198,15 @@ const reviewFlashcard = async (req, res) => {
await flashcard.save();
+ const { newlyUnlocked } = await recordActivity(userId);
+
return res.status(200).json({
success: true,
message: "Flashcard review recorded successfully",
flashcard,
+ // Streak milestones (e.g. "7-Day Streak") unlocked by this activity,
+ // if any ā lets the frontend show a toast.
+ newlyUnlockedAchievements: newlyUnlocked,
});
} catch (error) {
return res.status(500).json({
diff --git a/backend/controllers/interviewExperienceController.js b/backend/controllers/interviewExperienceController.js
index b703c733..c443fcd3 100644
--- a/backend/controllers/interviewExperienceController.js
+++ b/backend/controllers/interviewExperienceController.js
@@ -70,7 +70,17 @@ const createInterviewExperience = async (req, res) => {
payload.color = `hsl(${(payload.company.charCodeAt(0) * 37) % 360}, 55%, 50%)`;
}
- const existing = await InterviewExperience.findOne({ idempotencyKey });
+ // Scope idempotency to the author so a re-used key only matches the same
+ // submitter ā otherwise a different user/anonymous visitor re-using a key
+ // receives the first author's full submission and their own is lost.
+ const authorFilter = req.user?._id
+ ? { userId: req.user._id }
+ : { clientKey: payload.clientKey };
+
+ const existing = await InterviewExperience.findOne({
+ idempotencyKey,
+ ...authorFilter,
+ });
if (existing) {
return res.status(200).json({
success: true,
@@ -87,11 +97,16 @@ const createInterviewExperience = async (req, res) => {
experience: toClientShape(experience),
});
} catch (error) {
- // Concurrent retry won the unique index race ā return the first write.
+ // Concurrent retry won the unique index race ā return the first write by
+ // the same author (scoped, not a global key lookup).
if (error?.code === 11000 && req.body?.idempotencyKey) {
try {
+ const raceAuthorFilter = req.user?._id
+ ? { userId: req.user._id }
+ : { clientKey: req.body?.clientKey };
const existing = await InterviewExperience.findOne({
idempotencyKey: req.body.idempotencyKey,
+ ...raceAuthorFilter,
});
if (existing) {
return res.status(200).json({
diff --git a/backend/controllers/resumeController.js b/backend/controllers/resumeController.js
index a1b7b594..f895b12b 100644
--- a/backend/controllers/resumeController.js
+++ b/backend/controllers/resumeController.js
@@ -2,6 +2,9 @@ const axios = require('axios');
const FormData = require('form-data');
const fs = require('fs');
const { generateWithFallback } = require('../utils/geminiHelper');
+const { matchResumeKeywords } = require('../utils/atsKeywordMatch');
+const MAX_FILE_SIZE = 5 * 1024 * 1024; // 5 MB
+const MAX_ATS_INPUT = 50000; // chars; guards against oversized payloads
/**
* Compile LaTeX resume code to a PDF document.
@@ -312,4 +315,74 @@ const getResumeAnalysisHistory = async (req, res) => {
}
};
-module.exports = { compileResume, analyzeResume, saveResume, getMyResumes, deleteResume, getResumeAnalysisHistory };
+const validateFile = (file) => {
+ if (!file) return false;
+
+ if (file.type !== "application/pdf") {
+ setError("Only PDF files are allowed.");
+ return false;
+ }
+
+ if (file.size > MAX_FILE_SIZE) {
+ setError("File size must be less than 5MB.");
+ return false;
+ }
+
+ setError("");
+ return true;
+};
+const handleFileChange = (e) => {
+ const file = e.target.files[0];
+
+ if (!validateFile(file)) {
+ e.target.value = "";
+ setFile(null);
+ return;
+ }
+
+ setFile(file);
+};
+const handleDrop = (e) => {
+ e.preventDefault();
+
+ const file = e.dataTransfer.files[0];
+
+ if (!validateFile(file)) {
+ setFile(null);
+ return;
+ }
+
+ setFile(file);
+};
+
+
+/**
+ * Deterministic ATS keyword match between a resume and a job description.
+ * No AI / no file upload ā pure keyword overlap, instant and free.
+ * @route POST /api/resume/ats-match
+ * @param {import('express').Request} req
+ * @param {import('express').Response} res
+ * @returns {Promise}
+ */
+const atsMatch = async (req, res) => {
+ const { resumeText, jobDescription } = req.body || {};
+
+ if (
+ typeof resumeText !== 'string' || resumeText.trim().length === 0 ||
+ typeof jobDescription !== 'string' || jobDescription.trim().length === 0
+ ) {
+ return res.status(400).json({
+ success: false,
+ error: 'resumeText and jobDescription are required strings',
+ });
+ }
+
+ if (resumeText.length > MAX_ATS_INPUT || jobDescription.length > MAX_ATS_INPUT) {
+ return res.status(400).json({ success: false, error: 'Input too large' });
+ }
+
+ const result = matchResumeKeywords(resumeText, jobDescription);
+ return res.json({ success: true, ...result });
+};
+
+module.exports = { compileResume, analyzeResume, saveResume, getMyResumes, deleteResume, getResumeAnalysisHistory ,validateFile,handleFileChange,handleDrop,atsMatch};
diff --git a/backend/controllers/sessionController.js b/backend/controllers/sessionController.js
index 8688e21c..3c05844e 100644
--- a/backend/controllers/sessionController.js
+++ b/backend/controllers/sessionController.js
@@ -2,33 +2,11 @@ const Session = require("../models/Session");
const Question = require("../models/Question");
const mongoose = require("mongoose");
const User = require("../models/User");
-
+const { applyStreakForActivity } = require("../utils/streakTracker");
const MAX_SESSIONS = Number(process.env.MAX_SESSIONS) || 50;
const MAX_EXPERIENCE = 50;
-/**
- * Create a new practice session and associated questions.
- * @route POST /api/sessions/create
- * @param {import('express').Request} req
- * @param {import('express').Response} res
- * @returns {Promise}
- * @throws {Error} When required fields are missing or user exceeds session limits.
- * @example
- * POST /api/sessions/create
- * Authorization: Bearer eyJhb...
- * {
- * "role": "Backend Engineer",
- * "company": "Google",
- * "experience": "3 years",
- * "topicsToFocus": ["Node.js","Databases"],
- * "description": "Prepare for backend interview",
- * "question": [{"question":"Explain ACID properties","answer":"..."}]
- * }
- * @example
- * 201 {"success": true, "session": {"_id":"...","role":"Backend Engineer",...}}
- */
-
exports.createSession = async (req, res) => {
const mongoSession = await mongoose.startSession();
@@ -37,7 +15,7 @@ exports.createSession = async (req, res) => {
const userId = req.user._id;
const { role, company, experience, topicsToFocus, description } = req.body;
const experienceNumber = Number(experience);
-
+
if (!role || role.trim() === "") {
return res.status(400).json({
success: false,
@@ -95,55 +73,19 @@ await createdSession[0].save({
session: mongoSession,
});
- // Update user streak on successful session creation
+ let newlyUnlocked = [];
const user = await User.findById(userId).session(mongoSession);
if (user) {
- const now = new Date();
- const getUTCDayDifference = (date1, date2) => {
- const d1 = new Date(date1);
- const d2 = new Date(date2);
- const utc1 = Date.UTC(d1.getUTCFullYear(), d1.getUTCMonth(), d1.getUTCDate());
- const utc2 = Date.UTC(d2.getUTCFullYear(), d2.getUTCMonth(), d2.getUTCDate());
- const msPerDay = 1000 * 60 * 60 * 24;
- return Math.floor((utc2 - utc1) / msPerDay);
- };
-
- if (!user.lastPracticeDate) {
- user.currentStreak = 1;
- } else {
- const diff = getUTCDayDifference(user.lastPracticeDate, now);
- if (diff === 1) {
- user.currentStreak += 1;
- } else if (diff > 1) {
- user.currentStreak = 1;
- } // if diff === 0, keep current streak (already practiced today)
- }
-
- user.lastPracticeDate = now;
-
- if (user.currentStreak > (user.longestStreak || 0)) {
- user.longestStreak = user.currentStreak;
- }
-
- // Check milestone badges
- const streakMilestones = [
- { days: 3, badge: "3-Day Streak" },
- { days: 7, badge: "7-Day Streak" },
- { days: 30, badge: "30-Day Streak" }
- ];
-
- for (const milestone of streakMilestones) {
- if (user.currentStreak >= milestone.days && !user.unlockedAchievements.includes(milestone.badge)) {
- user.unlockedAchievements.push(milestone.badge);
- }
- }
-
+ newlyUnlocked = applyStreakForActivity(user);
await user.save({ session: mongoSession });
}
res.status(201).json({
success: true,
session: createdSession[0],
+ // Streak milestones (e.g. "7-Day Streak") unlocked by this
+ // activity, if any ā lets the frontend show a toast.
+ newlyUnlockedAchievements: newlyUnlocked,
});
});
} catch (err) {
@@ -164,19 +106,6 @@ await createdSession[0].save({
}
};
-/**
- * Get all sessions for the authenticated user.
- * @route GET /api/sessions/my-sessions
- * @param {import('express').Request} req
- * @param {import('express').Response} res
- * @returns {Promise}
- * @throws {Error} When fetch fails.
- * @example
- * GET /api/sessions/my-sessions
- * Authorization: Bearer eyJhb...
- * @example
- * 200 [{"_id":"...","role":"...","questions":[...]}]
- */
exports.getMySessions = async (req, res) => {
try {
const userId = req.user._id;
@@ -190,19 +119,6 @@ exports.getMySessions = async (req, res) => {
}
};
-/**
- * Get a specific session by ID with populated questions.
- * @route GET /api/sessions/:id
- * @param {import('express').Request} req
- * @param {import('express').Response} res
- * @returns {Promise}
- * @throws {Error} When session is not found.
- * @example
- * GET /api/sessions/6426c5a5...
- * Authorization: Bearer eyJhb...
- * @example
- * 200 {"success": true, "session": {"_id":"...","questions":[...]}}
- */
exports.getSessionById = async (req, res) => {
try {
const session = await Session.findById(req.params.id)
@@ -228,20 +144,6 @@ exports.getSessionById = async (req, res) => {
}
};
-/**
- * Delete a session and all linked questions for the authenticated user.
- * @route DELETE /api/sessions/:id
- * @param {import('express').Request} req
- * @param {import('express').Response} res
- * @returns {Promise}
- * @throws {Error} When session is missing or not owned by user.
- * @example
- * DELETE /api/sessions/6426c5a5...
- * Authorization: Bearer eyJhb...
- * @example
- * 200 {"message":"Session delete sucessfully"}
- */
-
exports.deleteSession = async (req, res) => {
const transaction = await mongoose.startSession();
try {
@@ -289,4 +191,4 @@ exports.deleteSession = async (req, res) => {
} finally {
await transaction.endSession();
}
-};
+};
\ No newline at end of file
diff --git a/backend/controllers/userSheetProgressController.js b/backend/controllers/userSheetProgressController.js
index 8fbd6eae..4ed4712d 100644
--- a/backend/controllers/userSheetProgressController.js
+++ b/backend/controllers/userSheetProgressController.js
@@ -1,4 +1,5 @@
const UserSheetProgress = require("../models/UserSheetProgress");
+const { recordActivity } = require("../utils/streakTracker");
const {
normalizeProgressItems,
buildBulkOps,
@@ -60,10 +61,19 @@ exports.saveProgress = async (req, res) => {
});
}
- if (completedTopics !== undefined && !Array.isArray(completedTopics)) {
+ if (
+ completedTopics !== undefined &&
+ (typeof completedTopics !== "object" ||
+ completedTopics === null ||
+ Array.isArray(completedTopics) ||
+ Object.values(completedTopics).some(
+ (value) => typeof value !== "boolean"
+ ))
+ ) {
return res.status(400).json({
success: false,
- error: "Invalid completedTopics field, must be an array",
+ error:
+ "Invalid completedTopics field, must be an object of boolean flags",
});
}
@@ -90,26 +100,26 @@ exports.saveProgress = async (req, res) => {
if (percentage !== undefined) updateFields.percentage = percentage;
try {
- const progress = await UserSheetProgress.findOneAndUpdate(
- {
- userId,
- sheetId: validatedSheetId,
- },
- {
- $set: updateFields,
- },
- {
- upsert: true,
- new: true,
- setDefaultsOnInsert: true,
- }
- );
+ const progress = await UserSheetProgress.findOneAndUpdate(
+ { userId, sheetId: validatedSheetId },
+ { $set: updateFields },
+ { upsert: true, new: true, setDefaultsOnInsert: true }
+ );
- return res.json({
- success: true,
- progress,
- });
- } catch (err) {
+ let newlyUnlocked = [];
+ try {
+ const streakResult = await recordActivity(userId);
+ newlyUnlocked = streakResult?.newlyUnlocked || [];
+ } catch (streakErr) {
+ console.error("Streak tracking failed:", streakErr);
+ }
+
+ return res.json({
+ success: true,
+ progress,
+ newlyUnlockedAchievements: newlyUnlocked,
+ });
+ } catch (err) {
// Rare duplicate-key race during concurrent upserts.
if (err.code === 11000) {
try {
@@ -126,9 +136,12 @@ exports.saveProgress = async (req, res) => {
}
);
+ const { newlyUnlocked: retryNewlyUnlocked } = await recordActivity(userId);
+
return res.json({
success: true,
progress,
+ newlyUnlockedAchievements: retryNewlyUnlocked,
});
} catch (retryErr) {
return res.status(500).json({
@@ -305,4 +318,4 @@ exports.importProgress = async (req, res) => {
error: "Internal server error occurred",
});
}
-};
\ No newline at end of file
+};
diff --git a/backend/models/Bookmark.js b/backend/models/Bookmark.js
new file mode 100644
index 00000000..e8a677fc
--- /dev/null
+++ b/backend/models/Bookmark.js
@@ -0,0 +1,72 @@
+const mongoose = require("mongoose");
+
+const BookmarkSchema = new mongoose.Schema(
+ {
+ user: {
+ type: mongoose.Schema.Types.ObjectId,
+ ref: "User",
+ required: true,
+ index: true,
+ },
+ question: {
+ type: String,
+ required: true,
+ trim: true,
+ maxlength: 500,
+ },
+ answer: {
+ type: String,
+ default: "",
+ maxlength: 2000,
+ },
+ category: {
+ type: String,
+ enum: [
+ "DSA",
+ "System Design",
+ "Behavioral",
+ "Aptitude",
+ "Coding",
+ "OS",
+ "DBMS",
+ "Networking",
+ "Other",
+ ],
+ default: "DSA",
+ },
+ difficulty: {
+ type: String,
+ enum: ["Easy", "Medium", "Hard"],
+ default: "Medium",
+ },
+ tags: {
+ type: [String],
+ default: [],
+ },
+ notes: {
+ type: String,
+ default: "",
+ maxlength: 500,
+ },
+ source: {
+ type: String,
+ enum: ["manual", "session", "sheet", "imported"],
+ default: "manual",
+ },
+ sourceRef: {
+ type: mongoose.Schema.Types.ObjectId,
+ default: null,
+ },
+ starred: {
+ type: Boolean,
+ default: false,
+ },
+ },
+ { timestamps: true }
+);
+
+BookmarkSchema.index({ user: 1, category: 1 });
+BookmarkSchema.index({ user: 1, starred: 1 });
+BookmarkSchema.index({ user: 1, tags: 1 });
+
+module.exports = mongoose.model("Bookmark", BookmarkSchema);
diff --git a/backend/models/InterviewExperience.js b/backend/models/InterviewExperience.js
index e4344558..3a849989 100644
--- a/backend/models/InterviewExperience.js
+++ b/backend/models/InterviewExperience.js
@@ -96,11 +96,23 @@ const interviewExperienceSchema = new mongoose.Schema(
);
interviewExperienceSchema.index(
- { idempotencyKey: 1 },
+ { idempotencyKey: 1, userId: 1 },
{
unique: true,
partialFilterExpression: {
idempotencyKey: { $type: "string", $gt: "" },
+ userId: { $type: "objectId" },
+ },
+ },
+);
+
+interviewExperienceSchema.index(
+ { idempotencyKey: 1, clientKey: 1 },
+ {
+ unique: true,
+ partialFilterExpression: {
+ idempotencyKey: { $type: "string", $gt: "" },
+ clientKey: { $type: "string", $gt: "" },
},
},
);
diff --git a/backend/package-lock.json b/backend/package-lock.json
index de17123a..073f4457 100644
--- a/backend/package-lock.json
+++ b/backend/package-lock.json
@@ -20,10 +20,11 @@
"express-rate-limit": "^8.2.1",
"file-type": "^18.5.0",
"form-data": "^4.0.5",
+ "googleapis": "^176.0.0",
"helmet": "^8.3.0",
"joi": "^18.0.2",
"jsonwebtoken": "^9.0.2",
- "mongoose": "^8.18.0",
+ "mongoose": "^8.24.3",
"multer": "^2.2.0",
"node-cache": "^5.1.2",
"nodemailer": "^9.0.4",
@@ -32,7 +33,7 @@
"zod": "^4.4.3"
},
"devDependencies": {
- "nodemon": "^3.1.10",
+ "nodemon": "^3.1.14",
"supertest": "^7.2.2",
"vitest": "^4.1.9"
}
@@ -152,6 +153,23 @@
"@hapi/hoek": "^11.0.2"
}
},
+ "node_modules/@isaacs/cliui": {
+ "version": "8.0.2",
+ "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz",
+ "integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==",
+ "license": "ISC",
+ "dependencies": {
+ "string-width": "^5.1.2",
+ "string-width-cjs": "npm:string-width@^4.2.0",
+ "strip-ansi": "^7.0.1",
+ "strip-ansi-cjs": "npm:strip-ansi@^6.0.1",
+ "wrap-ansi": "^8.1.0",
+ "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0"
+ },
+ "engines": {
+ "node": ">=12"
+ }
+ },
"node_modules/@jridgewell/sourcemap-codec": {
"version": "1.5.5",
"resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz",
@@ -404,6 +422,16 @@
"@noble/hashes": "^1.1.5"
}
},
+ "node_modules/@pkgjs/parseargs": {
+ "version": "0.11.0",
+ "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz",
+ "integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==",
+ "license": "MIT",
+ "optional": true,
+ "engines": {
+ "node": ">=14"
+ }
+ },
"node_modules/@rolldown/binding-android-arm64": {
"version": "1.1.3",
"resolved": "https://registry.npmjs.org/@rolldown/binding-android-arm64/-/binding-android-arm64-1.1.3.tgz",
@@ -918,6 +946,30 @@
"node": ">= 14"
}
},
+ "node_modules/ansi-regex": {
+ "version": "6.3.0",
+ "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.3.0.tgz",
+ "integrity": "sha512-WpDfL7NO6j7tH88IDBNVdUJxDh9nmCteAVW9dsep846XdwF4naCBK+/tGLX3KJgcpgMRXCFlTM2hKGoK9FsdrQ==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/ansi-regex?sponsor=1"
+ }
+ },
+ "node_modules/ansi-styles": {
+ "version": "6.2.3",
+ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.3.tgz",
+ "integrity": "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/ansi-styles?sponsor=1"
+ }
+ },
"node_modules/anymatch": {
"version": "3.1.3",
"resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz",
@@ -1002,7 +1054,6 @@
"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": {
@@ -1081,14 +1132,26 @@
}
},
"node_modules/brace-expansion": {
- "version": "1.1.15",
- "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.15.tgz",
- "integrity": "sha512-EwOCDEex4quD37XhqM3omwtMoJjr//isUZz1JopUNWms+4Z2ViyM/k1YIRePpoVNnQhENnxtFjLaxNHrT7xIUg==",
+ "version": "5.0.9",
+ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.9.tgz",
+ "integrity": "sha512-ScQ4IuvIEF1TMlP7Zt+vjJ//9zlPb2SDcxWxM3bk8s6t6GGdJ7KO1dCcTidOPJKePW30LE/2cT7wCyPho9/Wxg==",
"dev": true,
"license": "MIT",
"dependencies": {
- "balanced-match": "^1.0.0",
- "concat-map": "0.0.1"
+ "balanced-match": "^4.0.2"
+ },
+ "engines": {
+ "node": "20 || >=22"
+ }
+ },
+ "node_modules/brace-expansion/node_modules/balanced-match": {
+ "version": "4.0.4",
+ "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz",
+ "integrity": "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": "18 || 20 || >=22"
}
},
"node_modules/braces": {
@@ -1242,6 +1305,24 @@
"node": ">=0.8"
}
},
+ "node_modules/color-convert": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
+ "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
+ "license": "MIT",
+ "dependencies": {
+ "color-name": "~1.1.4"
+ },
+ "engines": {
+ "node": ">=7.0.0"
+ }
+ },
+ "node_modules/color-name": {
+ "version": "1.1.4",
+ "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
+ "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
+ "license": "MIT"
+ },
"node_modules/combined-stream": {
"version": "1.0.8",
"resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz",
@@ -1264,13 +1345,6 @@
"url": "https://github.com/sponsors/sindresorhus"
}
},
- "node_modules/concat-map": {
- "version": "0.0.1",
- "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
- "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==",
- "dev": true,
- "license": "MIT"
- },
"node_modules/concat-stream": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-2.0.0.tgz",
@@ -1371,6 +1445,29 @@
"node": ">= 0.10"
}
},
+ "node_modules/cross-spawn": {
+ "version": "7.0.6",
+ "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz",
+ "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==",
+ "license": "MIT",
+ "dependencies": {
+ "path-key": "^3.1.0",
+ "shebang-command": "^2.0.0",
+ "which": "^2.0.1"
+ },
+ "engines": {
+ "node": ">= 8"
+ }
+ },
+ "node_modules/data-uri-to-buffer": {
+ "version": "4.0.1",
+ "resolved": "https://registry.npmjs.org/data-uri-to-buffer/-/data-uri-to-buffer-4.0.1.tgz",
+ "integrity": "sha512-0R9ikRb668HB7QDxT1vkpuUBtqc53YyAwMwGeUFKRojY/NWKvdZ+9UYtRfGmhqNbRkTSVpMbmyhXipFFv2cb/A==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 12"
+ }
+ },
"node_modules/debug": {
"version": "4.4.3",
"resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz",
@@ -1453,6 +1550,12 @@
"node": ">= 0.4"
}
},
+ "node_modules/eastasianwidth": {
+ "version": "0.2.0",
+ "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz",
+ "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==",
+ "license": "MIT"
+ },
"node_modules/ecdsa-sig-formatter": {
"version": "1.0.11",
"resolved": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz",
@@ -1468,6 +1571,12 @@
"integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==",
"license": "MIT"
},
+ "node_modules/emoji-regex": {
+ "version": "9.2.2",
+ "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz",
+ "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==",
+ "license": "MIT"
+ },
"node_modules/encodeurl": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz",
@@ -1661,6 +1770,29 @@
"integrity": "sha512-n11RGP/lrWEFI/bWdygLxhI+pVeo1ZYIVwvvPkW7azl/rOy+F3HYRZ2K5zeE9mmkhQppyv9sQFx0JM9UabnpPQ==",
"license": "Unlicense"
},
+ "node_modules/fetch-blob": {
+ "version": "3.2.0",
+ "resolved": "https://registry.npmjs.org/fetch-blob/-/fetch-blob-3.2.0.tgz",
+ "integrity": "sha512-7yAQpD2UMJzLi1Dqv7qFYnPbaPx7ZfFK6PiIxQ4PfkGPyNyl2Ugx+a/umUonmKqjhM4DnfbMvdX6otXq83soQQ==",
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/jimmywarting"
+ },
+ {
+ "type": "paypal",
+ "url": "https://paypal.me/jimmywarting"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "node-domexception": "^1.0.0",
+ "web-streams-polyfill": "^3.0.3"
+ },
+ "engines": {
+ "node": "^12.20 || >= 14.13"
+ }
+ },
"node_modules/file-type": {
"version": "18.7.0",
"resolved": "https://registry.npmjs.org/file-type/-/file-type-18.7.0.tgz",
@@ -1728,6 +1860,22 @@
}
}
},
+ "node_modules/foreground-child": {
+ "version": "3.3.1",
+ "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.1.tgz",
+ "integrity": "sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==",
+ "license": "ISC",
+ "dependencies": {
+ "cross-spawn": "^7.0.6",
+ "signal-exit": "^4.0.1"
+ },
+ "engines": {
+ "node": ">=14"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/isaacs"
+ }
+ },
"node_modules/form-data": {
"version": "4.0.5",
"resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.5.tgz",
@@ -1765,6 +1913,18 @@
"node": ">= 0.6"
}
},
+ "node_modules/formdata-polyfill": {
+ "version": "4.0.10",
+ "resolved": "https://registry.npmjs.org/formdata-polyfill/-/formdata-polyfill-4.0.10.tgz",
+ "integrity": "sha512-buewHzMvYL29jdeQTVILecSaZKnt/RJWjoZCF5OW60Z67/GmSLBkOFM7qh1PI3zFNtJbaZL5eQu1vLfazOwj4g==",
+ "license": "MIT",
+ "dependencies": {
+ "fetch-blob": "^3.1.2"
+ },
+ "engines": {
+ "node": ">=12.20.0"
+ }
+ },
"node_modules/formidable": {
"version": "3.5.4",
"resolved": "https://registry.npmjs.org/formidable/-/formidable-3.5.4.tgz",
@@ -1892,6 +2052,27 @@
"node": ">= 0.4"
}
},
+ "node_modules/glob": {
+ "version": "10.5.0",
+ "resolved": "https://registry.npmjs.org/glob/-/glob-10.5.0.tgz",
+ "integrity": "sha512-DfXN8DfhJ7NH3Oe7cFmu3NCu1wKbkReJ8TorzSAFbSKrlNaQSKfIzqYqVY8zlbs2NLBbWpRiU52GX2PbaBVNkg==",
+ "deprecated": "Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me",
+ "license": "ISC",
+ "dependencies": {
+ "foreground-child": "^3.1.0",
+ "jackspeak": "^3.1.2",
+ "minimatch": "^9.0.4",
+ "minipass": "^7.1.2",
+ "package-json-from-dist": "^1.0.0",
+ "path-scurry": "^1.11.1"
+ },
+ "bin": {
+ "glob": "dist/esm/bin.mjs"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/isaacs"
+ }
+ },
"node_modules/glob-parent": {
"version": "5.1.2",
"resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz",
@@ -1905,6 +2086,30 @@
"node": ">= 6"
}
},
+ "node_modules/glob/node_modules/brace-expansion": {
+ "version": "2.1.4",
+ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.1.4.tgz",
+ "integrity": "sha512-hGfVzPxthbf3+2yjg/RBs60cB0FhqBS/zvdV/4wn4/BmN0bNMMHPc4V/BbFieqf1TKAGGAHnY4eSjajCl0f2Xg==",
+ "license": "MIT",
+ "dependencies": {
+ "balanced-match": "^1.0.0"
+ }
+ },
+ "node_modules/glob/node_modules/minimatch": {
+ "version": "9.0.9",
+ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.9.tgz",
+ "integrity": "sha512-OBwBN9AL4dqmETlpS2zasx+vTeWclWzkblfZk7KTA5j3jeOONz/tRCnZomUyvNg83wL5Zv9Ss6HMJXAgL8R2Yg==",
+ "license": "ISC",
+ "dependencies": {
+ "brace-expansion": "^2.0.2"
+ },
+ "engines": {
+ "node": ">=16 || 14 >=14.17"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/isaacs"
+ }
+ },
"node_modules/google-auth-library": {
"version": "9.15.1",
"resolved": "https://registry.npmjs.org/google-auth-library/-/google-auth-library-9.15.1.tgz",
@@ -1931,6 +2136,233 @@
"node": ">=14"
}
},
+ "node_modules/googleapis": {
+ "version": "176.0.0",
+ "resolved": "https://registry.npmjs.org/googleapis/-/googleapis-176.0.0.tgz",
+ "integrity": "sha512-v24+WfeIw3R9iF8LT7eixlawezy5SGGNB/WjFV8qeiBS9G6KCsqaFngsnTXvBrVH4womehN2Co8UYJxZ0KpbvQ==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "google-auth-library": "10.5.0",
+ "googleapis-common": "^8.0.0"
+ },
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/googleapis-common": {
+ "version": "8.0.3",
+ "resolved": "https://registry.npmjs.org/googleapis-common/-/googleapis-common-8.0.3.tgz",
+ "integrity": "sha512-7g1yzQKx0mmNTjiK0H9dJ8eqKqDBveES9vLHeg5neb3BMQy/d1oQefIMhIpOVT8a+f+LOcixMEdRbFIW/cQUJw==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "extend": "^3.0.2",
+ "gaxios": "7.1.3",
+ "google-auth-library": "10.5.0",
+ "google-logging-utils": "1.1.3",
+ "qs": "^6.7.0",
+ "url-template": "^2.0.8"
+ },
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/googleapis-common/node_modules/gaxios": {
+ "version": "7.1.3",
+ "resolved": "https://registry.npmjs.org/gaxios/-/gaxios-7.1.3.tgz",
+ "integrity": "sha512-YGGyuEdVIjqxkxVH1pUTMY/XtmmsApXrCVv5EU25iX6inEPbV+VakJfLealkBtJN69AQmh1eGOdCl9Sm1UP6XQ==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "extend": "^3.0.2",
+ "https-proxy-agent": "^7.0.1",
+ "node-fetch": "^3.3.2",
+ "rimraf": "^5.0.1"
+ },
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/googleapis-common/node_modules/gcp-metadata": {
+ "version": "8.1.4",
+ "resolved": "https://registry.npmjs.org/gcp-metadata/-/gcp-metadata-8.1.4.tgz",
+ "integrity": "sha512-iJ9KMsiu+xKtNRX0PmGLSaIU3bUBAyzWTyqKemKPzNPsmmsBCQYmlNg+brEbES7IHSXtdVwzBPzx1vz3FAaipw==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "gaxios": "7.1.3",
+ "google-logging-utils": "1.1.3",
+ "json-bigint": "^1.0.0"
+ },
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/googleapis-common/node_modules/google-auth-library": {
+ "version": "10.5.0",
+ "resolved": "https://registry.npmjs.org/google-auth-library/-/google-auth-library-10.5.0.tgz",
+ "integrity": "sha512-7ABviyMOlX5hIVD60YOfHw4/CxOfBhyduaYB+wbFWCWoni4N7SLcV46hrVRktuBbZjFC9ONyqamZITN7q3n32w==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "base64-js": "^1.3.0",
+ "ecdsa-sig-formatter": "^1.0.11",
+ "gaxios": "^7.0.0",
+ "gcp-metadata": "^8.0.0",
+ "google-logging-utils": "^1.0.0",
+ "gtoken": "^8.0.0",
+ "jws": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/googleapis-common/node_modules/google-logging-utils": {
+ "version": "1.1.3",
+ "resolved": "https://registry.npmjs.org/google-logging-utils/-/google-logging-utils-1.1.3.tgz",
+ "integrity": "sha512-eAmLkjDjAFCVXg7A1unxHsLf961m6y17QFqXqAXGj/gVkKFrEICfStRfwUlGNfeCEjNRa32JEWOUTlYXPyyKvA==",
+ "license": "Apache-2.0",
+ "engines": {
+ "node": ">=14"
+ }
+ },
+ "node_modules/googleapis-common/node_modules/gtoken": {
+ "version": "8.0.0",
+ "resolved": "https://registry.npmjs.org/gtoken/-/gtoken-8.0.0.tgz",
+ "integrity": "sha512-+CqsMbHPiSTdtSO14O51eMNlrp9N79gmeqmXeouJOhfucAedHw9noVe/n5uJk3tbKE6a+6ZCQg3RPhVhHByAIw==",
+ "license": "MIT",
+ "dependencies": {
+ "gaxios": "^7.0.0",
+ "jws": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/googleapis-common/node_modules/node-fetch": {
+ "version": "3.3.2",
+ "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-3.3.2.tgz",
+ "integrity": "sha512-dRB78srN/l6gqWulah9SrxeYnxeddIG30+GOqK/9OlLVyLg3HPnr6SqOWTWOXKRwC2eGYCkZ59NNuSgvSrpgOA==",
+ "license": "MIT",
+ "dependencies": {
+ "data-uri-to-buffer": "^4.0.0",
+ "fetch-blob": "^3.1.4",
+ "formdata-polyfill": "^4.0.10"
+ },
+ "engines": {
+ "node": "^12.20.0 || ^14.13.1 || >=16.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/node-fetch"
+ }
+ },
+ "node_modules/googleapis/node_modules/gaxios": {
+ "version": "7.3.1",
+ "resolved": "https://registry.npmjs.org/gaxios/-/gaxios-7.3.1.tgz",
+ "integrity": "sha512-kB3rzJV7d9juLZh8/56QTXCwQfxyhdOMdyYk1HdQKFtF8TJTDTZQJtixWIwXdE9Jji91mC41DUNpjleo4L4eAQ==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "extend": "^3.0.2",
+ "https-proxy-agent": "^7.0.1",
+ "node-fetch": "^3.3.2"
+ },
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/googleapis/node_modules/gcp-metadata": {
+ "version": "8.1.4",
+ "resolved": "https://registry.npmjs.org/gcp-metadata/-/gcp-metadata-8.1.4.tgz",
+ "integrity": "sha512-iJ9KMsiu+xKtNRX0PmGLSaIU3bUBAyzWTyqKemKPzNPsmmsBCQYmlNg+brEbES7IHSXtdVwzBPzx1vz3FAaipw==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "gaxios": "7.1.3",
+ "google-logging-utils": "1.1.3",
+ "json-bigint": "^1.0.0"
+ },
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/googleapis/node_modules/gcp-metadata/node_modules/gaxios": {
+ "version": "7.1.3",
+ "resolved": "https://registry.npmjs.org/gaxios/-/gaxios-7.1.3.tgz",
+ "integrity": "sha512-YGGyuEdVIjqxkxVH1pUTMY/XtmmsApXrCVv5EU25iX6inEPbV+VakJfLealkBtJN69AQmh1eGOdCl9Sm1UP6XQ==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "extend": "^3.0.2",
+ "https-proxy-agent": "^7.0.1",
+ "node-fetch": "^3.3.2",
+ "rimraf": "^5.0.1"
+ },
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/googleapis/node_modules/gcp-metadata/node_modules/google-logging-utils": {
+ "version": "1.1.3",
+ "resolved": "https://registry.npmjs.org/google-logging-utils/-/google-logging-utils-1.1.3.tgz",
+ "integrity": "sha512-eAmLkjDjAFCVXg7A1unxHsLf961m6y17QFqXqAXGj/gVkKFrEICfStRfwUlGNfeCEjNRa32JEWOUTlYXPyyKvA==",
+ "license": "Apache-2.0",
+ "engines": {
+ "node": ">=14"
+ }
+ },
+ "node_modules/googleapis/node_modules/google-auth-library": {
+ "version": "10.5.0",
+ "resolved": "https://registry.npmjs.org/google-auth-library/-/google-auth-library-10.5.0.tgz",
+ "integrity": "sha512-7ABviyMOlX5hIVD60YOfHw4/CxOfBhyduaYB+wbFWCWoni4N7SLcV46hrVRktuBbZjFC9ONyqamZITN7q3n32w==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "base64-js": "^1.3.0",
+ "ecdsa-sig-formatter": "^1.0.11",
+ "gaxios": "^7.0.0",
+ "gcp-metadata": "^8.0.0",
+ "google-logging-utils": "^1.0.0",
+ "gtoken": "^8.0.0",
+ "jws": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/googleapis/node_modules/google-logging-utils": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/google-logging-utils/-/google-logging-utils-1.2.0.tgz",
+ "integrity": "sha512-WE9av4wKDZgRjBwgVUabocx8T6/7o3Ca1Fat46FXDhXVAFibzNadedcOXrdgd1Kzmk8tsk/9ZH89Wyf/SqeZ3A==",
+ "license": "Apache-2.0",
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/googleapis/node_modules/gtoken": {
+ "version": "8.0.0",
+ "resolved": "https://registry.npmjs.org/gtoken/-/gtoken-8.0.0.tgz",
+ "integrity": "sha512-+CqsMbHPiSTdtSO14O51eMNlrp9N79gmeqmXeouJOhfucAedHw9noVe/n5uJk3tbKE6a+6ZCQg3RPhVhHByAIw==",
+ "license": "MIT",
+ "dependencies": {
+ "gaxios": "^7.0.0",
+ "jws": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/googleapis/node_modules/node-fetch": {
+ "version": "3.3.2",
+ "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-3.3.2.tgz",
+ "integrity": "sha512-dRB78srN/l6gqWulah9SrxeYnxeddIG30+GOqK/9OlLVyLg3HPnr6SqOWTWOXKRwC2eGYCkZ59NNuSgvSrpgOA==",
+ "license": "MIT",
+ "dependencies": {
+ "data-uri-to-buffer": "^4.0.0",
+ "fetch-blob": "^3.1.4",
+ "formdata-polyfill": "^4.0.10"
+ },
+ "engines": {
+ "node": "^12.20.0 || ^14.13.1 || >=16.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/node-fetch"
+ }
+ },
"node_modules/gopd": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz",
@@ -2140,6 +2572,15 @@
"node": ">=0.10.0"
}
},
+ "node_modules/is-fullwidth-code-point": {
+ "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==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ }
+ },
"node_modules/is-glob": {
"version": "4.0.3",
"resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz",
@@ -2181,6 +2622,27 @@
"url": "https://github.com/sponsors/sindresorhus"
}
},
+ "node_modules/isexe": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz",
+ "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==",
+ "license": "ISC"
+ },
+ "node_modules/jackspeak": {
+ "version": "3.4.3",
+ "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-3.4.3.tgz",
+ "integrity": "sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==",
+ "license": "BlueOak-1.0.0",
+ "dependencies": {
+ "@isaacs/cliui": "^8.0.2"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/isaacs"
+ },
+ "optionalDependencies": {
+ "@pkgjs/parseargs": "^0.11.0"
+ }
+ },
"node_modules/joi": {
"version": "18.0.2",
"resolved": "https://registry.npmjs.org/joi/-/joi-18.0.2.tgz",
@@ -2584,6 +3046,12 @@
"integrity": "sha512-Sb487aTOCr9drQVL8pIxOzVhafOjZN9UU54hiN8PU3uAiSV7lx1yYNpbNmex2PK6dSJoNTSJUUswT651yww3Mg==",
"license": "MIT"
},
+ "node_modules/lru-cache": {
+ "version": "10.4.3",
+ "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz",
+ "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==",
+ "license": "ISC"
+ },
"node_modules/magic-string": {
"version": "0.30.21",
"resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.21.tgz",
@@ -2675,16 +3143,28 @@
}
},
"node_modules/minimatch": {
- "version": "3.1.5",
- "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz",
- "integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==",
+ "version": "10.2.6",
+ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.6.tgz",
+ "integrity": "sha512-vpLQEs+VLCr1nU0BXS07maYoFwlDAH0gngQuuttxIwutDFEMHq2blX+8vpgxDdK3J1PwjCJiep77OitTZ4Ll1A==",
"dev": true,
- "license": "ISC",
+ "license": "BlueOak-1.0.0",
"dependencies": {
- "brace-expansion": "^1.1.7"
+ "brace-expansion": "^5.0.8"
},
"engines": {
- "node": "*"
+ "node": "18 || 20 || >=22"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/isaacs"
+ }
+ },
+ "node_modules/minipass": {
+ "version": "7.1.3",
+ "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.3.tgz",
+ "integrity": "sha512-tEBHqDnIoM/1rXME1zgka9g6Q2lcoCkxHLuc7ODJ5BxbP5d4c2Z5cGgtXAku59200Cx7diuHTOYfSBD8n6mm8A==",
+ "license": "BlueOak-1.0.0",
+ "engines": {
+ "node": ">=16 || 14 >=14.17"
}
},
"node_modules/mongodb-connection-string-url": {
@@ -2732,9 +3212,9 @@
}
},
"node_modules/mongoose": {
- "version": "8.24.0",
- "resolved": "https://registry.npmjs.org/mongoose/-/mongoose-8.24.0.tgz",
- "integrity": "sha512-EEZwOibDPZ5uZN3bFapfnRskEbdljAf6sP9ln6u+P4e5IfkOAh6Tqw2g8/Tag++KHOAJ095WXT/c0uqRq4Vckg==",
+ "version": "8.24.3",
+ "resolved": "https://registry.npmjs.org/mongoose/-/mongoose-8.24.3.tgz",
+ "integrity": "sha512-CMHqVyjK0Szc6irT14+O+QpN30kHxJLa0FjjLiBz/Txivmfg3mUUdMWAXfYM1Ubl0SNUkcG2cyLKOnhvttLtOA==",
"license": "MIT",
"dependencies": {
"bson": "^6.10.4",
@@ -2753,67 +3233,6 @@
"url": "https://opencollective.com/mongoose"
}
},
- "node_modules/mongoose/node_modules/agent-base": {
- "version": "6.0.2",
- "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz",
- "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==",
- "license": "MIT",
- "optional": true,
- "peer": true,
- "dependencies": {
- "debug": "4"
- },
- "engines": {
- "node": ">= 6.0.0"
- }
- },
- "node_modules/mongoose/node_modules/gaxios": {
- "version": "5.1.3",
- "resolved": "https://registry.npmjs.org/gaxios/-/gaxios-5.1.3.tgz",
- "integrity": "sha512-95hVgBRgEIRQQQHIbnxBXeHbW4TqFk4ZDJW7wmVtvYar72FdhRIo1UGOLS2eRAKCPEdPBWu+M7+A33D9CdX9rA==",
- "license": "Apache-2.0",
- "optional": true,
- "peer": true,
- "dependencies": {
- "extend": "^3.0.2",
- "https-proxy-agent": "^5.0.0",
- "is-stream": "^2.0.0",
- "node-fetch": "^2.6.9"
- },
- "engines": {
- "node": ">=12"
- }
- },
- "node_modules/mongoose/node_modules/gcp-metadata": {
- "version": "5.3.0",
- "resolved": "https://registry.npmjs.org/gcp-metadata/-/gcp-metadata-5.3.0.tgz",
- "integrity": "sha512-FNTkdNEnBdlqF2oatizolQqNANMrcqJt6AAYt99B3y1aLLC8Hc5IOBb+ZnnzllodEEf6xMBp6wRcBbc16fa65w==",
- "license": "Apache-2.0",
- "optional": true,
- "peer": true,
- "dependencies": {
- "gaxios": "^5.0.0",
- "json-bigint": "^1.0.0"
- },
- "engines": {
- "node": ">=12"
- }
- },
- "node_modules/mongoose/node_modules/https-proxy-agent": {
- "version": "5.0.1",
- "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz",
- "integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==",
- "license": "MIT",
- "optional": true,
- "peer": true,
- "dependencies": {
- "agent-base": "6",
- "debug": "4"
- },
- "engines": {
- "node": ">= 6"
- }
- },
"node_modules/mongoose/node_modules/mongodb": {
"version": "6.20.0",
"resolved": "https://registry.npmjs.org/mongodb/-/mongodb-6.20.0.tgz",
@@ -2989,6 +3408,26 @@
"node": ">= 8.0.0"
}
},
+ "node_modules/node-domexception": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/node-domexception/-/node-domexception-1.0.0.tgz",
+ "integrity": "sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ==",
+ "deprecated": "Use your platform's native DOMException instead",
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/jimmywarting"
+ },
+ {
+ "type": "github",
+ "url": "https://paypal.me/jimmywarting"
+ }
+ ],
+ "license": "MIT",
+ "engines": {
+ "node": ">=10.5.0"
+ }
+ },
"node_modules/node-fetch": {
"version": "2.7.0",
"resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz",
@@ -3019,16 +3458,16 @@
}
},
"node_modules/nodemon": {
- "version": "3.1.10",
- "resolved": "https://registry.npmjs.org/nodemon/-/nodemon-3.1.10.tgz",
- "integrity": "sha512-WDjw3pJ0/0jMFmyNDp3gvY2YizjLmmOUQo6DEBY+JgdvW/yQ9mEeSw6H5ythl5Ny2ytb7f9C2nIbjSxMNzbJXw==",
+ "version": "3.1.14",
+ "resolved": "https://registry.npmjs.org/nodemon/-/nodemon-3.1.14.tgz",
+ "integrity": "sha512-jakjZi93UtB3jHMWsXL68FXSAosbLfY0In5gtKq3niLSkrWznrVBzXFNOEMJUfc9+Ke7SHWoAZsiMkNP3vq6Jw==",
"dev": true,
"license": "MIT",
"dependencies": {
"chokidar": "^3.5.2",
"debug": "^4",
"ignore-by-default": "^1.0.1",
- "minimatch": "^3.1.2",
+ "minimatch": "^10.2.1",
"pstree.remy": "^1.1.8",
"semver": "^7.5.3",
"simple-update-notifier": "^2.0.0",
@@ -3113,6 +3552,12 @@
"wrappy": "1"
}
},
+ "node_modules/package-json-from-dist": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz",
+ "integrity": "sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==",
+ "license": "BlueOak-1.0.0"
+ },
"node_modules/parseurl": {
"version": "1.3.3",
"resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz",
@@ -3122,6 +3567,31 @@
"node": ">= 0.8"
}
},
+ "node_modules/path-key": {
+ "version": "3.1.1",
+ "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz",
+ "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "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==",
+ "license": "BlueOak-1.0.0",
+ "dependencies": {
+ "lru-cache": "^10.2.0",
+ "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0"
+ },
+ "engines": {
+ "node": ">=16 || 14 >=14.18"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/isaacs"
+ }
+ },
"node_modules/path-to-regexp": {
"version": "8.4.2",
"resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-8.4.2.tgz",
@@ -3405,6 +3875,21 @@
}
}
},
+ "node_modules/rimraf": {
+ "version": "5.0.10",
+ "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-5.0.10.tgz",
+ "integrity": "sha512-l0OE8wL34P4nJH/H2ffoaniAokM2qSmrtXHmlpvYr5AVVX8msAyW0l8NVJFDxlSK4u3Uh/f41cQheDVdnYijwQ==",
+ "license": "ISC",
+ "dependencies": {
+ "glob": "^10.3.7"
+ },
+ "bin": {
+ "rimraf": "dist/esm/bin.mjs"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/isaacs"
+ }
+ },
"node_modules/rolldown": {
"version": "1.1.3",
"resolved": "https://registry.npmjs.org/rolldown/-/rolldown-1.1.3.tgz",
@@ -3536,6 +4021,27 @@
"integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==",
"license": "ISC"
},
+ "node_modules/shebang-command": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz",
+ "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==",
+ "license": "MIT",
+ "dependencies": {
+ "shebang-regex": "^3.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/shebang-regex": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz",
+ "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ }
+ },
"node_modules/side-channel": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.0.tgz",
@@ -3621,6 +4127,18 @@
"dev": true,
"license": "ISC"
},
+ "node_modules/signal-exit": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz",
+ "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==",
+ "license": "ISC",
+ "engines": {
+ "node": ">=14"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/isaacs"
+ }
+ },
"node_modules/simple-update-notifier": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/simple-update-notifier/-/simple-update-notifier-2.0.0.tgz",
@@ -3703,6 +4221,102 @@
"safe-buffer": "~5.2.0"
}
},
+ "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==",
+ "license": "MIT",
+ "dependencies": {
+ "eastasianwidth": "^0.2.0",
+ "emoji-regex": "^9.2.2",
+ "strip-ansi": "^7.0.1"
+ },
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/string-width-cjs": {
+ "name": "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==",
+ "license": "MIT",
+ "dependencies": {
+ "emoji-regex": "^8.0.0",
+ "is-fullwidth-code-point": "^3.0.0",
+ "strip-ansi": "^6.0.1"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/string-width-cjs/node_modules/ansi-regex": {
+ "version": "5.0.1",
+ "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
+ "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/string-width-cjs/node_modules/emoji-regex": {
+ "version": "8.0.0",
+ "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
+ "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==",
+ "license": "MIT"
+ },
+ "node_modules/string-width-cjs/node_modules/strip-ansi": {
+ "version": "6.0.1",
+ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
+ "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
+ "license": "MIT",
+ "dependencies": {
+ "ansi-regex": "^5.0.1"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/strip-ansi": {
+ "version": "7.2.0",
+ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.2.0.tgz",
+ "integrity": "sha512-yDPMNjp4WyfYBkHnjIRLfca1i6KMyGCtsVgoKe/z1+6vukgaENdgGBZt+ZmKPc4gavvEZ5OgHfHdrazhgNyG7w==",
+ "license": "MIT",
+ "dependencies": {
+ "ansi-regex": "^6.2.2"
+ },
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/strip-ansi?sponsor=1"
+ }
+ },
+ "node_modules/strip-ansi-cjs": {
+ "name": "strip-ansi",
+ "version": "6.0.1",
+ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
+ "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
+ "license": "MIT",
+ "dependencies": {
+ "ansi-regex": "^5.0.1"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/strip-ansi-cjs/node_modules/ansi-regex": {
+ "version": "5.0.1",
+ "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
+ "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ }
+ },
"node_modules/strtok3": {
"version": "7.1.1",
"resolved": "https://registry.npmjs.org/strtok3/-/strtok3-7.1.1.tgz",
@@ -3943,6 +4557,12 @@
"node": ">= 0.8"
}
},
+ "node_modules/url-template": {
+ "version": "2.0.8",
+ "resolved": "https://registry.npmjs.org/url-template/-/url-template-2.0.8.tgz",
+ "integrity": "sha512-XdVKMF4SJ0nP/O7XIPB0JwAEuT9lDIYnNsK8yGVe43y0AWoKeJNdv3ZNWh7ksJ6KqQFjOO6ox/VEitLnaVNufw==",
+ "license": "BSD"
+ },
"node_modules/util-deprecate": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
@@ -4165,6 +4785,15 @@
"url": "https://github.com/sponsors/jonschlinkert"
}
},
+ "node_modules/web-streams-polyfill": {
+ "version": "3.3.3",
+ "resolved": "https://registry.npmjs.org/web-streams-polyfill/-/web-streams-polyfill-3.3.3.tgz",
+ "integrity": "sha512-d2JWLCivmZYTSIoge9MsgFCZrt571BikcWGYkjC1khllbTeDlGqZ2D8vD8E/lJa8WGWbb7Plm8/XJYV7IJHZZw==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 8"
+ }
+ },
"node_modules/webidl-conversions": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz",
@@ -4181,6 +4810,21 @@
"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==",
+ "license": "ISC",
+ "dependencies": {
+ "isexe": "^2.0.0"
+ },
+ "bin": {
+ "node-which": "bin/node-which"
+ },
+ "engines": {
+ "node": ">= 8"
+ }
+ },
"node_modules/why-is-node-running": {
"version": "2.3.0",
"resolved": "https://registry.npmjs.org/why-is-node-running/-/why-is-node-running-2.3.0.tgz",
@@ -4198,6 +4842,97 @@
"node": ">=8"
}
},
+ "node_modules/wrap-ansi": {
+ "version": "8.1.0",
+ "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz",
+ "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==",
+ "license": "MIT",
+ "dependencies": {
+ "ansi-styles": "^6.1.0",
+ "string-width": "^5.0.1",
+ "strip-ansi": "^7.0.1"
+ },
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/wrap-ansi?sponsor=1"
+ }
+ },
+ "node_modules/wrap-ansi-cjs": {
+ "name": "wrap-ansi",
+ "version": "7.0.0",
+ "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz",
+ "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==",
+ "license": "MIT",
+ "dependencies": {
+ "ansi-styles": "^4.0.0",
+ "string-width": "^4.1.0",
+ "strip-ansi": "^6.0.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/wrap-ansi?sponsor=1"
+ }
+ },
+ "node_modules/wrap-ansi-cjs/node_modules/ansi-regex": {
+ "version": "5.0.1",
+ "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
+ "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/wrap-ansi-cjs/node_modules/ansi-styles": {
+ "version": "4.3.0",
+ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
+ "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
+ "license": "MIT",
+ "dependencies": {
+ "color-convert": "^2.0.1"
+ },
+ "engines": {
+ "node": ">=8"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/ansi-styles?sponsor=1"
+ }
+ },
+ "node_modules/wrap-ansi-cjs/node_modules/emoji-regex": {
+ "version": "8.0.0",
+ "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
+ "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==",
+ "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==",
+ "license": "MIT",
+ "dependencies": {
+ "emoji-regex": "^8.0.0",
+ "is-fullwidth-code-point": "^3.0.0",
+ "strip-ansi": "^6.0.1"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/wrap-ansi-cjs/node_modules/strip-ansi": {
+ "version": "6.0.1",
+ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
+ "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
+ "license": "MIT",
+ "dependencies": {
+ "ansi-regex": "^5.0.1"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
"node_modules/wrappy": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
diff --git a/backend/package.json b/backend/package.json
index 60bff769..9e548445 100644
--- a/backend/package.json
+++ b/backend/package.json
@@ -23,10 +23,11 @@
"express-rate-limit": "^8.2.1",
"file-type": "^18.5.0",
"form-data": "^4.0.5",
+ "googleapis": "^176.0.0",
"helmet": "^8.3.0",
"joi": "^18.0.2",
"jsonwebtoken": "^9.0.2",
- "mongoose": "^8.18.0",
+ "mongoose": "^8.24.3",
"multer": "^2.2.0",
"node-cache": "^5.1.2",
"nodemailer": "^9.0.4",
@@ -35,11 +36,15 @@
"zod": "^4.4.3"
},
"devDependencies": {
- "nodemon": "^3.1.10",
+ "nodemon": "^3.1.14",
"supertest": "^7.2.2",
"vitest": "^4.1.9"
},
"overrides": {
"ip-address": "10.3.1"
- }
+ },
+ "directories": {
+ "test": "tests"
+ },
+ "type": "commonjs"
}
diff --git a/backend/routes/bookmarkRoutes.js b/backend/routes/bookmarkRoutes.js
new file mode 100644
index 00000000..f37729e7
--- /dev/null
+++ b/backend/routes/bookmarkRoutes.js
@@ -0,0 +1,30 @@
+const express = require("express");
+const router = express.Router();
+const { protect } = require("../middlewares/authMiddleware");
+const {
+ validateCreateBookmark,
+ validateUpdateBookmark,
+} = require("../Input_validators/ValidateBookmark");
+const {
+ createBookmark,
+ getBookmarks,
+ getBookmarkById,
+ updateBookmark,
+ toggleStar,
+ deleteBookmark,
+ getTags,
+ getStats,
+} = require("../controllers/bookmarkController");
+
+router.use(protect);
+
+router.post("/", validateCreateBookmark, createBookmark);
+router.get("/", getBookmarks);
+router.get("/tags", getTags);
+router.get("/stats", getStats);
+router.get("/:id", getBookmarkById);
+router.put("/:id", validateUpdateBookmark, updateBookmark);
+router.patch("/:id/star", toggleStar);
+router.delete("/:id", deleteBookmark);
+
+module.exports = router;
diff --git a/backend/routes/googleCalendarRoutes.js b/backend/routes/googleCalendarRoutes.js
new file mode 100644
index 00000000..1305821c
--- /dev/null
+++ b/backend/routes/googleCalendarRoutes.js
@@ -0,0 +1,40 @@
+const express = require("express");
+
+const router = express.Router();
+
+// GET /api/google-calendar/connect
+router.get("/connect", (req, res) => {
+ res.json({
+ success: true,
+ message: "Google Calendar connect route is working",
+ });
+});
+
+// GET /api/google-calendar/callback
+router.get("/callback", (req, res) => {
+ res.json({
+ success: true,
+ message: "Google Calendar callback route is working",
+ });
+});
+
+// GET /api/google-calendar/status
+router.get("/status", (req, res) => {
+ res.json({
+ success: true,
+ connected: false,
+ });
+});
+
+// POST /api/google-calendar/events
+router.post("/events", (req, res) => {
+ const { events } = req.body;
+
+ res.json({
+ success: true,
+ message: "Calendar events received successfully",
+ count: Array.isArray(events) ? events.length : 0,
+ });
+});
+
+module.exports = router;
\ No newline at end of file
diff --git a/backend/routes/resumeRoutes.js b/backend/routes/resumeRoutes.js
index deadbe5c..c7ed8651 100644
--- a/backend/routes/resumeRoutes.js
+++ b/backend/routes/resumeRoutes.js
@@ -1,6 +1,6 @@
const express = require('express');
const router = express.Router();
-const { compileResume, analyzeResume, saveResume, getMyResumes, deleteResume, getResumeAnalysisHistory } = require('../controllers/resumeController');
+const { compileResume, analyzeResume, saveResume, getMyResumes, deleteResume, getResumeAnalysisHistory, atsMatch } = require('../controllers/resumeController');
const { protect } = require('../middlewares/authMiddleware');
const { upload, uploadResume, validateResumeMagicBytes } = require('../middlewares/uploadMiddleware');
const { aiLimiter } = require('../middlewares/rateLimiter');
@@ -33,6 +33,11 @@ router.get('/my-resumes', getMyResumes);
// @access Private
router.get('/analysis-history', getResumeAnalysisHistory);
+// @route POST /api/resume/ats-match
+// @desc Deterministic keyword-overlap score between a resume and a JD
+// @access Private ā pure keyword match, no AI/file, so no aiLimiter needed
+router.post('/ats-match', atsMatch);
+
// @route DELETE /api/resume/:id
// @desc Delete a saved resume by ID
// @access Private
diff --git a/backend/server.js b/backend/server.js
index ff400658..1a89a682 100644
--- a/backend/server.js
+++ b/backend/server.js
@@ -7,7 +7,8 @@ const express = require("express");
process.on("unhandledRejection", (err) => {
console.error("Unhandled Promise Rejection:", err);
});
-
+//caleder------------
+const googleCalendarRoutes = require("./routes/googleCalendarRoutes");
const path = require("path");
const connectDB = require("./config/db");
const cookieParser = require("cookie-parser");
@@ -159,6 +160,8 @@ const roadmapRoutes = require("./routes/roadmapRoutes");
app.use("/api/roadmaps", roadmapRoutes);
const interviewExperienceRoutes = require("./routes/interviewExperienceRoutes");
app.use("/api/interview-experiences", generalLimiter, interviewExperienceRoutes);
+const bookmarkRoutes = require("./routes/bookmarkRoutes");
+app.use("/api/bookmarks", generalLimiter, bookmarkRoutes);
app.use(
@@ -215,4 +218,20 @@ process.on("uncaughtException", (err) => {
process.on("unhandledRejection", (reason, promise) => {
console.error("Unhandled Rejection at:", promise, "reason:", reason);
process.exit(1);
-});
\ No newline at end of file
+});
+
+app.use("/api/books", generalLimiter, booksRoutes);
+app.use("/api/jobs", jobRoutes);
+
+app.use("/api/google-calendar", googleCalendarRoutes);
+
+
+app.use(
+ "/api/google-calendar",
+ generalLimiter,
+ googleCalendarRoutes
+);
+
+
+app.use("/api/courses", generalLimiter, coursesRoutes);
+
diff --git a/backend/tests/atsKeywordMatch.unit.test.js b/backend/tests/atsKeywordMatch.unit.test.js
new file mode 100644
index 00000000..f9f1e3d7
--- /dev/null
+++ b/backend/tests/atsKeywordMatch.unit.test.js
@@ -0,0 +1,85 @@
+import { describe, it, expect } from "vitest";
+import {
+ extractKeywords,
+ matchResumeKeywords,
+} from "../utils/atsKeywordMatch.js";
+
+// ---------------------------------------------------------------------------
+// atsKeywordMatch unit tests.
+// Covers extractKeywords tokenization and matchResumeKeywords scoring.
+// ---------------------------------------------------------------------------
+
+describe("extractKeywords", () => {
+ it("lowercases and dedupes", () => {
+ expect(extractKeywords("React react REACT")).toEqual(["react"]);
+ });
+
+ it("removes stopwords", () => {
+ expect(extractKeywords("the and or with")).toEqual([]);
+ });
+
+ it("drops tokens shorter than 2 chars", () => {
+ expect(extractKeywords("a x go")).toEqual(["go"]);
+ });
+
+ it("preserves c++, c#, node.js as single tokens", () => {
+ expect(extractKeywords("C++ C# Node.js")).toEqual(["c++", "c#", "node.js"]);
+ });
+
+ it("strips surrounding punctuation but keeps first-seen order", () => {
+ expect(extractKeywords("React, Node; MongoDB.")).toEqual([
+ "react",
+ "node",
+ "mongodb",
+ ]);
+ });
+
+ it("returns [] for non-string or empty input", () => {
+ expect(extractKeywords(null)).toEqual([]);
+ expect(extractKeywords(123)).toEqual([]);
+ expect(extractKeywords("")).toEqual([]);
+ });
+});
+
+describe("matchResumeKeywords", () => {
+ it("scores 100 with full overlap and no missing keywords", () => {
+ const r = matchResumeKeywords("React Node MongoDB", "React Node MongoDB");
+ expect(r.score).toBe(100);
+ expect(r.missing).toEqual([]);
+ expect(r.totalKeywords).toBe(3);
+ });
+
+ it("scores partial overlap and lists matched + missing", () => {
+ const r = matchResumeKeywords("React Node", "React Node GraphQL AWS");
+ expect(r.score).toBe(50);
+ expect(r.matched).toEqual(["react", "node"]);
+ expect(r.missing).toEqual(["graphql", "aws"]);
+ expect(r.totalKeywords).toBe(4);
+ });
+
+ it("scores 0 with no overlap", () => {
+ const r = matchResumeKeywords("Python Django", "React Node");
+ expect(r.score).toBe(0);
+ expect(r.matched).toEqual([]);
+ });
+
+ it("returns a zeroed result for an empty job description", () => {
+ expect(matchResumeKeywords("React Node", "")).toEqual({
+ score: 0,
+ matched: [],
+ missing: [],
+ totalKeywords: 0,
+ });
+ });
+
+ it("handles non-string inputs gracefully", () => {
+ const zero = { score: 0, matched: [], missing: [], totalKeywords: 0 };
+ expect(matchResumeKeywords(null, undefined)).toEqual(zero);
+ expect(matchResumeKeywords(123, 456)).toEqual(zero);
+ });
+
+ it("does not let stopwords inflate totalKeywords", () => {
+ const r = matchResumeKeywords("React", "the React and Node");
+ expect(r.totalKeywords).toBe(2);
+ });
+});
diff --git a/backend/tests/authRegistration.unit.test.js b/backend/tests/authRegistration.unit.test.js
new file mode 100644
index 00000000..e87a3027
--- /dev/null
+++ b/backend/tests/authRegistration.unit.test.js
@@ -0,0 +1,114 @@
+import { describe, it, expect, vi, beforeEach } from 'vitest';
+const bcrypt = require('bcryptjs');
+const User = require('../models/User');
+const { registerUser, loginUser } = require('../controllers/authController');
+
+beforeEach(() => {
+ vi.restoreAllMocks();
+ process.env.JWT_SECRET = 'test_jwt_secret_key_12345';
+});
+
+function makeRes() {
+ const res = {};
+ res.status = vi.fn().mockReturnValue(res);
+ res.json = vi.fn().mockReturnValue(res);
+ res.cookie = vi.fn().mockReturnValue(res);
+ return res;
+}
+
+describe('Auth Password Hashing & Login Integration', () => {
+ it('hashes raw registration password exactly once via pre-save hook and passes isValidPassword', async () => {
+ const rawPassword = 'Password123!';
+
+ const userDoc = new User();
+ userDoc.name = 'Jane Doe';
+ userDoc.email = 'jane@example.com';
+ userDoc.password = rawPassword;
+
+ vi.spyOn(userDoc, 'isModified').mockReturnValue(true);
+
+ // Find the password hashing pre-save hook registered on UserSchema
+ const saveHooks = User.schema.s.hooks._pres.get('save') || [];
+ const pwHook = saveHooks.find(h => h.fn.toString().includes('password'))?.fn;
+ expect(pwHook).toBeDefined();
+
+ await pwHook.call(userDoc);
+
+ // 1. Password must be hashed exactly once (bcrypt format)
+ expect(userDoc.password).not.toBe(rawPassword);
+ expect(userDoc.password).toMatch(/^\$2[aby]\$\d+\$/);
+
+ // 2. Raw password must match the hashed password via isValidPassword
+ const isMatch = await userDoc.isValidPassword(rawPassword);
+ expect(isMatch).toBe(true);
+
+ // 3. Incorrect password must fail isValidPassword
+ const isWrongMatch = await userDoc.isValidPassword('WrongPassword123!');
+ expect(isWrongMatch).toBe(false);
+ });
+
+ it('registerUser passes raw password to User.create and returns 201', async () => {
+ vi.spyOn(User, 'findOne').mockResolvedValue(null);
+
+ let createdUserData = null;
+ vi.spyOn(User, 'create').mockImplementation(async (data) => {
+ createdUserData = data;
+ return {
+ _id: '507f1f77bcf86cd799439011',
+ ...data,
+ save: vi.fn().mockResolvedValue(true), // Mock save method to prevent 500 error
+ };
+ });
+
+ const req = {
+ body: {
+ name: 'Jane Doe',
+ email: 'jane@example.com',
+ password: 'Password123!',
+ },
+ };
+ const res = makeRes();
+
+ await registerUser(req, res);
+
+ expect(createdUserData).not.toBeNull();
+ // Raw password is passed to User.create so pre("save") hook handles single hashing
+ expect(createdUserData.password).toBe('Password123!');
+ expect(res.status).toHaveBeenCalledWith(201);
+ });
+
+ it('loginUser succeeds for a verified user with correct password', async () => {
+ const rawPassword = 'Password123!';
+ const salt = await bcrypt.genSalt(10);
+ const hashedPassword = await bcrypt.hash(rawPassword, salt);
+
+ const mockUser = new User({
+ _id: '507f1f77bcf86cd799439011',
+ name: 'Jane Doe',
+ email: 'jane@example.com',
+ password: hashedPassword,
+ isEmailVerified: true,
+ tokenVersion: 0,
+ });
+ mockUser.save = vi.fn().mockResolvedValue(true);
+
+ vi.spyOn(User, 'findOne').mockResolvedValue(mockUser);
+
+ const req = {
+ body: {
+ email: 'jane@example.com',
+ password: rawPassword,
+ },
+ };
+ const res = makeRes();
+
+ await loginUser(req, res);
+
+ expect(res.json).toHaveBeenCalledWith(
+ expect.objectContaining({
+ success: true,
+ accessToken: expect.any(String),
+ })
+ );
+ });
+});
\ No newline at end of file
diff --git a/backend/tests/flashcardController.sm2.unit.test.js b/backend/tests/flashcardController.sm2.unit.test.js
new file mode 100644
index 00000000..bf1f0909
--- /dev/null
+++ b/backend/tests/flashcardController.sm2.unit.test.js
@@ -0,0 +1,178 @@
+import { describe, it, expect, vi, beforeEach } from "vitest";
+
+// ---------------------------------------------------------------------------
+// SM-2 Unit Tests
+// Tests the calculateSM2 helper exported from flashcardController.js.
+// Uses the same Module._load shim pattern as other unit tests in this suite.
+// ---------------------------------------------------------------------------
+
+const sessionMock = vi.hoisted(() => ({ findOne: vi.fn() }));
+const flashcardMock = vi.hoisted(() => ({
+ findOne: vi.fn(),
+ findOneAndUpdate: vi.fn(),
+ find: vi.fn(),
+ create: vi.fn(),
+ findById: vi.fn(),
+}));
+
+const testDoubles = new Map([
+ ["../models/Session", sessionMock],
+ ["../models/Flashcard", flashcardMock],
+]);
+
+const originalLoad = require("module").Module._load;
+require("module").Module._load = function (request, parent, isMain) {
+ if (testDoubles.has(request)) {
+ return testDoubles.get(request);
+ }
+ return originalLoad.call(this, request, parent, isMain);
+};
+
+beforeEach(() => {
+ vi.clearAllMocks();
+});
+
+// Lazily import after mocks are wired so the controller uses test doubles.
+let calculateSM2;
+
+const getSM2 = async () => {
+ if (calculateSM2) return calculateSM2;
+ const mod = await import("../controllers/flashcardController.js");
+ calculateSM2 = mod.calculateSM2;
+ return calculateSM2;
+};
+
+describe("calculateSM2 ā rating \"again\"", () => {
+ it("resets repetition to 0 and interval to 1 on first \"again\"", async () => {
+ const fn = await getSM2();
+ const result = fn({ interval: 10, repetition: 3, efactor: 2.5 }, "again");
+ expect(result.repetition).toBe(0);
+ expect(result.interval).toBe(1);
+ });
+
+ it("accepts string rating \"1\" as equivalent to \"again\"", async () => {
+ const fn = await getSM2();
+ const result = fn({ interval: 10, repetition: 3, efactor: 2.5 }, "1");
+ expect(result.repetition).toBe(0);
+ expect(result.interval).toBe(1);
+ });
+});
+
+describe("calculateSM2 ā rating \"hard\"", () => {
+ it("keeps repetition at 0 on first hard review", async () => {
+ const fn = await getSM2();
+ const result = fn({ interval: 0, repetition: 0, efactor: 2.5 }, "hard");
+ expect(result.repetition).toBe(1);
+ expect(result.interval).toBe(1);
+ });
+
+ it("limits interval progression on hard (interval * 1.2, min 1)", async () => {
+ const fn = await getSM2();
+ const result = fn({ interval: 10, repetition: 2, efactor: 2.5 }, "hard");
+ expect(result.interval).toBeGreaterThanOrEqual(1);
+ expect(result.interval).toBeLessThanOrEqual(Math.round(10 * 1.2));
+ });
+
+ it("accepts string rating \"2\" as equivalent to \"hard\"", async () => {
+ const fn = await getSM2();
+ const result = fn({ interval: 5, repetition: 1, efactor: 2.5 }, "2");
+ expect(result.repetition).toBeGreaterThanOrEqual(1);
+ });
+});
+
+describe("calculateSM2 ā rating \"good\" / \"medium\"", () => {
+ it("sets interval to 1 on first successful review (repetition 0)", async () => {
+ const fn = await getSM2();
+ const result = fn({ interval: 0, repetition: 0, efactor: 2.5 }, "good");
+ expect(result.repetition).toBe(1);
+ expect(result.interval).toBe(1);
+ });
+
+ it("sets interval to 6 on second successful review", async () => {
+ const fn = await getSM2();
+ const result = fn({ interval: 1, repetition: 1, efactor: 2.5 }, "medium");
+ expect(result.repetition).toBe(2);
+ expect(result.interval).toBe(6);
+ });
+
+ it("multiplies interval by efactor on third+ review", async () => {
+ const fn = await getSM2();
+ const result = fn({ interval: 6, repetition: 2, efactor: 2.5 }, "good");
+ expect(result.repetition).toBe(3);
+ expect(result.interval).toBe(Math.round(6 * 2.5));
+ });
+
+ it("accepts string rating \"3\" as equivalent to \"good\"", async () => {
+ const fn = await getSM2();
+ const result = fn({ interval: 0, repetition: 0, efactor: 2.5 }, "3");
+ expect(result.repetition).toBe(1);
+ });
+});
+
+describe("calculateSM2 ā rating \"easy\"", () => {
+ it("sets interval to 2 on first easy review", async () => {
+ const fn = await getSM2();
+ const result = fn({ interval: 0, repetition: 0, efactor: 2.5 }, "easy");
+ expect(result.repetition).toBe(1);
+ expect(result.interval).toBe(2);
+ });
+
+ it("uses efactor * 1.3 as multiplier on third+ review", async () => {
+ const fn = await getSM2();
+ const result = fn({ interval: 6, repetition: 2, efactor: 2.5 }, "easy");
+ expect(result.repetition).toBe(3);
+ expect(result.interval).toBe(Math.round(6 * 2.5 * 1.3));
+ });
+
+ it("accepts string rating \"4\" as equivalent to \"easy\"", async () => {
+ const fn = await getSM2();
+ const result = fn({ interval: 0, repetition: 0, efactor: 2.5 }, "4");
+ expect(result.repetition).toBe(1);
+ expect(result.interval).toBe(2);
+ });
+});
+
+describe("calculateSM2 ā ease factor", () => {
+ it("never goes below 1.3", async () => {
+ const fn = await getSM2();
+ // Repeated failures should drive efactor down but never below 1.3
+ let state = { interval: 0, repetition: 0, efactor: 1.5 };
+ for (let i = 0; i < 10; i++) {
+ state = fn(state, "again");
+ }
+ expect(state.efactor).toBeGreaterThanOrEqual(1.3);
+ });
+
+ it("is rounded to 2 decimal places", async () => {
+ const fn = await getSM2();
+ const result = fn({ interval: 1, repetition: 1, efactor: 2.5 }, "good");
+ // The result should have at most 2 decimal places
+ expect(result.efactor.toString()).toMatch(/^\d+\.?\d{0,2}$/);
+ });
+
+ it("increases on easy ratings", async () => {
+ const fn = await getSM2();
+ const result = fn({ interval: 6, repetition: 2, efactor: 2.5 }, "easy");
+ expect(result.efactor).toBeGreaterThan(2.5);
+ });
+});
+
+describe("calculateSM2 ā dueDate", () => {
+ it("returns a Date in the future based on the new interval", async () => {
+ const fn = await getSM2();
+ const before = Date.now();
+ const result = fn({ interval: 0, repetition: 0, efactor: 2.5 }, "good");
+ expect(result.dueDate instanceof Date).toBe(true);
+ expect(result.dueDate.getTime()).toBeGreaterThan(before);
+ });
+
+ it("interval of 1 day adds approximately 1 day in milliseconds", async () => {
+ const fn = await getSM2();
+ const before = Date.now();
+ const result = fn({ interval: 0, repetition: 0, efactor: 2.5 }, "good");
+ const expectedMin = before + 1 * 24 * 60 * 60 * 1000;
+ const expectedMax = before + 2 * 24 * 60 * 60 * 1000;
+ expect(result.dueDate.getTime()).toBeGreaterThanOrEqual(expectedMin);
+ expect(result.dueDate.getTime()).toBeLessThanOrEqual(expectedMax);
+ });
+});
diff --git a/backend/tests/interviewExperienceController.unit.test.js b/backend/tests/interviewExperienceController.unit.test.js
index 0dbfcbd7..0953057a 100644
--- a/backend/tests/interviewExperienceController.unit.test.js
+++ b/backend/tests/interviewExperienceController.unit.test.js
@@ -164,6 +164,7 @@ describe("createInterviewExperience", () => {
expect(InterviewExperience.findOne).toHaveBeenCalledWith({
idempotencyKey: "submit-key-abc12345",
+ clientKey: "11111111-1111-4111-8111-111111111111",
});
expect(InterviewExperience.create).not.toHaveBeenCalled();
expect(res.status).toHaveBeenCalledWith(200);
@@ -176,6 +177,56 @@ describe("createInterviewExperience", () => {
}),
);
});
+
+ it("scopes the dedup to the authenticated user so a re-used key does not leak another author's submission", async () => {
+ InterviewExperience.findOne = vi.fn().mockResolvedValue(null);
+ InterviewExperience.create = vi.fn().mockResolvedValue(sampleDoc);
+
+ const userA = { _id: "507f1f77bcf86cd799439011" };
+ const req = makeReq(
+ {
+ company: "Google",
+ role: "SDE-2",
+ summary: "Summary",
+ idempotencyKey: "shared-key-abc12345",
+ },
+ {},
+ {},
+ userA,
+ );
+ const res = makeRes();
+
+ await createInterviewExperience(req, res);
+
+ expect(InterviewExperience.findOne).toHaveBeenCalledWith({
+ idempotencyKey: "shared-key-abc12345",
+ userId: "507f1f77bcf86cd799439011",
+ });
+ expect(InterviewExperience.create).toHaveBeenCalled();
+ expect(res.status).toHaveBeenCalledWith(201);
+ });
+
+ it("isolates anonymous submissions by clientKey so different visitors with the same key both persist", async () => {
+ InterviewExperience.findOne = vi.fn().mockResolvedValue(null);
+ InterviewExperience.create = vi.fn().mockResolvedValue(sampleDoc);
+
+ const req = makeReq({
+ company: "Google",
+ role: "SDE-2",
+ summary: "Summary",
+ clientKey: "22222222-2222-4222-8222-222222222222",
+ idempotencyKey: "shared-key-abc12345",
+ });
+ const res = makeRes();
+
+ await createInterviewExperience(req, res);
+
+ expect(InterviewExperience.findOne).toHaveBeenCalledWith({
+ idempotencyKey: "shared-key-abc12345",
+ clientKey: "22222222-2222-4222-8222-222222222222",
+ });
+ expect(InterviewExperience.create).toHaveBeenCalled();
+ });
});
describe("getMyInterviewExperiences", () => {
diff --git a/backend/tests/userSheetProgressController.unit.test.js b/backend/tests/userSheetProgressController.unit.test.js
index 0a17edc5..e19d7ddc 100644
--- a/backend/tests/userSheetProgressController.unit.test.js
+++ b/backend/tests/userSheetProgressController.unit.test.js
@@ -4,11 +4,16 @@ import { describe, it, expect, beforeAll, beforeEach, afterEach, vi } from "vite
// ---------------------------------------------------------------------------
// userSheetProgressController.js is CommonJS and loads its deps via
// require(), which vitest's vi.mock cannot intercept. We shim Node's module
-// loader so the real UserSheetProgress model is never touched.
+// loader so the real UserSheetProgress model and streak tracker are never touched.
//
// Covers issue #1449: saveProgress must reject out-of-range / malformed
// field values (percentage outside [0,100], non-boolean followed,
// non-object completedTopics) while still allowing partial payloads.
+//
+// Also mocks ../utils/streakTracker: saveProgress now calls recordActivity()
+// after a successful upsert (streak-milestone toast support). Without this
+// mock, recordActivity() would hit the real (unconnected) User model and
+// throw, which the controller's catch block turns into a false-negative 500.
// ---------------------------------------------------------------------------
const modelMock = vi.hoisted(() => ({
@@ -19,6 +24,10 @@ const modelMock = vi.hoisted(() => ({
bulkWrite: vi.fn(),
}));
+const streakTrackerMock = vi.hoisted(() => ({
+ recordActivity: vi.fn(),
+}));
+
const testDoubles = new Map();
const originalLoad = Module._load;
Module._load = function (request, parent, isMain) {
@@ -34,7 +43,9 @@ const clearRequireCache = () => {
key.includes("controllers\\userSheetProgressController") ||
key.includes("controllers/userSheetProgressController") ||
key.includes("models\\UserSheetProgress") ||
- key.includes("models/UserSheetProgress")
+ key.includes("models/UserSheetProgress") ||
+ key.includes("utils\\streakTracker") ||
+ key.includes("utils/streakTracker")
) {
delete require.cache[key];
}
@@ -55,6 +66,9 @@ beforeAll(async () => {
create: modelMock.create,
bulkWrite: modelMock.bulkWrite,
});
+ testDoubles.set("../utils/streakTracker", {
+ recordActivity: streakTrackerMock.recordActivity,
+ });
const mod = await import("../controllers/userSheetProgressController.js");
saveProgress = mod.saveProgress;
@@ -73,6 +87,12 @@ beforeEach(() => {
modelMock.findOneAndUpdate.mockReset();
modelMock.create.mockReset();
modelMock.bulkWrite.mockReset();
+
+ streakTrackerMock.recordActivity.mockReset();
+ // Sensible default so tests that don't care about streaks don't need to
+ // stub this individually; override with .mockResolvedValue(...) in any
+ // test that needs specific newlyUnlocked values.
+ streakTrackerMock.recordActivity.mockResolvedValue({ newlyUnlocked: [] });
});
afterEach(() => {
@@ -167,6 +187,7 @@ describe("saveProgress", () => {
percentage: 80,
};
modelMock.findOneAndUpdate.mockResolvedValue(fakeProgress);
+ streakTrackerMock.recordActivity.mockResolvedValue({ newlyUnlocked: [] });
const req = {
user: { _id: "user-1" },
@@ -181,7 +202,11 @@ describe("saveProgress", () => {
{ $set: { followed: true, percentage: 80 } },
expect.objectContaining({ upsert: true, new: true })
);
- expect(res.body).toEqual({ success: true, progress: fakeProgress });
+ expect(res.body).toEqual({
+ success: true,
+ progress: fakeProgress,
+ newlyUnlockedAchievements: [],
+ });
});
it("rejects an invalid sheetId with 400", async () => {
@@ -265,8 +290,8 @@ describe("getProgress", () => {
expect(res.body).toEqual({ success: true, progress: fakeProgress });
});
- it("rejects an invalid sheetId with 400", async () => {
- const req = { user: { _id: "user-4" }, params: { sheetId: "" } };
+ it("returns 400 for an invalid sheetId", async () => {
+ const req = { user: { _id: "user-3" }, params: { sheetId: " " } };
const res = mockRes();
await getProgress(req, res);
@@ -276,9 +301,9 @@ describe("getProgress", () => {
});
it("returns 500 when findOne throws", async () => {
- modelMock.findOne.mockRejectedValue(new Error("timeout"));
+ modelMock.findOne.mockRejectedValue(new Error("db error"));
- const req = { user: { _id: "user-4" }, params: { sheetId: "dp" } };
+ const req = { user: { _id: "user-3" }, params: { sheetId: "graphs" } };
const res = mockRes();
await getProgress(req, res);
diff --git a/backend/utils/atsKeywordMatch.js b/backend/utils/atsKeywordMatch.js
new file mode 100644
index 00000000..9c3fc781
--- /dev/null
+++ b/backend/utils/atsKeywordMatch.js
@@ -0,0 +1,75 @@
+// Deterministic ATS-style keyword overlap between a resume and a job
+// description. Unlike the Gemini-based /analyze flow, this is instant, free,
+// and fully unit-testable ā a quick "does my resume cover this JD" check.
+
+// Common words that carry no signal for keyword matching. Kept small and
+// obvious rather than a full linguistic stopword list.
+const STOPWORDS = new Set([
+ 'the', 'a', 'an', 'and', 'or', 'of', 'to', 'in', 'for', 'on', 'with',
+ 'at', 'by', 'is', 'are', 'be', 'as', 'that', 'this', 'it', 'you', 'your',
+ 'we', 'our', 'will', 'from', 'have', 'has', 'using', 'use', 'able', 'etc',
+]);
+
+// Strip leading/trailing punctuation from a token. '+' and '#' are kept at the
+// ends so "c++" and "c#" survive; '.' is allowed only *internally* (so
+// "node.js" survives) but trimmed at the ends (so a sentence-final "MongoDB."
+// becomes "mongodb"). We never split on these, so multi-char tech tokens stay
+// whole.
+const trimPunctuation = (token) => token.replace(/^[^a-z0-9+#]+|[^a-z0-9+#]+$/g, '');
+
+/**
+ * Extract unique, lowercased keywords from free text, preserving first-seen
+ * order. Stopwords and tokens shorter than 2 chars are dropped.
+ * @param {string} text
+ * @returns {string[]}
+ */
+function extractKeywords(text) {
+ if (typeof text !== 'string' || text.length === 0) return [];
+
+ const seen = new Set();
+ const keywords = [];
+
+ for (const raw of text.toLowerCase().split(/\s+/)) {
+ const token = trimPunctuation(raw);
+ if (token.length < 2 || STOPWORDS.has(token) || seen.has(token)) continue;
+ seen.add(token);
+ keywords.push(token);
+ }
+
+ return keywords;
+}
+
+/**
+ * Score how well a resume covers a job description's keywords.
+ * The JD drives the keyword set; `matched` are JD keywords present in the
+ * resume, `missing` are the rest. Score is the matched percentage (0-100).
+ * @param {string} resumeText
+ * @param {string} jobDescription
+ * @returns {{ score: number, matched: string[], missing: string[], totalKeywords: number }}
+ */
+function matchResumeKeywords(resumeText, jobDescription) {
+ const jdKeywords = extractKeywords(jobDescription);
+ const totalKeywords = jdKeywords.length;
+
+ if (totalKeywords === 0) {
+ return { score: 0, matched: [], missing: [], totalKeywords: 0 };
+ }
+
+ const resumeSet = new Set(extractKeywords(resumeText));
+ const matched = [];
+ const missing = [];
+
+ for (const keyword of jdKeywords) {
+ if (resumeSet.has(keyword)) matched.push(keyword);
+ else missing.push(keyword);
+ }
+
+ return {
+ score: Math.round((matched.length / totalKeywords) * 100),
+ matched,
+ missing,
+ totalKeywords,
+ };
+}
+
+module.exports = { extractKeywords, matchResumeKeywords, STOPWORDS };
diff --git a/backend/utils/nameCountry.js b/backend/utils/nameCountry.js
new file mode 100644
index 00000000..39984dae
--- /dev/null
+++ b/backend/utils/nameCountry.js
@@ -0,0 +1,207 @@
+const countries = [
+ "Afghanistan",
+ "Albania",
+ "Algeria",
+ "Andorra",
+ "Angola",
+ "Argentina",
+ "Armenia",
+ "Australia",
+ "Austria",
+ "Azerbaijan",
+ "Bahamas",
+ "Bahrain",
+ "Bangladesh",
+ "Barbados",
+ "Belarus",
+ "Belgium",
+ "Belize",
+ "Benin",
+ "Bhutan",
+ "Bolivia",
+ "Bosnia and Herzegovina",
+ "Botswana",
+ "Brazil",
+ "Brunei",
+ "Bulgaria",
+ "Burkina Faso",
+ "Burundi",
+ "Cambodia",
+ "Cameroon",
+ "Canada",
+ "Cape Verde",
+ "Central African Republic",
+ "Chad",
+ "Chile",
+ "China",
+ "Colombia",
+ "Comoros",
+ "Costa Rica",
+ "Croatia",
+ "Cuba",
+ "Cyprus",
+ "Czechia",
+ "Denmark",
+ "Djibouti",
+ "Dominica",
+ "Dominican Republic",
+ "Ecuador",
+ "Egypt",
+ "El Salvador",
+ "Equatorial Guinea",
+ "Eritrea",
+ "Estonia",
+ "Eswatini",
+ "Ethiopia",
+ "Fiji",
+ "Finland",
+ "France",
+ "Gabon",
+ "Gambia",
+ "Georgia",
+ "Germany",
+ "Ghana",
+ "Greece",
+ "Grenada",
+ "Guatemala",
+ "Guinea",
+ "Guinea-Bissau",
+ "Guyana",
+ "Haiti",
+ "Honduras",
+ "Hungary",
+ "Iceland",
+ "India",
+ "Indonesia",
+ "Iran",
+ "Iraq",
+ "Ireland",
+ "Israel",
+ "Italy",
+ "Jamaica",
+ "Japan",
+ "Jordan",
+ "Kazakhstan",
+ "Kenya",
+ "Kiribati",
+ "Kuwait",
+ "Kyrgyzstan",
+ "Laos",
+ "Latvia",
+ "Lebanon",
+ "Lesotho",
+ "Liberia",
+ "Libya",
+ "Liechtenstein",
+ "Lithuania",
+ "Luxembourg",
+ "Madagascar",
+ "Malawi",
+ "Malaysia",
+ "Maldives",
+ "Mali",
+ "Malta",
+ "Marshall Islands",
+ "Mauritania",
+ "Mauritius",
+ "Mexico",
+ "Micronesia",
+ "Moldova",
+ "Monaco",
+ "Mongolia",
+ "Montenegro",
+ "Morocco",
+ "Mozambique",
+ "Myanmar",
+ "Namibia",
+ "Nauru",
+ "Nepal",
+ "Netherlands",
+ "New Zealand",
+ "Nicaragua",
+ "Niger",
+ "Nigeria",
+ "North Korea",
+ "North Macedonia",
+ "Norway",
+ "Oman",
+ "Pakistan",
+ "Palau",
+ "Panama",
+ "Papua New Guinea",
+ "Paraguay",
+ "Peru",
+ "Philippines",
+ "Poland",
+ "Portugal",
+ "Qatar",
+ "Romania",
+ "Russia",
+ "Rwanda",
+ "Saint Kitts and Nevis",
+ "Saint Lucia",
+ "Saint Vincent and the Grenadines",
+ "Samoa",
+ "San Marino",
+ "Sao Tome and Principe",
+ "Saudi Arabia",
+ "Senegal",
+ "Serbia",
+ "Seychelles",
+ "Sierra Leone",
+ "Singapore",
+ "Slovakia",
+ "Slovenia",
+ "Solomon Islands",
+ "Somalia",
+ "South Africa",
+ "South Korea",
+ "South Sudan",
+ "Spain",
+ "Sri Lanka",
+ "Sudan",
+ "Suriname",
+ "Sweden",
+ "Switzerland",
+ "Syria",
+ "Taiwan",
+ "Tajikistan",
+ "Tanzania",
+ "Thailand",
+ "Timor-Leste",
+ "Togo",
+ "Tonga",
+ "Trinidad and Tobago",
+ "Tunisia",
+ "Turkey",
+ "Turkmenistan",
+ "Tuvalu",
+ "Uganda",
+ "Ukraine",
+ "United Arab Emirates",
+ "United Kingdom",
+ "United States",
+ "Uruguay",
+ "Uzbekistan",
+ "Vanuatu",
+ "Vatican City",
+ "Venezuela",
+ "Vietnam",
+ "Yemen",
+ "Zambia",
+ "Zimbabwe",
+ "CƓte d'Ivoire",
+ "SĆ£o TomĆ© and PrĆncipe"
+];
+
+const isValidCountry = (country) => {
+ if (typeof country !== "string" || !country.trim()) {
+ return false;
+ }
+
+ return countries.some(
+ (name) => name.toLocaleLowerCase() === country.trim().toLocaleLowerCase()
+ );
+};
+
+module.exports = { isValidCountry };
\ No newline at end of file
diff --git a/backend/utils/prompts.js b/backend/utils/prompts.js
index 8b0d0e3c..9f14e875 100644
--- a/backend/utils/prompts.js
+++ b/backend/utils/prompts.js
@@ -1,37 +1,23 @@
-const ALLOWED_ROLES = new Set([
- 'frontend developer',
- 'backend developer',
- 'full stack developer',
- 'react developer',
- 'node.js developer',
- 'python developer',
- 'java developer',
- 'devops engineer',
- 'cloud engineer',
- 'data scientist',
- 'machine learning engineer',
- 'systems engineer',
- 'software engineer',
- 'qa engineer',
- 'database administrator',
- 'web developer',
- 'mobile developer',
- 'ios developer',
- 'android developer',
- 'product manager',
- 'tech lead',
- 'solution architect',
- 'security engineer'
-]);
+const MAX_ROLE_LENGTH = 100;
const sanitizeRole = (role) => {
if (!role || typeof role !== 'string') {
throw new Error('Role must be a non-empty string');
}
- const trimmedRole = role.trim().toLowerCase();
- if (!ALLOWED_ROLES.has(trimmedRole)) {
- throw new Error(`Invalid role. Allowed roles: ${Array.from(ALLOWED_ROLES).join(', ')}`);
+ const trimmedRole = role
+ .trim()
+ .toLowerCase()
+ .replace(/[\u0000-\u001f\u007f]/g, '')
+ .replace(/\s+/g, ' ');
+
+ if (trimmedRole.length === 0) {
+ throw new Error('Role must be a non-empty string');
}
+
+ if (trimmedRole.length > MAX_ROLE_LENGTH) {
+ throw new Error(`Role must be at most ${MAX_ROLE_LENGTH} characters`);
+ }
+
return trimmedRole;
};
@@ -111,4 +97,4 @@ Important: Do NOT add any extra text outside the JSON format. Only return valid
`;
};
-module.exports = { questionAnswerPrompt, conceptExplainPrompt, interviewTipsPrompt, sanitizeRole, ALLOWED_ROLES };
\ No newline at end of file
+module.exports = { questionAnswerPrompt, conceptExplainPrompt, interviewTipsPrompt, sanitizeRole };
\ No newline at end of file
diff --git a/backend/utils/sendEmail.js b/backend/utils/sendEmail.js
index f3f65992..57168a2c 100644
--- a/backend/utils/sendEmail.js
+++ b/backend/utils/sendEmail.js
@@ -1,12 +1,19 @@
const nodemailer = require("nodemailer");
+// Hard deadline for a single sendMail call (10 seconds).
+// Without this, a misconfigured or unreachable SMTP server causes nodemailer
+// to hang indefinitely, which surfaces as a request timeout on /register.
+const SEND_TIMEOUT_MS = 10_000;
+
/**
* Creates the nodemailer transporter based on EMAIL_SERVICE env variable.
* Supports "gmail", "ethereal", or any custom SMTP provider.
* Set EMAIL_SERVICE in .env to switch between providers.
+ *
+ * Called per-send instead of once at module load so that a missing/wrong env
+ * var throws at send-time with a clear message rather than silently producing
+ * a broken transporter that hangs on every request.
*/
-
-
const createTransporter = () => {
const service = process.env.EMAIL_SERVICE?.toLowerCase();
@@ -16,6 +23,8 @@ const createTransporter = () => {
port: 465,
secure: true, // SSL ā works on Render (port 587 is blocked)
family: 4, // Force IPv4
+ connectionTimeout: SEND_TIMEOUT_MS,
+ socketTimeout: SEND_TIMEOUT_MS,
auth: {
user: process.env.EMAIL_USER,
pass: process.env.EMAIL_PASS,
@@ -29,6 +38,8 @@ const createTransporter = () => {
port: 587,
secure: false,
family: 4, // Force IPv4
+ connectionTimeout: SEND_TIMEOUT_MS,
+ socketTimeout: SEND_TIMEOUT_MS,
auth: {
user: process.env.EMAIL_USER,
pass: process.env.EMAIL_PASS,
@@ -41,6 +52,8 @@ const createTransporter = () => {
host: process.env.EMAIL_HOST,
port: parseInt(process.env.EMAIL_PORT, 10) || 587,
secure: process.env.EMAIL_SECURE === "true",
+ connectionTimeout: SEND_TIMEOUT_MS,
+ socketTimeout: SEND_TIMEOUT_MS,
auth: {
user: process.env.EMAIL_USER,
pass: process.env.EMAIL_PASS,
@@ -51,15 +64,17 @@ const createTransporter = () => {
});
};
-const transporter = createTransporter();
-
/**
* Send an email verification link to a newly registered user.
* @param {string} toEmail - Recipient email address.
* @param {string} verificationUrl - Full URL with token for email verification.
+ * @throws Will throw if SMTP credentials are missing, the connection times out,
+ * or the mail server rejects the message.
*/
const sendVerificationEmail = async (toEmail, verificationUrl) => {
- await transporter.sendMail({
+ const transporter = createTransporter();
+
+ const sendPromise = transporter.sendMail({
from: `"PrepPilot" <${process.env.EMAIL_USER}>`,
to: toEmail,
subject: "Verify your PrepPilot account",
@@ -79,6 +94,17 @@ const sendVerificationEmail = async (toEmail, verificationUrl) => {
`,
});
+
+ // Race the send against a hard deadline so a hung SMTP connection never
+ // blocks the HTTP response for more than SEND_TIMEOUT_MS.
+ const timeoutPromise = new Promise((_, reject) =>
+ setTimeout(
+ () => reject(new Error(`sendMail timed out after ${SEND_TIMEOUT_MS}ms`)),
+ SEND_TIMEOUT_MS
+ )
+ );
+
+ await Promise.race([sendPromise, timeoutPromise]);
};
-module.exports = { sendVerificationEmail };
\ No newline at end of file
+module.exports = { sendVerificationEmail };
diff --git a/backend/utils/streakTracker.js b/backend/utils/streakTracker.js
new file mode 100644
index 00000000..e25e64e1
--- /dev/null
+++ b/backend/utils/streakTracker.js
@@ -0,0 +1,86 @@
+const User = require("../models/User");
+
+const STREAK_MILESTONES = [
+ { days: 3, badge: "3-Day Streak" },
+ { days: 7, badge: "7-Day Streak" },
+ { days: 30, badge: "30-Day Streak" },
+];
+
+const getUTCDayDifference = (date1, date2) => {
+ const d1 = new Date(date1);
+ const d2 = new Date(date2);
+ const utc1 = Date.UTC(d1.getUTCFullYear(), d1.getUTCMonth(), d1.getUTCDate());
+ const utc2 = Date.UTC(d2.getUTCFullYear(), d2.getUTCMonth(), d2.getUTCDate());
+ return Math.floor((utc2 - utc1) / (1000 * 60 * 60 * 24));
+};
+
+// Now returns the list of badges newly unlocked by THIS call, instead of nothing.
+const applyStreakForActivity = (user) => {
+ const now = new Date();
+ const newlyUnlocked = [];
+
+ if (!user.lastPracticeDate) {
+ user.currentStreak = 1;
+ } else {
+ const diff = getUTCDayDifference(user.lastPracticeDate, now);
+ if (diff === 1) {
+ user.currentStreak += 1;
+ } else if (diff > 1) {
+ user.currentStreak = 1;
+ }
+ }
+
+ user.lastPracticeDate = now;
+
+ if (user.currentStreak > (user.longestStreak || 0)) {
+ user.longestStreak = user.currentStreak;
+ }
+
+ for (const milestone of STREAK_MILESTONES) {
+ if (
+ user.currentStreak >= milestone.days &&
+ !user.unlockedAchievements.includes(milestone.badge)
+ ) {
+ user.unlockedAchievements.push(milestone.badge);
+ newlyUnlocked.push(milestone.badge);
+ }
+ }
+
+ return newlyUnlocked;
+};
+
+const resetStreakIfMissed = (user) => {
+ if (user.lastPracticeDate && user.currentStreak > 0) {
+ const diff = getUTCDayDifference(user.lastPracticeDate, new Date());
+ if (diff > 1) {
+ user.currentStreak = 0;
+ return true;
+ }
+ }
+ return false;
+};
+
+// BREAKING CHANGE: now resolves to { user, newlyUnlockedMilestones } instead of
+// just `user`. Every caller of recordActivity() needs a small update ā see below.
+const recordActivity = async (userId, mongooseSession = null) => {
+ const query = User.findById(userId);
+ if (mongooseSession) query.session(mongooseSession);
+ const user = await query;
+ if (!user) return null;
+
+ if (user.lastPracticeDate) {
+ const diff = getUTCDayDifference(user.lastPracticeDate, new Date());
+ if (diff === 0) return { user, newlyUnlockedMilestones: [] };
+ }
+
+ const newlyUnlockedMilestones = applyStreakForActivity(user);
+ await user.save(mongooseSession ? { session: mongooseSession } : undefined);
+ return { user, newlyUnlockedMilestones };
+};
+
+module.exports = {
+ getUTCDayDifference,
+ applyStreakForActivity,
+ resetStreakIfMissed,
+ recordActivity,
+};
\ No newline at end of file
diff --git a/frontend/package-lock.json b/frontend/package-lock.json
index 23f9af57..feffb212 100644
--- a/frontend/package-lock.json
+++ b/frontend/package-lock.json
@@ -7,7 +7,9 @@
"": {
"name": "interviewprepai",
"version": "0.0.0",
+ "license": "ISC",
"dependencies": {
+ "@babel/parser": "^7.28.3",
"@monaco-editor/react": "^4.7.0",
"@tailwindcss/vite": "^4.1.12",
"axios": "^1.11.0",
@@ -20,7 +22,7 @@
"react-hot-toast": "^2.6.0",
"react-icons": "^5.5.0",
"react-markdown": "^10.1.0",
- "react-router-dom": "^7.8.2",
+ "react-router-dom": "^7.18.0",
"react-split": "^2.0.14",
"react-syntax-highlighter": "^15.6.6",
"recharts": "^2.15.3",
@@ -39,24 +41,10 @@
"eslint-plugin-react-refresh": "^0.4.20",
"globals": "^16.3.0",
"jsdom": "^29.1.1",
- "vite": "^7.1.2",
+ "vite": "^7.3.6",
"vitest": "^4.1.10"
}
},
- "node_modules/@ampproject/remapping": {
- "version": "2.3.0",
- "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.3.0.tgz",
- "integrity": "sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==",
- "dev": true,
- "license": "Apache-2.0",
- "dependencies": {
- "@jridgewell/gen-mapping": "^0.3.5",
- "@jridgewell/trace-mapping": "^0.3.24"
- },
- "engines": {
- "node": ">=6.0.0"
- }
- },
"node_modules/@asamuzakjp/css-color": {
"version": "5.1.11",
"resolved": "https://registry.npmjs.org/@asamuzakjp/css-color/-/css-color-5.1.11.tgz",
@@ -109,13 +97,13 @@
"license": "MIT"
},
"node_modules/@babel/code-frame": {
- "version": "7.27.1",
- "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.27.1.tgz",
- "integrity": "sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg==",
+ "version": "7.29.7",
+ "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.29.7.tgz",
+ "integrity": "sha512-Aup7aUOfpbAUg2ROOJN6Iw5f9DMBlzu0mIkm/malLQFN/YQgO48wCj0Kxa3sEHJvPVFg7siR+qRInwXd2qhQKw==",
"dev": true,
"license": "MIT",
"dependencies": {
- "@babel/helper-validator-identifier": "^7.27.1",
+ "@babel/helper-validator-identifier": "^7.29.7",
"js-tokens": "^4.0.0",
"picocolors": "^1.1.1"
},
@@ -124,9 +112,9 @@
}
},
"node_modules/@babel/compat-data": {
- "version": "7.28.0",
- "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.28.0.tgz",
- "integrity": "sha512-60X7qkglvrap8mn1lh2ebxXdZYtUcpd7gsmy9kLaBJ4i/WdY8PqTSdxyA8qraikqKQK5C1KRBKXqznrVapyNaw==",
+ "version": "7.29.7",
+ "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.29.7.tgz",
+ "integrity": "sha512-locTkQyKvwIEgBzVrn8693ebc97F2U8ZHjbXwDXJ5Fn2TCpNwTlKcaKLkdHop5c/icOFE7qt7Q9JC5hnKNa6Gg==",
"dev": true,
"license": "MIT",
"engines": {
@@ -134,22 +122,22 @@
}
},
"node_modules/@babel/core": {
- "version": "7.28.3",
- "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.28.3.tgz",
- "integrity": "sha512-yDBHV9kQNcr2/sUr9jghVyz9C3Y5G2zUM2H2lo+9mKv4sFgbA8s8Z9t8D1jiTkGoO/NoIfKMyKWr4s6CN23ZwQ==",
+ "version": "7.29.7",
+ "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.29.7.tgz",
+ "integrity": "sha512-RgHBCvtjbOK2gXSNBNIkNoEc9qoVEtau3hj8gEqKQuL3HZAibKarWFEI3Lfm6EYKkLalOh8eSrj9b+ch9H/VBA==",
"dev": true,
"license": "MIT",
"dependencies": {
- "@ampproject/remapping": "^2.2.0",
- "@babel/code-frame": "^7.27.1",
- "@babel/generator": "^7.28.3",
- "@babel/helper-compilation-targets": "^7.27.2",
- "@babel/helper-module-transforms": "^7.28.3",
- "@babel/helpers": "^7.28.3",
- "@babel/parser": "^7.28.3",
- "@babel/template": "^7.27.2",
- "@babel/traverse": "^7.28.3",
- "@babel/types": "^7.28.2",
+ "@babel/code-frame": "^7.29.7",
+ "@babel/generator": "^7.29.7",
+ "@babel/helper-compilation-targets": "^7.29.7",
+ "@babel/helper-module-transforms": "^7.29.7",
+ "@babel/helpers": "^7.29.7",
+ "@babel/parser": "^7.29.7",
+ "@babel/template": "^7.29.7",
+ "@babel/traverse": "^7.29.7",
+ "@babel/types": "^7.29.7",
+ "@jridgewell/remapping": "^2.3.5",
"convert-source-map": "^2.0.0",
"debug": "^4.1.0",
"gensync": "^1.0.0-beta.2",
@@ -165,14 +153,14 @@
}
},
"node_modules/@babel/generator": {
- "version": "7.28.3",
- "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.28.3.tgz",
- "integrity": "sha512-3lSpxGgvnmZznmBkCRnVREPUFJv2wrv9iAoFDvADJc0ypmdOxdUtcLeBgBJ6zE0PMeTKnxeQzyk0xTBq4Ep7zw==",
+ "version": "7.29.8",
+ "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.29.8.tgz",
+ "integrity": "sha512-gZbepsdh3WDtgZKWL+vTPh71LSBrm/Y4/QDZBVCcYfmeTEEuoOYwlSy+G1StfJg+/Zy550u/3TATbm7qDbbMtg==",
"dev": true,
"license": "MIT",
"dependencies": {
- "@babel/parser": "^7.28.3",
- "@babel/types": "^7.28.2",
+ "@babel/parser": "^7.29.8",
+ "@babel/types": "^7.29.8",
"@jridgewell/gen-mapping": "^0.3.12",
"@jridgewell/trace-mapping": "^0.3.28",
"jsesc": "^3.0.2"
@@ -182,14 +170,14 @@
}
},
"node_modules/@babel/helper-compilation-targets": {
- "version": "7.27.2",
- "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.27.2.tgz",
- "integrity": "sha512-2+1thGUUWWjLTYTHZWK1n8Yga0ijBz1XAhUXcKy81rd5g6yh7hGqMp45v7cadSbEHc9G3OTv45SyneRN3ps4DQ==",
+ "version": "7.29.7",
+ "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.29.7.tgz",
+ "integrity": "sha512-wem6WaBj4NaVYVdNhLPPVacES6ZJ+KBBfSkTMD3YZxbP3rm3Di85tJU5ljaUNhaOynt+Aj0xruhYuzQBt8n71g==",
"dev": true,
"license": "MIT",
"dependencies": {
- "@babel/compat-data": "^7.27.2",
- "@babel/helper-validator-option": "^7.27.1",
+ "@babel/compat-data": "^7.29.7",
+ "@babel/helper-validator-option": "^7.29.7",
"browserslist": "^4.24.0",
"lru-cache": "^5.1.1",
"semver": "^6.3.1"
@@ -199,9 +187,9 @@
}
},
"node_modules/@babel/helper-globals": {
- "version": "7.28.0",
- "resolved": "https://registry.npmjs.org/@babel/helper-globals/-/helper-globals-7.28.0.tgz",
- "integrity": "sha512-+W6cISkXFa1jXsDEdYA8HeevQT/FULhxzR99pxphltZcVaugps53THCeiWA8SguxxpSp3gKPiuYfSWopkLQ4hw==",
+ "version": "7.29.7",
+ "resolved": "https://registry.npmjs.org/@babel/helper-globals/-/helper-globals-7.29.7.tgz",
+ "integrity": "sha512-3nQVUAtvkKH9zahfWgw96Jc/uFOmjACE1kQz82E2lqWmHBgjzbNlsC22nuQTfahmWeQtTq5nQ/4Nnd2A1wj4zA==",
"dev": true,
"license": "MIT",
"engines": {
@@ -209,29 +197,29 @@
}
},
"node_modules/@babel/helper-module-imports": {
- "version": "7.27.1",
- "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.27.1.tgz",
- "integrity": "sha512-0gSFWUPNXNopqtIPQvlD5WgXYI5GY2kP2cCvoT8kczjbfcfuIljTbcWrulD1CIPIX2gt1wghbDy08yE1p+/r3w==",
+ "version": "7.29.7",
+ "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.29.7.tgz",
+ "integrity": "sha512-ejHwrQQYcm9xnTivShn2IDOlIzInN34AXskvq9QicvCtEzq1Vzclu/tKF8Jq1Cg8JG2GL6/EmjgsCT7lXepE3g==",
"dev": true,
"license": "MIT",
"dependencies": {
- "@babel/traverse": "^7.27.1",
- "@babel/types": "^7.27.1"
+ "@babel/traverse": "^7.29.7",
+ "@babel/types": "^7.29.7"
},
"engines": {
"node": ">=6.9.0"
}
},
"node_modules/@babel/helper-module-transforms": {
- "version": "7.28.3",
- "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.28.3.tgz",
- "integrity": "sha512-gytXUbs8k2sXS9PnQptz5o0QnpLL51SwASIORY6XaBKF88nsOT0Zw9szLqlSGQDP/4TljBAD5y98p2U1fqkdsw==",
+ "version": "7.29.7",
+ "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.29.7.tgz",
+ "integrity": "sha512-UPUVSyXbOh627KiCIGQSgwWzGeBKLkaJ9PJEdrngIwMSzxLR4jS4+f1f1jb7VzBbg8nFLaYotvVPFCTqdrmTAg==",
"dev": true,
"license": "MIT",
"dependencies": {
- "@babel/helper-module-imports": "^7.27.1",
- "@babel/helper-validator-identifier": "^7.27.1",
- "@babel/traverse": "^7.28.3"
+ "@babel/helper-module-imports": "^7.29.7",
+ "@babel/helper-validator-identifier": "^7.29.7",
+ "@babel/traverse": "^7.29.7"
},
"engines": {
"node": ">=6.9.0"
@@ -241,9 +229,9 @@
}
},
"node_modules/@babel/helper-plugin-utils": {
- "version": "7.27.1",
- "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.27.1.tgz",
- "integrity": "sha512-1gn1Up5YXka3YYAHGKpbideQ5Yjf1tDa9qYcgysz+cNCXukyLl6DjPXhD3VRwSb8c0J9tA4b2+rHEZtc6R0tlw==",
+ "version": "7.29.7",
+ "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.29.7.tgz",
+ "integrity": "sha512-G7sHYigPY17oO5SYWnfD/0MTBwVR781S/JI643e/JhUYgVgWE/61SoW3NH9KWUKyKq5LVh3npif99Wkt6j86Jw==",
"dev": true,
"license": "MIT",
"engines": {
@@ -254,7 +242,6 @@
"version": "7.29.7",
"resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.29.7.tgz",
"integrity": "sha512-Pb5ijPrZ89GDH8223L4UP8i6QApWxs04RbPQJTeWDV0/keR2E36MeKnyr6LYmUUvqRRI+Iv87SuF1W6ErINzYw==",
- "dev": true,
"license": "MIT",
"engines": {
"node": ">=6.9.0"
@@ -264,16 +251,15 @@
"version": "7.29.7",
"resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.29.7.tgz",
"integrity": "sha512-qehxGkRj55h/ff8EMaJ+cYhyaKlHIxqYDn682wQD7RNp9UujOQsHog2uS0r2vzr4pW+sXf90NeeayjcNaX3fFg==",
- "dev": true,
"license": "MIT",
"engines": {
"node": ">=6.9.0"
}
},
"node_modules/@babel/helper-validator-option": {
- "version": "7.27.1",
- "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.27.1.tgz",
- "integrity": "sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg==",
+ "version": "7.29.7",
+ "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.29.7.tgz",
+ "integrity": "sha512-N9ZErrD+yW5geCDtBqnOoxmR8+tNKiGuxKlDpuJxfsqpa2dFcexaziGAE/qoHLiDDreVNMupxGmSoNlyvsA3gw==",
"dev": true,
"license": "MIT",
"engines": {
@@ -281,14 +267,14 @@
}
},
"node_modules/@babel/helpers": {
- "version": "7.28.3",
- "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.28.3.tgz",
- "integrity": "sha512-PTNtvUQihsAsDHMOP5pfobP8C6CM4JWXmP8DrEIt46c3r2bf87Ua1zoqevsMo9g+tWDwgWrFP5EIxuBx5RudAw==",
+ "version": "7.29.7",
+ "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.29.7.tgz",
+ "integrity": "sha512-1k2lAGRMfHTcwuNYcCNUmaUffmQv8KWMfh2iJUUeRlwlwH4FdNG7mfPI10NPfLHJFThE4Tyr4mv7kTNZOiPuBg==",
"dev": true,
"license": "MIT",
"dependencies": {
- "@babel/template": "^7.27.2",
- "@babel/types": "^7.28.2"
+ "@babel/template": "^7.29.7",
+ "@babel/types": "^7.29.7"
},
"engines": {
"node": ">=6.9.0"
@@ -298,7 +284,6 @@
"version": "7.29.8",
"resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.29.8.tgz",
"integrity": "sha512-E8lTAYNB1KW+FH+VGJuZM1ioAx2E6oVlvQFRrf5P8ZZmsiJXYAD9vTFV7yyEURNzgh1dFqMZuO6tUwcARbqFCA==",
- "dev": true,
"license": "MIT",
"dependencies": {
"@babel/types": "^7.29.8"
@@ -311,13 +296,13 @@
}
},
"node_modules/@babel/plugin-transform-react-jsx-self": {
- "version": "7.27.1",
- "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-self/-/plugin-transform-react-jsx-self-7.27.1.tgz",
- "integrity": "sha512-6UzkCs+ejGdZ5mFFC/OCUrv028ab2fp1znZmCZjAOBKiBK2jXD1O+BPSfX8X2qjJ75fZBMSnQn3Rq2mrBJK2mw==",
+ "version": "7.29.7",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-self/-/plugin-transform-react-jsx-self-7.29.7.tgz",
+ "integrity": "sha512-TL0hMc9xzy86VD31nUiwzd5otRAcyEPcsegCxolO0PvcXuH1v0kECe/UIznYFihpkvU5wg/jk4v0TTEFfm53fw==",
"dev": true,
"license": "MIT",
"dependencies": {
- "@babel/helper-plugin-utils": "^7.27.1"
+ "@babel/helper-plugin-utils": "^7.29.7"
},
"engines": {
"node": ">=6.9.0"
@@ -327,13 +312,13 @@
}
},
"node_modules/@babel/plugin-transform-react-jsx-source": {
- "version": "7.27.1",
- "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-source/-/plugin-transform-react-jsx-source-7.27.1.tgz",
- "integrity": "sha512-zbwoTsBruTeKB9hSq73ha66iFeJHuaFkUbwvqElnygoNbj/jHRsSeokowZFN3CZ64IvEqcmmkVe89OPXc7ldAw==",
+ "version": "7.29.7",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-source/-/plugin-transform-react-jsx-source-7.29.7.tgz",
+ "integrity": "sha512-06IyK09H3wi4cGbhDBwp5gUGo0IKtnYa8tyTiephirPCK6fbobVGiXMMI5zLQ4aKEYP3wZ3ArU44o+8KMrSG/Q==",
"dev": true,
"license": "MIT",
"dependencies": {
- "@babel/helper-plugin-utils": "^7.27.1"
+ "@babel/helper-plugin-utils": "^7.29.7"
},
"engines": {
"node": ">=6.9.0"
@@ -352,33 +337,33 @@
}
},
"node_modules/@babel/template": {
- "version": "7.27.2",
- "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.27.2.tgz",
- "integrity": "sha512-LPDZ85aEJyYSd18/DkjNh4/y1ntkE5KwUHWTiqgRxruuZL2F1yuHligVHLvcHY2vMHXttKFpJn6LwfI7cw7ODw==",
+ "version": "7.29.7",
+ "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.29.7.tgz",
+ "integrity": "sha512-puq+Gf35oI24FeN11LkoUQFqv9uwNeWpxXZi/Ji3rRIoKAzKnxRaZ+Gkj0vKS9ZCiTESfng1N9LyOyXvo+m+Gg==",
"dev": true,
"license": "MIT",
"dependencies": {
- "@babel/code-frame": "^7.27.1",
- "@babel/parser": "^7.27.2",
- "@babel/types": "^7.27.1"
+ "@babel/code-frame": "^7.29.7",
+ "@babel/parser": "^7.29.7",
+ "@babel/types": "^7.29.7"
},
"engines": {
"node": ">=6.9.0"
}
},
"node_modules/@babel/traverse": {
- "version": "7.28.3",
- "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.28.3.tgz",
- "integrity": "sha512-7w4kZYHneL3A6NP2nxzHvT3HCZ7puDZZjFMqDpBPECub79sTtSO5CGXDkKrTQq8ksAwfD/XI2MRFX23njdDaIQ==",
+ "version": "7.29.8",
+ "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.29.8.tgz",
+ "integrity": "sha512-I5z7H3bf/41ktsNVLtpN0wAa336HkqIHQ5BuPLEhTkt1jVSyZpeNKIzTgEWmlxjdg81R0IgUCcaE+Ok3NvrfZg==",
"dev": true,
"license": "MIT",
"dependencies": {
- "@babel/code-frame": "^7.27.1",
- "@babel/generator": "^7.28.3",
- "@babel/helper-globals": "^7.28.0",
- "@babel/parser": "^7.28.3",
- "@babel/template": "^7.27.2",
- "@babel/types": "^7.28.2",
+ "@babel/code-frame": "^7.29.7",
+ "@babel/generator": "^7.29.8",
+ "@babel/helper-globals": "^7.29.7",
+ "@babel/parser": "^7.29.8",
+ "@babel/template": "^7.29.7",
+ "@babel/types": "^7.29.8",
"debug": "^4.3.1"
},
"engines": {
@@ -389,7 +374,6 @@
"version": "7.29.8",
"resolved": "https://registry.npmjs.org/@babel/types/-/types-7.29.8.tgz",
"integrity": "sha512-Vj1jF3cPfxg7OAfoI7QnVKLoILlm2JF9pnVHrX8qx7AHMiYWT+NDAA7jChlNgRS4WTLc/fD1lXLmPixluj+3Gg==",
- "dev": true,
"license": "MIT",
"dependencies": {
"@babel/helper-string-parser": "^7.29.7",
@@ -563,9 +547,9 @@
}
},
"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==",
+ "version": "0.28.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.28.2.tgz",
+ "integrity": "sha512-XExcO+dvLKvVtNTibSTBej1NCAbaGhWn9Ww1ZPx80qsahhPFe/8jgWP0IchNe0F3HwkU7n8ejhH8bjonqht8mQ==",
"cpu": [
"ppc64"
],
@@ -579,9 +563,9 @@
}
},
"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==",
+ "version": "0.28.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.28.2.tgz",
+ "integrity": "sha512-kXXoiPVVGQcnIYGOeaovwOURpniDBpSq4A03qkQ+BMQqtGG6HYap3xne9C1O1yo4TR3qxlCX5IqqmX6fFo2Lqg==",
"cpu": [
"arm"
],
@@ -595,9 +579,9 @@
}
},
"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==",
+ "version": "0.28.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.28.2.tgz",
+ "integrity": "sha512-5YfKeeI8qWfBZIX+u2xZC3Zlb3Os/gLS2sbEKM+I4ZOcsWmHS2WLysCcQZDAFRslDUU5Oiq44gf6PYN1vGwG5A==",
"cpu": [
"arm64"
],
@@ -611,9 +595,9 @@
}
},
"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==",
+ "version": "0.28.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.28.2.tgz",
+ "integrity": "sha512-O387ite7SzUyCcy3JQX4P4bLtEA7bLLkx+esve5JHnyYfNTxcVpXZo9jhdB0lTKN44gztELTdU7nS8Nr16Fs1Q==",
"cpu": [
"x64"
],
@@ -627,9 +611,9 @@
}
},
"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==",
+ "version": "0.28.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.28.2.tgz",
+ "integrity": "sha512-n4KqkOQrraxHJcgjM1RvwbigfQKIKJVpM7xp+KsxiyUSrRdIXnt73VhrPAx0fV44hgfmIVKjxMN9J1t5jySVkw==",
"cpu": [
"arm64"
],
@@ -643,9 +627,9 @@
}
},
"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==",
+ "version": "0.28.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.28.2.tgz",
+ "integrity": "sha512-uq6suIWYP37qzGddBKPw5QEQPi6HiLGsO7UmkpfyaYNQ3D+rN6w6WfwH+nuqcGXWvawGwxOEroO4YGnFh95azw==",
"cpu": [
"x64"
],
@@ -659,9 +643,9 @@
}
},
"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==",
+ "version": "0.28.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.28.2.tgz",
+ "integrity": "sha512-n+I0BTSRIoy+d6RPKnEVwql5UwBJolytvY4mAOIEJorKlqgPII8ix6slVVrfZ5Tnj7glIZvloylbB/EJPMWEXw==",
"cpu": [
"arm64"
],
@@ -675,9 +659,9 @@
}
},
"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==",
+ "version": "0.28.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.28.2.tgz",
+ "integrity": "sha512-78XJTJkvPs0kz2w61301PJjXl4g7q3JqiYMZ/M/yVI73EHBrCRTgkhu9oqG7vPqq+a/yadEW8aD+agKlk5xrmg==",
"cpu": [
"x64"
],
@@ -691,9 +675,9 @@
}
},
"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==",
+ "version": "0.28.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.28.2.tgz",
+ "integrity": "sha512-XlDnu2q5yoqems+xay6wSAcg9DDD7K9RLKZEBOMZm3ckNpJBvOX20tSfby8KfrrhINDyv9V2YVZKY/SpoGJI8w==",
"cpu": [
"arm"
],
@@ -707,9 +691,9 @@
}
},
"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==",
+ "version": "0.28.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.28.2.tgz",
+ "integrity": "sha512-pW4AC0P3it8c7do9MVM4p51FzHzdM/TZrerurgRcHJ2WTa1VQ1CIq18xncfpBJw4ojkiZZrKW2yIBWBP92j6Ug==",
"cpu": [
"arm64"
],
@@ -723,9 +707,9 @@
}
},
"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==",
+ "version": "0.28.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.28.2.tgz",
+ "integrity": "sha512-CYbnj78HsIeA+DhgUKgFCfvNsTHFhMMrinUrMZpDXJXKN8T3XViTZ/+wtHeVxEWY8ewSzTFN+nRmSwO2tZaLUQ==",
"cpu": [
"ia32"
],
@@ -739,9 +723,9 @@
}
},
"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==",
+ "version": "0.28.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.28.2.tgz",
+ "integrity": "sha512-buwkd8nsph4R+ajRvw0qM5Hja/TXQow3ptzWO2EbG/cqcIkHloRrdlBtQlshyYGTNFvfkfJ5tpPLVkY4DtsPfQ==",
"cpu": [
"loong64"
],
@@ -755,9 +739,9 @@
}
},
"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==",
+ "version": "0.28.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.28.2.tgz",
+ "integrity": "sha512-ZVykbDyk7519VwiNb9Lcj9m8XM6v5V9uKPvrEMkkEedVewf+0itkhahp4HDpgERXhwLRpWFypsGbG/J8s0QjJA==",
"cpu": [
"mips64el"
],
@@ -771,9 +755,9 @@
}
},
"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==",
+ "version": "0.28.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.28.2.tgz",
+ "integrity": "sha512-CAXl+Dtd9UUuJd8pKKdwh6MLm3MUMiqMPmhZ3tTSXPqfyQ3vDl6R5hZdZ/kYojK4ofXtdfSv1tFq8XzWx3heNQ==",
"cpu": [
"ppc64"
],
@@ -787,9 +771,9 @@
}
},
"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==",
+ "version": "0.28.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.28.2.tgz",
+ "integrity": "sha512-GeXCej4IQtU1B+QlDV8W/RRvbzI3O/Stss+/bCXv4lZls5WGRtu2a+3JkA3i4qIUlMXpcHebWpF8AkJhATowuA==",
"cpu": [
"riscv64"
],
@@ -803,9 +787,9 @@
}
},
"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==",
+ "version": "0.28.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.28.2.tgz",
+ "integrity": "sha512-3H1weTYZPxt/WOhByszQZybS9w5lKzUn1FDMsgEChbHWQwHYQQRfBxgCcZvPhjHfKyJjIievvMmEUawJrdY9Dg==",
"cpu": [
"s390x"
],
@@ -819,9 +803,9 @@
}
},
"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==",
+ "version": "0.28.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.28.2.tgz",
+ "integrity": "sha512-4xTZr1FUmSoQW4XIWmit3tzQrUTZM+N3P0XV8xROKYF50XfI7xeO90+1bZvNwxIufQ9hDQVRJH5YhgPVF8A/HQ==",
"cpu": [
"x64"
],
@@ -835,9 +819,9 @@
}
},
"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==",
+ "version": "0.28.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.28.2.tgz",
+ "integrity": "sha512-sSATRjPeDBg3pdgHoQfoYBob11Kk1FGa9lui5RIHZCoCkJa9QKlvl3/vKz2usCmYYjs7ymJR/2Nnsqe+Hjt5nw==",
"cpu": [
"arm64"
],
@@ -851,9 +835,9 @@
}
},
"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==",
+ "version": "0.28.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.28.2.tgz",
+ "integrity": "sha512-lqnzCV+mM0gIADaKihiCg6ifgfU2L3h5E33rNQBN1Y4MaVGnzryzmvvf7UHxprpQdE8hpqLolJ9Rl+SkIRDpyw==",
"cpu": [
"x64"
],
@@ -867,9 +851,9 @@
}
},
"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==",
+ "version": "0.28.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.28.2.tgz",
+ "integrity": "sha512-AL2qJILH7lNjrDmCQDvdxMfAUIv8KMNZOvrwAQ8i8//ntL9FflhOyMJ8OZSMBb8/AWXe3/5v5S20y3zCoZWKoQ==",
"cpu": [
"arm64"
],
@@ -883,9 +867,9 @@
}
},
"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==",
+ "version": "0.28.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.28.2.tgz",
+ "integrity": "sha512-QtiuPytchRyC4rwUKhexJdQKvDuZ6hWloi3igqPQNUJCS1/v9EiO3UTOXR6A3FoMo4fnAKbWJdqaIwhOzh8qEw==",
"cpu": [
"x64"
],
@@ -899,9 +883,9 @@
}
},
"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==",
+ "version": "0.28.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.28.2.tgz",
+ "integrity": "sha512-WkhYDmpTjLvGlScA1rwjRUmhl4k8oXR3cIbtqWmELgU/dFeHHlEllxDvdWcNJV9rbzCexB5vz8gtNewWLgCT7Q==",
"cpu": [
"arm64"
],
@@ -915,9 +899,9 @@
}
},
"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==",
+ "version": "0.28.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.28.2.tgz",
+ "integrity": "sha512-GPMSkTOtMnv2U2F8gxe4Io6qmVs+YKyp832Etqqxr0hFngmXQ3rzwytelm3GIn7T4VviRUlf3sOgBOiTdvaf7g==",
"cpu": [
"x64"
],
@@ -931,9 +915,9 @@
}
},
"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==",
+ "version": "0.28.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.28.2.tgz",
+ "integrity": "sha512-PIhhEkE9uPBleRBrQEJpUn7MBnibZzbGzYWPmY3x+YoVg/95zbjB4CxPPOQ8l5tYYM4mMaCthF8/1DIfBQQyWQ==",
"cpu": [
"arm64"
],
@@ -947,9 +931,9 @@
}
},
"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==",
+ "version": "0.28.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.28.2.tgz",
+ "integrity": "sha512-YmJbfTlvU7Sdn9BB+4PRES4oB6pxgS37MAONj+hBr/cpXS1aBPKXxNnDbu+QCWPj0o9dgyxeq79g6c5P8KeuYA==",
"cpu": [
"ia32"
],
@@ -963,9 +947,9 @@
}
},
"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==",
+ "version": "0.28.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.28.2.tgz",
+ "integrity": "sha512-5ebpxr3nWMzrL/rnUI755Jkuee0bHL/Gq0WTF9lvcpv73wAp5eu8MfBUgWK9bhWvZjj7yX8etf/8tI8Ney695g==",
"cpu": [
"x64"
],
@@ -979,9 +963,9 @@
}
},
"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",
- "integrity": "sha512-dyybb3AcajC7uha6CvhdVRJqaKyn7w2YKqKyAN37NKYgZT36w+iRb0Dymmc5qEJ549c/S31cMMSFd75bteCpCw==",
+ "version": "4.10.1",
+ "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.10.1.tgz",
+ "integrity": "sha512-cuadcxVFE8sDK6iWJbs8Sn0av2Nrh2QSGQhVlBW9AaAHqHwjWsZHT8LJ4hFGPh7ASBV2deFdM7H/DPjulmh8rg==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -1011,9 +995,9 @@
}
},
"node_modules/@eslint-community/regexpp": {
- "version": "4.12.1",
- "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.12.1.tgz",
- "integrity": "sha512-CCZCDJuduB9OUkFkY2IgppNZMi2lBQgD2qzwXkEia16cge2pijY/aXi96CJMquDMn3nJdlPV1A5KrJEXwfLNzQ==",
+ "version": "4.12.2",
+ "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.12.2.tgz",
+ "integrity": "sha512-EriSTlt5OC9/7SXkRSCAhfSxxoSUgBm33OH+IkwbdpgoqsSsUg7y3uh+IICI/Qg4BBWr3U2i39RpmycbxMq4ew==",
"dev": true,
"license": "MIT",
"engines": {
@@ -1021,34 +1005,37 @@
}
},
"node_modules/@eslint/config-array": {
- "version": "0.21.0",
- "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.21.0.tgz",
- "integrity": "sha512-ENIdc4iLu0d93HeYirvKmrzshzofPw6VkZRKQGe9Nv46ZnWUzcF1xV01dcvEg/1wXUR61OmmlSfyeyO7EvjLxQ==",
+ "version": "0.21.2",
+ "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.21.2.tgz",
+ "integrity": "sha512-nJl2KGTlrf9GjLimgIru+V/mzgSK0ABCDQRvxw5BjURL7WfH5uoWmizbH7QB6MmnMBd8cIC9uceWnezL1VZWWw==",
"dev": true,
"license": "Apache-2.0",
"dependencies": {
- "@eslint/object-schema": "^2.1.6",
+ "@eslint/object-schema": "^2.1.7",
"debug": "^4.3.1",
- "minimatch": "^3.1.2"
+ "minimatch": "^3.1.5"
},
"engines": {
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
}
},
"node_modules/@eslint/config-helpers": {
- "version": "0.3.1",
- "resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.3.1.tgz",
- "integrity": "sha512-xR93k9WhrDYpXHORXpxVL5oHj3Era7wo6k/Wd8/IsQNnZUTzkGS29lyn3nAT05v6ltUuTFVCCYDEGfy2Or/sPA==",
+ "version": "0.4.2",
+ "resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.4.2.tgz",
+ "integrity": "sha512-gBrxN88gOIf3R7ja5K9slwNayVcZgK6SOUORm2uBzTeIEfeVaIhOpCtTox3P6R7o2jLFwLFTLnC7kU/RGcYEgw==",
"dev": true,
"license": "Apache-2.0",
+ "dependencies": {
+ "@eslint/core": "^0.17.0"
+ },
"engines": {
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
}
},
"node_modules/@eslint/core": {
- "version": "0.15.2",
- "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.15.2.tgz",
- "integrity": "sha512-78Md3/Rrxh83gCxoUc0EiciuOHsIITzLy53m3d9UyiW8y9Dj2D29FeETqyKA+BRK76tnTp6RXWb3pCay8Oyomg==",
+ "version": "0.17.0",
+ "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.17.0.tgz",
+ "integrity": "sha512-yL/sLrpmtDaFEiUj1osRP4TI2MDz1AddJL+jZ7KSqvBuliN4xqYY54IfdN8qD8Toa6g1iloph1fxQNkjOxrrpQ==",
"dev": true,
"license": "Apache-2.0",
"dependencies": {
@@ -1059,20 +1046,20 @@
}
},
"node_modules/@eslint/eslintrc": {
- "version": "3.3.1",
- "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-3.3.1.tgz",
- "integrity": "sha512-gtF186CXhIl1p4pJNGZw8Yc6RlshoePRvE0X91oPGb3vZ8pM3qOS9W9NGPat9LziaBV7XrJWGylNQXkGcnM3IQ==",
+ "version": "3.3.6",
+ "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-3.3.6.tgz",
+ "integrity": "sha512-l2Ul9PrHsPCKcEY/ac7VgFj9D80C7S68sOKc618SyHDPK36s1XcFebXY0iTzUVn4Yq+YbwvSnDmCz9yxjX+QrA==",
"dev": true,
"license": "MIT",
"dependencies": {
- "ajv": "^6.12.4",
+ "ajv": "^6.14.0",
"debug": "^4.3.2",
"espree": "^10.0.1",
"globals": "^14.0.0",
"ignore": "^5.2.0",
"import-fresh": "^3.2.1",
- "js-yaml": "^4.1.0",
- "minimatch": "^3.1.2",
+ "js-yaml": "^4.3.0",
+ "minimatch": "^3.1.5",
"strip-json-comments": "^3.1.1"
},
"engines": {
@@ -1096,9 +1083,9 @@
}
},
"node_modules/@eslint/js": {
- "version": "9.34.0",
- "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.34.0.tgz",
- "integrity": "sha512-EoyvqQnBNsV1CWaEJ559rxXL4c8V92gxirbawSmVUOWXlsRxxQXl6LmCpdUblgxgSkDIqKnhzba2SjRTI/A5Rw==",
+ "version": "9.39.5",
+ "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.39.5.tgz",
+ "integrity": "sha512-QywQuszQh77pIXCsq998c8hbhSTI/azTty1Z6N53dmAudKHhy573j3yvRLsX2BSp8YpLtoCEG8E9DJe+8zUh4A==",
"dev": true,
"license": "MIT",
"engines": {
@@ -1109,9 +1096,9 @@
}
},
"node_modules/@eslint/object-schema": {
- "version": "2.1.6",
- "resolved": "https://registry.npmjs.org/@eslint/object-schema/-/object-schema-2.1.6.tgz",
- "integrity": "sha512-RBMg5FRL0I0gs51M/guSAj5/e14VQ4tpZnQNWwuDT66P14I43ItmPfIZRhO9fUVIPOAQXU47atlywZ/czoqFPA==",
+ "version": "2.1.7",
+ "resolved": "https://registry.npmjs.org/@eslint/object-schema/-/object-schema-2.1.7.tgz",
+ "integrity": "sha512-VtAOaymWVfZcmZbp6E2mympDIHvyjXs/12LqWYjVw6qjrfF+VK+fyG33kChz3nnK+SU5/NeHOqrTEHS8sXO3OA==",
"dev": true,
"license": "Apache-2.0",
"engines": {
@@ -1119,13 +1106,13 @@
}
},
"node_modules/@eslint/plugin-kit": {
- "version": "0.3.5",
- "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.3.5.tgz",
- "integrity": "sha512-Z5kJ+wU3oA7MMIqVR9tyZRtjYPr4OC004Q4Rw7pgOKUOKkJfZ3O24nz3WYfGRpMDNmcOi3TwQOmgm7B7Tpii0w==",
+ "version": "0.4.1",
+ "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.4.1.tgz",
+ "integrity": "sha512-43/qtrDUokr7LJqoF2c3+RInu/t4zfrpYdoSDfYyhg52rwLV6TnOvdG4fXm7IkSB3wErkcmJS9iEhjVtOSEjjA==",
"dev": true,
"license": "Apache-2.0",
"dependencies": {
- "@eslint/core": "^0.15.2",
+ "@eslint/core": "^0.17.0",
"levn": "^0.4.1"
},
"engines": {
@@ -1151,41 +1138,41 @@
}
},
"node_modules/@humanfs/core": {
- "version": "0.19.1",
- "resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.1.tgz",
- "integrity": "sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA==",
+ "version": "0.19.2",
+ "resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.2.tgz",
+ "integrity": "sha512-UhXNm+CFMWcbChXywFwkmhqjs3PRCmcSa/hfBgLIb7oQ5HNb1wS0icWsGtSAUNgefHeI+eBrA8I1fxmbHsGdvA==",
"dev": true,
"license": "Apache-2.0",
+ "dependencies": {
+ "@humanfs/types": "^0.15.0"
+ },
"engines": {
"node": ">=18.18.0"
}
},
"node_modules/@humanfs/node": {
- "version": "0.16.6",
- "resolved": "https://registry.npmjs.org/@humanfs/node/-/node-0.16.6.tgz",
- "integrity": "sha512-YuI2ZHQL78Q5HbhDiBA1X4LmYdXCKCMQIfw0pw7piHJwyREFebJUvrQN4cMssyES6x+vfUbx1CIpaQUKYdQZOw==",
+ "version": "0.16.8",
+ "resolved": "https://registry.npmjs.org/@humanfs/node/-/node-0.16.8.tgz",
+ "integrity": "sha512-gE1eQNZ3R++kTzFUpdGlpmy8kDZD/MLyHqDwqjkVQI0JMdI1D51sy1H958PNXYkM2rAac7e5/CnIKZrHtPh3BQ==",
"dev": true,
"license": "Apache-2.0",
"dependencies": {
- "@humanfs/core": "^0.19.1",
- "@humanwhocodes/retry": "^0.3.0"
+ "@humanfs/core": "^0.19.2",
+ "@humanfs/types": "^0.15.0",
+ "@humanwhocodes/retry": "^0.4.0"
},
"engines": {
"node": ">=18.18.0"
}
},
- "node_modules/@humanfs/node/node_modules/@humanwhocodes/retry": {
- "version": "0.3.1",
- "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.3.1.tgz",
- "integrity": "sha512-JBxkERygn7Bv/GbN5Rv8Ul6LVknS+5Bp6RgDC/O8gEBU/yeH5Ui5C/OlWrTb6qct7LjjfT6Re2NxB0ln0yYybA==",
+ "node_modules/@humanfs/types": {
+ "version": "0.15.0",
+ "resolved": "https://registry.npmjs.org/@humanfs/types/-/types-0.15.0.tgz",
+ "integrity": "sha512-ZZ1w0aoQkwuUuC7Yf+7sdeaNfqQiiLcSRbfI08oAxqLtpXQr9AIVX7Ay7HLDuiLYAaFPu8oBYNq/QIi9URHJ3Q==",
"dev": true,
"license": "Apache-2.0",
"engines": {
- "node": ">=18.18"
- },
- "funding": {
- "type": "github",
- "url": "https://github.com/sponsors/nzakas"
+ "node": ">=18.18.0"
}
},
"node_modules/@humanwhocodes/module-importer": {
@@ -1216,18 +1203,6 @@
"url": "https://github.com/sponsors/nzakas"
}
},
- "node_modules/@isaacs/fs-minipass": {
- "version": "4.0.1",
- "resolved": "https://registry.npmjs.org/@isaacs/fs-minipass/-/fs-minipass-4.0.1.tgz",
- "integrity": "sha512-wgm9Ehl2jpeqP3zw/7mo3kRHFp5MEDhqAdwy1fTGkHAwnkGOVsgpvQhL8B5n1qlb01jV3n/bI0ZfZp5lWA1k4w==",
- "license": "ISC",
- "dependencies": {
- "minipass": "^7.0.4"
- },
- "engines": {
- "node": ">=18.0.0"
- }
- },
"node_modules/@jridgewell/gen-mapping": {
"version": "0.3.13",
"resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz",
@@ -1296,17 +1271,36 @@
"react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0"
}
},
+ "node_modules/@napi-rs/lzma-linux-x64-gnu": {
+ "version": "1.5.1",
+ "resolved": "https://registry.npmjs.org/@napi-rs/lzma-linux-x64-gnu/-/lzma-linux-x64-gnu-1.5.1.tgz",
+ "integrity": "sha512-oTXEIha4SsuXdTA4Iyskj0kpdx2yVXdhd75c2v3xGrHFfVMsbhTPZU/nMPL4sWKo4pBHm3aucLaqGlF696dTyQ==",
+ "cpu": [
+ "x64"
+ ],
+ "libc": [
+ "glibc"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": "^22.20 || ^24.12 || >=25"
+ }
+ },
"node_modules/@rolldown/pluginutils": {
- "version": "1.0.0-beta.34",
- "resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.0-beta.34.tgz",
- "integrity": "sha512-LyAREkZHP5pMom7c24meKmJCdhf2hEyvam2q0unr3or9ydwDL+DJ8chTF6Av/RFPb3rH8UFBdMzO5MxTZW97oA==",
+ "version": "1.0.0-rc.3",
+ "resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.0-rc.3.tgz",
+ "integrity": "sha512-eybk3TjzzzV97Dlj5c+XrBFW57eTNhzod66y9HrBlzJ6NsCrWCp/2kaPS3K9wJmurBC0Tdw4yPjXKZqlznim3Q==",
"dev": true,
"license": "MIT"
},
"node_modules/@rollup/rollup-android-arm-eabi": {
- "version": "4.61.1",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.61.1.tgz",
- "integrity": "sha512-JnBB8MdXj45cajvTuO5FmPlvFVJRQgvrz1uSEl3NwqFnReAPGwb8EanbGi4z2nRaqLzjJSv5/JmycoTKlRZxHA==",
+ "version": "4.62.4",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.62.4.tgz",
+ "integrity": "sha512-RrPokAb7dmbxFoeO3TloqHyOjgye8RkBhSqmp4aJMIex4c9r46ZstPnleDQOq1t46VOVjwIuwNogIqbodV1Vvg==",
"cpu": [
"arm"
],
@@ -1317,9 +1311,9 @@
]
},
"node_modules/@rollup/rollup-android-arm64": {
- "version": "4.61.1",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.61.1.tgz",
- "integrity": "sha512-Jx2g7iSjw4AOT0HDPHM9RV3GNjRXwybWtSFZiZAYUTjUwjVrYIwq3kBf+LnhqJlzXFAqTAh2F7IGI+O568exPw==",
+ "version": "4.62.4",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.62.4.tgz",
+ "integrity": "sha512-JKuJc+pnpks2pjy7L/N3v/cAkZxYlnmuZoD840ldbMI5KDbC4iO9NKwPKYdjYFCMAIIlBzYSFHxIJVYzRo2/8A==",
"cpu": [
"arm64"
],
@@ -1330,9 +1324,9 @@
]
},
"node_modules/@rollup/rollup-darwin-arm64": {
- "version": "4.61.1",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.61.1.tgz",
- "integrity": "sha512-0F1L/Z3Eqv8mT2n3dCpeO8GcTvHvVqkP5/t6DMsn0KzhYVcg+s7Ncl5DS8qjKYEeio6Az0Gt6nyBORay5qIlCA==",
+ "version": "4.62.4",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.62.4.tgz",
+ "integrity": "sha512-krw5uS2STmvJ02x0uTXHbqQNuz+9eZ1iw+qXk9dmW2gvV4jV7O2hEoOnuhFrpOPiel1mBFtqbxYZZtC46hXLOw==",
"cpu": [
"arm64"
],
@@ -1343,9 +1337,9 @@
]
},
"node_modules/@rollup/rollup-darwin-x64": {
- "version": "4.61.1",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.61.1.tgz",
- "integrity": "sha512-qLttcH871ujY4YcVfUSShhOw+CsoTatYz8gRbHO7Bb92QH059/P0y5do1KMs41fY0BpD2x4AJH/gID0zFiqVKQ==",
+ "version": "4.62.4",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.62.4.tgz",
+ "integrity": "sha512-wsTxtgApb4PrOsNJIm0FZ1h3WvCC+k9uxLJ4ad75hgoS4NiRes2SoJFlDAyMwiUY8IssDqGcHbXuN0sx1tfF1A==",
"cpu": [
"x64"
],
@@ -1356,9 +1350,9 @@
]
},
"node_modules/@rollup/rollup-freebsd-arm64": {
- "version": "4.61.1",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.61.1.tgz",
- "integrity": "sha512-fUI4RapGE0Oh3mb8mgfvC1O2nU1RpDZUKnDQm3xB1Ipg7C2wTs5Kstz7G2uWK99a8S2yTMq8/P4uycwNa0nJyw==",
+ "version": "4.62.4",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.62.4.tgz",
+ "integrity": "sha512-GUOnQlyZe3yAXhWOtOMsn5Qkrv5E5mZXa0thbARWi5Ei2szlVXJFQhddZ4HbAzh8q92w5twp+CQvs/eFanz9YQ==",
"cpu": [
"arm64"
],
@@ -1369,9 +1363,9 @@
]
},
"node_modules/@rollup/rollup-freebsd-x64": {
- "version": "4.61.1",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.61.1.tgz",
- "integrity": "sha512-H5YrdvJaDtI/U9/emrD4b++xkvp3y/JvOe4rizHbxvkyMfRS/CiRYdji+Pl8D0brEaNFWUh1drQxgAGIl6Xudw==",
+ "version": "4.62.4",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.62.4.tgz",
+ "integrity": "sha512-/Y7f3QuxjzPKsjA/rfEDa3+0vXqyjmJ50Ln8dPpCmWkKTrUoWHG1cWhTqaAMLob2m2nESWuC7yGrREz019Ztqg==",
"cpu": [
"x64"
],
@@ -1382,12 +1376,15 @@
]
},
"node_modules/@rollup/rollup-linux-arm-gnueabihf": {
- "version": "4.61.1",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.61.1.tgz",
- "integrity": "sha512-Q8CBCCQtDFrYtXoeUXSrnFXKOnyUhx6bz+SkL6A0E7V8kAiCJ5pamq1WtbfpVGhR5TSpXY6ak3avmDc5fHTyJA==",
+ "version": "4.62.4",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.62.4.tgz",
+ "integrity": "sha512-81wiiX3v7aqy+T+bT61TJ78yJjRquqFFTTbAPt08imfQQzkPIW8t6aJbkTagtCCrXMNc9D66+geqlK7ydLPNqA==",
"cpu": [
"arm"
],
+ "libc": [
+ "glibc"
+ ],
"license": "MIT",
"optional": true,
"os": [
@@ -1395,12 +1392,15 @@
]
},
"node_modules/@rollup/rollup-linux-arm-musleabihf": {
- "version": "4.61.1",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.61.1.tgz",
- "integrity": "sha512-nwnhk1581l0FBVellGcVCAT0Oi06onEA3WB53sf01VO3I0UPBkMH9sXONYME2K0ovXcNayJfNtHfm6mpJElatQ==",
+ "version": "4.62.4",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.62.4.tgz",
+ "integrity": "sha512-9kmDIvNZqdoHOBZgNtpTBeLWYO/LVipM3H/j62P8848/l/VPEQL6N3uxU9pvP1oZAsXyC2MEnFP3ovRjo7WYNQ==",
"cpu": [
"arm"
],
+ "libc": [
+ "musl"
+ ],
"license": "MIT",
"optional": true,
"os": [
@@ -1408,12 +1408,15 @@
]
},
"node_modules/@rollup/rollup-linux-arm64-gnu": {
- "version": "4.61.1",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.61.1.tgz",
- "integrity": "sha512-x5Xr49hwt3hdW75UOZm3395YwwzPyauktslv29KpWL/T+vVAzoT3azLcTWv0eMciBNrx+DYjH4paehHoLpPvpg==",
+ "version": "4.62.4",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.62.4.tgz",
+ "integrity": "sha512-CcnXHWnXg69g+DX5VWL3FHts3qMRN2uVEHX+BZvGLdd07/gXkn3ePjYtO1LDJvxkGKVHMclKBRa1QUTH+6toYQ==",
"cpu": [
"arm64"
],
+ "libc": [
+ "glibc"
+ ],
"license": "MIT",
"optional": true,
"os": [
@@ -1421,12 +1424,15 @@
]
},
"node_modules/@rollup/rollup-linux-arm64-musl": {
- "version": "4.61.1",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.61.1.tgz",
- "integrity": "sha512-unMS3H73DpaoPyyEVPjGKleM/s0mkmsauTENpw4INQY8y4+IuLNjkueQ5QCtC0D3N38Y38yhAU8OoZ20S2Tm6w==",
+ "version": "4.62.4",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.62.4.tgz",
+ "integrity": "sha512-iFOibiHnTRuhrWLlRsOQFdZJJIa7S8OwkneJr4ocALP16u5yk6lWLINFwhHaEqBFMsKDUZofLkGos7+CPzGB3g==",
"cpu": [
"arm64"
],
+ "libc": [
+ "musl"
+ ],
"license": "MIT",
"optional": true,
"os": [
@@ -1434,12 +1440,15 @@
]
},
"node_modules/@rollup/rollup-linux-loong64-gnu": {
- "version": "4.61.1",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.61.1.tgz",
- "integrity": "sha512-zNZzGRnAhwjFEYmvphJRV5XaQGjs62cCmeYYHUT//NbvEnHauw+I85nGG+SiVg5ld4GX8D1IbKIX+ozITQnhMQ==",
+ "version": "4.62.4",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.62.4.tgz",
+ "integrity": "sha512-XnWYMI7euHlb5a871xPja+Gm7DRCFU+FGRrtS2sMq9N8FvqtpagUy6gD4YOemC5MRk9xbh8+jYMEJbigFQwsgA==",
"cpu": [
"loong64"
],
+ "libc": [
+ "glibc"
+ ],
"license": "MIT",
"optional": true,
"os": [
@@ -1447,12 +1456,15 @@
]
},
"node_modules/@rollup/rollup-linux-loong64-musl": {
- "version": "4.61.1",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-musl/-/rollup-linux-loong64-musl-4.61.1.tgz",
- "integrity": "sha512-LdpWGL8X209B2SIvWjqlc8VZgM6PKfontSerGepuldQmHYrAOtnMCXeJkxXGbC+PPZVOuu5czJo7fNV6aeW8rQ==",
+ "version": "4.62.4",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-musl/-/rollup-linux-loong64-musl-4.62.4.tgz",
+ "integrity": "sha512-qGDAlO0U8xedCcsdRm9oaoQY8DAx/QT7uIxJWhCdx0ceIWX783UC9QSYkdpzAe29wNiVfp24+bZdQmn49o45SQ==",
"cpu": [
"loong64"
],
+ "libc": [
+ "musl"
+ ],
"license": "MIT",
"optional": true,
"os": [
@@ -1460,12 +1472,15 @@
]
},
"node_modules/@rollup/rollup-linux-ppc64-gnu": {
- "version": "4.61.1",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.61.1.tgz",
- "integrity": "sha512-EC5kTtNaNGOmbMGqar8dvJy6y/hg99GAwjfBz++pxZhQATXGcRjd6c5en5wcbru0vkRmiMGsQKdMJOOf6sza4g==",
+ "version": "4.62.4",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.62.4.tgz",
+ "integrity": "sha512-ru4H6ezD7ysA5EiEK6qkkaEb4modH8CTej6kUy/gQi20u3kB3G7Zn8snXXkeJSCOFKG/rbPPtM/+9Wgas1961w==",
"cpu": [
"ppc64"
],
+ "libc": [
+ "glibc"
+ ],
"license": "MIT",
"optional": true,
"os": [
@@ -1473,12 +1488,15 @@
]
},
"node_modules/@rollup/rollup-linux-ppc64-musl": {
- "version": "4.61.1",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-musl/-/rollup-linux-ppc64-musl-4.61.1.tgz",
- "integrity": "sha512-8hiwp6D4acEcNK78I4rP0/XtS1sknWIAMJBPdR4l6zUtyTm5KiTDr5bXmWt4foY7nAN7AThDHgkLIEZOWKbzWw==",
+ "version": "4.62.4",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-musl/-/rollup-linux-ppc64-musl-4.62.4.tgz",
+ "integrity": "sha512-2W4MO5WQVJnbJaZdvDb9rhBDuFU1nKIepPFpJUBsTh2k1YY2g+ODViaWuyOAjQ5cOP7NvrvLzt3wvHOoiAvc7w==",
"cpu": [
"ppc64"
],
+ "libc": [
+ "musl"
+ ],
"license": "MIT",
"optional": true,
"os": [
@@ -1486,12 +1504,15 @@
]
},
"node_modules/@rollup/rollup-linux-riscv64-gnu": {
- "version": "4.61.1",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.61.1.tgz",
- "integrity": "sha512-10dh/h/BqA7DuMPWSxkR8uks18FRwnwOEqr5zOTEl+NOwP/OMzKX8OFR/Of9xxDA7D5qef1Nzar5WDD2kCCr1g==",
+ "version": "4.62.4",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.62.4.tgz",
+ "integrity": "sha512-+fxjfuoAmVMCYV5QyjoIpu0cp5DOiOTeqYFk1AVaxGr+/ravWLX89XfQmptsoWcaVy/TGf2hexzbUOrCQIL1CQ==",
"cpu": [
"riscv64"
],
+ "libc": [
+ "glibc"
+ ],
"license": "MIT",
"optional": true,
"os": [
@@ -1499,12 +1520,15 @@
]
},
"node_modules/@rollup/rollup-linux-riscv64-musl": {
- "version": "4.61.1",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.61.1.tgz",
- "integrity": "sha512-YKJ5lg35DP17gcAOggnihe+APw9HLyj1Xn7gsmGumBJAUDa6NGXNixJzmkWLhcK9TOuuyQjdamzvJefkO7qHZQ==",
+ "version": "4.62.4",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.62.4.tgz",
+ "integrity": "sha512-jTn8JfHGL4djjFxPuM06LmNUJDsst2jeVlsd9OmIH6zc5sC9K6rIuO4YajXatLUpBmBKl6b35ro1QZocLi+tcA==",
"cpu": [
"riscv64"
],
+ "libc": [
+ "musl"
+ ],
"license": "MIT",
"optional": true,
"os": [
@@ -1512,12 +1536,15 @@
]
},
"node_modules/@rollup/rollup-linux-s390x-gnu": {
- "version": "4.61.1",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.61.1.tgz",
- "integrity": "sha512-Mlil5G2Jj6a7B3LWGctg+XPL9vdXYuzCtNXfxOQ0nPjc2m6ueUktocPGH9bnAM0bNRKb/bAWTujUU7IJQdQA+g==",
+ "version": "4.62.4",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.62.4.tgz",
+ "integrity": "sha512-oCJCJL4pXsoDcP2QZ+JVlPTIRc6266zsIaeJJsWImmF7HO0W8nb6HuSgZlMWxJwaPf8ehbSw8yo0EUw925hKsA==",
"cpu": [
"s390x"
],
+ "libc": [
+ "glibc"
+ ],
"license": "MIT",
"optional": true,
"os": [
@@ -1525,12 +1552,15 @@
]
},
"node_modules/@rollup/rollup-linux-x64-gnu": {
- "version": "4.61.1",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.61.1.tgz",
- "integrity": "sha512-bVWIOIk6pV01p4CdUbPP7CJ/434z+OooYjDuFcR+44N35YvKUC66G8MGnvcWx5mWKW3g61J+t74l3Kj15Kwn2Q==",
+ "version": "4.62.4",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.62.4.tgz",
+ "integrity": "sha512-W69hukhZ3KKNRCaMIEzKvcFye42hh0FE1+YoYaf5+Ikacuftoco6yO/xouz0hc5d5W/s3yBro5jRiuEE/Q5vUw==",
"cpu": [
"x64"
],
+ "libc": [
+ "glibc"
+ ],
"license": "MIT",
"optional": true,
"os": [
@@ -1538,12 +1568,15 @@
]
},
"node_modules/@rollup/rollup-linux-x64-musl": {
- "version": "4.61.1",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.61.1.tgz",
- "integrity": "sha512-qy5pBvZbqNFheBz61R1rzsezjm0J7O2oNGoWtGoY89SZYLUfxAJTBAqDChqAIdB4rCiIbi9nF7yZ83GnNiLwSw==",
+ "version": "4.62.4",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.62.4.tgz",
+ "integrity": "sha512-qiXbGG2jkjXhzXpsFZSR2Xpb8DN/UaxYsbb/STbuR/6fpaDgRmmaq1B/LmtF2wQFOFOSsK2jdE0RZ3a0zHn4QA==",
"cpu": [
"x64"
],
+ "libc": [
+ "musl"
+ ],
"license": "MIT",
"optional": true,
"os": [
@@ -1551,9 +1584,9 @@
]
},
"node_modules/@rollup/rollup-openbsd-x64": {
- "version": "4.61.1",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-openbsd-x64/-/rollup-openbsd-x64-4.61.1.tgz",
- "integrity": "sha512-E83TXjI4zm0+5f2qO+UOudaCYIhYwpJ5jq6YCZNIZ+6CbfhKrkAGezeiASBL9ElxAxFsRS9ZhESv8mfnj6TKeg==",
+ "version": "4.62.4",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-openbsd-x64/-/rollup-openbsd-x64-4.62.4.tgz",
+ "integrity": "sha512-nWeM//hxv8mIo6jD7Hu4o48DVmV9pbV6gsKaWU+4NFyqHoPKwrkRiZGLKUhOBk8qNmDmpwFtPKg80Bo/Tn4xiQ==",
"cpu": [
"x64"
],
@@ -1564,9 +1597,9 @@
]
},
"node_modules/@rollup/rollup-openharmony-arm64": {
- "version": "4.61.1",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.61.1.tgz",
- "integrity": "sha512-fbWnKqVkjrJN38vNe3ahkbk6iejS/3b0Nt7EEtPpE6RBacZcGXNKbzfHN3GUUlXOPghUg0j6XUGrtjX9z1sIvA==",
+ "version": "4.62.4",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.62.4.tgz",
+ "integrity": "sha512-s62SQ/vgsRSvMwDkOEfTqfgASF0f26ZNaQuTA6Aok5lrikf89yI2W0gFHvZb2Jpgc6N8JnOKZgCK2iciO3CsxQ==",
"cpu": [
"arm64"
],
@@ -1577,9 +1610,9 @@
]
},
"node_modules/@rollup/rollup-win32-arm64-msvc": {
- "version": "4.61.1",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.61.1.tgz",
- "integrity": "sha512-ArMl38iVAbk0New1ogihQNY6iphLi4ZaRsa037gUzv5yeKPY8TD3Dmy4x2RNC1VztU/uqm+G+/RwFrSka3Oy2g==",
+ "version": "4.62.4",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.62.4.tgz",
+ "integrity": "sha512-J6wGf8TVGbXJq+HH+ttTvrcfNKPbuZecV6KT1B8I18BC5IURUh5kl4Yl5OEP5eFIUoI5BWxCsyYMhFsDx8kekw==",
"cpu": [
"arm64"
],
@@ -1590,9 +1623,9 @@
]
},
"node_modules/@rollup/rollup-win32-ia32-msvc": {
- "version": "4.61.1",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.61.1.tgz",
- "integrity": "sha512-0mYtjHS9ucAbcATycCNK9IGBk/cCe/ma7EmSLGZdsxnOA8cjRIyU04wDpVAD9NiOfLUR9KTxdiO53uOkherqjQ==",
+ "version": "4.62.4",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.62.4.tgz",
+ "integrity": "sha512-zmfrQd/0wu6oJs8Vq8KwY/YtsKSsLtKe/HwAP4Wqy8LhWjeT55fHRAkOhYQ12wI3ayS4Tt12d5CDRD7N96SAYQ==",
"cpu": [
"ia32"
],
@@ -1603,9 +1636,9 @@
]
},
"node_modules/@rollup/rollup-win32-x64-gnu": {
- "version": "4.61.1",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.61.1.tgz",
- "integrity": "sha512-gK1iCEPfpoSG9wfBihXxvBMi8ZfcWffYkEsC/Eih+iFENTaewvNcrEQ69lIOWYO5pePHKLHHO7nq5AILGO/HQQ==",
+ "version": "4.62.4",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.62.4.tgz",
+ "integrity": "sha512-qPzHqdj9rfUD+w79dtE07zi/kFwKyCJqplp5K5ygeLTp7jLpAoc16OAH39HSmRC9UpozaecsleI8uAdEj6v2yw==",
"cpu": [
"x64"
],
@@ -1616,9 +1649,9 @@
]
},
"node_modules/@rollup/rollup-win32-x64-msvc": {
- "version": "4.61.1",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.61.1.tgz",
- "integrity": "sha512-X+zaP2x+j4RXGfbp/seSoRHWnPxzApilDszisZxbYH5C/jTxFhCtDNdPGZb9lJyYPs24wGxruPF7Y+sIXt9Gzw==",
+ "version": "4.62.4",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.62.4.tgz",
+ "integrity": "sha512-zD6NdeWEByGE9QF9vCrlJ5YQB4oq9q91kPZS37Jwj5hOkvR1lTBSpsKhKDw4IJtbQ35LsTS1HD9DZYGKIshU1Q==",
"cpu": [
"x64"
],
@@ -1636,24 +1669,47 @@
"license": "MIT"
},
"node_modules/@tailwindcss/node": {
- "version": "4.1.12",
- "resolved": "https://registry.npmjs.org/@tailwindcss/node/-/node-4.1.12.tgz",
- "integrity": "sha512-3hm9brwvQkZFe++SBt+oLjo4OLDtkvlE8q2WalaD/7QWaeM7KEJbAiY/LJZUaCs7Xa8aUu4xy3uoyX4q54UVdQ==",
+ "version": "4.3.3",
+ "resolved": "https://registry.npmjs.org/@tailwindcss/node/-/node-4.3.3.tgz",
+ "integrity": "sha512-/T8IKEsf9VTU6tLjgC7+sv2mOPtQxzE2jMw7u4Tt40Tx+QSZxpzh95/H6cMKoja9XuW7iMdLJYBB0o9G1CaAgg==",
"license": "MIT",
"dependencies": {
- "@jridgewell/remapping": "^2.3.4",
- "enhanced-resolve": "^5.18.3",
- "jiti": "^2.5.1",
- "lightningcss": "1.30.1",
- "magic-string": "^0.30.17",
+ "@jridgewell/remapping": "^2.3.5",
+ "enhanced-resolve": "^5.24.1",
+ "jiti": "^2.7.0",
+ "lightningcss": "1.32.0",
+ "magic-string": "^0.30.21",
"source-map-js": "^1.2.1",
- "tailwindcss": "4.1.12"
+ "tailwindcss": "4.3.3"
+ }
+ },
+ "node_modules/@tailwindcss/oxide": {
+ "version": "4.3.3",
+ "resolved": "https://registry.npmjs.org/@tailwindcss/oxide/-/oxide-4.3.3.tgz",
+ "integrity": "sha512-krXjAikiaFSPaK/FkAQT5UTx3VormQaiZ5hBFlJZ9UFQGB/rwg1MZIhHAG9smMQRTdyJxP6Qt5MwMtdyU5FWrA==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 20"
+ },
+ "optionalDependencies": {
+ "@tailwindcss/oxide-android-arm64": "4.3.3",
+ "@tailwindcss/oxide-darwin-arm64": "4.3.3",
+ "@tailwindcss/oxide-darwin-x64": "4.3.3",
+ "@tailwindcss/oxide-freebsd-x64": "4.3.3",
+ "@tailwindcss/oxide-linux-arm-gnueabihf": "4.3.3",
+ "@tailwindcss/oxide-linux-arm64-gnu": "4.3.3",
+ "@tailwindcss/oxide-linux-arm64-musl": "4.3.3",
+ "@tailwindcss/oxide-linux-x64-gnu": "4.3.3",
+ "@tailwindcss/oxide-linux-x64-musl": "4.3.3",
+ "@tailwindcss/oxide-wasm32-wasi": "4.3.3",
+ "@tailwindcss/oxide-win32-arm64-msvc": "4.3.3",
+ "@tailwindcss/oxide-win32-x64-msvc": "4.3.3"
}
},
"node_modules/@tailwindcss/oxide-android-arm64": {
- "version": "4.1.12",
- "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-android-arm64/-/oxide-android-arm64-4.1.12.tgz",
- "integrity": "sha512-oNY5pq+1gc4T6QVTsZKwZaGpBb2N1H1fsc1GD4o7yinFySqIuRZ2E4NvGasWc6PhYJwGK2+5YT1f9Tp80zUQZQ==",
+ "version": "4.3.3",
+ "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-android-arm64/-/oxide-android-arm64-4.3.3.tgz",
+ "integrity": "sha512-Y85A2gmPSkl5Ve5qR86GL4HT509cFqQh1aes9p3sSkyTPwt0Pppf3GkwGe4JPACcRYjgJIEhQgM6dBClnr0NYw==",
"cpu": [
"arm64"
],
@@ -1663,13 +1719,13 @@
"android"
],
"engines": {
- "node": ">= 10"
+ "node": ">= 20"
}
},
"node_modules/@tailwindcss/oxide-darwin-arm64": {
- "version": "4.1.12",
- "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-darwin-arm64/-/oxide-darwin-arm64-4.1.12.tgz",
- "integrity": "sha512-cq1qmq2HEtDV9HvZlTtrj671mCdGB93bVY6J29mwCyaMYCP/JaUBXxrQQQm7Qn33AXXASPUb2HFZlWiiHWFytw==",
+ "version": "4.3.3",
+ "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-darwin-arm64/-/oxide-darwin-arm64-4.3.3.tgz",
+ "integrity": "sha512-BiaWatpBcERQFDlOjRDpIVXuFK5PJez5SA4JMg6VYZdBYU+qKfV/vqjcIs+IYmtitf1xYQZTwXvU/8y4lfZUGw==",
"cpu": [
"arm64"
],
@@ -1679,13 +1735,13 @@
"darwin"
],
"engines": {
- "node": ">= 10"
+ "node": ">= 20"
}
},
"node_modules/@tailwindcss/oxide-darwin-x64": {
- "version": "4.1.12",
- "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-darwin-x64/-/oxide-darwin-x64-4.1.12.tgz",
- "integrity": "sha512-6UCsIeFUcBfpangqlXay9Ffty9XhFH1QuUFn0WV83W8lGdX8cD5/+2ONLluALJD5+yJ7k8mVtwy3zMZmzEfbLg==",
+ "version": "4.3.3",
+ "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-darwin-x64/-/oxide-darwin-x64-4.3.3.tgz",
+ "integrity": "sha512-fAeUqfV5ndhxRwai8cXGzdLvul9utWOmeTkv69unv4ZXixjn61Z+p9lCWdwOwA3TYboG3BwdVuN/RDjhBRl0mw==",
"cpu": [
"x64"
],
@@ -1695,13 +1751,13 @@
"darwin"
],
"engines": {
- "node": ">= 10"
+ "node": ">= 20"
}
},
"node_modules/@tailwindcss/oxide-freebsd-x64": {
- "version": "4.1.12",
- "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-freebsd-x64/-/oxide-freebsd-x64-4.1.12.tgz",
- "integrity": "sha512-JOH/f7j6+nYXIrHobRYCtoArJdMJh5zy5lr0FV0Qu47MID/vqJAY3r/OElPzx1C/wdT1uS7cPq+xdYYelny1ww==",
+ "version": "4.3.3",
+ "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-freebsd-x64/-/oxide-freebsd-x64-4.3.3.tgz",
+ "integrity": "sha512-iyf5bV6+wnAlflVeEy7R25dupxTNECZN5QMI0qNT6eT+EgaGdZcKhGkr5SdoaWiLJ3spLqIY9VCeSGrwmtg4kw==",
"cpu": [
"x64"
],
@@ -1711,13 +1767,13 @@
"freebsd"
],
"engines": {
- "node": ">= 10"
+ "node": ">= 20"
}
},
"node_modules/@tailwindcss/oxide-linux-arm-gnueabihf": {
- "version": "4.1.12",
- "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm-gnueabihf/-/oxide-linux-arm-gnueabihf-4.1.12.tgz",
- "integrity": "sha512-v4Ghvi9AU1SYgGr3/j38PD8PEe6bRfTnNSUE3YCMIRrrNigCFtHZ2TCm8142X8fcSqHBZBceDx+JlFJEfNg5zQ==",
+ "version": "4.3.3",
+ "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm-gnueabihf/-/oxide-linux-arm-gnueabihf-4.3.3.tgz",
+ "integrity": "sha512-aAYUprJAJQWWbRrPvtjdroZ56Md+JM8pMiopS6xGEwDfLhqj+2ver2p4nU4Mb3CRqcMmNBjo8KkUgcxhkzVQGQ==",
"cpu": [
"arm"
],
@@ -1727,77 +1783,89 @@
"linux"
],
"engines": {
- "node": ">= 10"
+ "node": ">= 20"
}
},
"node_modules/@tailwindcss/oxide-linux-arm64-gnu": {
- "version": "4.1.12",
- "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm64-gnu/-/oxide-linux-arm64-gnu-4.1.12.tgz",
- "integrity": "sha512-YP5s1LmetL9UsvVAKusHSyPlzSRqYyRB0f+Kl/xcYQSPLEw/BvGfxzbH+ihUciePDjiXwHh+p+qbSP3SlJw+6g==",
+ "version": "4.3.3",
+ "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm64-gnu/-/oxide-linux-arm64-gnu-4.3.3.tgz",
+ "integrity": "sha512-nDxldcEENOxZRzC2uu9jrutZdAAQtb+8WWDCSnWL1zvBk1+FN+x6MtDViPB5AJMfttVCUhehGWus3XBPgatM/w==",
"cpu": [
"arm64"
],
+ "libc": [
+ "glibc"
+ ],
"license": "MIT",
"optional": true,
"os": [
"linux"
],
"engines": {
- "node": ">= 10"
+ "node": ">= 20"
}
},
"node_modules/@tailwindcss/oxide-linux-arm64-musl": {
- "version": "4.1.12",
- "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm64-musl/-/oxide-linux-arm64-musl-4.1.12.tgz",
- "integrity": "sha512-V8pAM3s8gsrXcCv6kCHSuwyb/gPsd863iT+v1PGXC4fSL/OJqsKhfK//v8P+w9ThKIoqNbEnsZqNy+WDnwQqCA==",
+ "version": "4.3.3",
+ "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm64-musl/-/oxide-linux-arm64-musl-4.3.3.tgz",
+ "integrity": "sha512-Md44bD6veX/PC5iyF8cDVnw4HBIANZepRZZ7a8DQOvkfo5WUBwcp6iAuCUz23u+4SUkhJlD3eL7hNdW8ezd/kA==",
"cpu": [
"arm64"
],
+ "libc": [
+ "musl"
+ ],
"license": "MIT",
"optional": true,
"os": [
"linux"
],
"engines": {
- "node": ">= 10"
+ "node": ">= 20"
}
},
"node_modules/@tailwindcss/oxide-linux-x64-gnu": {
- "version": "4.1.12",
- "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-x64-gnu/-/oxide-linux-x64-gnu-4.1.12.tgz",
- "integrity": "sha512-xYfqYLjvm2UQ3TZggTGrwxjYaLB62b1Wiysw/YE3Yqbh86sOMoTn0feF98PonP7LtjsWOWcXEbGqDL7zv0uW8Q==",
+ "version": "4.3.3",
+ "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-x64-gnu/-/oxide-linux-x64-gnu-4.3.3.tgz",
+ "integrity": "sha512-tx7us1muwOKAKWao2v/GaafFeQboE6aj88vC6ziN2NCGcRm8gWUhwjzg+YdVB1e4boAtdtma4L43onunI6NS4w==",
"cpu": [
"x64"
],
+ "libc": [
+ "glibc"
+ ],
"license": "MIT",
"optional": true,
"os": [
"linux"
],
"engines": {
- "node": ">= 10"
+ "node": ">= 20"
}
},
"node_modules/@tailwindcss/oxide-linux-x64-musl": {
- "version": "4.1.12",
- "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-x64-musl/-/oxide-linux-x64-musl-4.1.12.tgz",
- "integrity": "sha512-ha0pHPamN+fWZY7GCzz5rKunlv9L5R8kdh+YNvP5awe3LtuXb5nRi/H27GeL2U+TdhDOptU7T6Is7mdwh5Ar3A==",
+ "version": "4.3.3",
+ "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-x64-musl/-/oxide-linux-x64-musl-4.3.3.tgz",
+ "integrity": "sha512-SJxX60smvHgasZoBy11dX6YRjXJFovwWBoedhbQPOBzgFWBHGB+TVPWB9BxzR7TTxU8FQZAI2AyiNCMzFm8Img==",
"cpu": [
"x64"
],
+ "libc": [
+ "musl"
+ ],
"license": "MIT",
"optional": true,
"os": [
"linux"
],
"engines": {
- "node": ">= 10"
+ "node": ">= 20"
}
},
"node_modules/@tailwindcss/oxide-wasm32-wasi": {
- "version": "4.1.12",
- "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-wasm32-wasi/-/oxide-wasm32-wasi-4.1.12.tgz",
- "integrity": "sha512-4tSyu3dW+ktzdEpuk6g49KdEangu3eCYoqPhWNsZgUhyegEda3M9rG0/j1GV/JjVVsj+lG7jWAyrTlLzd/WEBg==",
+ "version": "4.3.3",
+ "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-wasm32-wasi/-/oxide-wasm32-wasi-4.3.3.tgz",
+ "integrity": "sha512-jx1+rPhY/5Ympkktd656HBWEBLxP7dH06losBLjjf5vgCODXvi9KhtftWcMIwTFIDqBr7cRnQkdLnAG+IOlGvQ==",
"bundleDependencies": [
"@napi-rs/wasm-runtime",
"@emnapi/core",
@@ -1812,79 +1880,97 @@
"license": "MIT",
"optional": true,
"dependencies": {
- "@emnapi/core": "^1.4.5",
- "@emnapi/runtime": "^1.4.5",
- "@emnapi/wasi-threads": "^1.0.4",
- "@napi-rs/wasm-runtime": "^0.2.12",
- "@tybys/wasm-util": "^0.10.0",
- "tslib": "^2.8.0"
+ "@emnapi/core": "^1.11.1",
+ "@emnapi/runtime": "^1.11.1",
+ "@emnapi/wasi-threads": "^1.2.2",
+ "@napi-rs/wasm-runtime": "^1.1.4",
+ "@tybys/wasm-util": "^0.10.2",
+ "tslib": "^2.8.1"
},
"engines": {
"node": ">=14.0.0"
}
},
- "node_modules/@tailwindcss/oxide-win32-arm64-msvc": {
- "version": "4.1.12",
- "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-arm64-msvc/-/oxide-win32-arm64-msvc-4.1.12.tgz",
- "integrity": "sha512-iGLyD/cVP724+FGtMWslhcFyg4xyYyM+5F4hGvKA7eifPkXHRAUDFaimu53fpNg9X8dfP75pXx/zFt/jlNF+lg==",
- "cpu": [
- "arm64"
- ],
+ "node_modules/@tailwindcss/oxide-wasm32-wasi/node_modules/@emnapi/core": {
+ "version": "1.11.1",
+ "inBundle": true,
"license": "MIT",
"optional": true,
- "os": [
- "win32"
- ],
- "engines": {
- "node": ">= 10"
+ "dependencies": {
+ "@emnapi/wasi-threads": "1.2.2",
+ "tslib": "^2.4.0"
}
},
- "node_modules/@tailwindcss/vite": {
- "version": "4.1.12",
- "resolved": "https://registry.npmjs.org/@tailwindcss/vite/-/vite-4.1.12.tgz",
- "integrity": "sha512-4pt0AMFDx7gzIrAOIYgYP0KCBuKWqyW8ayrdiLEjoJTT4pKTjrzG/e4uzWtTLDziC+66R9wbUqZBccJalSE5vQ==",
+ "node_modules/@tailwindcss/oxide-wasm32-wasi/node_modules/@emnapi/runtime": {
+ "version": "1.11.1",
+ "inBundle": true,
+ "license": "MIT",
+ "optional": true,
+ "dependencies": {
+ "tslib": "^2.4.0"
+ }
+ },
+ "node_modules/@tailwindcss/oxide-wasm32-wasi/node_modules/@emnapi/wasi-threads": {
+ "version": "1.2.2",
+ "inBundle": true,
+ "license": "MIT",
+ "optional": true,
+ "dependencies": {
+ "tslib": "^2.4.0"
+ }
+ },
+ "node_modules/@tailwindcss/oxide-wasm32-wasi/node_modules/@napi-rs/wasm-runtime": {
+ "version": "1.1.4",
+ "inBundle": true,
"license": "MIT",
+ "optional": true,
"dependencies": {
- "@tailwindcss/node": "4.1.12",
- "@tailwindcss/oxide": "4.1.12",
- "tailwindcss": "4.1.12"
+ "@tybys/wasm-util": "^0.10.1"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/Brooooooklyn"
},
"peerDependencies": {
- "vite": "^5.2.0 || ^6 || ^7"
+ "@emnapi/core": "^1.7.1",
+ "@emnapi/runtime": "^1.7.1"
}
},
- "node_modules/@tailwindcss/vite/node_modules/@tailwindcss/oxide": {
- "version": "4.1.12",
- "resolved": "https://registry.npmjs.org/@tailwindcss/oxide/-/oxide-4.1.12.tgz",
- "integrity": "sha512-gM5EoKHW/ukmlEtphNwaGx45fGoEmP10v51t9unv55voWh6WrOL19hfuIdo2FjxIaZzw776/BUQg7Pck++cIVw==",
- "hasInstallScript": true,
+ "node_modules/@tailwindcss/oxide-wasm32-wasi/node_modules/@tybys/wasm-util": {
+ "version": "0.10.2",
+ "inBundle": true,
"license": "MIT",
+ "optional": true,
"dependencies": {
- "detect-libc": "^2.0.4",
- "tar": "^7.4.3"
- },
+ "tslib": "^2.4.0"
+ }
+ },
+ "node_modules/@tailwindcss/oxide-wasm32-wasi/node_modules/tslib": {
+ "version": "2.8.1",
+ "inBundle": true,
+ "license": "0BSD",
+ "optional": true
+ },
+ "node_modules/@tailwindcss/oxide-win32-arm64-msvc": {
+ "version": "4.3.3",
+ "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-arm64-msvc/-/oxide-win32-arm64-msvc-4.3.3.tgz",
+ "integrity": "sha512-3rc292Ca2ceK6Ulcc/bAVnTs/3nDtoPhyEKlgPv+yQJQi/JS/AMJlqzxvlDacL1nekbrcf6bTqp/jV4qgnPxNQ==",
+ "cpu": [
+ "arm64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
"engines": {
- "node": ">= 10"
- },
- "optionalDependencies": {
- "@tailwindcss/oxide-android-arm64": "4.1.12",
- "@tailwindcss/oxide-darwin-arm64": "4.1.12",
- "@tailwindcss/oxide-darwin-x64": "4.1.12",
- "@tailwindcss/oxide-freebsd-x64": "4.1.12",
- "@tailwindcss/oxide-linux-arm-gnueabihf": "4.1.12",
- "@tailwindcss/oxide-linux-arm64-gnu": "4.1.12",
- "@tailwindcss/oxide-linux-arm64-musl": "4.1.12",
- "@tailwindcss/oxide-linux-x64-gnu": "4.1.12",
- "@tailwindcss/oxide-linux-x64-musl": "4.1.12",
- "@tailwindcss/oxide-wasm32-wasi": "4.1.12",
- "@tailwindcss/oxide-win32-arm64-msvc": "4.1.12",
- "@tailwindcss/oxide-win32-x64-msvc": "4.1.12"
- }
- },
- "node_modules/@tailwindcss/vite/node_modules/@tailwindcss/oxide-win32-x64-msvc": {
- "version": "4.1.12",
- "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-x64-msvc/-/oxide-win32-x64-msvc-4.1.12.tgz",
- "integrity": "sha512-NKIh5rzw6CpEodv/++r0hGLlfgT/gFN+5WNdZtvh6wpU2BpGNgdjvj6H2oFc8nCM839QM1YOhjpgbAONUb4IxA==",
+ "node": ">= 20"
+ }
+ },
+ "node_modules/@tailwindcss/oxide-win32-x64-msvc": {
+ "version": "4.3.3",
+ "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-x64-msvc/-/oxide-win32-x64-msvc-4.3.3.tgz",
+ "integrity": "sha512-yJ0pwIVc/nYeGoV02WtsN8KYyLQv7kyI2wDnkezyJlGGjkd4QLwDGAwl47YpPJeuI0M0ObaXGSPjvWDPeTPggw==",
"cpu": [
"x64"
],
@@ -1894,7 +1980,21 @@
"win32"
],
"engines": {
- "node": ">= 10"
+ "node": ">= 20"
+ }
+ },
+ "node_modules/@tailwindcss/vite": {
+ "version": "4.3.3",
+ "resolved": "https://registry.npmjs.org/@tailwindcss/vite/-/vite-4.3.3.tgz",
+ "integrity": "sha512-yYU8cogLeSh/ms2jh8Fj7jaba/EWa7Ja6GoUqYZaraEuCI5YS6ms6ObZgjjedm+jm6XZjdNRWBpPP6Z86oOxcw==",
+ "license": "MIT",
+ "dependencies": {
+ "@tailwindcss/node": "4.3.3",
+ "@tailwindcss/oxide": "4.3.3",
+ "tailwindcss": "4.3.3"
+ },
+ "peerDependencies": {
+ "vite": "^5.2.0 || ^6 || ^7 || ^8"
}
},
"node_modules/@testing-library/dom": {
@@ -2074,9 +2174,9 @@
"license": "MIT"
},
"node_modules/@types/debug": {
- "version": "4.1.12",
- "resolved": "https://registry.npmjs.org/@types/debug/-/debug-4.1.12.tgz",
- "integrity": "sha512-vIChWdVG3LG1SMxEvI/AK+FWJthlrqlTu7fbrlywTkkaONwk/UAGaULXRlf8vkzFBLVm0zkMdCquhL5aOjhXPQ==",
+ "version": "4.1.13",
+ "resolved": "https://registry.npmjs.org/@types/debug/-/debug-4.1.13.tgz",
+ "integrity": "sha512-KSVgmQmzMwPlmtljOomayoR89W4FynCAi3E8PPs7vmDVPe84hT+vGPKkJfThkmXs0x0jAaa9U8uW8bbfyS2fWw==",
"license": "MIT",
"dependencies": {
"@types/ms": "*"
@@ -2105,12 +2205,12 @@
}
},
"node_modules/@types/hast": {
- "version": "2.3.10",
- "resolved": "https://registry.npmjs.org/@types/hast/-/hast-2.3.10.tgz",
- "integrity": "sha512-McWspRw8xx8J9HurkVBfYj0xKoE25tOFlHGdx4MJ5xORQrMGZNqJhVQWaIbm6Oyla5kYOXtDiopzKRJzEOkwJw==",
+ "version": "3.0.5",
+ "resolved": "https://registry.npmjs.org/@types/hast/-/hast-3.0.5.tgz",
+ "integrity": "sha512-rp/ezSWaD1m44dPKICGhiskI13nVr7qTloFwDa/IYkhhf5nzwP+zIQcIJh3WIFSBOy/H1PzB40jPjMDksN4F+g==",
"license": "MIT",
"dependencies": {
- "@types/unist": "^2"
+ "@types/unist": "*"
}
},
"node_modules/@types/json-schema": {
@@ -2182,36 +2282,36 @@
"optional": true
},
"node_modules/@types/unist": {
- "version": "2.0.11",
- "resolved": "https://registry.npmjs.org/@types/unist/-/unist-2.0.11.tgz",
- "integrity": "sha512-CmBKiL6NNo/OqgmMn95Fk9Whlp2mtvIv+KNpQKN2F4SjvrEesubTRWGYSg+BnWZOnlCaSTU1sMpsBOzgbYhnsA==",
+ "version": "3.0.3",
+ "resolved": "https://registry.npmjs.org/@types/unist/-/unist-3.0.3.tgz",
+ "integrity": "sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==",
"license": "MIT"
},
"node_modules/@ungap/structured-clone": {
- "version": "1.3.0",
- "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.3.0.tgz",
- "integrity": "sha512-WmoN8qaIAo7WTYWbAZuG8PYEhn5fkz7dZrqTBZ7dtt//lL2Gwms1IcnQ5yHqjDfX8Ft5j4YzDM23f87zBfDe9g==",
+ "version": "1.3.3",
+ "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.3.3.tgz",
+ "integrity": "sha512-60YRaenCQcVjYEKOcG824+DRGGIQ3VKErcBoAEDJZz5bKIs2ZG+X/H9Nk+Q6EVkwJk5QNApxbrc5QtBSwtrXAg==",
"license": "ISC"
},
"node_modules/@vitejs/plugin-react": {
- "version": "5.0.2",
- "resolved": "https://registry.npmjs.org/@vitejs/plugin-react/-/plugin-react-5.0.2.tgz",
- "integrity": "sha512-tmyFgixPZCx2+e6VO9TNITWcCQl8+Nl/E8YbAyPVv85QCc7/A3JrdfG2A8gIzvVhWuzMOVrFW1aReaNxrI6tbw==",
+ "version": "5.2.0",
+ "resolved": "https://registry.npmjs.org/@vitejs/plugin-react/-/plugin-react-5.2.0.tgz",
+ "integrity": "sha512-YmKkfhOAi3wsB1PhJq5Scj3GXMn3WvtQ/JC0xoopuHoXSdmtdStOpFrYaT1kie2YgFBcIe64ROzMYRjCrYOdYw==",
"dev": true,
"license": "MIT",
"dependencies": {
- "@babel/core": "^7.28.3",
+ "@babel/core": "^7.29.0",
"@babel/plugin-transform-react-jsx-self": "^7.27.1",
"@babel/plugin-transform-react-jsx-source": "^7.27.1",
- "@rolldown/pluginutils": "1.0.0-beta.34",
+ "@rolldown/pluginutils": "1.0.0-rc.3",
"@types/babel__core": "^7.20.5",
- "react-refresh": "^0.17.0"
+ "react-refresh": "^0.18.0"
},
"engines": {
"node": "^20.19.0 || >=22.12.0"
},
"peerDependencies": {
- "vite": "^4.2.0 || ^5.0.0 || ^6.0.0 || ^7.0.0"
+ "vite": "^4.2.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0"
}
},
"node_modules/@vitest/coverage-v8": {
@@ -2359,9 +2459,9 @@
}
},
"node_modules/acorn": {
- "version": "8.15.0",
- "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz",
- "integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==",
+ "version": "8.18.0",
+ "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.18.0.tgz",
+ "integrity": "sha512-lGq+9yr1/GuAWaVYIHRjvvySG5/4VfKIvC8EWxStPdcDh/Ka7FG3twP6v4d5BkravUilhIAsG4Qj83t02LWUPQ==",
"dev": true,
"license": "MIT",
"bin": {
@@ -2491,13 +2591,13 @@
"license": "MIT"
},
"node_modules/axios": {
- "version": "1.17.0",
- "resolved": "https://registry.npmjs.org/axios/-/axios-1.17.0.tgz",
- "integrity": "sha512-J8SwNxprqqpbfenehxWYXE7CW+wM1BB4w3+N+g+/Wx40xM4rsLrfPmHHxSWIxJLYDgSY/HqlFPIYb2/S3rxafw==",
+ "version": "1.19.0",
+ "resolved": "https://registry.npmjs.org/axios/-/axios-1.19.0.tgz",
+ "integrity": "sha512-ht/iuYZXEjFxLH/Hkezgd7m6JKlHHXEUSneaDz8uZe1Gj5QZtCnpyDsckvAiEnT89OEbCLmnte4R4sn7P0EKFw==",
"license": "MIT",
"dependencies": {
"follow-redirects": "^1.16.0",
- "form-data": "^4.0.5",
+ "form-data": "^4.0.6",
"https-proxy-agent": "^5.0.1",
"proxy-from-env": "^2.1.0"
}
@@ -2528,6 +2628,19 @@
"node": ">= 0.6.0"
}
},
+ "node_modules/baseline-browser-mapping": {
+ "version": "2.11.13",
+ "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.11.13.tgz",
+ "integrity": "sha512-k9HNuUVMlqVjQ9UHzfPjIqiDbWw7WqT1AoT7GL8VwvF3r0ZfArtgiSPAlmupyNquNgOJHTuH4CKYf8ttMTWBTQ==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "bin": {
+ "baseline-browser-mapping": "dist/cli.cjs"
+ },
+ "engines": {
+ "node": ">=6.0.0"
+ }
+ },
"node_modules/bidi-js": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/bidi-js/-/bidi-js-1.0.3.tgz",
@@ -2539,9 +2652,9 @@
}
},
"node_modules/brace-expansion": {
- "version": "1.1.15",
- "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.15.tgz",
- "integrity": "sha512-EwOCDEex4quD37XhqM3omwtMoJjr//isUZz1JopUNWms+4Z2ViyM/k1YIRePpoVNnQhENnxtFjLaxNHrT7xIUg==",
+ "version": "1.1.18",
+ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.18.tgz",
+ "integrity": "sha512-Edep/X9fGqVNmzKBVsDYIOtD+z1tuezV70LBjdCst9Tqu76lsnvRiZ6oTic1n+/BIwX6QDGAO94PN4N2SADvtw==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -2550,9 +2663,9 @@
}
},
"node_modules/browserslist": {
- "version": "4.25.4",
- "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.25.4.tgz",
- "integrity": "sha512-4jYpcjabC606xJ3kw2QwGEZKX0Aw7sgQdZCvIK9dhVSPh76BKo+C+btT1RRofH7B+8iNpEbgGNVWiLki5q93yg==",
+ "version": "4.28.8",
+ "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.28.8.tgz",
+ "integrity": "sha512-V2NpofLblG64mfOtSgDhOJESZEGogzDMBv/q+W6oc4LXWP/q75eOXoOaaOu1EOadB9U4Bwx/e0yzbvwKH8zalA==",
"dev": true,
"funding": [
{
@@ -2570,10 +2683,11 @@
],
"license": "MIT",
"dependencies": {
- "caniuse-lite": "^1.0.30001737",
- "electron-to-chromium": "^1.5.211",
- "node-releases": "^2.0.19",
- "update-browserslist-db": "^1.1.3"
+ "baseline-browser-mapping": "^2.11.12",
+ "caniuse-lite": "^1.0.30001809",
+ "electron-to-chromium": "^1.5.402",
+ "node-releases": "^2.0.53",
+ "update-browserslist-db": "^1.3.0"
},
"bin": {
"browserslist": "cli.js"
@@ -2606,9 +2720,9 @@
}
},
"node_modules/caniuse-lite": {
- "version": "1.0.30001739",
- "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001739.tgz",
- "integrity": "sha512-y+j60d6ulelrNSwpPyrHdl+9mJnQzHBr08xm48Qno0nSk4h3Qojh+ziv2qE6rXf4k3tadF4o1J/1tAbVm1NtnA==",
+ "version": "1.0.30001809",
+ "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001809.tgz",
+ "integrity": "sha512-xxWVywk6a6Arlk+hymeycyn/VgqEfLDxupvhH/xiY5SJ/18kmi9o6MiO320DCUzypORHLtvh0I4i04tUhCNHNQ==",
"dev": true,
"funding": [
{
@@ -2684,9 +2798,9 @@
}
},
"node_modules/character-entities": {
- "version": "1.2.4",
- "resolved": "https://registry.npmjs.org/character-entities/-/character-entities-1.2.4.tgz",
- "integrity": "sha512-iBMyeEHxfVnIakwOuDXpVkc54HijNgCyQB2w0VfGQThle6NXn50zU6V/u+LDhxHcDUPojn6Kpga3PTAD8W1bQw==",
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/character-entities/-/character-entities-2.0.2.tgz",
+ "integrity": "sha512-shx7oQ0Awen/BRIdkjkvz54PnEEI/EjwXDSIZp86/KKdbafHh1Df/RYGBhn4hbe2+uKC9FnT5UCEdyPz3ai9hQ==",
"license": "MIT",
"funding": {
"type": "github",
@@ -2704,9 +2818,9 @@
}
},
"node_modules/character-entities-legacy": {
- "version": "1.1.4",
- "resolved": "https://registry.npmjs.org/character-entities-legacy/-/character-entities-legacy-1.1.4.tgz",
- "integrity": "sha512-3Xnr+7ZFS1uxeiUDvV02wQ+QDbc55o97tIV5zHScSPJpcLm/r0DFPcoY3tYRp+VZukxuMeKgXYmsXQHO05zQeA==",
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/character-entities-legacy/-/character-entities-legacy-3.0.0.tgz",
+ "integrity": "sha512-RpPp0asT/6ufRm//AJVwpViZbGM/MkjQFxJccQRHmISF/22NBtsHqAWmL+/pmkPWoIUJdWyeVleTl1wydHATVQ==",
"license": "MIT",
"funding": {
"type": "github",
@@ -2714,24 +2828,15 @@
}
},
"node_modules/character-reference-invalid": {
- "version": "1.1.4",
- "resolved": "https://registry.npmjs.org/character-reference-invalid/-/character-reference-invalid-1.1.4.tgz",
- "integrity": "sha512-mKKUkUbhPpQlCOfIuZkvSEgktjPFIsZKRRbC6KWVEMvlzblj3i3asQv5ODsrwt0N3pHAEvjP8KTQPHkp0+6jOg==",
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/character-reference-invalid/-/character-reference-invalid-2.0.1.tgz",
+ "integrity": "sha512-iBZ4F4wRbyORVsu0jPV7gXkOsGYjGHPmAyv+HiHG8gi5PtC9KI2j1+v8/tlibRvjoWX027ypmG/n0HtO5t7unw==",
"license": "MIT",
"funding": {
"type": "github",
"url": "https://github.com/sponsors/wooorm"
}
},
- "node_modules/chownr": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/chownr/-/chownr-3.0.0.tgz",
- "integrity": "sha512-+IxzY9BZOQd/XuYPRmrvEVjF/nqj5kgT4kEq7VofrDoM1MxoRjEWkrCC3EtLi59TVawxTAn+orJwFQcrqEN1+g==",
- "license": "BlueOak-1.0.0",
- "engines": {
- "node": ">=18"
- }
- },
"node_modules/clsx": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz",
@@ -2774,9 +2879,9 @@
}
},
"node_modules/comma-separated-tokens": {
- "version": "1.0.8",
- "resolved": "https://registry.npmjs.org/comma-separated-tokens/-/comma-separated-tokens-1.0.8.tgz",
- "integrity": "sha512-GHuDRO12Sypu2cV70d1dkA2EUmXHgntrzbpvOB+Qy+49ypNfGgFQIC2fhhXbnyrJRynDCAARsT7Ou0M6hirpfw==",
+ "version": "2.0.3",
+ "resolved": "https://registry.npmjs.org/comma-separated-tokens/-/comma-separated-tokens-2.0.3.tgz",
+ "integrity": "sha512-Fu4hJdvzeylCfQPp9SGWidpzrMs7tTrlu6Vb8XGaRGck8QSNZJJp538Wrb60Lax4fPwR64ViY468OIUTbRlGZg==",
"license": "MIT",
"funding": {
"type": "github",
@@ -2811,12 +2916,15 @@
}
},
"node_modules/core-js": {
- "version": "3.49.0",
- "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.49.0.tgz",
- "integrity": "sha512-es1U2+YTtzpwkxVLwAFdSpaIMyQaq0PBgm3YD1W3Qpsn1NAmO3KSgZfu+oGSWVu6NvLHoHCV/aYcsE5wiB7ALg==",
+ "version": "3.50.0",
+ "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.50.0.tgz",
+ "integrity": "sha512-BRWgOLKkFeCgRudR6zrs8p9XJZcE14grzKMMssoYrk6krtuEZ7MTKPIY5RzOnqsEKIR9kst7wNzphttraT+Yqw==",
"hasInstallScript": true,
"license": "MIT",
"optional": true,
+ "engines": {
+ "node": "*"
+ },
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/core-js"
@@ -3002,9 +3110,9 @@
}
},
"node_modules/debug": {
- "version": "4.4.1",
- "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.1.tgz",
- "integrity": "sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==",
+ "version": "4.4.3",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz",
+ "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==",
"license": "MIT",
"dependencies": {
"ms": "^2.1.3"
@@ -3032,9 +3140,9 @@
"license": "MIT"
},
"node_modules/decode-named-character-reference": {
- "version": "1.2.0",
- "resolved": "https://registry.npmjs.org/decode-named-character-reference/-/decode-named-character-reference-1.2.0.tgz",
- "integrity": "sha512-c6fcElNV6ShtZXmsgNgFFV5tVX2PaV4g+MOAkb8eXHvn6sryJBrZa9r0zV6+dtTyoCKxtDy5tyQ5ZwQuidtd+Q==",
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/decode-named-character-reference/-/decode-named-character-reference-1.3.0.tgz",
+ "integrity": "sha512-GtpQYB283KrPp6nRw50q3U9/VfOutZOe103qlN7BPP6Ad27xYnOIWv4lPzo8HCAL+mMZofJ9KEy30fq6MfaK6Q==",
"license": "MIT",
"dependencies": {
"character-entities": "^2.0.0"
@@ -3044,16 +3152,6 @@
"url": "https://github.com/sponsors/wooorm"
}
},
- "node_modules/decode-named-character-reference/node_modules/character-entities": {
- "version": "2.0.2",
- "resolved": "https://registry.npmjs.org/character-entities/-/character-entities-2.0.2.tgz",
- "integrity": "sha512-shx7oQ0Awen/BRIdkjkvz54PnEEI/EjwXDSIZp86/KKdbafHh1Df/RYGBhn4hbe2+uKC9FnT5UCEdyPz3ai9hQ==",
- "license": "MIT",
- "funding": {
- "type": "github",
- "url": "https://github.com/sponsors/wooorm"
- }
- },
"node_modules/deep-is": {
"version": "0.1.4",
"resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz",
@@ -3080,9 +3178,9 @@
}
},
"node_modules/detect-libc": {
- "version": "2.0.4",
- "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.0.4.tgz",
- "integrity": "sha512-3UDv+G9CsCKO1WKMGw9fwq/SWJYbI0c5Y7LU1AXYoDdbhE2AHQ6N6Nb34sG8Fj7T5APy8qXDCKuuIHd1BR0tVA==",
+ "version": "2.1.2",
+ "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz",
+ "integrity": "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==",
"license": "Apache-2.0",
"engines": {
"node": ">=8"
@@ -3120,11 +3218,10 @@
}
},
"node_modules/dompurify": {
- "version": "3.2.7",
- "resolved": "https://registry.npmjs.org/dompurify/-/dompurify-3.2.7.tgz",
- "integrity": "sha512-WhL/YuveyGXJaerVlMYGWhvQswa7myDG17P7Vu65EWC05o8vfeNbvNf4d/BOvH99+ZW+LlQsc1GDKMa1vNK6dw==",
+ "version": "3.4.13",
+ "resolved": "https://registry.npmjs.org/dompurify/-/dompurify-3.4.13.tgz",
+ "integrity": "sha512-2vmYIoqjze2d+kakP8S/nS5shfsl587kzwEjcGlTdiksUVgFHnFCsLYDVj/JNqJVOQZGSYBTmuycv0PodwmnMQ==",
"license": "(MPL-2.0 OR Apache-2.0)",
- "peer": true,
"optionalDependencies": {
"@types/trusted-types": "^2.0.7"
}
@@ -3144,20 +3241,20 @@
}
},
"node_modules/electron-to-chromium": {
- "version": "1.5.212",
- "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.212.tgz",
- "integrity": "sha512-gE7ErIzSW+d8jALWMcOIgf+IB6lpfsg6NwOhPVwKzDtN2qcBix47vlin4yzSregYDxTCXOUqAZjVY/Z3naS7ww==",
+ "version": "1.5.405",
+ "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.405.tgz",
+ "integrity": "sha512-bNglH7lPH5l+yHOes7Zr4VqxhOy4BQ9ZBUX4VdoFgxMpzJk7W1ZoO3Vgd9Pxa9PyjQ76sfm2aKH/nzEcCNRlew==",
"dev": true,
"license": "ISC"
},
"node_modules/enhanced-resolve": {
- "version": "5.18.3",
- "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.18.3.tgz",
- "integrity": "sha512-d4lC8xfavMeBjzGr2vECC3fsGXziXZQyJxD868h2M/mBI3PwAuODxAkLkq5HYuvrPYcUtiLzsTo8U3PgX3Ocww==",
+ "version": "5.24.5",
+ "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.24.5.tgz",
+ "integrity": "sha512-L1l8TNvomm6UVW5B253AGxQagSQr+vGwhMlrrfRS2qmhx46AMpMVJKQYLvWYbysTMY8VoicOvzHzoHMbyzB+4A==",
"license": "MIT",
"dependencies": {
"graceful-fs": "^4.2.4",
- "tapable": "^2.2.0"
+ "tapable": "^2.3.3"
},
"engines": {
"node": ">=10.13.0"
@@ -3229,9 +3326,9 @@
}
},
"node_modules/esbuild": {
- "version": "0.27.7",
- "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.27.7.tgz",
- "integrity": "sha512-IxpibTjyVnmrIQo5aqNpCgoACA/dTKLTlhMHihVHhdkxKyPO1uBBthumT0rdHmcsk9uMonIWS0m4FljWzILh3w==",
+ "version": "0.28.2",
+ "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.28.2.tgz",
+ "integrity": "sha512-HKVLS8dvII+xoKW9kmqxbRKrnWEXfJJr/FZhhJmiqIB0e053QNYFqOBouTMO/k5sID4MvCiUCvv8b9M4h32wIA==",
"hasInstallScript": true,
"license": "MIT",
"bin": {
@@ -3241,32 +3338,32 @@
"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"
+ "@esbuild/aix-ppc64": "0.28.2",
+ "@esbuild/android-arm": "0.28.2",
+ "@esbuild/android-arm64": "0.28.2",
+ "@esbuild/android-x64": "0.28.2",
+ "@esbuild/darwin-arm64": "0.28.2",
+ "@esbuild/darwin-x64": "0.28.2",
+ "@esbuild/freebsd-arm64": "0.28.2",
+ "@esbuild/freebsd-x64": "0.28.2",
+ "@esbuild/linux-arm": "0.28.2",
+ "@esbuild/linux-arm64": "0.28.2",
+ "@esbuild/linux-ia32": "0.28.2",
+ "@esbuild/linux-loong64": "0.28.2",
+ "@esbuild/linux-mips64el": "0.28.2",
+ "@esbuild/linux-ppc64": "0.28.2",
+ "@esbuild/linux-riscv64": "0.28.2",
+ "@esbuild/linux-s390x": "0.28.2",
+ "@esbuild/linux-x64": "0.28.2",
+ "@esbuild/netbsd-arm64": "0.28.2",
+ "@esbuild/netbsd-x64": "0.28.2",
+ "@esbuild/openbsd-arm64": "0.28.2",
+ "@esbuild/openbsd-x64": "0.28.2",
+ "@esbuild/openharmony-arm64": "0.28.2",
+ "@esbuild/sunos-x64": "0.28.2",
+ "@esbuild/win32-arm64": "0.28.2",
+ "@esbuild/win32-ia32": "0.28.2",
+ "@esbuild/win32-x64": "0.28.2"
}
},
"node_modules/escalade": {
@@ -3293,26 +3390,25 @@
}
},
"node_modules/eslint": {
- "version": "9.34.0",
- "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.34.0.tgz",
- "integrity": "sha512-RNCHRX5EwdrESy3Jc9o8ie8Bog+PeYvvSR8sDGoZxNFTvZ4dlxUB3WzQ3bQMztFrSRODGrLLj8g6OFuGY/aiQg==",
+ "version": "9.39.5",
+ "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.39.5.tgz",
+ "integrity": "sha512-DgZS62aPLXKlnxILS/AYCoRvHaZeXceIzlXPkkGGzJWSow1aEk0lbTlxUSlyjC8jcaKxAdOnTDz+o1JFSBsyjw==",
"dev": true,
"license": "MIT",
"dependencies": {
- "@eslint-community/eslint-utils": "^4.2.0",
+ "@eslint-community/eslint-utils": "^4.8.0",
"@eslint-community/regexpp": "^4.12.1",
- "@eslint/config-array": "^0.21.0",
- "@eslint/config-helpers": "^0.3.1",
- "@eslint/core": "^0.15.2",
- "@eslint/eslintrc": "^3.3.1",
- "@eslint/js": "9.34.0",
- "@eslint/plugin-kit": "^0.3.5",
+ "@eslint/config-array": "^0.21.2",
+ "@eslint/config-helpers": "^0.4.2",
+ "@eslint/core": "^0.17.0",
+ "@eslint/eslintrc": "^3.3.6",
+ "@eslint/js": "9.39.5",
+ "@eslint/plugin-kit": "^0.4.1",
"@humanfs/node": "^0.16.6",
"@humanwhocodes/module-importer": "^1.0.1",
"@humanwhocodes/retry": "^0.4.2",
"@types/estree": "^1.0.6",
- "@types/json-schema": "^7.0.15",
- "ajv": "^6.12.4",
+ "ajv": "^6.14.0",
"chalk": "^4.0.0",
"cross-spawn": "^7.0.6",
"debug": "^4.3.2",
@@ -3331,7 +3427,7 @@
"is-glob": "^4.0.0",
"json-stable-stringify-without-jsonify": "^1.0.1",
"lodash.merge": "^4.6.2",
- "minimatch": "^3.1.2",
+ "minimatch": "^3.1.5",
"natural-compare": "^1.4.0",
"optionator": "^0.9.3"
},
@@ -3367,9 +3463,9 @@
}
},
"node_modules/eslint-plugin-react-refresh": {
- "version": "0.4.20",
- "resolved": "https://registry.npmjs.org/eslint-plugin-react-refresh/-/eslint-plugin-react-refresh-0.4.20.tgz",
- "integrity": "sha512-XpbHQ2q5gUF8BGOX4dHe+71qoirYMhApEPZ7sfhF/dNnOF1UXnCMGZf79SFTBO7Bz5YEIT4TMieSlJBWhP9WBA==",
+ "version": "0.4.26",
+ "resolved": "https://registry.npmjs.org/eslint-plugin-react-refresh/-/eslint-plugin-react-refresh-0.4.26.tgz",
+ "integrity": "sha512-1RETEylht2O6FM/MvgnyvT+8K21wLqDNg4qD51Zj3guhjt433XbnnkVttHMyaVyAFD03QSV4LPS5iE3VQmO7XQ==",
"dev": true,
"license": "MIT",
"peerDependencies": {
@@ -3425,9 +3521,9 @@
}
},
"node_modules/esquery": {
- "version": "1.6.0",
- "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.6.0.tgz",
- "integrity": "sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg==",
+ "version": "1.7.0",
+ "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.7.0.tgz",
+ "integrity": "sha512-Ap6G0WQwcU/LHsvLwON1fAQX9Zp0A2Y6Y/cJBl9r/JbW90Zyg4/zbG6zzKa2OTALELarYHmKu0GhpM5EO+7T0g==",
"dev": true,
"license": "BSD-3-Clause",
"dependencies": {
@@ -3634,9 +3730,9 @@
}
},
"node_modules/flatted": {
- "version": "3.4.2",
- "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.4.2.tgz",
- "integrity": "sha512-PjDse7RzhcPkIJwy5t7KPWQSZ9cAbzQXcafsetQoD7sOJRQlGikNbx7yZp2OotDnJyrDcbyRq3Ttb18iYOqkxA==",
+ "version": "3.4.4",
+ "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.4.4.tgz",
+ "integrity": "sha512-5+ybhBZANEJxaH3X5evAFatUxLfEHSr7n6kYJ+1Qd0mUqr4eu9gIf6GDbWHf8RJijHrjjO8G+la14SlL2SeS1Q==",
"dev": true,
"license": "ISC"
},
@@ -3661,16 +3757,16 @@
}
},
"node_modules/form-data": {
- "version": "4.0.5",
- "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.5.tgz",
- "integrity": "sha512-8RipRLol37bNs2bhoV67fiTEvdTrbMUYcFTiy3+wuuOnUog2QBHCZWXDRijWQfAkhBj2Uf5UnVaiWwA5vdd82w==",
+ "version": "4.0.6",
+ "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.6.tgz",
+ "integrity": "sha512-vKatAh4SlVfgbv+YtmhiRjhEMJsYpsG1Y2rMQtR+SVSbytsSD1YGzDIcrAJmdFec88u/+VoGmxnl+80gL1tRCQ==",
"license": "MIT",
"dependencies": {
"asynckit": "^0.4.0",
"combined-stream": "^1.0.8",
"es-set-tostringtag": "^2.1.0",
- "hasown": "^2.0.2",
- "mime-types": "^2.1.12"
+ "hasown": "^2.0.4",
+ "mime-types": "^2.1.35"
},
"engines": {
"node": ">= 6"
@@ -3685,12 +3781,12 @@
}
},
"node_modules/framer-motion": {
- "version": "12.42.2",
- "resolved": "https://registry.npmjs.org/framer-motion/-/framer-motion-12.42.2.tgz",
- "integrity": "sha512-5XY9luDiu0oHfHBjpDthFMh0ES+122w6p/papSJBweMkO8Sn+PW2QaEgRblQBpWFnuvZS5qvarpt/hO2pjGmnw==",
+ "version": "12.43.0",
+ "resolved": "https://registry.npmjs.org/framer-motion/-/framer-motion-12.43.0.tgz",
+ "integrity": "sha512-1eaL3RvR/kAlbG7UYcpMptEyzPoENO0c6w7ZnB3/hh2vSAz/6uGAFn6fdoqTBguNstf3MsFhJHsD/0DHiclG+g==",
"license": "MIT",
"dependencies": {
- "motion-dom": "^12.42.2",
+ "motion-dom": "^12.43.0",
"motion-utils": "^12.39.0",
"tslib": "^2.4.0"
},
@@ -3795,9 +3891,9 @@
}
},
"node_modules/globals": {
- "version": "16.3.0",
- "resolved": "https://registry.npmjs.org/globals/-/globals-16.3.0.tgz",
- "integrity": "sha512-bqWEnJ1Nt3neqx2q5SFfGS8r/ahumIakg3HcwtNlrVlwXIeNumWn/c7Pn/wKzGhf6SaW6H6uWXLqC30STCMchQ==",
+ "version": "16.5.0",
+ "resolved": "https://registry.npmjs.org/globals/-/globals-16.5.0.tgz",
+ "integrity": "sha512-c/c15i26VrJ4IRt5Z89DnIzCGDn9EcebibhAOjw5ibqEHsE1wLUgkPn9RDmNcUKyU87GeaL633nyJ+pplFR2ZQ==",
"dev": true,
"license": "MIT",
"engines": {
@@ -3808,9 +3904,9 @@
}
},
"node_modules/goober": {
- "version": "2.1.16",
- "resolved": "https://registry.npmjs.org/goober/-/goober-2.1.16.tgz",
- "integrity": "sha512-erjk19y1U33+XAMe1VTvIONHYoSqE4iS7BYUZfHaqeohLmnC0FdxEh7rQU+6MZ4OajItzjZFSRtVANrQwNq6/g==",
+ "version": "2.1.19",
+ "resolved": "https://registry.npmjs.org/goober/-/goober-2.1.19.tgz",
+ "integrity": "sha512-U7veizMqxyKlM58+Z5j2ngJBH/r9siDmxpvNxSw0PylF6WQvrASJEZrxh1hidRBJc2jqoBVSyOban5u8m+6Rxg==",
"license": "MIT",
"peerDependencies": {
"csstype": "^3.0.10"
@@ -3920,88 +4016,82 @@
"url": "https://opencollective.com/unified"
}
},
- "node_modules/hast-util-to-jsx-runtime/node_modules/@types/hast": {
- "version": "3.0.4",
- "resolved": "https://registry.npmjs.org/@types/hast/-/hast-3.0.4.tgz",
- "integrity": "sha512-WPs+bbQw5aCj+x6laNGWLH3wviHtoCv/P3+otBhbOhJgG8qtpdAMlTCxLtsTWA7LH1Oh/bFCHsBn0TPS5m30EQ==",
+ "node_modules/hast-util-whitespace": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/hast-util-whitespace/-/hast-util-whitespace-3.0.0.tgz",
+ "integrity": "sha512-88JUN06ipLwsnv+dVn+OIYOvAuvBMy/Qoi6O7mQHxdPXpjy+Cd6xRkWwux7DKO+4sYILtLBRIKgsdpS2gQc7qw==",
"license": "MIT",
"dependencies": {
- "@types/unist": "*"
- }
- },
- "node_modules/hast-util-to-jsx-runtime/node_modules/@types/unist": {
- "version": "3.0.3",
- "resolved": "https://registry.npmjs.org/@types/unist/-/unist-3.0.3.tgz",
- "integrity": "sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==",
- "license": "MIT"
- },
- "node_modules/hast-util-to-jsx-runtime/node_modules/comma-separated-tokens": {
- "version": "2.0.3",
- "resolved": "https://registry.npmjs.org/comma-separated-tokens/-/comma-separated-tokens-2.0.3.tgz",
- "integrity": "sha512-Fu4hJdvzeylCfQPp9SGWidpzrMs7tTrlu6Vb8XGaRGck8QSNZJJp538Wrb60Lax4fPwR64ViY468OIUTbRlGZg==",
- "license": "MIT",
+ "@types/hast": "^3.0.0"
+ },
"funding": {
- "type": "github",
- "url": "https://github.com/sponsors/wooorm"
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
}
},
- "node_modules/hast-util-to-jsx-runtime/node_modules/property-information": {
- "version": "7.1.0",
- "resolved": "https://registry.npmjs.org/property-information/-/property-information-7.1.0.tgz",
- "integrity": "sha512-TwEZ+X+yCJmYfL7TPUOcvBZ4QfoT5YenQiJuX//0th53DE6w0xxLEtfK3iyryQFddXuvkIk51EEgrJQ0WJkOmQ==",
+ "node_modules/hastscript": {
+ "version": "6.0.0",
+ "resolved": "https://registry.npmjs.org/hastscript/-/hastscript-6.0.0.tgz",
+ "integrity": "sha512-nDM6bvd7lIqDUiYEiu5Sl/+6ReP0BMk/2f4U/Rooccxkj0P5nm+acM5PrGJ/t5I8qPGiqZSE6hVAwZEdZIvP4w==",
"license": "MIT",
- "funding": {
- "type": "github",
- "url": "https://github.com/sponsors/wooorm"
+ "dependencies": {
+ "@types/hast": "^2.0.0",
+ "comma-separated-tokens": "^1.0.0",
+ "hast-util-parse-selector": "^2.0.0",
+ "property-information": "^5.0.0",
+ "space-separated-tokens": "^1.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
}
},
- "node_modules/hast-util-to-jsx-runtime/node_modules/space-separated-tokens": {
- "version": "2.0.2",
- "resolved": "https://registry.npmjs.org/space-separated-tokens/-/space-separated-tokens-2.0.2.tgz",
- "integrity": "sha512-PEGlAwrG8yXGXRjW32fGbg66JAlOAwbObuqVoJpv/mRgoWDQfgH1wDPvtzWyUSNAXBGSk8h755YDbbcEy3SH2Q==",
+ "node_modules/hastscript/node_modules/@types/hast": {
+ "version": "2.3.10",
+ "resolved": "https://registry.npmjs.org/@types/hast/-/hast-2.3.10.tgz",
+ "integrity": "sha512-McWspRw8xx8J9HurkVBfYj0xKoE25tOFlHGdx4MJ5xORQrMGZNqJhVQWaIbm6Oyla5kYOXtDiopzKRJzEOkwJw==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/unist": "^2"
+ }
+ },
+ "node_modules/hastscript/node_modules/@types/unist": {
+ "version": "2.0.11",
+ "resolved": "https://registry.npmjs.org/@types/unist/-/unist-2.0.11.tgz",
+ "integrity": "sha512-CmBKiL6NNo/OqgmMn95Fk9Whlp2mtvIv+KNpQKN2F4SjvrEesubTRWGYSg+BnWZOnlCaSTU1sMpsBOzgbYhnsA==",
+ "license": "MIT"
+ },
+ "node_modules/hastscript/node_modules/comma-separated-tokens": {
+ "version": "1.0.8",
+ "resolved": "https://registry.npmjs.org/comma-separated-tokens/-/comma-separated-tokens-1.0.8.tgz",
+ "integrity": "sha512-GHuDRO12Sypu2cV70d1dkA2EUmXHgntrzbpvOB+Qy+49ypNfGgFQIC2fhhXbnyrJRynDCAARsT7Ou0M6hirpfw==",
"license": "MIT",
"funding": {
"type": "github",
"url": "https://github.com/sponsors/wooorm"
}
},
- "node_modules/hast-util-whitespace": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/hast-util-whitespace/-/hast-util-whitespace-3.0.0.tgz",
- "integrity": "sha512-88JUN06ipLwsnv+dVn+OIYOvAuvBMy/Qoi6O7mQHxdPXpjy+Cd6xRkWwux7DKO+4sYILtLBRIKgsdpS2gQc7qw==",
+ "node_modules/hastscript/node_modules/property-information": {
+ "version": "5.6.0",
+ "resolved": "https://registry.npmjs.org/property-information/-/property-information-5.6.0.tgz",
+ "integrity": "sha512-YUHSPk+A30YPv+0Qf8i9Mbfe/C0hdPXk1s1jPVToV8pk8BQtpw10ct89Eo7OWkutrwqvT0eicAxlOg3dOAu8JA==",
"license": "MIT",
"dependencies": {
- "@types/hast": "^3.0.0"
+ "xtend": "^4.0.0"
},
"funding": {
- "type": "opencollective",
- "url": "https://opencollective.com/unified"
- }
- },
- "node_modules/hast-util-whitespace/node_modules/@types/hast": {
- "version": "3.0.4",
- "resolved": "https://registry.npmjs.org/@types/hast/-/hast-3.0.4.tgz",
- "integrity": "sha512-WPs+bbQw5aCj+x6laNGWLH3wviHtoCv/P3+otBhbOhJgG8qtpdAMlTCxLtsTWA7LH1Oh/bFCHsBn0TPS5m30EQ==",
- "license": "MIT",
- "dependencies": {
- "@types/unist": "*"
+ "type": "github",
+ "url": "https://github.com/sponsors/wooorm"
}
},
- "node_modules/hastscript": {
- "version": "6.0.0",
- "resolved": "https://registry.npmjs.org/hastscript/-/hastscript-6.0.0.tgz",
- "integrity": "sha512-nDM6bvd7lIqDUiYEiu5Sl/+6ReP0BMk/2f4U/Rooccxkj0P5nm+acM5PrGJ/t5I8qPGiqZSE6hVAwZEdZIvP4w==",
+ "node_modules/hastscript/node_modules/space-separated-tokens": {
+ "version": "1.1.5",
+ "resolved": "https://registry.npmjs.org/space-separated-tokens/-/space-separated-tokens-1.1.5.tgz",
+ "integrity": "sha512-q/JSVd1Lptzhf5bkYm4ob4iWPjx0KiRe3sRFBNrVqbJkFaBm5vbbowy1mymoPNLRa52+oadOhJ+K49wsSeSjTA==",
"license": "MIT",
- "dependencies": {
- "@types/hast": "^2.0.0",
- "comma-separated-tokens": "^1.0.0",
- "hast-util-parse-selector": "^2.0.0",
- "property-information": "^5.0.0",
- "space-separated-tokens": "^1.0.0"
- },
"funding": {
- "type": "opencollective",
- "url": "https://opencollective.com/unified"
+ "type": "github",
+ "url": "https://github.com/sponsors/wooorm"
}
},
"node_modules/highlight.js": {
@@ -4073,15 +4163,6 @@
"jspdf": "^4.0.0"
}
},
- "node_modules/html2pdf.js/node_modules/dompurify": {
- "version": "3.4.12",
- "resolved": "https://registry.npmjs.org/dompurify/-/dompurify-3.4.12.tgz",
- "integrity": "sha512-zQvGet8Z2sWbQhCmfFz/T5QWH2oBmjnqK3qvOjaqaNLrLEF912WamU+ohnTp0TCep/MFVHpdJuCZEdFOdTnEFg==",
- "license": "(MPL-2.0 OR Apache-2.0)",
- "optionalDependencies": {
- "@types/trusted-types": "^2.0.7"
- }
- },
"node_modules/https-proxy-agent": {
"version": "5.0.1",
"resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz",
@@ -4133,9 +4214,9 @@
}
},
"node_modules/inline-style-parser": {
- "version": "0.2.4",
- "resolved": "https://registry.npmjs.org/inline-style-parser/-/inline-style-parser-0.2.4.tgz",
- "integrity": "sha512-0aO8FkhNZlj/ZIbNi7Lxxr12obT7cL1moPfE4tg1LkX7LlLfC6DeX4l2ZEud1ukP9jNQyNnfzQVqwbwmAATY4Q==",
+ "version": "0.2.7",
+ "resolved": "https://registry.npmjs.org/inline-style-parser/-/inline-style-parser-0.2.7.tgz",
+ "integrity": "sha512-Nb2ctOyNR8DqQoR0OwRG95uNWIC0C1lCgf5Naz5H6Ji72KZ8OcFZLz2P5sNgwlyoJ8Yif11oMuYs5pBQa86csA==",
"license": "MIT"
},
"node_modules/internmap": {
@@ -4154,9 +4235,9 @@
"license": "MIT"
},
"node_modules/is-alphabetical": {
- "version": "1.0.4",
- "resolved": "https://registry.npmjs.org/is-alphabetical/-/is-alphabetical-1.0.4.tgz",
- "integrity": "sha512-DwzsA04LQ10FHTZuL0/grVDk4rFoVH1pjAToYwBrHSxcrBIGQuXrQMtD5U1b0U2XVgKZCTLLP8u2Qxqhy3l2Vg==",
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/is-alphabetical/-/is-alphabetical-2.0.1.tgz",
+ "integrity": "sha512-FWyyY60MeTNyeSRpkM2Iry0G9hpr7/9kD40mD/cGQEuilcZYS4okz8SN2Q6rLCJ8gbCt6fN+rC+6tMGS99LaxQ==",
"license": "MIT",
"funding": {
"type": "github",
@@ -4164,13 +4245,13 @@
}
},
"node_modules/is-alphanumerical": {
- "version": "1.0.4",
- "resolved": "https://registry.npmjs.org/is-alphanumerical/-/is-alphanumerical-1.0.4.tgz",
- "integrity": "sha512-UzoZUr+XfVz3t3v4KyGEniVL9BDRoQtY7tOyrRybkVNjDFWyo1yhXNGrrBTQxp3ib9BLAWs7k2YKBQsFRkZG9A==",
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/is-alphanumerical/-/is-alphanumerical-2.0.1.tgz",
+ "integrity": "sha512-hmbYhX/9MUMF5uh7tOXyK/n0ZvWpad5caBA17GsC6vyuCqaWliRG5K1qS9inmUhEMaOBIW7/whAnSwveW/LtZw==",
"license": "MIT",
"dependencies": {
- "is-alphabetical": "^1.0.0",
- "is-decimal": "^1.0.0"
+ "is-alphabetical": "^2.0.0",
+ "is-decimal": "^2.0.0"
},
"funding": {
"type": "github",
@@ -4178,9 +4259,9 @@
}
},
"node_modules/is-decimal": {
- "version": "1.0.4",
- "resolved": "https://registry.npmjs.org/is-decimal/-/is-decimal-1.0.4.tgz",
- "integrity": "sha512-RGdriMmQQvZ2aqaQq3awNA6dCGtKpiDFcOzrTWrDAT2MiWrKQVPmxLGHl7Y2nNu6led0kEyoX0enY0qXYsv9zw==",
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/is-decimal/-/is-decimal-2.0.1.tgz",
+ "integrity": "sha512-AAB9hiomQs5DXWcRB1rqsxGUstbRroFOPPVAomNk/3XHR5JyEZChOyTWe2oayKnsSsr/kcGqF+z6yuH6HHpN0A==",
"license": "MIT",
"funding": {
"type": "github",
@@ -4211,9 +4292,9 @@
}
},
"node_modules/is-hexadecimal": {
- "version": "1.0.4",
- "resolved": "https://registry.npmjs.org/is-hexadecimal/-/is-hexadecimal-1.0.4.tgz",
- "integrity": "sha512-gyPJuv83bHMpocVYoqof5VDiZveEoGoFL8m3BXNb2VW8Xs+rz9kqO8LOQ5DH6EsuvilT1ApazU0pyl+ytbPtlw==",
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/is-hexadecimal/-/is-hexadecimal-2.0.1.tgz",
+ "integrity": "sha512-DgZQp241c8oO6cA1SbTEWiXeoxV42vlcJxgH+B3hi1AiqqKruZR3ZGF8In3fj4+/y/7rHvlOZLZtgJ/4ttYGZg==",
"license": "MIT",
"funding": {
"type": "github",
@@ -4286,9 +4367,9 @@
}
},
"node_modules/jiti": {
- "version": "2.5.1",
- "resolved": "https://registry.npmjs.org/jiti/-/jiti-2.5.1.tgz",
- "integrity": "sha512-twQoecYPiVA5K/h6SxtORw/Bs3ar+mLUtoPSc7iMXzQzK8d7eJ/R09wmTwAjiamETn1cXYPGfNnu7DMoHgu12w==",
+ "version": "2.7.0",
+ "resolved": "https://registry.npmjs.org/jiti/-/jiti-2.7.0.tgz",
+ "integrity": "sha512-AC/7JofJvZGrrneWNaEnJeOLUx+JlGt7tNa0wZiRPT4MY1wmfKjt2+6O2p2uz2+skll8OZZmJMNqeke7kKbNgQ==",
"license": "MIT",
"bin": {
"jiti": "lib/jiti-cli.mjs"
@@ -4301,9 +4382,9 @@
"license": "MIT"
},
"node_modules/js-yaml": {
- "version": "4.2.0",
- "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.2.0.tgz",
- "integrity": "sha512-ePWsvanv0DWuDRsW8dnt+R4jQ31SCRCQ7hhNcPXZPsoBZiemuZNYGf7adZdqX2D86j6rvKp3RpCxVTSb8WQlOw==",
+ "version": "4.3.1",
+ "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.3.1.tgz",
+ "integrity": "sha512-CY6crGq313MX8GkwvB7tzgp99vjQxY1++5y10/BKN/GUfHqWaOGQMNZkBvqSzsZKWk/ijwHlWzzkLulsGHhjWQ==",
"dev": true,
"funding": [
{
@@ -4438,16 +4519,6 @@
"html2canvas": "^1.0.0-rc.5"
}
},
- "node_modules/jspdf/node_modules/dompurify": {
- "version": "3.4.12",
- "resolved": "https://registry.npmjs.org/dompurify/-/dompurify-3.4.12.tgz",
- "integrity": "sha512-zQvGet8Z2sWbQhCmfFz/T5QWH2oBmjnqK3qvOjaqaNLrLEF912WamU+ohnTp0TCep/MFVHpdJuCZEdFOdTnEFg==",
- "license": "(MPL-2.0 OR Apache-2.0)",
- "optional": true,
- "optionalDependencies": {
- "@types/trusted-types": "^2.0.7"
- }
- },
"node_modules/keyv": {
"version": "4.5.4",
"resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz",
@@ -4473,9 +4544,9 @@
}
},
"node_modules/lightningcss": {
- "version": "1.30.1",
- "resolved": "https://registry.npmjs.org/lightningcss/-/lightningcss-1.30.1.tgz",
- "integrity": "sha512-xi6IyHML+c9+Q3W0S4fCQJOym42pyurFiJUHEcEyHS0CeKzia4yZDEsLlqOFykxOdHpNy0NmvVO31vcSqAxJCg==",
+ "version": "1.32.0",
+ "resolved": "https://registry.npmjs.org/lightningcss/-/lightningcss-1.32.0.tgz",
+ "integrity": "sha512-NXYBzinNrblfraPGyrbPoD19C1h9lfI/1mzgWYvXUTe414Gz/X1FD2XBZSZM7rRTrMA8JL3OtAaGifrIKhQ5yQ==",
"license": "MPL-2.0",
"dependencies": {
"detect-libc": "^2.0.3"
@@ -4488,22 +4559,43 @@
"url": "https://opencollective.com/parcel"
},
"optionalDependencies": {
- "lightningcss-darwin-arm64": "1.30.1",
- "lightningcss-darwin-x64": "1.30.1",
- "lightningcss-freebsd-x64": "1.30.1",
- "lightningcss-linux-arm-gnueabihf": "1.30.1",
- "lightningcss-linux-arm64-gnu": "1.30.1",
- "lightningcss-linux-arm64-musl": "1.30.1",
- "lightningcss-linux-x64-gnu": "1.30.1",
- "lightningcss-linux-x64-musl": "1.30.1",
- "lightningcss-win32-arm64-msvc": "1.30.1",
- "lightningcss-win32-x64-msvc": "1.30.1"
+ "lightningcss-android-arm64": "1.32.0",
+ "lightningcss-darwin-arm64": "1.32.0",
+ "lightningcss-darwin-x64": "1.32.0",
+ "lightningcss-freebsd-x64": "1.32.0",
+ "lightningcss-linux-arm-gnueabihf": "1.32.0",
+ "lightningcss-linux-arm64-gnu": "1.32.0",
+ "lightningcss-linux-arm64-musl": "1.32.0",
+ "lightningcss-linux-x64-gnu": "1.32.0",
+ "lightningcss-linux-x64-musl": "1.32.0",
+ "lightningcss-win32-arm64-msvc": "1.32.0",
+ "lightningcss-win32-x64-msvc": "1.32.0"
+ }
+ },
+ "node_modules/lightningcss-android-arm64": {
+ "version": "1.32.0",
+ "resolved": "https://registry.npmjs.org/lightningcss-android-arm64/-/lightningcss-android-arm64-1.32.0.tgz",
+ "integrity": "sha512-YK7/ClTt4kAK0vo6w3X+Pnm0D2cf2vPHbhOXdoNti1Ga0al1P4TBZhwjATvjNwLEBCnKvjJc2jQgHXH0NEwlAg==",
+ "cpu": [
+ "arm64"
+ ],
+ "license": "MPL-2.0",
+ "optional": true,
+ "os": [
+ "android"
+ ],
+ "engines": {
+ "node": ">= 12.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/parcel"
}
},
"node_modules/lightningcss-darwin-arm64": {
- "version": "1.30.1",
- "resolved": "https://registry.npmjs.org/lightningcss-darwin-arm64/-/lightningcss-darwin-arm64-1.30.1.tgz",
- "integrity": "sha512-c8JK7hyE65X1MHMN+Viq9n11RRC7hgin3HhYKhrMyaXflk5GVplZ60IxyoVtzILeKr+xAJwg6zK6sjTBJ0FKYQ==",
+ "version": "1.32.0",
+ "resolved": "https://registry.npmjs.org/lightningcss-darwin-arm64/-/lightningcss-darwin-arm64-1.32.0.tgz",
+ "integrity": "sha512-RzeG9Ju5bag2Bv1/lwlVJvBE3q6TtXskdZLLCyfg5pt+HLz9BqlICO7LZM7VHNTTn/5PRhHFBSjk5lc4cmscPQ==",
"cpu": [
"arm64"
],
@@ -4521,9 +4613,9 @@
}
},
"node_modules/lightningcss-darwin-x64": {
- "version": "1.30.1",
- "resolved": "https://registry.npmjs.org/lightningcss-darwin-x64/-/lightningcss-darwin-x64-1.30.1.tgz",
- "integrity": "sha512-k1EvjakfumAQoTfcXUcHQZhSpLlkAuEkdMBsI/ivWw9hL+7FtilQc0Cy3hrx0AAQrVtQAbMI7YjCgYgvn37PzA==",
+ "version": "1.32.0",
+ "resolved": "https://registry.npmjs.org/lightningcss-darwin-x64/-/lightningcss-darwin-x64-1.32.0.tgz",
+ "integrity": "sha512-U+QsBp2m/s2wqpUYT/6wnlagdZbtZdndSmut/NJqlCcMLTWp5muCrID+K5UJ6jqD2BFshejCYXniPDbNh73V8w==",
"cpu": [
"x64"
],
@@ -4541,9 +4633,9 @@
}
},
"node_modules/lightningcss-freebsd-x64": {
- "version": "1.30.1",
- "resolved": "https://registry.npmjs.org/lightningcss-freebsd-x64/-/lightningcss-freebsd-x64-1.30.1.tgz",
- "integrity": "sha512-kmW6UGCGg2PcyUE59K5r0kWfKPAVy4SltVeut+umLCFoJ53RdCUWxcRDzO1eTaxf/7Q2H7LTquFHPL5R+Gjyig==",
+ "version": "1.32.0",
+ "resolved": "https://registry.npmjs.org/lightningcss-freebsd-x64/-/lightningcss-freebsd-x64-1.32.0.tgz",
+ "integrity": "sha512-JCTigedEksZk3tHTTthnMdVfGf61Fky8Ji2E4YjUTEQX14xiy/lTzXnu1vwiZe3bYe0q+SpsSH/CTeDXK6WHig==",
"cpu": [
"x64"
],
@@ -4561,9 +4653,9 @@
}
},
"node_modules/lightningcss-linux-arm-gnueabihf": {
- "version": "1.30.1",
- "resolved": "https://registry.npmjs.org/lightningcss-linux-arm-gnueabihf/-/lightningcss-linux-arm-gnueabihf-1.30.1.tgz",
- "integrity": "sha512-MjxUShl1v8pit+6D/zSPq9S9dQ2NPFSQwGvxBCYaBYLPlCWuPh9/t1MRS8iUaR8i+a6w7aps+B4N0S1TYP/R+Q==",
+ "version": "1.32.0",
+ "resolved": "https://registry.npmjs.org/lightningcss-linux-arm-gnueabihf/-/lightningcss-linux-arm-gnueabihf-1.32.0.tgz",
+ "integrity": "sha512-x6rnnpRa2GL0zQOkt6rts3YDPzduLpWvwAF6EMhXFVZXD4tPrBkEFqzGowzCsIWsPjqSK+tyNEODUBXeeVHSkw==",
"cpu": [
"arm"
],
@@ -4581,12 +4673,15 @@
}
},
"node_modules/lightningcss-linux-arm64-gnu": {
- "version": "1.30.1",
- "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-gnu/-/lightningcss-linux-arm64-gnu-1.30.1.tgz",
- "integrity": "sha512-gB72maP8rmrKsnKYy8XUuXi/4OctJiuQjcuqWNlJQ6jZiWqtPvqFziskH3hnajfvKB27ynbVCucKSm2rkQp4Bw==",
+ "version": "1.32.0",
+ "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-gnu/-/lightningcss-linux-arm64-gnu-1.32.0.tgz",
+ "integrity": "sha512-0nnMyoyOLRJXfbMOilaSRcLH3Jw5z9HDNGfT/gwCPgaDjnx0i8w7vBzFLFR1f6CMLKF8gVbebmkUN3fa/kQJpQ==",
"cpu": [
"arm64"
],
+ "libc": [
+ "glibc"
+ ],
"license": "MPL-2.0",
"optional": true,
"os": [
@@ -4601,12 +4696,15 @@
}
},
"node_modules/lightningcss-linux-arm64-musl": {
- "version": "1.30.1",
- "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-musl/-/lightningcss-linux-arm64-musl-1.30.1.tgz",
- "integrity": "sha512-jmUQVx4331m6LIX+0wUhBbmMX7TCfjF5FoOH6SD1CttzuYlGNVpA7QnrmLxrsub43ClTINfGSYyHe2HWeLl5CQ==",
+ "version": "1.32.0",
+ "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-musl/-/lightningcss-linux-arm64-musl-1.32.0.tgz",
+ "integrity": "sha512-UpQkoenr4UJEzgVIYpI80lDFvRmPVg6oqboNHfoH4CQIfNA+HOrZ7Mo7KZP02dC6LjghPQJeBsvXhJod/wnIBg==",
"cpu": [
"arm64"
],
+ "libc": [
+ "musl"
+ ],
"license": "MPL-2.0",
"optional": true,
"os": [
@@ -4621,12 +4719,15 @@
}
},
"node_modules/lightningcss-linux-x64-gnu": {
- "version": "1.30.1",
- "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-gnu/-/lightningcss-linux-x64-gnu-1.30.1.tgz",
- "integrity": "sha512-piWx3z4wN8J8z3+O5kO74+yr6ze/dKmPnI7vLqfSqI8bccaTGY5xiSGVIJBDd5K5BHlvVLpUB3S2YCfelyJ1bw==",
+ "version": "1.32.0",
+ "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-gnu/-/lightningcss-linux-x64-gnu-1.32.0.tgz",
+ "integrity": "sha512-V7Qr52IhZmdKPVr+Vtw8o+WLsQJYCTd8loIfpDaMRWGUZfBOYEJeyJIkqGIDMZPwPx24pUMfwSxxI8phr/MbOA==",
"cpu": [
"x64"
],
+ "libc": [
+ "glibc"
+ ],
"license": "MPL-2.0",
"optional": true,
"os": [
@@ -4641,12 +4742,15 @@
}
},
"node_modules/lightningcss-linux-x64-musl": {
- "version": "1.30.1",
- "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-musl/-/lightningcss-linux-x64-musl-1.30.1.tgz",
- "integrity": "sha512-rRomAK7eIkL+tHY0YPxbc5Dra2gXlI63HL+v1Pdi1a3sC+tJTcFrHX+E86sulgAXeI7rSzDYhPSeHHjqFhqfeQ==",
+ "version": "1.32.0",
+ "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-musl/-/lightningcss-linux-x64-musl-1.32.0.tgz",
+ "integrity": "sha512-bYcLp+Vb0awsiXg/80uCRezCYHNg1/l3mt0gzHnWV9XP1W5sKa5/TCdGWaR/zBM2PeF/HbsQv/j2URNOiVuxWg==",
"cpu": [
"x64"
],
+ "libc": [
+ "musl"
+ ],
"license": "MPL-2.0",
"optional": true,
"os": [
@@ -4661,9 +4765,9 @@
}
},
"node_modules/lightningcss-win32-arm64-msvc": {
- "version": "1.30.1",
- "resolved": "https://registry.npmjs.org/lightningcss-win32-arm64-msvc/-/lightningcss-win32-arm64-msvc-1.30.1.tgz",
- "integrity": "sha512-mSL4rqPi4iXq5YVqzSsJgMVFENoa4nGTT/GjO2c0Yl9OuQfPsIfncvLrEW6RbbB24WtZ3xP/2CCmI3tNkNV4oA==",
+ "version": "1.32.0",
+ "resolved": "https://registry.npmjs.org/lightningcss-win32-arm64-msvc/-/lightningcss-win32-arm64-msvc-1.32.0.tgz",
+ "integrity": "sha512-8SbC8BR40pS6baCM8sbtYDSwEVQd4JlFTOlaD3gWGHfThTcABnNDBda6eTZeqbofalIJhFx0qKzgHJmcPTnGdw==",
"cpu": [
"arm64"
],
@@ -4680,10 +4784,10 @@
"url": "https://opencollective.com/parcel"
}
},
- "node_modules/lightningcss/node_modules/lightningcss-win32-x64-msvc": {
- "version": "1.30.1",
- "resolved": "https://registry.npmjs.org/lightningcss-win32-x64-msvc/-/lightningcss-win32-x64-msvc-1.30.1.tgz",
- "integrity": "sha512-PVqXh48wh4T53F/1CCu8PIPCxLzWyCnn/9T5W1Jpmdy5h9Cwd+0YQS6/LwhHXSafuc61/xg9Lv5OrCby6a++jg==",
+ "node_modules/lightningcss-win32-x64-msvc": {
+ "version": "1.32.0",
+ "resolved": "https://registry.npmjs.org/lightningcss-win32-x64-msvc/-/lightningcss-win32-x64-msvc-1.32.0.tgz",
+ "integrity": "sha512-Amq9B/SoZYdDi1kFrojnoqPLxYhQ4Wo5XiL8EVJrVsB8ARoC1PWW6VGtT0WKCemjy8aC+louJnjS7U18x3b06Q==",
"cpu": [
"x64"
],
@@ -4906,9 +5010,9 @@
}
},
"node_modules/mdast-util-from-markdown": {
- "version": "2.0.2",
- "resolved": "https://registry.npmjs.org/mdast-util-from-markdown/-/mdast-util-from-markdown-2.0.2.tgz",
- "integrity": "sha512-uZhTV/8NBuw0WHkPTrCqDOl0zVe1BIng5ZtHoDk49ME1qqcjYmmLmOf0gELgcRMxN4w2iuIeVso5/6QymSrgmA==",
+ "version": "2.0.3",
+ "resolved": "https://registry.npmjs.org/mdast-util-from-markdown/-/mdast-util-from-markdown-2.0.3.tgz",
+ "integrity": "sha512-W4mAWTvSlKvf8L6J+VN9yLSqQ9AOAAvHuoDAmPkz4dHf553m5gVj2ejadHJhoJmcmxEnOv6Pa8XJhpxE93kb8Q==",
"license": "MIT",
"dependencies": {
"@types/mdast": "^4.0.0",
@@ -4929,12 +5033,6 @@
"url": "https://opencollective.com/unified"
}
},
- "node_modules/mdast-util-from-markdown/node_modules/@types/unist": {
- "version": "3.0.3",
- "resolved": "https://registry.npmjs.org/@types/unist/-/unist-3.0.3.tgz",
- "integrity": "sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==",
- "license": "MIT"
- },
"node_modules/mdast-util-gfm": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/mdast-util-gfm/-/mdast-util-gfm-3.1.0.tgz",
@@ -5054,15 +5152,6 @@
"url": "https://opencollective.com/unified"
}
},
- "node_modules/mdast-util-mdx-expression/node_modules/@types/hast": {
- "version": "3.0.4",
- "resolved": "https://registry.npmjs.org/@types/hast/-/hast-3.0.4.tgz",
- "integrity": "sha512-WPs+bbQw5aCj+x6laNGWLH3wviHtoCv/P3+otBhbOhJgG8qtpdAMlTCxLtsTWA7LH1Oh/bFCHsBn0TPS5m30EQ==",
- "license": "MIT",
- "dependencies": {
- "@types/unist": "*"
- }
- },
"node_modules/mdast-util-mdx-jsx": {
"version": "3.2.0",
"resolved": "https://registry.npmjs.org/mdast-util-mdx-jsx/-/mdast-util-mdx-jsx-3.2.0.tgz",
@@ -5087,110 +5176,6 @@
"url": "https://opencollective.com/unified"
}
},
- "node_modules/mdast-util-mdx-jsx/node_modules/@types/hast": {
- "version": "3.0.4",
- "resolved": "https://registry.npmjs.org/@types/hast/-/hast-3.0.4.tgz",
- "integrity": "sha512-WPs+bbQw5aCj+x6laNGWLH3wviHtoCv/P3+otBhbOhJgG8qtpdAMlTCxLtsTWA7LH1Oh/bFCHsBn0TPS5m30EQ==",
- "license": "MIT",
- "dependencies": {
- "@types/unist": "*"
- }
- },
- "node_modules/mdast-util-mdx-jsx/node_modules/@types/unist": {
- "version": "3.0.3",
- "resolved": "https://registry.npmjs.org/@types/unist/-/unist-3.0.3.tgz",
- "integrity": "sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==",
- "license": "MIT"
- },
- "node_modules/mdast-util-mdx-jsx/node_modules/character-entities-legacy": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/character-entities-legacy/-/character-entities-legacy-3.0.0.tgz",
- "integrity": "sha512-RpPp0asT/6ufRm//AJVwpViZbGM/MkjQFxJccQRHmISF/22NBtsHqAWmL+/pmkPWoIUJdWyeVleTl1wydHATVQ==",
- "license": "MIT",
- "funding": {
- "type": "github",
- "url": "https://github.com/sponsors/wooorm"
- }
- },
- "node_modules/mdast-util-mdx-jsx/node_modules/character-reference-invalid": {
- "version": "2.0.1",
- "resolved": "https://registry.npmjs.org/character-reference-invalid/-/character-reference-invalid-2.0.1.tgz",
- "integrity": "sha512-iBZ4F4wRbyORVsu0jPV7gXkOsGYjGHPmAyv+HiHG8gi5PtC9KI2j1+v8/tlibRvjoWX027ypmG/n0HtO5t7unw==",
- "license": "MIT",
- "funding": {
- "type": "github",
- "url": "https://github.com/sponsors/wooorm"
- }
- },
- "node_modules/mdast-util-mdx-jsx/node_modules/is-alphabetical": {
- "version": "2.0.1",
- "resolved": "https://registry.npmjs.org/is-alphabetical/-/is-alphabetical-2.0.1.tgz",
- "integrity": "sha512-FWyyY60MeTNyeSRpkM2Iry0G9hpr7/9kD40mD/cGQEuilcZYS4okz8SN2Q6rLCJ8gbCt6fN+rC+6tMGS99LaxQ==",
- "license": "MIT",
- "funding": {
- "type": "github",
- "url": "https://github.com/sponsors/wooorm"
- }
- },
- "node_modules/mdast-util-mdx-jsx/node_modules/is-alphanumerical": {
- "version": "2.0.1",
- "resolved": "https://registry.npmjs.org/is-alphanumerical/-/is-alphanumerical-2.0.1.tgz",
- "integrity": "sha512-hmbYhX/9MUMF5uh7tOXyK/n0ZvWpad5caBA17GsC6vyuCqaWliRG5K1qS9inmUhEMaOBIW7/whAnSwveW/LtZw==",
- "license": "MIT",
- "dependencies": {
- "is-alphabetical": "^2.0.0",
- "is-decimal": "^2.0.0"
- },
- "funding": {
- "type": "github",
- "url": "https://github.com/sponsors/wooorm"
- }
- },
- "node_modules/mdast-util-mdx-jsx/node_modules/is-decimal": {
- "version": "2.0.1",
- "resolved": "https://registry.npmjs.org/is-decimal/-/is-decimal-2.0.1.tgz",
- "integrity": "sha512-AAB9hiomQs5DXWcRB1rqsxGUstbRroFOPPVAomNk/3XHR5JyEZChOyTWe2oayKnsSsr/kcGqF+z6yuH6HHpN0A==",
- "license": "MIT",
- "funding": {
- "type": "github",
- "url": "https://github.com/sponsors/wooorm"
- }
- },
- "node_modules/mdast-util-mdx-jsx/node_modules/is-hexadecimal": {
- "version": "2.0.1",
- "resolved": "https://registry.npmjs.org/is-hexadecimal/-/is-hexadecimal-2.0.1.tgz",
- "integrity": "sha512-DgZQp241c8oO6cA1SbTEWiXeoxV42vlcJxgH+B3hi1AiqqKruZR3ZGF8In3fj4+/y/7rHvlOZLZtgJ/4ttYGZg==",
- "license": "MIT",
- "funding": {
- "type": "github",
- "url": "https://github.com/sponsors/wooorm"
- }
- },
- "node_modules/mdast-util-mdx-jsx/node_modules/parse-entities": {
- "version": "4.0.2",
- "resolved": "https://registry.npmjs.org/parse-entities/-/parse-entities-4.0.2.tgz",
- "integrity": "sha512-GG2AQYWoLgL877gQIKeRPGO1xF9+eG1ujIb5soS5gPvLQ1y2o8FL90w2QWNdf9I361Mpp7726c+lj3U0qK1uGw==",
- "license": "MIT",
- "dependencies": {
- "@types/unist": "^2.0.0",
- "character-entities-legacy": "^3.0.0",
- "character-reference-invalid": "^2.0.0",
- "decode-named-character-reference": "^1.0.0",
- "is-alphanumerical": "^2.0.0",
- "is-decimal": "^2.0.0",
- "is-hexadecimal": "^2.0.0"
- },
- "funding": {
- "type": "github",
- "url": "https://github.com/sponsors/wooorm"
- }
- },
- "node_modules/mdast-util-mdx-jsx/node_modules/parse-entities/node_modules/@types/unist": {
- "version": "2.0.11",
- "resolved": "https://registry.npmjs.org/@types/unist/-/unist-2.0.11.tgz",
- "integrity": "sha512-CmBKiL6NNo/OqgmMn95Fk9Whlp2mtvIv+KNpQKN2F4SjvrEesubTRWGYSg+BnWZOnlCaSTU1sMpsBOzgbYhnsA==",
- "license": "MIT"
- },
"node_modules/mdast-util-mdxjs-esm": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/mdast-util-mdxjs-esm/-/mdast-util-mdxjs-esm-2.0.1.tgz",
@@ -5209,15 +5194,6 @@
"url": "https://opencollective.com/unified"
}
},
- "node_modules/mdast-util-mdxjs-esm/node_modules/@types/hast": {
- "version": "3.0.4",
- "resolved": "https://registry.npmjs.org/@types/hast/-/hast-3.0.4.tgz",
- "integrity": "sha512-WPs+bbQw5aCj+x6laNGWLH3wviHtoCv/P3+otBhbOhJgG8qtpdAMlTCxLtsTWA7LH1Oh/bFCHsBn0TPS5m30EQ==",
- "license": "MIT",
- "dependencies": {
- "@types/unist": "*"
- }
- },
"node_modules/mdast-util-phrasing": {
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/mdast-util-phrasing/-/mdast-util-phrasing-4.1.0.tgz",
@@ -5253,15 +5229,6 @@
"url": "https://opencollective.com/unified"
}
},
- "node_modules/mdast-util-to-hast/node_modules/@types/hast": {
- "version": "3.0.4",
- "resolved": "https://registry.npmjs.org/@types/hast/-/hast-3.0.4.tgz",
- "integrity": "sha512-WPs+bbQw5aCj+x6laNGWLH3wviHtoCv/P3+otBhbOhJgG8qtpdAMlTCxLtsTWA7LH1Oh/bFCHsBn0TPS5m30EQ==",
- "license": "MIT",
- "dependencies": {
- "@types/unist": "*"
- }
- },
"node_modules/mdast-util-to-markdown": {
"version": "2.1.2",
"resolved": "https://registry.npmjs.org/mdast-util-to-markdown/-/mdast-util-to-markdown-2.1.2.tgz",
@@ -5283,12 +5250,6 @@
"url": "https://opencollective.com/unified"
}
},
- "node_modules/mdast-util-to-markdown/node_modules/@types/unist": {
- "version": "3.0.3",
- "resolved": "https://registry.npmjs.org/@types/unist/-/unist-3.0.3.tgz",
- "integrity": "sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==",
- "license": "MIT"
- },
"node_modules/mdast-util-to-string": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/mdast-util-to-string/-/mdast-util-to-string-4.0.0.tgz",
@@ -5906,27 +5867,6 @@
"node": "*"
}
},
- "node_modules/minipass": {
- "version": "7.1.2",
- "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz",
- "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==",
- "license": "ISC",
- "engines": {
- "node": ">=16 || 14 >=14.17"
- }
- },
- "node_modules/minizlib": {
- "version": "3.1.0",
- "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-3.1.0.tgz",
- "integrity": "sha512-KZxYo1BUkWD2TVFLr0MQoM8vUUigWD3LlD83a/75BqC+4qE0Hb1Vo5v1FgcfaNXvfXzr+5EhQ6ing/CaBijTlw==",
- "license": "MIT",
- "dependencies": {
- "minipass": "^7.1.2"
- },
- "engines": {
- "node": ">= 18"
- }
- },
"node_modules/moment": {
"version": "2.30.1",
"resolved": "https://registry.npmjs.org/moment/-/moment-2.30.1.tgz",
@@ -5937,20 +5877,30 @@
}
},
"node_modules/monaco-editor": {
- "version": "0.55.1",
- "resolved": "https://registry.npmjs.org/monaco-editor/-/monaco-editor-0.55.1.tgz",
- "integrity": "sha512-jz4x+TJNFHwHtwuV9vA9rMujcZRb0CEilTEwG2rRSpe/A7Jdkuj8xPKttCgOh+v/lkHy7HsZ64oj+q3xoAFl9A==",
+ "version": "0.56.0",
+ "resolved": "https://registry.npmjs.org/monaco-editor/-/monaco-editor-0.56.0.tgz",
+ "integrity": "sha512-sXboRm3BeBeLm938eaiyLMe0OxzfXIlZvbv4ir/jVgQy1zDhWjgmny0WoN45fuDKhCCQsYMbBJrv/A6jd8aCUg==",
"license": "MIT",
"peer": true,
"dependencies": {
- "dompurify": "3.2.7",
+ "dompurify": "3.4.8",
"marked": "14.0.0"
}
},
+ "node_modules/monaco-editor/node_modules/dompurify": {
+ "version": "3.4.8",
+ "resolved": "https://registry.npmjs.org/dompurify/-/dompurify-3.4.8.tgz",
+ "integrity": "sha512-yb1cEmaOum7wFvOCSQxyfgVlv5D47Rc30iZWoMpbDIWTnJ6grDDQyu2KFJzB2k7u0pMuJcQ1zphH//fFnw2tjQ==",
+ "license": "(MPL-2.0 OR Apache-2.0)",
+ "peer": true,
+ "optionalDependencies": {
+ "@types/trusted-types": "^2.0.7"
+ }
+ },
"node_modules/motion-dom": {
- "version": "12.42.2",
- "resolved": "https://registry.npmjs.org/motion-dom/-/motion-dom-12.42.2.tgz",
- "integrity": "sha512-5gIMWLp/PycBtJRJWRgjxke5n8dlvkSn2DrYW+tr3XcqAZY1xZh6BJyooJXCM8wdfM7wfMjkBJNLge1CKPUIRA==",
+ "version": "12.43.0",
+ "resolved": "https://registry.npmjs.org/motion-dom/-/motion-dom-12.43.0.tgz",
+ "integrity": "sha512-azKON4d9S65PEoFUiQTMTgPheEmzf2QngdRc50AKfJp9Q9mmcBVw22c8eMq9k8kxOFHdL7+WZY7N/5F/lwiDag==",
"license": "MIT",
"dependencies": {
"motion-utils": "^12.39.0"
@@ -5969,9 +5919,9 @@
"license": "MIT"
},
"node_modules/nanoid": {
- "version": "3.3.12",
- "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.12.tgz",
- "integrity": "sha512-ZB9RH/39qpq5Vu6Y+NmUaFhQR6pp+M2Xt76XBnEwDaGcVAqhlvxrl3B2bKS5D3NH3QR76v3aSrKaF/Kiy7lEtQ==",
+ "version": "3.3.18",
+ "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.18.tgz",
+ "integrity": "sha512-DTg4MJbGMWkfi6VZFdNt2/caMbQy4Ou+Op/hJQvGEWcnVfoA1QA+xzRKAzw9jD6+GVOOeYr/mIcuDSdug6F6+w==",
"funding": [
{
"type": "github",
@@ -5994,11 +5944,14 @@
"license": "MIT"
},
"node_modules/node-releases": {
- "version": "2.0.19",
- "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.19.tgz",
- "integrity": "sha512-xxOWJsBKtzAq7DY0J+DTzuz58K8e7sJbdgwkbMWQe8UYB6ekmsQ45q0M/tJDsGaZmbC+l7n57UV8Hl5tHxO9uw==",
+ "version": "2.0.53",
+ "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.53.tgz",
+ "integrity": "sha512-D9UOmYG3UH1V+ENW56t5QXBwJw1YEY18ruVeus89Rw+SyIgjPkCO84bRzO3uNIYosJbNwiabWVn48o3uJLjxFQ==",
"dev": true,
- "license": "MIT"
+ "license": "MIT",
+ "engines": {
+ "node": ">=18"
+ }
},
"node_modules/object-assign": {
"version": "4.1.1",
@@ -6103,23 +6056,30 @@
}
},
"node_modules/parse-entities": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/parse-entities/-/parse-entities-2.0.0.tgz",
- "integrity": "sha512-kkywGpCcRYhqQIchaWqZ875wzpS/bMKhz5HnN3p7wveJTkTtyAB/AlnS0f8DFSqYW1T82t6yEAkEcB+A1I3MbQ==",
+ "version": "4.0.2",
+ "resolved": "https://registry.npmjs.org/parse-entities/-/parse-entities-4.0.2.tgz",
+ "integrity": "sha512-GG2AQYWoLgL877gQIKeRPGO1xF9+eG1ujIb5soS5gPvLQ1y2o8FL90w2QWNdf9I361Mpp7726c+lj3U0qK1uGw==",
"license": "MIT",
"dependencies": {
- "character-entities": "^1.0.0",
- "character-entities-legacy": "^1.0.0",
- "character-reference-invalid": "^1.0.0",
- "is-alphanumerical": "^1.0.0",
- "is-decimal": "^1.0.0",
- "is-hexadecimal": "^1.0.0"
+ "@types/unist": "^2.0.0",
+ "character-entities-legacy": "^3.0.0",
+ "character-reference-invalid": "^2.0.0",
+ "decode-named-character-reference": "^1.0.0",
+ "is-alphanumerical": "^2.0.0",
+ "is-decimal": "^2.0.0",
+ "is-hexadecimal": "^2.0.0"
},
"funding": {
"type": "github",
"url": "https://github.com/sponsors/wooorm"
}
},
+ "node_modules/parse-entities/node_modules/@types/unist": {
+ "version": "2.0.11",
+ "resolved": "https://registry.npmjs.org/@types/unist/-/unist-2.0.11.tgz",
+ "integrity": "sha512-CmBKiL6NNo/OqgmMn95Fk9Whlp2mtvIv+KNpQKN2F4SjvrEesubTRWGYSg+BnWZOnlCaSTU1sMpsBOzgbYhnsA==",
+ "license": "MIT"
+ },
"node_modules/parse5": {
"version": "8.0.1",
"resolved": "https://registry.npmjs.org/parse5/-/parse5-8.0.1.tgz",
@@ -6174,9 +6134,9 @@
"license": "ISC"
},
"node_modules/picomatch": {
- "version": "4.0.4",
- "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.4.tgz",
- "integrity": "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==",
+ "version": "4.0.5",
+ "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.5.tgz",
+ "integrity": "sha512-RvwwcruNjI1ncT5xRakeyS9Lf8lcItv34KD+aif+VH9kduAyfYBipGh12274xtenIPZ119/R9BdTBa8gAwSh0A==",
"license": "MIT",
"engines": {
"node": ">=12"
@@ -6253,14 +6213,6 @@
"url": "https://github.com/chalk/ansi-styles?sponsor=1"
}
},
- "node_modules/pretty-format/node_modules/react-is": {
- "version": "17.0.2",
- "resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz",
- "integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==",
- "dev": true,
- "license": "MIT",
- "peer": true
- },
"node_modules/prismjs": {
"version": "1.30.0",
"resolved": "https://registry.npmjs.org/prismjs/-/prismjs-1.30.0.tgz",
@@ -6281,14 +6233,17 @@
"react-is": "^16.13.1"
}
},
+ "node_modules/prop-types/node_modules/react-is": {
+ "version": "16.13.1",
+ "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz",
+ "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==",
+ "license": "MIT"
+ },
"node_modules/property-information": {
- "version": "5.6.0",
- "resolved": "https://registry.npmjs.org/property-information/-/property-information-5.6.0.tgz",
- "integrity": "sha512-YUHSPk+A30YPv+0Qf8i9Mbfe/C0hdPXk1s1jPVToV8pk8BQtpw10ct89Eo7OWkutrwqvT0eicAxlOg3dOAu8JA==",
+ "version": "7.2.0",
+ "resolved": "https://registry.npmjs.org/property-information/-/property-information-7.2.0.tgz",
+ "integrity": "sha512-IAtzIB6sUiWaJYrX9smp3V46pBGbBeLFRGdh25kg1334VcBlD8HzhPeNIWQH9zhGmo2itIe25EHt9dQP7G5hmg==",
"license": "MIT",
- "dependencies": {
- "xtend": "^4.0.0"
- },
"funding": {
"type": "github",
"url": "https://github.com/sponsors/wooorm"
@@ -6366,19 +6321,21 @@
}
},
"node_modules/react-icons": {
- "version": "5.5.0",
- "resolved": "https://registry.npmjs.org/react-icons/-/react-icons-5.5.0.tgz",
- "integrity": "sha512-MEFcXdkP3dLo8uumGI5xN3lDFNsRtrjbOEKDLD7yv76v4wpnEq2Lt2qeHaQOr34I/wPN3s3+N08WkQ+CW37Xiw==",
+ "version": "5.7.0",
+ "resolved": "https://registry.npmjs.org/react-icons/-/react-icons-5.7.0.tgz",
+ "integrity": "sha512-LBLy340Rzqy6+/yVhZKT3B/QpP1BZaesGqasf09HPOBzRarcDIFH0WwXlXQfE7q7ipxK4MSiC5DIBWURCny6fw==",
"license": "MIT",
"peerDependencies": {
"react": "*"
}
},
"node_modules/react-is": {
- "version": "16.13.1",
- "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz",
- "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==",
- "license": "MIT"
+ "version": "17.0.2",
+ "resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz",
+ "integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==",
+ "dev": true,
+ "license": "MIT",
+ "peer": true
},
"node_modules/react-markdown": {
"version": "10.1.0",
@@ -6407,19 +6364,10 @@
"react": ">=18"
}
},
- "node_modules/react-markdown/node_modules/@types/hast": {
- "version": "3.0.4",
- "resolved": "https://registry.npmjs.org/@types/hast/-/hast-3.0.4.tgz",
- "integrity": "sha512-WPs+bbQw5aCj+x6laNGWLH3wviHtoCv/P3+otBhbOhJgG8qtpdAMlTCxLtsTWA7LH1Oh/bFCHsBn0TPS5m30EQ==",
- "license": "MIT",
- "dependencies": {
- "@types/unist": "*"
- }
- },
"node_modules/react-refresh": {
- "version": "0.17.0",
- "resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.17.0.tgz",
- "integrity": "sha512-z6F7K9bV85EfseRCp2bzrpyQ0Gkw1uLoCel9XBVWPg/TjRj94SkJzUTGfOa4bs7iJvBWtQG0Wq7wnI0syw3EBQ==",
+ "version": "0.18.0",
+ "resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.18.0.tgz",
+ "integrity": "sha512-QgT5//D3jfjJb6Gsjxv0Slpj23ip+HtOpnNgnb2S5zU3CB26G/IDPGoy4RJB42wzFE46DRsstbW6tKHoKbhAxw==",
"dev": true,
"license": "MIT",
"engines": {
@@ -6427,9 +6375,9 @@
}
},
"node_modules/react-router": {
- "version": "7.17.0",
- "resolved": "https://registry.npmjs.org/react-router/-/react-router-7.17.0.tgz",
- "integrity": "sha512-FDELK7rTMlCHO5+reyXsPlmfr7N1F91lPHsWYfMEGQm/KQ+F4JFM8jGoeQDmDvdTs93Fw9aSilH+uKRb4/jXvQ==",
+ "version": "7.18.2",
+ "resolved": "https://registry.npmjs.org/react-router/-/react-router-7.18.2.tgz",
+ "integrity": "sha512-aUVMjFm3GAPTTZL7oYr5E7ETiqfQCHRLH+B+5afnICvf0r7kkK4eR6SMuwbSTJw/7t+12khT/Kahij49fqOCIg==",
"license": "MIT",
"dependencies": {
"cookie": "^1.0.1",
@@ -6449,12 +6397,12 @@
}
},
"node_modules/react-router-dom": {
- "version": "7.17.0",
- "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-7.17.0.tgz",
- "integrity": "sha512-fyU2yjGups/hE6Xz0I5ZYbVL8Gx29eCjgpHaRaTaVU+OOAdfRX05KsvyRm0GO8YQwOkhpU3MurW1jyMUJn+zSw==",
+ "version": "7.18.2",
+ "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-7.18.2.tgz",
+ "integrity": "sha512-AIKJ/jgGlFb3EbfCXk5Gzshiwt+l3mqbCrNjmEWMMjqQxNJ3svBa6bgzFyCC2Sw3RA0VWF1kg3uQf2OFhxb8hw==",
"license": "MIT",
"dependencies": {
- "react-router": "7.17.0"
+ "react-router": "7.18.2"
},
"engines": {
"node": ">=20.0.0"
@@ -6526,9 +6474,9 @@
}
},
"node_modules/recharts": {
- "version": "2.15.3",
- "resolved": "https://registry.npmjs.org/recharts/-/recharts-2.15.3.tgz",
- "integrity": "sha512-EdOPzTwcFSuqtvkDoaM5ws/Km1+WTAO2eizL7rqiG0V2UVhTnz0m7J2i0CjVPUCdEkZImaWvXLbZDS2H5t6GFQ==",
+ "version": "2.15.4",
+ "resolved": "https://registry.npmjs.org/recharts/-/recharts-2.15.4.tgz",
+ "integrity": "sha512-UT/q6fwS3c1dHbXv2uFgYJ9BMFHu3fwnd7AYZaEQhXuYQ4hgsxLvsUXzGdKeZrW5xopzDCvuA2N41WJ88I7zIw==",
"deprecated": "1.x and 2.x branches are no longer active. Bump to Recharts v3 to receive latest features and bugfixes. See https://github.com/recharts/recharts/wiki/3.0-migration-guide",
"license": "MIT",
"dependencies": {
@@ -6579,6 +6527,98 @@
"url": "https://github.com/sponsors/wooorm"
}
},
+ "node_modules/refractor/node_modules/character-entities": {
+ "version": "1.2.4",
+ "resolved": "https://registry.npmjs.org/character-entities/-/character-entities-1.2.4.tgz",
+ "integrity": "sha512-iBMyeEHxfVnIakwOuDXpVkc54HijNgCyQB2w0VfGQThle6NXn50zU6V/u+LDhxHcDUPojn6Kpga3PTAD8W1bQw==",
+ "license": "MIT",
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/wooorm"
+ }
+ },
+ "node_modules/refractor/node_modules/character-entities-legacy": {
+ "version": "1.1.4",
+ "resolved": "https://registry.npmjs.org/character-entities-legacy/-/character-entities-legacy-1.1.4.tgz",
+ "integrity": "sha512-3Xnr+7ZFS1uxeiUDvV02wQ+QDbc55o97tIV5zHScSPJpcLm/r0DFPcoY3tYRp+VZukxuMeKgXYmsXQHO05zQeA==",
+ "license": "MIT",
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/wooorm"
+ }
+ },
+ "node_modules/refractor/node_modules/character-reference-invalid": {
+ "version": "1.1.4",
+ "resolved": "https://registry.npmjs.org/character-reference-invalid/-/character-reference-invalid-1.1.4.tgz",
+ "integrity": "sha512-mKKUkUbhPpQlCOfIuZkvSEgktjPFIsZKRRbC6KWVEMvlzblj3i3asQv5ODsrwt0N3pHAEvjP8KTQPHkp0+6jOg==",
+ "license": "MIT",
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/wooorm"
+ }
+ },
+ "node_modules/refractor/node_modules/is-alphabetical": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/is-alphabetical/-/is-alphabetical-1.0.4.tgz",
+ "integrity": "sha512-DwzsA04LQ10FHTZuL0/grVDk4rFoVH1pjAToYwBrHSxcrBIGQuXrQMtD5U1b0U2XVgKZCTLLP8u2Qxqhy3l2Vg==",
+ "license": "MIT",
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/wooorm"
+ }
+ },
+ "node_modules/refractor/node_modules/is-alphanumerical": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/is-alphanumerical/-/is-alphanumerical-1.0.4.tgz",
+ "integrity": "sha512-UzoZUr+XfVz3t3v4KyGEniVL9BDRoQtY7tOyrRybkVNjDFWyo1yhXNGrrBTQxp3ib9BLAWs7k2YKBQsFRkZG9A==",
+ "license": "MIT",
+ "dependencies": {
+ "is-alphabetical": "^1.0.0",
+ "is-decimal": "^1.0.0"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/wooorm"
+ }
+ },
+ "node_modules/refractor/node_modules/is-decimal": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/is-decimal/-/is-decimal-1.0.4.tgz",
+ "integrity": "sha512-RGdriMmQQvZ2aqaQq3awNA6dCGtKpiDFcOzrTWrDAT2MiWrKQVPmxLGHl7Y2nNu6led0kEyoX0enY0qXYsv9zw==",
+ "license": "MIT",
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/wooorm"
+ }
+ },
+ "node_modules/refractor/node_modules/is-hexadecimal": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/is-hexadecimal/-/is-hexadecimal-1.0.4.tgz",
+ "integrity": "sha512-gyPJuv83bHMpocVYoqof5VDiZveEoGoFL8m3BXNb2VW8Xs+rz9kqO8LOQ5DH6EsuvilT1ApazU0pyl+ytbPtlw==",
+ "license": "MIT",
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/wooorm"
+ }
+ },
+ "node_modules/refractor/node_modules/parse-entities": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/parse-entities/-/parse-entities-2.0.0.tgz",
+ "integrity": "sha512-kkywGpCcRYhqQIchaWqZ875wzpS/bMKhz5HnN3p7wveJTkTtyAB/AlnS0f8DFSqYW1T82t6yEAkEcB+A1I3MbQ==",
+ "license": "MIT",
+ "dependencies": {
+ "character-entities": "^1.0.0",
+ "character-entities-legacy": "^1.0.0",
+ "character-reference-invalid": "^1.0.0",
+ "is-alphanumerical": "^1.0.0",
+ "is-decimal": "^1.0.0",
+ "is-hexadecimal": "^1.0.0"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/wooorm"
+ }
+ },
"node_modules/refractor/node_modules/prismjs": {
"version": "1.27.0",
"resolved": "https://registry.npmjs.org/prismjs/-/prismjs-1.27.0.tgz",
@@ -6646,15 +6686,6 @@
"url": "https://opencollective.com/unified"
}
},
- "node_modules/remark-rehype/node_modules/@types/hast": {
- "version": "3.0.4",
- "resolved": "https://registry.npmjs.org/@types/hast/-/hast-3.0.4.tgz",
- "integrity": "sha512-WPs+bbQw5aCj+x6laNGWLH3wviHtoCv/P3+otBhbOhJgG8qtpdAMlTCxLtsTWA7LH1Oh/bFCHsBn0TPS5m30EQ==",
- "license": "MIT",
- "dependencies": {
- "@types/unist": "*"
- }
- },
"node_modules/remark-stringify": {
"version": "11.0.0",
"resolved": "https://registry.npmjs.org/remark-stringify/-/remark-stringify-11.0.0.tgz",
@@ -6701,9 +6732,9 @@
}
},
"node_modules/rollup": {
- "version": "4.61.1",
- "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.61.1.tgz",
- "integrity": "sha512-I4KW6iuRpuu2uHBLraZ1wNZe0DP7lnRha+VJ9tNaYVaVgKhW0aI3h4RYnoRPeql0flHm/Co55b7snEDcOfOJrA==",
+ "version": "4.62.4",
+ "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.62.4.tgz",
+ "integrity": "sha512-RXOqwaPsBGjMNMa4sQjDjHieHEZDFoj/Rdr46l2MU5DfEs16wHJPC2RPTPHWhNl+M3aI472LLqFkFKut4SblOg==",
"license": "MIT",
"dependencies": {
"@types/estree": "1.0.9"
@@ -6716,31 +6747,32 @@
"npm": ">=8.0.0"
},
"optionalDependencies": {
- "@rollup/rollup-android-arm-eabi": "4.61.1",
- "@rollup/rollup-android-arm64": "4.61.1",
- "@rollup/rollup-darwin-arm64": "4.61.1",
- "@rollup/rollup-darwin-x64": "4.61.1",
- "@rollup/rollup-freebsd-arm64": "4.61.1",
- "@rollup/rollup-freebsd-x64": "4.61.1",
- "@rollup/rollup-linux-arm-gnueabihf": "4.61.1",
- "@rollup/rollup-linux-arm-musleabihf": "4.61.1",
- "@rollup/rollup-linux-arm64-gnu": "4.61.1",
- "@rollup/rollup-linux-arm64-musl": "4.61.1",
- "@rollup/rollup-linux-loong64-gnu": "4.61.1",
- "@rollup/rollup-linux-loong64-musl": "4.61.1",
- "@rollup/rollup-linux-ppc64-gnu": "4.61.1",
- "@rollup/rollup-linux-ppc64-musl": "4.61.1",
- "@rollup/rollup-linux-riscv64-gnu": "4.61.1",
- "@rollup/rollup-linux-riscv64-musl": "4.61.1",
- "@rollup/rollup-linux-s390x-gnu": "4.61.1",
- "@rollup/rollup-linux-x64-gnu": "4.61.1",
- "@rollup/rollup-linux-x64-musl": "4.61.1",
- "@rollup/rollup-openbsd-x64": "4.61.1",
- "@rollup/rollup-openharmony-arm64": "4.61.1",
- "@rollup/rollup-win32-arm64-msvc": "4.61.1",
- "@rollup/rollup-win32-ia32-msvc": "4.61.1",
- "@rollup/rollup-win32-x64-gnu": "4.61.1",
- "@rollup/rollup-win32-x64-msvc": "4.61.1",
+ "@napi-rs/lzma-linux-x64-gnu": "1.5.1",
+ "@rollup/rollup-android-arm-eabi": "4.62.4",
+ "@rollup/rollup-android-arm64": "4.62.4",
+ "@rollup/rollup-darwin-arm64": "4.62.4",
+ "@rollup/rollup-darwin-x64": "4.62.4",
+ "@rollup/rollup-freebsd-arm64": "4.62.4",
+ "@rollup/rollup-freebsd-x64": "4.62.4",
+ "@rollup/rollup-linux-arm-gnueabihf": "4.62.4",
+ "@rollup/rollup-linux-arm-musleabihf": "4.62.4",
+ "@rollup/rollup-linux-arm64-gnu": "4.62.4",
+ "@rollup/rollup-linux-arm64-musl": "4.62.4",
+ "@rollup/rollup-linux-loong64-gnu": "4.62.4",
+ "@rollup/rollup-linux-loong64-musl": "4.62.4",
+ "@rollup/rollup-linux-ppc64-gnu": "4.62.4",
+ "@rollup/rollup-linux-ppc64-musl": "4.62.4",
+ "@rollup/rollup-linux-riscv64-gnu": "4.62.4",
+ "@rollup/rollup-linux-riscv64-musl": "4.62.4",
+ "@rollup/rollup-linux-s390x-gnu": "4.62.4",
+ "@rollup/rollup-linux-x64-gnu": "4.62.4",
+ "@rollup/rollup-linux-x64-musl": "4.62.4",
+ "@rollup/rollup-openbsd-x64": "4.62.4",
+ "@rollup/rollup-openharmony-arm64": "4.62.4",
+ "@rollup/rollup-win32-arm64-msvc": "4.62.4",
+ "@rollup/rollup-win32-ia32-msvc": "4.62.4",
+ "@rollup/rollup-win32-x64-gnu": "4.62.4",
+ "@rollup/rollup-win32-x64-msvc": "4.62.4",
"fsevents": "~2.3.2"
}
},
@@ -6822,9 +6854,9 @@
}
},
"node_modules/space-separated-tokens": {
- "version": "1.1.5",
- "resolved": "https://registry.npmjs.org/space-separated-tokens/-/space-separated-tokens-1.1.5.tgz",
- "integrity": "sha512-q/JSVd1Lptzhf5bkYm4ob4iWPjx0KiRe3sRFBNrVqbJkFaBm5vbbowy1mymoPNLRa52+oadOhJ+K49wsSeSjTA==",
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/space-separated-tokens/-/space-separated-tokens-2.0.2.tgz",
+ "integrity": "sha512-PEGlAwrG8yXGXRjW32fGbg66JAlOAwbObuqVoJpv/mRgoWDQfgH1wDPvtzWyUSNAXBGSk8h755YDbbcEy3SH2Q==",
"license": "MIT",
"funding": {
"type": "github",
@@ -6881,16 +6913,6 @@
"url": "https://github.com/sponsors/wooorm"
}
},
- "node_modules/stringify-entities/node_modules/character-entities-legacy": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/character-entities-legacy/-/character-entities-legacy-3.0.0.tgz",
- "integrity": "sha512-RpPp0asT/6ufRm//AJVwpViZbGM/MkjQFxJccQRHmISF/22NBtsHqAWmL+/pmkPWoIUJdWyeVleTl1wydHATVQ==",
- "license": "MIT",
- "funding": {
- "type": "github",
- "url": "https://github.com/sponsors/wooorm"
- }
- },
"node_modules/strip-json-comments": {
"version": "3.1.1",
"resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz",
@@ -6905,21 +6927,21 @@
}
},
"node_modules/style-to-js": {
- "version": "1.1.17",
- "resolved": "https://registry.npmjs.org/style-to-js/-/style-to-js-1.1.17.tgz",
- "integrity": "sha512-xQcBGDxJb6jjFCTzvQtfiPn6YvvP2O8U1MDIPNfJQlWMYfktPy+iGsHE7cssjs7y84d9fQaK4UF3RIJaAHSoYA==",
+ "version": "1.1.21",
+ "resolved": "https://registry.npmjs.org/style-to-js/-/style-to-js-1.1.21.tgz",
+ "integrity": "sha512-RjQetxJrrUJLQPHbLku6U/ocGtzyjbJMP9lCNK7Ag0CNh690nSH8woqWH9u16nMjYBAok+i7JO1NP2pOy8IsPQ==",
"license": "MIT",
"dependencies": {
- "style-to-object": "1.0.9"
+ "style-to-object": "1.0.14"
}
},
"node_modules/style-to-object": {
- "version": "1.0.9",
- "resolved": "https://registry.npmjs.org/style-to-object/-/style-to-object-1.0.9.tgz",
- "integrity": "sha512-G4qppLgKu/k6FwRpHiGiKPaPTFcG3g4wNVX/Qsfu+RqQM30E7Tyu/TEgxcL9PNLF5pdRLwQdE3YKKf+KF2Dzlw==",
+ "version": "1.0.14",
+ "resolved": "https://registry.npmjs.org/style-to-object/-/style-to-object-1.0.14.tgz",
+ "integrity": "sha512-LIN7rULI0jBscWQYaSswptyderlarFkjQ+t79nzty8tcIAceVomEVlLzH5VP4Cmsv6MtKhs7qaAiwlcp+Mgaxw==",
"license": "MIT",
"dependencies": {
- "inline-style-parser": "0.2.4"
+ "inline-style-parser": "0.2.7"
}
},
"node_modules/supports-color": {
@@ -6953,15 +6975,15 @@
"license": "MIT"
},
"node_modules/tailwindcss": {
- "version": "4.1.12",
- "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-4.1.12.tgz",
- "integrity": "sha512-DzFtxOi+7NsFf7DBtI3BJsynR+0Yp6etH+nRPTbpWnS2pZBaSksv/JGctNwSWzbFjp0vxSqknaUylseZqMDGrA==",
+ "version": "4.3.3",
+ "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-4.3.3.tgz",
+ "integrity": "sha512-gOhV3P7ufE62QDGg1zVaTgCR+EtPv92k2nIhVcVKcLmxT1sUBsQGhnZj175j+MqRt4zLF7ic+sCYjfhxMxj7YQ==",
"license": "MIT"
},
"node_modules/tapable": {
- "version": "2.2.3",
- "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.2.3.tgz",
- "integrity": "sha512-ZL6DDuAlRlLGghwcfmSn9sK3Hr6ArtyudlSAiCqQ6IfE+b+HHbydbYDIG15IfS5do+7XQQBdBiubF/cV2dnDzg==",
+ "version": "2.3.3",
+ "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.3.3.tgz",
+ "integrity": "sha512-uxc/zpqFg6x7C8vOE7lh6Lbda8eEL9zmVm/PLeTPBRhh1xCgdWaQ+J1CUieGpIfm2HdtsUpRv+HshiasBMcc6A==",
"license": "MIT",
"engines": {
"node": ">=6"
@@ -6971,31 +6993,6 @@
"url": "https://opencollective.com/webpack"
}
},
- "node_modules/tar": {
- "version": "7.5.19",
- "resolved": "https://registry.npmjs.org/tar/-/tar-7.5.19.tgz",
- "integrity": "sha512-4LeEWl96twnS2Q7Bz4MGqgazLqO+hJN63GZxXoIqh1T3VweYD997gbU1ItNsQafqqXTXd5WFyFdReLtwvRBNiw==",
- "license": "BlueOak-1.0.0",
- "dependencies": {
- "@isaacs/fs-minipass": "^4.0.0",
- "chownr": "^3.0.0",
- "minipass": "^7.1.2",
- "minizlib": "^3.1.0",
- "yallist": "^5.0.0"
- },
- "engines": {
- "node": ">=18"
- }
- },
- "node_modules/tar/node_modules/yallist": {
- "version": "5.0.0",
- "resolved": "https://registry.npmjs.org/yallist/-/yallist-5.0.0.tgz",
- "integrity": "sha512-YgvUTfwqyc7UXVMrB+SImsVYSmTS8X/tSrtdNZMImM+n7+QTriRXyXim0mBrTXNeqzVF0KWGgHPeiyViFFrNDw==",
- "license": "BlueOak-1.0.0",
- "engines": {
- "node": ">=18"
- }
- },
"node_modules/text-segmentation": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/text-segmentation/-/text-segmentation-1.0.3.tgz",
@@ -7168,16 +7165,10 @@
"url": "https://opencollective.com/unified"
}
},
- "node_modules/unified/node_modules/@types/unist": {
- "version": "3.0.3",
- "resolved": "https://registry.npmjs.org/@types/unist/-/unist-3.0.3.tgz",
- "integrity": "sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==",
- "license": "MIT"
- },
"node_modules/unist-util-is": {
- "version": "6.0.0",
- "resolved": "https://registry.npmjs.org/unist-util-is/-/unist-util-is-6.0.0.tgz",
- "integrity": "sha512-2qCTHimwdxLfz+YzdGfkqNlH0tLi9xjTnHddPmJwtIG9MGsdbutfTc4P+haPD7l7Cjxf/WZj+we5qfVPvvxfYw==",
+ "version": "6.0.1",
+ "resolved": "https://registry.npmjs.org/unist-util-is/-/unist-util-is-6.0.1.tgz",
+ "integrity": "sha512-LsiILbtBETkDz8I9p1dQ0uyRUWuaQzd/cuEeS1hoRSyW5E5XGmTzlwY1OrNzzakGowI9Dr/I8HVaw4hTtnxy8g==",
"license": "MIT",
"dependencies": {
"@types/unist": "^3.0.0"
@@ -7187,12 +7178,6 @@
"url": "https://opencollective.com/unified"
}
},
- "node_modules/unist-util-is/node_modules/@types/unist": {
- "version": "3.0.3",
- "resolved": "https://registry.npmjs.org/@types/unist/-/unist-3.0.3.tgz",
- "integrity": "sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==",
- "license": "MIT"
- },
"node_modules/unist-util-position": {
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/unist-util-position/-/unist-util-position-5.0.0.tgz",
@@ -7206,12 +7191,6 @@
"url": "https://opencollective.com/unified"
}
},
- "node_modules/unist-util-position/node_modules/@types/unist": {
- "version": "3.0.3",
- "resolved": "https://registry.npmjs.org/@types/unist/-/unist-3.0.3.tgz",
- "integrity": "sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==",
- "license": "MIT"
- },
"node_modules/unist-util-stringify-position": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/unist-util-stringify-position/-/unist-util-stringify-position-4.0.0.tgz",
@@ -7225,16 +7204,10 @@
"url": "https://opencollective.com/unified"
}
},
- "node_modules/unist-util-stringify-position/node_modules/@types/unist": {
- "version": "3.0.3",
- "resolved": "https://registry.npmjs.org/@types/unist/-/unist-3.0.3.tgz",
- "integrity": "sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==",
- "license": "MIT"
- },
"node_modules/unist-util-visit": {
- "version": "5.0.0",
- "resolved": "https://registry.npmjs.org/unist-util-visit/-/unist-util-visit-5.0.0.tgz",
- "integrity": "sha512-MR04uvD+07cwl/yhVuVWAtw+3GOR/knlL55Nd/wAdblk27GCVt3lqpTivy/tkJcZoNPzTwS1Y+KMojlLDhoTzg==",
+ "version": "5.1.0",
+ "resolved": "https://registry.npmjs.org/unist-util-visit/-/unist-util-visit-5.1.0.tgz",
+ "integrity": "sha512-m+vIdyeCOpdr/QeQCu2EzxX/ohgS8KbnPDgFni4dQsfSCtpz8UqDyY5GjRru8PDKuYn7Fq19j1CQ+nJSsGKOzg==",
"license": "MIT",
"dependencies": {
"@types/unist": "^3.0.0",
@@ -7247,9 +7220,9 @@
}
},
"node_modules/unist-util-visit-parents": {
- "version": "6.0.1",
- "resolved": "https://registry.npmjs.org/unist-util-visit-parents/-/unist-util-visit-parents-6.0.1.tgz",
- "integrity": "sha512-L/PqWzfTP9lzzEa6CKs0k2nARxTdZduw3zyh8d2NVBnsyvHjSX4TWse388YrrQKbvI8w20fGjGlhgT96WwKykw==",
+ "version": "6.0.2",
+ "resolved": "https://registry.npmjs.org/unist-util-visit-parents/-/unist-util-visit-parents-6.0.2.tgz",
+ "integrity": "sha512-goh1s1TBrqSqukSc8wrjwWhL0hiJxgA8m4kFxGlQ+8FYQ3C/m11FcTs4YYem7V664AhHVvgoQLk890Ssdsr2IQ==",
"license": "MIT",
"dependencies": {
"@types/unist": "^3.0.0",
@@ -7260,22 +7233,10 @@
"url": "https://opencollective.com/unified"
}
},
- "node_modules/unist-util-visit-parents/node_modules/@types/unist": {
- "version": "3.0.3",
- "resolved": "https://registry.npmjs.org/@types/unist/-/unist-3.0.3.tgz",
- "integrity": "sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==",
- "license": "MIT"
- },
- "node_modules/unist-util-visit/node_modules/@types/unist": {
- "version": "3.0.3",
- "resolved": "https://registry.npmjs.org/@types/unist/-/unist-3.0.3.tgz",
- "integrity": "sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==",
- "license": "MIT"
- },
"node_modules/update-browserslist-db": {
- "version": "1.1.3",
- "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.3.tgz",
- "integrity": "sha512-UxhIZQ+QInVdunkDAaiazvvT/+fXL5Osr0JZlJulepYu6Jd7qJtDZjlur0emRlT71EN3ScPoE7gvsuIKKNavKw==",
+ "version": "1.3.1",
+ "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.3.1.tgz",
+ "integrity": "sha512-ZZ61DsRsOnakl74HAmp3oSN4aXUmEWXf+i/yv0h7tIBfICc3VdrFErQKUUKPgu3AMsTUMbcongALEN4l6GSUrQ==",
"dev": true,
"funding": [
{
@@ -7350,18 +7311,6 @@
"url": "https://opencollective.com/unified"
}
},
- "node_modules/vfile-message/node_modules/@types/unist": {
- "version": "3.0.3",
- "resolved": "https://registry.npmjs.org/@types/unist/-/unist-3.0.3.tgz",
- "integrity": "sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==",
- "license": "MIT"
- },
- "node_modules/vfile/node_modules/@types/unist": {
- "version": "3.0.3",
- "resolved": "https://registry.npmjs.org/@types/unist/-/unist-3.0.3.tgz",
- "integrity": "sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==",
- "license": "MIT"
- },
"node_modules/victory-vendor": {
"version": "36.9.2",
"resolved": "https://registry.npmjs.org/victory-vendor/-/victory-vendor-36.9.2.tgz",
@@ -7385,12 +7334,12 @@
}
},
"node_modules/vite": {
- "version": "7.3.5",
- "resolved": "https://registry.npmjs.org/vite/-/vite-7.3.5.tgz",
- "integrity": "sha512-KuOaNhcnGFN2zIPGA7wRmzF+lJA1sea7rHq17aiJ++9lzY1WWG6Jpwqwe1KNbRVPIqHmr8GLYx7jbrQcN/7/ww==",
+ "version": "7.3.6",
+ "resolved": "https://registry.npmjs.org/vite/-/vite-7.3.6.tgz",
+ "integrity": "sha512-4XP60spRGjSZFf1qYH+dJIkK2znL3zQfl9KkOV9MkkRR/3Dls0dxaBsQPTloEc5BLXWPL9vsOxopxyKoMmDueg==",
"license": "MIT",
"dependencies": {
- "esbuild": "^0.27.0",
+ "esbuild": "^0.27.0 || ^0.28.0",
"fdir": "^6.5.0",
"picomatch": "^4.0.3",
"postcss": "^8.5.6",
diff --git a/frontend/package.json b/frontend/package.json
index dee3dfad..8a1af94d 100644
--- a/frontend/package.json
+++ b/frontend/package.json
@@ -25,7 +25,7 @@
"react-hot-toast": "^2.6.0",
"react-icons": "^5.5.0",
"react-markdown": "^10.1.0",
- "react-router-dom": "^7.8.2",
+ "react-router-dom": "^7.18.0",
"react-split": "^2.0.14",
"react-syntax-highlighter": "^15.6.6",
"recharts": "^2.15.3",
@@ -44,11 +44,16 @@
"eslint-plugin-react-refresh": "^0.4.20",
"globals": "^16.3.0",
"jsdom": "^29.1.1",
- "vite": "^7.1.2",
+ "vite": "^7.3.6",
"vitest": "^4.1.10"
},
"overrides": {
"tar": "7.5.19",
"postcss": "8.5.18"
- }
+ },
+ "description": "This template provides a minimal setup to get React working in Vite with HMR and some ESLint rules.",
+ "main": "eslint.config.js",
+ "keywords": [],
+ "author": "",
+ "license": "ISC"
}
diff --git a/frontend/src/LandingPage.jsx b/frontend/src/LandingPage.jsx
index d4f86170..ad0bdb82 100644
--- a/frontend/src/LandingPage.jsx
+++ b/frontend/src/LandingPage.jsx
@@ -17,7 +17,7 @@ import ForgotPassword from "./pages/Auth/ForgotPAssword";
import { UserContext } from "./context/userContext";
import { motion, AnimatePresence } from "framer-motion";
import ServicesMarquee from "./components/ServicesMarquee";
-import { Star, ChevronLeft, ChevronRight } from "lucide-react"; // Import icons for testimonials
+import { Star, ChevronLeft, ChevronRight, Menu, X } from "lucide-react"; // Import icons for testimonials
import TermsandConditions from "./pages/Terms/TermsandConditions"; // ā Add this
@@ -244,6 +244,8 @@ const LandingPage = () => {
const [showScrollTop, setShowScrollTop] = useState(false);
const [isPaused, setIsPaused] = useState(false);
+ const [mobileMenuOpen,setMobileMenuOpen] = useState(false);
+ const publicRoutes = ["/ai-helper", "/coding-sheets", "/assessment"];
// Hero parallax
const heroRef = useRef(null);
@@ -270,13 +272,13 @@ const LandingPage = () => {
];
const handleNav = (route) => {
- if (!user) {
- setOpenAuthModal(true);
- setPendingRoute(route);
- } else {
- navigate(route);
- }
- };
+ if (!user && !publicRoutes.includes(route)) {
+ setOpenAuthModal(true);
+ setPendingRoute(route);
+ } else {
+ navigate(route);
+ }
+};
useEffect(() => {
const handleScroll = () => {
setShowScrollTop(window.scrollY > 300);
@@ -314,7 +316,7 @@ const LandingPage = () => {
>
{/* Floating pill ā true glassmorphism: ~40% opacity + strong blur */}
{
))}
+ {/* Mobile menu button */}
+
setMobileMenuOpen((prev) => !prev)}
+ className="lg:hidden absolute left-1/2 -translate-x-1/2 p-2 text-gray-300 hover:text-white transition-colors"
+ aria-label={mobileMenuOpen ? "Close menu" : "Open menu"}
+ aria-expanded={mobileMenuOpen}>
+
+ {mobileMenuOpen ? : }
+
+
{/* Right buttons */}
@@ -395,6 +407,37 @@ const LandingPage = () => {
)}
+ {/* Mobile navigation */}
+ {mobileMenuOpen && (
+
+
+ {navRoutes.map((item) => (
+ {
+ handleNav(item.route);
+ setMobileMenuOpen(false);
+ }}
+ className="w-full text-left px-4 py-3 rounded-xl text-sm text-gray-300 hover:bg-white/10 hover:text-white transition-colors"
+ >
+ {item.label}
+
+ ))}
+
+ {/* Login */}
+ {
+ setOpenAuthModal(true);
+ setMobileMenuOpen(false);
+ }}
+ className="w-full text-left px-4 py-3 rounded-xl text-sm text-gray-300 hover:bg-white/10 hover:text-white transition-colors"
+ >
+ Login
+
+
+
+
+ )}
{/* āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
diff --git a/frontend/src/components/BookmarkPanel.jsx b/frontend/src/components/BookmarkPanel.jsx
new file mode 100644
index 00000000..3ed6998a
--- /dev/null
+++ b/frontend/src/components/BookmarkPanel.jsx
@@ -0,0 +1,297 @@
+import { useState, useEffect, useCallback } from "react";
+import axiosInstance from "../utils/axiosInstance";
+import { motion, AnimatePresence } from "framer-motion";
+import {
+ Bookmark, Plus, Search, Star, Tag, Trash2, Filter,
+ ChevronDown, ChevronUp, Edit3, Save, X, BarChart3,
+} from "lucide-react";
+import toast from "react-hot-toast";
+
+const CATEGORIES = [
+ "DSA", "System Design", "Behavioral", "Aptitude", "Coding",
+ "OS", "DBMS", "Networking", "Other",
+];
+const DIFFICULTIES = ["Easy", "Medium", "Hard"];
+const DIFF_COLOR = { Easy: "text-green-500", Medium: "text-yellow-500", Hard: "text-red-500" };
+const CAT_ICON = { DSA: "š»", "System Design": "šļø", Behavioral: "š£ļø", Aptitude: "š§®", Coding: "āØļø", OS: "š„ļø", DBMS: "šļø", Networking: "š", Other: "š" };
+
+// āāā Create Form āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
+
+function CreateForm({ onClose, onCreated }) {
+ const [form, setForm] = useState({ question: "", answer: "", category: "DSA", difficulty: "Medium", notes: "", tags: "" });
+ const [loading, setLoading] = useState(false);
+ const handleSubmit = async (e) => {
+ e.preventDefault();
+ if (!form.question.trim()) return toast.error("Question is required");
+ setLoading(true);
+ try {
+ const tags = form.tags.split(",").map((t) => t.trim().toLowerCase()).filter(Boolean).slice(0, 5);
+ await axiosInstance.post("/api/bookmarks", { ...form, tags });
+ toast.success("Bookmark saved!");
+ onCreated();
+ onClose();
+ } catch (err) {
+ toast.error(err.response?.data?.message || "Failed to save");
+ } finally { setLoading(false); }
+ };
+ const inputCls = "w-full px-3 py-2 rounded-lg border border-gray-300 dark:border-gray-600 bg-white dark:bg-gray-700 text-gray-900 dark:text-white focus:ring-2 focus:ring-blue-500 outline-none text-sm";
+
+ return (
+
+ Add Bookmark
+
+
+ );
+}
+
+// āāā Stats Bar āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
+
+function StatsBar({ stats }) {
+ if (!stats) return null;
+ return (
+
+ {[
+ { icon:
, label: "Total", val: stats.total },
+ { icon:
, label: "Starred", val: stats.starredCount },
+ { icon:
, label: "Categories", val: Object.keys(stats.categoryBreakdown).length },
+ { icon:
, label: "Hard Q's", val: stats.difficultyBreakdown.Hard || 0 },
+ ].map((c) => (
+
+ ))}
+
+ );
+}
+
+// āāā Bookmark Card āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
+
+function BookmarkCard({ bm, onDeleted, onRefresh }) {
+ const [expanded, setExpanded] = useState(false);
+ const [editing, setEditing] = useState(false);
+ const [noteText, setNoteText] = useState(bm.notes);
+
+ const handleStar = async () => {
+ try {
+ await axiosInstance.patch(`/api/bookmarks/${bm._id}/star`);
+ onRefresh();
+ } catch { toast.error("Failed"); }
+ };
+ const handleDelete = async () => {
+ if (!window.confirm("Delete this bookmark?")) return;
+ try {
+ await axiosInstance.delete(`/api/bookmarks/${bm._id}`);
+ toast.success("Deleted");
+ onDeleted();
+ } catch { toast.error("Failed"); }
+ };
+ const handleSaveNote = async () => {
+ try {
+ await axiosInstance.put(`/api/bookmarks/${bm._id}`, { notes: noteText });
+ setEditing(false);
+ toast.success("Note saved");
+ onRefresh();
+ } catch { toast.error("Failed"); }
+ };
+
+ return (
+
+
+
+
+ {CAT_ICON[bm.category] || "š"}
+ {bm.difficulty}
+ {bm.category}
+
+
{bm.question}
+
+
+
+
+
+
+
+
+
+
+
+ {bm.tags?.length > 0 && (
+
+ {bm.tags.map((t) => {t} )}
+
+ )}
+
+ {(bm.answer || bm.notes) && (
+ setExpanded(!expanded)} className="flex items-center gap-1 text-xs text-blue-600 dark:text-blue-400 hover:underline mt-2">
+ {expanded ? : }
+ {expanded ? "Hide" : "Show"} details
+
+ )}
+
+
+ {expanded && (
+
+ {bm.answer && (
+
+ Answer:
+ {bm.answer}
+
+ )}
+
+
+ My Notes
+ {!editing && setEditing(true)} className="text-blue-500 hover:underline"> }
+
+ {editing ? (
+
+ setNoteText(e.target.value)} className="flex-1 text-xs px-2 py-1 rounded border border-gray-300 dark:border-gray-600 bg-white dark:bg-gray-700 text-gray-900 dark:text-white outline-none" />
+
+ { setEditing(false); setNoteText(bm.notes); }} className="p-1 text-gray-400">
+
+ ) : (
+
{bm.notes || "No notes yet"}
+ )}
+
+
+ )}
+
+
+ );
+}
+
+// āāā Main Panel āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
+
+export default function BookmarkPanel() {
+ const [bookmarks, setBookmarks] = useState([]);
+ const [stats, setStats] = useState(null);
+ const [loading, setLoading] = useState(true);
+ const [showCreate, setShowCreate] = useState(false);
+ const [search, setSearch] = useState("");
+ const [filters, setFilters] = useState({ category: "", difficulty: "", starred: false });
+ const [showFilters, setShowFilters] = useState(false);
+ const [page, setPage] = useState(1);
+ const [pagination, setPagination] = useState(null);
+
+ const fetchBookmarks = useCallback(async () => {
+ try {
+ setLoading(true);
+ const params = { page, limit: 15 };
+ if (search) params.search = search;
+ if (filters.category) params.category = filters.category;
+ if (filters.difficulty) params.difficulty = filters.difficulty;
+ if (filters.starred) params.starred = true;
+
+ const [bmRes, statsRes] = await Promise.all([
+ axiosInstance.get("/api/bookmarks", { params }),
+ axiosInstance.get("/api/bookmarks/stats"),
+ ]);
+ setBookmarks(bmRes.data.bookmarks || []);
+ setPagination(bmRes.data.pagination);
+ setStats(statsRes.data.stats || null);
+ } catch {
+ toast.error("Failed to load bookmarks");
+ } finally { setLoading(false); }
+ }, [page, search, filters]);
+
+ useEffect(() => { fetchBookmarks(); }, [fetchBookmarks]);
+ useEffect(() => { setPage(1); }, [search, filters]);
+
+ return (
+
+ {/* Header */}
+
+
+
+
+
Bookmarks
+
Your personal question collection
+
+
+
setShowCreate(!showCreate)} className="flex items-center gap-1.5 px-3 py-1.5 bg-blue-600 hover:bg-blue-700 text-white rounded-lg text-sm font-medium">
+ Add
+
+
+
+
{showCreate && setShowCreate(false)} onCreated={fetchBookmarks} />}
+
+
+ {/* Search & Filter */}
+
+
+
+ setSearch(e.target.value)} placeholder="Search questions, notes..." className="w-full pl-8 pr-3 py-2 rounded-lg border border-gray-300 dark:border-gray-600 bg-white dark:bg-gray-700 text-gray-900 dark:text-white text-sm outline-none focus:ring-2 focus:ring-blue-500" />
+
+
setShowFilters(!showFilters)} className={`px-3 py-2 rounded-lg border text-sm flex items-center gap-1 ${showFilters ? "bg-blue-50 dark:bg-blue-900/30 border-blue-300 dark:border-blue-700" : "bg-white dark:bg-gray-800 border-gray-300 dark:border-gray-600"}`}>
+ Filter
+
+
setFilters({ ...filters, starred: !filters.starred })} className={`px-3 py-2 rounded-lg border text-sm ${filters.starred ? "bg-yellow-50 dark:bg-yellow-900/20 border-yellow-300" : "bg-white dark:bg-gray-800 border-gray-300 dark:border-gray-600"}`}>
+
+
+
+
+
+ {showFilters && (
+
+
+ setFilters({ ...filters, category: e.target.value })} className="px-2 py-1 text-xs rounded border border-gray-300 dark:border-gray-600 bg-white dark:bg-gray-700 text-gray-900 dark:text-white">
+ All categories
+ {CATEGORIES.map((c) => {c} )}
+
+ setFilters({ ...filters, difficulty: e.target.value })} className="px-2 py-1 text-xs rounded border border-gray-300 dark:border-gray-600 bg-white dark:bg-gray-700 text-gray-900 dark:text-white">
+ All difficulties
+ {DIFFICULTIES.map((d) => {d} )}
+
+ { setFilters({ category: "", difficulty: "", starred: false }); setSearch(""); }} className="px-2 py-1 text-xs text-red-500 hover:underline">Clear all
+
+
+ )}
+
+
+ {/* Bookmarks list */}
+ {loading && bookmarks.length === 0 ? (
+
+ ) : bookmarks.length === 0 ? (
+
+
+
{search ? "No bookmarks match your search" : "No bookmarks yet"}
+ {!search &&
setShowCreate(true)} className="text-sm px-3 py-1.5 bg-blue-600 text-white rounded-lg hover:bg-blue-700">Add First Bookmark }
+
+ ) : (
+ <>
+
+ {bookmarks.map((bm) => )}
+
+ {pagination && pagination.pages > 1 && (
+
+ setPage(page - 1)} className="px-3 py-1 text-sm rounded-lg border border-gray-300 dark:border-gray-600 disabled:opacity-40">Prev
+ Page {page} of {pagination.pages}
+ = pagination.pages} onClick={() => setPage(page + 1)} className="px-3 py-1 text-sm rounded-lg border border-gray-300 dark:border-gray-600 disabled:opacity-40">Next
+
+ )}
+ >
+ )}
+
+ );
+}
diff --git a/frontend/src/components/SheetDetailsPage.jsx b/frontend/src/components/SheetDetailsPage.jsx
index dfb324a9..aabf5013 100644
--- a/frontend/src/components/SheetDetailsPage.jsx
+++ b/frontend/src/components/SheetDetailsPage.jsx
@@ -9,6 +9,13 @@ import { BASE_URL } from "../utils/apiPaths";
import axiosInstance from "../utils/axiosinstance";
import { UserContext } from "../context/userContext";
import { CheckCircle2, Circle, AlertCircle, BookOpen, Users, CheckSquare, RotateCcw } from "lucide-react";
+import toast from "react-hot-toast";
+
+const notifyStreakMilestones = (newlyUnlockedAchievements) => {
+ (newlyUnlockedAchievements || [])
+ .filter((badge) => badge.endsWith("Streak"))
+ .forEach((badge) => toast.success(`š Milestone unlocked: ${badge}!`));
+};
const SubtopicRow = memo(({ sub, sectionIdx, topicIdx, subIdx, completed, followed, onToggle }) => {
let diffColor = "bg-gray-100 text-gray-700 dark:bg-gray-800 dark:text-gray-300";
@@ -198,7 +205,10 @@ function SheetDetail() {
{ sheetId: id, followed: followedState, completedTopics: topicsToSave, percentage },
{ signal: controller.signal }
)
- .then(() => refreshSheetProgress?.())
+ .then((res) => {
+ notifyStreakMilestones(res.data?.newlyUnlockedAchievements);
+ refreshSheetProgress?.();
+ })
.catch((err) => {
if (err.name !== "CanceledError" && err.code !== "ERR_CANCELED") {
console.error("Failed to sync progress to backend:", err);
@@ -226,7 +236,10 @@ function SheetDetail() {
followed: false,
completedTopics: {},
percentage: 0
- }).then(() => refreshSheetProgress?.()).catch(err => console.error(err));
+ }).then((res) => {
+ notifyStreakMilestones(res.data?.newlyUnlockedAchievements);
+ refreshSheetProgress?.();
+ }).catch(err => console.error(err));
} else {
setFollowed(true);
@@ -242,7 +255,10 @@ function SheetDetail() {
followed: true,
completedTopics,
percentage: 0,
- }).then(() => refreshSheetProgress?.()).catch(err => console.error("Failed to sync follow to backend:", err));
+ }).then((res) => {
+ notifyStreakMilestones(res.data?.newlyUnlockedAchievements);
+ refreshSheetProgress?.();
+ }).catch(err => console.error("Failed to sync follow to backend:", err));
}
};
diff --git a/frontend/src/components/StreakBadge.jsx b/frontend/src/components/StreakBadge.jsx
new file mode 100644
index 00000000..de7104f5
--- /dev/null
+++ b/frontend/src/components/StreakBadge.jsx
@@ -0,0 +1,45 @@
+import { Flame, Trophy } from "lucide-react";
+
+/**
+ * @param {number} streak - current streak length in days.
+ * @param {string[]} [milestones] - optional list of unlocked streak
+ * milestone badge names (e.g. "7-Day Streak", "30-Day Streak") from
+ * `user.unlockedAchievements`. When provided, renders a compact row of
+ * trophy chips beneath the flame badge. Purely additive ā omit the prop
+ * to get the original flame-only badge.
+ */
+const StreakBadge = ({ streak, milestones = [] }) => {
+ if (!streak || streak <= 0) return null;
+
+ // Sort milestones by the day-count baked into their name so chips read
+ // left-to-right in unlock order (3 ā 7 ā 14 ā 30).
+ const sortedMilestones = [...milestones].sort(
+ (a, b) => (parseInt(a, 10) || 0) - (parseInt(b, 10) || 0)
+ );
+
+ return (
+
+
+
+ {streak} day{streak === 1 ? "" : "s"} streak
+
+
+ {sortedMilestones.length > 0 && (
+
+ {sortedMilestones.map((badge) => (
+
+
+ {badge}
+
+ ))}
+
+ )}
+
+ );
+};
+
+export default StreakBadge;
\ No newline at end of file
diff --git a/frontend/src/components/StreakMilestoneToast.jsx b/frontend/src/components/StreakMilestoneToast.jsx
new file mode 100644
index 00000000..9716713e
--- /dev/null
+++ b/frontend/src/components/StreakMilestoneToast.jsx
@@ -0,0 +1,35 @@
+import React, { useEffect, useState } from 'react';
+
+const MILESTONE_LABELS = {
+ 3: '3-Day Streak š„',
+ 7: 'Week Warrior š
',
+ 14: 'Two-Week Titan ā”',
+ 30: 'Month Master š',
+ 60: '60-Day Legend š',
+ 100: 'Century Club š'
+};
+
+/**
+ * Shows a dismissible toast when newlyUnlockedMilestones is passed in
+ * from an API response (e.g. after completing a session or flashcard review).
+ */
+export default function StreakMilestoneToast({ newlyUnlockedMilestones = [] }) {
+ const [visibleMilestone, setVisibleMilestone] = useState(null);
+
+ useEffect(() => {
+ if (newlyUnlockedMilestones.length > 0) {
+ setVisibleMilestone(newlyUnlockedMilestones[0]);
+ const timer = setTimeout(() => setVisibleMilestone(null), 4000);
+ return () => clearTimeout(timer);
+ }
+ }, [newlyUnlockedMilestones]);
+
+ if (!visibleMilestone) return null;
+
+ return (
+
+
Achievement Unlocked!
+
{MILESTONE_LABELS[visibleMilestone] || `${visibleMilestone}-Day Streak`}
+
+ );
+}
\ No newline at end of file
diff --git a/frontend/src/pages/AIInterviewAnswerImprovementVersioning.jsx b/frontend/src/pages/AIInterviewAnswerImprovementVersioning.jsx
new file mode 100644
index 00000000..80c6e890
--- /dev/null
+++ b/frontend/src/pages/AIInterviewAnswerImprovementVersioning.jsx
@@ -0,0 +1,184 @@
+import React, { useState } from "react";
+import {
+ Brain,
+ GitCompare,
+ CheckCircle2,
+ TrendingUp,
+} from "lucide-react";
+
+const versions = [
+ {
+ name: "Original",
+ score: 62,
+ text: "I worked on improving the API performance by optimizing queries.",
+ },
+ {
+ name: "Revision 1",
+ score: 74,
+ text: "I optimized database queries to improve API response performance.",
+ },
+ {
+ name: "Revision 2",
+ score: 86,
+ text: "I optimized database queries and introduced caching, reducing API response time by 35%.",
+ },
+];
+
+export default function AIInterviewAnswerImprovementVersioning() {
+ const [selected, setSelected] = useState(0);
+
+ return (
+
+
+ {/* Header */}
+
+
+
+
+
+
+
+ AI Answer Improvement Versioning
+
+
+
+ Compare different versions of your interview answer.
+
+
+
+
+ {/* Version Timeline */}
+
+
+
+
+
+
+ Answer Versions
+
+
+
+
+
+ {versions.map((version, index) => (
+ setSelected(index)}
+ className={`px-4 py-3 rounded-xl font-semibold ${
+ selected === index
+ ? "bg-indigo-600 text-white"
+ : "bg-gray-100 text-gray-600"
+ }`}
+ >
+ {version.name}
+
+ ))}
+
+
+
+
+
+ {/* Selected Version */}
+
+
+
+
+
+ Selected Version
+
+
+
+ {versions[selected].name}
+
+
+
+
+
+ Score
+
+
+
+ {versions[selected].score}%
+
+
+
+
+
+
+ {versions[selected].text}
+
+
+
+
+
+ {/* Version Comparison */}
+
+
+
+ Version Comparison
+
+
+
+
+ {versions.map((version) => (
+
+
+
+
+ {version.name}
+
+
+
+ {version.score}%
+
+
+
+
+ {version.text}
+
+
+
+ ))}
+
+
+
+
+
+ {/* AI Feedback */}
+
+
+
+
+
+
+
+ AI Improvement Feedback
+
+
+
+ Your latest version is stronger because it includes a specific
+ technical action and a measurable outcome. The answer became
+ more concise while providing stronger evidence of impact.
+
+
+
+
+
+
+ Improvement: +24 points
+
+
+
+
+
+
+
+
+ );
+}
\ No newline at end of file
diff --git a/frontend/src/pages/AIInterviewAnswerOpeningToConclusionFlowAnalyzer.jsx b/frontend/src/pages/AIInterviewAnswerOpeningToConclusionFlowAnalyzer.jsx
new file mode 100644
index 00000000..4ef56614
--- /dev/null
+++ b/frontend/src/pages/AIInterviewAnswerOpeningToConclusionFlowAnalyzer.jsx
@@ -0,0 +1,211 @@
+import React, { useState } from "react";
+import {
+ Brain,
+ ArrowRight,
+ CheckCircle2,
+ AlertTriangle,
+ Target,
+} from "lucide-react";
+
+const sections = [
+ ["Opening", 88],
+ ["Problem Understanding", 92],
+ ["Main Explanation", 84],
+ ["Supporting Example", 76],
+ ["Technical Details", 81],
+ ["Conclusion", 70],
+];
+
+export default function AIInterviewAnswerOpeningToConclusionFlowAnalyzer() {
+ const [answer, setAnswer] = useState("");
+ const [analyzed, setAnalyzed] = useState(false);
+
+ return (
+
+
+ {/* Header */}
+
+
+
+
+
+
+
+
+ AI Answer Flow Analyzer
+
+
+
+ Analyze your answer from opening statement to conclusion.
+
+
+
+
+
+ {/* Answer Input */}
+
+
+
+ Interview Answer
+
+
+ setAnswer(e.target.value)}
+ rows={9}
+ placeholder="Paste your complete interview response..."
+ className="w-full border rounded-xl p-4 mt-4 outline-none focus:ring-2 focus:ring-indigo-500"
+ />
+
+ setAnalyzed(true)}
+ className="mt-4 px-5 py-3 rounded-xl bg-indigo-600 text-white font-semibold disabled:opacity-50"
+ >
+ Analyze Answer Flow
+
+
+
+
+ {analyzed && (
+ <>
+ {/* Overall Score */}
+
+
+
+
+
+ Overall Flow Score
+
+
+
+ 82%
+
+
+
+ Your answer has a clear structure with some transition gaps.
+
+
+
+
+ {/* Flow */}
+
+
+
+ Answer Flow
+
+
+
+
+ {sections.map(([section], index) => (
+
+
+
+ {section}
+
+
+ {index < sections.length - 1 && (
+
+ )}
+
+
+ ))}
+
+
+
+
+
+ {/* Section Scores */}
+
+
+
+ Section Analysis
+
+
+
+
+ {sections.map(([name, score]) => (
+
+
+
+
+ {name}
+
+
+
+ {score}%
+
+
+
+
+
+
+ ))}
+
+
+
+
+
+ {/* Transition Feedback */}
+
+
+
+
+
+
+
+
+ Weak Transition Detected
+
+
+
+ Your transition from the supporting example to the technical
+ details is abrupt. Briefly explain how the example connects
+ to the technical decision before moving forward.
+
+
+
+
+
+
+
+ {/* AI Recommendation */}
+
+
+
+
+
+
+
+
+ AI Flow Recommendation
+
+
+
+ Start with the main idea, explain your reasoning, support it
+ with one relevant example, then summarize the technical
+ decision and outcome in the conclusion.
+
+
+
+
+
+
+
+ >
+ )}
+
+
+ );
+}
\ No newline at end of file
diff --git a/frontend/src/pages/AIInterviewAnswerQuestionRestatementCoach.jsx b/frontend/src/pages/AIInterviewAnswerQuestionRestatementCoach.jsx
new file mode 100644
index 00000000..b286b592
--- /dev/null
+++ b/frontend/src/pages/AIInterviewAnswerQuestionRestatementCoach.jsx
@@ -0,0 +1,189 @@
+import React, { useState } from "react";
+import {
+ Brain,
+ MessageSquare,
+ CheckCircle2,
+ AlertTriangle,
+} from "lucide-react";
+
+export default function AIInterviewAnswerQuestionRestatementCoach() {
+ const [restatement, setRestatement] = useState("");
+ const [analyzed, setAnalyzed] = useState(false);
+
+ return (
+
+
+ {/* Header */}
+
+
+
+
+
+
+
+
+ AI Question Restatement Coach
+
+
+
+ Restate the interview question before building your answer.
+
+
+
+
+
+ {/* Original Question */}
+
+
+
+ Interview Question
+
+
+
+ How would you design a scalable URL shortening service?
+
+
+
+
+ {/* Restatement */}
+
+
+
+
+
+
+ Restate the Question
+
+
+
+
+ Explain the question briefly in your own words before solving it.
+
+
+
setRestatement(e.target.value)}
+ rows={5}
+ placeholder="Example: I need to design a service that converts long URLs into short URLs and can handle high traffic..."
+ className="w-full border rounded-xl p-4 mt-4 bg-white outline-none focus:ring-2 focus:ring-indigo-500"
+ />
+
+ setAnalyzed(true)}
+ className="mt-4 px-5 py-3 rounded-xl bg-indigo-600 text-white font-semibold disabled:opacity-50"
+ >
+ Analyze Restatement
+
+
+
+
+ {analyzed && (
+ <>
+ {/* Score */}
+
+
+
+
+
+ Restatement Accuracy
+
+
+
+ 86%
+
+
+
+ Your interpretation closely matches the original question.
+
+
+
+
+ {/* Requirement Analysis */}
+
+
+
+ Requirement Coverage
+
+
+
+
+
+
+ URL shortening
+
+
+
+ Covered
+
+
+
+
+
+ Scalability
+
+
+
+ Covered
+
+
+
+
+
+ High traffic handling
+
+
+
+ Partially Covered
+
+
+
+
+
+
+
+ {/* Feedback */}
+
+
+
+
+
+
+
+ AI Feedback
+
+
+
+ Your restatement correctly identifies the main goal, but
+ explicitly mention the expected scale or traffic requirements
+ before starting your design.
+
+
+
+
+
+
+ {/* Better Restatement */}
+
+
+
+ Suggested Restatement
+
+
+
+ "I need to design a URL shortening service that converts long
+ URLs into short links while supporting scalable, high-volume
+ traffic."
+
+
+
+
+ >
+ )}
+
+
+ );
+}
\ No newline at end of file
diff --git a/frontend/src/pages/AIInterviewPreparationAdaptiveReviewMode.jsx b/frontend/src/pages/AIInterviewPreparationAdaptiveReviewMode.jsx
new file mode 100644
index 00000000..a31dfcf8
--- /dev/null
+++ b/frontend/src/pages/AIInterviewPreparationAdaptiveReviewMode.jsx
@@ -0,0 +1,189 @@
+import React, { useState } from "react";
+import {
+ Brain,
+ RefreshCw,
+ Target,
+ CheckCircle2,
+} from "lucide-react";
+
+const questions = [
+ {
+ question: "What is the time complexity of Binary Search?",
+ answer: "O(log n)",
+ },
+ {
+ question: "What condition is required for Binary Search?",
+ answer: "The data must be sorted.",
+ },
+ {
+ question: "What is the space complexity of iterative Binary Search?",
+ answer: "O(1)",
+ },
+];
+
+export default function AIInterviewPreparationAdaptiveReviewMode() {
+ const [current, setCurrent] = useState(0);
+ const [answer, setAnswer] = useState("");
+ const [score, setScore] = useState(0);
+ const [completed, setCompleted] = useState(false);
+
+ const submitAnswer = () => {
+ const correct = answer
+ .toLowerCase()
+ .includes(questions[current].answer.toLowerCase());
+
+ if (correct) setScore((prev) => prev + 1);
+
+ if (current < questions.length - 1) {
+ setCurrent((prev) => prev + 1);
+ setAnswer("");
+ } else {
+ setCompleted(true);
+ }
+ };
+
+ return (
+
+
+ {/* Header */}
+
+
+
+
+
+
+
+ AI Adaptive Review Mode
+
+
+
+ Revision content adapts to your performance in real time.
+
+
+
+
+ {!completed ? (
+ <>
+ {/* Progress */}
+
+
+
+
+ Binary Search Revision
+
+
+
+ {current + 1} / {questions.length}
+
+
+
+
+
+
+
+ {/* Question */}
+
+
+
+
+
+
+ Adaptive Question
+
+
+
+
+ {questions[current].question}
+
+
+
setAnswer(e.target.value)}
+ rows={4}
+ placeholder="Answer from memory..."
+ className="w-full bg-white border rounded-xl p-4 mt-4 outline-none focus:ring-2 focus:ring-indigo-500"
+ />
+
+
+ Submit Answer
+
+
+
+
+ {/* AI Strategy */}
+
+
+
+
+
+
+
+ AI Review Strategy
+
+
+
+ Difficult concepts will be shown more frequently, while
+ concepts you consistently answer correctly will appear less
+ often.
+
+
+
+
+
+ >
+ ) : (
+ <>
+ {/* Completion */}
+
+
+
+
+
+ Review Session Complete
+
+
+
+ {Math.round((score / questions.length) * 100)}%
+
+
+
+ Your review content was adapted according to your responses.
+
+
+
+
+ {/* Recommendation */}
+
+
+
+ AI Recommendation
+
+
+
+ Continue reviewing concepts you missed and reduce repetition
+ for concepts you have already mastered.
+
+
+
+ >
+ )}
+
+
+ );
+}
\ No newline at end of file
diff --git a/frontend/src/pages/AIInterviewPreparationPracticeConsistencyForecast.jsx b/frontend/src/pages/AIInterviewPreparationPracticeConsistencyForecast.jsx
new file mode 100644
index 00000000..ecec7347
--- /dev/null
+++ b/frontend/src/pages/AIInterviewPreparationPracticeConsistencyForecast.jsx
@@ -0,0 +1,173 @@
+import React from "react";
+import {
+ Brain,
+ CalendarDays,
+ TrendingUp,
+ AlertTriangle,
+ CheckCircle2,
+} from "lucide-react";
+
+export default function AIInterviewPreparationPracticeConsistencyForecast() {
+ return (
+
+
+ {/* Header */}
+
+
+
+
+
+
+
+
+ AI Practice Consistency Forecast
+
+
+
+ Understand whether your current practice routine is sustainable.
+
+
+
+
+
+ {/* Forecast */}
+
+
+
+
+
+ Consistency Forecast
+
+
+
+ 76%
+
+
+
+ Your current practice pattern is moderately sustainable.
+
+
+
+
+ {/* Activity Metrics */}
+
+
+
+ Practice Pattern
+
+
+
+
+
+
+
+
+
+ Sessions / Week
+
+
+
+ 5
+
+
+
+
+
+
+
+
+
+ Recent Activity
+
+
+
+ Stable
+
+
+
+
+
+
+
+
+
+ Revision Gap
+
+
+
+ 3 Days
+
+
+
+
+
+
+
+
+ {/* Pattern Analysis */}
+
+
+
+
+
+
+
+
+
+ Positive Pattern
+
+
+
+ You are revising important topics regularly instead of
+ relying only on last-minute practice.
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Risk Detected
+
+
+
+ Your longest sessions occur after inactive periods. This
+ burst-and-gap pattern may be difficult to maintain.
+
+
+
+
+
+
+
+
+ {/* Recommendation */}
+
+
+
+ AI Recommended Pattern
+
+
+
+ Aim for 45ā60 minutes of practice on 5 days each week, with short
+ revision sessions between larger practice sessions.
+
+
+
+ Apply Recommended Pattern
+
+
+
+
+
+ );
+}
\ No newline at end of file
diff --git a/frontend/src/pages/AIInterviewPreparationPracticeGapDetector.jsx b/frontend/src/pages/AIInterviewPreparationPracticeGapDetector.jsx
new file mode 100644
index 00000000..627ed785
--- /dev/null
+++ b/frontend/src/pages/AIInterviewPreparationPracticeGapDetector.jsx
@@ -0,0 +1,214 @@
+import React from "react";
+import {
+ Brain,
+ Clock,
+ AlertTriangle,
+ RotateCcw,
+ TrendingDown,
+} from "lucide-react";
+
+export default function AIInterviewPreparationPracticeGapDetector() {
+ return (
+
+
+ {/* Header */}
+
+
+
+
+
+
+
+
+ AI Practice Gap Detector
+
+
+
+ Detect practice gaps and understand their impact on preparation.
+
+
+
+
+
+ {/* Gap Summary */}
+
+
+
+
+
+ Longest Recent Practice Gap
+
+
+
+ 6 Days
+
+
+
+ Your last significant practice gap occurred recently.
+
+
+
+
+ {/* Performance Comparison */}
+
+
+
+ Performance Before vs After Gap
+
+
+
+
+
+
+
+ Before Gap
+
+
+
+ 84%
+
+
+
+ Average accuracy
+
+
+
+
+
+
+
+ After Gap
+
+
+
+ 72%
+
+
+
+ Average accuracy
+
+
+
+
+
+
+
+
+ {/* Affected Topics */}
+
+
+
+ Affected Topics
+
+
+
+
+
+
+ Dynamic Programming
+
+
+
+ -18%
+
+
+
+
+
+ Graph Algorithms
+
+
+
+ -12%
+
+
+
+
+
+ SQL
+
+
+
+ Stable
+
+
+
+
+
+
+
+ {/* AI Analysis */}
+
+
+
+
+
+
+
+
+ AI Gap Analysis
+
+
+
+ Your performance dropped after the six-day inactivity period,
+ particularly in topics that require frequent recall. A short
+ recovery session can help restore previous performance.
+
+
+
+
+
+
+
+ {/* Recovery */}
+
+
+
+
+
+
+
+
+ Recommended Recovery Session
+
+
+
+ Complete 2 revision questions, 2 Easy practice problems, and
+ one Medium problem from Dynamic Programming.
+
+
+
+ Start Recovery Session
+
+
+
+
+
+
+
+ {/* Consistency Tip */}
+
+
+
+
+
+
+
+ Consistency Insight
+
+
+
+ Short, regular practice sessions may help maintain retention
+ better than long sessions separated by extended gaps.
+
+
+
+
+
+
+
+ );
+}
\ No newline at end of file
diff --git a/frontend/src/pages/AIInterviewPreparationTopicMasteryDecayMonitor.jsx b/frontend/src/pages/AIInterviewPreparationTopicMasteryDecayMonitor.jsx
new file mode 100644
index 00000000..841fa18e
--- /dev/null
+++ b/frontend/src/pages/AIInterviewPreparationTopicMasteryDecayMonitor.jsx
@@ -0,0 +1,189 @@
+import React from "react";
+import {
+ Brain,
+ TrendingDown,
+ RefreshCw,
+ CheckCircle2,
+ AlertTriangle,
+} from "lucide-react";
+
+const topics = [
+ {
+ name: "Binary Search",
+ previous: 94,
+ current: 87,
+ status: "Declining",
+ },
+ {
+ name: "Arrays",
+ previous: 91,
+ current: 93,
+ status: "Stable",
+ },
+ {
+ name: "Hashing",
+ previous: 89,
+ current: 76,
+ status: "Needs Review",
+ },
+];
+
+export default function AIInterviewPreparationTopicMasteryDecayMonitor() {
+ return (
+
+
+ {/* Header */}
+
+
+
+
+
+
+
+
+ AI Topic Mastery Decay Monitor
+
+
+
+ Monitor previously mastered topics and prevent knowledge decay.
+
+
+
+
+
+ {/* Overview */}
+
+
+
+
+
+ Topics Requiring Reinforcement
+
+
+
+ 2
+
+
+
+ Some previously mastered concepts are showing performance decline.
+
+
+
+
+ {/* Topic List */}
+
+
+
+ Mastery History
+
+
+
+
+ {topics.map((topic) => (
+
+
+
+
+
+
+ {topic.name}
+
+
+
+ Previous: {topic.previous}% ā Current: {topic.current}%
+
+
+
+ {topic.status === "Stable" ? (
+
+
+ Stable
+
+ ) : (
+
+
+ {topic.status}
+
+ )}
+
+
+
+
+
+ {topic.current < topic.previous && (
+
+ Mastery declined by{" "}
+ {topic.previous - topic.current} percentage points.
+
+ )}
+
+
+ ))}
+
+
+
+
+
+ {/* Decay Alert */}
+
+
+
+
+
+
+
+
+ Knowledge Decay Detected
+
+
+
+ Hashing performance has declined significantly since the last
+ mastery assessment. A short targeted revision session is
+ recommended.
+
+
+
+
+
+
+
+ {/* Recommendation */}
+
+
+
+
+
+
+
+
+ AI Reinforcement Plan
+
+
+
+ Complete 5 quick recall questions on Hashing and 3 Binary Search
+ questions. Your mastery will be reassessed after the review.
+
+
+
+ Start Reinforcement
+
+
+
+
+
+
+
+
+ );
+}
\ No newline at end of file
diff --git a/frontend/src/pages/AIInterviewQuestionKeywordExtractor/AIInterviewQuestionKeywordExtractor.jsx b/frontend/src/pages/AIInterviewQuestionKeywordExtractor/AIInterviewQuestionKeywordExtractor.jsx
new file mode 100644
index 00000000..d9ecbf04
--- /dev/null
+++ b/frontend/src/pages/AIInterviewQuestionKeywordExtractor/AIInterviewQuestionKeywordExtractor.jsx
@@ -0,0 +1,173 @@
+import React, { useState } from "react";
+import {
+ Tag,
+ Search,
+ Brain,
+ Layers,
+ Zap,
+ BookOpen,
+ ChevronRight,
+ TrendingUp,
+ Target,
+} from "lucide-react";
+
+const AIInterviewQuestionKeywordExtractor = () => {
+ const [inputQuestion, setInputQuestion] = useState("How would you design a scalable microservices architecture using Kubernetes and handle service discovery in a distributed system?");
+
+ const stats = { totalKeywords: 8, technicalDepth: 82, breadthScore: 65 };
+
+ const keywords = [
+ { term: "scalable", category: "Concept", score: 95, color: "violet" },
+ { term: "microservices", category: "Technical", score: 90, color: "blue" },
+ { term: "Kubernetes", category: "Tool", score: 88, color: "blue" },
+ { term: "architecture", category: "Domain", score: 85, color: "green" },
+ { term: "service discovery", category: "Technical", score: 82, color: "blue" },
+ { term: "distributed system", category: "Technical", score: 80, color: "blue" },
+ { term: "scalability", category: "Concept", score: 78, color: "violet" },
+ { term: "containerization", category: "Concept", score: 65, color: "violet" },
+ ];
+
+ const questionIntent = { primary: "System Design", secondary: "Infrastructure", complexity: "Advanced", assessmentType: "Problem Solving & Architecture" };
+
+ const followUpQuestions = [
+ "How would you handle network partitions and eventual consistency?",
+ "What monitoring and observability tools would you use at this scale?",
+ "How would you approach database selection for each microservice?",
+ "Describe your strategy for API versioning and backward compatibility.",
+ ];
+
+ const getCategoryBadge = (category) => {
+ if (category === "Technical") return "bg-blue-100 text-blue-700 dark:bg-blue-900/20 dark:text-blue-400";
+ if (category === "Tool") return "bg-violet-100 text-violet-700 dark:bg-violet-900/20 dark:text-violet-400";
+ if (category === "Concept") return "bg-green-100 text-green-700 dark:bg-green-900/20 dark:text-green-400";
+ return "bg-amber-100 text-amber-700 dark:bg-amber-900/20 dark:text-amber-400";
+ };
+
+ const getScoreColor = (score) => {
+ if (score >= 80) return "text-green-600";
+ if (score >= 60) return "text-amber-500";
+ return "text-red-500";
+ };
+
+ const getBarColor = (color) => {
+ if (color === "violet") return "bg-gradient-to-r from-violet-500 to-purple-600";
+ if (color === "blue") return "bg-gradient-to-r from-blue-500 to-indigo-600";
+ if (color === "green") return "bg-gradient-to-r from-green-500 to-emerald-600";
+ return "bg-gradient-to-r from-amber-500 to-orange-500";
+ };
+
+ return (
+
+
+
+
+
+
+
+
AI Interview Question Keyword Extractor
+
Paste any interview question to extract key technical terms, understand the question intent, and identify what skills are being assessed.
+
+
+
+
+ {[
+ { icon:
, label: "Total Keywords", value: stats.totalKeywords },
+ { icon:
, label: "Technical Depth", value: `${stats.technicalDepth}%` },
+ { icon:
, label: "Breadth Score", value: `${stats.breadthScore}%` },
+ ].map((item, i) => (
+
+ {item.icon}
+
{item.label}
+
{item.value}
+
+ ))}
+
+
+
+
AI Keyword Extraction Analysis
+
The AI extracts and categorizes technical keywords, domain terms, and important concepts from interview questions. Understanding the keywords helps you prepare targeted, specific answers that demonstrate deep knowledge of each topic.
+
+
+
+
Enter Interview Question
+
setInputQuestion(e.target.value)}
+ className="w-full rounded-2xl border border-gray-200 dark:border-white/10 bg-gray-50 dark:bg-gray-800 p-5 text-lg leading-relaxed min-h-[120px] focus:outline-none focus:ring-2 focus:ring-indigo-500"
+ placeholder="Paste your interview question here..."
+ />
+
+
+
+
Extracted Keywords
+
+ {keywords.map((item, i) => (
+
+
+
+ {item.term}
+ {item.category}
+
+
{item.score}%
+
+
+
+ ))}
+
+
+
+
+
Question Intent Analysis
+
+ {[
+ { label: "Primary Assessment Area", value: questionIntent.primary },
+ { label: "Secondary Area", value: questionIntent.secondary },
+ { label: "Complexity Level", value: questionIntent.complexity },
+ { label: "Assessment Type", value: questionIntent.assessmentType },
+ ].map((item, i) => (
+
+
{item.label}
+
{item.value}
+
+ ))}
+
+
+
+
+
Related Follow-up Questions
+
+ {followUpQuestions.map((q, i) => (
+
+ ))}
+
+
+
+
+
Keyword Category Breakdown
+
+ {[
+ { category: "Technical", count: 4, color: "bg-blue-500" },
+ { category: "Tool", count: 1, color: "bg-violet-500" },
+ { category: "Concept", count: 2, color: "bg-green-500" },
+ { category: "Domain", count: 1, color: "bg-amber-500" },
+ ].map((item, i) => (
+
+
+
{item.category}
+
{item.count}
+
keywords
+
+ ))}
+
+
+
+
+ );
+};
+
+export default AIInterviewQuestionKeywordExtractor;
diff --git a/frontend/src/pages/AIInterviewQuestionSkillDifficultyMatrix.jsx b/frontend/src/pages/AIInterviewQuestionSkillDifficultyMatrix.jsx
new file mode 100644
index 00000000..692858ff
--- /dev/null
+++ b/frontend/src/pages/AIInterviewQuestionSkillDifficultyMatrix.jsx
@@ -0,0 +1,196 @@
+import React from "react";
+import {
+ Brain,
+ Target,
+ CheckCircle2,
+ AlertTriangle,
+} from "lucide-react";
+
+const matrix = [
+ {
+ skill: "Problem Solving",
+ levels: ["Strong", "Developing", "Weak"],
+ },
+ {
+ skill: "Algorithms",
+ levels: ["Strong", "Strong", "Developing"],
+ },
+ {
+ skill: "System Design",
+ levels: ["Developing", "Weak", "Weak"],
+ },
+];
+
+const getStyle = (level) => {
+ if (level === "Strong")
+ return "bg-green-100 text-green-700";
+
+ if (level === "Developing")
+ return "bg-yellow-100 text-yellow-700";
+
+ return "bg-red-100 text-red-700";
+};
+
+export default function AIInterviewQuestionSkillDifficultyMatrix() {
+ return (
+
+
+ {/* Header */}
+
+
+
+
+
+
+
+
+ AI Skill Difficulty Matrix
+
+
+
+ See how your skills perform across different difficulty levels.
+
+
+
+
+
+ {/* Matrix */}
+
+
+
+
+
+ Skill Performance Matrix
+
+
+
+
+
+
+
+
+
+ Skill
+
+
+
+ Easy
+
+
+
+ Medium
+
+
+
+ Hard
+
+
+
+
+
+
+
+ {matrix.map((row) => (
+
+
+
+ {row.skill}
+
+
+ {row.levels.map((level) => (
+
+
+ {level}
+
+
+ ))}
+
+
+ ))}
+
+
+
+
+
+
+
+ {/* Legend */}
+
+
+
+ Performance Legend
+
+
+
+
+
+ Strong
+
+
+
+ Developing
+
+
+
+ Weak
+
+
+
+
+
+
+ {/* AI Insight */}
+
+
+
+
+
+
+
+
+ AI Difficulty Insight
+
+
+
+ Your Problem Solving skill is strong at Easy difficulty but
+ becomes weak at Hard difficulty. Focus on advanced problems
+ before increasing your overall difficulty level.
+
+
+
+
+
+
+
+ {/* Recommendation */}
+
+
+
+
+
+
+
+
+ Recommended Practice
+
+
+
+ Complete 3 Hard Problem Solving questions and 2 Medium System
+ Design questions to strengthen your difficulty progression.
+
+
+
+
+
+
+
+
+ );
+}
\ No newline at end of file
diff --git a/frontend/src/pages/AIInterviewQuestionSolutionConstraintStressTest.jsx b/frontend/src/pages/AIInterviewQuestionSolutionConstraintStressTest.jsx
new file mode 100644
index 00000000..ead0ae88
--- /dev/null
+++ b/frontend/src/pages/AIInterviewQuestionSolutionConstraintStressTest.jsx
@@ -0,0 +1,406 @@
+import React, { useState } from "react";
+import {
+ Brain,
+ Gauge,
+ MemoryStick,
+ Server,
+ AlertTriangle,
+ CheckCircle2,
+ Zap,
+ Target,
+} from "lucide-react";
+
+const stressTests = [
+ {
+ name: "Input Size",
+ original: "n ⤠1,000",
+ stressed: "n ⤠1,000,000",
+ result: "Slow",
+ score: 54,
+ explanation:
+ "The current O(n²) approach becomes too expensive at one million elements.",
+ },
+ {
+ name: "Memory Constraint",
+ original: "512 MB",
+ stressed: "64 MB",
+ result: "Risk",
+ score: 61,
+ explanation:
+ "The auxiliary data structure may consume more memory than the new limit allows.",
+ },
+ {
+ name: "Request Volume",
+ original: "100 requests/min",
+ stressed: "10,000 requests/min",
+ result: "Needs Optimization",
+ score: 68,
+ explanation:
+ "The current design may struggle to maintain response times under higher traffic.",
+ },
+];
+
+export default function AIInterviewQuestionSolutionConstraintStressTest() {
+ const [solution, setSolution] = useState("");
+ const [analyzed, setAnalyzed] = useState(false);
+ const [selected, setSelected] = useState(null);
+ const [optimization, setOptimization] = useState("");
+ const [optimized, setOptimized] = useState(false);
+
+ return (
+
+
+ {/* Header */}
+
+
+
+
+
+
+
+
+ AI Solution Constraint Stress Test
+
+
+
+ Discover how your solution behaves when real-world constraints
+ become significantly more demanding.
+
+
+
+
+
+ {/* Problem */}
+
+
+
+ Interview Problem
+
+
+
+ Find the pair of numbers in an array that adds up to a target.
+
+
+
+
+
+
+ Input
+
+
+ n ⤠1,000
+
+
+
+
+
+ Memory
+
+
+ 512 MB
+
+
+
+
+
+ Expected
+
+
+ O(n)
+
+
+
+
+
+
+
+ {/* Solution Input */}
+
+
+
+ Your Proposed Solution
+
+
+ setSolution(e.target.value)}
+ rows={7}
+ placeholder="Explain your algorithm, complexity, and implementation..."
+ className="w-full border rounded-xl p-4 mt-4 outline-none focus:ring-2 focus:ring-indigo-500"
+ />
+
+ setAnalyzed(true)}
+ className="mt-4 px-5 py-3 rounded-xl bg-indigo-600 text-white font-semibold disabled:opacity-50"
+ >
+ Run Stress Test
+
+
+
+
+ {analyzed && (
+ <>
+ {/* Overall Result */}
+
+
+
+
+
+
+
+
+
+ Scalability Assessment
+
+
+
+ 63%
+
+
+
+ Optimization Recommended
+
+
+
+ Your solution works under the original constraints but
+ becomes inefficient when the input size and workload
+ increase.
+
+
+
+
+
+
+
+
+ {/* Stress Test Cards */}
+
+
+
+ Constraint Stress Tests
+
+
+
+
+ {stressTests.map((test) => (
+
+ setSelected(
+ selected?.name === test.name ? null : test
+ )
+ }
+ className="w-full text-left border rounded-2xl p-5 hover:border-indigo-400 transition"
+ >
+
+
+
+
+
+ {test.name === "Input Size" ? (
+
+ ) : test.name === "Memory Constraint" ? (
+
+ ) : (
+
+ )}
+
+
+
+
+ {test.name}
+
+
+
+ {test.original} ā{" "}
+ {test.stressed}
+
+
+
+
+
+
+
+ {test.score}%
+
+
+
+
+
+
+
+ {test.result}
+
+
+ {selected?.name === test.name && (
+
+
+
+ {test.explanation}
+
+
+
+ )}
+
+
+ ))}
+
+
+
+
+
+ {/* Complexity */}
+
+
+
+
+
+ Current Complexity
+
+
+
+ O(n²)
+
+
+
+ Performance degrades rapidly as input size increases.
+
+
+
+
+
+
+
+ Recommended Complexity
+
+
+
+ O(n)
+
+
+
+ A hash-based approach can provide linear-time processing.
+
+
+
+
+
+
+ {/* Optimization Challenge */}
+ {!optimized && (
+
+
+
+
+
+
+
+
+
+ Optimization Challenge
+
+
+
+ The input size has increased from 1,000 to 1,000,000.
+ How would you modify your solution to maintain acceptable
+ performance?
+
+
+
setOptimization(e.target.value)}
+ rows={5}
+ placeholder="Explain your optimized approach..."
+ className="w-full border rounded-xl p-4 mt-4 bg-white outline-none focus:ring-2 focus:ring-indigo-500"
+ />
+
+ setOptimized(true)}
+ className="mt-4 px-5 py-3 rounded-xl bg-indigo-600 text-white font-semibold disabled:opacity-50"
+ >
+ Submit Optimization
+
+
+
+
+
+
+
+ )}
+
+ {/* Success */}
+ {optimized && (
+
+
+
+
+
+
+
+
+
+ Optimization Response Recorded
+
+
+
+ Your revised approach has been submitted for evaluation.
+ AI will compare its complexity, memory usage, and
+ scalability against the stressed constraints.
+
+
+
+
+
+
+
+ )}
+
+ {/* AI Insight */}
+
+
+
+
+
+
+
+
+
+ AI Scalability Insight
+
+
+
+ A solution should not only satisfy the original constraints.
+ Strong interview candidates also consider what happens when
+ input size, memory limits, traffic, or workload increases.
+ Always connect complexity to the expected scale.
+
+
+
+
+
+
+
+
+ >
+ )}
+
+
+ );
+}
\ No newline at end of file
diff --git a/frontend/src/pages/Auth/SignUp.jsx b/frontend/src/pages/Auth/SignUp.jsx
index b4e054d3..3cf8bd5b 100644
--- a/frontend/src/pages/Auth/SignUp.jsx
+++ b/frontend/src/pages/Auth/SignUp.jsx
@@ -7,7 +7,6 @@ import { validateEmail } from "../../utils/helper";
import axiosInstance from "../../utils/axiosinstance";
import { API_PATHS } from "../../utils/apiPaths";
import { UserContext } from "../../context/userContext";
-import uploadImage from "../../utils/uploadimage";
import { LuArrowRight } from "react-icons/lu";
const SignUp = ({ setCurrentPage }) => {
@@ -18,10 +17,6 @@ const SignUp = ({ setCurrentPage }) => {
const [confirmPassword, setConfirmPassword] = useState("");
const [error, setError] = useState(null);
const [loading, setLoading] = useState(false);
- const [successMessage, setSuccessMessage] = useState("");
- const [resendLoading, setResendLoading] = useState(false);
- const [resendError, setResendError] = useState("");
- const [resendCooldown, setResendCooldown] = useState(0);
const { updateUser } = useContext(UserContext);
const navigate = useNavigate();
@@ -48,7 +43,6 @@ const SignUp = ({ setCurrentPage }) => {
}
const handleSignup = async (e) => {
e.preventDefault();
- let profileImageUrl = "";
if (!fullName) { setError("Please enter your full name"); return; }
if(containsNumber(fullName) || !containsAlphanumeric(fullName)) { setError("Full name should not contain numbers or special characters"); return; }
@@ -64,31 +58,30 @@ const SignUp = ({ setCurrentPage }) => {
setLoading(true);
try {
- if (profilePic) {
- const imgUploadRes = await uploadImage(profilePic);
- profileImageUrl = imgUploadRes.imageUrl || "";
- }
+ // Note: profile image upload requires auth, so we only send the
+ // profileImageUrl if the user picked a file ā the backend stores the
+ // URL reference; the actual upload happens after email verification + login.
+ // For now we skip the upload here to avoid a 401 on the protected endpoint.
const payload = {
name: fullName,
email,
password,
};
- if (profileImageUrl) payload.profileImageUrl = profileImageUrl;
const response = await axiosInstance.post(API_PATHS.AUTH.REGISTER, payload);
-
+
if (response.data.success) {
const authToken = response.data.token || response.data.accessToken;
- if (authToken) {
- sessionStorage.setItem("token", authToken);
- updateUser(response.data);
- navigate("/dashboard");
- }
+ sessionStorage.setItem("token", authToken);
+ updateUser(response.data);
+ navigate("/dashboard");
}
} catch (error) {
if (error.response && error.response.data.message) {
setError(error.response.data.message);
+ } else if (error.code === "ECONNABORTED" || error.message?.includes("timeout")) {
+ setError("Request timed out. Please check your connection and try again.");
} else {
setError("Something went wrong. Please try again");
}
@@ -97,25 +90,6 @@ const SignUp = ({ setCurrentPage }) => {
}
};
- const handleResend = async () => {
- setResendLoading(true);
- setResendError("");
- try {
- await axiosInstance.post(API_PATHS.AUTH.RESEND_VERIFICATION, { email });
- setResendCooldown(60);
- const interval = setInterval(() => {
- setResendCooldown((prev) => {
- if (prev <= 1) { clearInterval(interval); return 0; }
- return prev - 1;
- });
- }, 1000);
- } catch (error) {
- setResendError(error.response?.data?.message || "Failed to resend. Please try again.");
- } finally {
- setResendLoading(false);
- }
- };
-
return (
{/* Header */}
@@ -130,45 +104,8 @@ const SignUp = ({ setCurrentPage }) => {
Join thousands preparing smarter for their dream jobs
- {successMessage ? (
- /* Success state */
-
-
-
{successMessage}
-
Didn't receive it? Check your spam folder.
-
-
-
0}
- onClick={handleResend}
- className="w-full text-sm text-violet-400 hover:text-violet-300 transition-colors disabled:opacity-50 disabled:cursor-not-allowed cursor-pointer"
- >
- {resendLoading ? "Sending..." : resendCooldown > 0 ? `Resend email (${resendCooldown}s)` : "Resend verification email"}
-
-
- {resendError && (
-
- )}
-
-
-
- Already verified?{" "}
- { setCurrentPage("login"); setSuccessMessage(""); }}
- >
- Sign in
-
-
-
-
- ) : (
- /* Registration form */
-
+ {/* Registration form */}
+
@@ -303,7 +240,6 @@ const SignUp = ({ setCurrentPage }) => {
- )}
);
};
diff --git a/frontend/src/pages/Home/Dashboard.jsx b/frontend/src/pages/Home/Dashboard.jsx
index ada3170a..3dc1fa6f 100644
--- a/frontend/src/pages/Home/Dashboard.jsx
+++ b/frontend/src/pages/Home/Dashboard.jsx
@@ -1,4 +1,4 @@
-import React, { useEffect, useState } from "react";
+import React, { useEffect, useState, useContext } from "react";
import { LuPlus } from "react-icons/lu";
import toast from "react-hot-toast";
import { useNavigate } from "react-router-dom";
@@ -9,13 +9,16 @@ import SummaryCard from "../../components/Cards/SummaryCard";
import Modal from "../../components/Loader/Modal";
import CreateSessionForm from "./CreateSessionForm";
import DeleteAlertContent from "../../components/DeleteAlertContent";
+import StreakBadge from "../../components/StreakBadge";
import axiosInstance from "../../utils/axiosinstance";
import { API_PATHS } from "../../utils/apiPaths";
import { CARD_BG } from "../../utils/data";
+import { UserContext } from "../../context/userContext";
const Dashboard = () => {
const navigate = useNavigate();
+ const { user } = useContext(UserContext);
const [sessions, setSessions] = useState([]);
const [openCreateModal, setOpenCreateModal] = useState(false);
const [openDeleteAlert, setOpenDeleteAlert] = useState({
@@ -54,12 +57,14 @@ const Dashboard = () => {
<>
- {/* Header */}
-
- Your Interview Sessions
-
+
+
+ Your Interview Sessions
+
+
+
Manage, review, and dynamically create your AI-driven mock
@@ -75,7 +80,6 @@ const Dashboard = () => {
- {/* Sessions Grid */}
{sessions.length > 0 ? (
{sessions.map((data, index) => (
@@ -126,7 +130,6 @@ const Dashboard = () => {
)}
- {/* Floating Add Button */}
{sessions.length > 0 && (
{
- {/* Create Session Modal */}
setOpenCreateModal(false)}
@@ -148,7 +150,6 @@ const Dashboard = () => {
- {/* Delete Alert Modal */}
@@ -168,4 +169,11 @@ const Dashboard = () => {
);
};
-export default Dashboard;
\ No newline at end of file
+export default Dashboard;
+
+
+
+
+
+
+
diff --git a/frontend/src/pages/Home/ProgressTrackerDashboard.jsx b/frontend/src/pages/Home/ProgressTrackerDashboard.jsx
index 6c64cfea..755eb30e 100644
--- a/frontend/src/pages/Home/ProgressTrackerDashboard.jsx
+++ b/frontend/src/pages/Home/ProgressTrackerDashboard.jsx
@@ -11,8 +11,8 @@ import toast from "react-hot-toast";
import { UserContext } from "../../context/userContext";
import axiosInstance from "../../utils/axiosinstance";
import { API_PATHS } from "../../utils/apiPaths";
+import StreakBadge from "../../components/StreakBadge";
-/* āā Donut (SVG, zero deps) āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā */
const DonutChart = ({ value, max, size = 84, stroke = 9, color = "#7c3aed" }) => {
const r = (size - stroke) / 2;
const circ = 2 * Math.PI * r;
@@ -30,7 +30,6 @@ const DonutChart = ({ value, max, size = 84, stroke = 9, color = "#7c3aed" }) =>
);
};
-/* āā Difficulty breakdown āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā */
const DIFF_CONFIG = {
easy: { label: "Easy", bar: "bg-emerald-500", text: "text-emerald-400", bg: "bg-emerald-500/10" },
medium: { label: "Medium", bar: "bg-yellow-400", text: "text-yellow-400", bg: "bg-yellow-400/10" },
@@ -60,7 +59,6 @@ const DiffBar = ({ level, solved, total }) => {
);
};
-/* āā Small stat card āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā */
const StatCard = ({ label, value, icon: Icon, accent }) => (
@@ -71,7 +69,6 @@ const StatCard = ({ label, value, icon: Icon, accent }) => (
);
-/* āā Helpers āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā */
const fmtSheet = (id) =>
(id || "Unknown").split("-").map(w => w[0].toUpperCase() + w.slice(1)).join(" ");
@@ -99,8 +96,6 @@ function buildDifficultyStats(progress, sheetsMap) {
return stats;
}
-
-/* āā Main āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā */
const ProgressTrackerDashboard = () => {
const { user } = useContext(UserContext);
const navigate = useNavigate();
@@ -127,17 +122,13 @@ const ProgressTrackerDashboard = () => {
setSessions(sRes.data || []);
setResumes(rRes.data?.resumes || []);
- // Build a sheetId ā sheet object lookup
const map = {};
(shRes.data?.sheets || []).forEach(sh => { map[sh.id] = sh; });
setSheetsMap(map);
- // Start with backend data
const backendProgress = pRes.data?.progressList || [];
const backendIds = new Set(backendProgress.map(p => p.sheetId));
- // Merge localStorage entries for any followed sheets not yet in backend
- // and sync them up in the background
const localExtra = [];
for (let i = 0; i < localStorage.length; i++) {
const key = localStorage.key(i);
@@ -153,7 +144,6 @@ const ProgressTrackerDashboard = () => {
completedTopics: raw.completedTopics || {},
percentage: raw.percentage || 0,
});
- // Background sync to backend
axiosInstance.post("/api/user/sheet-progress", {
sheetId,
followed: true,
@@ -257,12 +247,9 @@ const ProgressTrackerDashboard = () => {
- {/* āā TOP ROW: Profile + Stats āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā */}
- {/* Profile card */}
- {/* Avatar */}
{user?.profileImageUrl ? (
{
- {/* Name + ID */}
{displayName}
{user?.prepPilotId &&
@{user.prepPilotId}
}
{user?.bio &&
{user.bio}
}
- {/* Socials */}
+
+
{(socials.github || socials.linkedin || socials.twitter || socials.portfolio) && (
{socials.github &&
}
@@ -292,7 +279,6 @@ const ProgressTrackerDashboard = () => {
)}
- {/* Meta */}
{user?.country && (
{user.country}
@@ -314,14 +300,12 @@ const ProgressTrackerDashboard = () => {
- {/* Stats grid */}
- {/* Quick actions */}
navigate("/role-prep")}
className="flex items-center justify-center gap-2 py-2.5 rounded-xl bg-violet-600 hover:bg-violet-700 text-white text-sm font-semibold transition-colors">
New Session
@@ -333,10 +317,8 @@ const ProgressTrackerDashboard = () => {
- {/* āā MIDDLE ROW: Analysis + Sessions āāāāāāāāāāāāāāāāāāāāāāāāāāāāāā */}
- {/* DSA Level Analysis */}
@@ -349,7 +331,6 @@ const ProgressTrackerDashboard = () => {
{(diffStats.easy.total + diffStats.medium.total + diffStats.hard.total) > 0 ? (
- {/* Summary badges */}
{[
{ level: "easy", ...diffStats.easy },
@@ -366,13 +347,11 @@ const ProgressTrackerDashboard = () => {
);
})}
- {/* Progress bars */}
- {/* Total */}
Total solved
@@ -392,7 +371,6 @@ const ProgressTrackerDashboard = () => {
)}
- {/* Recent Sessions */}
@@ -434,10 +412,8 @@ const ProgressTrackerDashboard = () => {
- {/* āā BOTTOM ROW: Sheet Progress + Solved Donuts + Resumes āāāāāāāāāā */}
- {/* Sheet Progress */}
@@ -519,7 +495,6 @@ const ProgressTrackerDashboard = () => {
)}
- {/* Problems Solved (donuts) */}
Problems Solved
@@ -566,7 +541,6 @@ const ProgressTrackerDashboard = () => {
- {/* Resumes */}
@@ -611,3 +585,5 @@ const ProgressTrackerDashboard = () => {
};
export default ProgressTrackerDashboard;
+
+
diff --git a/frontend/src/pages/InterviewPrep/Flashcards/Flashcards.jsx b/frontend/src/pages/InterviewPrep/Flashcards/Flashcards.jsx
new file mode 100644
index 00000000..4c129bd4
--- /dev/null
+++ b/frontend/src/pages/InterviewPrep/Flashcards/Flashcards.jsx
@@ -0,0 +1,26 @@
+import React, { useState, useEffect } from 'react';
+
+const Flashcards = () => {
+ const [flashcards, setFlashcards] = useState([]);
+ const [loading, setLoading] = useState(true);
+
+ useEffect(() => {
+ // TODO: Fetch from /api/flashcards
+ setLoading(false);
+ }, []);
+
+ return (
+
+
Spaced Repetition Flashcards
+ {loading ? (
+
Loading flashcards...
+ ) : (
+
+
Your review queue is currently empty. Add some flashcards!
+
+ )}
+
+ );
+};
+
+export default Flashcards;
diff --git a/frontend/src/pages/ResumeBulletEnhancer/ResumeBulletEnhancer.jsx b/frontend/src/pages/ResumeBulletEnhancer/ResumeBulletEnhancer.jsx
index 0fb5888f..e0e2168e 100644
--- a/frontend/src/pages/ResumeBulletEnhancer/ResumeBulletEnhancer.jsx
+++ b/frontend/src/pages/ResumeBulletEnhancer/ResumeBulletEnhancer.jsx
@@ -271,10 +271,21 @@ const ResumeBulletEnhancer = () => {
-
-
- Copy Rewrite
-
+ {
+ try {
+ if (!navigator.clipboard) {
+ throw new Error("Clipboard API unavailable");
+ }
+
+ await navigator.clipboard.writeText(text);
+ } catch (error) {
+ console.error("Failed to copy rewrite:", error);
+ }
+ }}
+ className="mt-6 px-5 py-3 rounded-xl bg-violet-600 hover:bg-violet-700 text-white font-semibold"
+ >
+ Copy Rewrite
diff --git a/frontend/src/pages/Settings/Settings.jsx b/frontend/src/pages/Settings/Settings.jsx
index 593710c2..6708357a 100644
--- a/frontend/src/pages/Settings/Settings.jsx
+++ b/frontend/src/pages/Settings/Settings.jsx
@@ -701,15 +701,24 @@ const Settings = () => {
Country
+
setCountry(e.target.value)}
+ onChange={(e) => {
+ const value = e.target.value;
+
+ const normalizedValue = value.normalize("NFC");
+
+ if (/^[\p{L}\p{M}\s'-]*$/u.test(normalizedValue)) {
+ setCountry(normalizedValue);
+}
+ }}
placeholder="Enter Country"
className="w-full bg-slate-50 dark:bg-slate-900 border border-slate-200 dark:border-slate-800 rounded-lg py-2.5 px-4 text-sm text-slate-900 dark:text-white"
/>
+
-
Educational Details
diff --git a/frontend/src/pages/SpacedRepetition/SpacedRepetitionPage.jsx b/frontend/src/pages/SpacedRepetition/SpacedRepetitionPage.jsx
index b30a3f5b..c265c7f2 100644
--- a/frontend/src/pages/SpacedRepetition/SpacedRepetitionPage.jsx
+++ b/frontend/src/pages/SpacedRepetition/SpacedRepetitionPage.jsx
@@ -11,6 +11,12 @@ import { API_PATHS } from "../../utils/apiPaths";
const CATEGORIES = ["All", "DSA", "Aptitude", "Role-Prep", "AI", "General", "Custom"];
+const notifyStreakMilestones = (newlyUnlockedAchievements) => {
+ (newlyUnlockedAchievements || [])
+ .filter((badge) => badge.endsWith("Streak"))
+ .forEach((badge) => toast.success(`š Milestone unlocked: ${badge}!`));
+};
+
const SpacedRepetitionPage = () => {
const [flashcards, setFlashcards] = useState([]);
const [stats, setStats] = useState({ totalCards: 0, dueCount: 0, masteredCount: 0, reviewedToday: 0 });
@@ -75,7 +81,8 @@ const SpacedRepetitionPage = () => {
const res = await axiosInstance.put(API_PATHS.FLASHCARD.REVIEW(currentCard._id), { rating });
if (res.data.success) {
toast.success(`Review saved! Next due in ${res.data.flashcard.interval} day(s)`);
-
+ notifyStreakMilestones(res.data.newlyUnlockedAchievements);
+
// Remove current card from queue or move to next
const updatedList = flashcards.filter((_, idx) => idx !== currentIndex);
setFlashcards(updatedList);
@@ -504,4 +511,4 @@ const SpacedRepetitionPage = () => {
);
};
-export default SpacedRepetitionPage;
+export default SpacedRepetitionPage;
\ No newline at end of file
diff --git a/frontend/src/utils/apiPaths.js b/frontend/src/utils/apiPaths.js
index b14936fc..39e6bbe8 100644
--- a/frontend/src/utils/apiPaths.js
+++ b/frontend/src/utils/apiPaths.js
@@ -87,4 +87,14 @@ export const API_PATHS = {
MINE: "/api/interview-experiences/mine",
UPDATE_STATUS: (id) => `/api/interview-experiences/${id}/status`,
},
+ BOOKMARKS: {
+ CREATE: "/api/bookmarks",
+ GET_ALL: "/api/bookmarks",
+ GET_ONE: (id) => `/api/bookmarks/${id}`,
+ UPDATE: (id) => `/api/bookmarks/${id}`,
+ TOGGLE_STAR: (id) => `/api/bookmarks/${id}/star`,
+ DELETE: (id) => `/api/bookmarks/${id}`,
+ TAGS: "/api/bookmarks/tags",
+ STATS: "/api/bookmarks/stats",
+ },
};
diff --git a/m.txt b/m.txt
new file mode 100644
index 00000000..dc2eace6
--- /dev/null
+++ b/m.txt
@@ -0,0 +1 @@
+in the site the agent recordings r hardcoded right, and one thing i want this agent to only speak and reply in kannada, english, hindi, primarily when it starts the conversation, it should be in english and it can be changed as per the users req, so where to make these changes, and it will take a while for us to get the whatsapp new number for this testing purpose, and as per the gsheet u have given, can u connect or wire the gsheet to the json flow and make the connection changes and give the updated correct json flow
\ No newline at end of file