mirror of
https://github.com/SrIzan10/nextbooru.git
synced 2026-06-06 00:57:02 +00:00
feat: very barebones search
This commit is contained in:
@@ -8,4 +8,14 @@ services:
|
||||
volumes:
|
||||
- ./psql:/var/lib/postgresql/data
|
||||
ports:
|
||||
- 5555:5432
|
||||
- 5555:5432
|
||||
dragonfly:
|
||||
image: 'docker.dragonflydb.io/dragonflydb/dragonfly'
|
||||
ulimits:
|
||||
memlock: -1
|
||||
ports:
|
||||
- "6379:6379"
|
||||
environment:
|
||||
DRAGONFLY_PASSWORD: dfsjhkdswkjntelsmldbfvsgknl5t
|
||||
volumes:
|
||||
- ./draognfly:/data
|
||||
@@ -25,6 +25,7 @@
|
||||
"class-variance-authority": "^0.7.1",
|
||||
"clsx": "^2.1.0",
|
||||
"cron": "^3.3.2",
|
||||
"ioredis": "^5.4.2",
|
||||
"lucia": "^3.1.1",
|
||||
"lucide-react": "^0.368.0",
|
||||
"minio": "^8.0.3",
|
||||
@@ -37,6 +38,7 @@
|
||||
"sonner": "^1.4.41",
|
||||
"tailwind-merge": "^2.2.2",
|
||||
"tailwindcss-animate": "^1.0.7",
|
||||
"unstorage": "^1.14.4",
|
||||
"zod": "^3.24.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
|
||||
29
src/app/(public)/api/autocomplete/route.ts
Normal file
29
src/app/(public)/api/autocomplete/route.ts
Normal file
@@ -0,0 +1,29 @@
|
||||
import ephemeralStorage from '@/lib/services/ephemeralStorage';
|
||||
import type { NextRequest } from 'next/server';
|
||||
|
||||
export async function GET(request: NextRequest) {
|
||||
const searchParams = request.nextUrl.searchParams;
|
||||
const query = searchParams.get('q')!.toLowerCase().split(' ').at(-1);
|
||||
if (query !== '' && !query) {
|
||||
return new Response('Invalid query', { status: 400 });
|
||||
}
|
||||
|
||||
const tags = await ephemeralStorage.keys('tag');
|
||||
const filteredTags = tags.filter((tag) => tag.replace('tag:', '').includes(query));
|
||||
|
||||
const mappedTags = await Promise.all(
|
||||
filteredTags.map(async (t) => {
|
||||
const getTag = parseInt((await ephemeralStorage.get(t)) as string);
|
||||
return { tag: t.replace('tag:', ''), count: getTag };
|
||||
})
|
||||
);
|
||||
mappedTags.sort((a, b) => {
|
||||
return b.count - a.count;
|
||||
});
|
||||
|
||||
return new Response(JSON.stringify(mappedTags), {
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
});
|
||||
}
|
||||
@@ -4,14 +4,24 @@ import { type NextRequest } from 'next/server';
|
||||
export async function GET(request: NextRequest) {
|
||||
const searchParams = request.nextUrl.searchParams;
|
||||
const page = parseInt(searchParams.get('page')!);
|
||||
const query = searchParams.get('q')?.trim().split(' ')!;
|
||||
if (page && isNaN(page)) {
|
||||
return new Response('Invalid page number', { status: 400 });
|
||||
}
|
||||
if (!query.length) {
|
||||
return new Response('Invalid query', { status: 400 });
|
||||
}
|
||||
|
||||
// TODO: negative tags
|
||||
const queryPosts = await prisma.post.findMany({
|
||||
take: 30,
|
||||
skip: page * 30,
|
||||
orderBy: { createdAt: 'desc' },
|
||||
where: {
|
||||
tags: {
|
||||
hasEvery: query[0] !== '' ? query : [],
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
return new Response(JSON.stringify(queryPosts), {
|
||||
|
||||
@@ -1,10 +1,6 @@
|
||||
import PostBrowser from '@/components/app/PostBrowser/PostBrowser';
|
||||
import prisma from '@/lib/db';
|
||||
import Image from 'next/image';
|
||||
import Link from 'next/link';
|
||||
|
||||
export default async function Home() {
|
||||
const posts = await prisma.post.findMany({ take: 30, orderBy: { createdAt: 'desc' } });
|
||||
return (
|
||||
<div className="text-center pt-2">
|
||||
<h1>This is nextbooru</h1>
|
||||
|
||||
@@ -6,16 +6,24 @@ import { Skeleton } from '@/components/ui/skeleton';
|
||||
import { Post } from '@prisma/client';
|
||||
import Image from 'next/image';
|
||||
import Link from 'next/link';
|
||||
import Search from '../Search/Search';
|
||||
|
||||
export default function PostBrowser() {
|
||||
const [page, setPage] = React.useState(0);
|
||||
const [loading, setLoading] = React.useState(false);
|
||||
const [hasMore, setHasMore] = React.useState(true);
|
||||
const [posts, setPosts] = React.useState<Post[]>([]);
|
||||
const [search, setSearch] = React.useState('');
|
||||
|
||||
const next = async () => {
|
||||
const next = async (reset = false, query = search) => {
|
||||
setLoading(true);
|
||||
const res = await fetch(`/api/posts?page=${page}`);
|
||||
const currentPage = reset ? 0 : page;
|
||||
if (reset) {
|
||||
setPosts([]);
|
||||
setPage(0);
|
||||
setHasMore(true);
|
||||
}
|
||||
const res = await fetch(`/api/posts?page=${currentPage}&q=${query}`);
|
||||
const newImages = await res.json();
|
||||
if (!newImages.length) {
|
||||
setHasMore(false);
|
||||
@@ -28,37 +36,42 @@ export default function PostBrowser() {
|
||||
};
|
||||
|
||||
return (
|
||||
<InfiniteScroll isLoading={loading} hasMore={hasMore} next={next} threshold={0}>
|
||||
<div className="grid grid-cols-2 sm:grid-cols-3 md:grid-cols-4 lg:grid-cols-5 xl:grid-cols-6 gap-4 p-4">
|
||||
{posts.map((post) => (
|
||||
<div key={post.id} className="relative aspect-square">
|
||||
<Image
|
||||
width={176}
|
||||
height={176}
|
||||
src={post.imageUrl}
|
||||
alt=""
|
||||
className="w-full h-full object-contain border-2 rounded-md border-dashed pointer-events-none"
|
||||
blurDataURL={`data:image/jpeg;base64,${post.previewHash}`}
|
||||
placeholder="blur"
|
||||
/>
|
||||
<Link
|
||||
href={`/post/${post.id}`}
|
||||
className="absolute inset-0 z-10 hover:bg-muted-foreground/5"
|
||||
aria-label="View post details"
|
||||
/>
|
||||
</div>
|
||||
))}
|
||||
{loading &&
|
||||
Array.from({ length: page === 0 ? 12 : 2 }).map((_, i) => (
|
||||
<div key={i} className="relative aspect-square">
|
||||
<Skeleton className="w-full h-full rounded-md border-2 border-dashed" />
|
||||
<>
|
||||
<Search onSearch={q => {
|
||||
setSearch(q);
|
||||
next(true, q);
|
||||
}} />
|
||||
<InfiniteScroll isLoading={loading} hasMore={hasMore} next={next} threshold={0}>
|
||||
<div className="grid grid-cols-2 sm:grid-cols-3 md:grid-cols-4 lg:grid-cols-5 xl:grid-cols-6 gap-4 p-4">
|
||||
{posts.map((post) => (
|
||||
<div key={post.id} className="relative aspect-square">
|
||||
<Image
|
||||
width={176}
|
||||
height={176}
|
||||
src={post.imageUrl}
|
||||
alt=""
|
||||
className="w-full h-full object-contain border-2 rounded-md border-dashed pointer-events-none"
|
||||
blurDataURL={`data:image/jpeg;base64,${post.previewHash}`}
|
||||
placeholder="blur"
|
||||
/>
|
||||
<Link
|
||||
href={`/post/${post.id}`}
|
||||
className="absolute inset-0 z-10 hover:bg-muted-foreground/5"
|
||||
aria-label="View post details"
|
||||
/>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
{loading &&
|
||||
Array.from({ length: page === 0 ? 12 : 2 }).map((_, i) => (
|
||||
<div key={i} className="relative aspect-square">
|
||||
<Skeleton className="w-full h-full rounded-md border-2 border-dashed" />
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
|
||||
{/* interactionobserver sentinel */}
|
||||
<div style={{ height: 1 }} />
|
||||
</InfiniteScroll>
|
||||
{/* interactionobserver sentinel */}
|
||||
<div style={{ height: 1 }} />
|
||||
</InfiniteScroll>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
108
src/components/app/Search/Search.tsx
Normal file
108
src/components/app/Search/Search.tsx
Normal file
@@ -0,0 +1,108 @@
|
||||
'use client'
|
||||
|
||||
import React, { useState, useEffect, useRef } from 'react'
|
||||
import { Input } from "@/components/ui/input"
|
||||
import { Button } from "@/components/ui/button"
|
||||
import { Search as SearchIcon } from 'lucide-react'
|
||||
|
||||
export default function Search(props: Props) {
|
||||
const [input, setInput] = useState('')
|
||||
const [suggestions, setSuggestions] = useState<{ tag: string, count: string }[]>([])
|
||||
const [selectedIndex, setSelectedIndex] = useState(-1)
|
||||
const inputRef = useRef<HTMLInputElement>(null)
|
||||
|
||||
useEffect(() => {
|
||||
const fetchSuggestions = async () => {
|
||||
const lastTag = input.split(' ').pop() || ''
|
||||
if (lastTag.length > 0) {
|
||||
try {
|
||||
const response = await fetch(`/api/autocomplete?q=${encodeURIComponent(lastTag)}`)
|
||||
if (response.ok) {
|
||||
const data = await response.json()
|
||||
setSuggestions(data.slice(0, 5))
|
||||
} else {
|
||||
setSuggestions([])
|
||||
}
|
||||
} catch (error) {
|
||||
setSuggestions([])
|
||||
}
|
||||
} else {
|
||||
setSuggestions([])
|
||||
}
|
||||
setSelectedIndex(-1)
|
||||
}
|
||||
|
||||
fetchSuggestions()
|
||||
}, [input])
|
||||
|
||||
const handleKeyDown = (e: React.KeyboardEvent<HTMLInputElement>) => {
|
||||
if (e.key === 'ArrowDown') {
|
||||
setSelectedIndex(prev => (prev < suggestions.length - 1 ? prev + 1 : prev))
|
||||
e.preventDefault()
|
||||
} else if (e.key === 'ArrowUp') {
|
||||
setSelectedIndex(prev => (prev > 0 ? prev - 1 : -1))
|
||||
e.preventDefault()
|
||||
} else if (e.key === 'Enter' && selectedIndex >= 0) {
|
||||
const tags = input.split(' ').slice(0, -1)
|
||||
tags.push(suggestions[selectedIndex].tag)
|
||||
setInput(tags.join(' ') + ' ')
|
||||
setSuggestions([])
|
||||
e.preventDefault()
|
||||
} else if (e.key === 'Escape') {
|
||||
setSuggestions([])
|
||||
} else if (e.key === 'Enter') {
|
||||
props.onSearch(input)
|
||||
}
|
||||
}
|
||||
|
||||
const handleSuggestionClick = (suggestion: string) => {
|
||||
const tags = input.split(' ').slice(0, -1)
|
||||
tags.push(suggestion)
|
||||
setInput(tags.join(' ') + ' ')
|
||||
setSuggestions([])
|
||||
inputRef.current?.focus()
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="w-full max-w-md mx-auto mt-10">
|
||||
<div className="relative">
|
||||
<Input
|
||||
ref={inputRef}
|
||||
type="text"
|
||||
placeholder="Enter tags..."
|
||||
value={input}
|
||||
onChange={(e) => setInput(e.target.value)}
|
||||
onKeyDown={handleKeyDown}
|
||||
className="pr-10 font-mono"
|
||||
/>
|
||||
<Button
|
||||
size="icon"
|
||||
className="absolute right-0 top-0 h-full"
|
||||
onClick={() => props.onSearch(input)}
|
||||
>
|
||||
<SearchIcon className="h-4 w-4" />
|
||||
<span className="sr-only">Search</span>
|
||||
</Button>
|
||||
</div>
|
||||
{suggestions.length > 0 && (
|
||||
<ul className="mt-1 bg-background border rounded-md shadow-lg max-h-60 overflow-auto">
|
||||
{suggestions.map((suggestion, index) => (
|
||||
<li
|
||||
key={suggestion.tag}
|
||||
className={`px-4 py-2 cursor-pointer hover:bg-muted ${
|
||||
index === selectedIndex ? 'bg-muted' : ''
|
||||
}`}
|
||||
onClick={() => handleSuggestionClick(suggestion.tag)}
|
||||
>
|
||||
{suggestion.tag} ({suggestion.count})
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
interface Props {
|
||||
onSearch: (q: string) => void
|
||||
}
|
||||
@@ -1,16 +1,16 @@
|
||||
import prisma from './lib/db';
|
||||
import ephemeralStorage from './lib/services/ephemeralStorage';
|
||||
|
||||
export async function register() {
|
||||
if (process.env.SAFEBOORU_PULL !== 'true') return;
|
||||
if (process.env.NEXT_RUNTIME === 'nodejs') {
|
||||
const { CronJob } = await import('cron');
|
||||
const crypto = await import('crypto');
|
||||
const { generateId } = await import('lucia');
|
||||
const fs = await import('fs/promises');
|
||||
const { default: hashImage } = await import('@/lib/hashImage');
|
||||
const minio = (await import('@/lib/services/minio')).default;
|
||||
|
||||
const job = async () => {
|
||||
const { performance } = await import('perf_hooks');
|
||||
|
||||
const safebooruJob = async () => {
|
||||
if (process.env.SAFEBOORU_PULL !== 'true') return;
|
||||
console.log('Deleting prior safebooru posts and accounts...');
|
||||
await prisma.post.deleteMany({
|
||||
where: { author: { username: { startsWith: 'safebooru-' } } },
|
||||
@@ -28,14 +28,13 @@ export async function register() {
|
||||
console.log('Pulling safebooru images...');
|
||||
console.log('Fetching...');
|
||||
const res = await fetch(
|
||||
'https://safebooru.org/index.php?page=dapi&s=post&q=index&json=1&limit=40',
|
||||
'https://safebooru.org/index.php?page=dapi&s=post&q=index&json=1&limit=300',
|
||||
{ headers: { 'User-Agent': 'nextbooru' } }
|
||||
);
|
||||
const posts = await res.json();
|
||||
|
||||
const genId = generateId(6);
|
||||
|
||||
|
||||
console.log('Creating account...');
|
||||
const genId = generateId(6);
|
||||
const account = await prisma.user.create({
|
||||
data: {
|
||||
username: `safebooru-${genId}`,
|
||||
@@ -49,7 +48,6 @@ export async function register() {
|
||||
const savedFilename = `http${minioIsSSL ? 's' : ''}://${process.env.MINIO_ENDPOINT}/${
|
||||
process.env.MINIO_BUCKET
|
||||
}/safebooru-${genId}-${post.id}.jpg`;
|
||||
//await fs.writeFile(savedFilename, new Uint8Array(imageUrl));
|
||||
await minio.putObject(
|
||||
process.env.MINIO_BUCKET!,
|
||||
`safebooru-${genId}-${post.id}.jpg`,
|
||||
@@ -68,9 +66,30 @@ export async function register() {
|
||||
console.log(`Downloaded id ${post.id}`);
|
||||
}
|
||||
};
|
||||
// await safebooruJob();
|
||||
|
||||
await job();
|
||||
const writeTagsToEphemeral = async () => {
|
||||
// TODO: move tags to another table. this is a temporary and inefficient solution.
|
||||
const perfStart = performance.now();
|
||||
const posts = await prisma.post.findMany({ select: { tags: true } });
|
||||
const tags = posts.flatMap((post) => post.tags);
|
||||
|
||||
// new CronJob('0 */2 * * *', async () => await job(), null, true);
|
||||
const occurrences: Record<string, number> = {};
|
||||
for (const tag of tags) {
|
||||
if (occurrences[tag]) {
|
||||
occurrences[tag]++;
|
||||
} else {
|
||||
occurrences[tag] = 1;
|
||||
}
|
||||
}
|
||||
|
||||
for (const [tag, count] of Object.entries(occurrences)) {
|
||||
await ephemeralStorage.set(`tag:${tag}`, count);
|
||||
}
|
||||
const perfEnd = performance.now();
|
||||
|
||||
console.log(`Writing tags to ephemeral took ${Math.round(perfEnd - perfStart)}ms`);
|
||||
}
|
||||
await writeTagsToEphemeral();
|
||||
}
|
||||
}
|
||||
|
||||
12
src/lib/services/ephemeralStorage.ts
Normal file
12
src/lib/services/ephemeralStorage.ts
Normal file
@@ -0,0 +1,12 @@
|
||||
import { createStorage } from "unstorage";
|
||||
import redisDriver from 'unstorage/drivers/redis';
|
||||
|
||||
const ephemeralStorage = createStorage({
|
||||
driver: redisDriver({
|
||||
host: process.env.REDIS_HOST,
|
||||
port: Number(process.env.REDIS_PORT),
|
||||
password: process.env.REDIS_PASSWORD,
|
||||
}),
|
||||
});
|
||||
|
||||
export default ephemeralStorage;
|
||||
183
yarn.lock
183
yarn.lock
@@ -473,6 +473,11 @@
|
||||
resolved "https://registry.yarnpkg.com/@img/sharp-win32-x64/-/sharp-win32-x64-0.33.5.tgz#56f00962ff0c4e0eb93d34a047d29fa995e3e342"
|
||||
integrity sha512-MpY/o8/8kj+EcnxwvrP4aTJSWw/aZ7JIGR4aBeZkZw5B7/Jn+tY9/VNwtcoGmdT7GfggGIU4kygOMSbYnOrAbg==
|
||||
|
||||
"@ioredis/commands@^1.1.1":
|
||||
version "1.2.0"
|
||||
resolved "https://registry.yarnpkg.com/@ioredis/commands/-/commands-1.2.0.tgz#6d61b3097470af1fdbbe622795b8921d42018e11"
|
||||
integrity sha512-Sx1pU8EM64o2BrqNpEO1CNLtKQwyhuXuqyfH7oGKCk+1a33d2r5saW8zNwm3j6BTExtjrv2BxTgzzkMwts6vGg==
|
||||
|
||||
"@isaacs/cliui@^8.0.2":
|
||||
version "8.0.2"
|
||||
resolved "https://registry.yarnpkg.com/@isaacs/cliui/-/cliui-8.0.2.tgz#b37667b7bc181c168782259bab42474fbf52b550"
|
||||
@@ -1430,7 +1435,7 @@ any-promise@^1.0.0:
|
||||
resolved "https://registry.yarnpkg.com/any-promise/-/any-promise-1.3.0.tgz#abc6afeedcea52e809cdc0376aed3ce39635d17f"
|
||||
integrity sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A==
|
||||
|
||||
anymatch@~3.1.2:
|
||||
anymatch@^3.1.3, anymatch@~3.1.2:
|
||||
version "3.1.3"
|
||||
resolved "https://registry.yarnpkg.com/anymatch/-/anymatch-3.1.3.tgz#790c58b19ba1720a84205b57c618d5ad8524973e"
|
||||
integrity sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==
|
||||
@@ -1760,7 +1765,7 @@ chalk@^5.0.0:
|
||||
resolved "https://registry.yarnpkg.com/chalk/-/chalk-5.4.1.tgz#1b48bf0963ec158dce2aacf69c093ae2dd2092d8"
|
||||
integrity sha512-zgVZuo2WcZgfUEmsn6eO3kINexW8RAE4maiQ8QNs8CtpPCSyMiYsULR3HQYkm3w8FIA3SberyMJMSldGsW+U3w==
|
||||
|
||||
chokidar@^3.5.3:
|
||||
chokidar@^3.5.3, chokidar@^3.6.0:
|
||||
version "3.6.0"
|
||||
resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-3.6.0.tgz#197c6cc669ef2a8dc5e7b4d97ee4e092c3eb0d5b"
|
||||
integrity sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==
|
||||
@@ -1809,6 +1814,11 @@ clsx@^2.1.0, clsx@^2.1.1:
|
||||
resolved "https://registry.yarnpkg.com/clsx/-/clsx-2.1.1.tgz#eed397c9fd8bd882bfb18deab7102049a2f32999"
|
||||
integrity sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==
|
||||
|
||||
cluster-key-slot@^1.1.0:
|
||||
version "1.1.2"
|
||||
resolved "https://registry.yarnpkg.com/cluster-key-slot/-/cluster-key-slot-1.1.2.tgz#88ddaa46906e303b5de30d3153b7d9fe0a0c19ac"
|
||||
integrity sha512-RMr0FhtfXemyinomL4hrWcYJxmX6deFdCxpJzhDttxgO1+bcCnkk+9drydLVDmAMG7NE6aN/fl4F7ucU/90gAA==
|
||||
|
||||
code-block-writer@^12.0.0:
|
||||
version "12.0.0"
|
||||
resolved "https://registry.yarnpkg.com/code-block-writer/-/code-block-writer-12.0.0.tgz#4dd58946eb4234105aff7f0035977b2afdc2a770"
|
||||
@@ -1857,11 +1867,21 @@ concat-map@0.0.1:
|
||||
resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b"
|
||||
integrity sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==
|
||||
|
||||
consola@^3.2.3:
|
||||
version "3.3.3"
|
||||
resolved "https://registry.yarnpkg.com/consola/-/consola-3.3.3.tgz#0dd8a2314b0f7bf18a49064138ad685f3346543d"
|
||||
integrity sha512-Qil5KwghMzlqd51UXM0b6fyaGHtOC22scxrwrz4A2882LyUMwQjnvaedN1HAeXzphspQ6CpHkzMAWxBTUruDLg==
|
||||
|
||||
convert-source-map@^2.0.0:
|
||||
version "2.0.0"
|
||||
resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-2.0.0.tgz#4b560f649fc4e918dd0ab75cf4961e8bc882d82a"
|
||||
integrity sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==
|
||||
|
||||
cookie-es@^1.2.2:
|
||||
version "1.2.2"
|
||||
resolved "https://registry.yarnpkg.com/cookie-es/-/cookie-es-1.2.2.tgz#18ceef9eb513cac1cb6c14bcbf8bdb2679b34821"
|
||||
integrity sha512-+W7VmiVINB+ywl1HGXJXmrqkOhpKrIiVZV6tQuV54ZyQC7MMuBt81Vc336GMLoHBq5hV/F9eXgt5Mnx0Rha5Fg==
|
||||
|
||||
cosmiconfig@^8.1.3:
|
||||
version "8.3.6"
|
||||
resolved "https://registry.yarnpkg.com/cosmiconfig/-/cosmiconfig-8.3.6.tgz#060a2b871d66dba6c8538ea1118ba1ac16f5fae3"
|
||||
@@ -1898,6 +1918,13 @@ cross-spawn@^7.0.3:
|
||||
shebang-command "^2.0.0"
|
||||
which "^2.0.1"
|
||||
|
||||
"crossws@>=0.2.0 <0.4.0":
|
||||
version "0.3.1"
|
||||
resolved "https://registry.yarnpkg.com/crossws/-/crossws-0.3.1.tgz#7980e0b6688fe23286661c3ab8deeccbaa05ca86"
|
||||
integrity sha512-HsZgeVYaG+b5zA+9PbIPGq4+J/CJynJuearykPsXx4V/eMhyQ5EDVg3Ak2FBZtVXCiOLu/U7IiwDHTr9MA+IKw==
|
||||
dependencies:
|
||||
uncrypto "^0.1.3"
|
||||
|
||||
cssesc@^3.0.0:
|
||||
version "3.0.0"
|
||||
resolved "https://registry.yarnpkg.com/cssesc/-/cssesc-3.0.0.tgz#37741919903b868565e1c09ea747445cd18983ee"
|
||||
@@ -2006,11 +2033,26 @@ define-properties@^1.1.3, define-properties@^1.2.0, define-properties@^1.2.1:
|
||||
has-property-descriptors "^1.0.0"
|
||||
object-keys "^1.1.1"
|
||||
|
||||
defu@^6.1.4:
|
||||
version "6.1.4"
|
||||
resolved "https://registry.yarnpkg.com/defu/-/defu-6.1.4.tgz#4e0c9cf9ff68fe5f3d7f2765cc1a012dfdcb0479"
|
||||
integrity sha512-mEQCMmwJu317oSz8CwdIOdwf3xMif1ttiM8LTufzc3g6kR+9Pe236twL8j3IYT1F7GfRgGcW6MWxzZjLIkuHIg==
|
||||
|
||||
denque@^2.1.0:
|
||||
version "2.1.0"
|
||||
resolved "https://registry.yarnpkg.com/denque/-/denque-2.1.0.tgz#e93e1a6569fb5e66f16a3c2a2964617d349d6ab1"
|
||||
integrity sha512-HVQE3AAb/pxF8fQAoiqpvg9i3evqug3hoiwakOyZAwJm+6vZehbkYXZ0l4JxS+I3QxM97v5aaRNhj8v5oBhekw==
|
||||
|
||||
dequal@^2.0.3:
|
||||
version "2.0.3"
|
||||
resolved "https://registry.yarnpkg.com/dequal/-/dequal-2.0.3.tgz#2644214f1997d39ed0ee0ece72335490a7ac67be"
|
||||
integrity sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==
|
||||
|
||||
destr@^2.0.3:
|
||||
version "2.0.3"
|
||||
resolved "https://registry.yarnpkg.com/destr/-/destr-2.0.3.tgz#7f9e97cb3d16dbdca7be52aca1644ce402cfe449"
|
||||
integrity sha512-2N3BOUU4gYMpTP24s5rF5iP7BDr7uNTCs4ozw3kf/eKfvWSIu93GEBi5m427YoyJoeOzQ5smuu4nNAPGb8idSQ==
|
||||
|
||||
detect-libc@^2.0.3:
|
||||
version "2.0.3"
|
||||
resolved "https://registry.yarnpkg.com/detect-libc/-/detect-libc-2.0.3.tgz#f0cd503b40f9939b894697d19ad50895e30cf700"
|
||||
@@ -2792,6 +2834,22 @@ graphemer@^1.4.0:
|
||||
resolved "https://registry.yarnpkg.com/graphemer/-/graphemer-1.4.0.tgz#fb2f1d55e0e3a1849aeffc90c4fa0dd53a0e66c6"
|
||||
integrity sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==
|
||||
|
||||
h3@^1.13.0:
|
||||
version "1.13.0"
|
||||
resolved "https://registry.yarnpkg.com/h3/-/h3-1.13.0.tgz#b5347a8936529794b6754b440e26c0ab8a60dceb"
|
||||
integrity sha512-vFEAu/yf8UMUcB4s43OaDaigcqpQd14yanmOsn+NcRX3/guSKncyE2rOYhq8RIchgJrPSs/QiIddnTTR1ddiAg==
|
||||
dependencies:
|
||||
cookie-es "^1.2.2"
|
||||
crossws ">=0.2.0 <0.4.0"
|
||||
defu "^6.1.4"
|
||||
destr "^2.0.3"
|
||||
iron-webcrypto "^1.2.1"
|
||||
ohash "^1.1.4"
|
||||
radix3 "^1.1.2"
|
||||
ufo "^1.5.4"
|
||||
uncrypto "^0.1.3"
|
||||
unenv "^1.10.0"
|
||||
|
||||
has-bigints@^1.0.1, has-bigints@^1.0.2:
|
||||
version "1.0.2"
|
||||
resolved "https://registry.yarnpkg.com/has-bigints/-/has-bigints-1.0.2.tgz#0871bd3e3d51626f6ca0966668ba35d5602d6eaa"
|
||||
@@ -2903,11 +2961,31 @@ invariant@^2.2.4:
|
||||
dependencies:
|
||||
loose-envify "^1.0.0"
|
||||
|
||||
ioredis@^5.4.2:
|
||||
version "5.4.2"
|
||||
resolved "https://registry.yarnpkg.com/ioredis/-/ioredis-5.4.2.tgz#ebb6f1a10b825b2c0fb114763d7e82114a0bee6c"
|
||||
integrity sha512-0SZXGNGZ+WzISQ67QDyZ2x0+wVxjjUndtD8oSeik/4ajifeiRufed8fCb8QW8VMyi4MXcS+UO1k/0NGhvq1PAg==
|
||||
dependencies:
|
||||
"@ioredis/commands" "^1.1.1"
|
||||
cluster-key-slot "^1.1.0"
|
||||
debug "^4.3.4"
|
||||
denque "^2.1.0"
|
||||
lodash.defaults "^4.2.0"
|
||||
lodash.isarguments "^3.1.0"
|
||||
redis-errors "^1.2.0"
|
||||
redis-parser "^3.0.0"
|
||||
standard-as-callback "^2.1.0"
|
||||
|
||||
ipaddr.js@^2.0.1:
|
||||
version "2.2.0"
|
||||
resolved "https://registry.yarnpkg.com/ipaddr.js/-/ipaddr.js-2.2.0.tgz#d33fa7bac284f4de7af949638c9d68157c6b92e8"
|
||||
integrity sha512-Ag3wB2o37wslZS19hZqorUnrnzSkpOVy+IiiDEiTqNubEYpYuHWIf6K4psgN2ZWKExS4xhVCrRVfb/wfW8fWJA==
|
||||
|
||||
iron-webcrypto@^1.2.1:
|
||||
version "1.2.1"
|
||||
resolved "https://registry.yarnpkg.com/iron-webcrypto/-/iron-webcrypto-1.2.1.tgz#aa60ff2aa10550630f4c0b11fd2442becdb35a6f"
|
||||
integrity sha512-feOM6FaSr6rEABp/eDfVseKyTMDt+KGpeB35SkVn9Tyn0CqvVsY3EwI0v5i8nMHyJnzCIQf7nsy3p41TPkJZhg==
|
||||
|
||||
is-arguments@^1.0.4:
|
||||
version "1.2.0"
|
||||
resolved "https://registry.yarnpkg.com/is-arguments/-/is-arguments-1.2.0.tgz#ad58c6aecf563b78ef2bf04df540da8f5d7d8e1b"
|
||||
@@ -3327,6 +3405,16 @@ lodash._reinterpolate@^3.0.0:
|
||||
resolved "https://registry.yarnpkg.com/lodash._reinterpolate/-/lodash._reinterpolate-3.0.0.tgz#0ccf2d89166af03b3663c796538b75ac6e114d9d"
|
||||
integrity sha512-xYHt68QRoYGjeeM/XOE1uJtvXQAgvszfBhjV4yvsQH0u2i9I6cI6c6/eG4Hh3UAOVn0y/xAXwmTzEay49Q//HA==
|
||||
|
||||
lodash.defaults@^4.2.0:
|
||||
version "4.2.0"
|
||||
resolved "https://registry.yarnpkg.com/lodash.defaults/-/lodash.defaults-4.2.0.tgz#d09178716ffea4dde9e5fb7b37f6f0802274580c"
|
||||
integrity sha512-qjxPLHd3r5DnsdGacqOMU6pb/avJzdh9tFX2ymgoZE27BmjXrNy/y4LoaiTeAb+O3gL8AfpJGtqfX/ae2leYYQ==
|
||||
|
||||
lodash.isarguments@^3.1.0:
|
||||
version "3.1.0"
|
||||
resolved "https://registry.yarnpkg.com/lodash.isarguments/-/lodash.isarguments-3.1.0.tgz#2f573d85c6a24289ff00663b491c1d338ff3458a"
|
||||
integrity sha512-chi4NHZlZqZD18a0imDHnZPrDeBbTtVN7GXMwuGdRH9qotxAjYs3aVLKc7zNOG9eddR5Ksd8rvFEBc9SsggPpg==
|
||||
|
||||
lodash.merge@^4.6.2:
|
||||
version "4.6.2"
|
||||
resolved "https://registry.yarnpkg.com/lodash.merge/-/lodash.merge-4.6.2.tgz#558aa53b43b661e1925a0afdfa36a9a1085fe57a"
|
||||
@@ -3372,6 +3460,11 @@ lru-cache@^10.2.0:
|
||||
resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-10.2.1.tgz#e8d901141f22937968e45a6533d52824070151e4"
|
||||
integrity sha512-tS24spDe/zXhWbNPErCHs/AGOzbKGHT+ybSBqmdLm8WZ1xXLWvH8Qn71QPAlqVhd0qUTWjy+Kl9JmISgDdEjsA==
|
||||
|
||||
lru-cache@^10.4.3:
|
||||
version "10.4.3"
|
||||
resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-10.4.3.tgz#410fc8a17b70e598013df257c2446b7f3383f119"
|
||||
integrity sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==
|
||||
|
||||
lru-cache@^5.1.1:
|
||||
version "5.1.1"
|
||||
resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-5.1.1.tgz#1da27e6710271947695daf6848e847f01d84b920"
|
||||
@@ -3452,6 +3545,11 @@ mime-types@^2.1.35:
|
||||
dependencies:
|
||||
mime-db "1.52.0"
|
||||
|
||||
mime@^3.0.0:
|
||||
version "3.0.0"
|
||||
resolved "https://registry.yarnpkg.com/mime/-/mime-3.0.0.tgz#b374550dca3a0c18443b0c950a6a58f1931cf7a7"
|
||||
integrity sha512-jSCU7/VB1loIWBZe14aEYHU/+1UMEHoaO7qxCOVJOw9GgH72VAWppxNcjU+x9a2k3GSIBXNKxXQFqRvvZ7vr3A==
|
||||
|
||||
mimic-fn@^2.1.0:
|
||||
version "2.1.0"
|
||||
resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-2.1.0.tgz#7ed2c2ccccaf84d3ffcb7a69b57711fc2083401b"
|
||||
@@ -3587,6 +3685,11 @@ node-domexception@^1.0.0:
|
||||
resolved "https://registry.yarnpkg.com/node-domexception/-/node-domexception-1.0.0.tgz#6888db46a1f71c0b76b3f7555016b63fe64766e5"
|
||||
integrity sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ==
|
||||
|
||||
node-fetch-native@^1.6.4:
|
||||
version "1.6.4"
|
||||
resolved "https://registry.yarnpkg.com/node-fetch-native/-/node-fetch-native-1.6.4.tgz#679fc8fd8111266d47d7e72c379f1bed9acff06e"
|
||||
integrity sha512-IhOigYzAKHd244OC0JIMIUrjzctirCmPkaIfhDeGcEETWof5zKYUW7e7MYvChGWh/4CJeXEgsRyGzuF334rOOQ==
|
||||
|
||||
node-fetch@^3.3.0:
|
||||
version "3.3.2"
|
||||
resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-3.3.2.tgz#d1e889bacdf733b4ff3b2b243eb7a12866a0b78b"
|
||||
@@ -3689,6 +3792,20 @@ object.values@^1.1.6, object.values@^1.1.7:
|
||||
define-properties "^1.2.1"
|
||||
es-object-atoms "^1.0.0"
|
||||
|
||||
ofetch@^1.4.1:
|
||||
version "1.4.1"
|
||||
resolved "https://registry.yarnpkg.com/ofetch/-/ofetch-1.4.1.tgz#b6bf6b0d75ba616cef6519dd8b6385a8bae480ec"
|
||||
integrity sha512-QZj2DfGplQAr2oj9KzceK9Hwz6Whxazmn85yYeVuS3u9XTMOGMRx0kO95MQ+vLsj/S/NwBDMMLU5hpxvI6Tklw==
|
||||
dependencies:
|
||||
destr "^2.0.3"
|
||||
node-fetch-native "^1.6.4"
|
||||
ufo "^1.5.4"
|
||||
|
||||
ohash@^1.1.4:
|
||||
version "1.1.4"
|
||||
resolved "https://registry.yarnpkg.com/ohash/-/ohash-1.1.4.tgz#ae8d83014ab81157d2c285abf7792e2995fadd72"
|
||||
integrity sha512-FlDryZAahJmEF3VR3w1KogSEdWX3WhA5GPakFx4J81kEAiHyLMpdLLElS8n8dfNadMgAne/MywcvmogzscVt4g==
|
||||
|
||||
once@^1.3.0:
|
||||
version "1.4.0"
|
||||
resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1"
|
||||
@@ -3819,6 +3936,11 @@ path-type@^4.0.0:
|
||||
resolved "https://registry.yarnpkg.com/path-type/-/path-type-4.0.0.tgz#84ed01c0a7ba380afe09d90a8c180dcd9d03043b"
|
||||
integrity sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==
|
||||
|
||||
pathe@^1.1.2:
|
||||
version "1.1.2"
|
||||
resolved "https://registry.yarnpkg.com/pathe/-/pathe-1.1.2.tgz#6c4cb47a945692e48a1ddd6e4094d170516437ec"
|
||||
integrity sha512-whLdWMYL2TwI08hn8/ZqAbrVemu0LNaNNJZX73O6qaIdCTfXutsLhMkjdENX0qhsQ9uIimo4/aQOmXkoon2nDQ==
|
||||
|
||||
picocolors@^1.0.0, picocolors@^1.1.0, picocolors@^1.1.1:
|
||||
version "1.1.1"
|
||||
resolved "https://registry.yarnpkg.com/picocolors/-/picocolors-1.1.1.tgz#3d321af3eab939b083c8f929a1d12cda81c26b6b"
|
||||
@@ -3957,6 +4079,11 @@ queue-microtask@^1.2.2:
|
||||
resolved "https://registry.yarnpkg.com/queue-microtask/-/queue-microtask-1.2.3.tgz#4929228bbc724dfac43e0efb058caf7b6cfb6243"
|
||||
integrity sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==
|
||||
|
||||
radix3@^1.1.2:
|
||||
version "1.1.2"
|
||||
resolved "https://registry.yarnpkg.com/radix3/-/radix3-1.1.2.tgz#fd27d2af3896c6bf4bcdfab6427c69c2afc69ec0"
|
||||
integrity sha512-b484I/7b8rDEdSDKckSSBA8knMpcdsXudlE/LNL639wFoHKwLbEkQFZHWEYwDC0wa0FKUcCY+GAF73Z7wxNVFA==
|
||||
|
||||
react-dom@19:
|
||||
version "19.0.0"
|
||||
resolved "https://registry.yarnpkg.com/react-dom/-/react-dom-19.0.0.tgz#43446f1f01c65a4cd7f7588083e686a6726cfb57"
|
||||
@@ -4041,6 +4168,18 @@ recast@^0.23.2:
|
||||
tiny-invariant "^1.3.3"
|
||||
tslib "^2.0.1"
|
||||
|
||||
redis-errors@^1.0.0, redis-errors@^1.2.0:
|
||||
version "1.2.0"
|
||||
resolved "https://registry.yarnpkg.com/redis-errors/-/redis-errors-1.2.0.tgz#eb62d2adb15e4eaf4610c04afe1529384250abad"
|
||||
integrity sha512-1qny3OExCf0UvUV/5wpYKf2YwPcOqXzkwKKSmKHiE6ZMQs5heeE/c8eXK+PNllPvmjgAbfnsbpkGZWy8cBpn9w==
|
||||
|
||||
redis-parser@^3.0.0:
|
||||
version "3.0.0"
|
||||
resolved "https://registry.yarnpkg.com/redis-parser/-/redis-parser-3.0.0.tgz#b66d828cdcafe6b4b8a428a7def4c6bcac31c8b4"
|
||||
integrity sha512-DJnGAeenTdpMEH6uAJRK/uiyEIH9WVsUmoLwzudwGJUwZPp80PDBWPHXSAGNPwNvIXAbe7MSUB1zQFugFml66A==
|
||||
dependencies:
|
||||
redis-errors "^1.0.0"
|
||||
|
||||
reflect.getprototypeof@^1.0.4:
|
||||
version "1.0.6"
|
||||
resolved "https://registry.yarnpkg.com/reflect.getprototypeof/-/reflect.getprototypeof-1.0.6.tgz#3ab04c32a8390b770712b7a8633972702d278859"
|
||||
@@ -4333,6 +4472,11 @@ split-on-first@^1.0.0:
|
||||
resolved "https://registry.yarnpkg.com/split-on-first/-/split-on-first-1.1.0.tgz#f610afeee3b12bce1d0c30425e76398b78249a5f"
|
||||
integrity sha512-43ZssAJaMusuKWL8sKUBQXHWOpq8d6CfN/u1p4gUzfJkM05C8rxTmYrkIPTXapZpORA6LkkzcUulJ8FqA7Uudw==
|
||||
|
||||
standard-as-callback@^2.1.0:
|
||||
version "2.1.0"
|
||||
resolved "https://registry.yarnpkg.com/standard-as-callback/-/standard-as-callback-2.1.0.tgz#8953fc05359868a77b5b9739a665c5977bb7df45"
|
||||
integrity sha512-qoRRSyROncaz1z0mvYqIE4lCd9p2R90i6GxW3uZv5ucSu8tU7B5HXUP1gG8pVZsYNVaXjk8ClXHPttLyxAL48A==
|
||||
|
||||
stdin-discarder@^0.1.0:
|
||||
version "0.1.0"
|
||||
resolved "https://registry.yarnpkg.com/stdin-discarder/-/stdin-discarder-0.1.0.tgz#22b3e400393a8e28ebf53f9958f3880622efde21"
|
||||
@@ -4715,6 +4859,11 @@ typescript@^5:
|
||||
resolved "https://registry.yarnpkg.com/typescript/-/typescript-5.7.2.tgz#3169cf8c4c8a828cde53ba9ecb3d2b1d5dd67be6"
|
||||
integrity sha512-i5t66RHxDvVN40HfDd1PsEThGNnlMCMT3jMUuoh9/0TaqWevNontacunWyN02LA9/fIbEWlcHZcgTKb9QoaLfg==
|
||||
|
||||
ufo@^1.5.4:
|
||||
version "1.5.4"
|
||||
resolved "https://registry.yarnpkg.com/ufo/-/ufo-1.5.4.tgz#16d6949674ca0c9e0fbbae1fa20a71d7b1ded754"
|
||||
integrity sha512-UsUk3byDzKd04EyoZ7U4DOlxQaD14JUKQl6/P7wiX4FNvUfm3XL246n9W5AmqwW5RSFJ27NAuM0iLscAOYUiGQ==
|
||||
|
||||
unbox-primitive@^1.0.2:
|
||||
version "1.0.2"
|
||||
resolved "https://registry.yarnpkg.com/unbox-primitive/-/unbox-primitive-1.0.2.tgz#29032021057d5e6cdbd08c5129c226dff8ed6f9e"
|
||||
@@ -4725,16 +4874,46 @@ unbox-primitive@^1.0.2:
|
||||
has-symbols "^1.0.3"
|
||||
which-boxed-primitive "^1.0.2"
|
||||
|
||||
uncrypto@^0.1.3:
|
||||
version "0.1.3"
|
||||
resolved "https://registry.yarnpkg.com/uncrypto/-/uncrypto-0.1.3.tgz#e1288d609226f2d02d8d69ee861fa20d8348ef2b"
|
||||
integrity sha512-Ql87qFHB3s/De2ClA9e0gsnS6zXG27SkTiSJwjCc9MebbfapQfuPzumMIUMi38ezPZVNFcHI9sUIepeQfw8J8Q==
|
||||
|
||||
undici-types@~5.26.4:
|
||||
version "5.26.5"
|
||||
resolved "https://registry.yarnpkg.com/undici-types/-/undici-types-5.26.5.tgz#bcd539893d00b56e964fd2657a4866b221a65617"
|
||||
integrity sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==
|
||||
|
||||
unenv@^1.10.0:
|
||||
version "1.10.0"
|
||||
resolved "https://registry.yarnpkg.com/unenv/-/unenv-1.10.0.tgz#c3394a6c6e4cfe68d699f87af456fe3f0db39571"
|
||||
integrity sha512-wY5bskBQFL9n3Eca5XnhH6KbUo/tfvkwm9OpcdCvLaeA7piBNbavbOKJySEwQ1V0RH6HvNlSAFRTpvTqgKRQXQ==
|
||||
dependencies:
|
||||
consola "^3.2.3"
|
||||
defu "^6.1.4"
|
||||
mime "^3.0.0"
|
||||
node-fetch-native "^1.6.4"
|
||||
pathe "^1.1.2"
|
||||
|
||||
universalify@^2.0.0:
|
||||
version "2.0.1"
|
||||
resolved "https://registry.yarnpkg.com/universalify/-/universalify-2.0.1.tgz#168efc2180964e6386d061e094df61afe239b18d"
|
||||
integrity sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==
|
||||
|
||||
unstorage@^1.14.4:
|
||||
version "1.14.4"
|
||||
resolved "https://registry.yarnpkg.com/unstorage/-/unstorage-1.14.4.tgz#620dd68997a3245fca1e04c0171335817525bc3d"
|
||||
integrity sha512-1SYeamwuYeQJtJ/USE1x4l17LkmQBzg7deBJ+U9qOBoHo15d1cDxG4jM31zKRgF7pG0kirZy4wVMX6WL6Zoscg==
|
||||
dependencies:
|
||||
anymatch "^3.1.3"
|
||||
chokidar "^3.6.0"
|
||||
destr "^2.0.3"
|
||||
h3 "^1.13.0"
|
||||
lru-cache "^10.4.3"
|
||||
node-fetch-native "^1.6.4"
|
||||
ofetch "^1.4.1"
|
||||
ufo "^1.5.4"
|
||||
|
||||
update-browserslist-db@^1.1.1:
|
||||
version "1.1.1"
|
||||
resolved "https://registry.yarnpkg.com/update-browserslist-db/-/update-browserslist-db-1.1.1.tgz#80846fba1d79e82547fb661f8d141e0945755fe5"
|
||||
|
||||
Reference in New Issue
Block a user