Support audio files in gallery and viewer, include Musik .txt in ZIP, exclude .txt from website
This commit is contained in:
1 parent
77e8876a07
commit
25e5f3d0a6
12 files changed
+245
-40
No files matched your search
@@ -59,8 +59,8 @@ export async function generateMetadata({
|
||||
where: { relativePath: mediaPath },
|
||||
});
|
||||
|
||||
// Don't leak metadata for unpublished items
|
||||
if (!item?.published) {
|
||||
// Don't leak metadata for unpublished items or text files
|
||||
if (!item?.published || item.filename.toLowerCase().endsWith('.txt') || item.mediaType === 'OTHER') {
|
||||
return { title: 'Nele & Till - Hochzeit' };
|
||||
}
|
||||
|
||||
@@ -93,7 +93,7 @@ export default async function MediaDirectRoute({
|
||||
where: { relativePath: mediaPath },
|
||||
});
|
||||
|
||||
if (!item) {
|
||||
if (!item || item.filename.toLowerCase().endsWith('.txt') || item.mediaType === 'OTHER') {
|
||||
notFound();
|
||||
}
|
||||
|
||||
|
||||
@@ -47,7 +47,10 @@ export default function AdminLayoutEditor() {
|
||||
const res = await fetch('/api/media?admin=true&limit=1000');
|
||||
if (res.ok) {
|
||||
const data = await res.json();
|
||||
setItems(data.items);
|
||||
const sortedItems = (data.items || []).slice().sort((a: MediaItem, b: MediaItem) =>
|
||||
a.relativePath.localeCompare(b.relativePath, undefined, { numeric: true, sensitivity: 'base' })
|
||||
);
|
||||
setItems(sortedItems);
|
||||
}
|
||||
} catch (err) {
|
||||
console.error('Error fetching admin data:', err);
|
||||
|
||||
+39
-12
@@ -54,11 +54,12 @@ export default function AdminDashboard() {
|
||||
const res = await fetch('/api/media?admin=true&limit=5000');
|
||||
if (res.ok) {
|
||||
const data = await res.json();
|
||||
setItems(data.items);
|
||||
const mediaOnly = data.items.filter((i: MediaItem) => !i.filename.toLowerCase().endsWith('.txt') && i.mediaType !== 'OTHER');
|
||||
setItems(mediaOnly);
|
||||
setStats({
|
||||
total: data.items.length,
|
||||
published: data.items.filter((i: MediaItem) => i.published).length,
|
||||
unpublished: data.items.filter((i: MediaItem) => !i.published).length,
|
||||
total: mediaOnly.length,
|
||||
published: mediaOnly.filter((i: MediaItem) => i.published).length,
|
||||
unpublished: mediaOnly.filter((i: MediaItem) => !i.published).length,
|
||||
});
|
||||
}
|
||||
} catch (err) {
|
||||
@@ -186,7 +187,22 @@ export default function AdminDashboard() {
|
||||
|
||||
if (!authChecked) return <div style={{ padding: '20px' }}>Prüfe Berechtigung...</div>;
|
||||
|
||||
const displayedItems = selectedFolder ? items.filter(i => i.directory === selectedFolder) : items;
|
||||
const displayedItems = (selectedFolder ? items.filter(i => i.directory === selectedFolder) : items)
|
||||
.slice()
|
||||
.sort((a, b) => {
|
||||
if (!selectedFolder && a.directory !== b.directory) {
|
||||
const indexA = orderedFolders.indexOf(a.directory);
|
||||
const indexB = orderedFolders.indexOf(b.directory);
|
||||
if (indexA !== -1 && indexB !== -1) return indexA - indexB;
|
||||
if (indexA !== -1) return -1;
|
||||
if (indexB !== -1) return 1;
|
||||
const dirCompare = (a.directory || '').localeCompare(b.directory || '', undefined, { numeric: true, sensitivity: 'base' });
|
||||
if (dirCompare !== 0) return dirCompare;
|
||||
}
|
||||
const fileCompare = a.filename.localeCompare(b.filename, undefined, { numeric: true, sensitivity: 'base' });
|
||||
if (fileCompare !== 0) return fileCompare;
|
||||
return a.relativePath.localeCompare(b.relativePath, undefined, { numeric: true, sensitivity: 'base' });
|
||||
});
|
||||
|
||||
return (
|
||||
<div className="admin-content" style={{ padding: '20px', maxWidth: '1200px', margin: '0 auto' }}>
|
||||
@@ -318,13 +334,24 @@ export default function AdminDashboard() {
|
||||
}}
|
||||
>
|
||||
<div style={{ position: 'relative', aspectRatio: '1/1', background: '#eee' }}>
|
||||
{/* eslint-disable-next-line @next/next/no-img-element */}
|
||||
<img
|
||||
src={`/api/media/${item.relativePath}?type=thumb`}
|
||||
alt={item.filename}
|
||||
style={{ width: '100%', height: '100%', objectFit: 'cover' }}
|
||||
loading="lazy"
|
||||
/>
|
||||
{item.mediaType === 'AUDIO' ? (
|
||||
<div style={{ width: '100%', height: '100%', display: 'flex', flexDirection: 'column', alignItems: 'center', justifyContent: 'center', background: '#f8f4ee', color: 'var(--color-primary)' }}>
|
||||
<svg width="40" height="40" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
|
||||
<path d="M9 18V5l12-2v13"></path>
|
||||
<circle cx="6" cy="18" r="3"></circle>
|
||||
<circle cx="18" cy="16" r="3"></circle>
|
||||
</svg>
|
||||
<span style={{ fontSize: '0.75rem', marginTop: '6px', fontWeight: 600 }}>Audio (MP3)</span>
|
||||
</div>
|
||||
) : (
|
||||
/* eslint-disable-next-line @next/next/no-img-element */
|
||||
<img
|
||||
src={`/api/media/${item.relativePath}?type=thumb`}
|
||||
alt={item.filename}
|
||||
style={{ width: '100%', height: '100%', objectFit: 'cover' }}
|
||||
loading="lazy"
|
||||
/>
|
||||
)}
|
||||
{item.mediaType === 'VIDEO' && (
|
||||
<div style={{ position: 'absolute', inset: 0, display: 'flex', alignItems: 'center', justifyContent: 'center', background: 'rgba(0,0,0,0.3)' }}>
|
||||
<svg width="24" height="24" viewBox="0 0 24 24" fill="white" stroke="none"><polygon points="5 3 19 12 5 21 5 3"></polygon></svg>
|
||||
|
||||
@@ -9,12 +9,19 @@ export async function GET(request: NextRequest) {
|
||||
try {
|
||||
const items = await prisma.mediaItem.findMany({
|
||||
orderBy: [
|
||||
{ featured: 'desc' },
|
||||
{ placementMode: 'asc' },
|
||||
{ sortOrder: 'asc' },
|
||||
{ createdAt: 'desc' }
|
||||
{ directory: 'asc' },
|
||||
{ filename: 'asc' },
|
||||
{ id: 'asc' }
|
||||
]
|
||||
});
|
||||
items.sort((a, b) => {
|
||||
if (a.directory !== b.directory) {
|
||||
return (a.directory || '').localeCompare(b.directory || '', undefined, { numeric: true, sensitivity: 'base' });
|
||||
}
|
||||
const fileCompare = a.filename.localeCompare(b.filename, undefined, { numeric: true, sensitivity: 'base' });
|
||||
if (fileCompare !== 0) return fileCompare;
|
||||
return a.relativePath.localeCompare(b.relativePath, undefined, { numeric: true, sensitivity: 'base' });
|
||||
});
|
||||
return NextResponse.json({ items });
|
||||
} catch (error) {
|
||||
console.error('Error fetching layout items:', error);
|
||||
|
||||
@@ -26,6 +26,8 @@ export async function GET(request: NextRequest) {
|
||||
const where: Record<string, unknown> = {};
|
||||
if (!isAdminRequest) {
|
||||
where.published = true;
|
||||
where.mediaType = { not: 'OTHER' };
|
||||
where.NOT = { filename: { endsWith: '.txt' } };
|
||||
}
|
||||
if (directoryParam) {
|
||||
const dirs = directoryParam.split(',');
|
||||
@@ -52,9 +54,9 @@ export async function GET(request: NextRequest) {
|
||||
take: limit + 1,
|
||||
...(cursor ? { skip: 1, cursor: { id: cursor } } : {}),
|
||||
orderBy: [
|
||||
{ featured: 'desc' },
|
||||
{ sortOrder: 'asc' },
|
||||
{ createdAt: 'desc' },
|
||||
{ directory: 'asc' },
|
||||
{ filename: 'asc' },
|
||||
{ id: 'asc' },
|
||||
],
|
||||
include: { _count: { select: { likes: true } } },
|
||||
});
|
||||
@@ -63,6 +65,15 @@ export async function GET(request: NextRequest) {
|
||||
const nextItem = items.pop();
|
||||
nextCursor = nextItem!.id;
|
||||
}
|
||||
|
||||
items.sort((a, b) => {
|
||||
if (a.directory !== b.directory) {
|
||||
return (a.directory || '').localeCompare(b.directory || '', undefined, { numeric: true, sensitivity: 'base' });
|
||||
}
|
||||
const fileCompare = a.filename.localeCompare(b.filename, undefined, { numeric: true, sensitivity: 'base' });
|
||||
if (fileCompare !== 0) return fileCompare;
|
||||
return a.relativePath.localeCompare(b.relativePath, undefined, { numeric: true, sensitivity: 'base' });
|
||||
});
|
||||
} else {
|
||||
// Custom pagination with randomized chunk sorting
|
||||
let seed = Math.floor(Math.random() * 1000000);
|
||||
|
||||
+10
-2
@@ -9,7 +9,11 @@ async function getInitialMedia() {
|
||||
try {
|
||||
// 1. Fetch metadata for all published items to sort them properly
|
||||
const allMetadata = await prisma.mediaItem.findMany({
|
||||
where: { published: true },
|
||||
where: {
|
||||
published: true,
|
||||
mediaType: { not: 'OTHER' },
|
||||
NOT: { filename: { endsWith: '.txt' } },
|
||||
},
|
||||
select: {
|
||||
id: true,
|
||||
featured: true,
|
||||
@@ -48,7 +52,11 @@ async function getInitialMedia() {
|
||||
|
||||
// Fetch available directories for filtering
|
||||
const dirs = await prisma.mediaItem.findMany({
|
||||
where: { published: true },
|
||||
where: {
|
||||
published: true,
|
||||
mediaType: { not: 'OTHER' },
|
||||
NOT: { filename: { endsWith: '.txt' } },
|
||||
},
|
||||
select: { directory: true },
|
||||
distinct: ['directory'],
|
||||
orderBy: { directory: 'asc' }
|
||||
|
||||
@@ -30,6 +30,7 @@ export default function FullscreenViewer({
|
||||
votingOpen,
|
||||
}: ViewerProps) {
|
||||
const isVideo = item.mediaType === 'VIDEO';
|
||||
const isAudio = item.mediaType === 'AUDIO';
|
||||
const touchStartX = useRef<number | null>(null);
|
||||
|
||||
// Update URL to reflect current media
|
||||
@@ -118,6 +119,58 @@ export default function FullscreenViewer({
|
||||
playsInline
|
||||
onClick={(e) => e.stopPropagation()}
|
||||
/>
|
||||
) : isAudio ? (
|
||||
<div
|
||||
className="viewer-content"
|
||||
onClick={(e) => e.stopPropagation()}
|
||||
style={{
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
background: 'rgba(30, 36, 28, 0.95)',
|
||||
border: '1px solid rgba(124, 154, 110, 0.4)',
|
||||
borderRadius: '16px',
|
||||
padding: '40px 30px',
|
||||
maxWidth: '460px',
|
||||
width: '90%',
|
||||
boxShadow: '0 20px 50px rgba(0,0,0,0.6)',
|
||||
color: '#fff',
|
||||
textAlign: 'center',
|
||||
}}
|
||||
>
|
||||
<div
|
||||
style={{
|
||||
width: '100px',
|
||||
height: '100px',
|
||||
borderRadius: '50%',
|
||||
background: 'linear-gradient(135deg, var(--color-primary) 0%, var(--color-primary-dark) 100%)',
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
marginBottom: '20px',
|
||||
boxShadow: '0 8px 24px rgba(0,0,0,0.3)',
|
||||
}}
|
||||
>
|
||||
<svg width="48" height="48" viewBox="0 0 24 24" fill="none" stroke="white" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
|
||||
<path d="M9 18V5l12-2v13"></path>
|
||||
<circle cx="6" cy="18" r="3"></circle>
|
||||
<circle cx="18" cy="16" r="3"></circle>
|
||||
</svg>
|
||||
</div>
|
||||
<h2 style={{ fontFamily: 'var(--font-display)', fontSize: '1.5rem', marginBottom: '8px', color: '#fff' }}>
|
||||
{item.filename.replace(/\.[^/.]+$/, '')}
|
||||
</h2>
|
||||
<p style={{ color: 'rgba(255,255,255,0.7)', fontSize: '0.9rem', marginBottom: '24px' }}>
|
||||
Nele & Till • Hochzeit
|
||||
</p>
|
||||
<audio
|
||||
src={`/api/media/${item.relativePath}`}
|
||||
controls
|
||||
autoPlay
|
||||
style={{ width: '100%', outline: 'none', borderRadius: '8px' }}
|
||||
/>
|
||||
</div>
|
||||
) : (
|
||||
// eslint-disable-next-line @next/next/no-img-element
|
||||
<img
|
||||
|
||||
@@ -59,7 +59,9 @@ export default function MediaTile({ item, index, onClick, votingOpen }: MediaTil
|
||||
};
|
||||
|
||||
const isVideo = item.mediaType === 'VIDEO';
|
||||
const isAudio = item.mediaType === 'AUDIO';
|
||||
const likesCount = item._count?.likes ?? 0;
|
||||
const songTitle = item.filename.replace(/\.[^/.]+$/, '');
|
||||
|
||||
return (
|
||||
<div
|
||||
@@ -77,17 +79,75 @@ export default function MediaTile({ item, index, onClick, votingOpen }: MediaTil
|
||||
<div style={{ display: 'block', paddingTop: `${(1 / displayRatio) * 100}%` }} />
|
||||
{isVisible && (
|
||||
<>
|
||||
{/* eslint-disable-next-line @next/next/no-img-element */}
|
||||
<img
|
||||
src={isVideo ? `/api/media/${item.relativePath}?type=poster` : `/api/media/${item.relativePath}?type=thumb`}
|
||||
alt={item.filename}
|
||||
loading="lazy"
|
||||
onLoad={(e) => {
|
||||
const target = e.target as HTMLImageElement;
|
||||
target.parentElement?.classList.remove('loading-skeleton');
|
||||
}}
|
||||
/>
|
||||
{isVideo && (
|
||||
{isAudio ? (
|
||||
<div
|
||||
style={{
|
||||
position: 'absolute',
|
||||
inset: 0,
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
background: 'linear-gradient(135deg, #4A5D40 0%, #2F3E28 100%)',
|
||||
color: '#fff',
|
||||
padding: '16px',
|
||||
textAlign: 'center',
|
||||
}}
|
||||
ref={(el) => {
|
||||
if (el) el.parentElement?.classList.remove('loading-skeleton');
|
||||
}}
|
||||
>
|
||||
<div style={{
|
||||
width: '56px',
|
||||
height: '56px',
|
||||
borderRadius: '50%',
|
||||
background: 'rgba(255, 255, 255, 0.15)',
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
marginBottom: '10px',
|
||||
backdropFilter: 'blur(4px)',
|
||||
boxShadow: '0 4px 12px rgba(0,0,0,0.15)',
|
||||
}}>
|
||||
<svg width="28" height="28" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
|
||||
<path d="M9 18V5l12-2v13"></path>
|
||||
<circle cx="6" cy="18" r="3"></circle>
|
||||
<circle cx="18" cy="16" r="3"></circle>
|
||||
</svg>
|
||||
</div>
|
||||
<div style={{
|
||||
fontFamily: 'var(--font-display)',
|
||||
fontSize: '1.1rem',
|
||||
fontWeight: 600,
|
||||
letterSpacing: '0.02em',
|
||||
marginBottom: '4px',
|
||||
lineHeight: 1.2,
|
||||
textShadow: '0 2px 4px rgba(0,0,0,0.3)',
|
||||
}}>
|
||||
{songTitle}
|
||||
</div>
|
||||
<span style={{
|
||||
fontSize: '0.75rem',
|
||||
textTransform: 'uppercase',
|
||||
letterSpacing: '0.1em',
|
||||
opacity: 0.8,
|
||||
}}>
|
||||
Hochzeitslied
|
||||
</span>
|
||||
</div>
|
||||
) : (
|
||||
/* eslint-disable-next-line @next/next/no-img-element */
|
||||
<img
|
||||
src={isVideo ? `/api/media/${item.relativePath}?type=poster` : `/api/media/${item.relativePath}?type=thumb`}
|
||||
alt={item.filename}
|
||||
loading="lazy"
|
||||
onLoad={(e) => {
|
||||
const target = e.target as HTMLImageElement;
|
||||
target.parentElement?.classList.remove('loading-skeleton');
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
{(isVideo || isAudio) && (
|
||||
<div className="play-icon">
|
||||
<svg width="24" height="24" viewBox="0 0 24 24" fill="currentColor" stroke="none">
|
||||
<polygon points="5 3 19 12 5 21 5 3"></polygon>
|
||||
|
||||
@@ -69,6 +69,7 @@ const EXCLUDED_SUFFIXES = [
|
||||
'.partial',
|
||||
'.crdownload',
|
||||
'.part',
|
||||
'.txt',
|
||||
];
|
||||
|
||||
const EXCLUDED_DIRECTORIES = new Set([
|
||||
|
||||
@@ -61,19 +61,53 @@ export async function buildZip(): Promise<boolean> {
|
||||
|
||||
// 5. Add each published file
|
||||
let addedCount = 0;
|
||||
const addedPaths = new Set<string>();
|
||||
|
||||
for (const item of publishedItems) {
|
||||
if (item.relativePath.toLowerCase().endsWith('.txt')) {
|
||||
continue;
|
||||
}
|
||||
const absolutePath = path.join(MEDIA_ROOT, item.relativePath);
|
||||
|
||||
// Verify file still exists
|
||||
try {
|
||||
await fs.promises.access(absolutePath, fs.constants.R_OK);
|
||||
archive.file(absolutePath, { name: item.relativePath });
|
||||
addedPaths.add(item.relativePath);
|
||||
addedCount++;
|
||||
} catch {
|
||||
console.warn(`[ZIP] File missing, skipping: ${item.relativePath}`);
|
||||
}
|
||||
}
|
||||
|
||||
// Always include .txt files from the Musik directory (e.g. song lyrics)
|
||||
try {
|
||||
if (fs.existsSync(MEDIA_ROOT)) {
|
||||
const rootEntries = await fs.promises.readdir(MEDIA_ROOT, { withFileTypes: true });
|
||||
const musicDirs = rootEntries.filter(
|
||||
(e) => e.isDirectory() && e.name.toLowerCase() === 'musik'
|
||||
);
|
||||
for (const mDir of musicDirs) {
|
||||
const musicDirPath = path.join(MEDIA_ROOT, mDir.name);
|
||||
const musicFiles = await fs.promises.readdir(musicDirPath, { withFileTypes: true });
|
||||
for (const file of musicFiles) {
|
||||
if (file.isFile() && file.name.toLowerCase().endsWith('.txt')) {
|
||||
const relPath = path.posix.join(mDir.name, file.name);
|
||||
const absPath = path.join(musicDirPath, file.name);
|
||||
if (!addedPaths.has(relPath)) {
|
||||
archive.file(absPath, { name: relPath });
|
||||
addedPaths.add(relPath);
|
||||
addedCount++;
|
||||
console.log(`[ZIP] Included text file in zip: ${relPath}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (err) {
|
||||
console.warn('[ZIP] Error reading Musik directory for .txt files:', err);
|
||||
}
|
||||
|
||||
// 6. Finalize archive
|
||||
await archive.finalize();
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
/**
|
||||
/**
|
||||
* Filesystem scanner — recursively scans media directory,
|
||||
* detects new/changed/removed files, and syncs with the database.
|
||||
*/
|
||||
@@ -61,8 +61,9 @@ export async function runFullScan(): Promise<{
|
||||
} else {
|
||||
unchanged++;
|
||||
dbPaths.delete(relPath);
|
||||
if (existing.thumbnailReady === false) {
|
||||
enqueueProcessing(relPath, getMediaType(file.ext)).catch(console.error);
|
||||
const mediaType = getMediaType(file.ext);
|
||||
if (existing.thumbnailReady === false && (mediaType === 'IMAGE' || mediaType === 'VIDEO')) {
|
||||
enqueueProcessing(relPath, mediaType).catch(console.error);
|
||||
console.log('Re-queued missing thumbnail:', relPath);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1 +1 @@
|
||||
{"program":{"fileNames":["./node_modules/typescript/lib/lib.es5.d.ts","./node_modules/typescript/lib/lib.es2015.d.ts","./node_modules/typescript/lib/lib.es2016.d.ts","./node_modules/typescript/lib/lib.es2017.d.ts","./node_modules/typescript/lib/lib.es2018.d.ts","./node_modules/typescript/lib/lib.es2019.d.ts","./node_modules/typescript/lib/lib.es2020.d.ts","./node_modules/typescript/lib/lib.es2021.d.ts","./node_modules/typescript/lib/lib.es2022.d.ts","./node_modules/typescript/lib/lib.es2023.d.ts","./node_modules/typescript/lib/lib.esnext.d.ts","./node_modules/typescript/lib/lib.dom.d.ts","./node_modules/typescript/lib/lib.dom.iterable.d.ts","./node_modules/typescript/lib/lib.es2015.core.d.ts","./node_modules/typescript/lib/lib.es2015.collection.d.ts","./node_modules/typescript/lib/lib.es2015.generator.d.ts","./node_modules/typescript/lib/lib.es2015.iterable.d.ts","./node_modules/typescript/lib/lib.es2015.promise.d.ts","./node_modules/typescript/lib/lib.es2015.proxy.d.ts","./node_modules/typescript/lib/lib.es2015.reflect.d.ts","./node_modules/typescript/lib/lib.es2015.symbol.d.ts","./node_modules/typescript/lib/lib.es2015.symbol.wellknown.d.ts","./node_modules/typescript/lib/lib.es2016.array.include.d.ts","./node_modules/typescript/lib/lib.es2016.intl.d.ts","./node_modules/typescript/lib/lib.es2017.date.d.ts","./node_modules/typescript/lib/lib.es2017.object.d.ts","./node_modules/typescript/lib/lib.es2017.sharedmemory.d.ts","./node_modules/typescript/lib/lib.es2017.string.d.ts","./node_modules/typescript/lib/lib.es2017.intl.d.ts","./node_modules/typescript/lib/lib.es2017.typedarrays.d.ts","./node_modules/typescript/lib/lib.es2018.asyncgenerator.d.ts","./node_modules/typescript/lib/lib.es2018.asynciterable.d.ts","./node_modules/typescript/lib/lib.es2018.intl.d.ts","./node_modules/typescript/lib/lib.es2018.promise.d.ts","./node_modules/typescript/lib/lib.es2018.regexp.d.ts","./node_modules/typescript/lib/lib.es2019.array.d.ts","./node_modules/typescript/lib/lib.es2019.object.d.ts","./node_modules/typescript/lib/lib.es2019.string.d.ts","./node_modules/typescript/lib/lib.es2019.symbol.d.ts","./node_modules/typescript/lib/lib.es2019.intl.d.ts","./node_modules/typescript/lib/lib.es2020.bigint.d.ts","./node_modules/typescript/lib/lib.es2020.date.d.ts","./node_modules/typescript/lib/lib.es2020.promise.d.ts","./node_modules/typescript/lib/lib.es2020.sharedmemory.d.ts","./node_modules/typescript/lib/lib.es2020.string.d.ts","./node_modules/typescript/lib/lib.es2020.symbol.wellknown.d.ts","./node_modules/typescript/lib/lib.es2020.intl.d.ts","./node_modules/typescript/lib/lib.es2020.number.d.ts","./node_modules/typescript/lib/lib.es2021.promise.d.ts","./node_modules/typescript/lib/lib.es2021.string.d.ts","./node_modules/typescript/lib/lib.es2021.weakref.d.ts","./node_modules/typescript/lib/lib.es2021.intl.d.ts","./node_modules/typescript/lib/lib.es2022.array.d.ts","./node_modules/typescript/lib/lib.es2022.error.d.ts","./node_modules/typescript/lib/lib.es2022.intl.d.ts","./node_modules/typescript/lib/lib.es2022.object.d.ts","./node_modules/typescript/lib/lib.es2022.sharedmemory.d.ts","./node_modules/typescript/lib/lib.es2022.string.d.ts","./node_modules/typescript/lib/lib.es2022.regexp.d.ts","./node_modules/typescript/lib/lib.es2023.array.d.ts","./node_modules/typescript/lib/lib.es2023.collection.d.ts","./node_modules/typescript/lib/lib.es2023.intl.d.ts","./node_modules/typescript/lib/lib.esnext.array.d.ts","./node_modules/typescript/lib/lib.esnext.collection.d.ts","./node_modules/typescript/lib/lib.esnext.intl.d.ts","./node_modules/typescript/lib/lib.esnext.disposable.d.ts","./node_modules/typescript/lib/lib.esnext.string.d.ts","./node_modules/typescript/lib/lib.esnext.promise.d.ts","./node_modules/typescript/lib/lib.esnext.decorators.d.ts","./node_modules/typescript/lib/lib.esnext.object.d.ts","./node_modules/typescript/lib/lib.esnext.regexp.d.ts","./node_modules/typescript/lib/lib.decorators.d.ts","./node_modules/typescript/lib/lib.decorators.legacy.d.ts","./node_modules/next/dist/styled-jsx/types/css.d.ts","./node_modules/@types/react/global.d.ts","./node_modules/csstype/index.d.ts","./node_modules/@types/prop-types/index.d.ts","./node_modules/@types/react/index.d.ts","./node_modules/next/dist/styled-jsx/types/index.d.ts","./node_modules/next/dist/styled-jsx/types/macro.d.ts","./node_modules/next/dist/styled-jsx/types/style.d.ts","./node_modules/next/dist/styled-jsx/types/global.d.ts","./node_modules/next/dist/shared/lib/amp.d.ts","./node_modules/next/amp.d.ts","./node_modules/@types/node/ts5.6/globals.typedarray.d.ts","./node_modules/@types/node/ts5.6/buffer.buffer.d.ts","./node_modules/@types/node/assert.d.ts","./node_modules/@types/node/assert/strict.d.ts","./node_modules/buffer/index.d.ts","./node_modules/undici-types/header.d.ts","./node_modules/undici-types/readable.d.ts","./node_modules/undici-types/file.d.ts","./node_modules/undici-types/fetch.d.ts","./node_modules/undici-types/formdata.d.ts","./node_modules/uLine truncated
|
||||
{"program":{"fileNames":["./node_modules/typescript/lib/lib.es5.d.ts","./node_modules/typescript/lib/lib.es2015.d.ts","./node_modules/typescript/lib/lib.es2016.d.ts","./node_modules/typescript/lib/lib.es2017.d.ts","./node_modules/typescript/lib/lib.es2018.d.ts","./node_modules/typescript/lib/lib.es2019.d.ts","./node_modules/typescript/lib/lib.es2020.d.ts","./node_modules/typescript/lib/lib.es2021.d.ts","./node_modules/typescript/lib/lib.es2022.d.ts","./node_modules/typescript/lib/lib.es2023.d.ts","./node_modules/typescript/lib/lib.esnext.d.ts","./node_modules/typescript/lib/lib.dom.d.ts","./node_modules/typescript/lib/lib.dom.iterable.d.ts","./node_modules/typescript/lib/lib.es2015.core.d.ts","./node_modules/typescript/lib/lib.es2015.collection.d.ts","./node_modules/typescript/lib/lib.es2015.generator.d.ts","./node_modules/typescript/lib/lib.es2015.iterable.d.ts","./node_modules/typescript/lib/lib.es2015.promise.d.ts","./node_modules/typescript/lib/lib.es2015.proxy.d.ts","./node_modules/typescript/lib/lib.es2015.reflect.d.ts","./node_modules/typescript/lib/lib.es2015.symbol.d.ts","./node_modules/typescript/lib/lib.es2015.symbol.wellknown.d.ts","./node_modules/typescript/lib/lib.es2016.array.include.d.ts","./node_modules/typescript/lib/lib.es2016.intl.d.ts","./node_modules/typescript/lib/lib.es2017.date.d.ts","./node_modules/typescript/lib/lib.es2017.object.d.ts","./node_modules/typescript/lib/lib.es2017.sharedmemory.d.ts","./node_modules/typescript/lib/lib.es2017.string.d.ts","./node_modules/typescript/lib/lib.es2017.intl.d.ts","./node_modules/typescript/lib/lib.es2017.typedarrays.d.ts","./node_modules/typescript/lib/lib.es2018.asyncgenerator.d.ts","./node_modules/typescript/lib/lib.es2018.asynciterable.d.ts","./node_modules/typescript/lib/lib.es2018.intl.d.ts","./node_modules/typescript/lib/lib.es2018.promise.d.ts","./node_modules/typescript/lib/lib.es2018.regexp.d.ts","./node_modules/typescript/lib/lib.es2019.array.d.ts","./node_modules/typescript/lib/lib.es2019.object.d.ts","./node_modules/typescript/lib/lib.es2019.string.d.ts","./node_modules/typescript/lib/lib.es2019.symbol.d.ts","./node_modules/typescript/lib/lib.es2019.intl.d.ts","./node_modules/typescript/lib/lib.es2020.bigint.d.ts","./node_modules/typescript/lib/lib.es2020.date.d.ts","./node_modules/typescript/lib/lib.es2020.promise.d.ts","./node_modules/typescript/lib/lib.es2020.sharedmemory.d.ts","./node_modules/typescript/lib/lib.es2020.string.d.ts","./node_modules/typescript/lib/lib.es2020.symbol.wellknown.d.ts","./node_modules/typescript/lib/lib.es2020.intl.d.ts","./node_modules/typescript/lib/lib.es2020.number.d.ts","./node_modules/typescript/lib/lib.es2021.promise.d.ts","./node_modules/typescript/lib/lib.es2021.string.d.ts","./node_modules/typescript/lib/lib.es2021.weakref.d.ts","./node_modules/typescript/lib/lib.es2021.intl.d.ts","./node_modules/typescript/lib/lib.es2022.array.d.ts","./node_modules/typescript/lib/lib.es2022.error.d.ts","./node_modules/typescript/lib/lib.es2022.intl.d.ts","./node_modules/typescript/lib/lib.es2022.object.d.ts","./node_modules/typescript/lib/lib.es2022.sharedmemory.d.ts","./node_modules/typescript/lib/lib.es2022.string.d.ts","./node_modules/typescript/lib/lib.es2022.regexp.d.ts","./node_modules/typescript/lib/lib.es2023.array.d.ts","./node_modules/typescript/lib/lib.es2023.collection.d.ts","./node_modules/typescript/lib/lib.es2023.intl.d.ts","./node_modules/typescript/lib/lib.esnext.array.d.ts","./node_modules/typescript/lib/lib.esnext.collection.d.ts","./node_modules/typescript/lib/lib.esnext.intl.d.ts","./node_modules/typescript/lib/lib.esnext.disposable.d.ts","./node_modules/typescript/lib/lib.esnext.string.d.ts","./node_modules/typescript/lib/lib.esnext.promise.d.ts","./node_modules/typescript/lib/lib.esnext.decorators.d.ts","./node_modules/typescript/lib/lib.esnext.object.d.ts","./node_modules/typescript/lib/lib.esnext.regexp.d.ts","./node_modules/typescript/lib/lib.decorators.d.ts","./node_modules/typescript/lib/lib.decorators.legacy.d.ts","./node_modules/next/dist/styled-jsx/types/css.d.ts","./node_modules/@types/react/global.d.ts","./node_modules/csstype/index.d.ts","./node_modules/@types/prop-types/index.d.ts","./node_modules/@types/react/index.d.ts","./node_modules/next/dist/styled-jsx/types/index.d.ts","./node_modules/next/dist/styled-jsx/types/macro.d.ts","./node_modules/next/dist/styled-jsx/types/style.d.ts","./node_modules/next/dist/styled-jsx/types/global.d.ts","./node_modules/next/dist/shared/lib/amp.d.ts","./node_modules/next/amp.d.ts","./node_modules/@types/node/ts5.6/globals.typedarray.d.ts","./node_modules/@types/node/ts5.6/buffer.buffer.d.ts","./node_modules/@types/node/assert.d.ts","./node_modules/@types/node/assert/strict.d.ts","./node_modules/buffer/index.d.ts","./node_modules/undici-types/header.d.ts","./node_modules/undici-types/readable.d.ts","./node_modules/undici-types/file.d.ts","./node_modules/undici-types/fetch.d.ts","./node_modules/undici-types/formdata.d.ts","./node_modules/uLine truncated
|
||||
Reference in new issue
Block a user