diff --git a/lib/cache/cache_manager.js b/lib/cache/cache_manager.js index ac48f558085..cb4ede9a17d 100644 --- a/lib/cache/cache_manager.js +++ b/lib/cache/cache_manager.js @@ -2,36 +2,45 @@ import BLOG from '@/blog.config' import FileCache from './local_file_cache' import MemoryCache from './memory_cache' import RedisCache from './redis_cache' +import VercelCache from './vercel_cache' -// 配置是否开启Vercel环境中的缓存,因为Vercel中现有两种缓存方式在无服务环境下基本都是无意义的,纯粹的浪费资源 -const enableCacheInVercel = +const cacheStats = { + hit: 0, + miss: 0, + set: 0, + error: 0, + total: 0, + perStore: {} // { redis: {hit, set}, memory: {...} } +} + +const isBuildPhase = process.env.npm_lifecycle_event === 'build' || - process.env.npm_lifecycle_event === 'export' || - !BLOG['isProd'] - -/** - * 尝试从缓存中获取数据,如果没有则尝试获取数据并写入缓存,最终返回所需数据 - * @param key - * @param getDataFunction - * @param getDataArgs - * @returns {Promise<*|null>} - */ -export async function getOrSetDataWithCache( - key, - getDataFunction, - ...getDataArgs -) { + process.env.npm_lifecycle_event === 'export' + + + +const enableLocalCache = isBuildPhase || !BLOG['isProd'] +const hasRedis = !!BLOG.REDIS_URL + +const inflightMap = new Map() + +const pid = process.pid + +function isVercelEnv() { + return !!process.env.VERCEL +} + +function cacheLog(action, key, extra = '') { + const type = getCacheType() + console.log( + `[Cache][${type.toUpperCase()}][pid:${process.pid}] ${action} key:${key} ${extra}` + ) +} + +export async function getOrSetDataWithCache(key, getDataFunction, ...getDataArgs) { return getOrSetDataWithCustomCache(key, null, getDataFunction, ...getDataArgs) } -/** - * 尝试从缓存中获取数据,如果没有则尝试获取数据并自定义写入缓存,最终返回所需数据 - * @param key - * @param customCacheTime - * @param getDataFunction - * @param getDataArgs - * @returns {Promise<*|null>} - */ export async function getOrSetDataWithCustomCache( key, customCacheTime, @@ -40,67 +49,161 @@ export async function getOrSetDataWithCustomCache( ) { const dataFromCache = await getDataFromCache(key) if (dataFromCache) { - // console.log('[缓存-->>API]:', key) // 避免过多的缓存日志输出 + // cacheLog('HIT', key) return dataFromCache } - const data = await getDataFunction(...getDataArgs) - if (data) { - // console.log('[API-->>缓存]:', key) - await setDataToCache(key, data, customCacheTime) + + if (inflightMap.has(key)) { + // cacheLog('INFLIGHT-WAIT', key) + return inflightMap.get(key) } - return data || null + + // cacheLog('MISS', key, '缓存未命中,发起真实请求') + + const promise = getDataFunction(...getDataArgs) + .then(async data => { + if (data) { + await setDataToCache(key, data, customCacheTime) + cacheLog('SET', key, '写入缓存成功') + } + inflightMap.delete(key) + return data || null + }) + .catch(err => { + inflightMap.delete(key) + // cacheLog('ERROR', key, err.message) + throw err + }) + + inflightMap.set(key, promise) + return promise } -/** - * 为减少频繁接口请求,notion数据将被缓存 - * @param {*} key - * @returns - */ -export async function getDataFromCache(key, force) { - if (JSON.parse(BLOG.ENABLE_CACHE) || force) { - const dataFromCache = await getApi().getCache(key) - if (!dataFromCache || JSON.stringify(dataFromCache) === '[]') { - return null +export async function setDataToCache(key, data, customCacheTime) { + if (!data) return + + const chain = getCacheChain() + + for (const { name, api } of chain) { + try { + await api.setCache(key, data, customCacheTime) + // cacheLog('SET', key, `to:${name}`) + + cacheStats.set++ + cacheStats.perStore[name] = cacheStats.perStore[name] || { hit: 0, set: 0 } + cacheStats.perStore[name].set++ + + return + } catch (e) { + console.warn(`[Cache] ${name} set failed key:${key}`, e.message) + cacheStats.error++ + } - // console.trace('[API-->>缓存]:', key, dataFromCache) - return dataFromCache - } else { - return null } + + console.warn(`[Cache] ALL set failed key:${key}`) } -/** - * 写入缓存 - * @param {*} key - * @param {*} data - * @param {*} customCacheTime - * @returns - */ -export async function setDataToCache(key, data, customCacheTime) { - if (!enableCacheInVercel || !data) { - return +export async function getDataFromCache(key, force) { + if (!JSON.parse(BLOG.ENABLE_CACHE) && !force) return null + + const chain = getCacheChain() + + for (const { name, api } of chain) { + try { + const data = await api.getCache(key) + + if (data && JSON.stringify(data) !== '[]') { + // cacheLog('HIT', key, `from:${name}`) + cacheStats.hit++ + cacheStats.perStore[name] = cacheStats.perStore[name] || { hit: 0, set: 0 } + cacheStats.perStore[name].hit++ + return data + } + } catch (e) { + cacheStats.error++ + console.warn(`[Cache] ${name} get failed key:${key}`, e.message) + } } - // console.trace('[API-->>缓存写入]:', key) - await getApi().setCache(key, data, customCacheTime) +cacheStats.miss++ + return null } export async function delCacheData(key) { - if (!JSON.parse(BLOG.ENABLE_CACHE)) { - return + const chain = getCacheChain() + + for (const { name, api } of chain) { + try { + await api.delCache(key) + } catch (e) { + console.warn(`[Cache] ${name} del failed key:${key}`, e.message) + } } - await getApi().delCache(key) } -/** - * 缓存实现类 - * @returns - */ +function getCacheType() { + if (hasRedis) return 'redis' + if (isVercelEnv()) return 'vercel' + if (isBuildPhase) return 'file' + return 'memory' +} + export function getApi() { - if (BLOG.REDIS_URL) { - return RedisCache - } else if (process.env.ENABLE_FILE_CACHE) { - return FileCache - } else { - return MemoryCache + const type = getCacheType() + + switch (type) { + case 'redis': + return RedisCache + case 'vercel': + return VercelCache + case 'file': + return FileCache + default: + return MemoryCache } } + +function getCacheChain() { + const chain = [] + + if (hasRedis) { + chain.push({ name: 'redis', api: RedisCache }) + } + + if (isVercelEnv()) { + chain.push({ name: 'vercel', api: VercelCache }) + } + + if (isBuildPhase || !BLOG.isProd) { + chain.push({ name: 'file', api: FileCache }) + } + + // 永远兜底 + chain.push({ name: 'memory', api: MemoryCache }) + + return chain +} + +function printCacheSummary() { + const hitRate = cacheStats.total + ? ((cacheStats.hit / cacheStats.total) * 100).toFixed(1) + : 0 + + console.log('\n[Cache Summary]') + console.log('Strategy:', getCacheChain().map(c => c.name).join(' → ')) + console.log( + `Stats: HIT ${hitRate}% | MISS ${cacheStats.miss} | ERROR ${cacheStats.error} | total ${cacheStats.total}` + ) + + console.log('[Per Store]') + Object.entries(cacheStats.perStore).forEach(([name, stat]) => { + console.log(` ${name}: hit=${stat.hit || 0}, set=${stat.set || 0}`) + }) + + console.log('----------------------------------\n') +} + +// Node 进程结束时触发 +if (typeof process !== 'undefined') { + process.on('exit', printCacheSummary) +} \ No newline at end of file diff --git a/lib/cache/vercel_cache.js b/lib/cache/vercel_cache.js new file mode 100644 index 00000000000..15e97195230 --- /dev/null +++ b/lib/cache/vercel_cache.js @@ -0,0 +1,25 @@ +import { getCache } from '@vercel/functions' + +const cache = getCache() + +const VercelCache = { + async getCache(key) { + const data = await cache.get(key) + return data || null + }, + + async setCache(key, data, ttl = 3600) { + await cache.set(key, data, { + ttl, + tags: ['notion'] + }) + }, + + async delCache(key) { + // ⚠️ vercel runtime cache 不支持直接删除 + // 可以用 tag 失效(可扩展) + console.warn('[VercelCache] delete not supported, use tag invalidation') + } +} + +export default VercelCache \ No newline at end of file diff --git a/lib/db/SiteDataApi.js b/lib/db/SiteDataApi.js index 7f6a990a8c0..6fe539d6751 100644 --- a/lib/db/SiteDataApi.js +++ b/lib/db/SiteDataApi.js @@ -1,5 +1,5 @@ import BLOG from '@/blog.config' -import { getOrSetDataWithCache } from '../cache/cache_manager' +import { getDataFromCache, getOrSetDataWithCache } from '../cache/cache_manager' import { getAllCategories } from '@/lib/db/notion/getAllCategories' import getAllPageIds from '@/lib/db/notion/getAllPageIds' import { getAllTags } from '@/lib/db/notion/getAllTags' @@ -7,44 +7,44 @@ import { getConfigMapFromConfigPage } from '@/lib/db/notion/getNotionConfig' import getPageProperties, { adjustPageProperties } from '@/lib/db/notion/getPageProperties' -import { fetchInBatches, fetchNotionPageBlocks, formatNotionBlock } from '@/lib/db/notion/getPostBlocks' +import { + fetchInBatches, + fetchNotionPageBlocks, + formatNotionBlock +} from '@/lib/db/notion/getPostBlocks' import { compressImage, mapImgUrl } from '@/lib/db/notion/mapImage' import { deepClone } from '@/lib/utils' import { idToUuid } from 'notion-utils' import { siteConfig } from '../config' import { extractLangId, extractLangPrefix, getShortId } from '../utils/pageId' -import { normalizeNotionMetadata, normalizeCollection, normalizeSchema, normalizePageBlock } from './notion/normalizeUtil' - +import { + normalizeNotionMetadata, + normalizeCollection, + normalizeSchema, + normalizePageBlock +} from './notion/normalizeUtil' import { fetchPageFromNotion } from './notion/getNotionPost' import { processPostData } from '../utils/post' import { adapterNotionBlockMap } from '../utils/notion.util' +import pLimit from 'p-limit' export { getAllTags } from './notion/getAllTags' export { fetchPageFromNotion as getPost } from './notion/getNotionPost' export { fetchNotionPageBlocks as getPostBlocks } from './notion/getPostBlocks' +// ─── 防击穿:同一个 key 只发一次请求,其余等待 ──────────────────────────────── +const inflightMap = new Map() + /** - * 获取全站数据; 基于Notion实现 - * TODO 计划这个文件改成类似Restful的接口形式; - * 按照站点数据封装,从而进一步提升兼容性和可维护性 - * @see /lib/site/site.api.ts - * /site-info - * /posts?tag=xxx&category=yyy&page=1&limit=10 - * /posts/:id - * /categories - * /tags - * @param {*} pageId - * @param {*} from - * @param {*} locale 语言 zh|en|jp 等等 - * @returns - * + * 获取全站数据;基于 Notion 实现 + * 支持多站点(pageId 逗号分隔)和多语言(locale 前缀) */ export async function fetchGlobalAllData({ pageId = BLOG.NOTION_PAGE_ID, from, locale }) { - // 获取站点数据 , 如果pageId有逗号隔开则分次取数据 + const siteIds = pageId?.split(',') || [] let data = EmptyData(pageId) @@ -57,57 +57,56 @@ export async function fetchGlobalAllData({ const siteId = siteIds[index] const id = extractLangId(siteId) const prefix = extractLangPrefix(siteId) - // 第一个id站点默认语言 if (index === 0 || locale === prefix) { - data = await getSiteDataByPageId({ - pageId: id, - from - }) + data = await getSiteDataByPageId({ pageId: id, from }) } } } catch (error) { console.error('异常', error) } - // 返回给客户端前的清理操作 return handleDataBeforeReturn(deepClone(data)) } /** - * 获取指定notion的collection数据 - * @param pageId - * @param from 请求来源 - * @returns {Promise} + * 获取指定 Notion collection 数据 + * 带防击穿缓存:同一 pageId 并发时只发一次 API 请求 */ export async function getSiteDataByPageId({ pageId, from }) { - // 获取NOTION原始数据,此接支持mem缓存。 - const originalPageRecordMap = await getOrSetDataWithCache( - `site_data_${pageId}`, + const cacheKey = `site_data_${pageId}` + + // 如果已有相同 key 的请求在飞行中,直接等它的结果 + if (inflightMap.has(cacheKey)) { + const raw = await inflightMap.get(cacheKey) + return convertNotionToSiteData(pageId, from, deepClone(raw)) + } + + const promise = getOrSetDataWithCache( + cacheKey, async (pageId, from) => { - const pageRecordMap = await fetchNotionPageBlocks(pageId, from) - return pageRecordMap + return fetchNotionPageBlocks(pageId, from) }, pageId, from ) - // 获取的数据格式与站点不同 + inflightMap.set(cacheKey, promise) + promise.finally(() => inflightMap.delete(cacheKey)) + + const originalPageRecordMap = await promise return convertNotionToSiteData(pageId, from, deepClone(originalPageRecordMap)) } /** - * 获取公告 + * 获取公告 block + * 拉取后必须经过 adapter + format,否则新格式双层嵌套导致 type undefined */ async function getNotice(post) { if (!post) return null try { const rawBlockMap = await fetchNotionPageBlocks(post.id, 'data-notice') - - // ✅ 必须经过 adapter 拍平结构,否则新格式的双层嵌套会导致 type undefined const adapted = adapterNotionBlockMap(rawBlockMap) - - // ✅ 再清理 crdt_data 等 react-notion-x 不认识的字段 post.blockMap = { ...adapted, block: formatNotionBlock(adapted.block) @@ -120,73 +119,54 @@ async function getNotice(post) { return post } - /** - * 空的默认数据 - * @param {*} pageId - * @returns + * 空的默认数据(Notion 拉取失败时的兜底) */ -const EmptyData = pageId => { - const empty = { - notice: null, - siteInfo: getSiteInfo({}), - allPages: [ - { - id: 1, - title: `无法获取Notion数据,请检查Notion_ID: \n 当前 ${pageId}`, - summary: - '访问文档获取帮助 → https://docs.tangly1024.com/article/vercel-deploy-notion-next', - status: 'Published', - type: 'Post', - slug: 'oops', - publishDay: '2024-11-13', - pageCoverThumbnail: BLOG.HOME_BANNER_IMAGE || '/bg_image.jpg', - date: { - start_date: '2023-04-24', - lastEditedDay: '2023-04-24', - tagItems: [] - } +const EmptyData = pageId => ({ + notice: null, + siteInfo: getSiteInfo({}), + allPages: [ + { + id: 1, + title: `无法获取Notion数据,请检查Notion_ID: \n 当前 ${pageId}`, + summary: + '访问文档获取帮助 → https://docs.tangly1024.com/article/vercel-deploy-notion-next', + status: 'Published', + type: 'Post', + slug: 'oops', + publishDay: '2024-11-13', + pageCoverThumbnail: BLOG.HOME_BANNER_IMAGE || '/bg_image.jpg', + date: { + start_date: '2023-04-24', + lastEditedDay: '2023-04-24', + tagItems: [] } - ], - allNavPages: [], - collection: [], - collectionQuery: {}, - collectionId: null, - collectionView: {}, - viewIds: [], - block: {}, - schema: {}, - tagOptions: [], - categoryOptions: [], - rawMetadata: {}, - customNav: [], - customMenu: [], - postCount: 1, - pageIds: [], - latestPosts: [] - } - return empty -} + } + ], + allNavPages: [], + collection: [], + collectionQuery: {}, + collectionId: null, + collectionView: {}, + viewIds: [], + block: {}, + schema: {}, + tagOptions: [], + categoryOptions: [], + rawMetadata: {}, + customNav: [], + customMenu: [], + postCount: 1, + pageIds: [], + latestPosts: [] +}) /** - * 在服务端解析 post 相关 props - * ✅ 兼容 prefix / slug / suffix 任意组合 - * ⚠️ 只能在 getStaticProps / getServerSideProps 使用 - */ -/** - * 生产级稳定版本 - * 严格精确匹配,不做模糊匹配 + * 在服务端解析单篇文章的 props + * 兼容 prefix / slug / suffix 任意组合 + * 只能在 getStaticProps / getServerSideProps 中使用 */ -export async function resolvePostProps({ - prefix, - slug, - suffix, - locale, - from, -}) { - /** - * 1️⃣ 统一路径 - */ +export async function resolvePostProps({ prefix, slug, suffix, locale, from }) { const segments = [] if (prefix) segments.push(prefix) if (slug) segments.push(slug) @@ -196,16 +176,11 @@ export async function resolvePostProps({ const lastSegment = segments[segments.length - 1] const source = from || `slug-props-${fullSlug}` - /** - * 2️⃣ 拉全局数据 - */ const props = await fetchGlobalAllData({ from: source, locale }) let post = null - /** - * 3️⃣ 一级匹配:完整 slug 精确匹配(核心) - */ + // 1. 完整 slug 精确匹配 if (fullSlug) { post = props?.allPages?.find(p => { if (!p || p?.type?.includes('Menu')) return false @@ -213,18 +188,12 @@ export async function resolvePostProps({ }) } - /** - * 4️⃣ 二级匹配:完整 UUID 精确匹配 - */ + // 2. UUID 精确匹配 if (!post && fullSlug) { - post = props?.allPages?.find(p => { - return p?.id === fullSlug - }) + post = props?.allPages?.find(p => p?.id === fullSlug) } - /** - * 5️⃣ 三级匹配:如果最后一段是 UUID,直接拉 Notion - */ + // 3. 最后一段是 UUID,直接拉 Notion if ( !post && typeof lastSegment === 'string' && @@ -237,28 +206,20 @@ export async function resolvePostProps({ } } - /** - * 6️⃣ 如果拿到了 post,但没有 blockMap,则拉 block - */ + // 4. 拿到 post 但没有 blockMap,补拉 block if (post?.id && !post?.blockMap) { try { const rawBlockMap = await fetchNotionPageBlocks(post.id, source) - - post.blockMap = adapterNotionBlockMap(rawBlockMap) - + const adapted = adapterNotionBlockMap(rawBlockMap) post.blockMap = { - ...post.blockMap, - block: formatNotionBlock(post.blockMap.block) + ...adapted, + block: formatNotionBlock(adapted.block) } - } catch (e) { console.warn('[resolvePostProps] fetchNotionPageBlocks failed:', post.id, e) } } - /** - * 7️⃣ 后处理 - */ if (post) { props.post = post try { @@ -271,27 +232,48 @@ export async function resolvePostProps({ } delete props.allPages - return props } - /** - * 将Notion数据转站点数据 - * 这里统一对数据格式化 - * @returns {Promise} + * 将 Notion 原始数据转换为站点数据 + * + * 排序策略(优先级从高到低): + * 1. NOTION_CONFIG.POSTS_SORT_BY === 'date' → 按 publishDate 降序 + * 2. NOTION_CONFIG.POSTS_SORT_BY === 'notion' → 保留 page_sort 原始顺序(默认) + * 3. 其他值 / 未配置 → 同 'notion',保留原始顺序 + * + * page_sort 的顺序来自 Notion 数据库第一个视图(NOTION_INDEX)的手动排序, + * 与 Notion 界面中拖拽调整的顺序一致。 + * 如需按视图的"排序规则"(如按字段升降序),请在 Notion 界面中改用手动排序, + * 或在此处扩展 applySortRule() 函数。 */ -async function convertNotionToSiteData(SITE_DATABASE_PAGE_ID, from, pageRecordMap) { +async function convertNotionToSiteData( + SITE_DATABASE_PAGE_ID, + from, + pageRecordMap +) { + + + const traceId = `${Date.now()}-${Math.random().toString(36).slice(2, 8)}` + + console.log(`\n================= [TRACE START] ${traceId} =================`) + console.log('[convert] pageId:', SITE_DATABASE_PAGE_ID) + console.log('[convert] from:', from) + + if (!pageRecordMap) { console.error('can`t get Notion Data ; Which id is: ', SITE_DATABASE_PAGE_ID) return {} } + SITE_DATABASE_PAGE_ID = idToUuid(SITE_DATABASE_PAGE_ID) - let block = pageRecordMap.block || {} + + // ── 原始 block,先做格式统一(兼容新旧双层嵌套)────────────────────────── + let block = adapterNotionBlockMap({ block: pageRecordMap.block || {} }).block + const rawMetadata = normalizeNotionMetadata(block, SITE_DATABASE_PAGE_ID) - // spaceId 提取备用 - const spaceId = rawMetadata?.space_id || null - // Check Type Page-Database和Inline-Database + if ( rawMetadata?.type !== 'collection_view_page' && rawMetadata?.type !== 'collection_view' @@ -300,7 +282,6 @@ async function convertNotionToSiteData(SITE_DATABASE_PAGE_ID, from, pageRecordMa return EmptyData(SITE_DATABASE_PAGE_ID) } - // 解析读取根数据库信息 const collectionId = rawMetadata?.collection_id const rawCollection = pageRecordMap.collection?.[collectionId] || @@ -314,10 +295,7 @@ async function convertNotionToSiteData(SITE_DATABASE_PAGE_ID, from, pageRecordMa const viewIds = rawMetadata?.view_ids const collectionData = [] - // ✅ 新增:先对原始 block 做格式统一,避免 normalizePageBlock 识别失败 - block = adapterNotionBlockMap({ block }).block - - + // ── 获取 pageIds(策略1 page_sort + 策略2 collectionQuery 补齐截断)──────── const pageIds = getAllPageIds( collectionQuery, collectionId, @@ -326,6 +304,10 @@ async function convertNotionToSiteData(SITE_DATABASE_PAGE_ID, from, pageRecordMa block ) + + + console.log(`[${traceId}] pageIds count:`, pageIds.length) + if (pageIds?.length === 0) { console.error( '获取到的文章列表为空,请检查notion模板', @@ -335,104 +317,89 @@ async function convertNotionToSiteData(SITE_DATABASE_PAGE_ID, from, pageRecordMa viewIds, pageRecordMap ) - } else { - // console.log('有效Page数量', pageIds?.length) } - // 1️⃣ 找出需要 fetch 的 block - const blockIdsNeedFetch = [] - for (let i = 0; i < pageIds.length; i++) { - const id = pageIds[i] - const pageBlock = normalizePageBlock(block[id]) + // ── 1. 找出需要补拉的 block ─────────────────────────────────────────────── + const blockIdsNeedFetch = pageIds.filter(id => !normalizePageBlock(block[id])) - if (!pageBlock) { - blockIdsNeedFetch.push(id) - } - } + const limit = pLimit(5) + const idsNeedFetch = ( + await Promise.all( + blockIdsNeedFetch.map(id => + limit(async () => { + const cache = await getDataFromCache(`page_block_${id}`) + return cache ? null : id + }) + ) + ) + ).filter(Boolean) - // 2️⃣ fetch 缺失的 blocks - const fetchedBlocks = await fetchInBatches(blockIdsNeedFetch) - // ✅ fetch 回来的也要 adapter - const adaptedFetchedBlocks = adapterNotionBlockMap({ block: fetchedBlocks }).block - block = Object.assign({}, block, adaptedFetchedBlocks) - // 3️⃣ 只执行一次:生成 collectionData - for (let i = 0; i < pageIds.length; i++) { - const id = pageIds[i] + // ── 2. 批量补拉,拉回来也要 adapter ────────────────────────────────────── + if (blockIdsNeedFetch.length > 0) { + // 批量拉取,注意控制并发和间隔,避免对 Notion API 造成压力 + const fetchedBlocks = await fetchInBatches(idsNeedFetch) + const adaptedFetchedBlocks = adapterNotionBlockMap({ + block: fetchedBlocks + }).block + block = { ...block, ...adaptedFetchedBlocks } + } - const rawBlock = block[id] - const pageBlock = normalizePageBlock(rawBlock) + // console.log(`[${traceId}] initial block count:`, Object.keys(block || {}).length) - if (!pageBlock) { - // console.warn('⚠️ 无法解析 page block:', id, rawBlock) - continue - } + // ── 3. 生成 collectionData,严格按 pageIds 顺序 ─────────────────────────── + for (const id of pageIds) { + const pageBlock = normalizePageBlock(block[id]) + + if (!pageBlock) continue const properties = - (await getPageProperties( - id, - pageBlock, - schema, - null, - getTagOptions(schema) - )) || null + (await getPageProperties(id, pageBlock, schema, null, getTagOptions(schema))) || + null if (properties) { collectionData.push(properties) } } - // 站点配置优先读取配置表格,否则读取blog.config.js 文件 + // ── 站点配置 ────────────────────────────────────────────────────────────── const NOTION_CONFIG = (await getConfigMapFromConfigPage(collectionData)) || {} - // 处理每一条数据的字段 - collectionData.forEach(function (element) { - adjustPageProperties(element, NOTION_CONFIG) - }) + collectionData.forEach(element => adjustPageProperties(element, NOTION_CONFIG)) - // 站点基础信息 const siteInfo = getSiteInfo({ collection, block, NOTION_CONFIG }) - // 文章计数 + // ── 筛选有效页面 ────────────────────────────────────────────────────────── let postCount = 0 - - // 查找所有的Post和Page const allPages = collectionData.filter(post => { - if (post?.type === 'Post' && post.status === 'Published') { - postCount++ - } - + if (post?.type === 'Post' && post.status === 'Published') postCount++ return ( - post && post?.slug && - // !post?.slug?.startsWith('http') && (post?.status === 'Invisible' || post?.status === 'Published') ) }) - // Sort by date - if (siteConfig('POSTS_SORT_BY', null, NOTION_CONFIG) === 'date') { - allPages.sort((a, b) => { - return b?.publishDate - a?.publishDate - }) + // ── 排序 ────────────────────────────────────────────────────────────────── + // allPages 此时已按 pageIds(page_sort)顺序排列,即 Notion 视图手动顺序 + // 仅当明确配置为 'date' 时才覆盖为日期排序 + const sortBy = siteConfig('POSTS_SORT_BY', null, NOTION_CONFIG) + if (sortBy === 'date') { + allPages.sort((a, b) => (b?.publishDate ?? 0) - (a?.publishDate ?? 0)) } + // sortBy === 'notion' 或未配置:保持 page_sort 原始顺序,不做任何处理 + // ── 其余数据 ────────────────────────────────────────────────────────────── const notice = await getNotice( - collectionData.filter(post => { - return ( - post && - post?.type && - post?.type === 'Notice' && - post.status === 'Published' - ) - })?.[0] + collectionData.find( + post => post?.type === 'Notice' && post.status === 'Published' + ) ) - // 所有分类 + const categoryOptions = getAllCategories({ allPages, categoryOptions: getCategoryOptions(schema) }) - // 所有标签 + const tagSchemaOptions = getTagOptions(schema) const tagOptions = getAllTags({ @@ -440,13 +407,13 @@ async function convertNotionToSiteData(SITE_DATABASE_PAGE_ID, from, pageRecordMa tagOptions: tagSchemaOptions ?? [], NOTION_CONFIG }) ?? null - // 旧的菜单 + const customNav = getCustomNav({ allPages: collectionData.filter( post => post?.type === 'Page' && post.status === 'Published' ) }) - // 新的菜单 + const customMenu = getCustomMenu({ collectionData, NOTION_CONFIG }) const latestPosts = getLatestPosts({ allPages, from, latestPostCount: 6 }) const allNavPages = getNavPages({ allPages }) @@ -476,14 +443,10 @@ async function convertNotionToSiteData(SITE_DATABASE_PAGE_ID, from, pageRecordMa } /** - * 返回给浏览器前端的数据处理 - * 适当脱敏 - * 减少体积 - * 其它处理 - * @param {*} db + * 返回给浏览器前端前的数据清理 + * 脱敏、减体积、定时发布处理 */ function handleDataBeforeReturn(db) { - // 清理多余数据 delete db.block delete db.schema delete db.rawMetadata @@ -494,65 +457,30 @@ function handleDataBeforeReturn(db) { delete db.collectionId delete db.collectionView - // 清理多余的块 if (db?.notice) { db.notice = cleanBlock(db?.notice) delete db.notice?.id } + db.categoryOptions = cleanIds(db?.categoryOptions) db.customMenu = cleanIds(db?.customMenu) - - // db.latestPosts = shortenIds(db?.latestPosts) db.allNavPages = shortenIds(db?.allNavPages) - // db.allPages = cleanBlocks(db?.allPages) db.allNavPages = cleanPages(db?.allNavPages, db.tagOptions) - db.allPages = stripHeavyData(cleanPages(db.allPages, db.tagOptions)) - db.latestPosts = stripHeavyData(cleanPages(db.latestPosts, db.tagOptions)) - // 必须在使用完毕后才能进行清理 + db.allPages = cleanPages(db.allPages, db.tagOptions) + db.latestPosts = cleanPages(db.latestPosts, db.tagOptions) db.tagOptions = cleanTagOptions(db?.tagOptions) + // 定时发布:检查发布时间窗口,超出范围的隐藏 const POST_SCHEDULE_PUBLISH = siteConfig( 'POST_SCHEDULE_PUBLISH', null, db.NOTION_CONFIG ) if (POST_SCHEDULE_PUBLISH) { - // console.log('[定时发布] 开启检测') db.allPages?.forEach(p => { - // 新特性,判断文章的发布和下架时间,如果不在有效期内则进行下架处理 - const publish = isInRange(p.title, p.date) - if (!publish) { - const currentTimestamp = Date.now() - const startTimestamp = getTimestamp( - p.date.start_date, - p.date.start_time || '00:00', - p.date.time_zone - ) - const endTimestamp = getTimestamp( - p.date.end_date, - p.date.end_time || '23:59', - p.date.time_zone - ) - console.log( - '[定时发布] 隐藏--> 文章:', - p.title, - '当前时间戳:', - currentTimestamp, - '目标时间戳:', - startTimestamp, - '-', - endTimestamp - ) - console.log( - '[定时发布] 隐藏--> 文章:', - p.title, - '当前时间:', - new Date(), - '目标时间:', - p.date - ) - // 隐藏 + if (!isInRange(p.title, p.date)) { + console.log('[定时发布] 隐藏-->', p.title, p.date) p.status = 'Invisible' } }) @@ -561,81 +489,28 @@ function handleDataBeforeReturn(db) { return db } -/** - * 剔除无关数据,减少传输体积 - * @param {*} pages - * @returns - */ -function stripHeavyData(pages) { - if (!Array.isArray(pages)) return pages - return pages.map(item => { - return { - id: item.id || '', - title: item.title || '', - slug: item.slug || '', - summary: item.summary || '', - tags: item.tags || [], - category: item.category || '', - status: item.status || '', - type: item.type || '', - publishDate: item.publishDate || 0, - publishDay: item.publishDay || '', - lastEditedDate: item.lastEditedDate - ? new Date(item.lastEditedDate).getTime() - : 0, - lastEditedDay: item.lastEditedDay || '', - pageCover: item.pageCover || '', - pageCoverThumbnail: item.pageCoverThumbnail || '', - pageIcon: item.pageIcon || '', - date: item.date || {}, - href: item.href || '', - target: item.target || '', - password: item.password || '', - ext: item.ext || {} - } - }) -} +// ─── 工具函数 ───────────────────────────────────────────────────────────────── -/** - * 处理文章列表中的异常数据 - * @param {Array} allPages - 所有页面数据 - * @param {Array} tagOptions - 标签选项 - * @returns {Array} 处理后的 allPages - */ function cleanPages(allPages, tagOptions) { - // 校验参数是否为数组 if (!Array.isArray(allPages) || !Array.isArray(tagOptions)) { console.warn('Invalid input: allPages and tagOptions should be arrays.') - return allPages || [] // 返回空数组或原始值 + return allPages || [] } - - // 提取 tagOptions 中所有合法的标签名 const validTags = new Set( tagOptions .map(tag => (typeof tag.name === 'string' ? tag.name : null)) - .filter(Boolean) // 只保留合法的字符串 + .filter(Boolean) ) - - // 遍历所有的 pages allPages.forEach(page => { - // 确保 tagItems 是数组 if (Array.isArray(page.tagItems)) { - // 对每个 page 的 tagItems 进行过滤 page.tagItems = page.tagItems.filter( - tagItem => - validTags.has(tagItem?.name) && typeof tagItem.name === 'string' // 校验 tagItem.name 是否是字符串 + tagItem => validTags.has(tagItem?.name) && typeof tagItem.name === 'string' ) } }) - return allPages } -/** - * 清理一组数据的id - * @param {*} items - * @returns - */ function shortenIds(items) { if (items && Array.isArray(items)) { return deepClone( @@ -649,11 +524,6 @@ function shortenIds(items) { return items } -/** - * 清理一组数据的id - * @param {*} items - * @returns - */ function cleanIds(items) { if (items && Array.isArray(items)) { return deepClone( @@ -666,31 +536,20 @@ function cleanIds(items) { return items } -/** - * 清理和过滤tagOptions - * @param {*} tagOptions - * @returns - */ function cleanTagOptions(tagOptions) { if (tagOptions && Array.isArray(tagOptions)) { return deepClone( tagOptions .filter(tagOption => tagOption.source === 'Published') - .map(({ id, source, ...newTagOption }) => newTagOption) + .map(({ id, source, ...rest }) => rest) ) } return tagOptions } -/** - * 清理block数据 - */ function cleanBlock(item) { const post = deepClone(item) const pageBlock = post?.blockMap?.block - // delete post?.id - // delete post?.blockMap?.collection - if (pageBlock) { for (const i in pageBlock) { pageBlock[i] = cleanBlock(pageBlock[i]) @@ -701,16 +560,11 @@ function cleanBlock(item) { delete pageBlock[i]?.value?.last_edited_by_table delete pageBlock[i]?.value?.last_edited_by_id delete pageBlock[i]?.value?.space_id - delete pageBlock[i]?.value?.version delete pageBlock[i]?.value?.format?.copied_from_pointer delete pageBlock[i]?.value?.format?.block_locked_by delete pageBlock[i]?.value?.parent_table delete pageBlock[i]?.value?.copied_from_pointer delete pageBlock[i]?.value?.copied_from - delete pageBlock[i]?.value?.created_by_table - delete pageBlock[i]?.value?.created_by_id - delete pageBlock[i]?.value?.last_edited_by_table - delete pageBlock[i]?.value?.last_edited_by_id delete pageBlock[i]?.value?.permissions delete pageBlock[i]?.value?.alive } @@ -719,29 +573,22 @@ function cleanBlock(item) { } /** - * 获取最新文章 根据最后修改时间倒序排列 - * @param {*}} param0 - * @returns + * 获取最新文章,按最后修改时间倒序 + * 修复:原代码用 Object.create(allPosts) 不是真正的数组副本,改为展开运算符 */ function getLatestPosts({ allPages, from, latestPostCount }) { const allPosts = allPages?.filter( page => page.type === 'Post' && page.status === 'Published' ) - - const latestPosts = Object.create(allPosts).sort((a, b) => { - const dateA = new Date(a?.lastEditedDate || a?.publishDate) - const dateB = new Date(b?.lastEditedDate || b?.publishDate) - return dateB - dateA - }) - return latestPosts.slice(0, latestPostCount) + return [...(allPosts ?? [])] + .sort((a, b) => { + const dateA = new Date(a?.lastEditedDate || a?.publishDate) + const dateB = new Date(b?.lastEditedDate || b?.publishDate) + return dateB - dateA + }) + .slice(0, latestPostCount) } -/** - * 获取用户自定义单页菜单 - * 旧版本,不读取Menu菜单,而是读取type=Page生成菜单 - * @param notionPageData - * @returns {Promise<[]|*[]>} - */ function getCustomNav({ allPages }) { const customNav = [] if (allPages && allPages.length > 0) { @@ -759,11 +606,6 @@ function getCustomNav({ allPages }) { return customNav } -/** - * 获取自定义菜单 - * @param {*} allPages - * @returns - */ function getCustomMenu({ collectionData, NOTION_CONFIG }) { const menuPages = collectionData.filter( post => @@ -791,11 +633,6 @@ function getCustomMenu({ collectionData, NOTION_CONFIG }) { return menus } -/** - * 获取标签选项 - * @param schema - * @returns {undefined} - */ function getTagOptions(schema) { if (!schema) return {} const tagSchema = Object.values(schema).find( @@ -804,11 +641,6 @@ function getTagOptions(schema) { return tagSchema?.options || [] } -/** - * 获取分类选项 - * @param schema - * @returns {{}|*|*[]} - */ function getCategoryOptions(schema) { if (!schema) return {} const categorySchema = Object.values(schema).find( @@ -817,12 +649,6 @@ function getCategoryOptions(schema) { return categorySchema?.options || [] } -/** - * 站点信息 - * @param notionPageData - * @param from - * @returns {Promise<{title,description,pageCover,icon}>} - */ function getSiteInfo({ collection, block, NOTION_CONFIG }) { const defaultTitle = NOTION_CONFIG?.TITLE || 'NotionNext BLOG' const defaultDescription = @@ -830,7 +656,7 @@ function getSiteInfo({ collection, block, NOTION_CONFIG }) { const defaultPageCover = NOTION_CONFIG?.HOME_BANNER_IMAGE || '/bg_image.jpg' const defaultIcon = NOTION_CONFIG?.AVATAR || '/avatar.svg' const defaultLink = NOTION_CONFIG?.LINK || BLOG.LINK - // 空数据的情况返回默认值 + if (!collection && !block) { return { title: defaultTitle, @@ -845,39 +671,22 @@ function getSiteInfo({ collection, block, NOTION_CONFIG }) { const description = collection?.description ? Object.assign(collection).description[0][0] : defaultDescription - const pageCover = collection?.cover ? mapImgUrl(collection?.cover, collection, 'collection') : defaultPageCover - // 用户头像压缩一下 let icon = compressImage( collection?.icon ? mapImgUrl(collection?.icon, collection, 'collection') : defaultIcon ) - // 站点网址 const link = NOTION_CONFIG?.LINK || defaultLink - - // 站点图标不能是emoji const emojiPattern = /\uD83C[\uDF00-\uDFFF]|\uD83D[\uDC00-\uDE4F]/g - if (!icon || emojiPattern.test(icon)) { - icon = defaultIcon - } + if (!icon || emojiPattern.test(icon)) icon = defaultIcon + return { title, description, pageCover, icon, link } } -/** - * 判断文章是否在发布时间内 - * @param {string} title - 文章标题 - * @param {Object} date - 时间范围参数 - * @param {string} date.start_date - 开始日期(格式:YYYY-MM-DD) - * @param {string} date.start_time - 开始时间(可选,格式:HH:mm) - * @param {string} date.end_date - 结束日期(格式:YYYY-MM-DD) - * @param {string} date.end_time - 结束时间(可选,格式:HH:mm) - * @param {string} date.time_zone - 时区(IANA格式,如 "Asia/Shanghai") - * @returns {boolean} 是否在范围内 - */ function isInRange(title, date = {}) { const { start_date, @@ -887,146 +696,68 @@ function isInRange(title, date = {}) { time_zone = 'Asia/Shanghai' } = date - // 获取当前时间的时间戳(基于目标时区) const currentTimestamp = Date.now() - - // 获取开始和结束时间的时间戳 const startTimestamp = getTimestamp(start_date, start_time, time_zone) const endTimestamp = getTimestamp(end_date, end_time, time_zone) - // 判断是否在范围内 - if (startTimestamp && currentTimestamp < startTimestamp) { - return false - } - - if (endTimestamp && currentTimestamp > endTimestamp) { - return false - } - + if (startTimestamp && currentTimestamp < startTimestamp) return false + if (endTimestamp && currentTimestamp > endTimestamp) return false return true } -/** - * 将指定时区的日期字符串转换为 UTC 时间 - * @param {string} dateStr - 日期字符串,格式为 YYYY-MM-DD HH:mm:ss - * @param {string} timeZone - 时区名称(如 "Asia/Shanghai") - * @returns {Date} - 转换后的 Date 对象(UTC 时间) - */ function convertToUTC(dateStr, timeZone = 'Asia/Shanghai') { - // 维护一个时区偏移映射(以小时为单位) const timeZoneOffsets = { - // UTC 基础 - UTC: 0, - 'Etc/GMT': 0, - 'Etc/GMT+0': 0, - - // 亚洲地区 - 'Asia/Shanghai': 8, // 中国 - 'Asia/Taipei': 8, // 台湾 - 'Asia/Tokyo': 9, // 日本 - 'Asia/Seoul': 9, // 韩国 - 'Asia/Kolkata': 5.5, // 印度 - 'Asia/Jakarta': 7, // 印尼 - 'Asia/Singapore': 8, // 新加坡 - 'Asia/Hong_Kong': 8, // 香港 - 'Asia/Bangkok': 7, // 泰国 - 'Asia/Dubai': 4, // 阿联酋 - 'Asia/Tehran': 3.5, // 伊朗 - 'Asia/Riyadh': 3, // 沙特阿拉伯 - - // 欧洲地区 - 'Europe/London': 0, // 英国(GMT) - 'Europe/Paris': 1, // 法国(CET) - 'Europe/Berlin': 1, // 德国 - 'Europe/Moscow': 3, // 俄罗斯 - 'Europe/Amsterdam': 1, // 荷兰 - - // 美洲地区 - 'America/New_York': -5, // 美国东部(EST) - 'America/Chicago': -6, // 美国中部(CST) - 'America/Denver': -7, // 美国山区时间(MST) - 'America/Los_Angeles': -8, // 美国西部(PST) - 'America/Sao_Paulo': -3, // 巴西 - 'America/Argentina/Buenos_Aires': -3, // 阿根廷 - - // 非洲地区 - 'Africa/Johannesburg': 2, // 南非 - 'Africa/Cairo': 2, // 埃及 - 'Africa/Nairobi': 3, // 肯尼亚 - - // 大洋洲地区 - 'Australia/Sydney': 10, // 澳大利亚东部 - 'Australia/Perth': 8, // 澳大利亚西部 - 'Pacific/Auckland': 13, // 新西兰 - 'Pacific/Fiji': 12, // 斐济 - - // 北极与南极 - 'Antarctica/Palmer': -3, // 南极洲帕尔默 - 'Antarctica/McMurdo': 13 // 南极洲麦克默多 + UTC: 0, 'Etc/GMT': 0, 'Etc/GMT+0': 0, + 'Asia/Shanghai': 8, 'Asia/Taipei': 8, 'Asia/Tokyo': 9, 'Asia/Seoul': 9, + 'Asia/Kolkata': 5.5, 'Asia/Jakarta': 7, 'Asia/Singapore': 8, + 'Asia/Hong_Kong': 8, 'Asia/Bangkok': 7, 'Asia/Dubai': 4, + 'Asia/Tehran': 3.5, 'Asia/Riyadh': 3, + 'Europe/London': 0, 'Europe/Paris': 1, 'Europe/Berlin': 1, + 'Europe/Moscow': 3, 'Europe/Amsterdam': 1, + 'America/New_York': -5, 'America/Chicago': -6, 'America/Denver': -7, + 'America/Los_Angeles': -8, 'America/Sao_Paulo': -3, + 'America/Argentina/Buenos_Aires': -3, + 'Africa/Johannesburg': 2, 'Africa/Cairo': 2, 'Africa/Nairobi': 3, + 'Australia/Sydney': 10, 'Australia/Perth': 8, + 'Pacific/Auckland': 13, 'Pacific/Fiji': 12, + 'Antarctica/Palmer': -3, 'Antarctica/McMurdo': 13 } - - // 预设每个大洲的默认时区 const continentDefaults = { - Asia: 'Asia/Shanghai', - Europe: 'Europe/London', - America: 'America/New_York', - Africa: 'Africa/Cairo', - Australia: 'Australia/Sydney', - Pacific: 'Pacific/Auckland', - Antarctica: 'Antarctica/Palmer', - UTC: 'UTC' + Asia: 'Asia/Shanghai', Europe: 'Europe/London', America: 'America/New_York', + Africa: 'Africa/Cairo', Australia: 'Australia/Sydney', + Pacific: 'Pacific/Auckland', Antarctica: 'Antarctica/Palmer', UTC: 'UTC' } - // 获取目标时区的偏移量(以小时为单位) let offsetHours = timeZoneOffsets[timeZone] - - // 未被支持的时区采用兼容 if (offsetHours === undefined) { - // 获取时区所属大洲("Continent/City" -> "Continent") const continent = timeZone.split('/')[0] - - // 选择该大洲的默认时区 const fallbackZone = continentDefaults[continent] || 'UTC' offsetHours = timeZoneOffsets[fallbackZone] - console.warn( - `Warning: Unsupported time zone "${timeZone}". Using default "${fallbackZone}" for continent "${continent}".` + `Warning: Unsupported time zone "${timeZone}". Using default "${fallbackZone}".` ) } - // 将日期字符串转换为本地时间的 Date 对象 const localDate = new Date(`${dateStr.replace(' ', 'T')}Z`) if (isNaN(localDate.getTime())) { throw new Error(`Invalid date string: ${dateStr}`) } - - // 计算 UTC 时间的时间戳 - const utcTimestamp = localDate.getTime() - offsetHours * 60 * 60 * 1000 - return new Date(utcTimestamp) + return new Date(localDate.getTime() - offsetHours * 3600 * 1000) } -// 辅助函数:生成指定日期时间的时间戳(基于目标时区) function getTimestamp(date, time = '00:00', time_zone) { if (!date) return null return convertToUTC(`${date} ${time}:00`, time_zone).getTime() } -/** - * 获取导航用的精减文章列表 - * gitbook主题用到,只保留文章的标题分类标签分类信息,精减掉摘要密码日期等数据 - * 导航页面的条件,必须是Posts - * @param {*} param0 - */ export function getNavPages({ allPages }) { - const allNavPages = allPages?.filter(post => { - return ( + const allNavPages = allPages?.filter( + post => post && post?.slug && post?.type === 'Post' && post?.status === 'Published' - ) - }) - + ) return allNavPages.map(item => ({ id: item.id, title: item.title || '', diff --git a/lib/db/notion/getAllPageIds.js b/lib/db/notion/getAllPageIds.js index fbd6b94d588..3c3358c3d66 100644 --- a/lib/db/notion/getAllPageIds.js +++ b/lib/db/notion/getAllPageIds.js @@ -3,33 +3,19 @@ import BLOG from "@/blog.config" export default function getAllPageIds(collectionQuery, collectionId, collectionView, viewIds, block = {}) { const pageSet = new Set() - // ── 策略1:从 collectionView[viewId].value.value.page_sort 取(新格式)── + // 策略1:page_sort(有顺序,但可能截断) if (collectionView && viewIds?.length > 0) { const groupIndex = BLOG.NOTION_INDEX || 0 const targetViewId = viewIds[groupIndex] const pageSort = collectionView?.[targetViewId]?.value?.value?.page_sort - if (Array.isArray(pageSort) && pageSort.length > 0) { pageSort.forEach(id => pageSet.add(id)) - // console.log('[getAllPageIds] 策略1命中 page_sort,数量:', pageSet.size) - } - } - - // ── 策略2:遍历所有 viewId 的 page_sort 兜底 ── - if (pageSet.size === 0 && collectionView) { - Object.values(collectionView).forEach(viewEntry => { - const pageSort = viewEntry?.value?.value?.page_sort - if (Array.isArray(pageSort)) { - pageSort.forEach(id => pageSet.add(id)) - } - }) - if (pageSet.size > 0) { - // console.log('[getAllPageIds] 策略2命中 page_sort(遍历),数量:', pageSet.size) } } - // ── 策略3:旧格式兼容,从 collectionQuery 取 ── - if (pageSet.size === 0 && collectionQuery && collectionId) { + // ✅ 策略补充:collectionQuery 始终运行,补齐 page_sort 截断的记录 + // 注意:补充的记录追加在末尾,不影响已有顺序 + if (collectionQuery && collectionId) { const viewQuery = collectionQuery?.[collectionId] if (viewQuery) { Object.values(viewQuery).forEach(viewData => { @@ -41,25 +27,16 @@ export default function getAllPageIds(collectionQuery, collectionId, collectionV if (Array.isArray(ids)) ids.forEach(id => pageSet.add(id)) }) }) - if (pageSet.size > 0) { - // console.log('[getAllPageIds] 策略3命中 collectionQuery(旧格式),数量:', pageSet.size) - } } } - if (pageSet.size === 0) { - // console.warn('[getAllPageIds] 所有策略均未命中,返回空数组') - return [] - } - - // ── 统一过滤:只保留有权限的 pageId ── + // 过滤无权限 // const accessibleIds = [...pageSet].filter(id => { // const entry = block[id] - // if (!entry) return true // block 里没有记录,保留交给后续 fetch 处理 - // return entry?.value?.role !== 'none' + // if (!entry) return true + // return entry?.value?.role !== 'none' && entry?.value?.value?.role !== 'none' // }) - // console.log(`[getAllPageIds] 过滤后可访问数量: ${accessibleIds.length}/${pageSet.size}`) - // return accessibleIds + // console.log(`[getAllPageIds] 最终数量: ${accessibleIds.length}`) return [...pageSet] } \ No newline at end of file diff --git a/lib/db/notion/getPostBlocks.js b/lib/db/notion/getPostBlocks.js index 42d42d5d3da..4d54bef703d 100644 --- a/lib/db/notion/getPostBlocks.js +++ b/lib/db/notion/getPostBlocks.js @@ -1,11 +1,19 @@ -import BLOG from '@/blog.config' import { getDataFromCache, getOrSetDataWithCache, - setDataToCache + setDataToCache, + } from '@/lib/cache/cache_manager' import { deepClone, delay } from '../../utils' import notionAPI from '@/lib/db/notion/getNotionAPI' +import pLimit from 'p-limit' + +// ⚠️ 全局限流器(非常关键) +// 建议 2~4,不要超过 5 +const limit = pLimit(3) + +// ⚠️ 每个请求之间的间隔(防 burst) +const REQUEST_INTERVAL = 200 // ms /** * 获取文章内容块 @@ -14,7 +22,7 @@ import notionAPI from '@/lib/db/notion/getNotionAPI' * @param {*} slice */ export async function fetchNotionPageBlocks(id, from = null, slice = 0) { - const cacheKey = `page_content_${id}` + const cacheKey = `page_block_${id}` // 1️⃣ 统一由缓存工具负责「读 / 写 / 兜底获取」 const pageBlock = await getOrSetDataWithCache( @@ -50,9 +58,9 @@ export async function getPageWithRetry(id, from, retryAttempts = 3) { retryAttempts < 3 ? `剩余重试次数:${retryAttempts}` : '' ) try { - const start = new Date().getTime() + const start = Date.now() const pageData = await notionAPI.getPage(id) - const end = new Date().getTime() + const end = Date.now() console.log('[API<<--响应]', `耗时:${end - start}ms - from:${from}`) return pageData } catch (e) { @@ -86,7 +94,7 @@ export function formatNotionBlock(block) { const clonedBlock = deepClone(block) const blocksToProcess = Object.keys(clonedBlock || {}) - for (let i = 0; i < blocksToProcess.length; i++) { + for (let i = 0; i < blocksToProcess.length;) { const blockId = blocksToProcess[i] let b = clonedBlock[blockId] @@ -100,6 +108,7 @@ export function formatNotionBlock(block) { b = clonedBlock[blockId] } else if (!b?.value?.id && b?.value?.role !== undefined) { // role:none 等无权限 block,直接跳过 + i++ continue } @@ -115,13 +124,14 @@ export function formatNotionBlock(block) { if (b?.value?.type === 'sync_block' && b?.value?.children) { const childBlocks = b.value.children + const childBlockIds = [] delete clonedBlock[blockId] childBlocks.forEach((childBlock, index) => { const newBlockId = `${blockId}_child_${index}` clonedBlock[newBlockId] = childBlock - blocksToProcess.splice(i + index + 1, 0, newBlockId) + childBlockIds.push(newBlockId) }) - i-- + blocksToProcess.splice(i, 1, ...childBlockIds) continue } @@ -147,6 +157,8 @@ export function formatNotionBlock(block) { const newUrl = `https://notion.so/signed/${encodeURIComponent(oldUrl)}?table=block&id=${b?.value?.id}` b.value.properties.source[0][0] = newUrl } + + i++ } return clonedBlock @@ -159,34 +171,86 @@ export function formatNotionBlock(block) { * @param {*} batchSize * @returns */ -export const fetchInBatches = async (ids, batchSize = 100) => { - // 如果 ids 不是数组,则将其转换为数组 +export const fetchInBatches = async (ids, batchSize = 10) => { if (!Array.isArray(ids)) { ids = [ids] } let fetchedBlocks = {} + + let cacheHit = 0 + let cacheMiss = 0 + + console.log('[Batch] START total ids:', ids.length) + for (let i = 0; i < ids.length; i += batchSize) { const batch = ids.slice(i, i + batchSize) - console.log('[API-->>请求] Fetching missing blocks', ids.length) - const start = new Date().getTime() - const pageChunk = await notionAPI.getBlocks(batch) - const end = new Date().getTime() - console.log( - `[API<<--响应] 耗时:${end - start}ms Fetching missing blocks count:${ids.length} ` + + console.log(`\n[Batch] processing ${i} ~ ${i + batch.length}`) + + const results = await Promise.all( + batch.map((id, index) => + limit(async () => { + const cacheKey = `page_block_${id}` + + try { + // ✅ 1. 先查缓存 + const cached = await getDataFromCache(cacheKey) + + if (cached) { + cacheHit++ + console.log('[Cache HIT]', id) + return cached + } + + cacheMiss++ + console.log('[Cache MISS]', id) + + // 👉 节流 + await delay(index * REQUEST_INTERVAL) + + console.log('[API-->>请求]', id) + + const start = Date.now() + const pageChunk = await notionAPI.getBlocks([id]) + const end = Date.now() + + console.log( + `[API<<--响应] ${id} 耗时:${end - start}ms blockCount:${ + Object.keys(pageChunk?.recordMap?.block || {}).length + }` + ) + + const blocks = pageChunk?.recordMap?.block || {} + + // ✅ 2. 写缓存(注意:这里缓存的是整个 block map,不只是 id) + await setDataToCache(cacheKey, blocks) + + return blocks + } catch (err) { + console.warn('[API异常]', id, err.message) + return {} + } + }) + ) ) - console.log('[API<<--响应]') - fetchedBlocks = Object.assign( - {}, - fetchedBlocks, - pageChunk?.recordMap?.block + // ✅ 合并 + for (const block of results) { + fetchedBlocks = { + ...fetchedBlocks, + ...block + } + } + + console.log( + `[Batch] 当前累计 blocks: ${Object.keys(fetchedBlocks).length}` ) } + return fetchedBlocks } - /** * 强制修复 block 中所有可能的非法 URL 字段 * @param {Object} blockValue - block.value diff --git a/lib/utils/index.js b/lib/utils/index.js index b6ed4e739cc..2dcbd04f6c3 100644 --- a/lib/utils/index.js +++ b/lib/utils/index.js @@ -304,6 +304,7 @@ export function deepClone(obj) { return obj } } + /** * 延时 * @param {*} ms diff --git a/next.config.js b/next.config.js index 5e955ed3fd5..d4dfe7ff29f 100644 --- a/next.config.js +++ b/next.config.js @@ -80,16 +80,18 @@ function scanSubdirectories(directory) { * @type {import('next').NextConfig} */ +function getOutput() { + if (process.env.EXPORT) return 'export' + if (process.env.NEXT_BUILD_STANDALONE === 'true') return 'standalone' + return undefined +} + const nextConfig = { eslint: { ignoreDuringBuilds: true }, - output: process.env.EXPORT - ? 'export' - : process.env.NEXT_BUILD_STANDALONE === 'true' - ? 'standalone' - : undefined, - staticPageGenerationTimeout: 120, + output: getOutput(), + staticPageGenerationTimeout: 300, // 性能优化配置 compress: true, @@ -110,10 +112,10 @@ const nextConfig = { i18n: process.env.EXPORT ? undefined : { - defaultLocale: BLOG.LANG, - // 支持的所有多语言,按需填写即可 - locales: locales - }, + defaultLocale: BLOG.LANG, + // 支持的所有多语言,按需填写即可 + locales: locales + }, images: { // 图片压缩和格式优化 formats: ['image/avif', 'image/webp'], @@ -144,91 +146,82 @@ const nextConfig = { redirects: process.env.EXPORT ? undefined : () => { - return [ - { - source: '/feed', - destination: '/rss/feed.xml', - permanent: true - } - ] - }, + return [ + { + source: '/feed', + destination: '/rss/feed.xml', + permanent: true + } + ] + }, // 重写url rewrites: process.env.EXPORT ? undefined : () => { - // 处理多语言重定向 - const langsRewrites = [] - if (BLOG.NOTION_PAGE_ID.indexOf(',') > 0) { - const siteIds = BLOG.NOTION_PAGE_ID.split(',') - const langs = [] - for (let index = 0; index < siteIds.length; index++) { - const siteId = siteIds[index] - const prefix = extractLangPrefix(siteId) - // 如果包含前缀 例如 zh , en 等 - if (prefix) { - langs.push(prefix) - } - console.log('[Locales]', siteId) + // 处理多语言重定向 + const langsRewrites = [] + if (BLOG.NOTION_PAGE_ID.indexOf(',') > 0) { + const siteIds = BLOG.NOTION_PAGE_ID.split(',') + const langs = [] + for (let index = 0; index < siteIds.length; index++) { + const siteId = siteIds[index] + const prefix = extractLangPrefix(siteId) + // 如果包含前缀 例如 zh , en 等 + if (prefix) { + langs.push(prefix) } - - // 映射多语言 - // 示例: source: '/:locale(zh|en)/:path*' ; :locale() 会将语言放入重写后的 `?locale=` 中。 - langsRewrites.push( - { - source: `/:locale(${langs.join('|')})/:path*`, - destination: '/:path*' - }, - // 匹配没有路径的情况,例如 [domain]/zh 或 [domain]/en - { - source: `/:locale(${langs.join('|')})`, - destination: '/' - }, - // 匹配没有路径的情况,例如 [domain]/zh/ 或 [domain]/en/ - { - source: `/:locale(${langs.join('|')})/`, - destination: '/' - } - ) + console.log('[Locales]', siteId) } - return [ - ...langsRewrites, - // 伪静态重写 + // 映射多语言 + // 示例: source: '/:locale(zh|en)/:path*' ; :locale() 会将语言放入重写后的 `?locale=` 中。 + langsRewrites.push( { - source: '/:path*.html', + source: `/:locale(${langs.join('|')})/:path*`, destination: '/:path*' + }, + // 匹配没有路径的情况,例如 [domain]/zh 或 [domain]/en + { + source: `/:locale(${langs.join('|')})`, + destination: '/' + }, + // 匹配没有路径的情况,例如 [domain]/zh/ 或 [domain]/en/ + { + source: `/:locale(${langs.join('|')})/`, + destination: '/' } - ] - }, + ) + } + + return [ + ...langsRewrites, + // 伪静态重写 + { + source: '/:path*.html', + destination: '/:path*' + } + ] + }, headers: process.env.EXPORT ? undefined : () => { - return [ - { - source: '/:path*{/}?', - headers: [ - // -------------------------------------------------------------------- - // 修改部分开始:允许 iframe 嵌入 - // -------------------------------------------------------------------- - { key: 'X-Frame-Options', value: 'ALLOWALL' }, - { key: 'Content-Security-Policy', value: 'frame-ancestors *' }, - // -------------------------------------------------------------------- - // 修改部分结束 - // -------------------------------------------------------------------- - - // 为了博客兼容性,不做过多安全限制 - { key: 'Access-Control-Allow-Credentials', value: 'true' }, - { key: 'Access-Control-Allow-Origin', value: '*' }, - { - key: 'Access-Control-Allow-Methods', - value: 'GET,OPTIONS,PATCH,DELETE,POST,PUT' - }, - { - key: 'Access-Control-Allow-Headers', - value: - 'X-CSRF-Token, X-Requested-With, Accept, Accept-Version, Content-Length, Content-MD5, Content-Type, Date, X-Api-Version' - } - // 下面是原有的被注释掉的安全配置,保持不动 + return [ + { + source: '/:path*{/}?', + headers: [ + // 为了博客兼容性,不做过多安全限制 + { key: 'Access-Control-Allow-Credentials', value: 'true' }, + { key: 'Access-Control-Allow-Origin', value: '*' }, + { + key: 'Access-Control-Allow-Methods', + value: 'GET,OPTIONS,PATCH,DELETE,POST,PUT' + }, + { + key: 'Access-Control-Allow-Headers', + value: + 'X-CSRF-Token, X-Requested-With, Accept, Accept-Version, Content-Length, Content-MD5, Content-Type, Date, X-Api-Version' + } + // 安全头部 相关配置,谨慎开启 // { key: 'X-Frame-Options', value: 'DENY' }, // { key: 'X-Content-Type-Options', value: 'nosniff' }, // { key: 'X-XSS-Protection', value: '1; mode=block' }, @@ -263,23 +256,23 @@ const nextConfig = { // : '*' // }, // { key: 'Access-Control-Max-Age', value: '86400' } - ] - }, - // { - // source: '/api/:path*', - // headers: [ - // // API 特定的安全头部 - // { key: 'X-Frame-Options', value: 'DENY' }, - // { key: 'X-Content-Type-Options', value: 'nosniff' }, - // { key: 'Cache-Control', value: 'no-store, max-age=0' }, - // { - // key: 'Access-Control-Allow-Methods', - // value: 'GET,POST,PUT,DELETE,OPTIONS' - // } - // ] - // } - ] - }, + ] + }, + // { + // source: '/api/:path*', + // headers: [ + // // API 特定的安全头部 + // { key: 'X-Frame-Options', value: 'DENY' }, + // { key: 'X-Content-Type-Options', value: 'nosniff' }, + // { key: 'Cache-Control', value: 'no-store, max-age=0' }, + // { + // key: 'Access-Control-Allow-Methods', + // value: 'GET,POST,PUT,DELETE,OPTIONS' + // } + // ] + // } + ] + }, webpack: (config, { dev, isServer }) => { // 动态主题:添加 resolve.alias 配置,将动态路径映射到实际路径 config.resolve.alias['@'] = path.resolve(__dirname) @@ -339,8 +332,10 @@ const nextConfig = { } return config - }, + } + , experimental: { + cpus: 1, scrollRestoration: true, // 性能优化实验性功能 optimizePackageImports: ['@heroicons/react', 'lodash'] diff --git a/package.json b/package.json index 58988bfe910..4bcf11af0f0 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "notion-next", - "version": "4.9.4", + "version": "4.9.4.1", "homepage": "https://github.com/tangly1024/NotionNext.git", "license": "MIT", "engines": { @@ -54,6 +54,7 @@ "@headlessui/react": "^1.7.19", "@next/bundle-analyzer": "^12.3.7", "@vercel/analytics": "^1.5.0", + "@vercel/functions": "^3.4.3", "algoliasearch": "^4.25.2", "axios": "^1.7.2", "critters": "^0.0.23", @@ -65,6 +66,7 @@ "next": "^14.2.30", "notion-client": "7.10.0", "notion-utils": "7.10.0", + "p-limit": "^7.3.0", "react": "^18.3.1", "react-dom": "^18.3.1", "react-facebook": "^8.1.4", diff --git a/pages/[prefix]/index.js b/pages/[prefix]/index.js index 7de9070793d..c0fa6ba7efa 100644 --- a/pages/[prefix]/index.js +++ b/pages/[prefix]/index.js @@ -6,11 +6,10 @@ import { fetchGlobalAllData, resolvePostProps } from '@/lib/db/SiteDataApi' import { useGlobal } from '@/lib/global' import { getPageTableOfContents } from '@/lib/db/notion/getPageTableOfContents' import { getPasswordQuery } from '@/lib/utils/password' -import { checkSlugHasMorThanTwoSlash, checkSlugHasNoSlash, processPostData } from '@/lib/utils/post' +import { checkSlugHasNoSlash } from '@/lib/utils/post' import { DynamicLayout } from '@/themes/theme' import md5 from 'js-md5' import { useRouter } from 'next/router' -import { idToUuid } from 'notion-utils' import { useEffect, useState } from 'react' /** diff --git a/yarn.lock b/yarn.lock index 8f1643901bb..b80fe4348b2 100644 --- a/yarn.lock +++ b/yarn.lock @@ -155,7 +155,7 @@ resolved "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.28.0.tgz" integrity sha512-60X7qkglvrap8mn1lh2ebxXdZYtUcpd7gsmy9kLaBJ4i/WdY8PqTSdxyA8qraikqKQK5C1KRBKXqznrVapyNaw== -"@babel/core@^7.0.0", "@babel/core@^7.0.0 || ^8.0.0-0", "@babel/core@^7.0.0-0", "@babel/core@^7.11.6", "@babel/core@^7.12.3", "@babel/core@^7.23.9", "@babel/core@^7.8.0": +"@babel/core@^7.11.6", "@babel/core@^7.12.3", "@babel/core@^7.23.9": version "7.28.3" resolved "https://registry.npmjs.org/@babel/core/-/core-7.28.3.tgz" integrity sha512-yDBHV9kQNcr2/sUr9jghVyz9C3Y5G2zUM2H2lo+9mKv4sFgbA8s8Z9t8D1jiTkGoO/NoIfKMyKWr4s6CN23ZwQ== @@ -381,7 +381,7 @@ dependencies: "@babel/helper-plugin-utils" "^7.27.1" -"@babel/runtime@^7.12.5", "@babel/runtime@^7.7.2", "@babel/runtime@>=7": +"@babel/runtime@^7.12.5", "@babel/runtime@^7.7.2": version "7.28.3" resolved "https://registry.npmjs.org/@babel/runtime/-/runtime-7.28.3.tgz" integrity sha512-9uIQ10o0WGdpP6GDhXcdOJPJuDgFtIDtN/9+ArJQ2NAfAmiuhTQdzkaTGR33v43GYS2UrSA0eX2pPPHoFVvpxA== @@ -480,13 +480,6 @@ std-env "^3.7.0" swr "^2.2.0" -"@clerk/types@^4.62.0": - version "4.62.0" - resolved "https://registry.npmjs.org/@clerk/types/-/types-4.62.0.tgz" - integrity sha512-Ps/8eQHCuv2bZYgTG3+4xgxlltoX91GNf+G5TG/DSSmECgR657qGoasOrLLcMMyE+OZiX57k51oH137Ohhggog== - dependencies: - csstype "3.1.3" - "@clerk/types@4.26.0": version "4.26.0" resolved "https://registry.npmmirror.com/@clerk/types/-/types-4.26.0.tgz" @@ -494,6 +487,13 @@ dependencies: csstype "3.1.1" +"@clerk/types@^4.62.0": + version "4.62.0" + resolved "https://registry.npmjs.org/@clerk/types/-/types-4.62.0.tgz" + integrity sha512-Ps/8eQHCuv2bZYgTG3+4xgxlltoX91GNf+G5TG/DSSmECgR657qGoasOrLLcMMyE+OZiX57k51oH137Ohhggog== + dependencies: + csstype "3.1.3" + "@corex/deepmerge@^2.6.148": version "2.6.148" resolved "https://registry.npmmirror.com/@corex/deepmerge/-/deepmerge-2.6.148.tgz" @@ -824,21 +824,6 @@ "@jridgewell/resolve-uri" "^3.1.0" "@jridgewell/sourcemap-codec" "^1.4.14" -"@mapbox/node-pre-gyp@^1.0.0": - version "1.0.11" - resolved "https://registry.npmjs.org/@mapbox/node-pre-gyp/-/node-pre-gyp-1.0.11.tgz" - integrity sha512-Yhlar6v9WQgUp/He7BdgzOz8lqMQ8sU+jkCq7Wx8Myc5YFJLbEe7lgui/V7G1qB1DJykHSGwreceSaD60Y0PUQ== - dependencies: - detect-libc "^2.0.0" - https-proxy-agent "^5.0.0" - make-dir "^3.1.0" - node-fetch "^2.6.7" - nopt "^5.0.0" - npmlog "^5.0.1" - rimraf "^3.0.2" - semver "^7.3.5" - tar "^6.1.11" - "@matejmazur/react-katex@^3.1.3": version "3.1.3" resolved "https://registry.npmmirror.com/@matejmazur/react-katex/-/react-katex-3.1.3.tgz" @@ -916,7 +901,7 @@ "@nodelib/fs.stat" "2.0.5" run-parallel "^1.1.9" -"@nodelib/fs.stat@^2.0.2", "@nodelib/fs.stat@2.0.5": +"@nodelib/fs.stat@2.0.5", "@nodelib/fs.stat@^2.0.2": version "2.0.5" resolved "https://registry.npmmirror.com/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz" integrity sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A== @@ -1003,7 +988,7 @@ resolved "https://registry.npmmirror.com/@tanstack/virtual-core/-/virtual-core-3.10.9.tgz" integrity sha512-kBknKOKzmeR7lN+vSadaKWXaLS0SZZG+oqpQ/k80Q6g9REn6zRHS/ZYdrIzHnpHgy/eWs00SujveUN/GJT2qTw== -"@testing-library/dom@^9.0.0", "@testing-library/dom@>=7.21.4": +"@testing-library/dom@^9.0.0": version "9.3.4" resolved "https://registry.npmjs.org/@testing-library/dom/-/dom-9.3.4.tgz" integrity sha512-FlS4ZWlp97iiNWig0Muq8p+3rVDjRiYE+YKGbAqXOu9nwJFFOdL00kFpz42M+4huzYi86vAK1sOOfyOG45muIQ== @@ -1150,7 +1135,7 @@ resolved "https://registry.npmjs.org/@types/react-dom/-/react-dom-18.3.7.tgz" integrity sha512-MEe3UeoENYVFXzoXEWsvcpg6ZvlrFNlOQ7EOsvhI3CfAXwzPfO8Qwuxd40nepsYKqyyVQnTdEfv68q91yLcKrQ== -"@types/react@^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0", "@types/react@^18.0.0", "@types/react@18.3.10": +"@types/react@18.3.10": version "18.3.10" resolved "https://registry.npmmirror.com/@types/react/-/react-18.3.10.tgz" integrity sha512-02sAAlBnP39JgXwkAq3PeU9DVaaGpZyF3MGcC0MKgQVkZor5IiiDAipVaxQHtDJAmO4GIy/rVBy/LzVj76Cyqg== @@ -1211,7 +1196,7 @@ "@typescript-eslint/visitor-keys" "6.21.0" debug "^4.3.4" -"@typescript-eslint/parser@^7.0.0", "@typescript-eslint/parser@^7.18.0": +"@typescript-eslint/parser@^7.18.0": version "7.18.0" resolved "https://registry.npmmirror.com/@typescript-eslint/parser/-/parser-7.18.0.tgz" integrity sha512-4Z+L8I2OqhZV8qA132M4wNL30ypZGYOQVBfMgxDH/K5UX0PNqTu1c6za9ST5r9+tavvHiTWmBnKzpCJ/GlVFtg== @@ -1322,6 +1307,18 @@ resolved "https://registry.npmjs.org/@vercel/analytics/-/analytics-1.5.0.tgz" integrity sha512-MYsBzfPki4gthY5HnYN7jgInhAZ7Ac1cYDoRWFomwGHWEX7odTEzbtg9kf/QSo7XEsEAqlQugA6gJ2WS2DEa3g== +"@vercel/functions@^3.4.3": + version "3.4.3" + resolved "https://registry.yarnpkg.com/@vercel/functions/-/functions-3.4.3.tgz#69b2c0d60164479b072605a7989462c7662ecc88" + integrity sha512-kA14KIUVgAY6VXbhZ5jjY+s0883cV3cZqIU3WhrSRxuJ9KvxatMjtmzl0K23HK59oOUjYl7HaE/eYMmhmqpZzw== + dependencies: + "@vercel/oidc" "3.2.0" + +"@vercel/oidc@3.2.0": + version "3.2.0" + resolved "https://registry.yarnpkg.com/@vercel/oidc/-/oidc-3.2.0.tgz#5782a4d4904443f015808705b5537cf9c3b68528" + integrity sha512-UycprH3T6n3jH0k44NHMa7pnFHGu/N05MjojYr+Mc6I7obkoLIJujSWwin1pCvdy/eOxrI/l3uDLQsmcrOb4ug== + "@vue/compiler-core@3.5.22": version "3.5.22" resolved "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.5.22.tgz" @@ -1449,11 +1446,6 @@ abab@^2.0.6: resolved "https://registry.npmjs.org/abab/-/abab-2.0.6.tgz" integrity sha512-j2afSsaIENvHZN2B8GOpF566vZ5WVk5opAiMTvWgaQT8DkbOqsTfvNAvHoRGU2zzP8cPoqys+xHTRDWW8L+/BA== -abbrev@1: - version "1.1.1" - resolved "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz" - integrity sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q== - acorn-globals@^7.0.0: version "7.0.1" resolved "https://registry.npmjs.org/acorn-globals/-/acorn-globals-7.0.1.tgz" @@ -1474,17 +1466,12 @@ acorn-walk@^8.0.0, acorn-walk@^8.0.2: dependencies: acorn "^8.11.0" -"acorn@^6.0.0 || ^7.0.0 || ^8.0.0", acorn@^8.0.4, acorn@^8.11.0, acorn@^8.9.0: +acorn@^8.0.4, acorn@^8.11.0, acorn@^8.9.0: version "8.14.0" resolved "https://registry.npmmirror.com/acorn/-/acorn-8.14.0.tgz" integrity sha512-cl669nCJTZBsL97OF4kUQm5g5hC2uihk0NxY3WENAC0TYdILVkAyHymAntgxGkl7K+t0cXIrH5siy5S4XkFycA== -acorn@^8.1.0: - version "8.15.0" - resolved "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz" - integrity sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg== - -acorn@^8.8.1: +acorn@^8.1.0, acorn@^8.8.1: version "8.15.0" resolved "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz" integrity sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg== @@ -1574,19 +1561,6 @@ anymatch@^3.0.3, anymatch@~3.1.2: normalize-path "^3.0.0" picomatch "^2.0.4" -"aproba@^1.0.3 || ^2.0.0": - version "2.1.0" - resolved "https://registry.npmjs.org/aproba/-/aproba-2.1.0.tgz" - integrity sha512-tLIEcj5GuR2RSTnxNKdkK0dJ/GrC7P38sUkiDmDuHfsHmbagTFAxDVIBltoklXEVIQ/f14IL8IMJ5pn9Hez1Ew== - -are-we-there-yet@^2.0.0: - version "2.0.0" - resolved "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-2.0.0.tgz" - integrity sha512-Ci/qENmwHnsYo9xKIcUJN5LeDKdJ6R1Z1j9V/J5wyq8nh/mYPEpIKJbBZXtZjG04HiK7zV/p6Vs9952MrMeUIw== - dependencies: - delegates "^1.0.0" - readable-stream "^3.6.0" - arg@^5.0.2: version "5.0.2" resolved "https://registry.npmmirror.com/arg/-/arg-5.0.2.tgz" @@ -1604,11 +1578,6 @@ argparse@^2.0.1: resolved "https://registry.npmmirror.com/argparse/-/argparse-2.0.1.tgz" integrity sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q== -aria-query@^5.0.0, aria-query@^5.3.2: - version "5.3.2" - resolved "https://registry.npmmirror.com/aria-query/-/aria-query-5.3.2.tgz" - integrity sha512-COROpnaoap1E2F000S62r6A60uHZnmlvomhfyT2DlTcrY1OrBKn2UhH7qn5wTC9zMvD0AY7csdPSNwKP+7WiQw== - aria-query@5.1.3: version "5.1.3" resolved "https://registry.npmjs.org/aria-query/-/aria-query-5.1.3.tgz" @@ -1616,6 +1585,11 @@ aria-query@5.1.3: dependencies: deep-equal "^2.0.5" +aria-query@^5.0.0, aria-query@^5.3.2: + version "5.3.2" + resolved "https://registry.npmmirror.com/aria-query/-/aria-query-5.3.2.tgz" + integrity sha512-COROpnaoap1E2F000S62r6A60uHZnmlvomhfyT2DlTcrY1OrBKn2UhH7qn5wTC9zMvD0AY7csdPSNwKP+7WiQw== + array-buffer-byte-length@^1.0.0, array-buffer-byte-length@^1.0.1, array-buffer-byte-length@^1.0.2: version "1.0.2" resolved "https://registry.npmjs.org/array-buffer-byte-length/-/array-buffer-byte-length-1.0.2.tgz" @@ -1756,6 +1730,15 @@ axe-core@^4.10.0: resolved "https://registry.npmmirror.com/axe-core/-/axe-core-4.10.2.tgz" integrity sha512-RE3mdQ7P3FRSe7eqCWoeQ/Z9QXrtniSjp1wUjt5nRC3WIpz5rSCve6o3fsZ2aCpJtrZjSZgjwXAoTO5k4tEI0w== +axios@>=0.21.1: + version "1.14.0" + resolved "https://registry.yarnpkg.com/axios/-/axios-1.14.0.tgz#7c29f4cf2ea91ef05018d5aa5399bf23ed3120eb" + integrity sha512-3Y8yrqLSwjuzpXuZ0oIYZ/XGgLwUIBU3uLvbcpb0pidD9ctpShJd43KSlEEkVQg6DS0G9NKyzOvBfUtDKEyHvQ== + dependencies: + follow-redirects "^1.15.11" + form-data "^4.0.5" + proxy-from-env "^2.1.0" + axios@^1.7.2: version "1.10.0" resolved "https://registry.npmjs.org/axios/-/axios-1.10.0.tgz" @@ -1894,7 +1877,7 @@ browserslist@^4.24.0: node-releases "^2.0.19" update-browserslist-db "^1.1.3" -browserslist@^4.24.4, "browserslist@>= 4.21.0": +browserslist@^4.24.4: version "4.25.1" resolved "https://registry.npmjs.org/browserslist/-/browserslist-4.25.1.tgz" integrity sha512-KGj0KoOMXLpSNkkEI6Z6mShmQy0bc1I+T7K9N81k4WWMrfz+6fQ6es80B/YLAeRoKvjYE1YSHHOW1qe9xIVzHw== @@ -1992,15 +1975,6 @@ caniuse-lite@^1.0.30001735: resolved "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001735.tgz" integrity sha512-EV/laoX7Wq2J9TQlyIXRxTJqIw4sxfXS4OYgudGxBYRuTv0q7AM6yMEpU/Vo1I94thg9U6EZ2NfZx9GJq83u7w== -canvas@^2.5.0: - version "2.11.2" - resolved "https://registry.npmjs.org/canvas/-/canvas-2.11.2.tgz" - integrity sha512-ItanGBMrmRV7Py2Z+Xhs7cT+FNt5K0vPL4p9EZ/UX/Mu7hFbkxSjKF2KVtPwX7UYWp7dRKnrTvReflgrItJbdw== - dependencies: - "@mapbox/node-pre-gyp" "^1.0.0" - nan "^2.17.0" - simple-get "^3.0.3" - canvas@^3.0.0-rc2: version "3.2.3" resolved "https://registry.npmjs.org/canvas/-/canvas-3.2.3.tgz" @@ -2042,11 +2016,6 @@ chownr@^1.1.1: resolved "https://registry.npmjs.org/chownr/-/chownr-1.1.4.tgz" integrity sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg== -chownr@^2.0.0: - version "2.0.0" - resolved "https://registry.npmjs.org/chownr/-/chownr-2.0.0.tgz" - integrity sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ== - ci-info@^3.2.0, ci-info@^3.7.0: version "3.9.0" resolved "https://registry.npmjs.org/ci-info/-/ci-info-3.9.0.tgz" @@ -2062,7 +2031,7 @@ classnames@^2.3.2: resolved "https://registry.npmmirror.com/classnames/-/classnames-2.5.1.tgz" integrity sha512-saHYOzhIQs6wy2sVxTM6bUDsQO4F50V9RQ22qBpEdCW+I+/Wmke2HOl6lS6dTpdxVhb88/I6+Hs+438c3lfUow== -client-only@^0.0.1, client-only@0.0.1: +client-only@0.0.1, client-only@^0.0.1: version "0.0.1" resolved "https://registry.npmmirror.com/client-only/-/client-only-0.0.1.tgz" integrity sha512-IV3Ou0jSMzZrd3pZ48nLkT9DA7Ag1pnPzaiQhpW7c3RbcqqzvzzVu+L8gfqMp/8IM2MQtSiqaCxrrcfu8I8rMA== @@ -2108,11 +2077,6 @@ color-name@~1.1.4: resolved "https://registry.npmmirror.com/color-name/-/color-name-1.1.4.tgz" integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA== -color-support@^1.1.2: - version "1.1.3" - resolved "https://registry.npmjs.org/color-support/-/color-support-1.1.3.tgz" - integrity sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg== - combined-stream@^1.0.8: version "1.0.8" resolved "https://registry.npmmirror.com/combined-stream/-/combined-stream-1.0.8.tgz" @@ -2145,11 +2109,6 @@ concat-map@0.0.1: resolved "https://registry.npmmirror.com/concat-map/-/concat-map-0.0.1.tgz" integrity sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg== -console-control-strings@^1.0.0, console-control-strings@^1.1.0: - version "1.1.0" - resolved "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz" - integrity sha512-ty/fTekppD2fIwRvnZAVdeOiGd1c7YXEixbgJTNzqcxJWKQnjJ/V1bNEEE6hygpM3WjwHFUVK6HTjWSzV4a8sQ== - convert-source-map@^2.0.0: version "2.0.0" resolved "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz" @@ -2250,17 +2209,12 @@ cssstyle@^2.3.0: dependencies: cssom "~0.3.6" -csstype@^3.0.2, csstype@3.1.1: +csstype@3.1.1, csstype@^3.0.2: version "3.1.1" resolved "https://registry.npmmirror.com/csstype/-/csstype-3.1.1.tgz" integrity sha512-DJR/VvkAvSZW9bTouZue2sSxDwdTN92uHjqeKVm+0dAqdfNykRzQ95tay8aXMBAAPpUiq4Qcug2L7neoRh2Egw== -csstype@^3.1.3: - version "3.1.3" - resolved "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz" - integrity sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw== - -csstype@3.1.3: +csstype@3.1.3, csstype@^3.1.3: version "3.1.3" resolved "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz" integrity sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw== @@ -2311,6 +2265,13 @@ debounce@^1.2.1: resolved "https://registry.npmmirror.com/debounce/-/debounce-1.2.1.tgz" integrity sha512-XRRe6Glud4rd/ZGQfiV1ruXSfbvfJedlV9Y6zOlP+2K04vBYiJEte6stfFkCP03aMnY5tsipamumUjL14fofug== +debug@4, debug@^4.1.0, debug@^4.1.1: + version "4.4.1" + resolved "https://registry.npmjs.org/debug/-/debug-4.4.1.tgz" + integrity sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ== + dependencies: + ms "^2.1.3" + debug@^2.1.3: version "2.6.9" resolved "https://registry.npmmirror.com/debug/-/debug-2.6.9.tgz" @@ -2325,20 +2286,6 @@ debug@^3.2.7: dependencies: ms "^2.1.1" -debug@^4.1.0: - version "4.4.1" - resolved "https://registry.npmjs.org/debug/-/debug-4.4.1.tgz" - integrity sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ== - dependencies: - ms "^2.1.3" - -debug@^4.1.1: - version "4.4.1" - resolved "https://registry.npmjs.org/debug/-/debug-4.4.1.tgz" - integrity sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ== - dependencies: - ms "^2.1.3" - debug@^4.3.1, debug@^4.3.2, debug@^4.3.4, debug@^4.3.7: version "4.4.0" resolved "https://registry.npmmirror.com/debug/-/debug-4.4.0.tgz" @@ -2346,25 +2293,11 @@ debug@^4.3.1, debug@^4.3.2, debug@^4.3.4, debug@^4.3.7: dependencies: ms "^2.1.3" -debug@4: - version "4.4.1" - resolved "https://registry.npmjs.org/debug/-/debug-4.4.1.tgz" - integrity sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ== - dependencies: - ms "^2.1.3" - decimal.js@^10.4.2: version "10.6.0" resolved "https://registry.npmjs.org/decimal.js/-/decimal.js-10.6.0.tgz" integrity sha512-YpgQiITW3JXGntzdUmyUR1V812Hn8T1YVXhCu+wO3OpS4eU9l4YdD3qjyiKdV6mvV29zapkMeD390UVEf2lkUg== -decompress-response@^4.2.0: - version "4.2.1" - resolved "https://registry.npmjs.org/decompress-response/-/decompress-response-4.2.1.tgz" - integrity sha512-jOSne2qbyE+/r8G1VU+G/82LBs2Fs4LAsTiLSHOCOMZQl2OKZ6i8i4IyHemTe+/yIXOtTcRQMzPcgyhoFlqPkw== - dependencies: - mimic-response "^2.0.0" - decompress-response@^6.0.0: version "6.0.0" resolved "https://registry.npmjs.org/decompress-response/-/decompress-response-6.0.0.tgz" @@ -2439,11 +2372,6 @@ delayed-stream@~1.0.0: resolved "https://registry.npmmirror.com/delayed-stream/-/delayed-stream-1.0.0.tgz" integrity sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ== -delegates@^1.0.0: - version "1.0.0" - resolved "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz" - integrity sha512-bd2L678uiWATM6m5Z1VzNCErI3jiGzt6HGY8OVICs40JQq/HALfbyNJmp0UDakEY4pMMaN0Ly5om/B1VI/+xfQ== - denque@^2.1.0: version "2.1.0" resolved "https://mirrors.cloud.tencent.com/npm/denque/-/denque-2.1.0.tgz" @@ -2817,7 +2745,7 @@ eslint-config-next@^13.5.11: eslint-plugin-react "^7.33.2" eslint-plugin-react-hooks "^4.5.0 || 5.0.0-canary-7118f5dd7-20230705" -eslint-config-prettier@^9.1.0, "eslint-config-prettier@>= 7.0.0 <10.0.0 || >=10.1.0": +eslint-config-prettier@^9.1.0: version "9.1.0" resolved "https://registry.npmmirror.com/eslint-config-prettier/-/eslint-config-prettier-9.1.0.tgz" integrity sha512-NSWl5BFQWEPi1j4TjVNItzYV7dZXZ+wP6I6ZhrBGpChQhZRUaElihE9uRRkcbRnNb76UMKDF3r+WTmNcGPKsqw== @@ -2860,7 +2788,7 @@ eslint-plugin-es@^3.0.0: eslint-utils "^2.0.0" regexpp "^3.0.0" -eslint-plugin-import@*, eslint-plugin-import@^2.28.1, eslint-plugin-import@^2.32.0: +eslint-plugin-import@^2.28.1, eslint-plugin-import@^2.32.0: version "2.32.0" resolved "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.32.0.tgz" integrity sha512-whOE1HFo/qJDyX4SnXzP4N6zOWn79WhnCUY/iDR0mPfQZO8wcYE4JClzI2oZrhBnnMUCBCHZhO6VQyoBU95mZA== @@ -2980,7 +2908,7 @@ eslint-visitor-keys@^3.4.1, eslint-visitor-keys@^3.4.3: resolved "https://registry.npmmirror.com/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz" integrity sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag== -eslint@*, "eslint@^2 || ^3 || ^4 || ^5 || ^6 || ^7.2.0 || ^8 || ^9", "eslint@^3 || ^4 || ^5 || ^6 || ^7 || ^8 || ^9", "eslint@^3 || ^4 || ^5 || ^6 || ^7 || ^8 || ^9.7", "eslint@^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0", "eslint@^6.0.0 || ^7.0.0 || >=8.0.0", "eslint@^7.0.0 || ^8.0.0", "eslint@^7.23.0 || ^8.0.0", eslint@^8.56.0, eslint@^8.57.1, eslint@>=4.19.1, eslint@>=5.16.0, eslint@>=7.0.0, eslint@>=8.0.0: +eslint@^8.57.1: version "8.57.1" resolved "https://registry.npmmirror.com/eslint/-/eslint-8.57.1.tgz" integrity sha512-ypowyDxpVSYpkXr9WPv2PAZCtNip1Mv5KTW0SCurXv/9iOpcrH9PaqUElksqEB6pChqHGDRCFTyrZlGhnLNGiA== @@ -3216,6 +3144,11 @@ flatted@^3.2.9: resolved "https://registry.npmmirror.com/flatted/-/flatted-3.3.2.tgz" integrity sha512-AiwGJM8YcNOaobumgtng+6NHuOqC3A7MixFeDafM3X9cIUM+xUXoS5Vfgf+OihAYe20fxqNM9yPBXJzRtZ/4eA== +follow-redirects@^1.15.11: + version "1.15.11" + resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.15.11.tgz#777d73d72a92f8ec4d2e410eb47352a56b8e8340" + integrity sha512-deG2P0JfjrTxl50XGCDyfI97ZGVCxIpfKYmfyrQ54n5FO/0gfIES8C/Psl6kWVDolizcaaxZJnTS0QSMxvnsBQ== + follow-redirects@^1.15.6: version "1.15.9" resolved "https://registry.npmmirror.com/follow-redirects/-/follow-redirects-1.15.9.tgz" @@ -3245,6 +3178,17 @@ form-data@^4.0.0: combined-stream "^1.0.8" mime-types "^2.1.12" +form-data@^4.0.5: + version "4.0.5" + resolved "https://registry.yarnpkg.com/form-data/-/form-data-4.0.5.tgz#b49e48858045ff4cbf6b03e1805cebcad3679053" + integrity sha512-8RipRLol37bNs2bhoV67fiTEvdTrbMUYcFTiy3+wuuOnUog2QBHCZWXDRijWQfAkhBj2Uf5UnVaiWwA5vdd82w== + 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" + fraction.js@^4.3.7: version "4.3.7" resolved "https://registry.npmmirror.com/fraction.js/-/fraction.js-4.3.7.tgz" @@ -3264,18 +3208,16 @@ fs-extra@^10.0.0: jsonfile "^6.0.1" universalify "^2.0.0" -fs-minipass@^2.0.0: - version "2.1.0" - resolved "https://registry.npmjs.org/fs-minipass/-/fs-minipass-2.1.0.tgz" - integrity sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg== - dependencies: - minipass "^3.0.0" - fs.realpath@^1.0.0: version "1.0.0" resolved "https://registry.npmmirror.com/fs.realpath/-/fs.realpath-1.0.0.tgz" integrity sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw== +fsevents@^2.3.2, fsevents@~2.3.2: + version "2.3.3" + resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.3.3.tgz#cac6407785d03675a2a5e1a5305c697b347d90d6" + integrity sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw== + function-bind@^1.1.2: version "1.1.2" resolved "https://registry.npmmirror.com/function-bind/-/function-bind-1.1.2.tgz" @@ -3298,21 +3240,6 @@ functions-have-names@^1.2.3: resolved "https://registry.npmjs.org/functions-have-names/-/functions-have-names-1.2.3.tgz" integrity sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ== -gauge@^3.0.0: - version "3.0.2" - resolved "https://registry.npmjs.org/gauge/-/gauge-3.0.2.tgz" - integrity sha512-+5J6MS/5XksCuXq++uFRsnUd7Ovu1XenbeuIuNRJxYWjgQbPuFhT14lAvsWfqfAmnwluf1OwMjz39HjfLPci0Q== - dependencies: - aproba "^1.0.3 || ^2.0.0" - color-support "^1.1.2" - console-control-strings "^1.0.0" - has-unicode "^2.0.1" - object-assign "^4.1.1" - signal-exit "^3.0.0" - string-width "^4.2.3" - strip-ansi "^6.0.1" - wide-align "^1.1.2" - gensync@^1.0.0-beta.2: version "1.0.0-beta.2" resolved "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz" @@ -3397,6 +3324,18 @@ glob-to-regexp@0.4.1: resolved "https://registry.npmmirror.com/glob-to-regexp/-/glob-to-regexp-0.4.1.tgz" integrity sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw== +glob@7.1.7, glob@^7.1.3: + version "7.1.7" + resolved "https://registry.npmmirror.com/glob/-/glob-7.1.7.tgz" + integrity sha512-OvD9ENzPLbegENnYP5UUfJIirTg4+XwMWGaQfQTY0JenxNvvIKP3U3/tAQSPIu/lHxXYSZmpXlUHeqAIdKzBLQ== + dependencies: + fs.realpath "^1.0.0" + inflight "^1.0.4" + inherits "2" + minimatch "^3.0.4" + once "^1.3.0" + path-is-absolute "^1.0.0" + glob@^10.3.10: version "10.4.5" resolved "https://registry.npmmirror.com/glob/-/glob-10.4.5.tgz" @@ -3409,18 +3348,6 @@ glob@^10.3.10: package-json-from-dist "^1.0.0" path-scurry "^1.11.1" -glob@^7.1.3, glob@7.1.7: - version "7.1.7" - resolved "https://registry.npmmirror.com/glob/-/glob-7.1.7.tgz" - integrity sha512-OvD9ENzPLbegENnYP5UUfJIirTg4+XwMWGaQfQTY0JenxNvvIKP3U3/tAQSPIu/lHxXYSZmpXlUHeqAIdKzBLQ== - dependencies: - fs.realpath "^1.0.0" - inflight "^1.0.4" - inherits "2" - minimatch "^3.0.4" - once "^1.3.0" - path-is-absolute "^1.0.0" - glob@^7.1.4: version "7.2.3" resolved "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz" @@ -3518,11 +3445,6 @@ has-tostringtag@^1.0.2: dependencies: has-symbols "^1.0.3" -has-unicode@^2.0.1: - version "2.0.1" - resolved "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz" - integrity sha512-8Rf9Y83NBReMnx0gFzA8JImQACstCYWUplepDa9xprwwtmgEZUF0h/i5xSA625zB/I37EtrswSST6OXxwaaIJQ== - hasown@^2.0.2: version "2.0.2" resolved "https://registry.npmmirror.com/hasown/-/hasown-2.0.2.tgz" @@ -3561,7 +3483,7 @@ http-proxy-agent@^5.0.0: agent-base "6" debug "4" -https-proxy-agent@^5.0.0, https-proxy-agent@^5.0.1: +https-proxy-agent@^5.0.1: version "5.0.1" resolved "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz" integrity sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA== @@ -3625,7 +3547,7 @@ inflight@^1.0.4: once "^1.3.0" wrappy "1" -inherits@^2.0.3, inherits@^2.0.4, inherits@2: +inherits@2, inherits@^2.0.3, inherits@^2.0.4: version "2.0.4" resolved "https://registry.npmmirror.com/inherits/-/inherits-2.0.4.tgz" integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== @@ -4222,7 +4144,7 @@ jest-resolve-dependencies@^29.7.0: jest-regex-util "^29.6.3" jest-snapshot "^29.7.0" -jest-resolve@*, jest-resolve@^29.7.0: +jest-resolve@^29.7.0: version "29.7.0" resolved "https://registry.npmjs.org/jest-resolve/-/jest-resolve-29.7.0.tgz" integrity sha512-IOVhZSrg+UvVAshDSDtHyFCCBUl/Q3AAJv8iZ6ZjnZ74xzvwuzLXid9IIIPgTnY62SJjfuupMKZsZQRsCvxEgA== @@ -4522,13 +4444,6 @@ jsonp@^0.2.1: object.assign "^4.1.4" object.values "^1.1.6" -katex@>=0.9: - version "0.16.22" - resolved "https://registry.npmmirror.com/katex/-/katex-0.16.22.tgz" - integrity sha512-XCHRdUw4lf3SKBaJe4EvgqIuWwkPSo9XoeO8GjQW94Bp7TWv9hNhzZjZ+OH9yf1UmLygb7DIT5GSFQiyt16zYg== - dependencies: - commander "^8.3.0" - katex@0.16.21: version "0.16.21" resolved "https://registry.npmjs.org/katex/-/katex-0.16.21.tgz" @@ -4672,13 +4587,6 @@ make-cancellable-promise@^1.3.1: resolved "https://registry.npmmirror.com/make-cancellable-promise/-/make-cancellable-promise-1.3.2.tgz" integrity sha512-GCXh3bq/WuMbS+Ky4JBPW1hYTOU+znU+Q5m9Pu+pI8EoUqIHk9+tviOKC6/qhHh8C4/As3tzJ69IF32kdz85ww== -make-dir@^3.1.0: - version "3.1.0" - resolved "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz" - integrity sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw== - dependencies: - semver "^6.0.0" - make-dir@^4.0.0: version "4.0.0" resolved "https://registry.npmjs.org/make-dir/-/make-dir-4.0.0.tgz" @@ -4708,7 +4616,7 @@ marked-highlight@^2.2.2: resolved "https://registry.npmjs.org/marked-highlight/-/marked-highlight-2.2.2.tgz" integrity sha512-KlHOP31DatbtPPXPaI8nx1KTrG3EW0Z5zewCwpUj65swbtKOTStteK3sNAjBqV75Pgo3fNEVNHeptg18mDuWgw== -marked@^16.0.0, "marked@>=4 <17": +marked@^16.0.0: version "16.4.0" resolved "https://registry.npmjs.org/marked/-/marked-16.4.0.tgz" integrity sha512-CTPAcRBq57cn3R8n3hwc2REddc28hjR7RzDXQ+lXLmMJYqn20BaI2cGw6QjgZGIgVfp2Wdfw4aMzgNteQ6qJgQ== @@ -4775,11 +4683,6 @@ mimic-function@^5.0.1: resolved "https://registry.npmmirror.com/mimic-function/-/mimic-function-5.0.1.tgz" integrity sha512-VP79XUPxV2CigYP3jWwAUFSku2aKqBH7uTAapFWCBqutsbmDo96KY5o8uh6U+/YSIn5OxJnXp73beVkpqMIGhA== -mimic-response@^2.0.0: - version "2.1.0" - resolved "https://registry.npmjs.org/mimic-response/-/mimic-response-2.1.0.tgz" - integrity sha512-wXqjST+SLt7R009ySCglWBCFpjUygmCIfD790/kVbiGmUgfYGuB14PiTd5DwVxSV4NcYHjzMkoj5LjQZwTQLEA== - mimic-response@^3.1.0: version "3.1.0" resolved "https://registry.npmjs.org/mimic-response/-/mimic-response-3.1.0.tgz" @@ -4790,6 +4693,13 @@ min-indent@^1.0.0: resolved "https://registry.npmjs.org/min-indent/-/min-indent-1.0.1.tgz" integrity sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg== +minimatch@9.0.3: + version "9.0.3" + resolved "https://registry.npmmirror.com/minimatch/-/minimatch-9.0.3.tgz" + integrity sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg== + dependencies: + brace-expansion "^2.0.1" + minimatch@^3.0.4, minimatch@^3.0.5, minimatch@^3.1.1, minimatch@^3.1.2: version "3.1.2" resolved "https://registry.npmmirror.com/minimatch/-/minimatch-3.1.2.tgz" @@ -4804,49 +4714,22 @@ minimatch@^9.0.4: dependencies: brace-expansion "^2.0.1" -minimatch@9.0.3: - version "9.0.3" - resolved "https://registry.npmmirror.com/minimatch/-/minimatch-9.0.3.tgz" - integrity sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg== - dependencies: - brace-expansion "^2.0.1" - minimist@^1.2.0, minimist@^1.2.3, minimist@^1.2.5, minimist@^1.2.6: version "1.2.8" resolved "https://registry.npmmirror.com/minimist/-/minimist-1.2.8.tgz" integrity sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA== -minipass@^3.0.0: - version "3.3.6" - resolved "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz" - integrity sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw== - dependencies: - yallist "^4.0.0" - "minipass@^5.0.0 || ^6.0.2 || ^7.0.0", minipass@^7.1.2: version "7.1.2" resolved "https://registry.npmmirror.com/minipass/-/minipass-7.1.2.tgz" integrity sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw== -minipass@^5.0.0: - version "5.0.0" - resolved "https://registry.npmjs.org/minipass/-/minipass-5.0.0.tgz" - integrity sha512-3FnjYuehv9k6ovOEbyOswadCDPX1piCfhV8ncmYtHOjuPwylVWsghTLo7rabjC3Rx5xD4HDx8Wm1xnMF7S5qFQ== - -minizlib@^2.1.1: - version "2.1.2" - resolved "https://registry.npmjs.org/minizlib/-/minizlib-2.1.2.tgz" - integrity sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg== - dependencies: - minipass "^3.0.0" - yallist "^4.0.0" - mkdirp-classic@^0.5.2, mkdirp-classic@^0.5.3: version "0.5.3" resolved "https://registry.npmjs.org/mkdirp-classic/-/mkdirp-classic-0.5.3.tgz" integrity sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A== -mkdirp@^1.0.3, mkdirp@^1.0.4: +mkdirp@^1.0.4: version "1.0.4" resolved "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz" integrity sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw== @@ -4861,16 +4744,16 @@ mrmime@^2.0.0: resolved "https://registry.npmmirror.com/mrmime/-/mrmime-2.0.0.tgz" integrity sha512-eu38+hdgojoyq63s+yTpN4XMBdt5l8HhMhc4VKLO9KM5caLIBvUm4thi7fFaxyTmCKeNnXZ5pAlBwCUnhA09uw== -ms@^2.1.1, ms@^2.1.3: - version "2.1.3" - resolved "https://registry.npmmirror.com/ms/-/ms-2.1.3.tgz" - integrity sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA== - ms@2.0.0: version "2.0.0" resolved "https://registry.npmmirror.com/ms/-/ms-2.0.0.tgz" integrity sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A== +ms@^2.1.1, ms@^2.1.3: + version "2.1.3" + resolved "https://registry.npmmirror.com/ms/-/ms-2.1.3.tgz" + integrity sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA== + mz@^2.7.0: version "2.7.0" resolved "https://registry.npmmirror.com/mz/-/mz-2.7.0.tgz" @@ -4880,11 +4763,6 @@ mz@^2.7.0: object-assign "^4.0.1" thenify-all "^1.0.0" -nan@^2.17.0: - version "2.26.2" - resolved "https://registry.npmjs.org/nan/-/nan-2.26.2.tgz" - integrity sha512-0tTvBTYkt3tdGw22nrAy50x7gpbGCCFH3AFcyS5WiUu7Eu4vWlri1woE6qHBSfy11vksDqkiwjOnlR7WV8G1Hw== - nanoid@^3.3.11, nanoid@^3.3.6: version "3.3.11" resolved "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz" @@ -4908,7 +4786,7 @@ next-sitemap@^1.9.12: "@corex/deepmerge" "^2.6.148" minimist "^1.2.5" -next@*, "next@^13.5.4 || ^14.0.3 || >=15.0.0-rc", next@^14.2.30, "next@>= 13": +next@^14.2.30: version "14.2.30" resolved "https://registry.npmjs.org/next/-/next-14.2.30.tgz" integrity sha512-+COdu6HQrHHFQ1S/8BBsCag61jZacmvbuL2avHvQFbWa2Ox7bE+d8FyNgxRLjXQ5wtPyQwEmk85js/AuaG2Sbg== @@ -4956,13 +4834,6 @@ node-fetch-native@^1.6.4: resolved "https://registry.npmjs.org/node-fetch-native/-/node-fetch-native-1.6.7.tgz" integrity sha512-g9yhqoedzIUm0nTnTqAQvueMPVOuIY16bqgAJJC8XOOubYFNwz6IER9qs0Gq2Xd0+CecCKFjtdDTMA4u4xG06Q== -node-fetch@^2.6.7: - version "2.7.0" - resolved "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz" - integrity sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A== - dependencies: - whatwg-url "^5.0.0" - node-int64@^0.4.0: version "0.4.0" resolved "https://registry.npmjs.org/node-int64/-/node-int64-0.4.0.tgz" @@ -4973,13 +4844,6 @@ node-releases@^2.0.19: resolved "https://registry.npmjs.org/node-releases/-/node-releases-2.0.19.tgz" integrity sha512-xxOWJsBKtzAq7DY0J+DTzuz58K8e7sJbdgwkbMWQe8UYB6ekmsQ45q0M/tJDsGaZmbC+l7n57UV8Hl5tHxO9uw== -nopt@^5.0.0: - version "5.0.0" - resolved "https://registry.npmjs.org/nopt/-/nopt-5.0.0.tgz" - integrity sha512-Tbj67rffqceeLpcRXrT7vKAN8CwfPeIBgM7E6iBkmKLV7bEMwpGgYLGv0jACUsECaa/vuxP0IjEont6umdMgtQ== - dependencies: - abbrev "1" - normalize-path@^3.0.0, normalize-path@~3.0.0: version "3.0.0" resolved "https://registry.npmmirror.com/normalize-path/-/normalize-path-3.0.0.tgz" @@ -5028,16 +4892,6 @@ npm-run-path@^4.0.1: dependencies: path-key "^3.0.0" -npmlog@^5.0.1: - version "5.0.1" - resolved "https://registry.npmjs.org/npmlog/-/npmlog-5.0.1.tgz" - integrity sha512-AqZtDUWOMKs1G/8lwylVjrdYgqA4d9nu8hc+0gzRxlDb1I10+FHBGMXs6aiQHFdCUUlqH99MUMuLfzWDNDtfxw== - dependencies: - are-we-there-yet "^2.0.0" - console-control-strings "^1.1.0" - gauge "^3.0.0" - set-blocking "^2.0.0" - nth-check@^2.0.1: version "2.1.1" resolved "https://registry.npmjs.org/nth-check/-/nth-check-2.1.1.tgz" @@ -5200,6 +5054,13 @@ p-limit@^3.0.2, p-limit@^3.1.0: dependencies: yocto-queue "^0.1.0" +p-limit@^7.3.0: + version "7.3.0" + resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-7.3.0.tgz#821398d91491c6b6a1340ecd09cdc402a9c8d0ee" + integrity sha512-7cIXg/Z0M5WZRblrsOla88S4wAK+zOQQWeBYfV3qJuJXMr+LnbYjaadrFaS0JILfEDPVqHyKnZ1Z/1d6J9VVUw== + dependencies: + yocto-queue "^1.2.1" + p-locate@^4.1.0: version "4.1.0" resolved "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz" @@ -5418,15 +5279,6 @@ postcss-value-parser@^4.0.0, postcss-value-parser@^4.2.0: resolved "https://registry.npmmirror.com/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz" integrity sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ== -postcss@^8.0.0, postcss@^8.1.0, postcss@^8.2.14, postcss@^8.4.21, postcss@^8.4.23, postcss@^8.4.47, postcss@^8.5.6, postcss@>=8.0.9: - version "8.5.6" - resolved "https://registry.npmjs.org/postcss/-/postcss-8.5.6.tgz" - integrity sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg== - dependencies: - nanoid "^3.3.11" - picocolors "^1.1.1" - source-map-js "^1.2.1" - postcss@8.4.31: version "8.4.31" resolved "https://registry.npmjs.org/postcss/-/postcss-8.4.31.tgz" @@ -5436,6 +5288,15 @@ postcss@8.4.31: picocolors "^1.0.0" source-map-js "^1.0.2" +postcss@^8.4.23, postcss@^8.4.47, postcss@^8.5.6: + version "8.5.6" + resolved "https://registry.npmjs.org/postcss/-/postcss-8.5.6.tgz" + integrity sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg== + dependencies: + nanoid "^3.3.11" + picocolors "^1.1.1" + source-map-js "^1.2.1" + prebuild-install@^7.1.3: version "7.1.3" resolved "https://registry.npmjs.org/prebuild-install/-/prebuild-install-7.1.3.tgz" @@ -5466,7 +5327,7 @@ prettier-linter-helpers@^1.0.0: dependencies: fast-diff "^1.1.2" -prettier@^3.6.2, prettier@>=3.0.0: +prettier@^3.6.2: version "3.6.2" resolved "https://registry.npmjs.org/prettier/-/prettier-3.6.2.tgz" integrity sha512-I7AIg5boAr5R0FFtJ6rCfD+LFsWHp81dolrFD8S79U9tb8Az2nGrJncnMSnys+bpQJfRUzqs9hnA81OAA3hCuQ== @@ -5516,6 +5377,11 @@ proxy-from-env@^1.1.0: resolved "https://registry.npmmirror.com/proxy-from-env/-/proxy-from-env-1.1.0.tgz" integrity sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg== +proxy-from-env@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/proxy-from-env/-/proxy-from-env-2.1.0.tgz#a7487568adad577cfaaa7e88c49cab3ab3081aba" + integrity sha512-cJ+oHTW1VAEa8cJslgmUZrc+sjRKgAKl3Zyse6+PV38hZe/V6Z14TbCuXcan9F9ghlz4QrFr2c92TNF82UkYHA== + psl@^1.1.33: version "1.15.0" resolved "https://registry.npmjs.org/psl/-/psl-1.15.0.tgz" @@ -5561,7 +5427,7 @@ rc@^1.2.7: minimist "^1.2.0" strip-json-comments "~2.0.1" -"react-dom@^0.14.0 || ^15.0.0 || ^16 || ^17 || ^18 || ^19", "react-dom@^16 || ^17 || ^18", "react-dom@^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0", "react-dom@^17.0.0 || ^18.0.0 || ^19.0.0", react-dom@^18.0.0, react-dom@^18.2.0, react-dom@^18.3.1, react-dom@>=16.8, react-dom@>=16.8.1, react-dom@>=18, "react-dom@>=18 || >=19.0.0-beta": +react-dom@^18.3.1: version "18.3.1" resolved "https://registry.npmmirror.com/react-dom/-/react-dom-18.3.1.tgz" integrity sha512-5m4nQKp+rZRb09LNH59GM4BxTh9251/ylbKIbpe7TpGxfJ+9kv6BLkLBXIjjspbgbnIBNqlI23tRnTWT0snUIw== @@ -5683,7 +5549,7 @@ react-tweet-embed@~2.0.0: resolved "https://registry.npmmirror.com/react-tweet-embed/-/react-tweet-embed-2.0.0.tgz" integrity sha512-g2kfPjSRTOKeJtaQF5EMuSTmp/q8I0qdDs/pZ2qLXZjCWExDT/JgjxSlyM65NyNzsz8072PDpvlO/sIXwwVpdQ== -"react@^0.14.0 || ^15.0.0 || ^16 || ^17 || ^18 || ^19", "react@^16 || ^17 || ^18", "react@^16.11.0 || ^17.0.0 || ^18.0.0", "react@^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0", "react@^17 || ^18 || ^19", "react@^17.0.0 || ^18.0.0 || ^19.0.0", "react@^18 || ^19 || ^19.0.0-rc", react@^18.0.0, react@^18.2.0, react@^18.3.1, "react@>= 16.8.0", "react@>= 16.8.0 || 17.x.x || ^18.0.0-0", react@>=16, react@>=16.8, react@>=16.8.1, react@>=17, react@>=18, "react@>=18 || >=19.0.0-beta", "react@15.x || 16.x": +react@^18.3.1: version "18.3.1" resolved "https://registry.npmmirror.com/react/-/react-18.3.1.tgz" integrity sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ== @@ -5697,7 +5563,7 @@ read-cache@^1.0.0: dependencies: pify "^2.3.0" -readable-stream@^3.1.1, readable-stream@^3.4.0, readable-stream@^3.6.0: +readable-stream@^3.1.1, readable-stream@^3.4.0: version "3.6.2" resolved "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz" integrity sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA== @@ -5909,11 +5775,6 @@ scheduler@^0.23.2: dependencies: loose-envify "^1.1.0" -semver@^6.0.0: - version "6.3.1" - resolved "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz" - integrity sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA== - semver@^6.1.0, semver@^6.3.0, semver@^6.3.1: version "6.3.1" resolved "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz" @@ -5929,17 +5790,7 @@ semver@^7.5.3: resolved "https://registry.npmjs.org/semver/-/semver-7.7.2.tgz" integrity sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA== -semver@^7.5.4: - version "7.6.3" - resolved "https://registry.npmmirror.com/semver/-/semver-7.6.3.tgz" - integrity sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A== - -semver@^7.6.0: - version "7.6.3" - resolved "https://registry.npmmirror.com/semver/-/semver-7.6.3.tgz" - integrity sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A== - -semver@^7.6.3: +semver@^7.5.4, semver@^7.6.0, semver@^7.6.3: version "7.6.3" resolved "https://registry.npmmirror.com/semver/-/semver-7.6.3.tgz" integrity sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A== @@ -5949,11 +5800,6 @@ server-only@0.0.1: resolved "https://registry.npmmirror.com/server-only/-/server-only-0.0.1.tgz" integrity sha512-qepMx2JxAa5jjfzxG79yPPq+8BuFToHd1hm7kI+Z4zAq1ftQiP7HcxMhDDItrbtwVeLg/cY2JnKnrcFkmiswNA== -set-blocking@^2.0.0: - version "2.0.0" - resolved "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz" - integrity sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw== - set-function-length@^1.2.2: version "1.2.2" resolved "https://registry.npmmirror.com/set-function-length/-/set-function-length-1.2.2.tgz" @@ -6037,7 +5883,7 @@ side-channel@^1.0.4, side-channel@^1.1.0: side-channel-map "^1.0.1" side-channel-weakmap "^1.0.2" -signal-exit@^3.0.0, signal-exit@^3.0.3, signal-exit@^3.0.7: +signal-exit@^3.0.3, signal-exit@^3.0.7: version "3.0.7" resolved "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz" integrity sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ== @@ -6052,15 +5898,6 @@ simple-concat@^1.0.0: resolved "https://registry.npmjs.org/simple-concat/-/simple-concat-1.0.1.tgz" integrity sha512-cSFtAPtRhljv69IK0hTVZQ+OfE9nePi/rtJmw5UjHeVyVroEqJXP1sFztKUy1qU+xvz3u/sfYJLa947b7nAN2Q== -simple-get@^3.0.3: - version "3.1.1" - resolved "https://registry.npmjs.org/simple-get/-/simple-get-3.1.1.tgz" - integrity sha512-CQ5LTKGfCpvE1K0n2us+kuMPbk/q0EKl82s4aheV9oXjFEz6W/Y7oQFVJuU6QG77hRT4Ghb5RURteF5vnWjupA== - dependencies: - decompress-response "^4.2.0" - once "^1.3.1" - simple-concat "^1.0.0" - simple-get@^4.0.0: version "4.0.1" resolved "https://registry.npmjs.org/simple-get/-/simple-get-4.0.1.tgz" @@ -6183,13 +6020,6 @@ streamsearch@^1.1.0: resolved "https://registry.npmmirror.com/streamsearch/-/streamsearch-1.1.0.tgz" integrity sha512-Mcc5wHehp9aXz1ax6bZUyY5afg9u2rv5cqQI3mRrYkGC8rW2hM02jWuwjtL++LS5qinSyhj2QfLyNsuc+VsExg== -string_decoder@^1.1.1: - 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== - dependencies: - safe-buffer "~5.2.0" - string-length@^4.0.1: version "4.0.2" resolved "https://registry.npmjs.org/string-length/-/string-length-4.0.2.tgz" @@ -6207,7 +6037,7 @@ string-length@^4.0.1: is-fullwidth-code-point "^3.0.0" strip-ansi "^6.0.1" -"string-width@^1.0.2 || 2 || 3 || 4", string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.3: +string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.3: version "4.2.3" resolved "https://registry.npmmirror.com/string-width/-/string-width-4.2.3.tgz" integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== @@ -6293,6 +6123,13 @@ string.prototype.trimstart@^1.0.8: define-properties "^1.2.1" es-object-atoms "^1.0.0" +string_decoder@^1.1.1: + 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== + dependencies: + safe-buffer "~5.2.0" + "strip-ansi-cjs@npm:strip-ansi@^6.0.1": version "6.0.1" resolved "https://registry.npmmirror.com/strip-ansi/-/strip-ansi-6.0.1.tgz" @@ -6459,18 +6296,6 @@ tar-stream@^2.1.4: inherits "^2.0.3" readable-stream "^3.1.1" -tar@^6.1.11: - version "6.2.1" - resolved "https://registry.npmjs.org/tar/-/tar-6.2.1.tgz" - integrity sha512-DZ4yORTwrbTj/7MZYq2w+/ZFdI6OZ/f9SFHR+71gIVUZhOQPHzVCLpvRnPgyaMpfWxxk/4ONva3GQSyNIKRv6A== - dependencies: - chownr "^2.0.0" - fs-minipass "^2.0.0" - minipass "^5.0.0" - minizlib "^2.1.1" - mkdirp "^1.0.3" - yallist "^4.0.0" - test-exclude@^6.0.0: version "6.0.0" resolved "https://registry.npmjs.org/test-exclude/-/test-exclude-6.0.0.tgz" @@ -6548,11 +6373,6 @@ tr46@^3.0.0: dependencies: punycode "^2.1.1" -tr46@~0.0.3: - version "0.0.3" - resolved "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz" - integrity sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw== - ts-api-utils@^1.0.1, ts-api-utils@^1.3.0: version "1.4.3" resolved "https://registry.npmmirror.com/ts-api-utils/-/ts-api-utils-1.4.3.tgz" @@ -6573,7 +6393,7 @@ tsconfig-paths@^3.15.0: minimist "^1.2.6" strip-bom "^3.0.0" -tslib@^2.0.3, tslib@^2.4.0, tslib@2.4.1: +tslib@2.4.1, tslib@^2.0.3, tslib@^2.4.0: version "2.4.1" resolved "https://registry.npmmirror.com/tslib/-/tslib-2.4.1.tgz" integrity sha512-tGyy4dAjRIEwI7BzsB0lynWgOpfqjUdq91XXAlIWD2OwKBH7oCl/GZG/HT4BOHrTlPMOASlMQ7veyTqpmRcrNA== @@ -6657,7 +6477,7 @@ typed-array-length@^1.0.7: possible-typed-array-names "^1.0.0" reflect.getprototypeof "^1.0.6" -typescript@*, typescript@>=3.3.1, typescript@>=4.2.0, typescript@5.6.2: +typescript@5.6.2: version "5.6.2" resolved "https://registry.npmmirror.com/typescript/-/typescript-5.6.2.tgz" integrity sha512-NW8ByodCSNCwZeghjN3o+JX5OFH0Ojg6sadjEKY4huZ52TqbJTJnDo5+Tw98lSy63NZvi4n+ez5m2u5d4PkZyw== @@ -6749,7 +6569,7 @@ v8-to-istanbul@^9.0.1: "@types/istanbul-lib-coverage" "^2.0.1" convert-source-map "^2.0.0" -vue@^3, vue@^3.5.0, vue@^3.5.17, vue@3.5.22: +vue@^3.5.17: version "3.5.22" resolved "https://registry.npmjs.org/vue/-/vue-3.5.22.tgz" integrity sha512-toaZjQ3a/G/mYaLSbV+QsQhIdMo9x5rrqIpYRObsJ6T/J+RyCSFwN2LHNVH9v8uIcljDNa3QzPVdv3Y6b9hAJQ== @@ -6781,16 +6601,26 @@ warning@^4.0.0, warning@^4.0.3: dependencies: loose-envify "^1.0.0" -webidl-conversions@^3.0.0: - version "3.0.1" - resolved "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz" - integrity sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ== - webidl-conversions@^7.0.0: version "7.0.0" resolved "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-7.0.0.tgz" integrity sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g== +webpack-bundle-analyzer@4.3.0: + version "4.3.0" + resolved "https://registry.npmmirror.com/webpack-bundle-analyzer/-/webpack-bundle-analyzer-4.3.0.tgz" + integrity sha512-J3TPm54bPARx6QG8z4cKBszahnUglcv70+N+8gUqv2I5KOFHJbzBiLx+pAp606so0X004fxM7hqRu10MLjJifA== + dependencies: + acorn "^8.0.4" + acorn-walk "^8.0.0" + chalk "^4.1.0" + commander "^6.2.0" + gzip-size "^6.0.0" + lodash "^4.17.20" + opener "^1.5.2" + sirv "^1.0.7" + ws "^7.3.1" + webpack-bundle-analyzer@^4.5.0: version "4.10.2" resolved "https://registry.npmmirror.com/webpack-bundle-analyzer/-/webpack-bundle-analyzer-4.10.2.tgz" @@ -6809,21 +6639,6 @@ webpack-bundle-analyzer@^4.5.0: sirv "^2.0.3" ws "^7.3.1" -webpack-bundle-analyzer@4.3.0: - version "4.3.0" - resolved "https://registry.npmmirror.com/webpack-bundle-analyzer/-/webpack-bundle-analyzer-4.3.0.tgz" - integrity sha512-J3TPm54bPARx6QG8z4cKBszahnUglcv70+N+8gUqv2I5KOFHJbzBiLx+pAp606so0X004fxM7hqRu10MLjJifA== - dependencies: - acorn "^8.0.4" - acorn-walk "^8.0.0" - chalk "^4.1.0" - commander "^6.2.0" - gzip-size "^6.0.0" - lodash "^4.17.20" - opener "^1.5.2" - sirv "^1.0.7" - ws "^7.3.1" - whatwg-encoding@^2.0.0: version "2.0.0" resolved "https://registry.npmjs.org/whatwg-encoding/-/whatwg-encoding-2.0.0.tgz" @@ -6844,14 +6659,6 @@ whatwg-url@^11.0.0: tr46 "^3.0.0" webidl-conversions "^7.0.0" -whatwg-url@^5.0.0: - version "5.0.0" - resolved "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz" - integrity sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw== - dependencies: - tr46 "~0.0.3" - webidl-conversions "^3.0.0" - which-boxed-primitive@^1.0.2, which-boxed-primitive@^1.1.0, which-boxed-primitive@^1.1.1: version "1.1.1" resolved "https://registry.npmjs.org/which-boxed-primitive/-/which-boxed-primitive-1.1.1.tgz" @@ -6912,13 +6719,6 @@ which@^2.0.1: dependencies: isexe "^2.0.0" -wide-align@^1.1.2: - version "1.1.5" - resolved "https://registry.npmjs.org/wide-align/-/wide-align-1.1.5.tgz" - integrity sha512-eDMORYaPNZ4sQIuuYPDHdQvf4gyCF9rEEV/yPxGfwPkRodwEgiMUUXTx/dex+Me0wxx53S+NgUHaP7y3MGlDmg== - dependencies: - string-width "^1.0.2 || 2 || 3 || 4" - word-wrap@^1.2.5: version "1.2.5" resolved "https://registry.npmmirror.com/word-wrap/-/word-wrap-1.2.5.tgz" @@ -7006,11 +6806,6 @@ yallist@^3.0.2: resolved "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz" integrity sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g== -yallist@^4.0.0: - version "4.0.0" - resolved "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz" - integrity sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A== - yaml@^2.2.2, yaml@^2.3.4: version "2.6.1" resolved "https://registry.npmmirror.com/yaml/-/yaml-2.6.1.tgz" @@ -7038,3 +6833,8 @@ yocto-queue@^0.1.0: version "0.1.0" resolved "https://registry.npmmirror.com/yocto-queue/-/yocto-queue-0.1.0.tgz" integrity sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q== + +yocto-queue@^1.2.1: + version "1.2.2" + resolved "https://registry.yarnpkg.com/yocto-queue/-/yocto-queue-1.2.2.tgz#3e09c95d3f1aa89a58c114c99223edf639152c00" + integrity sha512-4LCcse/U2MHZ63HAJVE+v71o7yOdIe4cZ70Wpf8D/IyjDKYQLV5GD46B+hSTjJsvV5PztjvHoU580EftxjDZFQ==