diff --git a/package.json b/package.json
index cbb3bf3..a27a412 100755
--- a/package.json
+++ b/package.json
@@ -20,6 +20,7 @@
"bcryptjs": "^3.0.2",
"bluebird": "^3.7.2",
"body-parser": "^1.20.3",
+ "cheerio": "^1.0.0",
"cookie-parser": "^1.4.7",
"cors": "^2.8.5",
"country-codes-list": "^2.0.0",
diff --git a/src/server/models/product.js b/src/server/models/product.js
index 626ccee..f088173 100755
--- a/src/server/models/product.js
+++ b/src/server/models/product.js
@@ -238,6 +238,12 @@ const productSchema = new Schema({
date_updated: {
type: Date,
},
+ scraped: {
+ type: Boolean,
+ },
+ scraped_date: {
+ type: Date,
+ },
validaprod: {
esito: {
type: Number,
@@ -422,6 +428,9 @@ module.exports.executeQueryPickup = async function (idapp, params) {
return [...risexact, ...ris];
};
+module.exports.getProductByIsbn = function (idapp, isbn) {
+ return Product.findAllIdApp(idapp, null, null, null, isbn);
+};
module.exports.getProductByCode = function (idapp, code) {
return Product.findAllIdApp(idapp, code);
};
@@ -454,7 +463,7 @@ module.exports.isLowQuantityInStockById = async function (id) {
return false;
};
-module.exports.findAllIdApp = async function (idapp, code, id, all) {
+module.exports.findAllIdApp = async function (idapp, code, id, all, isbn) {
let myfind = {};
let myqueryadd = {};
let query = [];
@@ -478,6 +487,9 @@ module.exports.findAllIdApp = async function (idapp, code, id, all) {
if (code) {
myfind = { ...myfind, code };
}
+ if (isbn) {
+ myfind = { ...myfind, sbn };
+ }
if (id) {
myqueryadd = {
$addFields: {
diff --git a/src/server/models/productInfo.js b/src/server/models/productInfo.js
index 532b303..07dccb5 100755
--- a/src/server/models/productInfo.js
+++ b/src/server/models/productInfo.js
@@ -183,6 +183,13 @@ const productInfoSchema = new Schema({
sottotitolo: String,
link_macro: String,
+ scraped: {
+ type: Boolean,
+ },
+ scraped_date: {
+ type: Date,
+ },
+
});
var productInfo = module.exports = mongoose.model('ProductInfo', productInfoSchema);
diff --git a/src/server/modules/Macro.js b/src/server/modules/Macro.js
index b850771..97f6138 100644
--- a/src/server/modules/Macro.js
+++ b/src/server/modules/Macro.js
@@ -587,7 +587,7 @@ class Macro {
setta = false,
importa = true;
- let product = { ...productInput };
+ let product = { ...productInput, deleted: false };
if (options.inputdaGM)
diff --git a/src/server/modules/Scraping.js b/src/server/modules/Scraping.js
index 7d7c0db..140ddcc 100644
--- a/src/server/modules/Scraping.js
+++ b/src/server/modules/Scraping.js
@@ -1,13 +1,18 @@
-import axios from 'axios';
-import cheerio from 'cheerio';
+const axios = require('axios');
+const cheerio = require('cheerio');
+
+const Product = require('../models/product');
+const ProductInfo = require('../models/productInfo');
+
+const tools = require('../tools/general');
class AmazonBookScraper {
constructor() {
this.baseUrl = 'https://www.amazon.it/dp/';
}
- async fetchPage(isbn) {
- const url = `${this.baseUrl}${isbn}`;
+ async fetchPageISBN10(isbn10) {
+ const url = `${this.baseUrl}${isbn10}`;
try {
const { data } = await axios.get(url, {
headers: {
@@ -20,106 +25,380 @@ class AmazonBookScraper {
});
return data;
} catch (err) {
- console.error(`Errore fetching ISBN ${isbn}:`, err.message);
+ console.error(`Errore fetching ISBN ${isbn10}:`, err.message);
+ return null;
+ }
+ }
+ isbn13to10(isbn13) {
+ try {
+ if (!isbn13.startsWith('978') || isbn13.length !== 13) return null;
+ const core = isbn13.slice(3, 12); // i 9 numeri centrali
+
+ let sum = 0;
+ for (let i = 0; i < 9; i++) {
+ sum += (10 - i) * parseInt(core[i], 10);
+ }
+ let check = 11 - (sum % 11);
+ if (check === 10) check = 'X';
+ else if (check === 11) check = '0';
+ else check = check.toString();
+ return core + check;
+ } catch (err) {
+ console.error('Errore calcolando ISBN-10 da ISBN-13:', err.message);
return null;
}
}
- extractData(html) {
+ getTitleByProductInfo(productInfo) {
+ try {
+ return productInfo?.name.trim();
+ } catch (e) {
+ return '';
+ }
+ }
+
+ async extractData(myproduct, html) {
const $ = cheerio.load(html);
+ const productInfo = await ProductInfo.findOne({ _id: myproduct.idProductInfo }).lean();
+
+ const title_ondb = this.getTitleByProductInfo(productInfo);
+
// Titolo
let title = $('#productTitle').text().trim() || null;
+ // Sottotitolo (Amazon lo mette nel titolo)
- // Sottotitolo (Amazon spesso lo mette in #productSubtitle o nel titolo, proveremo)
- let subtitle = $('#productSubtitle').text().trim() || null;
+ let dettagli = $('#productSubtitle').text().trim() || null;
- // Numero pagine, formato, edizione
- // Questi dati spesso sono nella tabella dettagli prodotto con id #detailBullets_feature_div o #productDetailsTable
- // Proviamo a estrarre da #detailBullets_feature_div
+ const ris = this.extractSubtitle(title, title_ondb);
- let pages = null;
- let format = null;
- let edition = null;
+ let sottotitolo = title_ondb ? ris?.subtitle : '';
- $('#detailBullets_feature_div li').each((i, el) => {
- const label = $(el).find('span.a-text-bold').text().trim().toLowerCase();
- const value = $(el).find('span').last().text().trim();
+ let titoloOriginale = '';
- if (label.includes('pagine') || label.includes('pagine stampate')) {
- pages = value;
- } else if (label.includes('formato')) {
- format = value;
- } else if (label.includes('edizione')) {
- edition = value;
- }
- });
-
- // fallback su #productDetailsTable (altro possibile layout)
- if (!pages || !format || !edition) {
- $('#productDetailsTable .content tr').each((i, el) => {
- const label = $(el).find('th').text().trim().toLowerCase();
- const value = $(el).find('td').text().trim();
-
- if (!pages && (label.includes('pagine') || label.includes('pagine stampate'))) {
- pages = value;
- } else if (!format && label.includes('formato')) {
- format = value;
- } else if (!edition && label.includes('edizione')) {
- edition = value;
- }
- });
+ if (ris?.titoloOriginale) {
+ // E' presente il vero titolo del Libro !
+ titoloOriginale = ris.titoloOriginale;
}
- return { title, subtitle, pages, format, edition };
+ if (sottotitolo && title_ondb) {
+ // significa che il titolo è dentro al sottotitolo, quindi prendi per buono quello nostro
+ title = title_ondb;
+ }
+
+ let numpagine = null;
+ let misure = null;
+ let edizione = null;
+ let publisher = null;
+ let data_pubblicazione = null;
+
+ numpagine = this.extractNumeroDiPagine($);
+ const dim = this.extractDimensions($);
+ misure = this.convertDimensionsToMisureMacro(dim);
+ const data_pubb = this.extractDataPubblicazione($);
+ data_pubblicazione = this.parseItalianDate(data_pubb);
+
+ publisher = this.extractEditore($);
+
+ if (data_pubb) {
+ edizione = this.extractMonthYear(data_pubb);
+ }
+
+ return {
+ titolo: title,
+ ...(titoloOriginale ? { titoloOriginale } : {}),
+ ...(sottotitolo ? { sottotitolo } : {sottotitolo: ''}),
+ numpagine,
+ misure,
+ edizione,
+ data_pubblicazione,
+ editore: publisher,
+ };
}
- async scrapeISBN(isbn) {
- const html = await this.fetchPage(isbn);
+ async scrapeISBN(myproduct, isbn, options) {
+ const isbn10 = this.isbn13to10(isbn);
+ const html = await this.fetchPageISBN10(isbn10);
if (!html) return null;
- const data = this.extractData(html);
+ const data = await this.extractData(myproduct, html);
+
+ if (!options?.update) return data;
+
+ const arrvariazioni = myproduct.arrvariazioni || [];
+ let index = -1;
+
+ if (arrvariazioni.length === 1) {
+ index = 0;
+ } else {
+ index = arrvariazioni.findIndex((v) => v.versione === shared_consts.PRODUCTTYPE.NUOVO);
+ if (index < 0) index = 0;
+ }
+
+ const productInfo = {};
+ let aggiornaDataPubb = false;
+
+ let aggiornaPages = false;
+ let aggiornaMisure = false;
+ let aggiornaEdizione = false;
+
+ if (index !== -1) {
+ const variante = arrvariazioni[index];
+
+ // Determina se aggiornare pagine
+ aggiornaPages = !options.aggiornasoloSeVuoti || !variante.pagine || variante.pagine === 0;
+ if (aggiornaPages) variante.pagine = Number(data.numpagine);
+
+ // Determina se aggiornare misure
+ aggiornaMisure = !options.aggiornasoloSeVuoti || !variante.misure;
+ if (aggiornaMisure) variante.misure = data.misure;
+
+ // Determina se aggiornare edizione
+ aggiornaEdizione = !options.aggiornasoloSeVuoti || !variante.edizione;
+ if (aggiornaEdizione) variante.edizione = data.edizione;
+ }
+
+ // Determina se aggiornare data pubblicazione
+ const currentDatePub = productInfo.date_pub;
+ aggiornaDataPubb = !options.aggiornasoloSeVuoti || !tools.isDateValid(currentDatePub);
+ if (aggiornaDataPubb && data.data_pubblicazione) {
+ productInfo.date_pub = new Date(data.data_pubblicazione);
+ }
+
+ // Aggiorna arrvariazioni se pagine o misure sono cambiati
+ const aggiornadati = aggiornaPages || aggiornaMisure || aggiornaEdizione;
+
+ if (aggiornadati) {
+ await Product.findOneAndUpdate(
+ { _id: myproduct._id },
+ { $set: { arrvariazioni, scraped: true, scraped_date: new Date() } },
+ { upsert: true, new: true, includeResultMetadata: true }
+ );
+ }
+
+ // Aggiorna productInfo se contiene dati
+ if (!tools.isObjectEmpty(productInfo)) {
+ const risupdate = await ProductInfo.findOneAndUpdate(
+ { _id: myproduct.idProductInfo },
+ { $set: productInfo, scraped: true, scraped_date: new Date() },
+ { new: true, upsert: true, returnOriginal: false }
+ ).lean();
+ console.log('risupdate', risupdate) ;
+ }
+
return data;
}
- async scrapeMultiple(isbnList) {
+ async scrapeMultiple(isbnList, options) {
const results = [];
for (const isbn of isbnList) {
console.log(`Scraping ISBN: ${isbn}`);
- const data = await this.scrapeISBN(isbn);
+ const myproduct = null;
+ /// myproduct...
+ const data = await this.scrapeISBN(myproduct, isbn, options);
results.push({ isbn, ...data });
// Per evitare blocchi, metti una pausa (es. 2 secondi)
await new Promise((r) => setTimeout(r, 2000));
}
return results;
}
-}
-export async function ScraperDataAmazon(idapp, options) {
- const scraper = new AmazonBookScraper();
- const isbn = options.isbn;
+ generateHtmlTableFromObject(obj) {
+ if (!obj || typeof obj !== 'object') return '';
- try {
- const data = await scraper.scrapeISBN(isbn);
- console.log(data);
- return data;
- } catch (e) {
- console.error(e);
- return res.status(400).send({ code: server_constants.RIS_CODE_ERR, msg: '' });
+ let html = '
';
+ html += '| Chiave | Valore |
';
+
+ for (const [key, value] of Object.entries(obj)) {
+ // Se il valore è un oggetto o array, lo converto in JSON stringa
+ const displayValue = value && typeof value === 'object' ? JSON.stringify(value) : String(value);
+
+ html += `| ${key} | ${displayValue} |
`;
+ }
+
+ html += '
';
+ return html;
+ }
+
+ async ScraperDataAmazon(idapp, options) {
+ const scraper = new AmazonBookScraper();
+ const isbn = options.isbn;
+
+ try {
+ const myproduct = await Product.getProductByIsbn(isbn);
+ const data = await scraper.scrapeISBN(myproduct, isbn, options);
+
+ // let html = this.generateHtmlTableFromObject(data);
+
+ console.log(data);
+ return res.status(200).send({ code: server_constants.RIS_CODE_OK, data, html });
+ } catch (e) {
+ console.error(e);
+ return res.status(400).send({ code: server_constants.RIS_CODE_ERR, msg: '' });
+ }
+ }
+
+ async ScraperMultipleDataAmazon(idapp, options) {
+ const scraper = new AmazonBookScraper();
+ const isbnList = ['8850224248']; // metti i tuoi ISBN qui
+
+ try {
+ const books = await scraper.scrapeMultiple(isbnList);
+ console.log(books);
+ } catch (e) {
+ console.error(e);
+ return res.status(400).send({ code: server_constants.RIS_CODE_ERR, msg: '' });
+ }
+ }
+
+ extractDataPubblicazione($) {
+ // Seleziona il div con id specifico per la data di pubblicazione
+ const publicationDate = $('#rpi-attribute-book_details-publication_date .rpi-attribute-value span').text().trim();
+
+ // Se non trova la data, ritorna null
+ return publicationDate || null;
+ }
+
+ extractNumeroDiPagine($) {
+ // Seleziona il div con id specifico per pagine
+ const pagesText = $('#rpi-attribute-book_details-fiona_pages .rpi-attribute-value span').text().trim();
+
+ // pagesText dovrebbe essere tipo "184 pagine"
+ if (!pagesText) return null;
+
+ // Estrai solo il numero (facoltativo)
+ const match = pagesText.match(/(\d+)/);
+ return match ? match[1] : pagesText; // ritorna solo il numero o il testo intero
+ }
+
+ extractDimensions($) {
+ // Seleziona il div con id specifico per dimensioni
+ const dimText = $('#rpi-attribute-book_details-dimensions .rpi-attribute-value span').text().trim();
+
+ // Se non trova niente ritorna null
+ return dimText || null;
+ }
+
+ convertDimensionsToMisureMacro(dimString) {
+ if (!dimString) return null;
+
+ // Estrai tutti i numeri (compresi decimali)
+ const numbers = dimString.match(/[\d.]+/g);
+ if (!numbers || numbers.length < 2) return null;
+
+ // Converti in numeri float e ordina decrescente
+ const sortedNums = numbers
+ .map((num) => parseFloat(num))
+ .filter((n) => !isNaN(n))
+ .sort((a, b) => b - a);
+
+ if (sortedNums.length < 2) return null;
+
+ // Prendi i due più grandi
+ const [first, second] = sortedNums;
+
+ return `cm ${first}x${second}`;
+ }
+
+ parseItalianDate(dateStr) {
+ if (!dateStr) return null;
+
+ // Mappa mesi in italiano a numeri (0-based per Date)
+ const months = {
+ gennaio: 0,
+ febbraio: 1,
+ marzo: 2,
+ aprile: 3,
+ maggio: 4,
+ giugno: 5,
+ luglio: 6,
+ agosto: 7,
+ settembre: 8,
+ ottobre: 9,
+ novembre: 10,
+ dicembre: 11,
+ };
+
+ // Divido la stringa (es. "14 maggio 2025")
+ const parts = dateStr.toLowerCase().split(' ');
+ if (parts.length !== 3) return null;
+
+ const day = parseInt(parts[0], 10);
+ const month = months[parts[1]];
+ const year = parseInt(parts[2], 10);
+
+ if (isNaN(day) || month === undefined || isNaN(year)) return null;
+
+ return new Date(year, month, day);
+ }
+ extractSubtitle(fullTitle, baseTitle) {
+ if (!fullTitle || !baseTitle) return null;
+
+ let mybaseTitle = '';
+ let numCharRemoved = 0;
+ let titoloOriginale = '';
+
+ // Se il fullTitle non contiene il baseTitle, ritorna null
+ const coniniziali = fullTitle.trim().toLowerCase().startsWith(baseTitle.trim().toLowerCase());
+ if (coniniziali) {
+ mybaseTitle = baseTitle;
+ }
+
+ let senzainiziali = false;
+
+ if (!coniniziali) {
+ // torna la posizione in cui l'ho trovato
+ const posizione = fullTitle.toLowerCase().indexOf(baseTitle.trim().toLowerCase());
+ if (posizione < 0 || posizione > 3) {
+ return null;
+ }
+ // torna il nome del titolo, compreso degli articoli, partendo da zero, fino a posizione + baseTitle
+ titoloOriginale = fullTitle.substring(0, posizione + baseTitle.length);
+
+ numCharRemoved = posizione;
+ senzainiziali = true;
+ }
+
+ if (!coniniziali && !senzainiziali) {
+ return null;
+ }
+
+ // Rimuovi il baseTitle dall'inizio
+ let remainder = fullTitle.slice(baseTitle.length + numCharRemoved).trim();
+
+ // Se la rimanenza inizia con ":" o "-" o ".", rimuovila
+ if (remainder.startsWith(':') || remainder.startsWith('-') || remainder.startsWith('.')) {
+ remainder = remainder.slice(1).trim();
+ }
+
+ // Se resta una stringa non vuota, è il sottotitolo
+ return { subtitle: remainder.length > 0 ? remainder : null, titoloOriginale };
+ }
+
+ extractMonthYear(dateStr) {
+ if (!dateStr) return null;
+
+ // Divide la stringa in parole
+ const parts = dateStr.trim().split(' ');
+
+ // Se ha almeno 3 parti (giorno, mese, anno)
+ if (parts.length >= 3) {
+ // Restituisce mese + anno
+ return parts
+ .slice(1)
+ .map((part, idx) => (idx === 0 ? part[0].toUpperCase() + part.slice(1) : part))
+ .join(' ');
+ }
+
+ return null;
+ }
+
+ extractEditore($) {
+ // Seleziona il testo dentro il div id rpi-attribute-book_details-publisher
+ const publisher = $('#rpi-attribute-book_details-publisher .rpi-attribute-value span').text().trim();
+
+ return publisher || null;
}
}
-export async function ScraperMultipleDataAmazon(idapp, options) {
- const scraper = new AmazonBookScraper();
- const isbnList = ['8850224248']; // metti i tuoi ISBN qui
-
- try {
- const books = await scraper.scrapeMultiple(isbnList);
- console.log(books);
- } catch (e) {
- console.error(e);
- return res.status(400).send({ code: server_constants.RIS_CODE_ERR, msg: '' });
- }
-}
-
-export default AmazonBookScraper;
+module.exports = AmazonBookScraper;
diff --git a/src/server/router/admin_router.js b/src/server/router/admin_router.js
index f62d813..244096a 100755
--- a/src/server/router/admin_router.js
+++ b/src/server/router/admin_router.js
@@ -27,7 +27,7 @@ const Gasordine = require('../models/gasordine');
const { User } = require('../models/user');
-const AmazonBookScraper = require('../modules/Scraping');
+const AmazonBookScraper = require('../modules/scraping');
const { Catalog } = require('../models/catalog');
const { RaccoltaCataloghi } = require('../models/raccoltacataloghi');
@@ -2363,13 +2363,24 @@ router.post('/cloudflare', authenticate, async (req, res) => {
});
router.post('/scraper', authenticate, async (req, res) => {
+ idapp = req.body.idapp;
+ idProduct = req.body.product_id;
+ options = req.body.options;
+
const scraper = new AmazonBookScraper();
- const isbn = req.data.options.isbn;
+
+ const product = await Product.getProductById(idProduct);
+ let isbn = '';
+ if (product) {
+ isbn = product.isbn;
+ }
try {
- const data = await scraper.scrapeISBN(isbn);
- console.log(data);
-
+ let data = null;
+ if (isbn) {
+ data = await scraper.scrapeISBN(product, isbn, options);
+ console.log(data);
+ }
return res.send(data);
} catch (e) {
console.error(e);
diff --git a/src/server/tools/general.js b/src/server/tools/general.js
index da1aa69..e58a231 100755
--- a/src/server/tools/general.js
+++ b/src/server/tools/general.js
@@ -6299,5 +6299,15 @@ module.exports = {
return filename;
},
+ isDateValid(mydate) {
+ try {
+ return (
+ mydate instanceof Date && isFinite(mydate.getTime()) && mydate.toISOString().split('T')[0] !== '1970-01-01'
+ );
+ } catch {
+ return false;
+ }
+ },
+
};
diff --git a/yarn.lock b/yarn.lock
index 8b6ffca..627fe02 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -2102,6 +2102,23 @@ cheerio@^0.22.0:
lodash.reject "^4.4.0"
lodash.some "^4.4.0"
+cheerio@^1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/cheerio/-/cheerio-1.0.0.tgz#1ede4895a82f26e8af71009f961a9b8cb60d6a81"
+ integrity sha512-quS9HgjQpdaXOvsZz82Oz7uxtXiy6UIsIQcpBj7HRw2M63Skasm9qlDocAM7jNuaxdhpPU7c4kJN+gA5MCu4ww==
+ dependencies:
+ cheerio-select "^2.1.0"
+ dom-serializer "^2.0.0"
+ domhandler "^5.0.3"
+ domutils "^3.1.0"
+ encoding-sniffer "^0.2.0"
+ htmlparser2 "^9.1.0"
+ parse5 "^7.1.2"
+ parse5-htmlparser2-tree-adapter "^7.0.0"
+ parse5-parser-stream "^7.1.2"
+ undici "^6.19.5"
+ whatwg-mimetype "^4.0.0"
+
chokidar@^3.5.1, chokidar@^3.5.2, chokidar@^3.5.3:
version "3.6.0"
resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-3.6.0.tgz#197c6cc669ef2a8dc5e7b4d97ee4e092c3eb0d5b"
@@ -2874,7 +2891,7 @@ domutils@^2.4.2:
domelementtype "^2.2.0"
domhandler "^4.2.0"
-domutils@^3.0.1:
+domutils@^3.0.1, domutils@^3.1.0:
version "3.2.2"
resolved "https://registry.yarnpkg.com/domutils/-/domutils-3.2.2.tgz#edbfe2b668b0c1d97c24baf0f1062b132221bc78"
integrity sha512-6kZKyUajlDuqlHKVX1w7gyslj9MPIXzIFiz/rGu35uC1wMi+kMhQwGhl4lt9unC9Vb9INnY9Z3/ZA3+FhASLaw==
@@ -3011,6 +3028,14 @@ encoding-japanese@2.2.0:
resolved "https://registry.yarnpkg.com/encoding-japanese/-/encoding-japanese-2.2.0.tgz#0ef2d2351250547f432a2dd155453555c16deb59"
integrity sha512-EuJWwlHPZ1LbADuKTClvHtwbaFn4rOD+dRAbWysqEOXRc2Uui0hJInNJrsdH0c+OhJA4nrCBdSkW4DD5YxAo6A==
+encoding-sniffer@^0.2.0:
+ version "0.2.0"
+ resolved "https://registry.yarnpkg.com/encoding-sniffer/-/encoding-sniffer-0.2.0.tgz#799569d66d443babe82af18c9f403498365ef1d5"
+ integrity sha512-ju7Wq1kg04I3HtiYIOrUrdfdDvkyO9s5XM8QAj/bN61Yo/Vb4vgJxy5vi4Yxk01gWHbrofpPtpxM8bKger9jhg==
+ dependencies:
+ iconv-lite "^0.6.3"
+ whatwg-encoding "^3.1.1"
+
end-of-stream@^1.1.0, end-of-stream@^1.4.4:
version "1.4.4"
resolved "https://registry.yarnpkg.com/end-of-stream/-/end-of-stream-1.4.4.tgz#5ae64a5f45057baf3626ec14da0ca5e4b2431eb0"
@@ -3064,6 +3089,11 @@ entities@^4.2.0, entities@^4.4.0, entities@^4.5.0:
resolved "https://registry.yarnpkg.com/entities/-/entities-4.5.0.tgz#5d268ea5e7113ec74c4d033b79ea5a35a488fb48"
integrity sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==
+entities@^6.0.0:
+ version "6.0.0"
+ resolved "https://registry.yarnpkg.com/entities/-/entities-6.0.0.tgz#09c9e29cb79b0a6459a9b9db9efb418ac5bb8e51"
+ integrity sha512-aKstq2TDOndCn4diEyp9Uq/Flu2i1GlLkc6XIDQSDMuaFE3OPW5OphLCyQ5SpSJZTb4reN+kTcYru5yIfXoRPw==
+
error-ex@^1.3.1:
version "1.3.2"
resolved "https://registry.yarnpkg.com/error-ex/-/error-ex-1.3.2.tgz#b4ac40648107fdcdcfae242f428bea8a14d4f1bf"
@@ -4175,6 +4205,16 @@ htmlparser2@^8.0.0, htmlparser2@^8.0.1, htmlparser2@^8.0.2:
domutils "^3.0.1"
entities "^4.4.0"
+htmlparser2@^9.1.0:
+ version "9.1.0"
+ resolved "https://registry.yarnpkg.com/htmlparser2/-/htmlparser2-9.1.0.tgz#cdb498d8a75a51f739b61d3f718136c369bc8c23"
+ integrity sha512-5zfg6mHUoaer/97TxnGpxmbR7zJtPwIYFMZ/H5ucTlPZhKvtum05yiPK3Mgai3a0DyVxv7qYqoweaEd2nrYQzQ==
+ dependencies:
+ domelementtype "^2.3.0"
+ domhandler "^5.0.3"
+ domutils "^3.1.0"
+ entities "^4.5.0"
+
http-errors@2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-2.0.0.tgz#b7774a1486ef73cf7667ac9ae0858c012c57b9d3"
@@ -6662,6 +6702,13 @@ parse5-htmlparser2-tree-adapter@^7.0.0:
domhandler "^5.0.3"
parse5 "^7.0.0"
+parse5-parser-stream@^7.1.2:
+ version "7.1.2"
+ resolved "https://registry.yarnpkg.com/parse5-parser-stream/-/parse5-parser-stream-7.1.2.tgz#d7c20eadc37968d272e2c02660fff92dd27e60e1"
+ integrity sha512-JyeQc9iwFLn5TbvvqACIF/VXG6abODeB3Fwmv/TGdLk2LfbWkaySGY72at4+Ty7EkPZj854u4CrICqNk2qIbow==
+ dependencies:
+ parse5 "^7.0.0"
+
parse5@^7.0.0, parse5@^7.2.1:
version "7.2.1"
resolved "https://registry.yarnpkg.com/parse5/-/parse5-7.2.1.tgz#8928f55915e6125f430cc44309765bf17556a33a"
@@ -6669,6 +6716,13 @@ parse5@^7.0.0, parse5@^7.2.1:
dependencies:
entities "^4.5.0"
+parse5@^7.1.2:
+ version "7.3.0"
+ resolved "https://registry.yarnpkg.com/parse5/-/parse5-7.3.0.tgz#d7e224fa72399c7a175099f45fc2ad024b05ec05"
+ integrity sha512-IInvU7fabl34qmi9gY8XOVxhYyMyuH2xUNpb2q8/Y+7552KlejkRvqvD19nMoUW/uQGGbqNpA6Tufu5FL5BZgw==
+ dependencies:
+ entities "^6.0.0"
+
parseley@^0.12.0:
version "0.12.1"
resolved "https://registry.yarnpkg.com/parseley/-/parseley-0.12.1.tgz#4afd561d50215ebe259e3e7a853e62f600683aef"
@@ -8632,6 +8686,11 @@ undici-types@~6.20.0:
resolved "https://registry.yarnpkg.com/undici-types/-/undici-types-6.20.0.tgz#8171bf22c1f588d1554d55bf204bc624af388433"
integrity sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg==
+undici@^6.19.5:
+ version "6.21.3"
+ resolved "https://registry.yarnpkg.com/undici/-/undici-6.21.3.tgz#185752ad92c3d0efe7a7d1f6854a50f83b552d7a"
+ integrity sha512-gBLkYIlEnSp8pFbT64yFgGE6UIB9tAkhukC23PmMDCe5Nd+cRqKxSjw5y54MK2AZMgZfJWMaNE4nYUHgi1XEOw==
+
unicode-emoji-modifier-base@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/unicode-emoji-modifier-base/-/unicode-emoji-modifier-base-1.0.0.tgz#dbbd5b54ba30f287e2a8d5a249da6c0cef369459"