commit 6c345df1c215d756f91072522278c6eeb2aaeea8 Author: Alaguraj0361 Date: Thu Oct 9 10:10:50 2025 +0530 first commit diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..a05184b --- /dev/null +++ b/.gitignore @@ -0,0 +1,4 @@ +# .gitignore +node_modules +.env +reports \ No newline at end of file diff --git a/config/db.js b/config/db.js new file mode 100644 index 0000000..d3d226f --- /dev/null +++ b/config/db.js @@ -0,0 +1,11 @@ +import mongoose from 'mongoose'; + +export async function connectDB() { + try { + await mongoose.connect(process.env.MONGODB_URI, { dbName: 'crawlerX' }); + console.log('✅ MongoDB connected'); + } catch (err) { + console.error('❌ MongoDB connection error:', err); + process.exit(1); + } +} diff --git a/controllers/auth.controller.js b/controllers/auth.controller.js new file mode 100644 index 0000000..68d7717 --- /dev/null +++ b/controllers/auth.controller.js @@ -0,0 +1,150 @@ +import bcrypt from "bcrypt"; +import jwt from "jsonwebtoken"; +import User from "../models/user.model.js"; +import { sendResetPasswordMail, sendSignupMail, } from "../utils/mailer.js"; +import crypto from "crypto"; + + +export async function signup(req, res) { + try { + const { email, password } = req.body; + if (!email || !password) + return res.status(400).json({ error: "Email and password required" }); + + const exists = await User.findOne({ email }); + if (exists) return res.status(400).json({ error: "User already exists" }); + + const passwordHash = await bcrypt.hash(password, 10); + const user = await User.create({ email, passwordHash }); + + // ✅ send confirmation email (non-blocking) + sendSignupMail(email) + .then(() => console.log("Signup email sent to", email)) + .catch(err => console.error("Email send failed:", err)); + + res.status(201).json({ message: "Signup success, email sent", id: user._id }); + } catch (err) { + console.error(err); + res.status(500).json({ error: "Signup failed" }); + } +} + +export async function login(req, res) { + try { + const { email, password } = req.body; + const user = await User.findOne({ email }); + if (!user) return res.status(401).json({ error: "Invalid credentials" }); + + const match = await bcrypt.compare(password, user.passwordHash); + if (!match) return res.status(401).json({ error: "Invalid credentials" }); + + const token = jwt.sign( + { id: user._id, email: user.email }, + process.env.JWT_SECRET, + { expiresIn: "1h" } + ); + + res.json({ message: "Login success", token }); + } catch (err) { + console.error(err); + res.status(500).json({ error: "Login failed" }); + } +} + +/** + * POST /api/auth/change-password + * Body: { currentPassword, newPassword } + * Header: Authorization: Bearer + */ +export async function changePassword(req, res) { + try { + const { currentPassword, newPassword } = req.body; + + // if using FormData, fields come from req.body AFTER a multipart parser + if (!currentPassword || !newPassword) { + return res.status(400).json({ error: "Current password and new password are required" }); + } + + const user = await User.findById(req.user.id); + if (!user) return res.status(404).json({ error: "User not found" }); + + const isMatch = await bcrypt.compare(currentPassword, user.passwordHash); + if (!isMatch) + return res.status(401).json({ error: "Current password is incorrect" }); + + user.passwordHash = await bcrypt.hash(newPassword, 10); + await user.save(); + + res.json({ message: "Password updated successfully" }); + } catch (err) { + console.error("changePassword error:", err); // ✅ show actual error + res.status(500).json({ error: "Failed to change password" }); + } +} + +/** + * POST /api/auth/forgot-password + * Body: { email } + */ +export async function forgotPassword(req, res) { + try { + const { email } = req.body; + if (!email) return res.status(400).json({ error: "Email is required" }); + + const user = await User.findOne({ email }); + + if (!user) + return res.json({ + message: "If the email is registered, a reset link has been sent.", + verificationCode: null, // user not found + }); + + // Generate 4-digit numeric verification code + const verificationCode = Math.floor(1000 + Math.random() * 9000).toString(); + + // Save code and expiry in DB + user.resetPasswordToken = verificationCode; + user.resetPasswordExpires = Date.now() + 60 * 60 * 1000; // 1 hour + await user.save(); + + // Send code via email + await sendResetPasswordMail(email, verificationCode); + + // ✅ Return verification code in response + res.json({ + message: "If the email is registered, a reset link has been sent.", + verificationCode, // This is the 4-digit code + }); + } catch (err) { + console.error("forgotPassword error:", err); + res.status(500).json({ error: "Failed to send reset link" }); + } +} + +/** + * POST /api/auth/reset-password + * Body: { token, newPassword } + */ +export async function resetPassword(req, res) { + try { + const { token, newPassword } = req.body; + if (!token || !newPassword) + return res.status(400).json({ error: "Token and new password are required" }); + + const user = await User.findOne({ + resetPasswordToken: token, + resetPasswordExpires: { $gt: Date.now() }, + }); + if (!user) return res.status(400).json({ error: "Invalid or expired token" }); + + user.passwordHash = await bcrypt.hash(newPassword, 10); + user.resetPasswordToken = undefined; + user.resetPasswordExpires = undefined; + await user.save(); + + res.json({ message: "Password has been reset successfully" }); + } catch (err) { + console.error("resetPassword error:", err); + res.status(500).json({ error: "Failed to reset password" }); + } +} \ No newline at end of file diff --git a/controllers/blog.controller.js b/controllers/blog.controller.js new file mode 100644 index 0000000..0df299f --- /dev/null +++ b/controllers/blog.controller.js @@ -0,0 +1,111 @@ +import Blog from '../models/blog.model.js'; +import Category from '../models/category.model.js'; +import slugify from 'slugify'; + +// ✅ Create Blog for particular project +export const createBlog = async (req, res) => { + try { + const { projectId, title, description, categoryId, tags } = req.body; + if (!projectId) return res.status(400).json({ message: 'projectId is required' }); + + const slug = slugify(title, { lower: true, strict: true }); + + const blog = await Blog.create({ + projectId, + title, + description, + slug, + category: categoryId, + tags, + imageUrl: req.files?.imageUrl ? `/uploads/${req.files.imageUrl[0].filename}` : '', + bigImageUrl: req.files?.bigImageUrl ? `/uploads/${req.files.bigImageUrl[0].filename}` : '' + }); + + res.status(201).json(blog); + } catch (err) { + res.status(500).json({ message: err.message }); + } +}; + +// ✅ Get All Blogs for a particular project +export const getAllBlogs = async (req, res) => { + try { + const { page = 1, limit = 10, search = '', category, projectId } = req.query; + if (!projectId) return res.status(400).json({ message: 'projectId is required' }); + + const query = { + projectId, + title: { $regex: search, $options: 'i' } + }; + + if (category) { + const cat = await Category.findOne({ slug: category }); + if (cat) query.category = cat._id; + } + + const blogs = await Blog.find(query) + .populate('category', 'name slug') + .sort({ createdAt: -1 }) + .skip((page - 1) * limit) + .limit(parseInt(limit)); + + const total = await Blog.countDocuments(query); + + res.json({ total, page: parseInt(page), blogs }); + } catch (err) { + res.status(500).json({ message: err.message }); + } +}; + +// ✅ Get Single Blog by Slug + projectId +export const getBlogBySlug = async (req, res) => { + try { + const { projectId } = req.query; // 👈 query param மூலம் + if (!projectId) return res.status(400).json({ message: 'projectId is required' }); + + const blog = await Blog.findOne({ slug: req.params.slug, projectId }) + .populate('category', 'name slug'); + + if (!blog) return res.status(404).json({ message: 'Blog not found' }); + res.json(blog); + } catch (err) { + res.status(500).json({ message: err.message }); + } +}; + +// ✅ Add Comment to Blog (projectId check optional – id already unique) +export const addComment = async (req, res) => { + try { + const { text, name } = req.body; + const blog = await Blog.findById(req.params.id); + if (!blog) return res.status(404).json({ message: 'Blog not found' }); + + blog.comments.push({ + user: req.user?._id, + name: name || 'Anonymous', + text + }); + + await blog.save(); + res.json(blog.comments); + } catch (err) { + res.status(500).json({ message: err.message }); + } +}; + +// ✅ Like / Unlike +export const likeBlog = async (req, res) => { + try { + const blog = await Blog.findById(req.params.id); + if (!blog) return res.status(404).json({ message: 'Blog not found' }); + + const userId = req.user._id; + if (blog.likes.includes(userId)) blog.likes.pull(userId); + else blog.likes.push(userId); + + await blog.save(); + res.json({ likesCount: blog.likes.length }); + } catch (err) { + res.status(500).json({ message: err.message }); + } +}; diff --git a/controllers/category.controller.js b/controllers/category.controller.js new file mode 100644 index 0000000..f88107e --- /dev/null +++ b/controllers/category.controller.js @@ -0,0 +1,46 @@ +import Category from '../models/category.model.js'; +import slugify from "slugify"; + +// Create a new category (Admin only) +export const createCategory = async (req, res) => { + try { + const { name, projectId } = req.body; + if (!projectId) return res.status(400).json({ message: "projectId is required" }); + + const slug = slugify(name, { lower: true, strict: true }); + + const category = await Category.create({ + name, + slug, + projectId + }); + + res.status(201).json(category); + } catch (err) { + res.status(500).json({ message: err.message }); + } +}; + +// Get all categories for a project +export const getCategories = async (req, res) => { + try { + const { projectId } = req.query; + if (!projectId) return res.status(400).json({ message: "projectId is required" }); + + const categories = await Category.find({ projectId }).sort({ name: 1 }); + res.json(categories); + } catch (err) { + res.status(500).json({ message: err.message }); + } +}; + +// Delete a category (Admin only) +export const deleteCategory = async (req, res) => { + try { + const { id } = req.params; + await Category.findByIdAndDelete(id); + res.json({ message: "Category deleted" }); + } catch (err) { + res.status(500).json({ message: err.message }); + } +}; diff --git a/controllers/comment.controller.js b/controllers/comment.controller.js new file mode 100644 index 0000000..f3af24b --- /dev/null +++ b/controllers/comment.controller.js @@ -0,0 +1,54 @@ +import Blog from "../models/blog.model.js"; + +// Add comment to a blog +export const addComment = async (req, res) => { + try { + const { blogId } = req.params; + const { text, name } = req.body; + + const blog = await Blog.findById(blogId); + if (!blog) return res.status(404).json({ message: "Blog not found" }); + + blog.comments.push({ + user: req.user?._id || null, + name: name || "Anonymous", + text + }); + + await blog.save(); + res.status(201).json(blog.comments); + } catch (err) { + res.status(500).json({ message: err.message }); + } +}; + +// Get all comments for a blog +export const getComments = async (req, res) => { + try { + const { blogId } = req.params; + + const blog = await Blog.findById(blogId); + if (!blog) return res.status(404).json({ message: "Blog not found" }); + + res.json(blog.comments.sort((a,b) => b.createdAt - a.createdAt)); + } catch (err) { + res.status(500).json({ message: err.message }); + } +}; + +// Delete a comment (Admin only) +export const deleteComment = async (req, res) => { + try { + const { blogId, commentId } = req.params; + + const blog = await Blog.findById(blogId); + if (!blog) return res.status(404).json({ message: "Blog not found" }); + + blog.comments.id(commentId)?.remove(); + await blog.save(); + + res.json({ message: "Comment deleted" }); + } catch (err) { + res.status(500).json({ message: err.message }); + } +}; diff --git a/controllers/crawl.controller.js b/controllers/crawl.controller.js new file mode 100644 index 0000000..042b925 --- /dev/null +++ b/controllers/crawl.controller.js @@ -0,0 +1,115 @@ +import path from "node:path"; +import fs from "node:fs"; +import { fileURLToPath } from "node:url"; +import { crawl } from "../crawler.js"; + +const __dirname = path.dirname(fileURLToPath(import.meta.url)); + +const ts = () => + new Date().toISOString().replaceAll(":", "-").replaceAll(".", "-"); + +function attachJson(res, filename, obj) { + const json = JSON.stringify(obj, null, 2); + res.setHeader("Content-Type", "application/json; charset=utf-8"); + res.setHeader("Content-Disposition", `attachment; filename="${filename}"`); + return res.send(json); +} +function isAbs(p) { + try { + return path.isAbsolute(p); + } catch { + return false; + } +} + +export async function crawlHandler(req, res) { + try { + const { url, max, stream, download, nostore } = req.query; + if (!url) return res.status(400).json({ error: "Missing url param" }); + + const target = new URL(String(url)); + const limit = Math.min(Math.max(parseInt(max ?? "50", 10), 1), 500); + const wantsStream = + String(stream) === "1" || + (req.get("accept") || "").includes("text/event-stream"); + + /* ---------- SSE mode ---------- */ + if (wantsStream) { + if (String(download) === "1") { + return res.status(400).json({ error: "download not supported with stream=1" }); + } + + res.setHeader("Content-Type", "text/event-stream"); + res.setHeader("Cache-Control", "no-cache, no-transform"); + res.setHeader("Connection", "keep-alive"); + res.flushHeaders?.(); + + const send = (obj, evt) => { + if (evt) res.write(`event: ${evt}\n`); + res.write(`data: ${JSON.stringify(obj)}\n\n`); + }; + + const heartbeat = setInterval(() => res.write(":\n\n"), 15000); + let finished = false; + + req.on("close", () => { + clearInterval(heartbeat); + if (!finished) console.warn("SSE client disconnected."); + }); + + const onProgress = (tick) => send(tick, "tick"); + send({ ok: true, message: "Crawl started", url: target.toString(), limit }, "started"); + + const result = await crawl(target.toString(), limit, onProgress, { + persistReports: false, + collectPages: true, + }); + + finished = true; + clearInterval(heartbeat); + send({ ok: true, done: true, result }, "done"); + return res.end(); + } + + /* ---------- Non-streaming mode ---------- */ + const preferMemory = String(nostore) === "1" || String(download) === "1"; + const result = await crawl( + target.toString(), + limit, + undefined, + preferMemory + ? { persistReports: false, collectPages: true } + : { persistReports: true, collectPages: true } + ); + + if (String(download) === "1") { + const filename = `crawl-${ts()}.json`; + + if (Array.isArray(result?.results)) { + return attachJson(res, filename, result.results); + } + + const jsonPath = result?.files?.json; + if (jsonPath) { + const abs = isAbs(jsonPath) ? jsonPath : path.join(__dirname, jsonPath); + if (fs.existsSync(abs)) { + res.setHeader("Content-Type", "application/json; charset=utf-8"); + res.setHeader("Content-Disposition", `attachment; filename="${filename}"`); + return fs.createReadStream(abs).pipe(res); + } + } + return attachJson(res, filename, result ?? {}); + } + + return res.json({ + ok: true, + message: "Crawl completed", + url: target.toString(), + limit, + ...result, + }); + } catch (err) { + console.error("Crawl error:", err); + res.status(500).json({ error: "Crawl failed", details: String(err?.message ?? err) }); + } +} diff --git a/controllers/lighthouseController.js b/controllers/lighthouseController.js new file mode 100644 index 0000000..fb34c87 --- /dev/null +++ b/controllers/lighthouseController.js @@ -0,0 +1,113 @@ +import lighthouse from 'lighthouse'; +import { launch } from 'chrome-launcher'; +import PageSpeedTest from '../models/pageSpeedTest.model.js'; +import path from 'path'; +import fs from 'fs'; + +const reportsDir = path.join(process.cwd(), 'public', 'lighthouse-treemap'); +// Ensure folder exists +if (!fs.existsSync(reportsDir)) fs.mkdirSync(reportsDir, { recursive: true }); + +const launchChromeAndRunLighthouse = async (url, device = 'mobile') => { + const chrome = await launch({ chromeFlags: ['--headless'] }); + + const options = { + port: chrome.port, + emulatedFormFactor: device, + throttlingMethod: device === 'mobile' ? 'simulate' : 'devtools', + output: 'json', // JSON for metrics + }; + + const runnerResult = await lighthouse(url, options); + const lhr = runnerResult.lhr; + + // Create HTML treemap report (only once, for mobile) + let treemapFile = null; + if (device === 'mobile') { + const fileName = `treemap-${Date.now()}.html`; + treemapFile = `/lighthouse-treemap/${fileName}`; + + // Generate HTML report + const htmlReport = await lighthouse(url, { + port: chrome.port, + emulatedFormFactor: device, + throttlingMethod: 'simulate', + output: 'html', + }); + + fs.writeFileSync(path.join(reportsDir, fileName), htmlReport.report); + } + + await chrome.kill(); + + // Structured result + const result = { + url, + device, + scores: { + performance: Math.round(lhr.categories.performance?.score * 100), + accessibility: Math.round(lhr.categories.accessibility?.score * 100), + bestPractices: Math.round(lhr.categories['best-practices']?.score * 100), + seo: Math.round(lhr.categories.seo?.score * 100), + pwa: lhr.categories.pwa?.score ? Math.round(lhr.categories.pwa.score * 100) : null, + }, + metrics: { + firstContentfulPaint: lhr.audits['first-contentful-paint']?.displayValue || null, + largestContentfulPaint: lhr.audits['largest-contentful-paint']?.displayValue || null, + totalBlockingTime: lhr.audits['total-blocking-time']?.displayValue || null, + timeToInteractive: lhr.audits['interactive']?.displayValue || null, + speedIndex: lhr.audits['speed-index']?.displayValue || null, + cumulativeLayoutShift: lhr.audits['cumulative-layout-shift']?.displayValue || null, + }, + opportunities: Object.values(lhr.audits) + .filter(a => a.details?.type === 'opportunity') + .map(a => ({ + title: a.title, + description: a.description, + estimatedSavings: a.details?.overallSavingsMs + ? `${Math.round(a.details.overallSavingsMs)} ms` + : null, + })), + diagnostics: { + usesHTTPS: lhr.audits['is-on-https']?.score === 1, + usesEfficientCachePolicy: lhr.audits['uses-long-cache-ttl']?.score === 1, + imageCompression: lhr.audits['uses-optimized-images']?.score === 1, + }, + failedAudits: Object.values(lhr.audits) + .filter(a => a.score !== null && a.score !== 1 && a.scoreDisplayMode !== 'notApplicable') + .map(a => ({ title: a.title, description: a.description })), + passedAudits: Object.values(lhr.audits) + .filter(a => a.score === 1 && a.scoreDisplayMode !== 'notApplicable' && !a.details?.type) + .map(a => a.title), + notApplicableAudits: Object.values(lhr.audits) + .filter(a => a.scoreDisplayMode === 'notApplicable') + .map(a => a.title), + screenshot: lhr.audits['final-screenshot']?.details?.data || null, + createdAt: new Date(), + treemapPath: treemapFile, + }; + + const report = await PageSpeedTest.create(result); + return { report }; +}; + +export const runAudit = async (req, res, next) => { + try { + const { url } = req.body; + if (!url) return res.status(400).json({ message: 'URL is required' }); + + const mobileResult = await launchChromeAndRunLighthouse(url, 'mobile'); + const desktopResult = await launchChromeAndRunLighthouse(url, 'desktop'); + + res.status(200).json({ + message: 'Audit completed successfully', + results: { + mobile: mobileResult.report, + desktop: desktopResult.report, + treemap: mobileResult.report.treemapPath, // HTML report + }, + }); + } catch (err) { + next(err); + } +}; diff --git a/controllers/maisondetreats/cakeOrder.controller.js b/controllers/maisondetreats/cakeOrder.controller.js new file mode 100644 index 0000000..0a4cd3d --- /dev/null +++ b/controllers/maisondetreats/cakeOrder.controller.js @@ -0,0 +1,39 @@ +import { CakeOrder } from "../../models/maisondetreats/cakeOrder.model.js"; +import { sendCakeOrderMail } from "../../utils/mailer.js"; + +export const createCakeOrder = async (req, res) => { + try { + const { order, email } = req.body; + + if (!order || typeof order !== "object") { + return res.status(400).json({ message: "Order data is required" }); + } + + const newOrder = await CakeOrder.create({ order, email }); + + // ✅ send confirmation email (non-blocking) + if (email) { + sendCakeOrderMail(email, order) + .then(() => console.log("Cake order email sent to", email)) + .catch((err) => console.error("Email send failed:", err)); + } + + res.status(201).json({ + message: "Cake order created successfully", + data: newOrder, + }); + } catch (err) { + console.error("Error creating cake order:", err); + res.status(500).json({ message: "Server error", error: err.message }); + } +}; +// GET /api/cake-orders → List all orders +export const getAllCakeOrders = async (_req, res) => { + try { + const orders = await CakeOrder.find().sort({ createdAt: -1 }); + res.json({ data: orders }); + } catch (err) { + console.error("Error fetching cake orders:", err); + res.status(500).json({ message: "Server error", error: err.message }); + } +}; diff --git a/controllers/message.controller.js b/controllers/message.controller.js new file mode 100644 index 0000000..921b74a --- /dev/null +++ b/controllers/message.controller.js @@ -0,0 +1,68 @@ +// message.controller.js + +import dotenv from "dotenv"; +import axios from "axios"; +import Message from "../models/message.model.js"; + +dotenv.config(); + +export const sendMessage = async (req, res) => { + try { + const { project, name, email, message } = req.body; + + if (!project) return res.status(400).json({ success: false, error: "Project is required" }); + if (!message) return res.status(400).json({ success: false, error: "Message is required" }); + + // Save message to MongoDB + const newMessage = await Message.create({ project, name, email, message }); + + // Send WhatsApp Template Message + const url = `https://graph.facebook.com/v22.0/774121419125441/messages`; + const payload = { + messaging_product: "whatsapp", + to: 917871207631, + type: "template", + template: { + name: "new_message_alert", + language: { code: "en_US" }, + components: [ + { + type: "body", + parameters: [ + { type: "text", text: project || "Project" }, + { type: "text", text: name || "Guest" }, + { type: "text", text: email || "N/A" }, + { type: "text", text: message || "No message" }, + ], + }, + ], + }, + }; + const headers = { + Authorization: `Bearer EAALKxEMPlp0BPkmoTAJlZAZAymtgqzcUuGVdZAZAKSZAw1csXR5Xy2DodBUC2zXckOYvQ2jOV4aFlZAeCo4IuJCyMb5aFt2UfNRQ1pDGk08QlbCjjCTMsZALipZCMNYyNVwN2pTDwUcYeNZByOrweVVdXD1ErZAbzjc04wmR8ilhQXink4it05BatwkZBf3xCLyy3k6R0tgx9JoymQTn83iZANBWDzvmX3vW5dx6Pud6xNEfqYNsjwZDZD`, + "Content-Type": "application/json", + }; + + const response = await axios.post(url, payload, { headers }); + console.log("✅ WhatsApp API Response:", response.data); + + return res.status(201).json({ success: true, data: newMessage }); + } catch (err) { + console.error("❌ WhatsApp API Error:", err.response?.data || err.message); + return res.status(500).json({ success: false, error: "Server Error" }); + } +}; + +// ✅ Add this function and export it +export const getMessages = async (req, res) => { + try { + const { project } = req.query; + if (!project) return res.status(400).json({ success: false, error: "Project is required" }); + + const messages = await Message.find({ project }).sort({ createdAt: -1 }); + return res.status(200).json({ success: true, data: messages }); + } catch (err) { + console.error(err); + return res.status(500).json({ success: false, error: "Server Error" }); + } +}; diff --git a/controllers/payment.controller.js b/controllers/payment.controller.js new file mode 100644 index 0000000..a465115 --- /dev/null +++ b/controllers/payment.controller.js @@ -0,0 +1,132 @@ +// controllers/payment.controller.js +import Stripe from "stripe"; +import { Payment } from "../models/payment.model.js"; + +// ✅ Load Stripe Secret Key from .env +const stripe = new Stripe("sk_test_51SB8SnIFk8fh986GkYaNPVSfZzh6gcuXhq3tOa5hyE4U4vYIqrHwyGRu2OE1N5TNW39tJmfFOyYfsh4HcZOjlsj100xIeM46zU", { + apiVersion: "2022-11-15", +}); + +/** + * 🔹 Option 1: PaymentIntent API (client uses clientSecret) + */ +export async function createPaymentIntent(req, res) { + try { + const { amount } = req.body; + if (!amount) return res.status(400).json({ error: "amount is required" }); + + const paymentIntent = await stripe.paymentIntents.create({ + amount: Math.round(amount * 100), // dollars → cents + currency: "usd", + automatic_payment_methods: { enabled: true }, + }); + + await Payment.create({ + amount: Math.round(amount * 100), + stripePaymentIntentId: paymentIntent.id, + status: "pending", + }); + + res.json({ clientSecret: paymentIntent.client_secret }); + } catch (err) { + console.error("❌ Error creating PaymentIntent:", err); + res.status(500).json({ error: "Internal Server Error" }); + } +} + +/** + * 🔹 Option 2: Stripe Checkout Session (redirect flow) + */ +export async function createCheckoutSession(req, res) { + try { + const { email, amount, planId } = req.body; + if (!email || !amount) { + return res.status(400).json({ error: "email and amount are required" }); + } + + const session = await stripe.checkout.sessions.create({ + payment_method_types: ["card"], + mode: "payment", + customer_email: email, + line_items: [ + { + price_data: { + currency: "usd", + product_data: { name: planId || "SEO Plan" }, + unit_amount: Math.round(amount * 100), + }, + quantity: 1, + }, + ], + success_url: "https://app.crawlerx.co/success", + cancel_url: "https://app.crawlerx.co/cancel", + }); + + // Save to DB using stripeSessionId instead of stripePaymentIntentId + await Payment.create({ + email, + amount: Math.round(amount * 100), + stripeSessionId: session.id, // ✅ use session id + status: "pending", + }); + + res.json({ sessionId: session.id }); + } catch (err) { + console.error("❌ Error creating checkout session:", err); + res.status(500).json({ error: "Internal Server Error" }); + } +} + + +/** + * 🔹 Stripe Webhook + * Stripe requires `express.raw({ type: "application/json" })` in route + */ +export async function handleWebhook(req, res) { + const sig = req.headers["stripe-signature"]; + let event; + + try { + event = stripe.webhooks.constructEvent( + req.rawBody, // Must be raw body + sig, + process.env.STRIPE_WEBHOOK_SECRET + ); + } catch (err) { + console.error("❌ Webhook signature verification failed:", err.message); + return res.status(400).send(`Webhook Error: ${err.message}`); + } + + switch (event.type) { + case "payment_intent.succeeded": { + const paymentIntent = event.data.object; + console.log("✅ PaymentIntent succeeded:", paymentIntent.id); + + await Payment.findOneAndUpdate( + { stripePaymentIntentId: paymentIntent.id }, + { status: "succeeded" } + ); + break; + } + + case "checkout.session.completed": { + const session = event.data.object; + console.log("✅ Checkout session completed:", session.id); + + // Update DB record created earlier + await Payment.findOneAndUpdate( + { email: session.customer_email, status: "pending" }, + { + stripePaymentIntentId: session.payment_intent, + status: "succeeded", + } + ); + break; + } + + default: + console.log(`Unhandled event type ${event.type}`); + } + + res.json({ received: true }); +} diff --git a/controllers/sitemap.controller.js b/controllers/sitemap.controller.js new file mode 100644 index 0000000..25bf7f2 --- /dev/null +++ b/controllers/sitemap.controller.js @@ -0,0 +1,20 @@ +import { getSitemapUrls } from "../utils/sitemap.js"; + +export async function sitemapHandler(req, res) { + try { + const { u } = req.query; + if (!u) return res.status(400).json({ error: "Missing ?u=https://site.com" }); + + const origin = new URL(String(u)); + const urls = await getSitemapUrls(origin.toString()); + res.json({ + ok: true, + origin: origin.origin, + count: urls.length, + urls, + }); + } catch (err) { + console.error("sitemap error:", err); + res.status(500).json({ error: "Failed to fetch sitemap", details: String(err?.message ?? err) }); + } +} diff --git a/crawler copy.js b/crawler copy.js new file mode 100644 index 0000000..a23651e --- /dev/null +++ b/crawler copy.js @@ -0,0 +1,709 @@ +import got from "got"; +import * as cheerio from "cheerio"; +import normalizeUrl from "normalize-url"; +import { isInternal } from "./utils/urlHelpers.js"; +import { getSitemapUrls } from "./utils/sitemap.js"; +import fs from "node:fs"; +import path from "node:path"; +import { chromium } from "playwright"; + +// NEW libs +import pixelWidth from "string-pixel-width"; +import * as readability from "text-readability"; +import stringSimilarity from "string-similarity"; + +/* ------------------------------ globals --------------------------------- */ +const visited = new Set(); +const queue = []; +const results = []; + +// Link provenance: every discovered edge (source -> target) +const edges = []; // { from, raw_href, to, discovered_by } + +// Quick referrer map for error report +const referrers = new Map(); // url -> Array<{from, raw_href, discovered_by}> + +const REAL_UA = + "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0.0.0 Safari/537.36"; +const REAL_HEADERS = { + "user-agent": REAL_UA, + "accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8", + "accept-language": "en-US,en;q=0.9", + "upgrade-insecure-requests": "1", +}; + +/* ------------------------------ utils ----------------------------------- */ +function csvEscape(v) { + if (v === undefined || v === null) return ""; + const s = String(v); + return /[",\n]/.test(s) ? `"${s.replace(/"/g, '""')}"` : s; +} +function ensureDir(dir) { + if (!fs.existsSync(dir)) fs.mkdirSync(dir, { recursive: true }); +} +function writePageReports(results) { + ensureDir("reports"); + const stamp = new Date().toISOString().replace(/[:T]/g, "-").slice(0, 19); + const base = path.join("reports", `crawl-${stamp}`); + + fs.writeFileSync(`${base}.json`, JSON.stringify(results, null, 2), "utf8"); + console.log(`\n📝 Full JSON report saved: ${base}.json`); + + // Columns (a Screaming-Frog-ish shape with our extras) + const headers = [ + "url", "status", "status_text", "time_ms", "bytes", "content_type", "http_version", + "title", "title_length", "title_pixel_width", + "meta_description", "meta_description_length", "meta_description_pixel_width", + "h1_1", "h1_1_length", "h1_1_pixel_width", "h1_2", "h1_2_length", "h1_2_pixel_width", + "h2_1", "h2_2", + "canonical", "robots_meta", "x_robots_tag", "noindex", "nofollow", + "lang", "word_count", "flesch_reading_ease", "flesch_kincaid_grade", + "gunning_fog", "coleman_liau", "ari", "smog", + "schema_types", "inlinks", "outlinks", "render_mode", + "last_modified", "set_cookie", "crawl_timestamp", + "duplicate_title_exact", "nearest_title_similarity", "nearest_title_url", + "duplicate_description_exact", "nearest_description_similarity", "nearest_description_url" + ]; + const lines = [headers.join(",")]; + for (const r of results) { + lines.push([ + r.url, + r.status, + r.status_text ?? "", + r.time_ms, + r.bytes, + r.content_type, + r.http_version ?? "", + r.title, + r.title_length, + r.title_pixel_width, + r.meta_description, + r.meta_description_length, + r.meta_description_pixel_width, + r.h1_1 ?? "", + r.h1_1_length ?? 0, + r.h1_1_pixel_width ?? "", + r.h1_2 ?? "", + r.h1_2_length ?? 0, + r.h1_2_pixel_width ?? "", + r.h2_1 ?? "", + r.h2_2 ?? "", + r.canonical, + r.robots_meta, + r.x_robots_tag ?? "", + r.noindex, + r.nofollow, + r.lang ?? "", + r.word_count ?? "", + r.flesch_reading_ease ?? "", + r.flesch_kincaid_grade ?? "", + r.gunning_fog ?? "", + r.coleman_liau ?? "", + r.ari ?? "", + r.smog ?? "", + Array.isArray(r.schema_types) ? r.schema_types.join("|") : "", + r.inlinks ?? 0, + r.outlinks ?? 0, + r.render_mode, + r.last_modified ?? "", + r.set_cookie ? "yes" : "no", + r.crawl_timestamp ?? "", + r.duplicate_title_exact ?? "", + r.nearest_title_similarity ?? "", + r.nearest_title_url ?? "", + r.duplicate_description_exact ?? "", + r.nearest_description_similarity ?? "", + r.nearest_description_url ?? "" + ].map(csvEscape).join(",")); + } + //fs.writeFileSync(`${base}.csv`, lines.join("\n"), "utf8"); + //console.log(`\n📝 Page reports saved:\n - ${base}.csv\n - ${base}.json`); +} +function writeLinkEdges(edges) { + ensureDir("reports"); + const stamp = new Date().toISOString().replace(/[:T]/g, "-").slice(0, 19); + const file = path.join("reports", `links-${stamp}.csv`); + const headers = ["from", "raw_href", "to", "discovered_by"]; + const lines = [headers.join(",")]; + for (const e of edges) { + lines.push([e.from, e.raw_href, e.to, e.discovered_by].map(csvEscape).join(",")); + } + fs.writeFileSync(file, lines.join("\n"), "utf8"); + console.log(`🔗 Link provenance saved: ${file}`); +} +function writeErrors(results) { + ensureDir("reports"); + const stamp = new Date().toISOString().replace(/[:T]/g, "-").slice(0, 19); + const file = path.join("reports", `errors-${stamp}.csv`); + const headers = ["url", "status", "title", "from_page", "raw_href", "discovered_by"]; + const lines = [headers.join(",")]; + + for (const r of results) { + if (r && r.status !== null && r.status >= 400) { + const refs = referrers.get(r.url) || []; + if (refs.length === 0) { + lines.push([r.url, r.status, r.title, "", "", ""].map(csvEscape).join(",")); + } else { + for (const ref of refs) { + lines.push([r.url, r.status, r.title, ref.from, ref.raw_href, ref.discovered_by].map(csvEscape).join(",")); + } + } + } + } + fs.writeFileSync(file, lines.join("\n"), "utf8"); + console.log(`❗ Error report saved: ${file}`); +} +function addEdge(from, rawHref, to, discovered_by) { + edges.push({ from, raw_href: rawHref || "", to, discovered_by }); + if (!referrers.has(to)) referrers.set(to, []); + referrers.get(to).push({ from, raw_href: rawHref || "", discovered_by }); +} + +/* ---------------------- parse HTML without JS --------------------------- */ +function safeJsonParse(txt) { + try { return JSON.parse(txt); } catch { return null; } +} +function parseSchemaTypes($) { + const types = new Set(); + $('script[type="application/ld+json"]').each((_, el) => { + const raw = $(el).contents().text(); + const parsed = safeJsonParse(raw); + if (!parsed) return; + const collect = (obj) => { + if (!obj) return; + if (Array.isArray(obj)) { obj.forEach(collect); return; } + if (typeof obj === "object") { + const t = obj["@type"]; + if (typeof t === "string") types.add(t); + else if (Array.isArray(t)) t.forEach(x => typeof x === "string" && types.add(x)); + // nested + Object.values(obj).forEach(collect); + } + }; + collect(parsed); + }); + return [...types]; +} +function parseHtml(html, url) { + const $ = cheerio.load(html); + + let title = ($("title").first().text() || "").trim(); + const ogTitle = $('meta[property="og:title"]').attr("content") || ""; + const twTitle = $('meta[name="twitter:title"]').attr("content") || ""; + + // Headings (capture top two H1s and H2s) + const h1s = $("h1").map((_, el) => $(el).text().trim()).get(); + const h2s = $("h2").map((_, el) => $(el).text().trim()).get(); + + const h1_1 = h1s[0] || ""; + const h1_2 = h1s[1] || ""; + const h2_1 = h2s[0] || ""; + const h2_2 = h2s[1] || ""; + + const totalHeadings = $("h1,h2,h3,h4,h5,h6,[role='heading']").length; + + if (!title) title = (ogTitle || twTitle || h1_1 || "").trim(); + + const metaDesc = ($('meta[name="description"]').attr("content") || "").trim(); + const canonical = ($('link[rel="canonical"]').attr("href") || "").trim(); + const robotsMeta = ($('meta[name="robots"]').attr("content") || "").trim(); + const robotsLower = robotsMeta.toLowerCase(); + const noindex = /(^|[,;\s])noindex([,;\s]|$)/.test(robotsLower); + const nofollow = /(^|[,;\s])nofollow([,;\s]|$)/.test(robotsLower); + + const lang = ($("html").attr("lang") || "").trim(); + + // Basic text body for word count / readability + const bodyText = ($("main").text() || $("body").text() || "").replace(/\s+/g, " ").trim(); + const wordCount = bodyText ? bodyText.split(/\s+/).length : 0; + + // Internal links + raw href + const internalLinks = new Set(); + const rawLinks = []; + $("a[href]").each((_, el) => { + const href = $(el).attr("href"); + if (!href) return; + try { + const abs = new URL(href, url).toString(); + rawLinks.push({ raw: href, abs }); + internalLinks.add(abs); + } catch { } + }); + + // Schema.org JSON-LD types + const schemaTypes = parseSchemaTypes($); + + return { + title, + metaDesc, + h1_1, h1_2, h2_1, h2_2, + totalHeadings, + canonical, robotsMeta, noindex, nofollow, + internalLinks, rawLinks, + lang, + wordCount, + schemaTypes, + bodyText + }; +} + +/* ------------------------------ fetchers -------------------------------- */ +async function fetchWithGot(url) { + const t0 = Date.now(); + const res = await got(url, { + timeout: { request: 20000 }, + throwHttpErrors: false, + headers: REAL_HEADERS, + http2: false + }); + const dt = Date.now() - t0; + const contentType = (res.headers["content-type"] || "").toLowerCase(); + const bytes = res.headers["content-length"] + ? Number(res.headers["content-length"]) + : Buffer.byteLength(res.body || "", "utf8"); + + return { + status: res.statusCode ?? null, + status_text: res.statusMessage ?? "", + time_ms: dt, + contentType, + body: res.body, + bytes, + render_mode: "http", + httpVersion: res.httpVersion ?? "", + headers: res.headers + }; +} + +async function createBrowserContext() { + const browser = await chromium.launch({ headless: true, args: ["--disable-blink-features=AutomationControlled"] }); + const context = await browser.newContext({ + ignoreHTTPSErrors: true, // Ignore SSL certificate errors + userAgent: REAL_UA, + viewport: { width: 1366, height: 768 }, + deviceScaleFactor: 1, + isMobile: false, + locale: "en-US", + extraHTTPHeaders: REAL_HEADERS + }); + await context.addInitScript(() => { + Object.defineProperty(navigator, "webdriver", { get: () => false }); + Object.defineProperty(navigator, "plugins", { get: () => [1, 2, 3] }); + Object.defineProperty(navigator, "languages", { get: () => ["en-US", "en"] }); + }); + return { browser: context.browser(), context }; +} + +async function fetchWithPlaywrightAndExtract(url, shared) { + const page = await shared.context.newPage(); + const t0 = Date.now(); + let status = null, mainHeaders = {}, statusText = ""; + + try { + const resp = await page.goto(url, { waitUntil: "domcontentloaded", timeout: 30000 }); + status = resp?.status() ?? null; + statusText = resp?.statusText() ?? ""; + try { mainHeaders = resp ? await resp.headers() : {}; } catch { } + + try { await page.waitForLoadState("networkidle", { timeout: 12000 }); } catch { } + try { + await page.waitForFunction(() => { + const main = document.querySelector("main") || document.body; + const textLen = (main?.innerText || "").replace(/\s+/g, " ").trim().length; + const hasHeading = !!document.querySelector("h1, h2, [role='heading'], [class*='title'], [class*='heading'], [class*='hero'], [class*='banner']"); + return textLen > 160 || hasHeading; + }, { timeout: 8000 }); + } catch { } + + const dom = await page.evaluate(() => { + const clean = s => (s || "").replace(/\s+/g, " ").trim(); + const getTextList = sel => Array.from(document.querySelectorAll(sel)) + .map(el => clean(el.textContent)).filter(Boolean); + + const title = document.title || ""; + const ogTitle = document.querySelector('meta[property="og:title"]')?.content || ""; + const twTitle = document.querySelector('meta[name="twitter:title"]')?.content || ""; + const metaDesc = document.querySelector('meta[name="description"]')?.content || ""; + const canonical = document.querySelector('link[rel="canonical"]')?.href || ""; + const robotsMeta = document.querySelector('meta[name="robots"]')?.content || ""; + const lang = document.documentElement.getAttribute("lang") || ""; + + const h1 = getTextList("h1"); + const h2 = getTextList("h2"); + const h3 = getTextList("h3"); + const totalHeadings = document.querySelectorAll("h1,h2,h3,h4,h5,h6,[role='heading']").length; + + const links = Array.from(document.querySelectorAll("a[href]")) + .map(a => { + const raw = a.getAttribute("href"); + try { return { raw, abs: new URL(raw, location.href).toString() }; } + catch { return null; } + }) + .filter(Boolean); + + const firstHeading = h1[0] || h2[0] || ""; + const bodyText = clean((document.querySelector("main") || document.body).innerText || ""); + + const schemaScripts = Array.from(document.querySelectorAll('script[type="application/ld+json"]')).map(s => s.textContent || ""); + + return { + htmlLen: (document.documentElement.outerHTML || "").length, + title, ogTitle, twTitle, metaDesc, canonical, robotsMeta, lang, + h1, h2, totalHeadings, + links, + bodyText, + schemaScripts + }; + }); + + // Parse schema types from strings (outside of page) + const schemaTypes = []; + for (const raw of dom.schemaScripts || []) { + try { + const parsed = JSON.parse(raw); + const collect = (obj) => { + if (!obj) return; + if (Array.isArray(obj)) { obj.forEach(collect); return; } + if (typeof obj === "object") { + const t = obj["@type"]; + if (typeof t === "string") schemaTypes.push(t); + else if (Array.isArray(t)) t.forEach(x => typeof x === "string" && schemaTypes.push(x)); + Object.values(obj).forEach(collect); + } + }; + collect(parsed); + } catch { } + } + + const dt = Date.now() - t0; + const robotsLower = (dom.robotsMeta || "").toLowerCase(); + const noindex = /(^|[,;\s])noindex([,;\s]|$)/.test(robotsLower); + const nofollow = /(^|[,;\s])nofollow([,;\s]|$)/.test(robotsLower); + const finalTitle = (dom.title || dom.ogTitle || dom.twTitle || dom.h1?.[0] || "").trim(); + + return { + status, + status_text: statusText, + time_ms: dt, + contentType: "text/html", + bytes: dom.htmlLen || 0, + render_mode: "rendered", + headers: mainHeaders, + domExtract: { + title: finalTitle, + metaDesc: dom.metaDesc || "", + canonical: dom.canonical || "", + robotsMeta: dom.robotsMeta || "", + lang: dom.lang || "", + noindex, nofollow, + h1_1: dom.h1?.[0] || "", + h1_2: dom.h1?.[1] || "", + h2_1: dom.h2?.[0] || "", + h2_2: dom.h2?.[1] || "", + totalHeadings: dom.totalHeadings || 0, + links: new Set((dom.links || []).map(l => l.abs)), + rawLinks: dom.links || [], + bodyText: dom.bodyText || "", + schemaTypes: Array.from(new Set(schemaTypes)) + } + }; + } finally { + await page.close(); + } +} + +/* ------------------------- render decision ------------------------------ */ +function shouldRender(currentUrl, httpRes, parsed, homeTitle) { + const { pathname } = new URL(currentUrl); + if ((httpRes.bytes ?? 0) < 4000) return true; // tiny HTML shell + if (parsed.totalHeadings === 0) return true; + if (homeTitle && parsed.title && parsed.title === homeTitle && pathname !== "/") return true; + return false; +} +function withWWW(urlStr) { + try { const u = new URL(urlStr); if (!u.hostname.startsWith("www.")) u.hostname = "www." + u.hostname; return u.toString(); } + catch { return urlStr; } +} + +/* ------------------------ per-page enrichers ---------------------------- */ +function measurePixelWidth(text, size = 16, font = "arial") { + if (!text) return 0; + try { return pixelWidth(text, { font, size }); } catch { return Math.round(text.length * size * 0.5); } +} +function computeReadability(text) { + if (!text) return {}; + const safe = text.slice(0, 200000); // cap + const out = {}; + try { out.flesch_reading_ease = readability.fleschReadingEase(safe); } catch { } + try { out.flesch_kincaid_grade = readability.fleschKincaidGrade(safe); } catch { } + try { out.gunning_fog = readability.gunningFog(safe); } catch { } + try { out.coleman_liau = readability.colemanLiauIndex(safe); } catch { } + try { out.ari = readability.automatedReadabilityIndex(safe); } catch { } + try { out.smog = readability.smogIndex(safe); } catch { } + return out; +} + +/* -------------------------------- main ---------------------------------- */ +// async function crawl(startUrl, maxPages = 50) { + +export async function crawl(startUrl, maxPages = 50) { + const start = normalizeUrl(startUrl, { stripHash: true }); + queue.push(start); + + // Seed from sitemap.xml + record provenance + try { + const sitemapUrls = await getSitemapUrls(start); + for (const u of sitemapUrls) { + queue.push(u); + addEdge("sitemap.xml", u, u, "sitemap"); + } + console.log(`📌 Seeded ${sitemapUrls.length} URL(s) from sitemap.xml`); + } catch (e) { + console.log("⚠️ Sitemap step skipped:", e.message); + } + + let shared = null; + async function getShared() { if (!shared) shared = await createBrowserContext(); return shared; } + + let homeTitle = null; + + while (queue.length > 0 && visited.size < maxPages) { + const url = queue.shift(); + if (!url) continue; + + const normUrl = normalizeUrl(url, { stripHash: true }); + if (visited.has(normUrl)) continue; + visited.add(normUrl); + + let attemptUrls = [normUrl]; + let usedWWWRetry = false; + + for (let attempt = 0; attempt < attemptUrls.length; attempt++) { + const currentUrl = attemptUrls[attempt]; + try { + // 1) HTTP fetch + let pageRes = await fetchWithGot(currentUrl); + + let parsed = { + title: "", metaDesc: "", h1_1: "", h1_2: "", h2_1: "", h2_2: "", + totalHeadings: 0, canonical: "", robotsMeta: "", noindex: false, nofollow: false, + internalLinks: new Set(), rawLinks: [], + lang: "", wordCount: 0, bodyText: "", schemaTypes: [] + }; + if (pageRes.contentType.includes("text/html")) { + const p = parseHtml(pageRes.body || "", currentUrl); + parsed = { ...parsed, ...p }; + } + + if (!homeTitle && new URL(currentUrl).pathname === "/") { + homeTitle = parsed.title || ""; + } + + // 2) Render if needed + if (pageRes.contentType.includes("text/html") && shouldRender(currentUrl, pageRes, parsed, homeTitle)) { + const s = await getShared(); + const rendered = await fetchWithPlaywrightAndExtract(currentUrl, s); + if (rendered.domExtract) { + pageRes = { ...rendered, body: null }; + parsed = { + ...parsed, + title: rendered.domExtract.title, + metaDesc: rendered.domExtract.metaDesc, + h1_1: rendered.domExtract.h1_1, + h1_2: rendered.domExtract.h1_2, + h2_1: rendered.domExtract.h2_1, + h2_2: rendered.domExtract.h2_2, + totalHeadings: rendered.domExtract.totalHeadings, + canonical: rendered.domExtract.canonical, + robotsMeta: rendered.domExtract.robotsMeta, + noindex: rendered.domExtract.noindex, + nofollow: rendered.domExtract.nofollow, + internalLinks: rendered.domExtract.links, + rawLinks: rendered.domExtract.rawLinks, + lang: rendered.domExtract.lang || parsed.lang, + bodyText: rendered.domExtract.bodyText || parsed.bodyText, + wordCount: (rendered.domExtract.bodyText || "").split(/\s+/).filter(Boolean).length, + schemaTypes: rendered.domExtract.schemaTypes + }; + } + } + + // If still looks empty, try www once + if (!usedWWWRetry && parsed.totalHeadings === 0 && !parsed.h1_1) { + attemptUrls.push(withWWW(currentUrl)); + usedWWWRetry = true; + continue; + } + + // Enqueue internal links + record provenance + for (const link of parsed.internalLinks) { + if (isInternal(start, link)) { + const ln = normalizeUrl(link, { stripHash: true }); + const rawMatch = (parsed.rawLinks || []).find(r => r.abs === link)?.raw ?? ""; + addEdge(currentUrl, rawMatch, ln, pageRes.render_mode); + if (!visited.has(ln)) queue.push(ln); + } + } + + // ---- Per-page metrics & enrichers ---- + const title = parsed.title || ""; + const metaDesc = parsed.metaDesc || ""; + const h1_1 = parsed.h1_1 || ""; + const h1_2 = parsed.h1_2 || ""; + const lang = parsed.lang || ""; + const bodyText = parsed.bodyText || ""; + const wordCount = parsed.wordCount || (bodyText ? bodyText.split(/\s+/).filter(Boolean).length : 0); + + const titlePx = measurePixelWidth(title, 16, "arial"); + const descPx = measurePixelWidth(metaDesc, 14, "arial"); + const h1_1_px = measurePixelWidth(h1_1, 24, "arial"); + const h1_2_px = measurePixelWidth(h1_2, 24, "arial"); + + const read = computeReadability(bodyText); + + const headers = pageRes.headers || {}; + const xRobots = (headers["x-robots-tag"] || headers["x-robots-tag".toLowerCase()]) ?? ""; + const lastModified = headers["last-modified"] ?? headers["Last-Modified"] ?? ""; + const setCookie = !!headers["set-cookie"]; + + const outlinks = parsed.internalLinks.size; + const inlinks = (referrers.get(currentUrl) || []).length; + + // Save page row + results.push({ + url: currentUrl, + status: pageRes.status, + status_text: pageRes.status_text ?? "", + time_ms: pageRes.time_ms, + bytes: pageRes.bytes, + content_type: pageRes.contentType, + http_version: pageRes.httpVersion ?? "", + title, + title_length: title.length, + title_pixel_width: titlePx, + meta_description: metaDesc, + meta_description_length: metaDesc.length, + meta_description_pixel_width: descPx, + h1_1, + h1_1_length: h1_1.length, + h1_1_pixel_width: h1_1_px, + h1_2, + h1_2_length: h1_2.length, + h1_2_pixel_width: h1_2_px, + h2_1: parsed.h2_1 || "", + h2_2: parsed.h2_2 || "", + canonical: parsed.canonical, + robots_meta: parsed.robotsMeta, + x_robots_tag: Array.isArray(xRobots) ? xRobots.join("; ") : xRobots, + noindex: parsed.noindex, + nofollow: parsed.nofollow, + lang, + word_count: wordCount, + flesch_reading_ease: read.flesch_reading_ease ?? "", + flesch_kincaid_grade: read.flesch_kincaid_grade ?? "", + gunning_fog: read.gunning_fog ?? "", + coleman_liau: read.coleman_liau ?? "", + ari: read.ari ?? "", + smog: read.smog ?? "", + schema_types: parsed.schemaTypes || [], + inlinks, + outlinks, + render_mode: pageRes.render_mode, + last_modified: lastModified, + set_cookie: setCookie, + crawl_timestamp: new Date().toISOString() + }); + + console.log( + `[${pageRes.status ?? "ERR"}] ${pageRes.time_ms}ms ${String(pageRes.render_mode).padEnd(8)} H:${parsed.totalHeadings} ${currentUrl} ${title || h1_1}` + ); + break; // success for this URL; stop attempts + } catch (err) { + console.error(`[ERROR] ${currentUrl} -> ${err.message}`); + results.push({ + url: currentUrl, + status: null, status_text: "", time_ms: null, bytes: null, content_type: "", + http_version: "", title: "", title_length: 0, title_pixel_width: "", + meta_description: "", meta_description_length: 0, meta_description_pixel_width: "", + h1_1: "", h1_1_length: 0, h1_1_pixel_width: "", h1_2: "", h1_2_length: 0, h1_2_pixel_width: "", + h2_1: "", h2_2: "", + canonical: "", robots_meta: "", x_robots_tag: "", noindex: false, nofollow: false, + lang: "", word_count: "", flesch_reading_ease: "", flesch_kincaid_grade: "", + gunning_fog: "", coleman_liau: "", ari: "", smog: "", + schema_types: [], inlinks: 0, outlinks: 0, render_mode: "error", + last_modified: "", set_cookie: "", crawl_timestamp: new Date().toISOString() + }); + } + } + } + + if (shared) await shared.browser.close(); + + // -------------------- Post-process: duplicates & similarity ------------- + // Titles + const titleMap = new Map(); + for (const r of results) { + const key = (r.title || "").trim(); + if (!titleMap.has(key)) titleMap.set(key, []); + titleMap.get(key).push(r); + } + for (const [t, arr] of titleMap.entries()) { + if (!t) continue; + const isDup = arr.length > 1; + for (const row of arr) row.duplicate_title_exact = isDup ? "yes" : "no"; + } + + // Meta descriptions + const descMap = new Map(); + for (const r of results) { + const key = (r.meta_description || "").trim(); + if (!descMap.has(key)) descMap.set(key, []); + descMap.get(key).push(r); + } + for (const [d, arr] of descMap.entries()) { + if (!d) continue; + const isDup = arr.length > 1; + for (const row of arr) row.duplicate_description_exact = isDup ? "yes" : "no"; + } + + // Nearest neighbor similarities (within site, lightweight) + const titleList = results.map(r => ({ url: r.url, text: (r.title || "").trim() })); + const descList = results.map(r => ({ url: r.url, text: (r.meta_description || "").trim() })); + for (const r of results) { + // titles + const others = titleList.filter(x => x.url !== r.url && x.text); + let bestT = { rating: 0, target: "" }; + if (r.title && others.length) { + const ratings = stringSimilarity.findBestMatch(r.title, others.map(x => x.text)); + const best = ratings.bestMatch; + bestT.rating = best.rating; + const idx = ratings.ratings.findIndex(x => x.rating === best.rating); + bestT.target = others[idx]?.url || ""; + } + r.nearest_title_similarity = bestT.rating ? bestT.rating.toFixed(3) : ""; + r.nearest_title_url = bestT.target; + + // descriptions + const othersD = descList.filter(x => x.url !== r.url && x.text); + let bestD = { rating: 0, target: "" }; + if (r.meta_description && othersD.length) { + const ratingsD = stringSimilarity.findBestMatch(r.meta_description, othersD.map(x => x.text)); + const best = ratingsD.bestMatch; + bestD.rating = best.rating; + const idx = ratingsD.ratings.findIndex(x => x.rating === best.rating); + bestD.target = othersD[idx]?.url || ""; + } + r.nearest_description_similarity = bestD.rating ? bestD.rating.toFixed(3) : ""; + r.nearest_description_url = bestD.target; + } + + console.log(`\n✅ Crawl finished. Total pages: ${visited.size}`); + writePageReports(results); + writeLinkEdges(edges); + writeErrors(results); +} + +// // CLI: node crawler.js https://site.com 200 +// const START_URL = process.argv[2] || "https://example.com"; +// const MAX_PAGES = Number(process.argv[3] || 100); +// crawl(START_URL, MAX_PAGES); diff --git a/crawler.js b/crawler.js new file mode 100644 index 0000000..aa93be8 --- /dev/null +++ b/crawler.js @@ -0,0 +1,921 @@ +// crawler.js +import got from "got"; +import * as cheerio from "cheerio"; +import normalizeUrl from "normalize-url"; +import { isInternal } from "./utils/urlHelpers.js"; +import { getSitemapUrls } from "./utils/sitemap.js"; +import fs from "node:fs"; +import path from "node:path"; +import { chromium } from "playwright"; + +// NEW libs +import pixelWidth from "string-pixel-width"; +import * as readability from "text-readability"; +import stringSimilarity from "string-similarity"; + +/* ------------------------------ globals --------------------------------- */ +// NOTE: We'll reset these at the start of crawl() so repeated runs don't share state. +const visited = new Set(); +const queue = []; +const results = []; + +// Link provenance: every discovered edge (source -> target) +const edges = []; // { from, raw_href, to, discovered_by } + +// Quick referrer map for error report +const referrers = new Map(); // url -> Array<{from, raw_href, discovered_by}> + +const REAL_UA = + "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0.0.0 Safari/537.36"; +const REAL_HEADERS = { + "user-agent": REAL_UA, + accept: + "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8", + "accept-language": "en-US,en;q=0.9", + "upgrade-insecure-requests": "1", +}; + +/* ------------------------------ utils ----------------------------------- */ +function csvEscape(v) { + if (v === undefined || v === null) return ""; + const s = String(v); + return /[",\n]/.test(s) ? `"${s.replace(/"/g, '""')}"` : s; +} +function ensureDir(dir) { + if (!fs.existsSync(dir)) fs.mkdirSync(dir, { recursive: true }); +} +function writePageReports(rows) { + ensureDir("reports"); + const stamp = new Date().toISOString().replace(/[:T]/g, "-").slice(0, 19); + const base = path.join("reports", `crawl-${stamp}`); + + fs.writeFileSync(`${base}.json`, JSON.stringify(rows, null, 2), "utf8"); + console.log(`\n📝 Full JSON report saved: ${base}.json`); + + // Columns (a Screaming-Frog-ish shape with our extras) + const headers = [ + "url", + "status", + "status_text", + "time_ms", + "bytes", + "content_type", + "http_version", + "title", + "title_length", + "title_pixel_width", + "meta_description", + "meta_description_length", + "meta_description_pixel_width", + "h1_1", + "h1_1_length", + "h1_1_pixel_width", + "h1_2", + "h1_2_length", + "h1_2_pixel_width", + "h2_1", + "h2_2", + "canonical", + "robots_meta", + "x_robots_tag", + "noindex", + "nofollow", + "lang", + "word_count", + "flesch_reading_ease", + "flesch_kincaid_grade", + "gunning_fog", + "coleman_liau", + "ari", + "smog", + "schema_types", + "inlinks", + "outlinks", + "render_mode", + "last_modified", + "set_cookie", + "crawl_timestamp", + "duplicate_title_exact", + "nearest_title_similarity", + "nearest_title_url", + "duplicate_description_exact", + "nearest_description_similarity", + "nearest_description_url", + ]; + const lines = [headers.join(",")]; + for (const r of rows) { + lines.push( + [ + r.url, + r.status, + r.status_text ?? "", + r.time_ms, + r.bytes, + r.content_type, + r.http_version ?? "", + r.title, + r.title_length, + r.title_pixel_width, + r.meta_description, + r.meta_description_length, + r.meta_description_pixel_width, + r.h1_1 ?? "", + r.h1_1_length ?? 0, + r.h1_1_pixel_width ?? "", + r.h1_2 ?? "", + r.h1_2_length ?? 0, + r.h1_2_pixel_width ?? "", + r.h2_1 ?? "", + r.h2_2 ?? "", + r.canonical, + r.robots_meta, + r.x_robots_tag ?? "", + r.noindex, + r.nofollow, + r.lang ?? "", + r.word_count ?? "", + r.flesch_reading_ease ?? "", + r.flesch_kincaid_grade ?? "", + r.gunning_fog ?? "", + r.coleman_liau ?? "", + r.ari ?? "", + r.smog ?? "", + Array.isArray(r.schema_types) ? r.schema_types.join("|") : "", + r.inlinks ?? 0, + r.outlinks ?? 0, + r.render_mode, + r.last_modified ?? "", + r.set_cookie ? "yes" : "no", + r.crawl_timestamp ?? "", + r.duplicate_title_exact ?? "", + r.nearest_title_similarity ?? "", + r.nearest_title_url ?? "", + r.duplicate_description_exact ?? "", + r.nearest_description_similarity ?? "", + r.nearest_description_url ?? "", + ] + .map(csvEscape) + .join(",") + ); + } + // If you also want CSV persisted, uncomment: + // fs.writeFileSync(`${base}.csv`, lines.join("\n"), "utf8"); + // console.log(`📝 CSV report saved: ${base}.csv`); + + return { json: path.resolve(`${base}.json`) /*, csv: path.resolve(`${base}.csv`)*/ }; +} +function writeLinkEdges(edges) { + ensureDir("reports"); + const stamp = new Date().toISOString().replace(/[:T]/g, "-").slice(0, 19); + const file = path.join("reports", `links-${stamp}.csv`); + const headers = ["from", "raw_href", "to", "discovered_by"]; + const lines = [headers.join(",")]; + for (const e of edges) { + lines.push([e.from, e.raw_href, e.to, e.discovered_by].map(csvEscape).join(",")); + } + fs.writeFileSync(file, lines.join("\n"), "utf8"); + console.log(`🔗 Link provenance saved: ${file}`); + return { linksCsv: path.resolve(file) }; +} +function writeErrors(rows) { + ensureDir("reports"); + const stamp = new Date().toISOString().replace(/[:T]/g, "-").slice(0, 19); + const file = path.join("reports", `errors-${stamp}.csv`); + const headers = ["url", "status", "title", "from_page", "raw_href", "discovered_by"]; + const lines = [headers.join(",")]; + + for (const r of rows) { + if (r && r.status !== null && r.status >= 400) { + const refs = referrers.get(r.url) || []; + if (refs.length === 0) { + lines.push([r.url, r.status, r.title, "", "", ""].map(csvEscape).join(",")); + } else { + for (const ref of refs) { + lines.push([r.url, r.status, r.title, ref.from, ref.raw_href, ref.discovered_by].map(csvEscape).join(",")); + } + } + } + } + fs.writeFileSync(file, lines.join("\n"), "utf8"); + console.log(`❗ Error report saved: ${file}`); + return { errorsCsv: path.resolve(file) }; +} +function addEdge(from, rawHref, to, discovered_by) { + edges.push({ from, raw_href: rawHref || "", to, discovered_by }); + if (!referrers.has(to)) referrers.set(to, []); + referrers.get(to).push({ from, raw_href: rawHref || "", discovered_by }); +} + +/* ---------------------- parse HTML without JS --------------------------- */ +function safeJsonParse(txt) { + try { + return JSON.parse(txt); + } catch { + return null; + } +} +function parseSchemaTypes($) { + const types = new Set(); + $('script[type="application/ld+json"]').each((_, el) => { + const raw = $(el).contents().text(); + const parsed = safeJsonParse(raw); + if (!parsed) return; + const collect = (obj) => { + if (!obj) return; + if (Array.isArray(obj)) { + obj.forEach(collect); + return; + } + if (typeof obj === "object") { + const t = obj["@type"]; + if (typeof t === "string") types.add(t); + else if (Array.isArray(t)) t.forEach((x) => typeof x === "string" && types.add(x)); + // nested + Object.values(obj).forEach(collect); + } + }; + collect(parsed); + }); + return [...types]; +} +function parseHtml(html, url) { + const $ = cheerio.load(html); + + let title = ($("title").first().text() || "").trim(); + const ogTitle = $('meta[property="og:title"]').attr("content") || ""; + const twTitle = $('meta[name="twitter:title"]').attr("content") || ""; + + // Headings (capture top two H1s and H2s) + const h1s = $("h1") + .map((_, el) => $(el).text().trim()) + .get(); + const h2s = $("h2") + .map((_, el) => $(el).text().trim()) + .get(); + + const h1_1 = h1s[0] || ""; + const h1_2 = h1s[1] || ""; + const h2_1 = h2s[0] || ""; + const h2_2 = h2s[1] || ""; + + const totalHeadings = $("h1,h2,h3,h4,h5,h6,[role='heading']").length; + + if (!title) title = (ogTitle || twTitle || h1_1 || "").trim(); + + const metaDesc = ($('meta[name="description"]').attr("content") || "").trim(); + const canonical = ($('link[rel="canonical"]').attr("href") || "").trim(); + const robotsMeta = ($('meta[name="robots"]').attr("content") || "").trim(); + const robotsLower = robotsMeta.toLowerCase(); + const noindex = /(^|[,;\s])noindex([,;\s]|$)/.test(robotsLower); + const nofollow = /(^|[,;\s])nofollow([,;\s]|$)/.test(robotsLower); + + const lang = ($("html").attr("lang") || "").trim(); + + // Basic text body for word count / readability + const bodyText = ($("main").text() || $("body").text() || "").replace(/\s+/g, " ").trim(); + const wordCount = bodyText ? bodyText.split(/\s+/).length : 0; + + // Internal links + raw href + const internalLinks = new Set(); + const rawLinks = []; + $("a[href]").each((_, el) => { + const href = $(el).attr("href"); + if (!href) return; + try { + const abs = new URL(href, url).toString(); + rawLinks.push({ raw: href, abs }); + internalLinks.add(abs); + } catch {} + }); + + // Schema.org JSON-LD types + const schemaTypes = parseSchemaTypes($); + + return { + title, + metaDesc, + h1_1, + h1_2, + h2_1, + h2_2, + totalHeadings, + canonical, + robotsMeta, + noindex, + nofollow, + internalLinks, + rawLinks, + lang, + wordCount, + schemaTypes, + bodyText, + }; +} + +/* ------------------------------ fetchers -------------------------------- */ +async function fetchWithGot(url) { + const t0 = Date.now(); + const res = await got(url, { + timeout: { request: 20000 }, + throwHttpErrors: false, + headers: REAL_HEADERS, + http2: false, + }); + const dt = Date.now() - t0; + const contentType = (res.headers["content-type"] || "").toLowerCase(); + const bytes = res.headers["content-length"] + ? Number(res.headers["content-length"]) + : Buffer.byteLength(res.body || "", "utf8"); + + return { + status: res.statusCode ?? null, + status_text: res.statusMessage ?? "", + time_ms: dt, + contentType, + body: res.body, + bytes, + render_mode: "http", + httpVersion: res.httpVersion ?? "", + headers: res.headers, + }; +} + +async function createBrowserContext() { + const browser = await chromium.launch({ + headless: true, + args: ["--disable-blink-features=AutomationControlled"], + }); + const context = await browser.newContext({ + ignoreHTTPSErrors: true, // Ignore SSL certificate errors + userAgent: REAL_UA, + viewport: { width: 1366, height: 768 }, + deviceScaleFactor: 1, + isMobile: false, + locale: "en-US", + extraHTTPHeaders: REAL_HEADERS, + }); + await context.addInitScript(() => { + Object.defineProperty(navigator, "webdriver", { get: () => false }); + Object.defineProperty(navigator, "plugins", { get: () => [1, 2, 3] }); + Object.defineProperty(navigator, "languages", { get: () => ["en-US", "en"] }); + }); + return { browser: context.browser(), context }; +} + +async function fetchWithPlaywrightAndExtract(url, shared) { + const page = await shared.context.newPage(); + const t0 = Date.now(); + let status = null, + mainHeaders = {}, + statusText = ""; + + try { + const resp = await page.goto(url, { waitUntil: "domcontentloaded", timeout: 30000 }); + status = resp?.status() ?? null; + statusText = resp?.statusText() ?? ""; + try { + mainHeaders = resp ? await resp.headers() : {}; + } catch {} + + try { + await page.waitForLoadState("networkidle", { timeout: 12000 }); + } catch {} + try { + await page.waitForFunction( + () => { + const main = document.querySelector("main") || document.body; + const textLen = (main?.innerText || "").replace(/\s+/g, " ").trim().length; + const hasHeading = !!document.querySelector( + "h1, h2, [role='heading'], [class*='title'], [class*='heading'], [class*='hero'], [class*='banner']" + ); + return textLen > 160 || hasHeading; + }, + { timeout: 8000 } + ); + } catch {} + + const dom = await page.evaluate(() => { + const clean = (s) => (s || "").replace(/\s+/g, " ").trim(); + const getTextList = (sel) => + Array.from(document.querySelectorAll(sel)) + .map((el) => clean(el.textContent)) + .filter(Boolean); + + const title = document.title || ""; + const ogTitle = document.querySelector('meta[property="og:title"]')?.content || ""; + const twTitle = document.querySelector('meta[name="twitter:title"]')?.content || ""; + const metaDesc = document.querySelector('meta[name="description"]')?.content || ""; + const canonical = document.querySelector('link[rel="canonical"]')?.href || ""; + const robotsMeta = document.querySelector('meta[name="robots"]')?.content || ""; + const lang = document.documentElement.getAttribute("lang") || ""; + + const h1 = getTextList("h1"); + const h2 = getTextList("h2"); + const totalHeadings = document.querySelectorAll("h1,h2,h3,h4,h5,h6,[role='heading']").length; + + const links = Array.from(document.querySelectorAll("a[href]")) + .map((a) => { + const raw = a.getAttribute("href"); + try { + return { raw, abs: new URL(raw, location.href).toString() }; + } catch { + return null; + } + }) + .filter(Boolean); + + const bodyText = clean((document.querySelector("main") || document.body).innerText || ""); + + const schemaScripts = Array.from( + document.querySelectorAll('script[type="application/ld+json"]') + ).map((s) => s.textContent || ""); + + return { + htmlLen: (document.documentElement.outerHTML || "").length, + title, + ogTitle, + twTitle, + metaDesc, + canonical, + robotsMeta, + lang, + h1, + h2, + totalHeadings, + links, + bodyText, + schemaScripts, + }; + }); + + // Parse schema types from strings (outside of page) + const schemaTypes = []; + for (const raw of dom.schemaScripts || []) { + try { + const parsed = JSON.parse(raw); + const collect = (obj) => { + if (!obj) return; + if (Array.isArray(obj)) { + obj.forEach(collect); + return; + } + if (typeof obj === "object") { + const t = obj["@type"]; + if (typeof t === "string") schemaTypes.push(t); + else if (Array.isArray(t)) t.forEach((x) => typeof x === "string" && schemaTypes.push(x)); + Object.values(obj).forEach(collect); + } + }; + collect(parsed); + } catch {} + } + + const dt = Date.now() - t0; + const robotsLower = (dom.robotsMeta || "").toLowerCase(); + const noindex = /(^|[,;\s])noindex([,;\s]|$)/.test(robotsLower); + const nofollow = /(^|[,;\s])nofollow([,;\s]|$)/.test(robotsLower); + const finalTitle = (dom.title || dom.ogTitle || dom.twTitle || dom.h1?.[0] || "").trim(); + + return { + status, + status_text: statusText, + time_ms: dt, + contentType: "text/html", + bytes: dom.htmlLen || 0, + render_mode: "rendered", + headers: mainHeaders, + domExtract: { + title: finalTitle, + metaDesc: dom.metaDesc || "", + canonical: dom.canonical || "", + robotsMeta: dom.robotsMeta || "", + lang: dom.lang || "", + noindex, + nofollow, + h1_1: dom.h1?.[0] || "", + h1_2: dom.h1?.[1] || "", + h2_1: dom.h2?.[0] || "", + h2_2: dom.h2?.[1] || "", + totalHeadings: dom.totalHeadings || 0, + links: new Set((dom.links || []).map((l) => l.abs)), + rawLinks: dom.links || [], + bodyText: dom.bodyText || "", + schemaTypes: Array.from(new Set(schemaTypes)), + }, + }; + } finally { + await page.close(); + } +} + +/* ------------------------- render decision ------------------------------ */ +function shouldRender(currentUrl, httpRes, parsed, homeTitle) { + const { pathname } = new URL(currentUrl); + if ((httpRes.bytes ?? 0) < 4000) return true; // tiny HTML shell + if (parsed.totalHeadings === 0) return true; + if (homeTitle && parsed.title && parsed.title === homeTitle && pathname !== "/") return true; + return false; +} +function withWWW(urlStr) { + try { + const u = new URL(urlStr); + if (!u.hostname.startsWith("www.")) u.hostname = "www." + u.hostname; + return u.toString(); + } catch { + return urlStr; + } +} + +/* ------------------------ per-page enrichers ---------------------------- */ +function measurePixelWidth(text, size = 16, font = "arial") { + if (!text) return 0; + try { + return pixelWidth(text, { font, size }); + } catch { + return Math.round(text.length * size * 0.5); + } +} +function computeReadability(text) { + if (!text) return {}; + const safe = text.slice(0, 200000); // cap + const out = {}; + try { + out.flesch_reading_ease = readability.fleschReadingEase(safe); + } catch {} + try { + out.flesch_kincaid_grade = readability.fleschKincaidGrade(safe); + } catch {} + try { + out.gunning_fog = readability.gunningFog(safe); + } catch {} + try { + out.coleman_liau = readability.colemanLiauIndex(safe); + } catch {} + try { + out.ari = readability.automatedReadabilityIndex(safe); + } catch {} + try { + out.smog = readability.smogIndex(safe); + } catch {} + return out; +} + +/* -------------------------------- main ---------------------------------- */ +/** + * Crawl a site and return a structured report. + * @param {string} startUrl + * @param {number} maxPages + * @param {(tick:any)=>void} [onProgress] optional callback for progress events + * @param {{persistReports?: boolean, collectPages?: boolean}} [options] + * @returns {{ results: any[], files: Record, total: number }} + */ +export async function crawl(startUrl, maxPages = 50, onProgress, options = {}) { + const persistReports = options.persistReports !== false; // default true + + // Reset global state per run + visited.clear(); + queue.length = 0; + results.length = 0; + edges.length = 0; + referrers.clear(); + + const start = normalizeUrl(startUrl, { stripHash: true }); + queue.push(start); + + // Seed from sitemap.xml + record provenance + try { + const sitemapUrls = await getSitemapUrls(start); + for (const u of sitemapUrls) { + queue.push(u); + addEdge("sitemap.xml", u, u, "sitemap"); + } + console.log(`📌 Seeded ${sitemapUrls.length} URL(s) from sitemap.xml`); + } catch (e) { + console.log("⚠️ Sitemap step skipped:", e.message); + } + + let shared = null; + async function getShared() { + if (!shared) shared = await createBrowserContext(); + return shared; + } + + let homeTitle = null; + + while (queue.length > 0 && visited.size < maxPages) { + const url = queue.shift(); + if (!url) continue; + + const normUrl = normalizeUrl(url, { stripHash: true }); + if (visited.has(normUrl)) continue; + visited.add(normUrl); + + const attemptUrls = [normUrl]; + let usedWWWRetry = false; + + for (let attempt = 0; attempt < attemptUrls.length; attempt++) { + const currentUrl = attemptUrls[attempt]; + try { + // 1) HTTP fetch + let pageRes = await fetchWithGot(currentUrl); + + let parsed = { + title: "", + metaDesc: "", + h1_1: "", + h1_2: "", + h2_1: "", + h2_2: "", + totalHeadings: 0, + canonical: "", + robotsMeta: "", + noindex: false, + nofollow: false, + internalLinks: new Set(), + rawLinks: [], + lang: "", + wordCount: 0, + bodyText: "", + schemaTypes: [], + }; + if (pageRes.contentType.includes("text/html")) { + const p = parseHtml(pageRes.body || "", currentUrl); + parsed = { ...parsed, ...p }; + } + + if (!homeTitle && new URL(currentUrl).pathname === "/") { + homeTitle = parsed.title || ""; + } + + // 2) Render if needed + if (pageRes.contentType.includes("text/html") && shouldRender(currentUrl, pageRes, parsed, homeTitle)) { + const s = await getShared(); + const rendered = await fetchWithPlaywrightAndExtract(currentUrl, s); + if (rendered.domExtract) { + pageRes = { ...rendered, body: null }; + parsed = { + ...parsed, + title: rendered.domExtract.title, + metaDesc: rendered.domExtract.metaDesc, + h1_1: rendered.domExtract.h1_1, + h1_2: rendered.domExtract.h1_2, + h2_1: rendered.domExtract.h2_1, + h2_2: rendered.domExtract.h2_2, + totalHeadings: rendered.domExtract.totalHeadings, + canonical: rendered.domExtract.canonical, + robotsMeta: rendered.domExtract.robotsMeta, + noindex: rendered.domExtract.noindex, + nofollow: rendered.domExtract.nofollow, + internalLinks: rendered.domExtract.links, + rawLinks: rendered.domExtract.rawLinks, + lang: rendered.domExtract.lang || parsed.lang, + bodyText: rendered.domExtract.bodyText || parsed.bodyText, + wordCount: (rendered.domExtract.bodyText || "") + .split(/\s+/) + .filter(Boolean).length, + schemaTypes: rendered.domExtract.schemaTypes, + }; + } + } + + // If still looks empty, try www once + if (!usedWWWRetry && parsed.totalHeadings === 0 && !parsed.h1_1) { + attemptUrls.push(withWWW(currentUrl)); + usedWWWRetry = true; + continue; + } + + // Enqueue internal links + record provenance + for (const link of parsed.internalLinks) { + if (isInternal(start, link)) { + const ln = normalizeUrl(link, { stripHash: true }); + const rawMatch = (parsed.rawLinks || []).find((r) => r.abs === link)?.raw ?? ""; + addEdge(currentUrl, rawMatch, ln, pageRes.render_mode); + if (!visited.has(ln)) queue.push(ln); + } + } + + // ---- Per-page metrics & enrichers ---- + const title = parsed.title || ""; + const metaDesc = parsed.metaDesc || ""; + const h1_1 = parsed.h1_1 || ""; + const h1_2 = parsed.h1_2 || ""; + const lang = parsed.lang || ""; + const bodyText = parsed.bodyText || ""; + const wordCount = parsed.wordCount || (bodyText ? bodyText.split(/\s+/).filter(Boolean).length : 0); + + const titlePx = measurePixelWidth(title, 16, "arial"); + const descPx = measurePixelWidth(metaDesc, 14, "arial"); + const h1_1_px = measurePixelWidth(h1_1, 24, "arial"); + const h1_2_px = measurePixelWidth(h1_2, 24, "arial"); + + const read = computeReadability(bodyText); + + const headers = pageRes.headers || {}; + const xRobots = (headers["x-robots-tag"] || headers["x-robots-tag".toLowerCase()]) ?? ""; + const lastModified = headers["last-modified"] ?? headers["Last-Modified"] ?? ""; + const setCookie = !!headers["set-cookie"]; + + const outlinks = parsed.internalLinks.size; + const inlinks = (referrers.get(currentUrl) || []).length; + + // Save page row + results.push({ + url: currentUrl, + status: pageRes.status, + status_text: pageRes.status_text ?? "", + time_ms: pageRes.time_ms, + bytes: pageRes.bytes, + content_type: pageRes.contentType, + http_version: pageRes.httpVersion ?? "", + title, + title_length: title.length, + title_pixel_width: titlePx, + meta_description: metaDesc, + meta_description_length: metaDesc.length, + meta_description_pixel_width: descPx, + h1_1, + h1_1_length: h1_1.length, + h1_1_pixel_width: h1_1_px, + h1_2, + h1_2_length: h1_2.length, + h1_2_pixel_width: h1_2_px, + h2_1: parsed.h2_1 || "", + h2_2: parsed.h2_2 || "", + canonical: parsed.canonical, + robots_meta: parsed.robotsMeta, + x_robots_tag: Array.isArray(xRobots) ? xRobots.join("; ") : xRobots, + noindex: parsed.noindex, + nofollow: parsed.nofollow, + lang, + word_count: wordCount, + flesch_reading_ease: read.flesch_reading_ease ?? "", + flesch_kincaid_grade: read.flesch_kincaid_grade ?? "", + gunning_fog: read.gunning_fog ?? "", + coleman_liau: read.coleman_liau ?? "", + ari: read.ari ?? "", + smog: read.smog ?? "", + schema_types: parsed.schemaTypes || [], + inlinks, + outlinks, + render_mode: pageRes.render_mode, + last_modified: lastModified, + set_cookie: setCookie, + crawl_timestamp: new Date().toISOString(), + }); + + console.log( + `[${pageRes.status ?? "ERR"}] ${pageRes.time_ms}ms ${String(pageRes.render_mode).padEnd(8)} H:${parsed.totalHeadings} ${currentUrl} ${ + title || h1_1 + }` + ); + + // optional progress callback (non-fatal) + try { + onProgress?.({ + url: currentUrl, + status: pageRes.status, + title, + inlinks, + outlinks, + visited: visited.size, + queued: queue.length, + }); + } catch {} + + break; // success for this URL; stop attempts + } catch (err) { + console.error(`[ERROR] ${currentUrl} -> ${err.message}`); + results.push({ + url: currentUrl, + status: null, + status_text: "", + time_ms: null, + bytes: null, + content_type: "", + http_version: "", + title: "", + title_length: 0, + title_pixel_width: "", + meta_description: "", + meta_description_length: 0, + meta_description_pixel_width: "", + h1_1: "", + h1_1_length: 0, + h1_1_pixel_width: "", + h1_2: "", + h1_2_length: 0, + h1_2_pixel_width: "", + h2_1: "", + h2_2: "", + canonical: "", + robots_meta: "", + x_robots_tag: "", + noindex: false, + nofollow: false, + lang: "", + word_count: "", + flesch_reading_ease: "", + flesch_kincaid_grade: "", + gunning_fog: "", + coleman_liau: "", + ari: "", + smog: "", + schema_types: [], + inlinks: 0, + outlinks: 0, + render_mode: "error", + last_modified: "", + set_cookie: "", + crawl_timestamp: new Date().toISOString(), + }); + + try { + onProgress?.({ + url: currentUrl, + error: String(err?.message || err), + visited: visited.size, + queued: queue.length, + }); + } catch {} + } + } + } + + if (shared) await shared.browser.close(); + + // -------------------- Post-process: duplicates & similarity ------------- + // Titles + const titleMap = new Map(); + for (const r of results) { + const key = (r.title || "").trim(); + if (!titleMap.has(key)) titleMap.set(key, []); + titleMap.get(key).push(r); + } + for (const [t, arr] of titleMap.entries()) { + if (!t) continue; + const isDup = arr.length > 1; + for (const row of arr) row.duplicate_title_exact = isDup ? "yes" : "no"; + } + + // Meta descriptions + const descMap = new Map(); + for (const r of results) { + const key = (r.meta_description || "").trim(); + if (!descMap.has(key)) descMap.set(key, []); + descMap.get(key).push(r); + } + for (const [d, arr] of descMap.entries()) { + if (!d) continue; + const isDup = arr.length > 1; + for (const row of arr) row.duplicate_description_exact = isDup ? "yes" : "no"; + } + + // Nearest neighbor similarities (within site, lightweight) + const titleList = results.map((r) => ({ url: r.url, text: (r.title || "").trim() })); + const descList = results.map((r) => ({ url: r.url, text: (r.meta_description || "").trim() })); + for (const r of results) { + // titles + const others = titleList.filter((x) => x.url !== r.url && x.text); + let bestT = { rating: 0, target: "" }; + if (r.title && others.length) { + const ratings = stringSimilarity.findBestMatch(r.title, others.map((x) => x.text)); + const best = ratings.bestMatch; + bestT.rating = best.rating; + const idx = ratings.ratings.findIndex((x) => x.rating === best.rating); + bestT.target = others[idx]?.url || ""; + } + r.nearest_title_similarity = bestT.rating ? bestT.rating.toFixed(3) : ""; + r.nearest_title_url = bestT.target; + + // descriptions + const othersD = descList.filter((x) => x.url !== r.url && x.text); + let bestD = { rating: 0, target: "" }; + if (r.meta_description && othersD.length) { + const ratingsD = stringSimilarity.findBestMatch(r.meta_description, othersD.map((x) => x.text)); + const best = ratingsD.bestMatch; + bestD.rating = best.rating; + const idx = ratingsD.ratings.findIndex((x) => x.rating === best.rating); + bestD.target = othersD[idx]?.url || ""; + } + r.nearest_description_similarity = bestD.rating ? bestD.rating.toFixed(3) : ""; + r.nearest_description_url = bestD.target; + } + + console.log(`\n✅ Crawl finished. Total pages: ${visited.size}`); + + let files = {}; + if (persistReports) { + const a = writePageReports(results); + const b = writeLinkEdges(edges); + const c = writeErrors(results); + files = { ...a, ...b, ...c }; + } + + return { results, files, total: results.length }; +} + +// // CLI: node crawler.js https://site.com 200 +// const START_URL = process.argv[2] || "https://example.com"; +// const MAX_PAGES = Number(process.argv[3] || 100); +// crawl(START_URL, MAX_PAGES); diff --git a/middlewares/auth.middleware.js b/middlewares/auth.middleware.js new file mode 100644 index 0000000..fdf98f9 --- /dev/null +++ b/middlewares/auth.middleware.js @@ -0,0 +1,19 @@ +import jwt from "jsonwebtoken"; + +export function authMiddleware(req, res, next) { + const header = req.headers.authorization; + + if (!header?.startsWith("Bearer ")) { + return res.status(401).json({ error: "Missing token" }); + } + + const token = header.split(" ")[1]; + + try { + req.user = jwt.verify(token, process.env.JWT_SECRET); + next(); + } catch (err) { + console.error("JWT verification failed:", err.message); + return res.status(401).json({ error: "Invalid or expired token" }); + } +} diff --git a/middlewares/pageSpeedErrorHandler.js b/middlewares/pageSpeedErrorHandler.js new file mode 100644 index 0000000..3ad04ed --- /dev/null +++ b/middlewares/pageSpeedErrorHandler.js @@ -0,0 +1,4 @@ +export const errorHandler = (err, req, res, next) => { + console.error(err); + res.status(500).json({ message: err.message || 'Internal Server Error' }); +}; diff --git a/models/blog.model.js b/models/blog.model.js new file mode 100644 index 0000000..cadb8a7 --- /dev/null +++ b/models/blog.model.js @@ -0,0 +1,41 @@ +import mongoose from 'mongoose'; + +const commentSchema = new mongoose.Schema({ + user: { type: mongoose.Schema.Types.ObjectId, ref: 'User' }, + name: String, + text: { type: String, required: true }, + createdAt: { type: Date, default: Date.now } +}); + +const blogSchema = new mongoose.Schema({ + projectId: { type: String, required: true, index: true }, + title: { type: String, required: true }, + slug: { type: String, required: true, unique: false }, + description: { type: String, required: true }, + imageUrl: String, + bigImageUrl: String, // ✅ New field + category: { type: mongoose.Schema.Types.ObjectId, ref: 'Category' }, + tags: [String], + comments: [commentSchema], + likes: [{ type: mongoose.Schema.Types.ObjectId, ref: 'User' }], + author: { type: mongoose.Schema.Types.ObjectId, ref: 'User' } +}, { timestamps: true }); + +// 👇 projectId + slug combo unique +blogSchema.index({ projectId: 1, slug: 1 }, { unique: true }); + +// 👇 Add base URL when converting to JSON +blogSchema.set('toJSON', { + transform: (doc, ret) => { + const baseUrl = process.env.BACKEND_URL || 'http://localhost:3010'; + if (ret.imageUrl && !ret.imageUrl.startsWith('http')) { + ret.imageUrl = `${baseUrl}${ret.imageUrl}`; + } + if (ret.bigImageUrl && !ret.bigImageUrl.startsWith('http')) { + ret.bigImageUrl = `${baseUrl}${ret.bigImageUrl}`; + } + return ret; + } +}); + +export default mongoose.model('Blog', blogSchema); diff --git a/models/category.model.js b/models/category.model.js new file mode 100644 index 0000000..eaff001 --- /dev/null +++ b/models/category.model.js @@ -0,0 +1,21 @@ +import mongoose from "mongoose"; + +const categorySchema = new mongoose.Schema( + { + name: { + type: String, + required: true, + unique: true, + trim: true, + }, + projectId: { + type: String, // For multi-project support + required: true, + }, + }, + { timestamps: true } +); + +const Category = mongoose.model("Category", categorySchema); + +export default Category; diff --git a/models/comments.model.js b/models/comments.model.js new file mode 100644 index 0000000..03060e5 --- /dev/null +++ b/models/comments.model.js @@ -0,0 +1,10 @@ +import mongoose from "mongoose"; + +const commentSchema = new mongoose.Schema({ + blog: { type: mongoose.Schema.Types.ObjectId, ref: "Blog" }, + name: String, + text: String, + createdAt: { type: Date, default: Date.now } +}); + +export default mongoose.model("Comment", commentSchema); diff --git a/models/maisondetreats/cakeOrder.model.js b/models/maisondetreats/cakeOrder.model.js new file mode 100644 index 0000000..f7569c2 --- /dev/null +++ b/models/maisondetreats/cakeOrder.model.js @@ -0,0 +1,18 @@ +import mongoose from "mongoose"; + +const CakeOrderSchema = new mongoose.Schema( + { + order: { + type: Object, + required: true, + // Example format: + // { + // "Mini Cakes": { "Thandai Cake": 1, "Mango Cardamom": 1 }, + // "Mithai-Inspired Macarons": { "Mango macarons (pack of 6)": 1, "Pista (pack of 6)": 10 } + // } + }, + }, + { timestamps: true } +); + +export const CakeOrder = mongoose.model("CakeOrder", CakeOrderSchema); diff --git a/models/message.model.js b/models/message.model.js new file mode 100644 index 0000000..3f604d9 --- /dev/null +++ b/models/message.model.js @@ -0,0 +1,12 @@ +import mongoose from "mongoose"; + +const MessageSchema = new mongoose.Schema({ + project: { type: String, required: true }, // new field to identify project + name: { type: String, default: "Guest" }, + email: { type: String }, + message: { type: String, required: true }, +}, { timestamps: true }); + +const Message = mongoose.models.Message || mongoose.model("Message", MessageSchema); + +export default Message; diff --git a/models/pageSpeedTest.model.js b/models/pageSpeedTest.model.js new file mode 100644 index 0000000..36f6ad9 --- /dev/null +++ b/models/pageSpeedTest.model.js @@ -0,0 +1,42 @@ +import mongoose from 'mongoose'; + +const pageSpeedTestSchema = new mongoose.Schema({ + url: { type: String, required: true }, + device: { type: String, enum: ['mobile', 'desktop'], required: true }, + scores: { + performance: Number, + accessibility: Number, + bestPractices: Number, + seo: Number, + pwa: Number, + }, + metrics: { + firstContentfulPaint: String, + largestContentfulPaint: String, + totalBlockingTime: String, + timeToInteractive: String, + speedIndex: String, + cumulativeLayoutShift: String, + }, + opportunities: [ + { + title: String, + description: String, + estimatedSavings: String, + }, + ], + diagnostics: Object, + failedAudits: [ + { + title: String, + description: String, + }, + ], + passedAudits: [String], + notApplicableAudits: [String], + screenshot: String, + treemapPath: { type: String }, + createdAt: { type: Date, default: Date.now }, +}); + +export default mongoose.model('PageSpeedTest', pageSpeedTestSchema); diff --git a/models/payment.model.js b/models/payment.model.js new file mode 100644 index 0000000..aac5aa3 --- /dev/null +++ b/models/payment.model.js @@ -0,0 +1,12 @@ +import mongoose from "mongoose"; + +const paymentSchema = new mongoose.Schema({ + email: { type: String, required: true }, + amount: { type: Number, required: true }, // store in cents + currency: { type: String, default: "usd" }, + stripePaymentIntentId: { type: String }, // ❌ remove required: true + stripeSessionId: { type: String }, // ✅ store Checkout Session ID + status: { type: String, default: "pending" }, // pending, succeeded, failed +}, { timestamps: true }); + +export const Payment = mongoose.model("Payment", paymentSchema); diff --git a/models/user.model.js b/models/user.model.js new file mode 100644 index 0000000..702250d --- /dev/null +++ b/models/user.model.js @@ -0,0 +1,14 @@ +import mongoose from "mongoose"; + +const userSchema = new mongoose.Schema( + { + email: { type: String, required: true, unique: true, lowercase: true }, + passwordHash: { type: String, required: true }, + // ➡️ Add these two lines + resetPasswordToken: { type: String }, + resetPasswordExpires: { type: Date }, + }, + { timestamps: true } +); + +export default mongoose.model("User", userSchema); diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 0000000..a45859b --- /dev/null +++ b/package-lock.json @@ -0,0 +1,5093 @@ +{ + "name": "crawlerx", + "version": "1.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "crawlerx", + "version": "1.0.0", + "dependencies": { + "axios": "^1.12.2", + "bcrypt": "^6.0.0", + "cheerio": "^1.1.0", + "chrome-launcher": "^1.2.1", + "cors": "^2.8.5", + "dotenv": "^17.2.2", + "express": "^5.1.0", + "got": "^14.4.7", + "jsonwebtoken": "^9.0.2", + "lighthouse": "^12.8.2", + "mongoose": "^8.18.1", + "multer": "^2.0.2", + "nodemailer": "^7.0.6", + "normalize-url": "^8.0.2", + "sitemapper": "^3.2.7", + "slugify": "^1.6.6", + "string-pixel-width": "^1.11.0", + "string-similarity": "^4.0.4", + "stripe": "^18.5.0", + "text-readability": "^1.1.1" + }, + "devDependencies": { + "nodemon": "^3.1.10", + "playwright": "^1.55.0" + } + }, + "node_modules/@formatjs/ecma402-abstract": { + "version": "2.3.4", + "resolved": "https://registry.npmjs.org/@formatjs/ecma402-abstract/-/ecma402-abstract-2.3.4.tgz", + "integrity": "sha512-qrycXDeaORzIqNhBOx0btnhpD1c+/qFIHAN9znofuMJX6QBwtbrmlpWfD4oiUUD2vJUOIYFA/gYtg2KAMGG7sA==", + "license": "MIT", + "dependencies": { + "@formatjs/fast-memoize": "2.2.7", + "@formatjs/intl-localematcher": "0.6.1", + "decimal.js": "^10.4.3", + "tslib": "^2.8.0" + } + }, + "node_modules/@formatjs/fast-memoize": { + "version": "2.2.7", + "resolved": "https://registry.npmjs.org/@formatjs/fast-memoize/-/fast-memoize-2.2.7.tgz", + "integrity": "sha512-Yabmi9nSvyOMrlSeGGWDiH7rf3a7sIwplbvo/dlz9WCIjzIQAfy1RMf4S0X3yG724n5Ghu2GmEl5NJIV6O9sZQ==", + "license": "MIT", + "dependencies": { + "tslib": "^2.8.0" + } + }, + "node_modules/@formatjs/icu-messageformat-parser": { + "version": "2.11.2", + "resolved": "https://registry.npmjs.org/@formatjs/icu-messageformat-parser/-/icu-messageformat-parser-2.11.2.tgz", + "integrity": "sha512-AfiMi5NOSo2TQImsYAg8UYddsNJ/vUEv/HaNqiFjnI3ZFfWihUtD5QtuX6kHl8+H+d3qvnE/3HZrfzgdWpsLNA==", + "license": "MIT", + "dependencies": { + "@formatjs/ecma402-abstract": "2.3.4", + "@formatjs/icu-skeleton-parser": "1.8.14", + "tslib": "^2.8.0" + } + }, + "node_modules/@formatjs/icu-skeleton-parser": { + "version": "1.8.14", + "resolved": "https://registry.npmjs.org/@formatjs/icu-skeleton-parser/-/icu-skeleton-parser-1.8.14.tgz", + "integrity": "sha512-i4q4V4qslThK4Ig8SxyD76cp3+QJ3sAqr7f6q9VVfeGtxG9OhiAk3y9XF6Q41OymsKzsGQ6OQQoJNY4/lI8TcQ==", + "license": "MIT", + "dependencies": { + "@formatjs/ecma402-abstract": "2.3.4", + "tslib": "^2.8.0" + } + }, + "node_modules/@formatjs/intl-localematcher": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/@formatjs/intl-localematcher/-/intl-localematcher-0.6.1.tgz", + "integrity": "sha512-ePEgLgVCqi2BBFnTMWPfIghu6FkbZnnBVhO2sSxvLfrdFw7wCHAHiDoM2h4NRgjbaY7+B7HgOLZGkK187pZTZg==", + "license": "MIT", + "dependencies": { + "tslib": "^2.8.0" + } + }, + "node_modules/@mongodb-js/saslprep": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/@mongodb-js/saslprep/-/saslprep-1.3.0.tgz", + "integrity": "sha512-zlayKCsIjYb7/IdfqxorK5+xUMyi4vOKcFy10wKJYc63NSdKI8mNME+uJqfatkPmOSMMUiojrL58IePKBm3gvQ==", + "license": "MIT", + "dependencies": { + "sparse-bitfield": "^3.0.3" + } + }, + "node_modules/@opentelemetry/api": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/api/-/api-1.9.0.tgz", + "integrity": "sha512-3giAOQvZiH5F9bMlMiv8+GSPMeqg0dbaeo58/0SlA9sxSqZhnUtxzX9/2FzyhS9sWQf5S0GJE0AKBrFqjpeYcg==", + "license": "Apache-2.0", + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/@opentelemetry/api-logs": { + "version": "0.57.2", + "resolved": "https://registry.npmjs.org/@opentelemetry/api-logs/-/api-logs-0.57.2.tgz", + "integrity": "sha512-uIX52NnTM0iBh84MShlpouI7UKqkZ7MrUszTmaypHBu4r7NofznSnQRfJ+uUeDtQDj6w8eFGg5KBLDAwAPz1+A==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/api": "^1.3.0" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/@opentelemetry/context-async-hooks": { + "version": "1.30.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/context-async-hooks/-/context-async-hooks-1.30.1.tgz", + "integrity": "sha512-s5vvxXPVdjqS3kTLKMeBMvop9hbWkwzBpu+mUO2M7sZtlkyDJGwFe33wRKnbaYDo8ExRVBIIdwIGrqpxHuKttA==", + "license": "Apache-2.0", + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/core": { + "version": "1.30.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/core/-/core-1.30.1.tgz", + "integrity": "sha512-OOCM2C/QIURhJMuKaekP3TRBxBKxG/TWWA0TL2J6nXUtDnuCtccy49LUJF8xPFXMX+0LMcxFpCo8M9cGY1W6rQ==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/semantic-conventions": "1.28.0" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/core/node_modules/@opentelemetry/semantic-conventions": { + "version": "1.28.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/semantic-conventions/-/semantic-conventions-1.28.0.tgz", + "integrity": "sha512-lp4qAiMTD4sNWW4DbKLBkfiMZ4jbAboJIGOQr5DvciMRI494OapieI9qiODpOt0XBr1LjIDy1xAGAnVs5supTA==", + "license": "Apache-2.0", + "engines": { + "node": ">=14" + } + }, + "node_modules/@opentelemetry/instrumentation": { + "version": "0.57.2", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation/-/instrumentation-0.57.2.tgz", + "integrity": "sha512-BdBGhQBh8IjZ2oIIX6F2/Q3LKm/FDDKi6ccYKcBTeilh6SNdNKveDOLk73BkSJjQLJk6qe4Yh+hHw1UPhCDdrg==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/api-logs": "0.57.2", + "@types/shimmer": "^1.2.0", + "import-in-the-middle": "^1.8.1", + "require-in-the-middle": "^7.1.1", + "semver": "^7.5.2", + "shimmer": "^1.2.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/instrumentation-amqplib": { + "version": "0.46.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-amqplib/-/instrumentation-amqplib-0.46.1.tgz", + "integrity": "sha512-AyXVnlCf/xV3K/rNumzKxZqsULyITJH6OVLiW6730JPRqWA7Zc9bvYoVNpN6iOpTU8CasH34SU/ksVJmObFibQ==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/core": "^1.8.0", + "@opentelemetry/instrumentation": "^0.57.1", + "@opentelemetry/semantic-conventions": "^1.27.0" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/instrumentation-connect": { + "version": "0.43.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-connect/-/instrumentation-connect-0.43.1.tgz", + "integrity": "sha512-ht7YGWQuV5BopMcw5Q2hXn3I8eG8TH0J/kc/GMcW4CuNTgiP6wCu44BOnucJWL3CmFWaRHI//vWyAhaC8BwePw==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/core": "^1.8.0", + "@opentelemetry/instrumentation": "^0.57.1", + "@opentelemetry/semantic-conventions": "^1.27.0", + "@types/connect": "3.4.38" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/instrumentation-dataloader": { + "version": "0.16.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-dataloader/-/instrumentation-dataloader-0.16.1.tgz", + "integrity": "sha512-K/qU4CjnzOpNkkKO4DfCLSQshejRNAJtd4esgigo/50nxCB6XCyi1dhAblUHM9jG5dRm8eu0FB+t87nIo99LYQ==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/instrumentation": "^0.57.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/instrumentation-express": { + "version": "0.47.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-express/-/instrumentation-express-0.47.1.tgz", + "integrity": "sha512-QNXPTWteDclR2B4pDFpz0TNghgB33UMjUt14B+BZPmtH1MwUFAfLHBaP5If0Z5NZC+jaH8oF2glgYjrmhZWmSw==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/core": "^1.8.0", + "@opentelemetry/instrumentation": "^0.57.1", + "@opentelemetry/semantic-conventions": "^1.27.0" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/instrumentation-fs": { + "version": "0.19.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-fs/-/instrumentation-fs-0.19.1.tgz", + "integrity": "sha512-6g0FhB3B9UobAR60BGTcXg4IHZ6aaYJzp0Ki5FhnxyAPt8Ns+9SSvgcrnsN2eGmk3RWG5vYycUGOEApycQL24A==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/core": "^1.8.0", + "@opentelemetry/instrumentation": "^0.57.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/instrumentation-generic-pool": { + "version": "0.43.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-generic-pool/-/instrumentation-generic-pool-0.43.1.tgz", + "integrity": "sha512-M6qGYsp1cURtvVLGDrPPZemMFEbuMmCXgQYTReC/IbimV5sGrLBjB+/hANUpRZjX67nGLdKSVLZuQQAiNz+sww==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/instrumentation": "^0.57.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/instrumentation-graphql": { + "version": "0.47.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-graphql/-/instrumentation-graphql-0.47.1.tgz", + "integrity": "sha512-EGQRWMGqwiuVma8ZLAZnExQ7sBvbOx0N/AE/nlafISPs8S+QtXX+Viy6dcQwVWwYHQPAcuY3bFt3xgoAwb4ZNQ==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/instrumentation": "^0.57.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/instrumentation-hapi": { + "version": "0.45.2", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-hapi/-/instrumentation-hapi-0.45.2.tgz", + "integrity": "sha512-7Ehow/7Wp3aoyCrZwQpU7a2CnoMq0XhIcioFuKjBb0PLYfBfmTsFTUyatlHu0fRxhwcRsSQRTvEhmZu8CppBpQ==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/core": "^1.8.0", + "@opentelemetry/instrumentation": "^0.57.1", + "@opentelemetry/semantic-conventions": "^1.27.0" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/instrumentation-http": { + "version": "0.57.2", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-http/-/instrumentation-http-0.57.2.tgz", + "integrity": "sha512-1Uz5iJ9ZAlFOiPuwYg29Bf7bJJc/GeoeJIFKJYQf67nTVKFe8RHbEtxgkOmK4UGZNHKXcpW4P8cWBYzBn1USpg==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/core": "1.30.1", + "@opentelemetry/instrumentation": "0.57.2", + "@opentelemetry/semantic-conventions": "1.28.0", + "forwarded-parse": "2.1.2", + "semver": "^7.5.2" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/instrumentation-http/node_modules/@opentelemetry/semantic-conventions": { + "version": "1.28.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/semantic-conventions/-/semantic-conventions-1.28.0.tgz", + "integrity": "sha512-lp4qAiMTD4sNWW4DbKLBkfiMZ4jbAboJIGOQr5DvciMRI494OapieI9qiODpOt0XBr1LjIDy1xAGAnVs5supTA==", + "license": "Apache-2.0", + "engines": { + "node": ">=14" + } + }, + "node_modules/@opentelemetry/instrumentation-ioredis": { + "version": "0.47.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-ioredis/-/instrumentation-ioredis-0.47.1.tgz", + "integrity": "sha512-OtFGSN+kgk/aoKgdkKQnBsQFDiG8WdCxu+UrHr0bXScdAmtSzLSraLo7wFIb25RVHfRWvzI5kZomqJYEg/l1iA==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/instrumentation": "^0.57.1", + "@opentelemetry/redis-common": "^0.36.2", + "@opentelemetry/semantic-conventions": "^1.27.0" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/instrumentation-kafkajs": { + "version": "0.7.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-kafkajs/-/instrumentation-kafkajs-0.7.1.tgz", + "integrity": "sha512-OtjaKs8H7oysfErajdYr1yuWSjMAectT7Dwr+axIoZqT9lmEOkD/H/3rgAs8h/NIuEi2imSXD+vL4MZtOuJfqQ==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/instrumentation": "^0.57.1", + "@opentelemetry/semantic-conventions": "^1.27.0" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/instrumentation-knex": { + "version": "0.44.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-knex/-/instrumentation-knex-0.44.1.tgz", + "integrity": "sha512-U4dQxkNhvPexffjEmGwCq68FuftFK15JgUF05y/HlK3M6W/G2iEaACIfXdSnwVNe9Qh0sPfw8LbOPxrWzGWGMQ==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/instrumentation": "^0.57.1", + "@opentelemetry/semantic-conventions": "^1.27.0" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/instrumentation-koa": { + "version": "0.47.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-koa/-/instrumentation-koa-0.47.1.tgz", + "integrity": "sha512-l/c+Z9F86cOiPJUllUCt09v+kICKvT+Vg1vOAJHtHPsJIzurGayucfCMq2acd/A/yxeNWunl9d9eqZ0G+XiI6A==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/core": "^1.8.0", + "@opentelemetry/instrumentation": "^0.57.1", + "@opentelemetry/semantic-conventions": "^1.27.0" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/instrumentation-lru-memoizer": { + "version": "0.44.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-lru-memoizer/-/instrumentation-lru-memoizer-0.44.1.tgz", + "integrity": "sha512-5MPkYCvG2yw7WONEjYj5lr5JFehTobW7wX+ZUFy81oF2lr9IPfZk9qO+FTaM0bGEiymwfLwKe6jE15nHn1nmHg==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/instrumentation": "^0.57.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/instrumentation-mongodb": { + "version": "0.52.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-mongodb/-/instrumentation-mongodb-0.52.0.tgz", + "integrity": "sha512-1xmAqOtRUQGR7QfJFfGV/M2kC7wmI2WgZdpru8hJl3S0r4hW0n3OQpEHlSGXJAaNFyvT+ilnwkT+g5L4ljHR6g==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/instrumentation": "^0.57.1", + "@opentelemetry/semantic-conventions": "^1.27.0" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/instrumentation-mongoose": { + "version": "0.46.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-mongoose/-/instrumentation-mongoose-0.46.1.tgz", + "integrity": "sha512-3kINtW1LUTPkiXFRSSBmva1SXzS/72we/jL22N+BnF3DFcoewkdkHPYOIdAAk9gSicJ4d5Ojtt1/HeibEc5OQg==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/core": "^1.8.0", + "@opentelemetry/instrumentation": "^0.57.1", + "@opentelemetry/semantic-conventions": "^1.27.0" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/instrumentation-mysql": { + "version": "0.45.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-mysql/-/instrumentation-mysql-0.45.1.tgz", + "integrity": "sha512-TKp4hQ8iKQsY7vnp/j0yJJ4ZsP109Ht6l4RHTj0lNEG1TfgTrIH5vJMbgmoYXWzNHAqBH2e7fncN12p3BP8LFg==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/instrumentation": "^0.57.1", + "@opentelemetry/semantic-conventions": "^1.27.0", + "@types/mysql": "2.15.26" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/instrumentation-mysql2": { + "version": "0.45.2", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-mysql2/-/instrumentation-mysql2-0.45.2.tgz", + "integrity": "sha512-h6Ad60FjCYdJZ5DTz1Lk2VmQsShiViKe0G7sYikb0GHI0NVvApp2XQNRHNjEMz87roFttGPLHOYVPlfy+yVIhQ==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/instrumentation": "^0.57.1", + "@opentelemetry/semantic-conventions": "^1.27.0", + "@opentelemetry/sql-common": "^0.40.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/instrumentation-pg": { + "version": "0.51.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-pg/-/instrumentation-pg-0.51.1.tgz", + "integrity": "sha512-QxgjSrxyWZc7Vk+qGSfsejPVFL1AgAJdSBMYZdDUbwg730D09ub3PXScB9d04vIqPriZ+0dqzjmQx0yWKiCi2Q==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/core": "^1.26.0", + "@opentelemetry/instrumentation": "^0.57.1", + "@opentelemetry/semantic-conventions": "^1.27.0", + "@opentelemetry/sql-common": "^0.40.1", + "@types/pg": "8.6.1", + "@types/pg-pool": "2.0.6" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/instrumentation-redis-4": { + "version": "0.46.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-redis-4/-/instrumentation-redis-4-0.46.1.tgz", + "integrity": "sha512-UMqleEoabYMsWoTkqyt9WAzXwZ4BlFZHO40wr3d5ZvtjKCHlD4YXLm+6OLCeIi/HkX7EXvQaz8gtAwkwwSEvcQ==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/instrumentation": "^0.57.1", + "@opentelemetry/redis-common": "^0.36.2", + "@opentelemetry/semantic-conventions": "^1.27.0" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/instrumentation-tedious": { + "version": "0.18.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-tedious/-/instrumentation-tedious-0.18.1.tgz", + "integrity": "sha512-5Cuy/nj0HBaH+ZJ4leuD7RjgvA844aY2WW+B5uLcWtxGjRZl3MNLuxnNg5DYWZNPO+NafSSnra0q49KWAHsKBg==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/instrumentation": "^0.57.1", + "@opentelemetry/semantic-conventions": "^1.27.0", + "@types/tedious": "^4.0.14" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/instrumentation-undici": { + "version": "0.10.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-undici/-/instrumentation-undici-0.10.1.tgz", + "integrity": "sha512-rkOGikPEyRpMCmNu9AQuV5dtRlDmJp2dK5sw8roVshAGoB6hH/3QjDtRhdwd75SsJwgynWUNRUYe0wAkTo16tQ==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/core": "^1.8.0", + "@opentelemetry/instrumentation": "^0.57.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.7.0" + } + }, + "node_modules/@opentelemetry/redis-common": { + "version": "0.36.2", + "resolved": "https://registry.npmjs.org/@opentelemetry/redis-common/-/redis-common-0.36.2.tgz", + "integrity": "sha512-faYX1N0gpLhej/6nyp6bgRjzAKXn5GOEMYY7YhciSfCoITAktLUtQ36d24QEWNA1/WA1y6qQunCe0OhHRkVl9g==", + "license": "Apache-2.0", + "engines": { + "node": ">=14" + } + }, + "node_modules/@opentelemetry/resources": { + "version": "1.30.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/resources/-/resources-1.30.1.tgz", + "integrity": "sha512-5UxZqiAgLYGFjS4s9qm5mBVo433u+dSPUFWVWXmLAD4wB65oMCoXaJP1KJa9DIYYMeHu3z4BZcStG3LC593cWA==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/core": "1.30.1", + "@opentelemetry/semantic-conventions": "1.28.0" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/resources/node_modules/@opentelemetry/semantic-conventions": { + "version": "1.28.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/semantic-conventions/-/semantic-conventions-1.28.0.tgz", + "integrity": "sha512-lp4qAiMTD4sNWW4DbKLBkfiMZ4jbAboJIGOQr5DvciMRI494OapieI9qiODpOt0XBr1LjIDy1xAGAnVs5supTA==", + "license": "Apache-2.0", + "engines": { + "node": ">=14" + } + }, + "node_modules/@opentelemetry/sdk-trace-base": { + "version": "1.30.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-trace-base/-/sdk-trace-base-1.30.1.tgz", + "integrity": "sha512-jVPgBbH1gCy2Lb7X0AVQ8XAfgg0pJ4nvl8/IiQA6nxOsPvS+0zMJaFSs2ltXe0J6C8dqjcnpyqINDJmU30+uOg==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/core": "1.30.1", + "@opentelemetry/resources": "1.30.1", + "@opentelemetry/semantic-conventions": "1.28.0" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/sdk-trace-base/node_modules/@opentelemetry/semantic-conventions": { + "version": "1.28.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/semantic-conventions/-/semantic-conventions-1.28.0.tgz", + "integrity": "sha512-lp4qAiMTD4sNWW4DbKLBkfiMZ4jbAboJIGOQr5DvciMRI494OapieI9qiODpOt0XBr1LjIDy1xAGAnVs5supTA==", + "license": "Apache-2.0", + "engines": { + "node": ">=14" + } + }, + "node_modules/@opentelemetry/semantic-conventions": { + "version": "1.37.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/semantic-conventions/-/semantic-conventions-1.37.0.tgz", + "integrity": "sha512-JD6DerIKdJGmRp4jQyX5FlrQjA4tjOw1cvfsPAZXfOOEErMUHjPcPSICS+6WnM0nB0efSFARh0KAZss+bvExOA==", + "license": "Apache-2.0", + "engines": { + "node": ">=14" + } + }, + "node_modules/@opentelemetry/sql-common": { + "version": "0.40.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/sql-common/-/sql-common-0.40.1.tgz", + "integrity": "sha512-nSDlnHSqzC3pXn/wZEZVLuAuJ1MYMXPBwtv2qAbCa3847SaHItdE7SzUq/Jtb0KZmh1zfAbNi3AAMjztTT4Ugg==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/core": "^1.1.0" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.1.0" + } + }, + "node_modules/@paulirish/trace_engine": { + "version": "0.0.59", + "resolved": "https://registry.npmjs.org/@paulirish/trace_engine/-/trace_engine-0.0.59.tgz", + "integrity": "sha512-439NUzQGmH+9Y017/xCchBP9571J4bzhpcNhrxorf7r37wcyJZkgUfrUsRL3xl+JDcZ6ORhoFCzCw98c6S3YHw==", + "license": "BSD-3-Clause", + "dependencies": { + "legacy-javascript": "latest", + "third-party-web": "latest" + } + }, + "node_modules/@prisma/instrumentation": { + "version": "6.11.1", + "resolved": "https://registry.npmjs.org/@prisma/instrumentation/-/instrumentation-6.11.1.tgz", + "integrity": "sha512-mrZOev24EDhnefmnZX7WVVT7v+r9LttPRqf54ONvj6re4XMF7wFTpK2tLJi4XHB7fFp/6xhYbgRel8YV7gQiyA==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/instrumentation": "^0.52.0 || ^0.53.0 || ^0.54.0 || ^0.55.0 || ^0.56.0 || ^0.57.0" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.8" + } + }, + "node_modules/@puppeteer/browsers": { + "version": "2.10.10", + "resolved": "https://registry.npmjs.org/@puppeteer/browsers/-/browsers-2.10.10.tgz", + "integrity": "sha512-3ZG500+ZeLql8rE0hjfhkycJjDj0pI/btEh3L9IkWUYcOrgP0xCNRq3HbtbqOPbvDhFaAWD88pDFtlLv8ns8gA==", + "license": "Apache-2.0", + "dependencies": { + "debug": "^4.4.3", + "extract-zip": "^2.0.1", + "progress": "^2.0.3", + "proxy-agent": "^6.5.0", + "semver": "^7.7.2", + "tar-fs": "^3.1.0", + "yargs": "^17.7.2" + }, + "bin": { + "browsers": "lib/cjs/main-cli.js" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@sec-ant/readable-stream": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/@sec-ant/readable-stream/-/readable-stream-0.4.1.tgz", + "integrity": "sha512-831qok9r2t8AlxLko40y2ebgSDhenenCatLVeW/uBtnHPyhHOvG0C7TvfgecV+wHzIm5KUICgzmVpWS+IMEAeg==", + "license": "MIT" + }, + "node_modules/@sentry/core": { + "version": "9.46.0", + "resolved": "https://registry.npmjs.org/@sentry/core/-/core-9.46.0.tgz", + "integrity": "sha512-it7JMFqxVproAgEtbLgCVBYtQ9fIb+Bu0JD+cEplTN/Ukpe6GaolyYib5geZqslVxhp2sQgT+58aGvfd/k0N8Q==", + "license": "MIT", + "engines": { + "node": ">=18" + } + }, + "node_modules/@sentry/node": { + "version": "9.46.0", + "resolved": "https://registry.npmjs.org/@sentry/node/-/node-9.46.0.tgz", + "integrity": "sha512-pRLqAcd7GTGvN8gex5FtkQR5Mcol8gOy1WlyZZFq4rBbVtMbqKOQRhohwqnb+YrnmtFpj7IZ7KNDo077MvNeOQ==", + "license": "MIT", + "dependencies": { + "@opentelemetry/api": "^1.9.0", + "@opentelemetry/context-async-hooks": "^1.30.1", + "@opentelemetry/core": "^1.30.1", + "@opentelemetry/instrumentation": "^0.57.2", + "@opentelemetry/instrumentation-amqplib": "^0.46.1", + "@opentelemetry/instrumentation-connect": "0.43.1", + "@opentelemetry/instrumentation-dataloader": "0.16.1", + "@opentelemetry/instrumentation-express": "0.47.1", + "@opentelemetry/instrumentation-fs": "0.19.1", + "@opentelemetry/instrumentation-generic-pool": "0.43.1", + "@opentelemetry/instrumentation-graphql": "0.47.1", + "@opentelemetry/instrumentation-hapi": "0.45.2", + "@opentelemetry/instrumentation-http": "0.57.2", + "@opentelemetry/instrumentation-ioredis": "0.47.1", + "@opentelemetry/instrumentation-kafkajs": "0.7.1", + "@opentelemetry/instrumentation-knex": "0.44.1", + "@opentelemetry/instrumentation-koa": "0.47.1", + "@opentelemetry/instrumentation-lru-memoizer": "0.44.1", + "@opentelemetry/instrumentation-mongodb": "0.52.0", + "@opentelemetry/instrumentation-mongoose": "0.46.1", + "@opentelemetry/instrumentation-mysql": "0.45.1", + "@opentelemetry/instrumentation-mysql2": "0.45.2", + "@opentelemetry/instrumentation-pg": "0.51.1", + "@opentelemetry/instrumentation-redis-4": "0.46.1", + "@opentelemetry/instrumentation-tedious": "0.18.1", + "@opentelemetry/instrumentation-undici": "0.10.1", + "@opentelemetry/resources": "^1.30.1", + "@opentelemetry/sdk-trace-base": "^1.30.1", + "@opentelemetry/semantic-conventions": "^1.34.0", + "@prisma/instrumentation": "6.11.1", + "@sentry/core": "9.46.0", + "@sentry/node-core": "9.46.0", + "@sentry/opentelemetry": "9.46.0", + "import-in-the-middle": "^1.14.2", + "minimatch": "^9.0.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@sentry/node-core": { + "version": "9.46.0", + "resolved": "https://registry.npmjs.org/@sentry/node-core/-/node-core-9.46.0.tgz", + "integrity": "sha512-XRVu5pqoklZeh4wqhxCLZkz/ipoKhitctgEFXX9Yh1e1BoHM2pIxT52wf+W6hHM676TFmFXW3uKBjsmRM3AjgA==", + "license": "MIT", + "dependencies": { + "@sentry/core": "9.46.0", + "@sentry/opentelemetry": "9.46.0", + "import-in-the-middle": "^1.14.2" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.9.0", + "@opentelemetry/context-async-hooks": "^1.30.1 || ^2.0.0", + "@opentelemetry/core": "^1.30.1 || ^2.0.0", + "@opentelemetry/instrumentation": ">=0.57.1 <1", + "@opentelemetry/resources": "^1.30.1 || ^2.0.0", + "@opentelemetry/sdk-trace-base": "^1.30.1 || ^2.0.0", + "@opentelemetry/semantic-conventions": "^1.34.0" + } + }, + "node_modules/@sentry/node/node_modules/brace-expansion": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", + "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/@sentry/node/node_modules/minimatch": { + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", + "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/@sentry/opentelemetry": { + "version": "9.46.0", + "resolved": "https://registry.npmjs.org/@sentry/opentelemetry/-/opentelemetry-9.46.0.tgz", + "integrity": "sha512-w2zTxqrdmwRok0cXBoh+ksXdGRUHUZhlpfL/H2kfTodOL+Mk8rW72qUmfqQceXoqgbz8UyK8YgJbyt+XS5H4Qg==", + "license": "MIT", + "dependencies": { + "@sentry/core": "9.46.0" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.9.0", + "@opentelemetry/context-async-hooks": "^1.30.1 || ^2.0.0", + "@opentelemetry/core": "^1.30.1 || ^2.0.0", + "@opentelemetry/sdk-trace-base": "^1.30.1 || ^2.0.0", + "@opentelemetry/semantic-conventions": "^1.34.0" + } + }, + "node_modules/@sindresorhus/is": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-7.0.2.tgz", + "integrity": "sha512-d9xRovfKNz1SKieM0qJdO+PQonjnnIfSNWfHYnBSJ9hkjm0ZPw6HlxscDXYstp3z+7V2GOFHc+J0CYrYTjqCJw==", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sindresorhus/is?sponsor=1" + } + }, + "node_modules/@szmarczak/http-timer": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/@szmarczak/http-timer/-/http-timer-5.0.1.tgz", + "integrity": "sha512-+PmQX0PiAYPMeVYe237LJAYvOMYW1j2rH5YROyS3b4CTVJum34HfRvKvAzozHAQG0TnHNdUfY9nCeUyRAs//cw==", + "license": "MIT", + "dependencies": { + "defer-to-connect": "^2.0.1" + }, + "engines": { + "node": ">=14.16" + } + }, + "node_modules/@tootallnate/quickjs-emscripten": { + "version": "0.23.0", + "resolved": "https://registry.npmjs.org/@tootallnate/quickjs-emscripten/-/quickjs-emscripten-0.23.0.tgz", + "integrity": "sha512-C5Mc6rdnsaJDjO3UpGW/CQTHtCKaYlScZTly4JIu97Jxo/odCiH0ITnDXSJPTOrEKk/ycSZ0AOgTmkDtkOsvIA==", + "license": "MIT" + }, + "node_modules/@types/cacheable-request": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/@types/cacheable-request/-/cacheable-request-6.0.3.tgz", + "integrity": "sha512-IQ3EbTzGxIigb1I3qPZc1rWJnH0BmSKv5QYTalEwweFvyBDLSAe24zP0le/hyi7ecGfZVlIVAg4BZqb8WBwKqw==", + "license": "MIT", + "dependencies": { + "@types/http-cache-semantics": "*", + "@types/keyv": "^3.1.4", + "@types/node": "*", + "@types/responselike": "^1.0.0" + } + }, + "node_modules/@types/connect": { + "version": "3.4.38", + "resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.38.tgz", + "integrity": "sha512-K6uROf1LD88uDQqJCktA4yzL1YYAK6NgfsI0v/mTgyPKWsX1CnJ0XPSDhViejru1GcRkLWb8RlzFYJRqGUbaug==", + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/http-cache-semantics": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/@types/http-cache-semantics/-/http-cache-semantics-4.0.4.tgz", + "integrity": "sha512-1m0bIFVc7eJWyve9S0RnuRgcQqF/Xd5QsUZAZeQFr1Q3/p9JWoQQEqmVy+DPTNpGXwhgIetAoYF8JSc33q29QA==", + "license": "MIT" + }, + "node_modules/@types/keyv": { + "version": "3.1.4", + "resolved": "https://registry.npmjs.org/@types/keyv/-/keyv-3.1.4.tgz", + "integrity": "sha512-BQ5aZNSCpj7D6K2ksrRCTmKRLEpnPvWDiLPfoGyhZ++8YtiK9d/3DBKPJgry359X/P1PfruyYwvnvwFjuEiEIg==", + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/mysql": { + "version": "2.15.26", + "resolved": "https://registry.npmjs.org/@types/mysql/-/mysql-2.15.26.tgz", + "integrity": "sha512-DSLCOXhkvfS5WNNPbfn2KdICAmk8lLc+/PNvnPnF7gOdMZCxopXduqv0OQ13y/yA/zXTSikZZqVgybUxOEg6YQ==", + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/node": { + "version": "24.3.0", + "resolved": "https://registry.npmjs.org/@types/node/-/node-24.3.0.tgz", + "integrity": "sha512-aPTXCrfwnDLj4VvXrm+UUCQjNEvJgNA8s5F1cvwQU+3KNltTOkBm1j30uNLyqqPNe7gE3KFzImYoZEfLhp4Yow==", + "license": "MIT", + "dependencies": { + "undici-types": "~7.10.0" + } + }, + "node_modules/@types/pg": { + "version": "8.6.1", + "resolved": "https://registry.npmjs.org/@types/pg/-/pg-8.6.1.tgz", + "integrity": "sha512-1Kc4oAGzAl7uqUStZCDvaLFqZrW9qWSjXOmBfdgyBP5La7Us6Mg4GBvRlSoaZMhQF/zSj1C8CtKMBkoiT8eL8w==", + "license": "MIT", + "dependencies": { + "@types/node": "*", + "pg-protocol": "*", + "pg-types": "^2.2.0" + } + }, + "node_modules/@types/pg-pool": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/@types/pg-pool/-/pg-pool-2.0.6.tgz", + "integrity": "sha512-TaAUE5rq2VQYxab5Ts7WZhKNmuN78Q6PiFonTDdpbx8a1H0M1vhy3rhiMjl+e2iHmogyMw7jZF4FrE6eJUy5HQ==", + "license": "MIT", + "dependencies": { + "@types/pg": "*" + } + }, + "node_modules/@types/pluralize": { + "version": "0.0.29", + "resolved": "https://registry.npmjs.org/@types/pluralize/-/pluralize-0.0.29.tgz", + "integrity": "sha512-BYOID+l2Aco2nBik+iYS4SZX0Lf20KPILP5RGmM1IgzdwNdTs0eebiFriOPcej1sX9mLnSoiNte5zcFxssgpGA==", + "license": "MIT" + }, + "node_modules/@types/responselike": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@types/responselike/-/responselike-1.0.3.tgz", + "integrity": "sha512-H/+L+UkTV33uf49PH5pCAUBVPNj2nDBXTN+qS1dOwyyg24l3CcicicCA7ca+HMvJBZcFgl5r8e+RR6elsb4Lyw==", + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/shimmer": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@types/shimmer/-/shimmer-1.2.0.tgz", + "integrity": "sha512-UE7oxhQLLd9gub6JKIAhDq06T0F6FnztwMNRvYgjeQSBeMc1ZG/tA47EwfduvkuQS8apbkM/lpLpWsaCeYsXVg==", + "license": "MIT" + }, + "node_modules/@types/tedious": { + "version": "4.0.14", + "resolved": "https://registry.npmjs.org/@types/tedious/-/tedious-4.0.14.tgz", + "integrity": "sha512-KHPsfX/FoVbUGbyYvk1q9MMQHLPeRZhRJZdO45Q4YjvFkv4hMNghCWTvy7rdKessBsmtz4euWCWAB6/tVpI1Iw==", + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/webidl-conversions": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/@types/webidl-conversions/-/webidl-conversions-7.0.3.tgz", + "integrity": "sha512-CiJJvcRtIgzadHCYXw7dqEnMNRjhGZlYK05Mj9OyktqV8uVT8fD2BFOB7S1uwBE3Kj2Z+4UyPmFw/Ixgw/LAlA==", + "license": "MIT" + }, + "node_modules/@types/whatwg-url": { + "version": "11.0.5", + "resolved": "https://registry.npmjs.org/@types/whatwg-url/-/whatwg-url-11.0.5.tgz", + "integrity": "sha512-coYR071JRaHa+xoEvvYqvnIHaVqaYrLPbsufM9BF63HkwI5Lgmy2QR8Q5K/lYDYo5AK82wOvSOS0UsLTpTG7uQ==", + "license": "MIT", + "dependencies": { + "@types/webidl-conversions": "*" + } + }, + "node_modules/@types/yauzl": { + "version": "2.10.3", + "resolved": "https://registry.npmjs.org/@types/yauzl/-/yauzl-2.10.3.tgz", + "integrity": "sha512-oJoftv0LSuaDZE3Le4DbKX+KS9G36NzOeSap90UIK0yMA/NhKJhqlSGtNDORNRaIbQfzjXDrQa0ytJ6mNRGz/Q==", + "license": "MIT", + "optional": true, + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/accepts": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-2.0.0.tgz", + "integrity": "sha512-5cvg6CtKwfgdmVqY1WIiXKc3Q1bkRqGLi+2W/6ao+6Y7gu/RCwRuAhGEzh5B4KlszSuTLgZYuqFqo5bImjNKng==", + "license": "MIT", + "dependencies": { + "mime-types": "^3.0.0", + "negotiator": "^1.0.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/acorn": { + "version": "8.15.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz", + "integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==", + "license": "MIT", + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/acorn-import-attributes": { + "version": "1.9.5", + "resolved": "https://registry.npmjs.org/acorn-import-attributes/-/acorn-import-attributes-1.9.5.tgz", + "integrity": "sha512-n02Vykv5uA3eHGM/Z2dQrcD56kL8TyDb2p1+0P83PClMnC/nc+anbQRhIOWnSq4Ke/KvDPrY3C9hDtC/A3eHnQ==", + "license": "MIT", + "peerDependencies": { + "acorn": "^8" + } + }, + "node_modules/agent-base": { + "version": "7.1.4", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.4.tgz", + "integrity": "sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ==", + "license": "MIT", + "engines": { + "node": ">= 14" + } + }, + "node_modules/ansi-colors": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.3.tgz", + "integrity": "sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "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/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/anymatch": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", + "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", + "dev": true, + "license": "ISC", + "dependencies": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/append-field": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/append-field/-/append-field-1.0.0.tgz", + "integrity": "sha512-klpgFSWLW1ZEs8svjfb7g4qWY0YS5imI82dTg+QahUvJ8YqAY0P10Uk8tTyh9ZGuYEZEMaeJYCF5BFuX552hsw==", + "license": "MIT" + }, + "node_modules/ast-types": { + "version": "0.13.4", + "resolved": "https://registry.npmjs.org/ast-types/-/ast-types-0.13.4.tgz", + "integrity": "sha512-x1FCFnFifvYDDzTaLII71vG5uvDwgtmDTEVWAxrgeiR8VjMONcCXJx7E+USjDtHlwFmt9MysbqgF9b9Vjr6w+w==", + "license": "MIT", + "dependencies": { + "tslib": "^2.0.1" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==", + "license": "MIT" + }, + "node_modules/atomically": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/atomically/-/atomically-2.0.3.tgz", + "integrity": "sha512-kU6FmrwZ3Lx7/7y3hPS5QnbJfaohcIul5fGqf7ok+4KklIEk9tJ0C2IQPdacSbVUWv6zVHXEBWoWd6NrVMT7Cw==", + "dependencies": { + "stubborn-fs": "^1.2.5", + "when-exit": "^2.1.1" + } + }, + "node_modules/axe-core": { + "version": "4.10.3", + "resolved": "https://registry.npmjs.org/axe-core/-/axe-core-4.10.3.tgz", + "integrity": "sha512-Xm7bpRXnDSX2YE2YFfBk2FnF0ep6tmG7xPh8iHee8MIcrgq762Nkce856dYtJYLkuIoYZvGfTs/PbZhideTcEg==", + "license": "MPL-2.0", + "engines": { + "node": ">=4" + } + }, + "node_modules/axios": { + "version": "1.12.2", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.12.2.tgz", + "integrity": "sha512-vMJzPewAlRyOgxV2dU0Cuz2O8zzzx9VYtbJOaBgXFeLc4IV/Eg50n4LowmehOOR61S8ZMpc2K5Sa7g6A4jfkUw==", + "license": "MIT", + "dependencies": { + "follow-redirects": "^1.15.6", + "form-data": "^4.0.4", + "proxy-from-env": "^1.1.0" + } + }, + "node_modules/b4a": { + "version": "1.7.3", + "resolved": "https://registry.npmjs.org/b4a/-/b4a-1.7.3.tgz", + "integrity": "sha512-5Q2mfq2WfGuFp3uS//0s6baOJLMoVduPYVeNmDYxu5OUA1/cBfvr2RIS7vi62LdNj/urk1hfmj867I3qt6uZ7Q==", + "license": "Apache-2.0", + "peerDependencies": { + "react-native-b4a": "*" + }, + "peerDependenciesMeta": { + "react-native-b4a": { + "optional": true + } + } + }, + "node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "license": "MIT" + }, + "node_modules/bare-events": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/bare-events/-/bare-events-2.7.0.tgz", + "integrity": "sha512-b3N5eTW1g7vXkw+0CXh/HazGTcO5KYuu/RCNaJbDMPI6LHDi+7qe8EmxKUVe1sUbY2KZOVZFyj62x0OEz9qyAA==", + "license": "Apache-2.0" + }, + "node_modules/bare-fs": { + "version": "4.4.4", + "resolved": "https://registry.npmjs.org/bare-fs/-/bare-fs-4.4.4.tgz", + "integrity": "sha512-Q8yxM1eLhJfuM7KXVP3zjhBvtMJCYRByoTT+wHXjpdMELv0xICFJX+1w4c7csa+WZEOsq4ItJ4RGwvzid6m/dw==", + "license": "Apache-2.0", + "optional": true, + "dependencies": { + "bare-events": "^2.5.4", + "bare-path": "^3.0.0", + "bare-stream": "^2.6.4", + "bare-url": "^2.2.2", + "fast-fifo": "^1.3.2" + }, + "engines": { + "bare": ">=1.16.0" + }, + "peerDependencies": { + "bare-buffer": "*" + }, + "peerDependenciesMeta": { + "bare-buffer": { + "optional": true + } + } + }, + "node_modules/bare-os": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/bare-os/-/bare-os-3.6.2.tgz", + "integrity": "sha512-T+V1+1srU2qYNBmJCXZkUY5vQ0B4FSlL3QDROnKQYOqeiQR8UbjNHlPa+TIbM4cuidiN9GaTaOZgSEgsvPbh5A==", + "license": "Apache-2.0", + "optional": true, + "engines": { + "bare": ">=1.14.0" + } + }, + "node_modules/bare-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/bare-path/-/bare-path-3.0.0.tgz", + "integrity": "sha512-tyfW2cQcB5NN8Saijrhqn0Zh7AnFNsnczRcuWODH0eYAXBsJ5gVxAUuNr7tsHSC6IZ77cA0SitzT+s47kot8Mw==", + "license": "Apache-2.0", + "optional": true, + "dependencies": { + "bare-os": "^3.0.1" + } + }, + "node_modules/bare-stream": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/bare-stream/-/bare-stream-2.7.0.tgz", + "integrity": "sha512-oyXQNicV1y8nc2aKffH+BUHFRXmx6VrPzlnaEvMhram0nPBrKcEdcyBg5r08D0i8VxngHFAiVyn1QKXpSG0B8A==", + "license": "Apache-2.0", + "optional": true, + "dependencies": { + "streamx": "^2.21.0" + }, + "peerDependencies": { + "bare-buffer": "*", + "bare-events": "*" + }, + "peerDependenciesMeta": { + "bare-buffer": { + "optional": true + }, + "bare-events": { + "optional": true + } + } + }, + "node_modules/bare-url": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/bare-url/-/bare-url-2.2.2.tgz", + "integrity": "sha512-g+ueNGKkrjMazDG3elZO1pNs3HY5+mMmOet1jtKyhOaCnkLzitxf26z7hoAEkDNgdNmnc1KIlt/dw6Po6xZMpA==", + "license": "Apache-2.0", + "optional": true, + "dependencies": { + "bare-path": "^3.0.0" + } + }, + "node_modules/basic-ftp": { + "version": "5.0.5", + "resolved": "https://registry.npmjs.org/basic-ftp/-/basic-ftp-5.0.5.tgz", + "integrity": "sha512-4Bcg1P8xhUuqcii/S0Z9wiHIrQVPMermM1any+MX5GeGD7faD3/msQUDGLol9wOcz4/jbg/WJnGqoJF6LiBdtg==", + "license": "MIT", + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/bcrypt": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/bcrypt/-/bcrypt-6.0.0.tgz", + "integrity": "sha512-cU8v/EGSrnH+HnxV2z0J7/blxH8gq7Xh2JFT6Aroax7UohdmiJJlxApMxtKfuI7z68NvvVcmR78k2LbT6efhRg==", + "hasInstallScript": true, + "license": "MIT", + "dependencies": { + "node-addon-api": "^8.3.0", + "node-gyp-build": "^4.8.4" + }, + "engines": { + "node": ">= 18" + } + }, + "node_modules/binary-extensions": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz", + "integrity": "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/body-parser": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-2.2.0.tgz", + "integrity": "sha512-02qvAaxv8tp7fBa/mw1ga98OGm+eCbqzJOKoRt70sLmfEEi+jyBYVTDGfCL/k06/4EMk/z01gCe7HoCH/f2LTg==", + "license": "MIT", + "dependencies": { + "bytes": "^3.1.2", + "content-type": "^1.0.5", + "debug": "^4.4.0", + "http-errors": "^2.0.0", + "iconv-lite": "^0.6.3", + "on-finished": "^2.4.1", + "qs": "^6.14.0", + "raw-body": "^3.0.0", + "type-is": "^2.0.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/boolbase": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz", + "integrity": "sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==", + "license": "ISC" + }, + "node_modules/brace-expansion": { + "version": "1.1.12", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", + "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/braces": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", + "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", + "dev": true, + "license": "MIT", + "dependencies": { + "fill-range": "^7.1.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/bson": { + "version": "6.10.4", + "resolved": "https://registry.npmjs.org/bson/-/bson-6.10.4.tgz", + "integrity": "sha512-WIsKqkSC0ABoBJuT1LEX+2HEvNmNKKgnTAyd0fL8qzK4SH2i9NXg+t08YtdZp/V9IZ33cxe3iV4yM0qg8lMQng==", + "license": "Apache-2.0", + "engines": { + "node": ">=16.20.1" + } + }, + "node_modules/buffer-crc32": { + "version": "0.2.13", + "resolved": "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-0.2.13.tgz", + "integrity": "sha512-VO9Ht/+p3SN7SKWqcrgEzjGbRSJYTx+Q1pTQC0wrWqHx0vpJraQ6GtHx8tvcg1rlK1byhU5gccxgOgj7B0TDkQ==", + "license": "MIT", + "engines": { + "node": "*" + } + }, + "node_modules/buffer-equal-constant-time": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz", + "integrity": "sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA==", + "license": "BSD-3-Clause" + }, + "node_modules/buffer-from": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", + "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", + "license": "MIT" + }, + "node_modules/busboy": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/busboy/-/busboy-1.6.0.tgz", + "integrity": "sha512-8SFQbg/0hQ9xy3UNTB0YEnsNBbWfhf7RtnzpL7TkBiTBRfrQ9Fxcnz7VJsleJpyp6rVLvXiuORqjlHi5q+PYuA==", + "dependencies": { + "streamsearch": "^1.1.0" + }, + "engines": { + "node": ">=10.16.0" + } + }, + "node_modules/bytes": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", + "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/cacheable-lookup": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/cacheable-lookup/-/cacheable-lookup-7.0.0.tgz", + "integrity": "sha512-+qJyx4xiKra8mZrcwhjMRMUhD5NR1R8esPkzIYxX96JiecFoxAXFuz/GpR3+ev4PE1WamHip78wV0vcmPQtp8w==", + "license": "MIT", + "engines": { + "node": ">=14.16" + } + }, + "node_modules/cacheable-request": { + "version": "12.0.1", + "resolved": "https://registry.npmjs.org/cacheable-request/-/cacheable-request-12.0.1.tgz", + "integrity": "sha512-Yo9wGIQUaAfIbk+qY0X4cDQgCosecfBe3V9NSyeY4qPC2SAkbCS4Xj79VP8WOzitpJUZKc/wsRCYF5ariDIwkg==", + "license": "MIT", + "dependencies": { + "@types/http-cache-semantics": "^4.0.4", + "get-stream": "^9.0.1", + "http-cache-semantics": "^4.1.1", + "keyv": "^4.5.4", + "mimic-response": "^4.0.0", + "normalize-url": "^8.0.1", + "responselike": "^3.0.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/call-bind-apply-helpers": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", + "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/call-bound": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.4.tgz", + "integrity": "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==", + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "get-intrinsic": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/cheerio": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/cheerio/-/cheerio-1.1.0.tgz", + "integrity": "sha512-+0hMx9eYhJvWbgpKV9hN7jg0JcwydpopZE4hgi+KvQtByZXPp04NiCWU0LzcAbP63abZckIHkTQaXVF52mX3xQ==", + "license": "MIT", + "dependencies": { + "cheerio-select": "^2.1.0", + "dom-serializer": "^2.0.0", + "domhandler": "^5.0.3", + "domutils": "^3.2.2", + "encoding-sniffer": "^0.2.0", + "htmlparser2": "^10.0.0", + "parse5": "^7.3.0", + "parse5-htmlparser2-tree-adapter": "^7.1.0", + "parse5-parser-stream": "^7.1.2", + "undici": "^7.10.0", + "whatwg-mimetype": "^4.0.0" + }, + "engines": { + "node": ">=18.17" + }, + "funding": { + "url": "https://github.com/cheeriojs/cheerio?sponsor=1" + } + }, + "node_modules/cheerio-select": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/cheerio-select/-/cheerio-select-2.1.0.tgz", + "integrity": "sha512-9v9kG0LvzrlcungtnJtpGNxY+fzECQKhK4EGJX2vByejiMX84MFNQw4UxPJl3bFbTMw+Dfs37XaIkCwTZfLh4g==", + "license": "BSD-2-Clause", + "dependencies": { + "boolbase": "^1.0.0", + "css-select": "^5.1.0", + "css-what": "^6.1.0", + "domelementtype": "^2.3.0", + "domhandler": "^5.0.3", + "domutils": "^3.0.1" + }, + "funding": { + "url": "https://github.com/sponsors/fb55" + } + }, + "node_modules/chokidar": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz", + "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==", + "dev": true, + "license": "MIT", + "dependencies": { + "anymatch": "~3.1.2", + "braces": "~3.0.2", + "glob-parent": "~5.1.2", + "is-binary-path": "~2.1.0", + "is-glob": "~4.0.1", + "normalize-path": "~3.0.0", + "readdirp": "~3.6.0" + }, + "engines": { + "node": ">= 8.10.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + }, + "optionalDependencies": { + "fsevents": "~2.3.2" + } + }, + "node_modules/chrome-launcher": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/chrome-launcher/-/chrome-launcher-1.2.1.tgz", + "integrity": "sha512-qmFR5PLMzHyuNJHwOloHPAHhbaNglkfeV/xDtt5b7xiFFyU1I+AZZX0PYseMuhenJSSirgxELYIbswcoc+5H4A==", + "license": "Apache-2.0", + "dependencies": { + "@types/node": "*", + "escape-string-regexp": "^4.0.0", + "is-wsl": "^2.2.0", + "lighthouse-logger": "^2.0.1" + }, + "bin": { + "print-chrome-path": "bin/print-chrome-path.cjs" + }, + "engines": { + "node": ">=12.13.0" + } + }, + "node_modules/chromium-bidi": { + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/chromium-bidi/-/chromium-bidi-9.1.0.tgz", + "integrity": "sha512-rlUzQ4WzIAWdIbY/viPShhZU2n21CxDUgazXVbw4Hu1MwaeUSEksSeM6DqPgpRjCLXRk702AVRxJxoOz0dw4OA==", + "license": "Apache-2.0", + "dependencies": { + "mitt": "^3.0.1", + "zod": "^3.24.1" + }, + "peerDependencies": { + "devtools-protocol": "*" + } + }, + "node_modules/cjs-module-lexer": { + "version": "1.4.3", + "resolved": "https://registry.npmjs.org/cjs-module-lexer/-/cjs-module-lexer-1.4.3.tgz", + "integrity": "sha512-9z8TZaGM1pfswYeXrUpzPrkx8UnWYdhJclsiYMm6x/w5+nN+8Tf/LnAgfLGQCm59qAOxU8WwHEq2vNwF6i4j+Q==", + "license": "MIT" + }, + "node_modules/cliui": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", + "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", + "license": "ISC", + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.1", + "wrap-ansi": "^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/clone-response": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/clone-response/-/clone-response-1.0.3.tgz", + "integrity": "sha512-ROoL94jJH2dUVML2Y/5PEDNaSHgeOdSDicUyS7izcF63G6sTc/FTjLub4b8Il9S8S0beOfYt0TaA5qvFK+w0wA==", + "license": "MIT", + "dependencies": { + "mimic-response": "^1.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/clone-response/node_modules/mimic-response": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-1.0.1.tgz", + "integrity": "sha512-j5EctnkH7amfV/q5Hgmoal1g2QHFJRraOtmx0JpIqkxhBhI/lJSl1nMpQ45hVarwNETOoWEimndZ4QK0RHxuxQ==", + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "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", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "license": "MIT", + "dependencies": { + "delayed-stream": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "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", + "integrity": "sha512-MWufYdFw53ccGjCA+Ol7XJYpAlW6/prSMzuPOTRnJGcGzuhLn4Scrz7qf6o8bROZ514ltazcIFJZevcfbo0x7A==", + "engines": [ + "node >= 6.0" + ], + "license": "MIT", + "dependencies": { + "buffer-from": "^1.0.0", + "inherits": "^2.0.3", + "readable-stream": "^3.0.2", + "typedarray": "^0.0.6" + } + }, + "node_modules/configstore": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/configstore/-/configstore-7.1.0.tgz", + "integrity": "sha512-N4oog6YJWbR9kGyXvS7jEykLDXIE2C0ILYqNBZBp9iwiJpoCBWYsuAdW6PPFn6w06jjnC+3JstVvWHO4cZqvRg==", + "license": "BSD-2-Clause", + "dependencies": { + "atomically": "^2.0.3", + "dot-prop": "^9.0.0", + "graceful-fs": "^4.2.11", + "xdg-basedir": "^5.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/content-disposition": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-1.0.0.tgz", + "integrity": "sha512-Au9nRL8VNUut/XSzbQA38+M78dzP4D+eqg3gfJHMIHHYa3bg067xj1KxMUWj+VULbiZMowKngFFbKczUrNJ1mg==", + "license": "MIT", + "dependencies": { + "safe-buffer": "5.2.1" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/content-type": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz", + "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/cookie": { + "version": "0.7.2", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.2.tgz", + "integrity": "sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/cookie-signature": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.2.2.tgz", + "integrity": "sha512-D76uU73ulSXrD1UXF4KE2TMxVVwhsnCgfAyTg9k8P6KGZjlXKrOLe4dJQKI3Bxi5wjesZoFXJWElNWBjPZMbhg==", + "license": "MIT", + "engines": { + "node": ">=6.6.0" + } + }, + "node_modules/cors": { + "version": "2.8.5", + "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.5.tgz", + "integrity": "sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==", + "license": "MIT", + "dependencies": { + "object-assign": "^4", + "vary": "^1" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/csp_evaluator": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/csp_evaluator/-/csp_evaluator-1.1.5.tgz", + "integrity": "sha512-EL/iN9etCTzw/fBnp0/uj0f5BOOGvZut2mzsiiBZ/FdT6gFQCKRO/tmcKOxn5drWZ2Ndm/xBb1SI4zwWbGtmIw==", + "license": "Apache-2.0" + }, + "node_modules/css-select": { + "version": "5.2.2", + "resolved": "https://registry.npmjs.org/css-select/-/css-select-5.2.2.tgz", + "integrity": "sha512-TizTzUddG/xYLA3NXodFM0fSbNizXjOKhqiQQwvhlspadZokn1KDy0NZFS0wuEubIYAV5/c1/lAr0TaaFXEXzw==", + "license": "BSD-2-Clause", + "dependencies": { + "boolbase": "^1.0.0", + "css-what": "^6.1.0", + "domhandler": "^5.0.2", + "domutils": "^3.0.1", + "nth-check": "^2.0.1" + }, + "funding": { + "url": "https://github.com/sponsors/fb55" + } + }, + "node_modules/css-what": { + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/css-what/-/css-what-6.2.2.tgz", + "integrity": "sha512-u/O3vwbptzhMs3L1fQE82ZSLHQQfto5gyZzwteVIEyeaY5Fc7R4dapF/BvRoSYFeqfBk4m0V1Vafq5Pjv25wvA==", + "license": "BSD-2-Clause", + "engines": { + "node": ">= 6" + }, + "funding": { + "url": "https://github.com/sponsors/fb55" + } + }, + "node_modules/data-uri-to-buffer": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/data-uri-to-buffer/-/data-uri-to-buffer-6.0.2.tgz", + "integrity": "sha512-7hvf7/GW8e86rW0ptuwS3OcBGDjIi6SZva7hCyWC0yYry2cOPmLIjXAUHI6DK2HsnwJd9ifmt57i8eV2n4YNpw==", + "license": "MIT", + "engines": { + "node": ">= 14" + } + }, + "node_modules/debug": { + "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" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/decimal.js": { + "version": "10.6.0", + "resolved": "https://registry.npmjs.org/decimal.js/-/decimal.js-10.6.0.tgz", + "integrity": "sha512-YpgQiITW3JXGntzdUmyUR1V812Hn8T1YVXhCu+wO3OpS4eU9l4YdD3qjyiKdV6mvV29zapkMeD390UVEf2lkUg==", + "license": "MIT" + }, + "node_modules/decompress-response": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-6.0.0.tgz", + "integrity": "sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==", + "license": "MIT", + "dependencies": { + "mimic-response": "^3.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/decompress-response/node_modules/mimic-response": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-3.1.0.tgz", + "integrity": "sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==", + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/defer-to-connect": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/defer-to-connect/-/defer-to-connect-2.0.1.tgz", + "integrity": "sha512-4tvttepXG1VaYGrRibk5EwJd1t4udunSOVMdLSAL6mId1ix438oPwPZMALY41FCijukO1L0twNcGsdzS7dHgDg==", + "license": "MIT", + "engines": { + "node": ">=10" + } + }, + "node_modules/define-lazy-prop": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/define-lazy-prop/-/define-lazy-prop-2.0.0.tgz", + "integrity": "sha512-Ds09qNh8yw3khSjiJjiUInaGX9xlqZDY7JVryGxdxV7NPeuqQfplOpQ66yJFZut3jLa5zOwkXw1g9EI2uKh4Og==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/degenerator": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/degenerator/-/degenerator-5.0.1.tgz", + "integrity": "sha512-TllpMR/t0M5sqCXfj85i4XaAzxmS5tVA16dqvdkMwGmzI+dXLXnw3J+3Vdv7VKw+ThlTMboK6i9rnZ6Nntj5CQ==", + "license": "MIT", + "dependencies": { + "ast-types": "^0.13.4", + "escodegen": "^2.1.0", + "esprima": "^4.0.1" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", + "license": "MIT", + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/depd": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", + "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/devtools-protocol": { + "version": "0.0.1507524", + "resolved": "https://registry.npmjs.org/devtools-protocol/-/devtools-protocol-0.0.1507524.tgz", + "integrity": "sha512-OjaNE7qpk6GRTXtqQjAE5bGx6+c4F1zZH0YXtpZQLM92HNXx4zMAaqlKhP4T52DosG6hDW8gPMNhGOF8xbwk/w==", + "license": "BSD-3-Clause" + }, + "node_modules/dom-serializer": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-2.0.0.tgz", + "integrity": "sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==", + "license": "MIT", + "dependencies": { + "domelementtype": "^2.3.0", + "domhandler": "^5.0.2", + "entities": "^4.2.0" + }, + "funding": { + "url": "https://github.com/cheeriojs/dom-serializer?sponsor=1" + } + }, + "node_modules/domelementtype": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.3.0.tgz", + "integrity": "sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fb55" + } + ], + "license": "BSD-2-Clause" + }, + "node_modules/domhandler": { + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-5.0.3.tgz", + "integrity": "sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==", + "license": "BSD-2-Clause", + "dependencies": { + "domelementtype": "^2.3.0" + }, + "engines": { + "node": ">= 4" + }, + "funding": { + "url": "https://github.com/fb55/domhandler?sponsor=1" + } + }, + "node_modules/domutils": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/domutils/-/domutils-3.2.2.tgz", + "integrity": "sha512-6kZKyUajlDuqlHKVX1w7gyslj9MPIXzIFiz/rGu35uC1wMi+kMhQwGhl4lt9unC9Vb9INnY9Z3/ZA3+FhASLaw==", + "license": "BSD-2-Clause", + "dependencies": { + "dom-serializer": "^2.0.0", + "domelementtype": "^2.3.0", + "domhandler": "^5.0.3" + }, + "funding": { + "url": "https://github.com/fb55/domutils?sponsor=1" + } + }, + "node_modules/dot-prop": { + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/dot-prop/-/dot-prop-9.0.0.tgz", + "integrity": "sha512-1gxPBJpI/pcjQhKgIU91II6Wkay+dLcN3M6rf2uwP8hRur3HtQXjVrdAK3sjC0piaEuxzMwjXChcETiJl47lAQ==", + "license": "MIT", + "dependencies": { + "type-fest": "^4.18.2" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/dotenv": { + "version": "17.2.2", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-17.2.2.tgz", + "integrity": "sha512-Sf2LSQP+bOlhKWWyhFsn0UsfdK/kCWRv1iuA2gXAwt3dyNabr6QSj00I2V10pidqz69soatm9ZwZvpQMTIOd5Q==", + "license": "BSD-2-Clause", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://dotenvx.com" + } + }, + "node_modules/dunder-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", + "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.1", + "es-errors": "^1.3.0", + "gopd": "^1.2.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/ecdsa-sig-formatter": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz", + "integrity": "sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==", + "license": "Apache-2.0", + "dependencies": { + "safe-buffer": "^5.0.1" + } + }, + "node_modules/ee-first": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", + "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==", + "license": "MIT" + }, + "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/encodeurl": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz", + "integrity": "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/encoding-sniffer": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/encoding-sniffer/-/encoding-sniffer-0.2.1.tgz", + "integrity": "sha512-5gvq20T6vfpekVtqrYQsSCFZ1wEg5+wW0/QaZMWkFr6BqD3NfKs0rLCx4rrVlSWJeZb5NBJgVLswK/w2MWU+Gw==", + "license": "MIT", + "dependencies": { + "iconv-lite": "^0.6.3", + "whatwg-encoding": "^3.1.1" + }, + "funding": { + "url": "https://github.com/fb55/encoding-sniffer?sponsor=1" + } + }, + "node_modules/end-of-stream": { + "version": "1.4.5", + "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.5.tgz", + "integrity": "sha512-ooEGc6HP26xXq/N+GCGOT0JKCLDGrq2bQUZrQ7gyrJiZANJ/8YDTxTpQBXGMn+WbIQXNVpyWymm7KYVICQnyOg==", + "license": "MIT", + "dependencies": { + "once": "^1.4.0" + } + }, + "node_modules/enquirer": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/enquirer/-/enquirer-2.4.1.tgz", + "integrity": "sha512-rRqJg/6gd538VHvR3PSrdRBb/1Vy2YfzHqzvbhGIQpDRKIa4FgV/54b5Q1xYSxOOwKvjXweS26E0Q+nAMwp2pQ==", + "license": "MIT", + "dependencies": { + "ansi-colors": "^4.1.1", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8.6" + } + }, + "node_modules/entities": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz", + "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==", + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.12" + }, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, + "node_modules/es-define-property": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", + "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-errors": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", + "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-object-atoms": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz", + "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-set-tostringtag": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz", + "integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.6", + "has-tostringtag": "^1.0.2", + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/escalade": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", + "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/escape-html": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", + "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==", + "license": "MIT" + }, + "node_modules/escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/escodegen": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-2.1.0.tgz", + "integrity": "sha512-2NlIDTwUWJN0mRPQOdtQBzbUHvdGY2P1VXSyU83Q3xKxM7WHX2Ql8dKq782Q9TgQUNOLEzEYu9bzLNj1q88I5w==", + "license": "BSD-2-Clause", + "dependencies": { + "esprima": "^4.0.1", + "estraverse": "^5.2.0", + "esutils": "^2.0.2" + }, + "bin": { + "escodegen": "bin/escodegen.js", + "esgenerate": "bin/esgenerate.js" + }, + "engines": { + "node": ">=6.0" + }, + "optionalDependencies": { + "source-map": "~0.6.1" + } + }, + "node_modules/esprima": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", + "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", + "license": "BSD-2-Clause", + "bin": { + "esparse": "bin/esparse.js", + "esvalidate": "bin/esvalidate.js" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "license": "BSD-2-Clause", + "engines": { + "node": ">=4.0" + } + }, + "node_modules/esutils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/etag": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", + "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/events-universal": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/events-universal/-/events-universal-1.0.1.tgz", + "integrity": "sha512-LUd5euvbMLpwOF8m6ivPCbhQeSiYVNb8Vs0fQ8QjXo0JTkEHpz8pxdQf0gStltaPpw0Cca8b39KxvK9cfKRiAw==", + "license": "Apache-2.0", + "dependencies": { + "bare-events": "^2.7.0" + } + }, + "node_modules/express": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/express/-/express-5.1.0.tgz", + "integrity": "sha512-DT9ck5YIRU+8GYzzU5kT3eHGA5iL+1Zd0EutOmTE9Dtk+Tvuzd23VBU+ec7HPNSTxXYO55gPV/hq4pSBJDjFpA==", + "license": "MIT", + "dependencies": { + "accepts": "^2.0.0", + "body-parser": "^2.2.0", + "content-disposition": "^1.0.0", + "content-type": "^1.0.5", + "cookie": "^0.7.1", + "cookie-signature": "^1.2.1", + "debug": "^4.4.0", + "encodeurl": "^2.0.0", + "escape-html": "^1.0.3", + "etag": "^1.8.1", + "finalhandler": "^2.1.0", + "fresh": "^2.0.0", + "http-errors": "^2.0.0", + "merge-descriptors": "^2.0.0", + "mime-types": "^3.0.0", + "on-finished": "^2.4.1", + "once": "^1.4.0", + "parseurl": "^1.3.3", + "proxy-addr": "^2.0.7", + "qs": "^6.14.0", + "range-parser": "^1.2.1", + "router": "^2.2.0", + "send": "^1.1.0", + "serve-static": "^2.2.0", + "statuses": "^2.0.1", + "type-is": "^2.0.1", + "vary": "^1.1.2" + }, + "engines": { + "node": ">= 18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/extract-zip": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extract-zip/-/extract-zip-2.0.1.tgz", + "integrity": "sha512-GDhU9ntwuKyGXdZBUgTIe+vXnWj0fppUEtMDL0+idd5Sta8TGpHssn/eusA9mrPr9qNDym6SxAYZjNvCn/9RBg==", + "license": "BSD-2-Clause", + "dependencies": { + "debug": "^4.1.1", + "get-stream": "^5.1.0", + "yauzl": "^2.10.0" + }, + "bin": { + "extract-zip": "cli.js" + }, + "engines": { + "node": ">= 10.17.0" + }, + "optionalDependencies": { + "@types/yauzl": "^2.9.1" + } + }, + "node_modules/extract-zip/node_modules/get-stream": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.2.0.tgz", + "integrity": "sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==", + "license": "MIT", + "dependencies": { + "pump": "^3.0.0" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/fast-fifo": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/fast-fifo/-/fast-fifo-1.3.2.tgz", + "integrity": "sha512-/d9sfos4yxzpwkDkuN7k2SqFKtYNmCTzgfEpz82x34IM9/zc8KGxQoXg1liNC/izpRM/MBdt44Nmx41ZWqk+FQ==", + "license": "MIT" + }, + "node_modules/fast-xml-parser": { + "version": "4.5.3", + "resolved": "https://registry.npmjs.org/fast-xml-parser/-/fast-xml-parser-4.5.3.tgz", + "integrity": "sha512-RKihhV+SHsIUGXObeVy9AXiBbFwkVk7Syp8XgwN5U3JV416+Gwp/GO9i0JYKmikykgz/UHRrrV4ROuZEo/T0ig==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/NaturalIntelligence" + } + ], + "license": "MIT", + "dependencies": { + "strnum": "^1.1.1" + }, + "bin": { + "fxparser": "src/cli/cli.js" + } + }, + "node_modules/fd-slicer": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/fd-slicer/-/fd-slicer-1.1.0.tgz", + "integrity": "sha512-cE1qsB/VwyQozZ+q1dGxR8LBYNZeofhEdUNGSMbQD3Gw2lAzX9Zb3uIU6Ebc/Fmyjo9AWWfnn0AUCHqtevs/8g==", + "license": "MIT", + "dependencies": { + "pend": "~1.2.0" + } + }, + "node_modules/fill-range": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", + "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", + "dev": true, + "license": "MIT", + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/finalhandler": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-2.1.0.tgz", + "integrity": "sha512-/t88Ty3d5JWQbWYgaOGCCYfXRwV1+be02WqYYlL6h0lEiUAMPM8o8qKGO01YIkOHzka2up08wvgYD0mDiI+q3Q==", + "license": "MIT", + "dependencies": { + "debug": "^4.4.0", + "encodeurl": "^2.0.0", + "escape-html": "^1.0.3", + "on-finished": "^2.4.1", + "parseurl": "^1.3.3", + "statuses": "^2.0.1" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/follow-redirects": { + "version": "1.15.11", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.11.tgz", + "integrity": "sha512-deG2P0JfjrTxl50XGCDyfI97ZGVCxIpfKYmfyrQ54n5FO/0gfIES8C/Psl6kWVDolizcaaxZJnTS0QSMxvnsBQ==", + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/RubenVerborgh" + } + ], + "license": "MIT", + "engines": { + "node": ">=4.0" + }, + "peerDependenciesMeta": { + "debug": { + "optional": true + } + } + }, + "node_modules/form-data": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.4.tgz", + "integrity": "sha512-KrGhL9Q4zjj0kiUt5OO4Mr/A/jlI2jDYs5eHBpYHPcBEVSiipAvn2Ko2HnPe20rmcuuvMHNdZFp+4IlGTMF0Ow==", + "license": "MIT", + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "es-set-tostringtag": "^2.1.0", + "hasown": "^2.0.2", + "mime-types": "^2.1.12" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/form-data-encoder": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/form-data-encoder/-/form-data-encoder-4.1.0.tgz", + "integrity": "sha512-G6NsmEW15s0Uw9XnCg+33H3ViYRyiM0hMrMhhqQOR8NFc5GhYrI+6I3u7OTw7b91J2g8rtvMBZJDbcGb2YUniw==", + "license": "MIT", + "engines": { + "node": ">= 18" + } + }, + "node_modules/form-data/node_modules/mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/form-data/node_modules/mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "license": "MIT", + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/forwarded": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", + "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/forwarded-parse": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/forwarded-parse/-/forwarded-parse-2.1.2.tgz", + "integrity": "sha512-alTFZZQDKMporBH77856pXgzhEzaUVmLCDk+egLgIgHst3Tpndzz8MnKe+GzRJRfvVdn69HhpW7cmXzvtLvJAw==", + "license": "MIT" + }, + "node_modules/fresh": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/fresh/-/fresh-2.0.0.tgz", + "integrity": "sha512-Rx/WycZ60HOaqLKAi6cHRKKI7zxWbJ31MhntmtwMoaTeF7XFH9hhBp8vITaMidfljRQ6eYWCKkaTK+ykVJHP2A==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/function-bind": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-caller-file": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", + "license": "ISC", + "engines": { + "node": "6.* || 8.* || >= 10.*" + } + }, + "node_modules/get-intrinsic": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", + "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==", + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "es-define-property": "^1.0.1", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.1.1", + "function-bind": "^1.1.2", + "get-proto": "^1.0.1", + "gopd": "^1.2.0", + "has-symbols": "^1.1.0", + "hasown": "^2.0.2", + "math-intrinsics": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", + "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", + "license": "MIT", + "dependencies": { + "dunder-proto": "^1.0.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/get-stream": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-9.0.1.tgz", + "integrity": "sha512-kVCxPF3vQM/N0B1PmoqVUqgHP+EeVjmZSQn+1oCRPxd2P21P2F19lIgbR3HBosbB1PUhOAoctJnfEn2GbN2eZA==", + "license": "MIT", + "dependencies": { + "@sec-ant/readable-stream": "^0.4.1", + "is-stream": "^4.0.1" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/get-uri": { + "version": "6.0.5", + "resolved": "https://registry.npmjs.org/get-uri/-/get-uri-6.0.5.tgz", + "integrity": "sha512-b1O07XYq8eRuVzBNgJLstU6FYc1tS6wnMtF1I1D9lE8LxZSOGZ7LhxN54yPP6mGw5f2CkXY2BQUL9Fx41qvcIg==", + "license": "MIT", + "dependencies": { + "basic-ftp": "^5.0.2", + "data-uri-to-buffer": "^6.0.2", + "debug": "^4.3.4" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/gopd": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", + "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/got": { + "version": "14.4.7", + "resolved": "https://registry.npmjs.org/got/-/got-14.4.7.tgz", + "integrity": "sha512-DI8zV1231tqiGzOiOzQWDhsBmncFW7oQDH6Zgy6pDPrqJuVZMtoSgPLLsBZQj8Jg4JFfwoOsDA8NGtLQLnIx2g==", + "license": "MIT", + "dependencies": { + "@sindresorhus/is": "^7.0.1", + "@szmarczak/http-timer": "^5.0.1", + "cacheable-lookup": "^7.0.0", + "cacheable-request": "^12.0.1", + "decompress-response": "^6.0.0", + "form-data-encoder": "^4.0.2", + "http2-wrapper": "^2.2.1", + "lowercase-keys": "^3.0.0", + "p-cancelable": "^4.0.1", + "responselike": "^3.0.0", + "type-fest": "^4.26.1" + }, + "engines": { + "node": ">=20" + }, + "funding": { + "url": "https://github.com/sindresorhus/got?sponsor=1" + } + }, + "node_modules/graceful-fs": { + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", + "license": "ISC" + }, + "node_modules/has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/has-symbols": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", + "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-tostringtag": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz", + "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==", + "license": "MIT", + "dependencies": { + "has-symbols": "^1.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/hasown": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", + "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", + "license": "MIT", + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/htmlparser2": { + "version": "10.0.0", + "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-10.0.0.tgz", + "integrity": "sha512-TwAZM+zE5Tq3lrEHvOlvwgj1XLWQCtaaibSN11Q+gGBAS7Y1uZSWwXXRe4iF6OXnaq1riyQAPFOBtYc77Mxq0g==", + "funding": [ + "https://github.com/fb55/htmlparser2?sponsor=1", + { + "type": "github", + "url": "https://github.com/sponsors/fb55" + } + ], + "license": "MIT", + "dependencies": { + "domelementtype": "^2.3.0", + "domhandler": "^5.0.3", + "domutils": "^3.2.1", + "entities": "^6.0.0" + } + }, + "node_modules/htmlparser2/node_modules/entities": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/entities/-/entities-6.0.1.tgz", + "integrity": "sha512-aN97NXWF6AWBTahfVOIrB/NShkzi5H7F9r1s9mD3cDj4Ko5f2qhhVoYMibXF7GlLveb/D2ioWay8lxI97Ven3g==", + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.12" + }, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, + "node_modules/http-cache-semantics": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.2.0.tgz", + "integrity": "sha512-dTxcvPXqPvXBQpq5dUr6mEMJX4oIEFv6bwom3FDwKRDsuIjjJGANqhBuoAn9c1RQJIdAKav33ED65E2ys+87QQ==", + "license": "BSD-2-Clause" + }, + "node_modules/http-errors": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz", + "integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==", + "license": "MIT", + "dependencies": { + "depd": "2.0.0", + "inherits": "2.0.4", + "setprototypeof": "1.2.0", + "statuses": "2.0.1", + "toidentifier": "1.0.1" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/http-errors/node_modules/statuses": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", + "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/http-link-header": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/http-link-header/-/http-link-header-1.1.3.tgz", + "integrity": "sha512-3cZ0SRL8fb9MUlU3mKM61FcQvPfXx2dBrZW3Vbg5CXa8jFlK8OaEpePenLe1oEXQduhz8b0QjsqfS59QP4AJDQ==", + "license": "MIT", + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/http-proxy-agent": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-7.0.2.tgz", + "integrity": "sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig==", + "license": "MIT", + "dependencies": { + "agent-base": "^7.1.0", + "debug": "^4.3.4" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/http2-wrapper": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/http2-wrapper/-/http2-wrapper-2.2.1.tgz", + "integrity": "sha512-V5nVw1PAOgfI3Lmeaj2Exmeg7fenjhRUgz1lPSezy1CuhPYbgQtbQj4jZfEAEMlaL+vupsvhjqCyjzob0yxsmQ==", + "license": "MIT", + "dependencies": { + "quick-lru": "^5.1.1", + "resolve-alpn": "^1.2.0" + }, + "engines": { + "node": ">=10.19.0" + } + }, + "node_modules/https-proxy-agent": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.6.tgz", + "integrity": "sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==", + "license": "MIT", + "dependencies": { + "agent-base": "^7.1.2", + "debug": "4" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/iconv-lite": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", + "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", + "license": "MIT", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/ignore-by-default": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/ignore-by-default/-/ignore-by-default-1.0.1.tgz", + "integrity": "sha512-Ius2VYcGNk7T90CppJqcIkS5ooHUZyIQK+ClZfMfMNFEF9VSE73Fq+906u/CWu92x4gzZMWOwfFYckPObzdEbA==", + "dev": true, + "license": "ISC" + }, + "node_modules/image-ssim": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/image-ssim/-/image-ssim-0.2.0.tgz", + "integrity": "sha512-W7+sO6/yhxy83L0G7xR8YAc5Z5QFtYEXXRV6EaE8tuYBZJnA3gVgp3q7X7muhLZVodeb9UfvjSbwt9VJwjIYAg==", + "license": "MIT" + }, + "node_modules/import-in-the-middle": { + "version": "1.14.4", + "resolved": "https://registry.npmjs.org/import-in-the-middle/-/import-in-the-middle-1.14.4.tgz", + "integrity": "sha512-eWjxh735SJLFJJDs5X82JQ2405OdJeAHDBnaoFCfdr5GVc7AWc9xU7KbrF+3Xd5F2ccP1aQFKtY+65X6EfKZ7A==", + "license": "Apache-2.0", + "dependencies": { + "acorn": "^8.14.0", + "acorn-import-attributes": "^1.9.5", + "cjs-module-lexer": "^1.2.2", + "module-details-from-path": "^1.0.3" + } + }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "license": "ISC" + }, + "node_modules/intl-messageformat": { + "version": "10.7.16", + "resolved": "https://registry.npmjs.org/intl-messageformat/-/intl-messageformat-10.7.16.tgz", + "integrity": "sha512-UmdmHUmp5CIKKjSoE10la5yfU+AYJAaiYLsodbjL4lji83JNvgOQUjGaGhGrpFCb0Uh7sl7qfP1IyILa8Z40ug==", + "license": "BSD-3-Clause", + "dependencies": { + "@formatjs/ecma402-abstract": "2.3.4", + "@formatjs/fast-memoize": "2.2.7", + "@formatjs/icu-messageformat-parser": "2.11.2", + "tslib": "^2.8.0" + } + }, + "node_modules/ip-address": { + "version": "10.0.1", + "resolved": "https://registry.npmjs.org/ip-address/-/ip-address-10.0.1.tgz", + "integrity": "sha512-NWv9YLW4PoW2B7xtzaS3NCot75m6nK7Icdv0o3lfMceJVRfSoQwqD4wEH5rLwoKJwUiZ/rfpiVBhnaF0FK4HoA==", + "license": "MIT", + "engines": { + "node": ">= 12" + } + }, + "node_modules/ipaddr.js": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", + "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==", + "license": "MIT", + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/is-binary-path": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", + "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", + "dev": true, + "license": "MIT", + "dependencies": { + "binary-extensions": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-core-module": { + "version": "2.16.1", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.16.1.tgz", + "integrity": "sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==", + "license": "MIT", + "dependencies": { + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-docker": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-2.2.1.tgz", + "integrity": "sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ==", + "license": "MIT", + "bin": { + "is-docker": "cli.js" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-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", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-gzip": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-gzip/-/is-gzip-2.0.0.tgz", + "integrity": "sha512-jtO4Njg6q58zDo/Pu4027beSZ0VdsZlt8/5Moco6yAg+DIxb5BK/xUYqYG2+MD4+piKldXJNHxRkhEYI2fvrxA==", + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/is-promise": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-4.0.0.tgz", + "integrity": "sha512-hvpoI6korhJMnej285dSg6nu1+e6uxs7zG3BYAm5byqDsgJNWwxzM6z6iZiAgQR4TJ30JmBTOwqZUw3WlyH3AQ==", + "license": "MIT" + }, + "node_modules/is-stream": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-4.0.1.tgz", + "integrity": "sha512-Dnz92NInDqYckGEUJv689RbRiTSEHCQ7wOVeALbkOz999YpqT46yMRIGtSNl2iCL1waAZSx40+h59NV/EwzV/A==", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-wsl": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-2.2.0.tgz", + "integrity": "sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==", + "license": "MIT", + "dependencies": { + "is-docker": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/jpeg-js": { + "version": "0.4.4", + "resolved": "https://registry.npmjs.org/jpeg-js/-/jpeg-js-0.4.4.tgz", + "integrity": "sha512-WZzeDOEtTOBK4Mdsar0IqEU5sMr3vSV2RqkAIzUEV2BHnUfKGyswWFPFwK5EeDo93K3FohSHbLAjj0s1Wzd+dg==", + "license": "BSD-3-Clause" + }, + "node_modules/js-library-detector": { + "version": "6.7.0", + "resolved": "https://registry.npmjs.org/js-library-detector/-/js-library-detector-6.7.0.tgz", + "integrity": "sha512-c80Qupofp43y4cJ7+8TTDN/AsDwLi5oOm/plBrWI+iQt485vKXCco+yVmOwEgdo9VOdsYTuV0UlTeetVPTriXA==", + "license": "MIT", + "engines": { + "node": ">=12" + } + }, + "node_modules/json-buffer": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", + "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", + "license": "MIT" + }, + "node_modules/jsonwebtoken": { + "version": "9.0.2", + "resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-9.0.2.tgz", + "integrity": "sha512-PRp66vJ865SSqOlgqS8hujT5U4AOgMfhrwYIuIhfKaoSCZcirrmASQr8CX7cUg+RMih+hgznrjp99o+W4pJLHQ==", + "license": "MIT", + "dependencies": { + "jws": "^3.2.2", + "lodash.includes": "^4.3.0", + "lodash.isboolean": "^3.0.3", + "lodash.isinteger": "^4.0.4", + "lodash.isnumber": "^3.0.3", + "lodash.isplainobject": "^4.0.6", + "lodash.isstring": "^4.0.1", + "lodash.once": "^4.0.0", + "ms": "^2.1.1", + "semver": "^7.5.4" + }, + "engines": { + "node": ">=12", + "npm": ">=6" + } + }, + "node_modules/jwa": { + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/jwa/-/jwa-1.4.2.tgz", + "integrity": "sha512-eeH5JO+21J78qMvTIDdBXidBd6nG2kZjg5Ohz/1fpa28Z4CcsWUzJ1ZZyFq/3z3N17aZy+ZuBoHljASbL1WfOw==", + "license": "MIT", + "dependencies": { + "buffer-equal-constant-time": "^1.0.1", + "ecdsa-sig-formatter": "1.0.11", + "safe-buffer": "^5.0.1" + } + }, + "node_modules/jws": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/jws/-/jws-3.2.2.tgz", + "integrity": "sha512-YHlZCB6lMTllWDtSPHz/ZXTsi8S00usEV6v1tjq8tOUZzw7DpSDWVXjXDre6ed1w/pd495ODpHZYSdkRTsa0HA==", + "license": "MIT", + "dependencies": { + "jwa": "^1.4.1", + "safe-buffer": "^5.0.1" + } + }, + "node_modules/kareem": { + "version": "2.6.3", + "resolved": "https://registry.npmjs.org/kareem/-/kareem-2.6.3.tgz", + "integrity": "sha512-C3iHfuGUXK2u8/ipq9LfjFfXFxAZMQJJq7vLS45r3D9Y2xQ/m4S8zaR4zMLFWh9AsNPXmcFfUDhTEO8UIC/V6Q==", + "license": "Apache-2.0", + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/keyv": { + "version": "4.5.4", + "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", + "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", + "license": "MIT", + "dependencies": { + "json-buffer": "3.0.1" + } + }, + "node_modules/legacy-javascript": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/legacy-javascript/-/legacy-javascript-0.0.1.tgz", + "integrity": "sha512-lPyntS4/aS7jpuvOlitZDFifBCb4W8L/3QU0PLbUTUj+zYah8rfVjYic88yG7ZKTxhS5h9iz7duT8oUXKszLhg==", + "license": "Apache-2.0" + }, + "node_modules/lighthouse": { + "version": "12.8.2", + "resolved": "https://registry.npmjs.org/lighthouse/-/lighthouse-12.8.2.tgz", + "integrity": "sha512-+5SKYzVaTFj22MgoYDPNrP9tlD2/Ay7j3SxPSFD9FpPyVxGr4UtOQGKyrdZ7wCmcnBaFk0mCkPfARU3CsE0nvA==", + "license": "Apache-2.0", + "dependencies": { + "@paulirish/trace_engine": "0.0.59", + "@sentry/node": "^9.28.1", + "axe-core": "^4.10.3", + "chrome-launcher": "^1.2.0", + "configstore": "^7.0.0", + "csp_evaluator": "1.1.5", + "devtools-protocol": "0.0.1507524", + "enquirer": "^2.3.6", + "http-link-header": "^1.1.1", + "intl-messageformat": "^10.5.3", + "jpeg-js": "^0.4.4", + "js-library-detector": "^6.7.0", + "lighthouse-logger": "^2.0.2", + "lighthouse-stack-packs": "1.12.2", + "lodash-es": "^4.17.21", + "lookup-closest-locale": "6.2.0", + "metaviewport-parser": "0.3.0", + "open": "^8.4.0", + "parse-cache-control": "1.0.1", + "puppeteer-core": "^24.17.1", + "robots-parser": "^3.0.1", + "speedline-core": "^1.4.3", + "third-party-web": "^0.27.0", + "tldts-icann": "^7.0.12", + "ws": "^7.0.0", + "yargs": "^17.3.1", + "yargs-parser": "^21.0.0" + }, + "bin": { + "chrome-debug": "core/scripts/manual-chrome-launcher.js", + "lighthouse": "cli/index.js", + "smokehouse": "cli/test/smokehouse/frontends/smokehouse-bin.js" + }, + "engines": { + "node": ">=18.16" + } + }, + "node_modules/lighthouse-logger": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/lighthouse-logger/-/lighthouse-logger-2.0.2.tgz", + "integrity": "sha512-vWl2+u5jgOQuZR55Z1WM0XDdrJT6mzMP8zHUct7xTlWhuQs+eV0g+QL0RQdFjT54zVmbhLCP8vIVpy1wGn/gCg==", + "license": "Apache-2.0", + "dependencies": { + "debug": "^4.4.1", + "marky": "^1.2.2" + } + }, + "node_modules/lighthouse-stack-packs": { + "version": "1.12.2", + "resolved": "https://registry.npmjs.org/lighthouse-stack-packs/-/lighthouse-stack-packs-1.12.2.tgz", + "integrity": "sha512-Ug8feS/A+92TMTCK6yHYLwaFMuelK/hAKRMdldYkMNwv+d9PtWxjXEg6rwKtsUXTADajhdrhXyuNCJ5/sfmPFw==", + "license": "Apache-2.0" + }, + "node_modules/lodash-es": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash-es/-/lodash-es-4.17.21.tgz", + "integrity": "sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw==", + "license": "MIT" + }, + "node_modules/lodash.deburr": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/lodash.deburr/-/lodash.deburr-4.1.0.tgz", + "integrity": "sha512-m/M1U1f3ddMCs6Hq2tAsYThTBDaAKFDX3dwDo97GEYzamXi9SqUpjWi/Rrj/gf3X2n8ktwgZrlP1z6E3v/IExQ==", + "license": "MIT" + }, + "node_modules/lodash.includes": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/lodash.includes/-/lodash.includes-4.3.0.tgz", + "integrity": "sha512-W3Bx6mdkRTGtlJISOvVD/lbqjTlPPUDTMnlXZFnVwi9NKJ6tiAk6LVdlhZMm17VZisqhKcgzpO5Wz91PCt5b0w==", + "license": "MIT" + }, + "node_modules/lodash.isboolean": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/lodash.isboolean/-/lodash.isboolean-3.0.3.tgz", + "integrity": "sha512-Bz5mupy2SVbPHURB98VAcw+aHh4vRV5IPNhILUCsOzRmsTmSQ17jIuqopAentWoehktxGd9e/hbIXq980/1QJg==", + "license": "MIT" + }, + "node_modules/lodash.isinteger": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/lodash.isinteger/-/lodash.isinteger-4.0.4.tgz", + "integrity": "sha512-DBwtEWN2caHQ9/imiNeEA5ys1JoRtRfY3d7V9wkqtbycnAmTvRRmbHKDV4a0EYc678/dia0jrte4tjYwVBaZUA==", + "license": "MIT" + }, + "node_modules/lodash.isnumber": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/lodash.isnumber/-/lodash.isnumber-3.0.3.tgz", + "integrity": "sha512-QYqzpfwO3/CWf3XP+Z+tkQsfaLL/EnUlXWVkIk5FUPc4sBdTehEqZONuyRt2P67PXAk+NXmTBcc97zw9t1FQrw==", + "license": "MIT" + }, + "node_modules/lodash.isplainobject": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz", + "integrity": "sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA==", + "license": "MIT" + }, + "node_modules/lodash.isstring": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/lodash.isstring/-/lodash.isstring-4.0.1.tgz", + "integrity": "sha512-0wJxfxH1wgO3GrbuP+dTTk7op+6L41QCXbGINEmD+ny/G/eCqGzxyCsh7159S+mgDDcoarnBw6PC1PS5+wUGgw==", + "license": "MIT" + }, + "node_modules/lodash.once": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/lodash.once/-/lodash.once-4.1.1.tgz", + "integrity": "sha512-Sb487aTOCr9drQVL8pIxOzVhafOjZN9UU54hiN8PU3uAiSV7lx1yYNpbNmex2PK6dSJoNTSJUUswT651yww3Mg==", + "license": "MIT" + }, + "node_modules/lookup-closest-locale": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/lookup-closest-locale/-/lookup-closest-locale-6.2.0.tgz", + "integrity": "sha512-/c2kL+Vnp1jnV6K6RpDTHK3dgg0Tu2VVp+elEiJpjfS1UyY7AjOYHohRug6wT0OpoX2qFgNORndE9RqesfVxWQ==", + "license": "MIT" + }, + "node_modules/lowercase-keys": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-3.0.0.tgz", + "integrity": "sha512-ozCC6gdQ+glXOQsveKD0YsDy8DSQFjDTz4zyzEHNV5+JP5D62LmfDZ6o1cycFx9ouG940M5dE8C8CTewdj2YWQ==", + "license": "MIT", + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/lru-cache": { + "version": "7.18.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-7.18.3.tgz", + "integrity": "sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA==", + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/marky": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/marky/-/marky-1.3.0.tgz", + "integrity": "sha512-ocnPZQLNpvbedwTy9kNrQEsknEfgvcLMvOtz3sFeWApDq1MXH1TqkCIx58xlpESsfwQOnuBO9beyQuNGzVvuhQ==", + "license": "Apache-2.0" + }, + "node_modules/math-intrinsics": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", + "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/media-typer": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-1.1.0.tgz", + "integrity": "sha512-aisnrDP4GNe06UcKFnV5bfMNPBUw4jsLGaWwWfnH3v02GnBuXX2MCVn5RbrWo0j3pczUilYblq7fQ7Nw2t5XKw==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/memory-pager": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/memory-pager/-/memory-pager-1.5.0.tgz", + "integrity": "sha512-ZS4Bp4r/Zoeq6+NLJpP+0Zzm0pR8whtGPf1XExKLJBAczGMnSi3It14OiNCStjQjM6NU1okjQGSxgEZN8eBYKg==", + "license": "MIT" + }, + "node_modules/merge-descriptors": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-2.0.0.tgz", + "integrity": "sha512-Snk314V5ayFLhp3fkUREub6WtjBfPdCPY1Ln8/8munuLuiYhsABgBVWsozAG+MWMbVEvcdcpbi9R7ww22l9Q3g==", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/metaviewport-parser": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/metaviewport-parser/-/metaviewport-parser-0.3.0.tgz", + "integrity": "sha512-EoYJ8xfjQ6kpe9VbVHvZTZHiOl4HL1Z18CrZ+qahvLXT7ZO4YTC2JMyt5FaUp9JJp6J4Ybb/z7IsCXZt86/QkQ==", + "license": "MIT" + }, + "node_modules/mime-db": { + "version": "1.54.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.54.0.tgz", + "integrity": "sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-3.0.1.tgz", + "integrity": "sha512-xRc4oEhT6eaBpU1XF7AjpOFD+xQmXNB5OVKwp4tqCuBpHLS/ZbBDrc07mYTDqVMg6PfxUjjNp85O6Cd2Z/5HWA==", + "license": "MIT", + "dependencies": { + "mime-db": "^1.54.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mimic-response": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-4.0.0.tgz", + "integrity": "sha512-e5ISH9xMYU0DzrT+jl8q2ze9D6eWBto+I8CNpe+VI+K2J/F/k3PdkdTdz4wvGVH4NTpo+NRYTVIuMQEMMcsLqg==", + "license": "MIT", + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/minimist": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", + "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/mitt": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/mitt/-/mitt-3.0.1.tgz", + "integrity": "sha512-vKivATfr97l2/QBCYAkXYDbrIWPM2IIKEl7YPhjCvKlG3kE2gm+uBo6nEXK3M5/Ffh/FLpKExzOQ3JJoJGFKBw==", + "license": "MIT" + }, + "node_modules/mkdirp": { + "version": "0.5.6", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz", + "integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==", + "license": "MIT", + "dependencies": { + "minimist": "^1.2.6" + }, + "bin": { + "mkdirp": "bin/cmd.js" + } + }, + "node_modules/module-details-from-path": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/module-details-from-path/-/module-details-from-path-1.0.4.tgz", + "integrity": "sha512-EGWKgxALGMgzvxYF1UyGTy0HXX/2vHLkw6+NvDKW2jypWbHpjQuj4UMcqQWXHERJhVGKikolT06G3bcKe4fi7w==", + "license": "MIT" + }, + "node_modules/mongodb": { + "version": "6.18.0", + "resolved": "https://registry.npmjs.org/mongodb/-/mongodb-6.18.0.tgz", + "integrity": "sha512-fO5ttN9VC8P0F5fqtQmclAkgXZxbIkYRTUi1j8JO6IYwvamkhtYDilJr35jOPELR49zqCJgXZWwCtW7B+TM8vQ==", + "license": "Apache-2.0", + "dependencies": { + "@mongodb-js/saslprep": "^1.1.9", + "bson": "^6.10.4", + "mongodb-connection-string-url": "^3.0.0" + }, + "engines": { + "node": ">=16.20.1" + }, + "peerDependencies": { + "@aws-sdk/credential-providers": "^3.188.0", + "@mongodb-js/zstd": "^1.1.0 || ^2.0.0", + "gcp-metadata": "^5.2.0", + "kerberos": "^2.0.1", + "mongodb-client-encryption": ">=6.0.0 <7", + "snappy": "^7.2.2", + "socks": "^2.7.1" + }, + "peerDependenciesMeta": { + "@aws-sdk/credential-providers": { + "optional": true + }, + "@mongodb-js/zstd": { + "optional": true + }, + "gcp-metadata": { + "optional": true + }, + "kerberos": { + "optional": true + }, + "mongodb-client-encryption": { + "optional": true + }, + "snappy": { + "optional": true + }, + "socks": { + "optional": true + } + } + }, + "node_modules/mongodb-connection-string-url": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/mongodb-connection-string-url/-/mongodb-connection-string-url-3.0.2.tgz", + "integrity": "sha512-rMO7CGo/9BFwyZABcKAWL8UJwH/Kc2x0g72uhDWzG48URRax5TCIcJ7Rc3RZqffZzO/Gwff/jyKwCU9TN8gehA==", + "license": "Apache-2.0", + "dependencies": { + "@types/whatwg-url": "^11.0.2", + "whatwg-url": "^14.1.0 || ^13.0.0" + } + }, + "node_modules/mongoose": { + "version": "8.18.1", + "resolved": "https://registry.npmjs.org/mongoose/-/mongoose-8.18.1.tgz", + "integrity": "sha512-K0RfrUXXufqNRZZjvAGdyjydB91SnbWxlwFYi5t7zN2DxVWFD3c6puia0/7xfBwZm6RCpYOVdYFlRFpoDWiC+w==", + "license": "MIT", + "dependencies": { + "bson": "^6.10.4", + "kareem": "2.6.3", + "mongodb": "~6.18.0", + "mpath": "0.9.0", + "mquery": "5.0.0", + "ms": "2.1.3", + "sift": "17.1.3" + }, + "engines": { + "node": ">=16.20.1" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/mongoose" + } + }, + "node_modules/mpath": { + "version": "0.9.0", + "resolved": "https://registry.npmjs.org/mpath/-/mpath-0.9.0.tgz", + "integrity": "sha512-ikJRQTk8hw5DEoFVxHG1Gn9T/xcjtdnOKIU1JTmGjZZlg9LST2mBLmcX3/ICIbgJydT2GOc15RnNy5mHmzfSew==", + "license": "MIT", + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/mquery": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/mquery/-/mquery-5.0.0.tgz", + "integrity": "sha512-iQMncpmEK8R8ncT8HJGsGc9Dsp8xcgYMVSbs5jgnm1lFHTZqMJTUWTDx1LBO8+mK3tPNZWFLBghQEIOULSTHZg==", + "license": "MIT", + "dependencies": { + "debug": "4.x" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "license": "MIT" + }, + "node_modules/multer": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/multer/-/multer-2.0.2.tgz", + "integrity": "sha512-u7f2xaZ/UG8oLXHvtF/oWTRvT44p9ecwBBqTwgJVq0+4BW1g8OW01TyMEGWBHbyMOYVHXslaut7qEQ1meATXgw==", + "license": "MIT", + "dependencies": { + "append-field": "^1.0.0", + "busboy": "^1.6.0", + "concat-stream": "^2.0.0", + "mkdirp": "^0.5.6", + "object-assign": "^4.1.1", + "type-is": "^1.6.18", + "xtend": "^4.0.2" + }, + "engines": { + "node": ">= 10.16.0" + } + }, + "node_modules/multer/node_modules/media-typer": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", + "integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/multer/node_modules/mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/multer/node_modules/mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "license": "MIT", + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/multer/node_modules/type-is": { + "version": "1.6.18", + "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", + "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", + "license": "MIT", + "dependencies": { + "media-typer": "0.3.0", + "mime-types": "~2.1.24" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/negotiator": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-1.0.0.tgz", + "integrity": "sha512-8Ofs/AUQh8MaEcrlq5xOX0CQ9ypTF5dl78mjlMNfOK08fzpgTHQRQPBxcPlEtIw0yRpws+Zo/3r+5WRby7u3Gg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/netmask": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/netmask/-/netmask-2.0.2.tgz", + "integrity": "sha512-dBpDMdxv9Irdq66304OLfEmQ9tbNRFnFTuZiLo+bD+r332bBmMJ8GBLXklIXXgxd3+v9+KUnZaUR5PJMa75Gsg==", + "license": "MIT", + "engines": { + "node": ">= 0.4.0" + } + }, + "node_modules/node-addon-api": { + "version": "8.5.0", + "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-8.5.0.tgz", + "integrity": "sha512-/bRZty2mXUIFY/xU5HLvveNHlswNJej+RnxBjOMkidWfwZzgTbPG1E3K5TOxRLOR+5hX7bSofy8yf1hZevMS8A==", + "license": "MIT", + "engines": { + "node": "^18 || ^20 || >= 21" + } + }, + "node_modules/node-gyp-build": { + "version": "4.8.4", + "resolved": "https://registry.npmjs.org/node-gyp-build/-/node-gyp-build-4.8.4.tgz", + "integrity": "sha512-LA4ZjwlnUblHVgq0oBF3Jl/6h/Nvs5fzBLwdEF4nuxnFdsfajde4WfxtJr3CaiH+F6ewcIB/q4jQ4UzPyid+CQ==", + "license": "MIT", + "bin": { + "node-gyp-build": "bin.js", + "node-gyp-build-optional": "optional.js", + "node-gyp-build-test": "build-test.js" + } + }, + "node_modules/nodemailer": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/nodemailer/-/nodemailer-7.0.6.tgz", + "integrity": "sha512-F44uVzgwo49xboqbFgBGkRaiMgtoBrBEWCVincJPK9+S9Adkzt/wXCLKbf7dxucmxfTI5gHGB+bEmdyzN6QKjw==", + "license": "MIT-0", + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/nodemon": { + "version": "3.1.10", + "resolved": "https://registry.npmjs.org/nodemon/-/nodemon-3.1.10.tgz", + "integrity": "sha512-WDjw3pJ0/0jMFmyNDp3gvY2YizjLmmOUQo6DEBY+JgdvW/yQ9mEeSw6H5ythl5Ny2ytb7f9C2nIbjSxMNzbJXw==", + "dev": true, + "license": "MIT", + "dependencies": { + "chokidar": "^3.5.2", + "debug": "^4", + "ignore-by-default": "^1.0.1", + "minimatch": "^3.1.2", + "pstree.remy": "^1.1.8", + "semver": "^7.5.3", + "simple-update-notifier": "^2.0.0", + "supports-color": "^5.5.0", + "touch": "^3.1.0", + "undefsafe": "^2.0.5" + }, + "bin": { + "nodemon": "bin/nodemon.js" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/nodemon" + } + }, + "node_modules/normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/normalize-strings": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/normalize-strings/-/normalize-strings-1.1.1.tgz", + "integrity": "sha512-fARPRdTwmrQDLYhmeh7j/eZwrCP6WzxD6uKOdK/hT/uKACAE9AG2Bc2dgqOZLkfmmctHpfcJ9w3AQnfLgg3GYg==", + "license": "MIT" + }, + "node_modules/normalize-url": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-8.0.2.tgz", + "integrity": "sha512-Ee/R3SyN4BuynXcnTaekmaVdbDAEiNrHqjQIA37mHU8G9pf7aaAD4ZX3XjBLo6rsdcxA/gtkcNYZLt30ACgynw==", + "license": "MIT", + "engines": { + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/nth-check": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-2.1.1.tgz", + "integrity": "sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==", + "license": "BSD-2-Clause", + "dependencies": { + "boolbase": "^1.0.0" + }, + "funding": { + "url": "https://github.com/fb55/nth-check?sponsor=1" + } + }, + "node_modules/object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object-inspect": { + "version": "1.13.4", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz", + "integrity": "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/on-finished": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", + "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", + "license": "MIT", + "dependencies": { + "ee-first": "1.1.1" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "license": "ISC", + "dependencies": { + "wrappy": "1" + } + }, + "node_modules/open": { + "version": "8.4.2", + "resolved": "https://registry.npmjs.org/open/-/open-8.4.2.tgz", + "integrity": "sha512-7x81NCL719oNbsq/3mh+hVrAWmFuEYUqrq/Iw3kUzH8ReypT9QQ0BLoJS7/G9k6N81XjW4qHWtjWwe/9eLy1EQ==", + "license": "MIT", + "dependencies": { + "define-lazy-prop": "^2.0.0", + "is-docker": "^2.1.1", + "is-wsl": "^2.2.0" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-cancelable": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/p-cancelable/-/p-cancelable-4.0.1.tgz", + "integrity": "sha512-wBowNApzd45EIKdO1LaU+LrMBwAcjfPaYtVzV3lmfM3gf8Z4CHZsiIqlM8TZZ8okYvh5A1cP6gTfCRQtwUpaUg==", + "license": "MIT", + "engines": { + "node": ">=14.16" + } + }, + "node_modules/p-limit": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "license": "MIT", + "dependencies": { + "yocto-queue": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/pac-proxy-agent": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/pac-proxy-agent/-/pac-proxy-agent-7.2.0.tgz", + "integrity": "sha512-TEB8ESquiLMc0lV8vcd5Ql/JAKAoyzHFXaStwjkzpOpC5Yv+pIzLfHvjTSdf3vpa2bMiUQrg9i6276yn8666aA==", + "license": "MIT", + "dependencies": { + "@tootallnate/quickjs-emscripten": "^0.23.0", + "agent-base": "^7.1.2", + "debug": "^4.3.4", + "get-uri": "^6.0.1", + "http-proxy-agent": "^7.0.0", + "https-proxy-agent": "^7.0.6", + "pac-resolver": "^7.0.1", + "socks-proxy-agent": "^8.0.5" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/pac-resolver": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/pac-resolver/-/pac-resolver-7.0.1.tgz", + "integrity": "sha512-5NPgf87AT2STgwa2ntRMr45jTKrYBGkVU36yT0ig/n/GMAa3oPqhZfIQ2kMEimReg0+t9kZViDVZ83qfVUlckg==", + "license": "MIT", + "dependencies": { + "degenerator": "^5.0.0", + "netmask": "^2.0.2" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/parse-cache-control": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/parse-cache-control/-/parse-cache-control-1.0.1.tgz", + "integrity": "sha512-60zvsJReQPX5/QP0Kzfd/VrpjScIQ7SHBW6bFCYfEP+fp0Eppr1SHhIO5nd1PjZtvclzSzES9D/p5nFJurwfWg==" + }, + "node_modules/parse5": { + "version": "7.3.0", + "resolved": "https://registry.npmjs.org/parse5/-/parse5-7.3.0.tgz", + "integrity": "sha512-IInvU7fabl34qmi9gY8XOVxhYyMyuH2xUNpb2q8/Y+7552KlejkRvqvD19nMoUW/uQGGbqNpA6Tufu5FL5BZgw==", + "license": "MIT", + "dependencies": { + "entities": "^6.0.0" + }, + "funding": { + "url": "https://github.com/inikulin/parse5?sponsor=1" + } + }, + "node_modules/parse5-htmlparser2-tree-adapter": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/parse5-htmlparser2-tree-adapter/-/parse5-htmlparser2-tree-adapter-7.1.0.tgz", + "integrity": "sha512-ruw5xyKs6lrpo9x9rCZqZZnIUntICjQAd0Wsmp396Ul9lN/h+ifgVV1x1gZHi8euej6wTfpqX8j+BFQxF0NS/g==", + "license": "MIT", + "dependencies": { + "domhandler": "^5.0.3", + "parse5": "^7.0.0" + }, + "funding": { + "url": "https://github.com/inikulin/parse5?sponsor=1" + } + }, + "node_modules/parse5-parser-stream": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/parse5-parser-stream/-/parse5-parser-stream-7.1.2.tgz", + "integrity": "sha512-JyeQc9iwFLn5TbvvqACIF/VXG6abODeB3Fwmv/TGdLk2LfbWkaySGY72at4+Ty7EkPZj854u4CrICqNk2qIbow==", + "license": "MIT", + "dependencies": { + "parse5": "^7.0.0" + }, + "funding": { + "url": "https://github.com/inikulin/parse5?sponsor=1" + } + }, + "node_modules/parse5/node_modules/entities": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/entities/-/entities-6.0.1.tgz", + "integrity": "sha512-aN97NXWF6AWBTahfVOIrB/NShkzi5H7F9r1s9mD3cDj4Ko5f2qhhVoYMibXF7GlLveb/D2ioWay8lxI97Ven3g==", + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.12" + }, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, + "node_modules/parseurl": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", + "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/path-parse": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", + "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", + "license": "MIT" + }, + "node_modules/path-to-regexp": { + "version": "8.2.0", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-8.2.0.tgz", + "integrity": "sha512-TdrF7fW9Rphjq4RjrW0Kp2AW0Ahwu9sRGTkS6bvDi0SCwZlEZYmcfDbEsTz8RVk0EHIS/Vd1bv3JhG+1xZuAyQ==", + "license": "MIT", + "engines": { + "node": ">=16" + } + }, + "node_modules/pend": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/pend/-/pend-1.2.0.tgz", + "integrity": "sha512-F3asv42UuXchdzt+xXqfW1OGlVBe+mxa2mqI0pg5yAHZPvFmY3Y6drSf/GQ1A86WgWEN9Kzh/WrgKa6iGcHXLg==", + "license": "MIT" + }, + "node_modules/pg-int8": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/pg-int8/-/pg-int8-1.0.1.tgz", + "integrity": "sha512-WCtabS6t3c8SkpDBUlb1kjOs7l66xsGdKpIPZsg4wR+B3+u9UAum2odSsF9tnvxg80h4ZxLWMy4pRjOsFIqQpw==", + "license": "ISC", + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/pg-protocol": { + "version": "1.10.3", + "resolved": "https://registry.npmjs.org/pg-protocol/-/pg-protocol-1.10.3.tgz", + "integrity": "sha512-6DIBgBQaTKDJyxnXaLiLR8wBpQQcGWuAESkRBX/t6OwA8YsqP+iVSiond2EDy6Y/dsGk8rh/jtax3js5NeV7JQ==", + "license": "MIT" + }, + "node_modules/pg-types": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/pg-types/-/pg-types-2.2.0.tgz", + "integrity": "sha512-qTAAlrEsl8s4OiEQY69wDvcMIdQN6wdz5ojQiOy6YRMuynxenON0O5oCpJI6lshc6scgAY8qvJ2On/p+CXY0GA==", + "license": "MIT", + "dependencies": { + "pg-int8": "1.0.1", + "postgres-array": "~2.0.0", + "postgres-bytea": "~1.0.0", + "postgres-date": "~1.0.4", + "postgres-interval": "^1.1.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/playwright": { + "version": "1.55.0", + "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.55.0.tgz", + "integrity": "sha512-sdCWStblvV1YU909Xqx0DhOjPZE4/5lJsIS84IfN9dAZfcl/CIZ5O8l3o0j7hPMjDvqoTF8ZUcc+i/GL5erstA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "playwright-core": "1.55.0" + }, + "bin": { + "playwright": "cli.js" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "fsevents": "2.3.2" + } + }, + "node_modules/playwright-core": { + "version": "1.55.0", + "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.55.0.tgz", + "integrity": "sha512-GvZs4vU3U5ro2nZpeiwyb0zuFaqb9sUiAJuyrWpcGouD8y9/HLgGbNRjIph7zU9D3hnPaisMl9zG9CgFi/biIg==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "playwright-core": "cli.js" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/playwright/node_modules/fsevents": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", + "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/pluralize": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/pluralize/-/pluralize-8.0.0.tgz", + "integrity": "sha512-Nc3IT5yHzflTfbjgqWcCPpo7DaKy4FnpB0l/zCAW0Tc7jxAiuqSxHasntB3D7887LSrA93kDJ9IXovxJYxyLCA==", + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/postgres-array": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/postgres-array/-/postgres-array-2.0.0.tgz", + "integrity": "sha512-VpZrUqU5A69eQyW2c5CA1jtLecCsN2U/bD6VilrFDWq5+5UIEVO7nazS3TEcHf1zuPYO/sqGvUvW62g86RXZuA==", + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/postgres-bytea": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/postgres-bytea/-/postgres-bytea-1.0.0.tgz", + "integrity": "sha512-xy3pmLuQqRBZBXDULy7KbaitYqLcmxigw14Q5sj8QBVLqEwXfeybIKVWiqAXTlcvdvb0+xkOtDbfQMOf4lST1w==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/postgres-date": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/postgres-date/-/postgres-date-1.0.7.tgz", + "integrity": "sha512-suDmjLVQg78nMK2UZ454hAG+OAW+HQPZ6n++TNDUX+L0+uUlLywnoxJKDou51Zm+zTCjrCl0Nq6J9C5hP9vK/Q==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/postgres-interval": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/postgres-interval/-/postgres-interval-1.2.0.tgz", + "integrity": "sha512-9ZhXKM/rw350N1ovuWHbGxnGh/SNJ4cnxHiM0rxE4VN41wsg8P8zWn9hv/buK00RP4WvlOyr/RBDiptyxVbkZQ==", + "license": "MIT", + "dependencies": { + "xtend": "^4.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/progress": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz", + "integrity": "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==", + "license": "MIT", + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/proxy-addr": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", + "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==", + "license": "MIT", + "dependencies": { + "forwarded": "0.2.0", + "ipaddr.js": "1.9.1" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/proxy-agent": { + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/proxy-agent/-/proxy-agent-6.5.0.tgz", + "integrity": "sha512-TmatMXdr2KlRiA2CyDu8GqR8EjahTG3aY3nXjdzFyoZbmB8hrBsTyMezhULIXKnC0jpfjlmiZ3+EaCzoInSu/A==", + "license": "MIT", + "dependencies": { + "agent-base": "^7.1.2", + "debug": "^4.3.4", + "http-proxy-agent": "^7.0.1", + "https-proxy-agent": "^7.0.6", + "lru-cache": "^7.14.1", + "pac-proxy-agent": "^7.1.0", + "proxy-from-env": "^1.1.0", + "socks-proxy-agent": "^8.0.5" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/proxy-from-env": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", + "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==", + "license": "MIT" + }, + "node_modules/pstree.remy": { + "version": "1.1.8", + "resolved": "https://registry.npmjs.org/pstree.remy/-/pstree.remy-1.1.8.tgz", + "integrity": "sha512-77DZwxQmxKnu3aR542U+X8FypNzbfJ+C5XQDk3uWjWxn6151aIMGthWYRXTqT1E5oJvg+ljaa2OJi+VfvCOQ8w==", + "dev": true, + "license": "MIT" + }, + "node_modules/pump": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.3.tgz", + "integrity": "sha512-todwxLMY7/heScKmntwQG8CXVkWUOdYxIvY2s0VWAAMh/nd8SoYiRaKjlr7+iCs984f2P8zvrfWcDDYVb73NfA==", + "license": "MIT", + "dependencies": { + "end-of-stream": "^1.1.0", + "once": "^1.3.1" + } + }, + "node_modules/punycode": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", + "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/puppeteer-core": { + "version": "24.22.3", + "resolved": "https://registry.npmjs.org/puppeteer-core/-/puppeteer-core-24.22.3.tgz", + "integrity": "sha512-M/Jhg4PWRANSbL/C9im//Yb55wsWBS5wdp+h59iwM+EPicVQQCNs56iC5aEAO7avfDPRfxs4MM16wHjOYHNJEw==", + "license": "Apache-2.0", + "dependencies": { + "@puppeteer/browsers": "2.10.10", + "chromium-bidi": "9.1.0", + "debug": "^4.4.3", + "devtools-protocol": "0.0.1495869", + "typed-query-selector": "^2.12.0", + "webdriver-bidi-protocol": "0.2.11", + "ws": "^8.18.3" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/puppeteer-core/node_modules/devtools-protocol": { + "version": "0.0.1495869", + "resolved": "https://registry.npmjs.org/devtools-protocol/-/devtools-protocol-0.0.1495869.tgz", + "integrity": "sha512-i+bkd9UYFis40RcnkW7XrOprCujXRAHg62IVh/Ah3G8MmNXpCGt1m0dTFhSdx/AVs8XEMbdOGRwdkR1Bcta8AA==", + "license": "BSD-3-Clause" + }, + "node_modules/puppeteer-core/node_modules/ws": { + "version": "8.18.3", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.18.3.tgz", + "integrity": "sha512-PEIGCY5tSlUt50cqyMXfCzX+oOPqN0vuGqWzbcJ2xvnkzkq46oOpz7dQaTDBdfICb4N14+GARUDw2XV2N4tvzg==", + "license": "MIT", + "engines": { + "node": ">=10.0.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": ">=5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + }, + "node_modules/qs": { + "version": "6.14.0", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.14.0.tgz", + "integrity": "sha512-YWWTjgABSKcvs/nWBi9PycY/JiPJqOD4JA6o9Sej2AtvSGarXxKC3OQSk4pAarbdQlKAh5D4FCQkJNkW+GAn3w==", + "license": "BSD-3-Clause", + "dependencies": { + "side-channel": "^1.1.0" + }, + "engines": { + "node": ">=0.6" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/quick-lru": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/quick-lru/-/quick-lru-5.1.1.tgz", + "integrity": "sha512-WuyALRjWPDGtt/wzJiadO5AXY+8hZ80hVpe6MyivgraREW751X3SbhRvG3eLKOYN+8VEvqLcf3wdnt44Z4S4SA==", + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/range-parser": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", + "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/raw-body": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-3.0.0.tgz", + "integrity": "sha512-RmkhL8CAyCRPXCE28MMH0z2PNWQBNk2Q09ZdxM9IOOXwxwZbN+qbWaatPkdkWIKL2ZVDImrN/pK5HTRz2PcS4g==", + "license": "MIT", + "dependencies": { + "bytes": "3.1.2", + "http-errors": "2.0.0", + "iconv-lite": "0.6.3", + "unpipe": "1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/readable-stream": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", + "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", + "license": "MIT", + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/readdirp": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", + "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", + "dev": true, + "license": "MIT", + "dependencies": { + "picomatch": "^2.2.1" + }, + "engines": { + "node": ">=8.10.0" + } + }, + "node_modules/require-directory": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/require-in-the-middle": { + "version": "7.5.2", + "resolved": "https://registry.npmjs.org/require-in-the-middle/-/require-in-the-middle-7.5.2.tgz", + "integrity": "sha512-gAZ+kLqBdHarXB64XpAe2VCjB7rIRv+mU8tfRWziHRJ5umKsIHN2tLLv6EtMw7WCdP19S0ERVMldNvxYCHnhSQ==", + "license": "MIT", + "dependencies": { + "debug": "^4.3.5", + "module-details-from-path": "^1.0.3", + "resolve": "^1.22.8" + }, + "engines": { + "node": ">=8.6.0" + } + }, + "node_modules/resolve": { + "version": "1.22.10", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.10.tgz", + "integrity": "sha512-NPRy+/ncIMeDlTAsuqwKIiferiawhefFJtkNSW0qZJEqMEb+qBt/77B/jGeeek+F0uOeN05CDa6HXbbIgtVX4w==", + "license": "MIT", + "dependencies": { + "is-core-module": "^2.16.0", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" + }, + "bin": { + "resolve": "bin/resolve" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/resolve-alpn": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/resolve-alpn/-/resolve-alpn-1.2.1.tgz", + "integrity": "sha512-0a1F4l73/ZFZOakJnQ3FvkJ2+gSTQWz/r2KE5OdDY0TxPm5h4GkqkWWfM47T7HsbnOtcJVEF4epCVy6u7Q3K+g==", + "license": "MIT" + }, + "node_modules/responselike": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/responselike/-/responselike-3.0.0.tgz", + "integrity": "sha512-40yHxbNcl2+rzXvZuVkrYohathsSJlMTXKryG5y8uciHv1+xDLHQpgjG64JUO9nrEq2jGLH6IZ8BcZyw3wrweg==", + "license": "MIT", + "dependencies": { + "lowercase-keys": "^3.0.0" + }, + "engines": { + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/robots-parser": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/robots-parser/-/robots-parser-3.0.1.tgz", + "integrity": "sha512-s+pyvQeIKIZ0dx5iJiQk1tPLJAWln39+MI5jtM8wnyws+G5azk+dMnMX0qfbqNetKKNgcWWOdi0sfm+FbQbgdQ==", + "license": "MIT", + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/router": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/router/-/router-2.2.0.tgz", + "integrity": "sha512-nLTrUKm2UyiL7rlhapu/Zl45FwNgkZGaCpZbIHajDYgwlJCOzLSk+cIPAnsEqV955GjILJnKbdQC1nVPz+gAYQ==", + "license": "MIT", + "dependencies": { + "debug": "^4.4.0", + "depd": "^2.0.0", + "is-promise": "^4.0.0", + "parseurl": "^1.3.3", + "path-to-regexp": "^8.0.0" + }, + "engines": { + "node": ">= 18" + } + }, + "node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", + "license": "MIT" + }, + "node_modules/semver": { + "version": "7.7.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.2.tgz", + "integrity": "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==", + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/send": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/send/-/send-1.2.0.tgz", + "integrity": "sha512-uaW0WwXKpL9blXE2o0bRhoL2EGXIrZxQ2ZQ4mgcfoBxdFmQold+qWsD2jLrfZ0trjKL6vOw0j//eAwcALFjKSw==", + "license": "MIT", + "dependencies": { + "debug": "^4.3.5", + "encodeurl": "^2.0.0", + "escape-html": "^1.0.3", + "etag": "^1.8.1", + "fresh": "^2.0.0", + "http-errors": "^2.0.0", + "mime-types": "^3.0.1", + "ms": "^2.1.3", + "on-finished": "^2.4.1", + "range-parser": "^1.2.1", + "statuses": "^2.0.1" + }, + "engines": { + "node": ">= 18" + } + }, + "node_modules/serve-static": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-2.2.0.tgz", + "integrity": "sha512-61g9pCh0Vnh7IutZjtLGGpTA355+OPn2TyDv/6ivP2h/AdAVX9azsoxmg2/M6nZeQZNYBEwIcsne1mJd9oQItQ==", + "license": "MIT", + "dependencies": { + "encodeurl": "^2.0.0", + "escape-html": "^1.0.3", + "parseurl": "^1.3.3", + "send": "^1.2.0" + }, + "engines": { + "node": ">= 18" + } + }, + "node_modules/setprototypeof": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", + "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==", + "license": "ISC" + }, + "node_modules/shimmer": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/shimmer/-/shimmer-1.2.1.tgz", + "integrity": "sha512-sQTKC1Re/rM6XyFM6fIAGHRPVGvyXfgzIDvzoq608vM+jeyVD0Tu1E6Np0Kc2zAIFWIj963V2800iF/9LPieQw==", + "license": "BSD-2-Clause" + }, + "node_modules/side-channel": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.0.tgz", + "integrity": "sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "object-inspect": "^1.13.3", + "side-channel-list": "^1.0.0", + "side-channel-map": "^1.0.1", + "side-channel-weakmap": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-list": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.0.tgz", + "integrity": "sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "object-inspect": "^1.13.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-map": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/side-channel-map/-/side-channel-map-1.0.1.tgz", + "integrity": "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==", + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-weakmap": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz", + "integrity": "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==", + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3", + "side-channel-map": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/sift": { + "version": "17.1.3", + "resolved": "https://registry.npmjs.org/sift/-/sift-17.1.3.tgz", + "integrity": "sha512-Rtlj66/b0ICeFzYTuNvX/EF1igRbbnGSvEyT79McoZa/DeGhMyC5pWKOEsZKnpkqtSeovd5FL/bjHWC3CIIvCQ==", + "license": "MIT" + }, + "node_modules/simple-update-notifier": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/simple-update-notifier/-/simple-update-notifier-2.0.0.tgz", + "integrity": "sha512-a2B9Y0KlNXl9u/vsW6sTIu9vGEpfKu2wRV6l1H3XEas/0gUIzGzBoP/IouTcUQbm9JWZLH3COxyn03TYlFax6w==", + "dev": true, + "license": "MIT", + "dependencies": { + "semver": "^7.5.3" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/sitemapper": { + "version": "3.2.24", + "resolved": "https://registry.npmjs.org/sitemapper/-/sitemapper-3.2.24.tgz", + "integrity": "sha512-AWOmGaRyShZAoAPr4StgX0bgk15+J04J165qP6z9CpZ+8EUvmOpV0F0Gol28WbQW2prNALWFRf3X04q6lUr4mA==", + "license": "MIT", + "dependencies": { + "fast-xml-parser": "^4.5.0", + "got": "^11.8.0", + "is-gzip": "2.0.0", + "p-limit": "^3.1.0" + }, + "engines": { + "node": ">= 10.0.0" + } + }, + "node_modules/sitemapper/node_modules/@sindresorhus/is": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-4.6.0.tgz", + "integrity": "sha512-t09vSN3MdfsyCHoFcTRCH/iUtG7OJ0CsjzB8cjAmKc/va/kIgeDI/TxsigdncE/4be734m0cvIYwNaV4i2XqAw==", + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sindresorhus/is?sponsor=1" + } + }, + "node_modules/sitemapper/node_modules/@szmarczak/http-timer": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/@szmarczak/http-timer/-/http-timer-4.0.6.tgz", + "integrity": "sha512-4BAffykYOgO+5nzBWYwE3W90sBgLJoUPRWWcL8wlyiM8IB8ipJz3UMJ9KXQd1RKQXpKp8Tutn80HZtWsu2u76w==", + "license": "MIT", + "dependencies": { + "defer-to-connect": "^2.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/sitemapper/node_modules/cacheable-lookup": { + "version": "5.0.4", + "resolved": "https://registry.npmjs.org/cacheable-lookup/-/cacheable-lookup-5.0.4.tgz", + "integrity": "sha512-2/kNscPhpcxrOigMZzbiWF7dz8ilhb/nIHU3EyZiXWXpeq/au8qJ8VhdftMkty3n7Gj6HIGalQG8oiBNB3AJgA==", + "license": "MIT", + "engines": { + "node": ">=10.6.0" + } + }, + "node_modules/sitemapper/node_modules/cacheable-request": { + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/cacheable-request/-/cacheable-request-7.0.4.tgz", + "integrity": "sha512-v+p6ongsrp0yTGbJXjgxPow2+DL93DASP4kXCDKb8/bwRtt9OEF3whggkkDkGNzgcWy2XaF4a8nZglC7uElscg==", + "license": "MIT", + "dependencies": { + "clone-response": "^1.0.2", + "get-stream": "^5.1.0", + "http-cache-semantics": "^4.0.0", + "keyv": "^4.0.0", + "lowercase-keys": "^2.0.0", + "normalize-url": "^6.0.1", + "responselike": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/sitemapper/node_modules/get-stream": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.2.0.tgz", + "integrity": "sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==", + "license": "MIT", + "dependencies": { + "pump": "^3.0.0" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/sitemapper/node_modules/got": { + "version": "11.8.6", + "resolved": "https://registry.npmjs.org/got/-/got-11.8.6.tgz", + "integrity": "sha512-6tfZ91bOr7bOXnK7PRDCGBLa1H4U080YHNaAQ2KsMGlLEzRbk44nsZF2E1IeRc3vtJHPVbKCYgdFbaGO2ljd8g==", + "license": "MIT", + "dependencies": { + "@sindresorhus/is": "^4.0.0", + "@szmarczak/http-timer": "^4.0.5", + "@types/cacheable-request": "^6.0.1", + "@types/responselike": "^1.0.0", + "cacheable-lookup": "^5.0.3", + "cacheable-request": "^7.0.2", + "decompress-response": "^6.0.0", + "http2-wrapper": "^1.0.0-beta.5.2", + "lowercase-keys": "^2.0.0", + "p-cancelable": "^2.0.0", + "responselike": "^2.0.0" + }, + "engines": { + "node": ">=10.19.0" + }, + "funding": { + "url": "https://github.com/sindresorhus/got?sponsor=1" + } + }, + "node_modules/sitemapper/node_modules/http2-wrapper": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/http2-wrapper/-/http2-wrapper-1.0.3.tgz", + "integrity": "sha512-V+23sDMr12Wnz7iTcDeJr3O6AIxlnvT/bmaAAAP/Xda35C90p9599p0F1eHR/N1KILWSoWVAiOMFjBBXaXSMxg==", + "license": "MIT", + "dependencies": { + "quick-lru": "^5.1.1", + "resolve-alpn": "^1.0.0" + }, + "engines": { + "node": ">=10.19.0" + } + }, + "node_modules/sitemapper/node_modules/lowercase-keys": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-2.0.0.tgz", + "integrity": "sha512-tqNXrS78oMOE73NMxK4EMLQsQowWf8jKooH9g7xPavRT706R6bkQJ6DY2Te7QukaZsulxa30wQ7bk0pm4XiHmA==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/sitemapper/node_modules/normalize-url": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-6.1.0.tgz", + "integrity": "sha512-DlL+XwOy3NxAQ8xuC0okPgK46iuVNAK01YN7RueYBqqFeGsBjV9XmCAzAdgt+667bCl5kPh9EqKKDwnaPG1I7A==", + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/sitemapper/node_modules/p-cancelable": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/p-cancelable/-/p-cancelable-2.1.1.tgz", + "integrity": "sha512-BZOr3nRQHOntUjTrH8+Lh54smKHoHyur8We1V8DSMVrl5A2malOOwuJRnKRDjSnkoeBh4at6BwEnb5I7Jl31wg==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/sitemapper/node_modules/responselike": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/responselike/-/responselike-2.0.1.tgz", + "integrity": "sha512-4gl03wn3hj1HP3yzgdI7d3lCkF95F21Pz4BPGvKHinyQzALR5CapwC8yIi0Rh58DEMQ/SguC03wFj2k0M/mHhw==", + "license": "MIT", + "dependencies": { + "lowercase-keys": "^2.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/slugify": { + "version": "1.6.6", + "resolved": "https://registry.npmjs.org/slugify/-/slugify-1.6.6.tgz", + "integrity": "sha512-h+z7HKHYXj6wJU+AnS/+IH8Uh9fdcX1Lrhg1/VMdf9PwoBQXFcXiAdsy2tSK0P6gKwJLXp02r90ahUCqHk9rrw==", + "license": "MIT", + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/smart-buffer": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/smart-buffer/-/smart-buffer-4.2.0.tgz", + "integrity": "sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg==", + "license": "MIT", + "engines": { + "node": ">= 6.0.0", + "npm": ">= 3.0.0" + } + }, + "node_modules/socks": { + "version": "2.8.7", + "resolved": "https://registry.npmjs.org/socks/-/socks-2.8.7.tgz", + "integrity": "sha512-HLpt+uLy/pxB+bum/9DzAgiKS8CX1EvbWxI4zlmgGCExImLdiad2iCwXT5Z4c9c3Eq8rP2318mPW2c+QbtjK8A==", + "license": "MIT", + "dependencies": { + "ip-address": "^10.0.1", + "smart-buffer": "^4.2.0" + }, + "engines": { + "node": ">= 10.0.0", + "npm": ">= 3.0.0" + } + }, + "node_modules/socks-proxy-agent": { + "version": "8.0.5", + "resolved": "https://registry.npmjs.org/socks-proxy-agent/-/socks-proxy-agent-8.0.5.tgz", + "integrity": "sha512-HehCEsotFqbPW9sJ8WVYB6UbmIMv7kUUORIF2Nncq4VQvBfNBLibW9YZR5dlYCSUhwcD628pRllm7n+E+YTzJw==", + "license": "MIT", + "dependencies": { + "agent-base": "^7.1.2", + "debug": "^4.3.4", + "socks": "^2.8.3" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "license": "BSD-3-Clause", + "optional": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/sparse-bitfield": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/sparse-bitfield/-/sparse-bitfield-3.0.3.tgz", + "integrity": "sha512-kvzhi7vqKTfkh0PZU+2D2PIllw2ymqJKujUcyPMd9Y75Nv4nPbGJZXNhxsgdQab2BmlDct1YnfQCguEvHr7VsQ==", + "license": "MIT", + "dependencies": { + "memory-pager": "^1.0.2" + } + }, + "node_modules/speedline-core": { + "version": "1.4.3", + "resolved": "https://registry.npmjs.org/speedline-core/-/speedline-core-1.4.3.tgz", + "integrity": "sha512-DI7/OuAUD+GMpR6dmu8lliO2Wg5zfeh+/xsdyJZCzd8o5JgFUjCeLsBDuZjIQJdwXS3J0L/uZYrELKYqx+PXog==", + "license": "MIT", + "dependencies": { + "@types/node": "*", + "image-ssim": "^0.2.0", + "jpeg-js": "^0.4.1" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/statuses": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.2.tgz", + "integrity": "sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/streamsearch": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/streamsearch/-/streamsearch-1.1.0.tgz", + "integrity": "sha512-Mcc5wHehp9aXz1ax6bZUyY5afg9u2rv5cqQI3mRrYkGC8rW2hM02jWuwjtL++LS5qinSyhj2QfLyNsuc+VsExg==", + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/streamx": { + "version": "2.23.0", + "resolved": "https://registry.npmjs.org/streamx/-/streamx-2.23.0.tgz", + "integrity": "sha512-kn+e44esVfn2Fa/O0CPFcex27fjIL6MkVae0Mm6q+E6f0hWv578YCERbv+4m02cjxvDsPKLnmxral/rR6lBMAg==", + "license": "MIT", + "dependencies": { + "events-universal": "^1.0.0", + "fast-fifo": "^1.3.2", + "text-decoder": "^1.1.0" + } + }, + "node_modules/string_decoder": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", + "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", + "license": "MIT", + "dependencies": { + "safe-buffer": "~5.2.0" + } + }, + "node_modules/string-pixel-width": { + "version": "1.11.0", + "resolved": "https://registry.npmjs.org/string-pixel-width/-/string-pixel-width-1.11.0.tgz", + "integrity": "sha512-GeKuNcCza7Gf3tlMJiZY8SF1LtbFGeMUEpHifncgJn+ZcUpnoPyE69HEyb0rXrJ3bejY/M/kBylu7IDlPJD9Ng==", + "license": "MIT", + "dependencies": { + "lodash.deburr": "^4.1.0" + } + }, + "node_modules/string-similarity": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/string-similarity/-/string-similarity-4.0.4.tgz", + "integrity": "sha512-/q/8Q4Bl4ZKAPjj8WerIBJWALKkaPRfrvhfF8k/B23i4nzrlRj2/go1m90In7nG/3XDSbOo0+pu6RvCTM9RGMQ==", + "deprecated": "Package no longer supported. Contact Support at https://www.npmjs.com/support for more info.", + "license": "ISC" + }, + "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/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/stripe": { + "version": "18.5.0", + "resolved": "https://registry.npmjs.org/stripe/-/stripe-18.5.0.tgz", + "integrity": "sha512-Hp+wFiEQtCB0LlNgcFh5uVyKznpDjzyUZ+CNVEf+I3fhlYvh7rZruIg+jOwzJRCpy0ZTPMjlzm7J2/M2N6d+DA==", + "license": "MIT", + "dependencies": { + "qs": "^6.11.0" + }, + "engines": { + "node": ">=12.*" + }, + "peerDependencies": { + "@types/node": ">=12.x.x" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } + }, + "node_modules/strnum": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/strnum/-/strnum-1.1.2.tgz", + "integrity": "sha512-vrN+B7DBIoTTZjnPNewwhx6cBA/H+IS7rfW68n7XxC1y7uoiGQBxaKzqucGUgavX15dJgiGztLJ8vxuEzwqBdA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/NaturalIntelligence" + } + ], + "license": "MIT" + }, + "node_modules/stubborn-fs": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/stubborn-fs/-/stubborn-fs-1.2.5.tgz", + "integrity": "sha512-H2N9c26eXjzL/S/K+i/RHHcFanE74dptvvjM8iwzwbVcWY/zjBbgRqF3K0DY4+OD+uTTASTBvDoxPDaPN02D7g==" + }, + "node_modules/supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/supports-preserve-symlinks-flag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", + "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/syllable": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/syllable/-/syllable-5.0.1.tgz", + "integrity": "sha512-HWtNCp6v7J8H0lrT8j1HHjfOLltRoDcC7QRFVu25p4BE52JqetXG65nqC7CsatT8WQRfY4Qvh93BWJIUxbmXFg==", + "license": "MIT", + "dependencies": { + "@types/pluralize": "^0.0.29", + "normalize-strings": "^1.1.0", + "pluralize": "^8.0.0" + }, + "bin": { + "syllable": "cli.js" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/tar-fs": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-3.1.1.tgz", + "integrity": "sha512-LZA0oaPOc2fVo82Txf3gw+AkEd38szODlptMYejQUhndHMLQ9M059uXR+AfS7DNo0NpINvSqDsvyaCrBVkptWg==", + "license": "MIT", + "dependencies": { + "pump": "^3.0.0", + "tar-stream": "^3.1.5" + }, + "optionalDependencies": { + "bare-fs": "^4.0.1", + "bare-path": "^3.0.0" + } + }, + "node_modules/tar-stream": { + "version": "3.1.7", + "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-3.1.7.tgz", + "integrity": "sha512-qJj60CXt7IU1Ffyc3NJMjh6EkuCFej46zUqJ4J7pqYlThyd9bO0XBTmcOIhSzZJVWfsLks0+nle/j538YAW9RQ==", + "license": "MIT", + "dependencies": { + "b4a": "^1.6.4", + "fast-fifo": "^1.2.0", + "streamx": "^2.15.0" + } + }, + "node_modules/text-decoder": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/text-decoder/-/text-decoder-1.2.3.tgz", + "integrity": "sha512-3/o9z3X0X0fTupwsYvR03pJ/DjWuqqrfwBgTQzdWDiQSm9KitAyz/9WqsT2JQW7KV2m+bC2ol/zqpW37NHxLaA==", + "license": "Apache-2.0", + "dependencies": { + "b4a": "^1.6.4" + } + }, + "node_modules/text-readability": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/text-readability/-/text-readability-1.1.1.tgz", + "integrity": "sha512-lo0KlcNRhlBAxTCEPXMFricim2vJVm0xAVdy40N6WQDn+MeKDBFxl0bMdP2YIMm9ph34CPvxYO/Lxb1QIi67ig==", + "license": "ISC", + "dependencies": { + "pluralize": "^8.0.0", + "syllable": "^5.0.1" + } + }, + "node_modules/third-party-web": { + "version": "0.27.0", + "resolved": "https://registry.npmjs.org/third-party-web/-/third-party-web-0.27.0.tgz", + "integrity": "sha512-h0JYX+dO2Zr3abCQpS6/uFjujaOjA1DyDzGQ41+oFn9VW/ARiq9g5ln7qEP9+BTzDpOMyIfsfj4OvfgXAsMUSA==", + "license": "MIT" + }, + "node_modules/tldts-core": { + "version": "7.0.16", + "resolved": "https://registry.npmjs.org/tldts-core/-/tldts-core-7.0.16.tgz", + "integrity": "sha512-XHhPmHxphLi+LGbH0G/O7dmUH9V65OY20R7vH8gETHsp5AZCjBk9l8sqmRKLaGOxnETU7XNSDUPtewAy/K6jbA==", + "license": "MIT" + }, + "node_modules/tldts-icann": { + "version": "7.0.16", + "resolved": "https://registry.npmjs.org/tldts-icann/-/tldts-icann-7.0.16.tgz", + "integrity": "sha512-WS/pPasPs2cx6orcxCcIz01SlG3dwYlgjLAnQt7vLAusTuTLqdI8zmkqbM8TWYEf3Z0o1S4BzM3oSRFPk/6WnA==", + "license": "MIT", + "dependencies": { + "tldts-core": "^7.0.16" + } + }, + "node_modules/to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/toidentifier": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", + "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==", + "license": "MIT", + "engines": { + "node": ">=0.6" + } + }, + "node_modules/touch": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/touch/-/touch-3.1.1.tgz", + "integrity": "sha512-r0eojU4bI8MnHr8c5bNo7lJDdI2qXlWWJk6a9EAFG7vbhTjElYhBVS3/miuE0uOuoLdb8Mc/rVfsmm6eo5o9GA==", + "dev": true, + "license": "ISC", + "bin": { + "nodetouch": "bin/nodetouch.js" + } + }, + "node_modules/tr46": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-5.1.1.tgz", + "integrity": "sha512-hdF5ZgjTqgAntKkklYw0R03MG2x/bSzTtkxmIRw/sTNV8YXsCJ1tfLAX23lhxhHJlEf3CRCOCGGWw3vI3GaSPw==", + "license": "MIT", + "dependencies": { + "punycode": "^2.3.1" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "license": "0BSD" + }, + "node_modules/type-fest": { + "version": "4.41.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-4.41.0.tgz", + "integrity": "sha512-TeTSQ6H5YHvpqVwBRcnLDCBnDOHWYu7IvGbHT6N8AOymcr9PJGjc1GTtiWZTYg0NCgYwvnYWEkVChQAr9bjfwA==", + "license": "(MIT OR CC0-1.0)", + "engines": { + "node": ">=16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/type-is": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/type-is/-/type-is-2.0.1.tgz", + "integrity": "sha512-OZs6gsjF4vMp32qrCbiVSkrFmXtG/AZhY3t0iAMrMBiAZyV9oALtXO8hsrHbMXF9x6L3grlFuwW2oAz7cav+Gw==", + "license": "MIT", + "dependencies": { + "content-type": "^1.0.5", + "media-typer": "^1.1.0", + "mime-types": "^3.0.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/typed-query-selector": { + "version": "2.12.0", + "resolved": "https://registry.npmjs.org/typed-query-selector/-/typed-query-selector-2.12.0.tgz", + "integrity": "sha512-SbklCd1F0EiZOyPiW192rrHZzZ5sBijB6xM+cpmrwDqObvdtunOHHIk9fCGsoK5JVIYXoyEp4iEdE3upFH3PAg==", + "license": "MIT" + }, + "node_modules/typedarray": { + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz", + "integrity": "sha512-/aCDEGatGvZ2BIk+HmLf4ifCJFwvKFNb9/JeZPMulfgFracn9QFcAf5GO8B/mweUjSoblS5In0cWhqpfs/5PQA==", + "license": "MIT" + }, + "node_modules/undefsafe": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/undefsafe/-/undefsafe-2.0.5.tgz", + "integrity": "sha512-WxONCrssBM8TSPRqN5EmsjVrsv4A8X12J4ArBiiayv3DyyG3ZlIg6yysuuSYdZsVz3TKcTg2fd//Ujd4CHV1iA==", + "dev": true, + "license": "MIT" + }, + "node_modules/undici": { + "version": "7.14.0", + "resolved": "https://registry.npmjs.org/undici/-/undici-7.14.0.tgz", + "integrity": "sha512-Vqs8HTzjpQXZeXdpsfChQTlafcMQaaIwnGwLam1wudSSjlJeQ3bw1j+TLPePgrCnCpUXx7Ba5Pdpf5OBih62NQ==", + "license": "MIT", + "engines": { + "node": ">=20.18.1" + } + }, + "node_modules/undici-types": { + "version": "7.10.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.10.0.tgz", + "integrity": "sha512-t5Fy/nfn+14LuOc2KNYg75vZqClpAiqscVvMygNnlsHBFpSXdJaYtXMcdNLpl/Qvc3P2cB3s6lOV51nqsFq4ag==", + "license": "MIT" + }, + "node_modules/unpipe": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", + "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", + "license": "MIT" + }, + "node_modules/vary": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", + "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/webdriver-bidi-protocol": { + "version": "0.2.11", + "resolved": "https://registry.npmjs.org/webdriver-bidi-protocol/-/webdriver-bidi-protocol-0.2.11.tgz", + "integrity": "sha512-Y9E1/oi4XMxcR8AT0ZC4OvYntl34SPgwjmELH+owjBr0korAX4jKgZULBWILGCVGdVCQ0dodTToIETozhG8zvA==", + "license": "Apache-2.0" + }, + "node_modules/webidl-conversions": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-7.0.0.tgz", + "integrity": "sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g==", + "license": "BSD-2-Clause", + "engines": { + "node": ">=12" + } + }, + "node_modules/whatwg-encoding": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/whatwg-encoding/-/whatwg-encoding-3.1.1.tgz", + "integrity": "sha512-6qN4hJdMwfYBtE3YBTTHhoeuUrDBPZmbQaxWAqSALV/MeEnR5z1xd8UKud2RAkFoPkmB+hli1TZSnyi84xz1vQ==", + "license": "MIT", + "dependencies": { + "iconv-lite": "0.6.3" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/whatwg-mimetype": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/whatwg-mimetype/-/whatwg-mimetype-4.0.0.tgz", + "integrity": "sha512-QaKxh0eNIi2mE9p2vEdzfagOKHCcj1pJ56EEHGQOVxp8r9/iszLUUV7v89x9O1p/T+NlTM5W7jW6+cz4Fq1YVg==", + "license": "MIT", + "engines": { + "node": ">=18" + } + }, + "node_modules/whatwg-url": { + "version": "14.2.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-14.2.0.tgz", + "integrity": "sha512-De72GdQZzNTUBBChsXueQUnPKDkg/5A5zp7pFDuQAj5UFoENpiACU0wlCvzpAGnTkj++ihpKwKyYewn/XNUbKw==", + "license": "MIT", + "dependencies": { + "tr46": "^5.1.0", + "webidl-conversions": "^7.0.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/when-exit": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/when-exit/-/when-exit-2.1.4.tgz", + "integrity": "sha512-4rnvd3A1t16PWzrBUcSDZqcAmsUIy4minDXT/CZ8F2mVDgd65i4Aalimgz1aQkRGU0iH5eT5+6Rx2TK8o443Pg==", + "license": "MIT" + }, + "node_modules/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/wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", + "license": "ISC" + }, + "node_modules/ws": { + "version": "7.5.10", + "resolved": "https://registry.npmjs.org/ws/-/ws-7.5.10.tgz", + "integrity": "sha512-+dbF1tHwZpXcbOJdVOkzLDxZP1ailvSxM6ZweXTegylPny803bFhA+vqBYw4s31NSAk4S2Qz+AKXK9a4wkdjcQ==", + "license": "MIT", + "engines": { + "node": ">=8.3.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": "^5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + }, + "node_modules/xdg-basedir": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/xdg-basedir/-/xdg-basedir-5.1.0.tgz", + "integrity": "sha512-GCPAHLvrIH13+c0SuacwvRYj2SxJXQ4kaVTT5xgL3kPrz56XxkF21IGhjSE1+W0aw7gpBWRGXLCPnPby6lSpmQ==", + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/xtend": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", + "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==", + "license": "MIT", + "engines": { + "node": ">=0.4" + } + }, + "node_modules/y18n": { + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", + "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", + "license": "ISC", + "engines": { + "node": ">=10" + } + }, + "node_modules/yargs": { + "version": "17.7.2", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", + "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", + "license": "MIT", + "dependencies": { + "cliui": "^8.0.1", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.3", + "y18n": "^5.0.5", + "yargs-parser": "^21.1.1" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/yargs-parser": { + "version": "21.1.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", + "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/yauzl": { + "version": "2.10.0", + "resolved": "https://registry.npmjs.org/yauzl/-/yauzl-2.10.0.tgz", + "integrity": "sha512-p4a9I6X6nu6IhoGmBqAcbJy1mlC4j27vEPZX9F4L4/vZT3Lyq1VkFHw/V/PUcB9Buo+DG3iHkT0x3Qya58zc3g==", + "license": "MIT", + "dependencies": { + "buffer-crc32": "~0.2.3", + "fd-slicer": "~1.1.0" + } + }, + "node_modules/yocto-queue": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", + "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/zod": { + "version": "3.25.76", + "resolved": "https://registry.npmjs.org/zod/-/zod-3.25.76.tgz", + "integrity": "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/colinhacks" + } + } + } +} diff --git a/package.json b/package.json new file mode 100644 index 0000000..5157ea1 --- /dev/null +++ b/package.json @@ -0,0 +1,36 @@ +{ + "name": "crawlerx", + "version": "1.0.0", + "type": "module", + "main": "crawler.js", + "scripts": { + "start": "node crawler.js https://example.com 200", + "dev": "nodemon crawler.js https://example.com 200" + }, + "dependencies": { + "axios": "^1.12.2", + "bcrypt": "^6.0.0", + "cheerio": "^1.1.0", + "chrome-launcher": "^1.2.1", + "cors": "^2.8.5", + "dotenv": "^17.2.2", + "express": "^5.1.0", + "got": "^14.4.7", + "jsonwebtoken": "^9.0.2", + "lighthouse": "^12.8.2", + "mongoose": "^8.18.1", + "multer": "^2.0.2", + "nodemailer": "^7.0.6", + "normalize-url": "^8.0.2", + "sitemapper": "^3.2.7", + "slugify": "^1.6.6", + "string-pixel-width": "^1.11.0", + "string-similarity": "^4.0.4", + "stripe": "^18.5.0", + "text-readability": "^1.1.1" + }, + "devDependencies": { + "nodemon": "^3.1.10", + "playwright": "^1.55.0" + } +} diff --git a/public/lighthouse-treemap/treemap-1758885492002.html b/public/lighthouse-treemap/treemap-1758885492002.html new file mode 100644 index 0000000..027dd23 --- /dev/null +++ b/public/lighthouse-treemap/treemap-1758885492002.html @@ -0,0 +1,2897 @@ + + + + + + + + Lighthouse Report + + + + + +
+ + + + + + diff --git a/public/lighthouse-treemap/treemap-1758885890915.html b/public/lighthouse-treemap/treemap-1758885890915.html new file mode 100644 index 0000000..b22768f --- /dev/null +++ b/public/lighthouse-treemap/treemap-1758885890915.html @@ -0,0 +1,2897 @@ + + + + + + + + Lighthouse Report + + + + + +
+ + + + + + diff --git a/public/lighthouse-treemap/treemap-1758958684569.html b/public/lighthouse-treemap/treemap-1758958684569.html new file mode 100644 index 0000000..00d5467 --- /dev/null +++ b/public/lighthouse-treemap/treemap-1758958684569.html @@ -0,0 +1,2897 @@ + + + + + + + + Lighthouse Report + + + + + +
+ + + + + + diff --git a/routes/auth.routes.js b/routes/auth.routes.js new file mode 100644 index 0000000..3976890 --- /dev/null +++ b/routes/auth.routes.js @@ -0,0 +1,18 @@ +import express from "express"; +import { signup, login, changePassword, forgotPassword, resetPassword } from "../controllers/auth.controller.js"; +import { authMiddleware } from "../middlewares/auth.middleware.js"; + +const router = express.Router(); + +router.post("/signup", signup); +router.post("/login", login); +router.post("/change-password", authMiddleware, changePassword); +router.post("/forgot-password", forgotPassword); +router.post("/reset-password", resetPassword); + +// example protected route +router.get("/profile", authMiddleware, (req, res) => { + res.json({ user: req.user }); +}); + +export default router; diff --git a/routes/blog.routes.js b/routes/blog.routes.js new file mode 100644 index 0000000..0ca6d09 --- /dev/null +++ b/routes/blog.routes.js @@ -0,0 +1,54 @@ +import express from "express"; +import multer from "multer"; +import { + createBlog, + getAllBlogs, + getBlogBySlug, + likeBlog, +} from "../controllers/blog.controller.js"; + +import { + createCategory, + getCategories, + deleteCategory, +} from "../controllers/category.controller.js"; + +import { + addComment as addCommentController, + getComments, + deleteComment, +} from "../controllers/comment.controller.js"; + +const router = express.Router(); +const upload = multer({ dest: "uploads/" }); + +// ======================= +// Blog Routes +// ======================= +// Create a blog +router.post("/", upload.single("image"), createBlog); + +// Get all blogs +router.get("/", getAllBlogs); + +// Get blog by slug +router.get("/:slug", getBlogBySlug); + +// Like a blog +router.post("/:id/like", likeBlog); + +// ======================= +// Category Routes +// ======================= +router.post("/category", createCategory); // Create Category (admin) +router.get("/category", getCategories); // List Categories +router.delete("/category/:id", deleteCategory); // Delete Category (admin) + +// ======================= +// Comment Routes +// ======================= +router.post("/:blogId/comments", addCommentController); // Add Comment +router.get("/:blogId/comments", getComments); // Get Comments +router.delete("/:blogId/comments/:commentId", deleteComment); // Delete Comment (admin) + +export default router; diff --git a/routes/crawl.routes.js b/routes/crawl.routes.js new file mode 100644 index 0000000..1262c1c --- /dev/null +++ b/routes/crawl.routes.js @@ -0,0 +1,6 @@ +import { Router } from "express"; +import { crawlHandler } from "../controllers/crawl.controller.js"; + +const router = Router(); +router.get("/", crawlHandler); +export default router; diff --git a/routes/lighthouse.routes.js b/routes/lighthouse.routes.js new file mode 100644 index 0000000..d24e133 --- /dev/null +++ b/routes/lighthouse.routes.js @@ -0,0 +1,8 @@ +import express from 'express'; +import { runAudit } from '../controllers/lighthouseController.js'; + +const router = express.Router(); + +router.post('/audit', runAudit); + +export default router; diff --git a/routes/maisondetreats/cakeOrder.routes.js b/routes/maisondetreats/cakeOrder.routes.js new file mode 100644 index 0000000..7d31960 --- /dev/null +++ b/routes/maisondetreats/cakeOrder.routes.js @@ -0,0 +1,12 @@ +import express from "express"; +import { createCakeOrder, getAllCakeOrders } from "../../controllers/maisondetreats/cakeOrder.controller.js"; + +const router = express.Router(); + +// Create a new cake order +router.post("/", createCakeOrder); + +// Get all cake orders +router.get("/", getAllCakeOrders); + +export default router; diff --git a/routes/message.routes.js b/routes/message.routes.js new file mode 100644 index 0000000..18ec256 --- /dev/null +++ b/routes/message.routes.js @@ -0,0 +1,12 @@ +import express from "express"; +import { sendMessage, getMessages } from "../controllers/message.controller.js"; + +const router = express.Router(); + +// POST /api/messages → Save a message +router.post("/", sendMessage); + +// GET /api/messages → Get all messages (optional) +router.get("/", getMessages); + +export default router; diff --git a/routes/payment.route.js b/routes/payment.route.js new file mode 100644 index 0000000..b7eafe2 --- /dev/null +++ b/routes/payment.route.js @@ -0,0 +1,14 @@ +import express from "express"; +import { + createPaymentIntent, + createCheckoutSession, + handleWebhook +} from "../controllers/payment.controller.js"; + +const router = express.Router(); + +router.post("/create-intent", createPaymentIntent); +router.post("/create-checkout-session", createCheckoutSession); +router.post("/webhook", express.raw({ type: "application/json" }), handleWebhook); + +export default router; diff --git a/routes/sitemap.routes.js b/routes/sitemap.routes.js new file mode 100644 index 0000000..0ba998d --- /dev/null +++ b/routes/sitemap.routes.js @@ -0,0 +1,6 @@ +import { Router } from "express"; +import { sitemapHandler } from "../controllers/sitemap.controller.js"; + +const router = Router(); +router.get("/", sitemapHandler); +export default router; diff --git a/server copy.js b/server copy.js new file mode 100644 index 0000000..6aeb5a3 --- /dev/null +++ b/server copy.js @@ -0,0 +1,237 @@ +// // server.js +// import express from "express"; +// import { Queue } from "bullmq"; +// import { connection } from "./redis.js"; +// import crypto from "crypto"; + +// const app = express(); +// app.use(express.json()); + +// const crawlQueue = new Queue("crawl", { connection }); + +// // Start a new crawl +// app.post("/crawl", async (req, res) => { +// const { startUrl } = req.body; +// if (!startUrl) return res.status(400).json({ error: "Missing startUrl" }); + +// const crawlId = crypto.randomUUID(); +// await crawlQueue.add("fetch", { crawlId, url: startUrl }); + +// res.json({ crawlId, message: "Crawl started" }); +// }); + +// // (Optional) Check progress +// app.get("/status/:id", async (req, res) => { +// // For now just reply with "in progress" +// res.json({ crawlId: req.params.id, status: "in progress" }); +// }); + +// app.listen(3000, () => { +// console.log("Crawler API running at http://localhost:3000"); +// }); + + +// // server.js +// import express from "express"; +// import cors from "cors"; // ← optional but recommended +// import { crawl } from "./crawler.js"; // ensure crawl is a NAMED export; if default, use: import crawl from "./crawler.js"; + +// const app = express(); +// const PORT = process.env.PORT || 3010; + +// /* Parse JSON BEFORE any middleware that might read req.body */ +// app.use(express.json()); + +// /* CORS (adjust origins as needed) */ +// app.use(cors({ +// origin: [ +// "http://localhost:3000", +// "https://your-frontend.example" // ← replace or remove +// ], +// })); + +// /* Safe request logger */ +// app.use((req, res, next) => { +// console.log(`[${new Date().toISOString()}] ${req.method} ${req.originalUrl}`); +// if (req.query && Object.keys(req.query).length) console.log("Query:", req.query); +// if (req.body && typeof req.body === "object" && Object.keys(req.body).length) console.log("Body:", req.body); +// next(); +// }); + +// /* GET /crawl?url=https://site.com&max=50 */ +// app.get("/crawl", async (req, res) => { +// try { +// const { url, max } = req.query; +// if (!url) return res.status(400).json({ error: "Missing url param" }); + +// // validate & normalize +// const target = new URL(String(url)); // throws if invalid +// const limit = Math.min(Math.max(parseInt(max ?? "50", 10), 1), 500); + +// await crawl(target.toString(), limit); +// res.json({ ok: true, message: `Crawl started`, url: target.toString(), limit }); +// } catch (err) { +// console.error("Crawl error:", err); +// res.status(500).json({ error: "Crawl failed", details: String(err?.message ?? err) }); +// } +// }); + +// /* Global safety nets so crashes don’t become silent restart loops */ +// process.on("unhandledRejection", (err) => console.error("unhandledRejection:", err)); +// process.on("uncaughtException", (err) => console.error("uncaughtException:", err)); + +// /* Bind to all interfaces so remote calls work */ +// app.listen(PORT, "0.0.0.0", () => { +// console.log(`🚀 Server running at http://localhost:${PORT}`); +// }); + +// server.js +import express from "express"; +import cors from "cors"; +import path from "node:path"; +import fs from "node:fs"; +import fsp from "node:fs/promises"; +import { fileURLToPath } from "node:url"; +import { crawl } from "./crawler.js"; // crawl(target, limit, onProgress?, options?) + +const app = express(); +const PORT = process.env.PORT || 3010; +const __dirname = path.dirname(fileURLToPath(import.meta.url)); + +/* ------------ Middleware ------------ */ +app.use(express.json()); +app.use(cors({ origin: ["http://localhost:3000", "https://app.crawlerx.co"] })); +app.use(express.static(path.join(__dirname, "public"))); +app.get("/", (_req, res) => { + const viewer = path.join(__dirname, "public", "crawlerx_viewer.html"); + return fs.existsSync(viewer) + ? res.sendFile(viewer) + : res.type("text/plain").send("CrawlerX backend is running."); +}); +app.get("/healthz", (_req, res) => res.json({ ok: true, time: new Date().toISOString() })); + +/* ------------ Helpers ------------ */ +const ts = () => + new Date().toISOString().replaceAll(":", "-").replaceAll(".", "-"); // safe filename +function attachJson(res, filename, obj) { + const json = JSON.stringify(obj, null, 2); + res.setHeader("Content-Type", "application/json; charset=utf-8"); + res.setHeader("Content-Disposition", `attachment; filename="${filename}"`); + return res.send(json); +} +function isAbs(p) { + try { return path.isAbsolute(p); } catch { return false; } +} + +/* ------------ Crawl endpoint ------------ */ +/** + * GET /crawl?url=https://site.com&max=50[&stream=1][&download=1][&nostore=1] + * - stream=1 : SSE live progress (no download) + * - download=1 : respond as a JSON download (attachment) + * - nostore=1 : ask crawler not to write files (if supported by your crawler) + */ +app.get("/crawl", async (req, res) => { + try { + const { url, max, stream, download, nostore } = req.query; + if (!url) return res.status(400).json({ error: "Missing url param" }); + + const target = new URL(String(url)); // validate + const limit = Math.min(Math.max(parseInt(max ?? "50", 10), 1), 500); + const wantsStream = + String(stream) === "1" || + (req.get("accept") || "").includes("text/event-stream"); + + /* ---------- SSE mode ---------- */ + if (wantsStream) { + if (String(download) === "1") { + return res.status(400).json({ error: "download not supported with stream=1" }); + } + res.setHeader("Content-Type", "text/event-stream"); + res.setHeader("Cache-Control", "no-cache, no-transform"); + res.setHeader("Connection", "keep-alive"); + res.flushHeaders?.(); + const heartbeat = setInterval(() => res.write(":\n\n"), 15000); + const send = (obj, evt) => { + if (evt) res.write(`event: ${evt}\n`); + res.write(`data: ${JSON.stringify(obj)}\n\n`); + }; + send({ ok: true, message: "Crawl started", url: target.toString(), limit }, "started"); + + let finished = false; + req.on("close", () => { clearInterval(heartbeat); if (!finished) console.warn("SSE client disconnected."); }); + + const result = await crawl( + target.toString(), + limit, + (tick) => send(tick), + // If your crawler supports it, this avoids writing files during SSE runs: + { persistReports: false, collectPages: true } + ); + + finished = true; + clearInterval(heartbeat); + send({ ok: true, done: true, result }, "done"); + return res.end(); + } + + /* ---------- Non-streaming mode ---------- */ + // Ask crawler (if it supports options) to avoid writing files when nostore=1 or download requested. + const preferMemory = String(nostore) === "1" || String(download) === "1"; + const result = await crawl( + target.toString(), + limit, + undefined, + preferMemory ? { persistReports: false, collectPages: true } : undefined + ); + + // If caller wants a downloadable JSON file... + if (String(download) === "1") { + const filename = `crawl-${ts()}.json`; + + // 1) Best case: crawler returned in-memory data (no disk IO). + // Use whichever property your crawler exposes. We try common shapes. + const inMemory = + result?.jsonData ?? + result?.pages ?? + result?.report ?? + (Array.isArray(result) ? result : null); + + if (inMemory) { + return attachJson(res, filename, inMemory); + } + + // 2) Fallback: crawler saved a JSON report path that we can stream. + const jsonPath = result?.reports?.json; + if (jsonPath && fs.existsSync(isAbs(jsonPath) ? jsonPath : path.join(__dirname, jsonPath))) { + const abs = isAbs(jsonPath) ? jsonPath : path.join(__dirname, jsonPath); + res.setHeader("Content-Type", "application/json; charset=utf-8"); + res.setHeader("Content-Disposition", `attachment; filename="${filename}"`); + return fs.createReadStream(abs).pipe(res); + } + + // 3) Last resort: send the entire result itself as JSON. + return attachJson(res, filename, result); + } + + // Default JSON (inline, not attachment) + return res.json({ + ok: true, + message: "Crawl completed", + url: target.toString(), + limit, + result + }); + } catch (err) { + console.error("Crawl error:", err); + return res.status(500).json({ error: "Crawl failed", details: String(err?.message ?? err) }); + } +}); + +/* ------------ Safety nets ------------ */ +process.on("unhandledRejection", (e) => console.error("unhandledRejection:", e)); +process.on("uncaughtException", (e) => console.error("uncaughtException:", e)); + +/* ------------ Start server ------------ */ +app.listen(PORT, "0.0.0.0", () => { + console.log(`🚀 Server running at http://localhost:${PORT}`); +}); \ No newline at end of file diff --git a/server.js b/server.js new file mode 100644 index 0000000..ae301e5 --- /dev/null +++ b/server.js @@ -0,0 +1,80 @@ +import express from "express"; +import cors from "cors"; +import path from "node:path"; +import fs from "node:fs"; +import { fileURLToPath } from "node:url"; +import dotenv from "dotenv"; + +import crawlRoutes from "./routes/crawl.routes.js"; +import sitemapRoutes from "./routes/sitemap.routes.js"; +import authRoutes from "./routes/auth.routes.js"; // Login & Signup endpoints +import paymentRoutes from "./routes/payment.route.js"; +import lighthouseRoutes from "./routes/lighthouse.routes.js"; // <-- ADD THIS +import messageRoutes from "./routes/message.routes.js"; +import cakeOrderRoutes from "./routes/maisondetreats/cakeOrder.routes.js"; +import blogRoutes from "./routes/blog.routes.js"; +import { connectDB } from "./config/db.js"; +import { mailer } from "./utils/mailer.js"; + +// ------------------ Load environment ------------------ +dotenv.config(); // Must be first + +// ------------------ Connect database ------------------ +await connectDB(); + +// ------------------ Express setup ------------------ +const app = express(); +const PORT = process.env.PORT || 3010; +const __dirname = path.dirname(fileURLToPath(import.meta.url)); + +app.use(express.json()); +app.use( + cors({ + origin: [ + "http://localhost:3000", + "http://127.0.0.1:3000", + "https://api.crawlerx.co", + "https://app.crawlerx.co", + ], + }) +); +app.use(express.static(path.join(__dirname, "public"))); + +// ------------------ SMTP verification ------------------ + +console.log("SMTP Host:", process.env.SMTP_HOST); +console.log("SMTP Port:", process.env.SMTP_PORT); +// ------------------ Routes ------------------ +app.get("/", (_req, res) => { + const viewer = path.join(__dirname, "public", "crawlerx_viewer.html"); + if (fs.existsSync(viewer)) { + return res.sendFile(viewer); + } else { + return res + .type("text/plain") + .send("CrawlerX backend is running."); + } +}); + +app.get("/healthz", (_req, res) => res.json({ ok: true, time: new Date().toISOString() })); + +app.use("/crawl", crawlRoutes); +app.use("/sitemap", sitemapRoutes); +app.use("/api/auth", authRoutes); // Login & Signup endpoints +app.use("/api/payment", paymentRoutes); +app.use("/api/lighthouse", lighthouseRoutes); +app.use("/api/blog", blogRoutes); // All blog/category/comment routes now prefixed with /api/blog +app.use("/api/messages", messageRoutes); +app.use("/api/cake-orders", cakeOrderRoutes); + +// Serve uploaded files +app.use('/uploads', express.static(path.join(process.cwd(), 'uploads'))); + +// ------------------ Safety nets ------------------ +process.on("unhandledRejection", (err) => console.error("Unhandled Rejection:", err)); +process.on("uncaughtException", (err) => console.error("Uncaught Exception:", err)); + +// ------------------ Start server ------------------ +app.listen(PORT, "0.0.0.0", () => { + console.log(`🚀 Server running at http://localhost:${PORT}`); +}); diff --git a/uploads/2176fe75251a06cf07a08e32a845b537 b/uploads/2176fe75251a06cf07a08e32a845b537 new file mode 100644 index 0000000..f20633e Binary files /dev/null and b/uploads/2176fe75251a06cf07a08e32a845b537 differ diff --git a/uploads/240f5f275adf05f94ee3cb81d3a4c829 b/uploads/240f5f275adf05f94ee3cb81d3a4c829 new file mode 100644 index 0000000..f20633e Binary files /dev/null and b/uploads/240f5f275adf05f94ee3cb81d3a4c829 differ diff --git a/uploads/3d2a70806f726d28bd1814bce39b0906 b/uploads/3d2a70806f726d28bd1814bce39b0906 new file mode 100644 index 0000000..c353b23 Binary files /dev/null and b/uploads/3d2a70806f726d28bd1814bce39b0906 differ diff --git a/uploads/866ff6699ca2b462d73b20c4d150b2e0 b/uploads/866ff6699ca2b462d73b20c4d150b2e0 new file mode 100644 index 0000000..f20633e Binary files /dev/null and b/uploads/866ff6699ca2b462d73b20c4d150b2e0 differ diff --git a/uploads/8930208f2d9cc158282fc31953abc6a3 b/uploads/8930208f2d9cc158282fc31953abc6a3 new file mode 100644 index 0000000..f20633e Binary files /dev/null and b/uploads/8930208f2d9cc158282fc31953abc6a3 differ diff --git a/uploads/d6d36b979d636c9f38a712bd3c298563 b/uploads/d6d36b979d636c9f38a712bd3c298563 new file mode 100644 index 0000000..f20633e Binary files /dev/null and b/uploads/d6d36b979d636c9f38a712bd3c298563 differ diff --git a/utils/mailer.js b/utils/mailer.js new file mode 100644 index 0000000..0c4849f --- /dev/null +++ b/utils/mailer.js @@ -0,0 +1,105 @@ +import nodemailer from "nodemailer"; + +// +// Create reusable transporter object +// +export const mailer = nodemailer.createTransport({ + host: "mail.crawlerx.co", // your Hestia mail host + port: 587, // STARTTLS + secure: false, // must be false for 587 + auth: { + user: "info@crawlerx.co", // e.g. info@crawlerx.co + pass: "CrawlerX@2025", // mailbox password + }, + name: "mail.crawlerx.co", // explicitly set hostname + tls: { + rejectUnauthorized: false, // allow self-signed certs + }, + logger: true, // optional: logs connection steps + debug: true, // optional: debug SMTP connection +}); + + +// +// Send welcome / signup email +// +export async function sendSignupMail(toEmail) { + try { + await mailer.sendMail({ + from: `"CrawlerX" <${process.env.SMTP_USER}>`, + to: toEmail, + subject: "Welcome to CrawlerX", + html: ` +

Welcome!

+

Your signup was successful. You can now log in and start using the app.

+ `, + }); + console.log(`✅ Signup email sent to ${toEmail}`); + } catch (err) { + console.error("❌ Error sending signup email:", err); + } +} + +// +// Send reset-password email with 4-digit code or token link +// +export async function sendResetPasswordMail(email, token) { + try { + const resetURL = `${process.env.FRONTEND_URL}/reset-password?email=${email}&token=${token}`; + await mailer.sendMail({ + from: `"CrawlerX" <${process.env.SMTP_USER}>`, + to: email, + subject: "Reset your password", + html: ` +

You requested a password reset.

+

Click here to reset: ${resetURL}

+

This link is valid for 1 hour.

+ `, + }); + console.log(`✅ Reset password email sent to ${email}`); + } catch (err) { + console.error("❌ Error sending reset password email:", err); + } +} + + +export const sendCakeOrderMail = async (toEmail, orderData) => { + try { + const transporter = nodemailer.createTransport({ + host: "mail.crawlerx.co", + port: 587, + secure: false, // use TLS? false for port 587 + auth: { + user: "info@crawlerx.co", + pass: "CrawlerX@2025", + }, + tls: { + rejectUnauthorized: false, // <--- allow self-signed certificate + }, + }); + + const orderItems = Object.entries(orderData) + .map(([category, flavours]) => { + const items = Object.entries(flavours) + .map(([flavour, qty]) => `• ${flavour}: ${qty}`) + .join("\n"); + return `${category}:\n${items}`; + }) + .join("\n\n"); + + const mailOptions = { + from: `"Maison de Treats" `, + to: toEmail, + subject: "🎉 Your Cake Order Confirmation", + text: `Thank you for your order! Here are the details:\n\n${orderItems}`, + html: `

Thank you for your order!

+

Here are your cake order details:

+
${orderItems}
`, + }; + + await transporter.sendMail(mailOptions); + console.log("Cake order email sent to", toEmail); + } catch (err) { + console.error("Failed to send cake order email:", err); + } +}; \ No newline at end of file diff --git a/utils/sitemap.js b/utils/sitemap.js new file mode 100644 index 0000000..9ee751c --- /dev/null +++ b/utils/sitemap.js @@ -0,0 +1,20 @@ +import Sitemapper from "sitemapper"; +import normalizeUrl from "normalize-url"; + +/** Return an array of normalized URLs from /sitemap.xml (or [] if none). */ +export async function getSitemapUrls(startUrl) { + const origin = new URL(startUrl).origin; + const sitemapUrl = `${origin}/sitemap.xml`; + const sm = new Sitemapper({ url: sitemapUrl, timeout: 15000 }); + + try { + const { sites } = await sm.fetch(); + const out = []; + for (const u of sites || []) { + try { out.push(normalizeUrl(u, { stripHash: true })); } catch {} + } + return out; + } catch { + return []; + } +} diff --git a/utils/stripe.js b/utils/stripe.js new file mode 100644 index 0000000..a8ded10 --- /dev/null +++ b/utils/stripe.js @@ -0,0 +1,8 @@ +import Stripe from "stripe"; +import dotenv from "dotenv"; + +dotenv.config(); + +export const stripe = new Stripe(process.env.STRIPE_SECRET_KEY, { + apiVersion: "2024-06-20", +}); diff --git a/utils/urlHelpers.js b/utils/urlHelpers.js new file mode 100644 index 0000000..e21b4ee --- /dev/null +++ b/utils/urlHelpers.js @@ -0,0 +1,11 @@ +export function isInternal(base, candidate) { + try { + const baseUrl = new URL(base); + const testUrl = new URL(candidate, base); + const protocolOk = testUrl.protocol === "http:" || testUrl.protocol === "https:"; + const stripWWW = (h) => h.replace(/^www\./i, ""); + return protocolOk && stripWWW(baseUrl.hostname) === stripWWW(testUrl.hostname); + } catch { + return false; + } +}