commit b72c6f39707f3883b30efea3079587bb1740de4c
parent 5ae0fcf4aef52324e714d2c32b589afebf1007c4
Author: I Mean I'm Just Saying <imeanimjustsaying@kiwifarms.st>
Date: Wed, 15 Apr 2026 13:54:36 -0400
Initialize the Jeralyzer
Diffstat:
19 files changed, 2001 insertions(+), 88 deletions(-)
diff --git a/.gitignore b/.gitignore
@@ -39,3 +39,12 @@ yarn-error.log*
# typescript
*.tsbuildinfo
next-env.d.ts
+
+# nested transcripts repo
+/transcripts
+
+# public downloadable repo archives (generate with create-archives.sh)
+/public/code.tar.gz
+/public/code.tar.xz
+/public/transcripts.tar.gz
+/public/transcripts.tar.xz
diff --git a/README.md b/README.md
@@ -1,36 +1,54 @@
-This is a [Next.js](https://nextjs.org) project bootstrapped with [`create-next-app`](https://nextjs.org/docs/app/api-reference/cli/create-next-app).
+# Jeralyzer
-## Getting Started
+This is [Next.js](https://nextjs.org) vibe-coded slop built to make it easy to browse auto-generated YouTube transcripts downloaded with yt-dlp.
-First, run the development server:
+There's basic white-labeling functionality with the `settings.json` file if you feel like using this code for another purpose.
-```bash
-npm run dev
-# or
-yarn dev
-# or
-pnpm dev
-# or
-bun dev
+## Adding Transcripts
+
+This repo hosts the code for the app, and transcripts are to be inserted in the "transcripts" directory at the top level (alongside this README).
+
+The file structure looks like this:
+
+```
+.
+├── README.md
+├── transcripts
+│ └── data
+│ ├── 20180627_wCLdlbEHmLE-Introducing_The_Incredible_Salt_Mine
+│ │ ├── metadata.info.json
+│ │ └── transcript.en.vtt
+│ ├── 20180714_P1Lfy0rStkI-The_Incredible_Salt_Mine_Episode_001
+│ │ ├── metadata.info.json
+│ │ └── transcript.en.vtt
```
-Open [http://localhost:3000](http://localhost:3000) with your browser to see the result.
+The directory names could be anything, but are prefaced by date for easy lazy date sorting when reading the dir.
+The `metadata.info.json` and `transcript.en.vtt` filenames are hard-coded for simplicity's sake.
+
+## Getting Started
-You can start editing the page by modifying `app/page.tsx`. The page auto-updates as you edit the file.
+Make sure you have `pnpm` installed. Run these commands to build the static site and locally host it.
-This project uses [`next/font`](https://nextjs.org/docs/app/building-your-application/optimizing/fonts) to automatically optimize and load [Geist](https://vercel.com/font), a new font family for Vercel.
+```bash
+pnpm run build
+pnpm run start
+```
-## Learn More
+Open [http://localhost:3000](http://localhost:3000) with your browser to see the result.
-To learn more about Next.js, take a look at the following resources:
+## Development
-- [Next.js Documentation](https://nextjs.org/docs) - learn about Next.js features and API.
-- [Learn Next.js](https://nextjs.org/learn) - an interactive Next.js tutorial.
+If you want to change the code, use the development server for hot-reloading and error messages.
-You can check out [the Next.js GitHub repository](https://github.com/vercel/next.js) - your feedback and contributions are welcome!
+```bash
+pnpm run dev
+```
-## Deploy on Vercel
+## Downloading Transcripts
-The easiest way to deploy your Next.js app is to use the [Vercel Platform](https://vercel.com/new?utm_medium=default-template&filter=next.js&utm_source=create-next-app&utm_campaign=create-next-app-readme) from the creators of Next.js.
+Use this command to download transcripts that end up in the expected format:
-Check out our [Next.js deployment documentation](https://nextjs.org/docs/app/building-your-application/deploying) for more details.
+```
+yt-dlp --ignore-config --skip-download --restrict-filenames --write-info-json -o "subtitle:%(upload_date)s_%(id)s-%(fulltitle,title)s/transcript" -o "infojson:%(upload_date)s_%(id)s-%(fulltitle,title)s/metadata" --write-auto-subs -- <VIDEO_OR_PLAYLIST>
+```
diff --git a/app/PlayerProvider.tsx b/app/PlayerProvider.tsx
@@ -0,0 +1,211 @@
+"use client";
+
+import dynamic from "next/dynamic";
+import {
+ createContext,
+ useCallback,
+ useContext,
+ useEffect,
+ useMemo,
+ useRef,
+ useState,
+} from "react";
+import type ReactPlayerType from "react-player";
+import { fetchTranscript } from "./transcriptCache";
+
+const ReactPlayer = dynamic(() => import("react-player/youtube"), {
+ ssr: false,
+});
+
+export type Cue = { start: number; end: number; text: string };
+
+export type TranscriptSummary = {
+ slug: string;
+ id: string;
+ title: string;
+ uploadDate: string;
+ date: string;
+ duration: string;
+ channel: string;
+};
+
+export type TranscriptData = TranscriptSummary & {
+ description?: string;
+ cues?: Cue[];
+};
+
+type Status = "idle" | "loading" | "ready";
+
+type PlayerState = {
+ activeSlug: string | null;
+ data: TranscriptData | null;
+ status: Status;
+ modalOpen: boolean;
+ playing: boolean;
+ currentTime: number;
+ openTranscript: (slug: string, start?: number) => void;
+ closeModal: () => void;
+ seekTo: (seconds: number) => void;
+};
+
+const Ctx = createContext<PlayerState | null>(null);
+
+export function usePlayer(): PlayerState {
+ const v = useContext(Ctx);
+ if (!v) throw new Error("usePlayer must be inside PlayerProvider");
+ return v;
+}
+
+export function PlayerProvider({
+ children,
+ transcripts,
+}: {
+ children: React.ReactNode;
+ transcripts: TranscriptSummary[];
+}) {
+ const [activeSlug, setActiveSlug] = useState<string | null>(null);
+ const [data, setData] = useState<TranscriptData | null>(null);
+ const [status, setStatus] = useState<Status>("idle");
+ const [modalOpen, setModalOpen] = useState(false);
+ const [playing, setPlaying] = useState(false);
+ const [currentTime, setCurrentTime] = useState(0);
+ const playerRef = useRef<ReactPlayerType | null>(null);
+ const pendingSeekRef = useRef<number | null>(null);
+ const readyForSlugRef = useRef<string | null>(null);
+
+ const bySlug = useMemo(() => {
+ const m = new Map<string, TranscriptSummary>();
+ for (const t of transcripts) m.set(t.slug, t);
+ return m;
+ }, [transcripts]);
+
+ const openTranscript = useCallback(
+ (slug: string, start?: number) => {
+ setModalOpen(true);
+ if (slug === activeSlug) {
+ if (typeof start === "number" && playerRef.current) {
+ playerRef.current.seekTo(start, "seconds");
+ setPlaying(true);
+ }
+ return;
+ }
+ const summary = bySlug.get(slug);
+ if (!summary) return;
+ setActiveSlug(slug);
+ setData({ ...summary });
+ setStatus("loading");
+ readyForSlugRef.current = null;
+ pendingSeekRef.current = typeof start === "number" ? start : 0;
+ fetchTranscript(slug).then((full) => {
+ setActiveSlug((current) => {
+ if (current !== slug) return current;
+ setData({ ...summary, description: full.description, cues: full.cues });
+ setStatus("ready");
+ return current;
+ });
+ }).catch(() => {
+ setActiveSlug((current) => {
+ if (current !== slug) return current;
+ setStatus("ready");
+ return current;
+ });
+ });
+ },
+ [activeSlug, bySlug],
+ );
+
+ const closeModal = useCallback(() => setModalOpen(false), []);
+
+ const seekTo = useCallback((seconds: number) => {
+ if (playerRef.current) {
+ playerRef.current.seekTo(seconds, "seconds");
+ setPlaying(true);
+ } else {
+ pendingSeekRef.current = seconds;
+ }
+ }, []);
+
+ useEffect(() => {
+ if (!modalOpen) return;
+ const onKey = (e: KeyboardEvent) => {
+ if (e.key === "Escape") closeModal();
+ };
+ window.addEventListener("keydown", onKey);
+ return () => window.removeEventListener("keydown", onKey);
+ }, [modalOpen, closeModal]);
+
+ const handleReady = () => {
+ if (
+ readyForSlugRef.current !== activeSlug &&
+ pendingSeekRef.current !== null &&
+ playerRef.current
+ ) {
+ playerRef.current.seekTo(pendingSeekRef.current, "seconds");
+ setPlaying(true);
+ pendingSeekRef.current = null;
+ readyForSlugRef.current = activeSlug;
+ }
+ };
+
+ const value: PlayerState = {
+ activeSlug,
+ data,
+ status,
+ modalOpen,
+ playing,
+ currentTime,
+ openTranscript,
+ closeModal,
+ seekTo,
+ };
+
+ const playerMode: "hidden" | "mini" | "modal" = !activeSlug
+ ? "hidden"
+ : modalOpen
+ ? "modal"
+ : "mini";
+
+ return (
+ <Ctx.Provider value={value}>
+ {children}
+
+ {playerMode !== "hidden" && activeSlug && data && (
+ <div
+ className={
+ playerMode === "modal"
+ ? "fixed left-1/2 -translate-x-1/2 top-16 w-[min(92vw,960px)] aspect-video z-[60] rounded-lg overflow-hidden bg-black shadow-2xl"
+ : "fixed bottom-4 right-4 w-72 sm:w-96 aspect-video z-[60] rounded-lg overflow-hidden bg-black shadow-2xl ring-1 ring-black/40"
+ }
+ >
+ <ReactPlayer
+ ref={(p: ReactPlayerType | null) => {
+ playerRef.current = p;
+ }}
+ url={`https://www.youtube.com/watch?v=${data.id}`}
+ width="100%"
+ height="100%"
+ playing={playing}
+ controls
+ onReady={handleReady}
+ onPlay={() => setPlaying(true)}
+ onPause={() => setPlaying(false)}
+ onProgress={(s: { playedSeconds: number }) =>
+ setCurrentTime(s.playedSeconds)
+ }
+ />
+ {playerMode === "mini" && (
+ <button
+ type="button"
+ aria-label="Reopen transcript"
+ onClick={() => setModalOpen(true)}
+ className="absolute inset-x-0 bottom-0 bg-gradient-to-t from-black/80 to-transparent text-white text-xs px-3 py-1.5 text-left hover:from-black/95"
+ >
+ <span className="font-medium line-clamp-1">{data.title}</span>
+ <span className="opacity-70">click to reopen</span>
+ </button>
+ )}
+ </div>
+ )}
+ </Ctx.Provider>
+ );
+}
diff --git a/app/TranscriptModal.tsx b/app/TranscriptModal.tsx
@@ -0,0 +1,142 @@
+"use client";
+
+import { useEffect, useRef } from "react";
+import { usePlayer } from "./PlayerProvider";
+import { formatTimestamp } from "@/lib/vtt";
+import { formatDate } from "@/lib/format";
+
+export default function TranscriptModal() {
+ const {
+ modalOpen,
+ activeSlug,
+ data,
+ status,
+ currentTime,
+ closeModal,
+ seekTo,
+ } = usePlayer();
+ const activeRef = useRef<HTMLLIElement | null>(null);
+ const scrollRef = useRef<HTMLDivElement | null>(null);
+
+ const cues = data?.cues ?? [];
+ const activeIndex = findActiveIndex(cues, currentTime);
+
+ useEffect(() => {
+ if (!modalOpen) return;
+ activeRef.current?.scrollIntoView({ block: "nearest", behavior: "smooth" });
+ }, [activeIndex, modalOpen]);
+
+ useEffect(() => {
+ if (modalOpen) {
+ const prev = document.body.style.overflow;
+ document.body.style.overflow = "hidden";
+ return () => {
+ document.body.style.overflow = prev;
+ };
+ }
+ }, [modalOpen]);
+
+ if (!modalOpen || !activeSlug) return null;
+
+ return (
+ <div className="fixed inset-0 z-50 flex flex-col">
+ <button
+ type="button"
+ aria-label="Close"
+ onClick={closeModal}
+ className="absolute inset-0 bg-black/70 backdrop-blur-sm"
+ />
+ <div className="relative flex flex-col items-center w-full h-full overflow-hidden pt-4 pointer-events-none">
+ <div
+ className="w-[min(92vw,960px)] pointer-events-auto flex flex-col gap-3"
+ style={{
+ marginTop: "calc(min(92vw, 960px) * 9 / 16 + 3rem)",
+ maxHeight:
+ "calc(100vh - (min(92vw, 960px) * 9 / 16) - 5rem)",
+ }}
+ >
+ <div className="flex items-start justify-between gap-3 text-white">
+ <div className="min-w-0">
+ <h2 className="text-lg font-semibold leading-tight truncate">
+ {data?.title ?? "Loading…"}
+ </h2>
+ {data && (
+ <p className="text-xs text-zinc-300 mt-0.5">
+ {formatDate(data.uploadDate)}
+ {data.channel && ` · ${data.channel}`}
+ </p>
+ )}
+ </div>
+ <button
+ type="button"
+ onClick={closeModal}
+ className="shrink-0 rounded-md bg-white/10 hover:bg-white/20 text-white px-3 py-1.5 text-sm"
+ >
+ Close
+ </button>
+ </div>
+
+ <div
+ ref={scrollRef}
+ className="flex-1 overflow-y-auto rounded-lg border border-zinc-700 bg-zinc-900"
+ >
+ {status === "loading" && (
+ <p className="text-sm text-zinc-400 p-4">Loading transcript…</p>
+ )}
+ {status === "ready" && cues.length === 0 && (
+ <p className="text-sm text-zinc-400 p-4">No cues available.</p>
+ )}
+ {status === "ready" && cues.length > 0 && (
+ <ol className="divide-y divide-zinc-800 text-zinc-100">
+ {cues.map((cue, i) => {
+ const isActive = i === activeIndex;
+ return (
+ <li
+ key={i}
+ ref={isActive ? activeRef : undefined}
+ className={
+ isActive ? "bg-blue-950/50" : ""
+ }
+ >
+ <button
+ type="button"
+ onClick={() => seekTo(cue.start)}
+ className="w-full text-left flex gap-3 px-3 py-2 hover:bg-zinc-800"
+ >
+ <span className="text-xs font-mono text-zinc-400 shrink-0 w-16 pt-0.5">
+ {formatTimestamp(cue.start)}
+ </span>
+ <span className="text-sm">{cue.text}</span>
+ </button>
+ </li>
+ );
+ })}
+ </ol>
+ )}
+ </div>
+ </div>
+ </div>
+ </div>
+ );
+}
+
+function findActiveIndex(
+ cues: { start: number }[],
+ t: number,
+): number {
+ if (!cues.length) return -1;
+ let lo = 0;
+ let hi = cues.length - 1;
+ let found = -1;
+ while (lo <= hi) {
+ const mid = (lo + hi) >> 1;
+ if (cues[mid].start <= t) {
+ found = mid;
+ lo = mid + 1;
+ } else {
+ hi = mid - 1;
+ }
+ }
+ return found;
+}
+
diff --git a/app/TranscriptSearch.tsx b/app/TranscriptSearch.tsx
@@ -0,0 +1,389 @@
+"use client";
+
+import { useEffect, useMemo, useState, useSyncExternalStore } from "react";
+import { usePlayer } from "./PlayerProvider";
+import { fetchTranscript } from "./transcriptCache";
+import type { TranscriptDetail } from "@/lib/transcripts";
+
+type Summary = {
+ slug: string;
+ id: string;
+ title: string;
+ uploadDate: string;
+ date: string;
+ duration: string;
+ channel: string;
+};
+
+type Hit = {
+ slug: string;
+ title: string;
+ date: string;
+ start: number;
+ text: string;
+};
+
+const MAX_HITS = 200;
+
+const urlListeners = new Set<() => void>();
+function subscribeUrl(cb: () => void) {
+ urlListeners.add(cb);
+ window.addEventListener("popstate", cb);
+ return () => {
+ urlListeners.delete(cb);
+ window.removeEventListener("popstate", cb);
+ };
+}
+function getUrlSnapshot() {
+ return window.location.search;
+}
+function getServerUrlSnapshot() {
+ return "";
+}
+function writeUrl(query: string, useRegex: boolean) {
+ const params = new URLSearchParams(window.location.search);
+ if (query) params.set("q", query);
+ else params.delete("q");
+ if (useRegex) params.set("re", "1");
+ else params.delete("re");
+ const qs = params.toString();
+ const next = `${window.location.pathname}${qs ? `?${qs}` : ""}`;
+ if (next === window.location.pathname + window.location.search) return;
+ window.history.replaceState(window.history.state, "", next);
+ for (const l of urlListeners) l();
+}
+
+export default function TranscriptSearch({
+ transcripts,
+}: {
+ transcripts: Summary[];
+}) {
+ const search = useSyncExternalStore(
+ subscribeUrl,
+ getUrlSnapshot,
+ getServerUrlSnapshot,
+ );
+ const { query, useRegex } = useMemo(() => {
+ const params = new URLSearchParams(search);
+ return {
+ query: params.get("q") ?? "",
+ useRegex: params.get("re") === "1",
+ };
+ }, [search]);
+ const setQuery = (q: string) => writeUrl(q, useRegex);
+ const setUseRegex = (r: boolean) => writeUrl(query, r);
+ const [lastHit, setLastHit] = useState<{
+ slug: string;
+ start: number;
+ } | null>(null);
+ const [fetched, setFetched] = useState<Record<string, TranscriptDetail>>({});
+ const { openTranscript } = usePlayer();
+
+ const trimmed = query.trim();
+
+ useEffect(() => {
+ if (!trimmed) return;
+ let cancelled = false;
+ for (const t of transcripts) {
+ if (fetched[t.slug]) continue;
+ fetchTranscript(t.slug)
+ .then((full) => {
+ if (cancelled) return;
+ setFetched((prev) =>
+ prev[t.slug] ? prev : { ...prev, [t.slug]: full },
+ );
+ })
+ .catch(() => {});
+ }
+ return () => {
+ cancelled = true;
+ };
+ }, [trimmed, transcripts, fetched]);
+
+ const loadedCount = Object.keys(fetched).length;
+ const totalCount = transcripts.length;
+ const fetching = Boolean(trimmed) && loadedCount < totalCount;
+
+ const regex = useMemo<{ re: RegExp | null; error: string | null }>(() => {
+ if (!trimmed || !useRegex) return { re: null, error: null };
+ try {
+ return { re: new RegExp(trimmed, "i"), error: null };
+ } catch (e) {
+ return { re: null, error: (e as Error).message };
+ }
+ }, [trimmed, useRegex]);
+
+ const matches = (haystack: string): boolean => {
+ if (!trimmed) return true;
+ if (useRegex) return regex.re ? regex.re.test(haystack) : false;
+ return haystack.toLowerCase().includes(trimmed.toLowerCase());
+ };
+
+ const summaryBySlug = useMemo(() => {
+ const m = new Map<string, Summary>();
+ for (const t of transcripts) m.set(t.slug, t);
+ return m;
+ }, [transcripts]);
+
+ const titleMatches = useMemo(() => {
+ if (!trimmed) return transcripts;
+ if (useRegex && !regex.re) return [];
+ return transcripts.filter((t) => matches(t.title));
+ // eslint-disable-next-line react-hooks/exhaustive-deps
+ }, [trimmed, transcripts, useRegex, regex.re]);
+
+ const cueHits = useMemo<Hit[]>(() => {
+ if (!trimmed) return [];
+ if (useRegex && !regex.re) return [];
+ const hits: Hit[] = [];
+ for (const t of transcripts) {
+ const full = fetched[t.slug];
+ if (!full?.cues) continue;
+ for (const c of full.cues) {
+ if (matches(c.text)) {
+ hits.push({
+ slug: t.slug,
+ title: t.title,
+ date: summaryBySlug.get(t.slug)?.date ?? "",
+ start: Math.round(c.start),
+ text: c.text,
+ });
+ if (hits.length >= MAX_HITS) return hits;
+ }
+ }
+ }
+ return hits;
+ // eslint-disable-next-line react-hooks/exhaustive-deps
+ }, [trimmed, transcripts, fetched, summaryBySlug, useRegex, regex.re]);
+
+ return (
+ <div className="flex flex-col gap-6">
+ <div className="flex flex-col gap-1">
+ <input
+ type="search"
+ value={query}
+ onChange={(e) => setQuery(e.target.value)}
+ placeholder={
+ useRegex
+ ? "Regex pattern (case-insensitive)…"
+ : "Search titles and transcripts..."
+ }
+ className={`w-full rounded-lg border ${
+ regex.error
+ ? "border-red-500"
+ : "border-zinc-300 dark:border-zinc-700"
+ } bg-white dark:bg-zinc-900 px-4 py-3 text-base outline-none focus:ring-2 focus:ring-blue-500 ${
+ useRegex ? "font-mono" : ""
+ }`}
+ />
+ <label className="flex items-center gap-2 text-sm text-zinc-600 dark:text-zinc-400 select-none">
+ <input
+ type="checkbox"
+ checked={useRegex}
+ onChange={(e) => setUseRegex(e.target.checked)}
+ className="accent-blue-600"
+ />
+ Regex
+ {regex.error && (
+ <span className="text-red-600 font-mono text-xs">
+ {regex.error}
+ </span>
+ )}
+ </label>
+ </div>
+
+ {!trimmed && (
+ <TranscriptGrid
+ items={transcripts}
+ onOpen={(slug) => {
+ setLastHit({ slug, start: 0 });
+ openTranscript(slug);
+ }}
+ lastSlug={lastHit?.slug ?? null}
+ />
+ )}
+
+ {trimmed && (
+ <>
+ <section>
+ <h2 className="text-sm font-medium text-zinc-500 mb-2">
+ Titles ({titleMatches.length})
+ </h2>
+ {titleMatches.length > 0 ? (
+ <TranscriptGrid
+ items={titleMatches}
+ onOpen={(slug) => {
+ setLastHit({ slug, start: 0 });
+ openTranscript(slug);
+ }}
+ lastSlug={lastHit?.slug ?? null}
+ />
+ ) : (
+ <p className="text-sm text-zinc-500">No title matches.</p>
+ )}
+ </section>
+
+ <section>
+ <h2 className="text-sm font-medium text-zinc-500 mb-2 flex items-center gap-2">
+ <span>
+ Transcript mentions{" "}
+ {cueHits.length >= MAX_HITS
+ ? `(${MAX_HITS}+)`
+ : `(${cueHits.length})`}
+ </span>
+ {fetching && (
+ <span className="text-xs text-zinc-400 font-normal">
+ loaded {loadedCount}/{totalCount}…
+ </span>
+ )}
+ </h2>
+ {cueHits.length === 0 && !fetching && (
+ <p className="text-sm text-zinc-500">No transcript mentions.</p>
+ )}
+ <ul className="flex flex-col divide-y divide-zinc-200 dark:divide-zinc-800 border border-zinc-200 dark:border-zinc-800 rounded-lg overflow-hidden bg-white dark:bg-zinc-900">
+ {cueHits.map((h, i) => {
+ const isLast =
+ lastHit?.slug === h.slug && lastHit?.start === h.start;
+ return (
+ <li key={i}>
+ <button
+ type="button"
+ onClick={() => {
+ setLastHit({ slug: h.slug, start: h.start });
+ openTranscript(h.slug, h.start);
+ }}
+ className={`w-full text-left flex gap-3 px-4 py-2 hover:bg-zinc-50 dark:hover:bg-zinc-800 ${
+ isLast
+ ? "bg-amber-100/70 dark:bg-amber-500/10 ring-1 ring-inset ring-amber-400/60"
+ : ""
+ }`}
+ >
+ <span className="text-xs font-mono text-zinc-500 shrink-0 w-28">
+ {formatSeconds(h.start)}
+ </span>
+ <span className="flex-1 min-w-0">
+ <span className="block text-xs text-zinc-500 truncate">
+ {h.title}
+ {h.date && ` · ${h.date}`}
+ </span>
+ <span className="block text-sm">
+ {highlight(h.text, trimmed, useRegex)}
+ </span>
+ </span>
+ </button>
+ </li>
+ );
+ })}
+ </ul>
+ </section>
+ </>
+ )}
+ </div>
+ );
+}
+
+function TranscriptGrid({
+ items,
+ onOpen,
+ lastSlug,
+}: {
+ items: Summary[];
+ onOpen: (slug: string) => void;
+ lastSlug?: string | null;
+}) {
+ return (
+ <ul className="flex flex-col divide-y divide-zinc-200 dark:divide-zinc-800 border border-zinc-200 dark:border-zinc-800 rounded-lg overflow-hidden bg-white dark:bg-zinc-900">
+ {items.map((t) => {
+ const isLast = lastSlug === t.slug;
+ return (
+ <li key={t.slug}>
+ <button
+ type="button"
+ onClick={() => onOpen(t.slug)}
+ className={`w-full text-left flex items-baseline gap-3 px-4 py-2 hover:bg-zinc-50 dark:hover:bg-zinc-800 ${
+ isLast
+ ? "bg-amber-100/70 dark:bg-amber-500/10 ring-1 ring-inset ring-amber-400/60"
+ : ""
+ }`}
+ >
+ <span className="text-xs font-mono text-zinc-500 shrink-0 w-24">
+ {t.date}
+ </span>
+ <span className="flex-1 min-w-0 font-medium truncate">
+ {t.title}
+ </span>
+ {t.duration && (
+ <span className="text-xs font-mono text-zinc-500 shrink-0">
+ {t.duration}
+ </span>
+ )}
+ </button>
+ </li>
+ );
+ })}
+ </ul>
+ );
+}
+
+function formatSeconds(s: number): string {
+ const h = Math.floor(s / 3600);
+ const m = Math.floor((s % 3600) / 60);
+ const sec = s % 60;
+ const mm = String(m).padStart(2, "0");
+ const ss = String(sec).padStart(2, "0");
+ return h > 0 ? `${h}:${mm}:${ss}` : `${mm}:${ss}`;
+}
+
+function highlight(text: string, q: string, useRegex = false) {
+ if (!q) return text;
+ const parts: React.ReactNode[] = [];
+
+ if (useRegex) {
+ let re: RegExp;
+ try {
+ re = new RegExp(q, "gi");
+ } catch {
+ return text;
+ }
+ let last = 0;
+ let m: RegExpExecArray | null;
+ let key = 0;
+ while ((m = re.exec(text)) !== null) {
+ if (m.index > last) parts.push(text.slice(last, m.index));
+ parts.push(
+ <mark
+ key={key++}
+ className="bg-yellow-200 dark:bg-yellow-500/30 text-inherit"
+ >
+ {m[0]}
+ </mark>,
+ );
+ last = m.index + m[0].length;
+ if (m[0].length === 0) re.lastIndex++;
+ }
+ if (last < text.length) parts.push(text.slice(last));
+ return <>{parts}</>;
+ }
+
+ const lower = text.toLowerCase();
+ const ql = q.toLowerCase();
+ let i = 0;
+ while (i < text.length) {
+ const idx = lower.indexOf(ql, i);
+ if (idx === -1) {
+ parts.push(text.slice(i));
+ break;
+ }
+ if (idx > i) parts.push(text.slice(i, idx));
+ parts.push(
+ <mark
+ key={idx}
+ className="bg-yellow-200 dark:bg-yellow-500/30 text-inherit"
+ >
+ {text.slice(idx, idx + q.length)}
+ </mark>,
+ );
+ i = idx + q.length;
+ }
+ return <>{parts}</>;
+}
diff --git a/app/layout.tsx b/app/layout.tsx
@@ -1,5 +1,7 @@
import type { Metadata } from "next";
import { Geist, Geist_Mono } from "next/font/google";
+import Link from "next/link";
+import { getSettings } from "@/lib/settings";
import "./globals.css";
const geistSans = Geist({
@@ -12,22 +14,37 @@ const geistMono = Geist_Mono({
subsets: ["latin"],
});
-export const metadata: Metadata = {
- title: "Create Next App",
- description: "Generated by create next app",
-};
+export function generateMetadata(): Metadata {
+ const s = getSettings();
+ return {
+ title: s.siteTitle,
+ description: s.siteDescription,
+ };
+}
-export default function RootLayout({
+export default async function RootLayout({
children,
}: Readonly<{
children: React.ReactNode;
}>) {
+ const settings = getSettings();
return (
<html
lang="en"
className={`${geistSans.variable} ${geistMono.variable} h-full antialiased`}
>
- <body className="min-h-full flex flex-col">{children}</body>
+ <body className="min-h-full flex flex-col bg-zinc-50 text-zinc-900 dark:bg-zinc-950 dark:text-zinc-100 font-sans">
+ <header className="border-b border-zinc-200 dark:border-zinc-800 bg-white/70 dark:bg-zinc-900/70 backdrop-blur sticky top-0 z-20">
+ <div className="max-w-6xl mx-auto px-4 py-3 flex items-center justify-between">
+ <Link href="/" className="font-semibold tracking-tight">
+ {settings.headerTitle}
+ </Link>
+ </div>
+ </header>
+ <main className="flex-1 w-full max-w-6xl mx-auto px-4 py-6">
+ {children}
+ </main>
+ </body>
</html>
);
}
diff --git a/app/page.tsx b/app/page.tsx
@@ -1,65 +1,49 @@
-import Image from "next/image";
+import {
+ listTranscripts,
+ formatDate,
+ formatDuration,
+} from "@/lib/transcripts";
+import { getSettings } from "@/lib/settings";
+import { PlayerProvider } from "./PlayerProvider";
+import TranscriptModal from "./TranscriptModal";
+import TranscriptSearch from "./TranscriptSearch";
+
+export default async function Home() {
+ const settings = getSettings();
+ const transcripts = await listTranscripts();
+ const summaries = transcripts.map((t) => ({
+ slug: t.slug,
+ id: t.id,
+ title: t.title,
+ uploadDate: t.uploadDate,
+ date: formatDate(t.uploadDate),
+ duration: formatDuration(t.duration),
+ channel: t.channel,
+ }));
-export default function Home() {
return (
- <div className="flex flex-col flex-1 items-center justify-center bg-zinc-50 font-sans dark:bg-black">
- <main className="flex flex-1 w-full max-w-3xl flex-col items-center justify-between py-32 px-16 bg-white dark:bg-black sm:items-start">
- <Image
- className="dark:invert"
- src="/next.svg"
- alt="Next.js logo"
- width={100}
- height={20}
- priority
- />
- <div className="flex flex-col items-center gap-6 text-center sm:items-start sm:text-left">
- <h1 className="max-w-xs text-3xl font-semibold leading-10 tracking-tight text-black dark:text-zinc-50">
- To get started, edit the page.tsx file.
+ <PlayerProvider transcripts={summaries}>
+ <div className="flex flex-col gap-6">
+ <div>
+ <h1 className="text-2xl font-semibold">
+ {summaries.length} transcripts
</h1>
- <p className="max-w-md text-lg leading-8 text-zinc-600 dark:text-zinc-400">
- Looking for a starting point or more instructions? Head over to{" "}
- <a
- href="https://vercel.com/templates?framework=next.js&utm_source=create-next-app&utm_medium=appdir-template-tw&utm_campaign=create-next-app"
- className="font-medium text-zinc-950 dark:text-zinc-50"
- >
- Templates
- </a>{" "}
- or the{" "}
- <a
- href="https://nextjs.org/learn?utm_source=create-next-app&utm_medium=appdir-template-tw&utm_campaign=create-next-app"
- className="font-medium text-zinc-950 dark:text-zinc-50"
- >
- Learning
- </a>{" "}
- center.
- </p>
+ {settings.homeTagline && (
+ <p className="text-sm text-zinc-500 mt-1">{settings.homeTagline}</p>
+ )}
</div>
- <div className="flex flex-col gap-4 text-base font-medium sm:flex-row">
- <a
- className="flex h-12 w-full items-center justify-center gap-2 rounded-full bg-foreground px-5 text-background transition-colors hover:bg-[#383838] dark:hover:bg-[#ccc] md:w-[158px]"
- href="https://vercel.com/new?utm_source=create-next-app&utm_medium=appdir-template-tw&utm_campaign=create-next-app"
- target="_blank"
- rel="noopener noreferrer"
- >
- <Image
- className="dark:invert"
- src="/vercel.svg"
- alt="Vercel logomark"
- width={16}
- height={16}
- />
- Deploy Now
+ <TranscriptSearch transcripts={summaries} />
+ <section className="border-t border-zinc-200 dark:border-zinc-800 pt-4 text-sm text-zinc-500 flex gap-4">
+ <span>Downloads:</span>
+ <a href="/code.tar.gz" className="underline hover:text-zinc-900 dark:hover:text-zinc-100">
+ code.tar.gz
</a>
- <a
- className="flex h-12 w-full items-center justify-center rounded-full border border-solid border-black/[.08] px-5 transition-colors hover:border-transparent hover:bg-black/[.04] dark:border-white/[.145] dark:hover:bg-[#1a1a1a] md:w-[158px]"
- href="https://nextjs.org/docs?utm_source=create-next-app&utm_medium=appdir-template-tw&utm_campaign=create-next-app"
- target="_blank"
- rel="noopener noreferrer"
- >
- Documentation
+ <a href="/transcripts.tar.xz" className="underline hover:text-zinc-900 dark:hover:text-zinc-100">
+ transcripts.tar.xz
</a>
- </div>
- </main>
- </div>
+ </section>
+ </div>
+ <TranscriptModal />
+ </PlayerProvider>
);
}
diff --git a/app/transcriptCache.ts b/app/transcriptCache.ts
@@ -0,0 +1,21 @@
+"use client";
+
+import type { TranscriptDetail } from "@/lib/transcripts";
+
+const cache = new Map<string, Promise<TranscriptDetail>>();
+
+export function fetchTranscript(slug: string): Promise<TranscriptDetail> {
+ const hit = cache.get(slug);
+ if (hit) return hit;
+ const p = fetch(`/transcripts/${slug}`).then((r) => {
+ if (!r.ok) throw new Error(`Failed to fetch transcript ${slug}`);
+ return r.json() as Promise<TranscriptDetail>;
+ });
+ cache.set(slug, p);
+ p.catch(() => cache.delete(slug));
+ return p;
+}
+
+export function peekTranscript(slug: string): Promise<TranscriptDetail> | undefined {
+ return cache.get(slug);
+}
diff --git a/app/transcripts/[slug]/route.ts b/app/transcripts/[slug]/route.ts
@@ -0,0 +1,15 @@
+import { listSlugs, loadTranscript } from "@/lib/transcripts";
+
+export const dynamic = "force-static";
+
+export function generateStaticParams() {
+ return listSlugs().map((slug) => ({ slug }));
+}
+
+export async function GET(
+ _req: Request,
+ { params }: { params: Promise<{ slug: string }> },
+) {
+ const { slug } = await params;
+ return Response.json(await loadTranscript(slug));
+}
diff --git a/create-archives.sh b/create-archives.sh
@@ -0,0 +1,9 @@
+#!/bin/bash
+git archive -9 --format tar.gz -o "./public/code.tar.gz" main
+cd transcripts
+
+# use xz over gz to get the file below 25MB
+
+# Uncommend this line to use .tar.gz for compatability and compression speed, but at a significantly higher file size
+# git archive -9 --format tar.gz -o "../public/transcripts.tar.gz" main
+git archive --format tar main | xz -9 > ../public/transcripts.tar.xz
diff --git a/lib/format.ts b/lib/format.ts
@@ -0,0 +1,14 @@
+export function formatDate(yyyymmdd: string): string {
+ if (!/^\d{8}$/.test(yyyymmdd)) return yyyymmdd;
+ return `${yyyymmdd.slice(0, 4)}-${yyyymmdd.slice(4, 6)}-${yyyymmdd.slice(6, 8)}`;
+}
+
+export function formatDuration(seconds: number): string {
+ if (!seconds) return "";
+ const h = Math.floor(seconds / 3600);
+ const m = Math.floor((seconds % 3600) / 60);
+ const s = Math.floor(seconds % 60);
+ const mm = String(m).padStart(2, "0");
+ const ss = String(s).padStart(2, "0");
+ return h > 0 ? `${h}:${mm}:${ss}` : `${m}:${ss}`;
+}
diff --git a/lib/settings.ts b/lib/settings.ts
@@ -0,0 +1,31 @@
+import fs from "node:fs";
+import path from "node:path";
+
+export type SiteSettings = {
+ siteTitle: string;
+ siteDescription: string;
+ headerTitle: string;
+ homeTagline: string;
+};
+
+const DEFAULTS: SiteSettings = {
+ siteTitle: "Transcript Browser",
+ siteDescription: "Browse and search video transcripts",
+ headerTitle: "Transcript Browser",
+ homeTagline: "",
+};
+
+let cached: SiteSettings | null = null;
+
+export function getSettings(): SiteSettings {
+ if (cached) return cached;
+ const file = path.join(process.cwd(), "settings.json");
+ let parsed: Partial<SiteSettings> = {};
+ try {
+ parsed = JSON.parse(fs.readFileSync(file, "utf8")) as Partial<SiteSettings>;
+ } catch {
+ parsed = {};
+ }
+ cached = { ...DEFAULTS, ...parsed };
+ return cached;
+}
diff --git a/lib/transcripts.ts b/lib/transcripts.ts
@@ -0,0 +1,86 @@
+import fs from "node:fs";
+import path from "node:path";
+import { parseVtt, type Cue } from "./vtt";
+import { readFile } from "fs-extra";
+
+const ROOT = path.join(process.cwd(), "transcripts", "data");
+
+export type TranscriptSummary = {
+ slug: string;
+ id: string;
+ title: string;
+ uploadDate: string;
+ duration: number;
+ channel: string;
+ description: string;
+};
+
+export type TranscriptDetail = TranscriptSummary & {
+ cues: Cue[] | undefined;
+};
+
+type RawMetadata = {
+ id?: string;
+ title?: string;
+ upload_date?: string;
+ duration?: number;
+ channel?: string;
+ uploader?: string;
+ description?: string;
+};
+
+async function readMetadata(slug: string): Promise<RawMetadata> {
+ const file = path.join(ROOT, slug, "metadata.info.json");
+ const raw = await readFile(file, "utf8");
+ return JSON.parse(raw) as RawMetadata;
+}
+
+async function readTranscript(slug: string): Promise<Cue[] | undefined> {
+ const dir = path.join(ROOT, slug);
+ const vttPath = path.join(dir, "transcript.en.vtt");
+ try {
+ const fileContents = await readFile(vttPath, "utf8");
+ return parseVtt(fileContents);
+ } catch {
+ return undefined;
+ }
+}
+
+function summarize(slug: string, meta: RawMetadata): TranscriptSummary {
+ const dateFromSlug = slug.match(/^(\d{8})_/)?.[1];
+ return {
+ slug,
+ id: meta.id ?? slug.split("_")[1]?.split("-")[0] ?? slug,
+ title: meta.title ?? slug,
+ uploadDate: meta.upload_date ?? dateFromSlug ?? "",
+ duration: meta.duration ?? 0,
+ channel: meta.channel ?? meta.uploader ?? "",
+ description: meta.description ?? "",
+ };
+}
+
+export function listSlugs(): string[] {
+ return fs
+ .readdirSync(ROOT, { withFileTypes: true })
+ .filter((d) => d.isDirectory())
+ .map((d) => d.name)
+ .sort();
+}
+
+export async function listTranscripts(): Promise<TranscriptSummary[]> {
+ return await Promise.all(
+ listSlugs().map(async (slug) => summarize(slug, await readMetadata(slug))),
+ );
+}
+
+export async function loadTranscript(slug: string): Promise<TranscriptDetail> {
+ const meta = await readMetadata(slug);
+ const cues = await readTranscript(slug);
+ return { ...summarize(slug, meta), cues };
+}
+
+export async function loadAllTranscripts(): Promise<TranscriptDetail[]> {
+ return Promise.all(listSlugs().map((slug) => loadTranscript(slug)));
+}
+
+export { formatDate, formatDuration } from "./format";
diff --git a/lib/vtt.ts b/lib/vtt.ts
@@ -0,0 +1,85 @@
+export type Cue = { start: number; end: number; text: string };
+
+const TIMING_TAG_RE = /<\d{2}:\d{2}:\d{2}\.\d{3}>/;
+
+function parseTimestamp(ts: string): number {
+ const m = ts.match(/(\d+):(\d+):(\d+)\.(\d+)/);
+ if (!m) return 0;
+ const [, h, mm, s, ms] = m;
+ return +h * 3600 + +mm * 60 + +s + +ms / 1000;
+}
+
+function decodeEntities(s: string): string {
+ return s
+ .replace(/ /g, " ")
+ .replace(/&/g, "&")
+ .replace(/</g, "<")
+ .replace(/>/g, ">")
+ .replace(/'/g, "'")
+ .replace(/"/g, '"');
+}
+
+function stripTags(s: string): string {
+ return decodeEntities(s)
+ .replace(/<\d{2}:\d{2}:\d{2}\.\d{3}>/g, "")
+ .replace(/<\/?c[^>]*>/g, "")
+ .replace(/\s+/g, " ")
+ .trim();
+}
+
+export function parseVtt(src: string): Cue[] {
+ const lines = src.replace(/\r\n/g, "\n").split("\n");
+ const cues: Cue[] = [];
+ let i = 0;
+
+ while (i < lines.length) {
+ const line = lines[i];
+ const m = line.match(
+ /(\d{2}:\d{2}:\d{2}\.\d{3})\s+-->\s+(\d{2}:\d{2}:\d{2}\.\d{3})/,
+ );
+ if (!m) {
+ i++;
+ continue;
+ }
+ const start = parseTimestamp(m[1]);
+ const end = parseTimestamp(m[2]);
+ i++;
+ const body: string[] = [];
+ while (i < lines.length && lines[i].trim() !== "") {
+ body.push(lines[i]);
+ i++;
+ }
+
+ const taggedLines = body.filter((l) => TIMING_TAG_RE.test(l));
+ let text: string;
+ if (taggedLines.length > 0) {
+ text = stripTags(taggedLines[taggedLines.length - 1]);
+ } else {
+ continue;
+ }
+
+ if (text) cues.push({ start, end, text });
+ }
+
+ const deduped: Cue[] = [];
+ for (const c of cues) {
+ const prev = deduped[deduped.length - 1];
+ if (prev && prev.text === c.text) {
+ prev.end = c.end;
+ continue;
+ }
+ deduped.push({ ...c });
+ }
+
+ return deduped;
+}
+
+export function formatTimestamp(seconds: number): string {
+ const s = Math.floor(seconds);
+ const h = Math.floor(s / 3600);
+ const m = Math.floor((s % 3600) / 60);
+ const sec = s % 60;
+ const mm = String(m).padStart(2, "0");
+ const ss = String(sec).padStart(2, "0");
+ return h > 0 ? `${h}:${mm}:${ss}` : `${mm}:${ss}`;
+}
diff --git a/next.config.ts b/next.config.ts
@@ -1,7 +1,9 @@
import type { NextConfig } from "next";
const nextConfig: NextConfig = {
- /* config options here */
+ output: "export",
+ trailingSlash: true,
+ images: { unoptimized: true },
};
export default nextConfig;
diff --git a/package.json b/package.json
@@ -5,22 +5,27 @@
"scripts": {
"dev": "next dev",
"build": "next build",
- "start": "next start",
+ "start": "serve out",
"lint": "eslint"
},
"dependencies": {
"next": "16.2.3",
"react": "19.2.4",
- "react-dom": "19.2.4"
+ "react-dom": "19.2.4",
+ "react-player": "^2.16.0",
+ "serve": "^14.2.6"
},
"devDependencies": {
"@tailwindcss/postcss": "^4",
+ "@types/fs-extra": "^11.0.4",
"@types/node": "^20",
"@types/react": "^19",
"@types/react-dom": "^19",
"eslint": "^9",
"eslint-config-next": "16.2.3",
+ "fs-extra": "^11.3.4",
"tailwindcss": "^4",
+ "tsx": "^4.19.2",
"typescript": "^5"
}
}
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
@@ -17,10 +17,19 @@ importers:
react-dom:
specifier: 19.2.4
version: 19.2.4(react@19.2.4)
+ react-player:
+ specifier: ^2.16.0
+ version: 2.16.1(react@19.2.4)
+ serve:
+ specifier: ^14.2.6
+ version: 14.2.6
devDependencies:
'@tailwindcss/postcss':
specifier: ^4
version: 4.2.2
+ '@types/fs-extra':
+ specifier: ^11.0.4
+ version: 11.0.4
'@types/node':
specifier: ^20
version: 20.19.39
@@ -36,9 +45,15 @@ importers:
eslint-config-next:
specifier: 16.2.3
version: 16.2.3(@typescript-eslint/parser@8.58.2(eslint@9.39.4(jiti@2.6.1))(typescript@5.9.3))(eslint@9.39.4(jiti@2.6.1))(typescript@5.9.3)
+ fs-extra:
+ specifier: ^11.3.4
+ version: 11.3.4
tailwindcss:
specifier: ^4
version: 4.2.2
+ tsx:
+ specifier: ^4.19.2
+ version: 4.21.0
typescript:
specifier: ^5
version: 5.9.3
@@ -125,6 +140,162 @@ packages:
'@emnapi/wasi-threads@1.2.1':
resolution: {integrity: sha512-uTII7OYF+/Mes/MrcIOYp5yOtSMLBWSIoLPpcgwipoiKbli6k322tcoFsxoIIxPDqW01SQGAgko4EzZi2BNv2w==}
+ '@esbuild/aix-ppc64@0.27.7':
+ resolution: {integrity: sha512-EKX3Qwmhz1eMdEJokhALr0YiD0lhQNwDqkPYyPhiSwKrh7/4KRjQc04sZ8db+5DVVnZ1LmbNDI1uAMPEUBnQPg==}
+ engines: {node: '>=18'}
+ cpu: [ppc64]
+ os: [aix]
+
+ '@esbuild/android-arm64@0.27.7':
+ resolution: {integrity: sha512-62dPZHpIXzvChfvfLJow3q5dDtiNMkwiRzPylSCfriLvZeq0a1bWChrGx/BbUbPwOrsWKMn8idSllklzBy+dgQ==}
+ engines: {node: '>=18'}
+ cpu: [arm64]
+ os: [android]
+
+ '@esbuild/android-arm@0.27.7':
+ resolution: {integrity: sha512-jbPXvB4Yj2yBV7HUfE2KHe4GJX51QplCN1pGbYjvsyCZbQmies29EoJbkEc+vYuU5o45AfQn37vZlyXy4YJ8RQ==}
+ engines: {node: '>=18'}
+ cpu: [arm]
+ os: [android]
+
+ '@esbuild/android-x64@0.27.7':
+ resolution: {integrity: sha512-x5VpMODneVDb70PYV2VQOmIUUiBtY3D3mPBG8NxVk5CogneYhkR7MmM3yR/uMdITLrC1ml/NV1rj4bMJuy9MCg==}
+ engines: {node: '>=18'}
+ cpu: [x64]
+ os: [android]
+
+ '@esbuild/darwin-arm64@0.27.7':
+ resolution: {integrity: sha512-5lckdqeuBPlKUwvoCXIgI2D9/ABmPq3Rdp7IfL70393YgaASt7tbju3Ac+ePVi3KDH6N2RqePfHnXkaDtY9fkw==}
+ engines: {node: '>=18'}
+ cpu: [arm64]
+ os: [darwin]
+
+ '@esbuild/darwin-x64@0.27.7':
+ resolution: {integrity: sha512-rYnXrKcXuT7Z+WL5K980jVFdvVKhCHhUwid+dDYQpH+qu+TefcomiMAJpIiC2EM3Rjtq0sO3StMV/+3w3MyyqQ==}
+ engines: {node: '>=18'}
+ cpu: [x64]
+ os: [darwin]
+
+ '@esbuild/freebsd-arm64@0.27.7':
+ resolution: {integrity: sha512-B48PqeCsEgOtzME2GbNM2roU29AMTuOIN91dsMO30t+Ydis3z/3Ngoj5hhnsOSSwNzS+6JppqWsuhTp6E82l2w==}
+ engines: {node: '>=18'}
+ cpu: [arm64]
+ os: [freebsd]
+
+ '@esbuild/freebsd-x64@0.27.7':
+ resolution: {integrity: sha512-jOBDK5XEjA4m5IJK3bpAQF9/Lelu/Z9ZcdhTRLf4cajlB+8VEhFFRjWgfy3M1O4rO2GQ/b2dLwCUGpiF/eATNQ==}
+ engines: {node: '>=18'}
+ cpu: [x64]
+ os: [freebsd]
+
+ '@esbuild/linux-arm64@0.27.7':
+ resolution: {integrity: sha512-RZPHBoxXuNnPQO9rvjh5jdkRmVizktkT7TCDkDmQ0W2SwHInKCAV95GRuvdSvA7w4VMwfCjUiPwDi0ZO6Nfe9A==}
+ engines: {node: '>=18'}
+ cpu: [arm64]
+ os: [linux]
+
+ '@esbuild/linux-arm@0.27.7':
+ resolution: {integrity: sha512-RkT/YXYBTSULo3+af8Ib0ykH8u2MBh57o7q/DAs3lTJlyVQkgQvlrPTnjIzzRPQyavxtPtfg0EopvDyIt0j1rA==}
+ engines: {node: '>=18'}
+ cpu: [arm]
+ os: [linux]
+
+ '@esbuild/linux-ia32@0.27.7':
+ resolution: {integrity: sha512-GA48aKNkyQDbd3KtkplYWT102C5sn/EZTY4XROkxONgruHPU72l+gW+FfF8tf2cFjeHaRbWpOYa/uRBz/Xq1Pg==}
+ engines: {node: '>=18'}
+ cpu: [ia32]
+ os: [linux]
+
+ '@esbuild/linux-loong64@0.27.7':
+ resolution: {integrity: sha512-a4POruNM2oWsD4WKvBSEKGIiWQF8fZOAsycHOt6JBpZ+JN2n2JH9WAv56SOyu9X5IqAjqSIPTaJkqN8F7XOQ5Q==}
+ engines: {node: '>=18'}
+ cpu: [loong64]
+ os: [linux]
+
+ '@esbuild/linux-mips64el@0.27.7':
+ resolution: {integrity: sha512-KabT5I6StirGfIz0FMgl1I+R1H73Gp0ofL9A3nG3i/cYFJzKHhouBV5VWK1CSgKvVaG4q1RNpCTR2LuTVB3fIw==}
+ engines: {node: '>=18'}
+ cpu: [mips64el]
+ os: [linux]
+
+ '@esbuild/linux-ppc64@0.27.7':
+ resolution: {integrity: sha512-gRsL4x6wsGHGRqhtI+ifpN/vpOFTQtnbsupUF5R5YTAg+y/lKelYR1hXbnBdzDjGbMYjVJLJTd2OFmMewAgwlQ==}
+ engines: {node: '>=18'}
+ cpu: [ppc64]
+ os: [linux]
+
+ '@esbuild/linux-riscv64@0.27.7':
+ resolution: {integrity: sha512-hL25LbxO1QOngGzu2U5xeXtxXcW+/GvMN3ejANqXkxZ/opySAZMrc+9LY/WyjAan41unrR3YrmtTsUpwT66InQ==}
+ engines: {node: '>=18'}
+ cpu: [riscv64]
+ os: [linux]
+
+ '@esbuild/linux-s390x@0.27.7':
+ resolution: {integrity: sha512-2k8go8Ycu1Kb46vEelhu1vqEP+UeRVj2zY1pSuPdgvbd5ykAw82Lrro28vXUrRmzEsUV0NzCf54yARIK8r0fdw==}
+ engines: {node: '>=18'}
+ cpu: [s390x]
+ os: [linux]
+
+ '@esbuild/linux-x64@0.27.7':
+ resolution: {integrity: sha512-hzznmADPt+OmsYzw1EE33ccA+HPdIqiCRq7cQeL1Jlq2gb1+OyWBkMCrYGBJ+sxVzve2ZJEVeePbLM2iEIZSxA==}
+ engines: {node: '>=18'}
+ cpu: [x64]
+ os: [linux]
+
+ '@esbuild/netbsd-arm64@0.27.7':
+ resolution: {integrity: sha512-b6pqtrQdigZBwZxAn1UpazEisvwaIDvdbMbmrly7cDTMFnw/+3lVxxCTGOrkPVnsYIosJJXAsILG9XcQS+Yu6w==}
+ engines: {node: '>=18'}
+ cpu: [arm64]
+ os: [netbsd]
+
+ '@esbuild/netbsd-x64@0.27.7':
+ resolution: {integrity: sha512-OfatkLojr6U+WN5EDYuoQhtM+1xco+/6FSzJJnuWiUw5eVcicbyK3dq5EeV/QHT1uy6GoDhGbFpprUiHUYggrw==}
+ engines: {node: '>=18'}
+ cpu: [x64]
+ os: [netbsd]
+
+ '@esbuild/openbsd-arm64@0.27.7':
+ resolution: {integrity: sha512-AFuojMQTxAz75Fo8idVcqoQWEHIXFRbOc1TrVcFSgCZtQfSdc1RXgB3tjOn/krRHENUB4j00bfGjyl2mJrU37A==}
+ engines: {node: '>=18'}
+ cpu: [arm64]
+ os: [openbsd]
+
+ '@esbuild/openbsd-x64@0.27.7':
+ resolution: {integrity: sha512-+A1NJmfM8WNDv5CLVQYJ5PshuRm/4cI6WMZRg1by1GwPIQPCTs1GLEUHwiiQGT5zDdyLiRM/l1G0Pv54gvtKIg==}
+ engines: {node: '>=18'}
+ cpu: [x64]
+ os: [openbsd]
+
+ '@esbuild/openharmony-arm64@0.27.7':
+ resolution: {integrity: sha512-+KrvYb/C8zA9CU/g0sR6w2RBw7IGc5J2BPnc3dYc5VJxHCSF1yNMxTV5LQ7GuKteQXZtspjFbiuW5/dOj7H4Yw==}
+ engines: {node: '>=18'}
+ cpu: [arm64]
+ os: [openharmony]
+
+ '@esbuild/sunos-x64@0.27.7':
+ resolution: {integrity: sha512-ikktIhFBzQNt/QDyOL580ti9+5mL/YZeUPKU2ivGtGjdTYoqz6jObj6nOMfhASpS4GU4Q/Clh1QtxWAvcYKamA==}
+ engines: {node: '>=18'}
+ cpu: [x64]
+ os: [sunos]
+
+ '@esbuild/win32-arm64@0.27.7':
+ resolution: {integrity: sha512-7yRhbHvPqSpRUV7Q20VuDwbjW5kIMwTHpptuUzV+AA46kiPze5Z7qgt6CLCK3pWFrHeNfDd1VKgyP4O+ng17CA==}
+ engines: {node: '>=18'}
+ cpu: [arm64]
+ os: [win32]
+
+ '@esbuild/win32-ia32@0.27.7':
+ resolution: {integrity: sha512-SmwKXe6VHIyZYbBLJrhOoCJRB/Z1tckzmgTLfFYOfpMAx63BJEaL9ExI8x7v0oAO3Zh6D/Oi1gVxEYr5oUCFhw==}
+ engines: {node: '>=18'}
+ cpu: [ia32]
+ os: [win32]
+
+ '@esbuild/win32-x64@0.27.7':
+ resolution: {integrity: sha512-56hiAJPhwQ1R4i+21FVF7V8kSD5zZTdHcVuRFMW0hn753vVfQN8xlx4uOPT4xoGH0Z/oVATuR82AiqSTDIpaHg==}
+ engines: {node: '>=18'}
+ cpu: [x64]
+ os: [win32]
+
'@eslint-community/eslint-utils@4.9.1':
resolution: {integrity: sha512-phrYmNiYppR7znFEdqgfWHXR6NCkZEK7hwWDHZUjit/2/U0r6XvkDl0SYnoM51Hq7FhCGdLDT6zxCCOY1hexsQ==}
engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
@@ -529,12 +700,18 @@ packages:
'@types/estree@1.0.8':
resolution: {integrity: sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==}
+ '@types/fs-extra@11.0.4':
+ resolution: {integrity: sha512-yTbItCNreRooED33qjunPthRcSjERP1r4MqCZc7wv0u2sUkzTFp45tgUfS5+r7FrZPdmCCNflLhVSP/o+SemsQ==}
+
'@types/json-schema@7.0.15':
resolution: {integrity: sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==}
'@types/json5@0.0.29':
resolution: {integrity: sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==}
+ '@types/jsonfile@6.1.4':
+ resolution: {integrity: sha512-D5qGUYwjvnNNextdU59/+fI+spnwtTFmyQP0h+PfIOSkNfpU6AOICUOkm4i0OnSk+NyjdPJrxCDro0sJsWlRpQ==}
+
'@types/node@20.19.39':
resolution: {integrity: sha512-orrrD74MBUyK8jOAD/r0+lfa1I2MO6I+vAkmAWzMYbCcgrN4lCrmK52gRFQq/JRxfYPfonkr4b0jcY7Olqdqbw==}
@@ -708,6 +885,9 @@ packages:
cpu: [x64]
os: [win32]
+ '@zeit/schemas@2.36.0':
+ resolution: {integrity: sha512-7kjMwcChYEzMKjeex9ZFXkt1AyNov9R5HZtjBKVsmVpw7pa7ZtlCGvCBC2vnnXctaYN+aRI61HjIqeetZW5ROg==}
+
acorn-jsx@5.3.2:
resolution: {integrity: sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==}
peerDependencies:
@@ -721,10 +901,34 @@ packages:
ajv@6.14.0:
resolution: {integrity: sha512-IWrosm/yrn43eiKqkfkHis7QioDleaXQHdDVPKg0FSwwd/DuvyX79TZnFOnYpB7dcsFAMmtFztZuXPDvSePkFw==}
+ ajv@8.18.0:
+ resolution: {integrity: sha512-PlXPeEWMXMZ7sPYOHqmDyCJzcfNrUr3fGNKtezX14ykXOEIvyK81d+qydx89KY5O71FKMPaQ2vBfBFI5NHR63A==}
+
+ ansi-align@3.0.1:
+ resolution: {integrity: sha512-IOfwwBF5iczOjp/WeY4YxyjqAFMQoZufdQWDd19SEExbVLNXqvpzSJ/M7Za4/sCPmQ0+GRquoA7bGcINcxew6w==}
+
+ ansi-regex@5.0.1:
+ resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==}
+ engines: {node: '>=8'}
+
+ ansi-regex@6.2.2:
+ resolution: {integrity: sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg==}
+ engines: {node: '>=12'}
+
ansi-styles@4.3.0:
resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==}
engines: {node: '>=8'}
+ ansi-styles@6.2.3:
+ resolution: {integrity: sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==}
+ engines: {node: '>=12'}
+
+ arch@2.2.0:
+ resolution: {integrity: sha512-Of/R0wqp83cgHozfIYLbBMnej79U/SVGOOyuB3VVFv1NRM/PSFMK12x9KVtiYzJqmnU5WR2qp0Z5rHb7sWGnFQ==}
+
+ arg@5.0.2:
+ resolution: {integrity: sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg==}
+
argparse@2.0.1:
resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==}
@@ -795,6 +999,10 @@ packages:
engines: {node: '>=6.0.0'}
hasBin: true
+ boxen@7.0.0:
+ resolution: {integrity: sha512-j//dBVuyacJbvW+tvZ9HuH03fZ46QcaKvvhZickZqtB271DxJ7SNRSNxrV/dZX0085m7hISRZWbzWlJvx/rHSg==}
+ engines: {node: '>=14.16'}
+
brace-expansion@1.1.14:
resolution: {integrity: sha512-MWPGfDxnyzKU7rNOW9SP/c50vi3xrmrua/+6hfPbCS2ABNWfx24vPidzvC7krjU/RTo235sV776ymlsMtGKj8g==}
@@ -811,6 +1019,14 @@ packages:
engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7}
hasBin: true
+ bytes@3.0.0:
+ resolution: {integrity: sha512-pMhOfFDPiv9t5jjIXkHosWmkSyQbvsgEVNkz0ERHbuLh2T/7j4Mqqpz523Fe8MVY89KC6Sh/QfS2sM+SjgFDcw==}
+ engines: {node: '>= 0.8'}
+
+ bytes@3.1.2:
+ resolution: {integrity: sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==}
+ engines: {node: '>= 0.8'}
+
call-bind-apply-helpers@1.0.2:
resolution: {integrity: sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==}
engines: {node: '>= 0.4'}
@@ -827,16 +1043,36 @@ packages:
resolution: {integrity: sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==}
engines: {node: '>=6'}
+ camelcase@7.0.1:
+ resolution: {integrity: sha512-xlx1yCK2Oc1APsPXDL2LdlNP6+uu8OCDdhOBSVT279M/S+y75O30C2VuD8T2ogdePBBl7PfPF4504tnLgX3zfw==}
+ engines: {node: '>=14.16'}
+
caniuse-lite@1.0.30001788:
resolution: {integrity: sha512-6q8HFp+lOQtcf7wBK+uEenxymVWkGKkjFpCvw5W25cmMwEDU45p1xQFBQv8JDlMMry7eNxyBaR+qxgmTUZkIRQ==}
+ chalk-template@0.4.0:
+ resolution: {integrity: sha512-/ghrgmhfY8RaSdeo43hNXxpoHAtxdbskUHjPpfqUWGttFgycUhYPGx3YZBCnUCvOa7Doivn1IZec3DEGFoMgLg==}
+ engines: {node: '>=12'}
+
chalk@4.1.2:
resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==}
engines: {node: '>=10'}
+ chalk@5.0.1:
+ resolution: {integrity: sha512-Fo07WOYGqMfCWHOzSXOt2CxDbC6skS/jO9ynEcmpANMoPrD+W1r1K6Vx7iNm+AQmETU1Xr2t+n8nzkV9t6xh3w==}
+ engines: {node: ^12.17.0 || ^14.13 || >=16.0.0}
+
+ cli-boxes@3.0.0:
+ resolution: {integrity: sha512-/lzGpEWL/8PfI0BmBOPRwp0c/wFNX1RdUML3jK/RcSBA9T8mZDdQpqYBKtCFTOfQbwPqWEOpjqW+Fnayc0969g==}
+ engines: {node: '>=10'}
+
client-only@0.0.1:
resolution: {integrity: sha512-IV3Ou0jSMzZrd3pZ48nLkT9DA7Ag1pnPzaiQhpW7c3RbcqqzvzzVu+L8gfqMp/8IM2MQtSiqaCxrrcfu8I8rMA==}
+ clipboardy@3.0.0:
+ resolution: {integrity: sha512-Su+uU5sr1jkUy1sGRpLKjKrvEOVXgSgiSInwa/qeID6aJ07yh+5NWc3h2QfjHjBnfX4LhtFcuAWKUsJ3r+fjbg==}
+ engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0}
+
color-convert@2.0.1:
resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==}
engines: {node: '>=7.0.0'}
@@ -844,9 +1080,21 @@ packages:
color-name@1.1.4:
resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==}
+ compressible@2.0.18:
+ resolution: {integrity: sha512-AF3r7P5dWxL8MxyITRMlORQNaOA2IkAFaTr4k7BUumjPtRpGDTZpl0Pb1XCO6JeDCBdp126Cgs9sMxqSjgYyRg==}
+ engines: {node: '>= 0.6'}
+
+ compression@1.8.1:
+ resolution: {integrity: sha512-9mAqGPHLakhCLeNyxPkK4xVo746zQ/czLH1Ky+vkitMnWfWZps8r0qXuwhwizagCRttsL4lfG4pIOvaWLpAP0w==}
+ engines: {node: '>= 0.8.0'}
+
concat-map@0.0.1:
resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==}
+ content-disposition@0.5.2:
+ resolution: {integrity: sha512-kRGRZw3bLlFISDBgwTSA1TMBFN6J6GWDeubmDE3AF+3+yXL8hTWv8r5rkLbqYXY4RjPk/EzHnClI3zQf1cFmHA==}
+ engines: {node: '>= 0.6'}
+
convert-source-map@2.0.0:
resolution: {integrity: sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==}
@@ -872,6 +1120,14 @@ packages:
resolution: {integrity: sha512-BS8PfmtDGnrgYdOonGZQdLZslWIeCGFP9tpan0hi1Co2Zr2NKADsvGYA8XxuG/4UWgJ6Cjtv+YJnB6MM69QGlQ==}
engines: {node: '>= 0.4'}
+ debug@2.6.9:
+ resolution: {integrity: sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==}
+ peerDependencies:
+ supports-color: '*'
+ peerDependenciesMeta:
+ supports-color:
+ optional: true
+
debug@3.2.7:
resolution: {integrity: sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==}
peerDependencies:
@@ -889,9 +1145,17 @@ packages:
supports-color:
optional: true
+ deep-extend@0.6.0:
+ resolution: {integrity: sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==}
+ engines: {node: '>=4.0.0'}
+
deep-is@0.1.4:
resolution: {integrity: sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==}
+ deepmerge@4.3.1:
+ resolution: {integrity: sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==}
+ engines: {node: '>=0.10.0'}
+
define-data-property@1.1.4:
resolution: {integrity: sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==}
engines: {node: '>= 0.4'}
@@ -912,9 +1176,15 @@ packages:
resolution: {integrity: sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==}
engines: {node: '>= 0.4'}
+ eastasianwidth@0.2.0:
+ resolution: {integrity: sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==}
+
electron-to-chromium@1.5.336:
resolution: {integrity: sha512-AbH9q9J455r/nLmdNZes0G0ZKcRX73FicwowalLs6ijwOmCJSRRrLX63lcAlzy9ux3dWK1w1+1nsBJEWN11hcQ==}
+ emoji-regex@8.0.0:
+ resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==}
+
emoji-regex@9.2.2:
resolution: {integrity: sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==}
@@ -954,6 +1224,11 @@ packages:
resolution: {integrity: sha512-w+5mJ3GuFL+NjVtJlvydShqE1eN3h3PbI7/5LAsYJP/2qtuMXjfL2LpHSRqo4b4eSF5K/DH1JXKUAHSB2UW50g==}
engines: {node: '>= 0.4'}
+ esbuild@0.27.7:
+ resolution: {integrity: sha512-IxpibTjyVnmrIQo5aqNpCgoACA/dTKLTlhMHihVHhdkxKyPO1uBBthumT0rdHmcsk9uMonIWS0m4FljWzILh3w==}
+ engines: {node: '>=18'}
+ hasBin: true
+
escalade@3.2.0:
resolution: {integrity: sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==}
engines: {node: '>=6'}
@@ -1082,6 +1357,10 @@ packages:
resolution: {integrity: sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==}
engines: {node: '>=0.10.0'}
+ execa@5.1.1:
+ resolution: {integrity: sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==}
+ engines: {node: '>=10'}
+
fast-deep-equal@3.1.3:
resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==}
@@ -1095,6 +1374,9 @@ packages:
fast-levenshtein@2.0.6:
resolution: {integrity: sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==}
+ fast-uri@3.1.0:
+ resolution: {integrity: sha512-iPeeDKJSWf4IEOasVVrknXpaBV0IApz/gp7S2bb7Z4Lljbl2MGJRqInZiUrQwV16cpzw/D3S5j5Julj/gT52AA==}
+
fastq@1.20.1:
resolution: {integrity: sha512-GGToxJ/w1x32s/D2EKND7kTil4n8OVk/9mycTc4VDza13lOvpUZTGX3mFSCtV9ksdGBVzvsyAVLM6mHFThxXxw==}
@@ -1130,6 +1412,15 @@ packages:
resolution: {integrity: sha512-dKx12eRCVIzqCxFGplyFKJMPvLEWgmNtUrpTiJIR5u97zEhRG8ySrtboPHZXx7daLxQVrl643cTzbab2tkQjxg==}
engines: {node: '>= 0.4'}
+ fs-extra@11.3.4:
+ resolution: {integrity: sha512-CTXd6rk/M3/ULNQj8FBqBWHYBVYybQ3VPBw0xGKFe3tuH7ytT6ACnvzpIQ3UZtB8yvUKC2cXn1a+x+5EVQLovA==}
+ engines: {node: '>=14.14'}
+
+ fsevents@2.3.3:
+ resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==}
+ engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0}
+ os: [darwin]
+
function-bind@1.1.2:
resolution: {integrity: sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==}
@@ -1156,6 +1447,10 @@ packages:
resolution: {integrity: sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==}
engines: {node: '>= 0.4'}
+ get-stream@6.0.1:
+ resolution: {integrity: sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==}
+ engines: {node: '>=10'}
+
get-symbol-description@1.1.0:
resolution: {integrity: sha512-w9UMqWwJxHNOvoNzSJ2oPF5wvYcvP7jUvYzhp67yEhTi17ZDBBC1z9pTdGuzjD+EFIqLSYRweZjqfiPzQ06Ebg==}
engines: {node: '>= 0.4'}
@@ -1223,6 +1518,10 @@ packages:
hermes-parser@0.25.1:
resolution: {integrity: sha512-6pEjquH3rqaI6cYAXYPcz9MS4rY6R4ngRgrgfDshRptUZIc3lw0MCIJIGDj9++mfySOuPTHB4nrSW99BCvOPIA==}
+ human-signals@2.1.0:
+ resolution: {integrity: sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==}
+ engines: {node: '>=10.17.0'}
+
ignore@5.3.2:
resolution: {integrity: sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==}
engines: {node: '>= 4'}
@@ -1239,6 +1538,9 @@ packages:
resolution: {integrity: sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==}
engines: {node: '>=0.8.19'}
+ ini@1.3.8:
+ resolution: {integrity: sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==}
+
internal-slot@1.1.0:
resolution: {integrity: sha512-4gd7VpWNQNB4UKKCFFVcp1AVv+FMOgs9NKzjHKusc8jTMhd5eL1NqQqOpE0KzMds804/yHlglp3uxgluOqAPLw==}
engines: {node: '>= 0.4'}
@@ -1278,6 +1580,11 @@ packages:
resolution: {integrity: sha512-PwwhEakHVKTdRNVOw+/Gyh0+MzlCl4R6qKvkhuvLtPMggI1WAHt9sOwZxQLSGpUaDnrdyDsomoRgNnCfKNSXXg==}
engines: {node: '>= 0.4'}
+ is-docker@2.2.1:
+ resolution: {integrity: sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ==}
+ engines: {node: '>=8'}
+ hasBin: true
+
is-extglob@2.1.1:
resolution: {integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==}
engines: {node: '>=0.10.0'}
@@ -1286,6 +1593,10 @@ packages:
resolution: {integrity: sha512-1pC6N8qWJbWoPtEjgcL2xyhQOP491EQjeUo3qTKcmV8YSDDJrOepfG8pcC7h/QgnQHYSv0mJ3Z/ZWxmatVrysg==}
engines: {node: '>= 0.4'}
+ is-fullwidth-code-point@3.0.0:
+ resolution: {integrity: sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==}
+ engines: {node: '>=8'}
+
is-generator-function@1.1.2:
resolution: {integrity: sha512-upqt1SkGkODW9tsGNG5mtXTXtECizwtS2kA161M+gJPc1xdb/Ax629af6YrTwcOeQHbewrPNlE5Dx7kzvXTizA==}
engines: {node: '>= 0.4'}
@@ -1310,6 +1621,10 @@ packages:
resolution: {integrity: sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==}
engines: {node: '>=0.12.0'}
+ is-port-reachable@4.0.0:
+ resolution: {integrity: sha512-9UoipoxYmSk6Xy7QFgRv2HDyaysmgSG75TFQs6S+3pDM7ZhKTF/bskZV+0UlABHzKjNVhPjYCLfeZUEg1wXxig==}
+ engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0}
+
is-regex@1.2.1:
resolution: {integrity: sha512-MjYsKHO5O7mCsmRGxWcLWheFqN9DJ/2TmngvjKXihe6efViPqc274+Fx/4fYj/r03+ESvBdTXK0V6tA3rgez1g==}
engines: {node: '>= 0.4'}
@@ -1322,6 +1637,10 @@ packages:
resolution: {integrity: sha512-ISWac8drv4ZGfwKl5slpHG9OwPNty4jOWPRIhBpxOoD+hqITiwuipOQ2bNthAzwA3B4fIjO4Nln74N0S9byq8A==}
engines: {node: '>= 0.4'}
+ is-stream@2.0.1:
+ resolution: {integrity: sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==}
+ engines: {node: '>=8'}
+
is-string@1.1.1:
resolution: {integrity: sha512-BtEeSsoaQjlSPBemMQIrY1MY0uM6vnS1g5fmufYOtnxLGUZM2178PKbhsk7Ffv58IX+ZtcvoGwccYsh0PglkAA==}
engines: {node: '>= 0.4'}
@@ -1346,6 +1665,10 @@ packages:
resolution: {integrity: sha512-mfcwb6IzQyOKTs84CQMrOwW4gQcaTOAWJ0zzJCl2WSPDrWk/OzDaImWFH3djXhb24g4eudZfLRozAvPGw4d9hQ==}
engines: {node: '>= 0.4'}
+ is-wsl@2.2.0:
+ resolution: {integrity: sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==}
+ engines: {node: '>=8'}
+
isarray@2.0.5:
resolution: {integrity: sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==}
@@ -1378,6 +1701,9 @@ packages:
json-schema-traverse@0.4.1:
resolution: {integrity: sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==}
+ json-schema-traverse@1.0.0:
+ resolution: {integrity: sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==}
+
json-stable-stringify-without-jsonify@1.0.1:
resolution: {integrity: sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==}
@@ -1390,6 +1716,9 @@ packages:
engines: {node: '>=6'}
hasBin: true
+ jsonfile@6.2.0:
+ resolution: {integrity: sha512-FGuPw30AdOIUTRMC2OMRtQV+jkVj2cfPqSeWXv1NEAJ1qZ5zb1X6z1mFhbfOB/iy3ssJCD+3KuZ8r8C3uVFlAg==}
+
jsx-ast-utils@3.3.5:
resolution: {integrity: sha512-ZZow9HBI5O6EPgSJLUb8n2NKgmVWTwCvHGwFuJlMjvLFqlGG6pjirPhtdsseaLZjSibD8eegzmYpUZwoIlj2cQ==}
engines: {node: '>=4.0'}
@@ -1482,6 +1811,9 @@ packages:
resolution: {integrity: sha512-NXYBzinNrblfraPGyrbPoD19C1h9lfI/1mzgWYvXUTe414Gz/X1FD2XBZSZM7rRTrMA8JL3OtAaGifrIKhQ5yQ==}
engines: {node: '>= 12.0.0'}
+ load-script@1.0.0:
+ resolution: {integrity: sha512-kPEjMFtZvwL9TaZo0uZ2ml+Ye9HUMmPwbYRJ324qF9tqMejwykJ5ggTyvzmrbBeapCAbk98BSbTeovHEEP1uCA==}
+
locate-path@6.0.0:
resolution: {integrity: sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==}
engines: {node: '>=10'}
@@ -1503,6 +1835,12 @@ packages:
resolution: {integrity: sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==}
engines: {node: '>= 0.4'}
+ memoize-one@5.2.1:
+ resolution: {integrity: sha512-zYiwtZUcYyXKo/np96AGZAckk+FWWsUdJ3cHGGmld7+AhvcWmQyGCYUh1hc4Q/pkOhb65dQR/pqCyK0cOaHz4Q==}
+
+ merge-stream@2.0.0:
+ resolution: {integrity: sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==}
+
merge2@1.4.1:
resolution: {integrity: sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==}
engines: {node: '>= 8'}
@@ -1511,6 +1849,22 @@ packages:
resolution: {integrity: sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==}
engines: {node: '>=8.6'}
+ mime-db@1.33.0:
+ resolution: {integrity: sha512-BHJ/EKruNIqJf/QahvxwQZXKygOQ256myeN/Ew+THcAa5q+PjyTTMMeNQC4DZw5AwfvelsUrA6B67NKMqXDbzQ==}
+ engines: {node: '>= 0.6'}
+
+ mime-db@1.54.0:
+ resolution: {integrity: sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ==}
+ engines: {node: '>= 0.6'}
+
+ mime-types@2.1.18:
+ resolution: {integrity: sha512-lc/aahn+t4/SWV/qcmumYjymLsWfN3ELhpmVuUFjgsORruuZPVSwAQryq+HHGvO/SI2KVX26bx+En+zhM8g8hQ==}
+ engines: {node: '>= 0.6'}
+
+ mimic-fn@2.1.0:
+ resolution: {integrity: sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==}
+ engines: {node: '>=6'}
+
minimatch@10.2.5:
resolution: {integrity: sha512-MULkVLfKGYDFYejP07QOurDLLQpcjk7Fw+7jXS2R2czRQzR56yHRveU5NDJEOviH+hETZKSkIk5c+T23GjFUMg==}
engines: {node: 18 || 20 || >=22}
@@ -1521,6 +1875,9 @@ packages:
minimist@1.2.8:
resolution: {integrity: sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==}
+ ms@2.0.0:
+ resolution: {integrity: sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==}
+
ms@2.1.3:
resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==}
@@ -1537,6 +1894,10 @@ packages:
natural-compare@1.4.0:
resolution: {integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==}
+ negotiator@0.6.4:
+ resolution: {integrity: sha512-myRT3DiWPHqho5PrJaIRyaMv2kgYf0mUVgBNOYMuCH5Ki1yEiQaf/ZJuQ62nvpc44wL5WDbTX7yGJi1Neevw8w==}
+ engines: {node: '>= 0.6'}
+
next@16.2.3:
resolution: {integrity: sha512-9V3zV4oZFza3PVev5/poB9g0dEafVcgNyQ8eTRop8GvxZjV2G15FC5ARuG1eFD42QgeYkzJBJzHghNP8Ad9xtA==}
engines: {node: '>=20.9.0'}
@@ -1565,6 +1926,10 @@ packages:
node-releases@2.0.37:
resolution: {integrity: sha512-1h5gKZCF+pO/o3Iqt5Jp7wc9rH3eJJ0+nh/CIoiRwjRxde/hAHyLPXYN4V3CqKAbiZPSeJFSWHmJsbkicta0Eg==}
+ npm-run-path@4.0.1:
+ resolution: {integrity: sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==}
+ engines: {node: '>=8'}
+
object-assign@4.1.1:
resolution: {integrity: sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==}
engines: {node: '>=0.10.0'}
@@ -1597,6 +1962,14 @@ packages:
resolution: {integrity: sha512-gXah6aZrcUxjWg2zR2MwouP2eHlCBzdV4pygudehaKXSGW4v2AsRQUK+lwwXhii6KFZcunEnmSUoYp5CXibxtA==}
engines: {node: '>= 0.4'}
+ on-headers@1.1.0:
+ resolution: {integrity: sha512-737ZY3yNnXy37FHkQxPzt4UZ2UWPWiCZWLvFZ4fu5cueciegX0zGPnrlY6bwRg4FdQOe9YU8MkmJwGhoMybl8A==}
+ engines: {node: '>= 0.8'}
+
+ onetime@5.1.2:
+ resolution: {integrity: sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==}
+ engines: {node: '>=6'}
+
optionator@0.9.4:
resolution: {integrity: sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==}
engines: {node: '>= 0.8.0'}
@@ -1621,6 +1994,9 @@ packages:
resolution: {integrity: sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==}
engines: {node: '>=8'}
+ path-is-inside@1.0.2:
+ resolution: {integrity: sha512-DUWJr3+ULp4zXmol/SZkFf3JGsS9/SIv+Y3Rt93/UjPpDpklB5f1er4O3POIbUuUJ3FXgqte2Q7SrU6zAqwk8w==}
+
path-key@3.1.1:
resolution: {integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==}
engines: {node: '>=8'}
@@ -1628,6 +2004,9 @@ packages:
path-parse@1.0.7:
resolution: {integrity: sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==}
+ path-to-regexp@3.3.0:
+ resolution: {integrity: sha512-qyCH421YQPS2WFDxDjftfc1ZR5WKQzVzqsp4n9M2kQhVOo/ByahFoUNJfl58kOcEGfQ//7weFTDhm+ss8Ecxgw==}
+
picocolors@1.1.1:
resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==}
@@ -1665,14 +2044,30 @@ packages:
queue-microtask@1.2.3:
resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==}
+ range-parser@1.2.0:
+ resolution: {integrity: sha512-kA5WQoNVo4t9lNx2kQNFCxKeBl5IbbSNBl1M/tLkw9WCn+hxNBAW5Qh8gdhs63CJnhjJ2zQWFoqPJP2sK1AV5A==}
+ engines: {node: '>= 0.6'}
+
+ rc@1.2.8:
+ resolution: {integrity: sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==}
+ hasBin: true
+
react-dom@19.2.4:
resolution: {integrity: sha512-AXJdLo8kgMbimY95O2aKQqsz2iWi9jMgKJhRBAxECE4IFxfcazB2LmzloIoibJI3C12IlY20+KFaLv+71bUJeQ==}
peerDependencies:
react: ^19.2.4
+ react-fast-compare@3.2.2:
+ resolution: {integrity: sha512-nsO+KSNgo1SbJqJEYRE9ERzo7YtYbou/OqjSQKxV7jcKox7+usiUVZOAC+XnDOABXggQTno0Y1CpVnuWEc1boQ==}
+
react-is@16.13.1:
resolution: {integrity: sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==}
+ react-player@2.16.1:
+ resolution: {integrity: sha512-mxP6CqjSWjidtyDoMOSHVPdhX0pY16aSvw5fVr44EMaT7X5Xz46uQ4b/YBm1v2x+3hHkB9PmjEEkmbHb9PXQ4w==}
+ peerDependencies:
+ react: '>=16.6.0'
+
react@19.2.4:
resolution: {integrity: sha512-9nfp2hYpCwOjAN+8TZFGhtWEwgvWHXqESH8qT89AT/lWklpLON22Lc8pEtnpsZz7VmawabSU0gCjnj8aC0euHQ==}
engines: {node: '>=0.10.0'}
@@ -1685,6 +2080,17 @@ packages:
resolution: {integrity: sha512-dYqgNSZbDwkaJ2ceRd9ojCGjBq+mOm9LmtXnAnEGyHhN/5R7iDW2TRw3h+o/jCFxus3P2LfWIIiwowAjANm7IA==}
engines: {node: '>= 0.4'}
+ registry-auth-token@3.3.2:
+ resolution: {integrity: sha512-JL39c60XlzCVgNrO+qq68FoNb56w/m7JYvGR2jT5iR1xBrUA3Mfx5Twk5rqTThPmQKMWydGmq8oFtDlxfrmxnQ==}
+
+ registry-url@3.1.0:
+ resolution: {integrity: sha512-ZbgR5aZEdf4UKZVBPYIgaglBmSF2Hi94s2PcIHhRGFjKYu+chjJdYfHn4rt3hB6eCKLJ8giVIIfgMa1ehDfZKA==}
+ engines: {node: '>=0.10.0'}
+
+ require-from-string@2.0.2:
+ resolution: {integrity: sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==}
+ engines: {node: '>=0.10.0'}
+
resolve-from@4.0.0:
resolution: {integrity: sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==}
engines: {node: '>=4'}
@@ -1708,6 +2114,9 @@ packages:
resolution: {integrity: sha512-AURm5f0jYEOydBj7VQlVvDrjeFgthDdEF5H1dP+6mNpoXOMo1quQqJ4wvJDyRZ9+pO3kGWoOdmV08cSv2aJV6Q==}
engines: {node: '>=0.4'}
+ safe-buffer@5.2.1:
+ resolution: {integrity: sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==}
+
safe-push-apply@1.0.0:
resolution: {integrity: sha512-iKE9w/Z7xCzUMIZqdBsp6pEQvwuEebH4vdpjcDWnyzaI6yl6O9FHvVpmGelvEHNsoY6wGblkxR6Zty/h00WiSA==}
engines: {node: '>= 0.4'}
@@ -1728,6 +2137,14 @@ packages:
engines: {node: '>=10'}
hasBin: true
+ serve-handler@6.1.7:
+ resolution: {integrity: sha512-CinAq1xWb0vR3twAv9evEU8cNWkXCb9kd5ePAHUKJBkOsUpR1wt/CvGdeca7vqumL1U5cSaeVQ6zZMxiJ3yWsg==}
+
+ serve@14.2.6:
+ resolution: {integrity: sha512-QEjUSA+sD4Rotm1znR8s50YqA3kYpRGPmtd5GlFxbaL9n/FdUNbqMhxClqdditSk0LlZyA/dhud6XNRTOC9x2Q==}
+ engines: {node: '>= 14'}
+ hasBin: true
+
set-function-length@1.2.2:
resolution: {integrity: sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==}
engines: {node: '>= 0.4'}
@@ -1768,6 +2185,9 @@ packages:
resolution: {integrity: sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==}
engines: {node: '>= 0.4'}
+ signal-exit@3.0.7:
+ resolution: {integrity: sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==}
+
source-map-js@1.2.1:
resolution: {integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==}
engines: {node: '>=0.10.0'}
@@ -1779,6 +2199,14 @@ packages:
resolution: {integrity: sha512-eLoXW/DHyl62zxY4SCaIgnRhuMr6ri4juEYARS8E6sCEqzKpOiE521Ucofdx+KnDZl5xmvGYaaKCk5FEOxJCoQ==}
engines: {node: '>= 0.4'}
+ string-width@4.2.3:
+ resolution: {integrity: sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==}
+ engines: {node: '>=8'}
+
+ string-width@5.1.2:
+ resolution: {integrity: sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==}
+ engines: {node: '>=12'}
+
string.prototype.includes@2.0.1:
resolution: {integrity: sha512-o7+c9bW6zpAdJHTtujeePODAhkuicdAryFsfVKwA+wGw89wJ4GTY484WTucM9hLtDEOpOvI+aHnzqnC5lHp4Rg==}
engines: {node: '>= 0.4'}
@@ -1802,10 +2230,26 @@ packages:
resolution: {integrity: sha512-UXSH262CSZY1tfu3G3Secr6uGLCFVPMhIqHjlgCUtCCcgihYc/xKs9djMTMUOb2j1mVSeU8EU6NWc/iQKU6Gfg==}
engines: {node: '>= 0.4'}
+ strip-ansi@6.0.1:
+ resolution: {integrity: sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==}
+ engines: {node: '>=8'}
+
+ strip-ansi@7.2.0:
+ resolution: {integrity: sha512-yDPMNjp4WyfYBkHnjIRLfca1i6KMyGCtsVgoKe/z1+6vukgaENdgGBZt+ZmKPc4gavvEZ5OgHfHdrazhgNyG7w==}
+ engines: {node: '>=12'}
+
strip-bom@3.0.0:
resolution: {integrity: sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==}
engines: {node: '>=4'}
+ strip-final-newline@2.0.0:
+ resolution: {integrity: sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==}
+ engines: {node: '>=6'}
+
+ strip-json-comments@2.0.1:
+ resolution: {integrity: sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ==}
+ engines: {node: '>=0.10.0'}
+
strip-json-comments@3.1.1:
resolution: {integrity: sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==}
engines: {node: '>=8'}
@@ -1858,10 +2302,19 @@ packages:
tslib@2.8.1:
resolution: {integrity: sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==}
+ tsx@4.21.0:
+ resolution: {integrity: sha512-5C1sg4USs1lfG0GFb2RLXsdpXqBSEhAaA/0kPL01wxzpMqLILNxIxIOKiILz+cdg/pLnOUxFYOR5yhHU666wbw==}
+ engines: {node: '>=18.0.0'}
+ hasBin: true
+
type-check@0.4.0:
resolution: {integrity: sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==}
engines: {node: '>= 0.8.0'}
+ type-fest@2.19.0:
+ resolution: {integrity: sha512-RAH822pAdBgcNMAfWnCBU3CFZcfZ/i1eZjwFU/dsLKumyuuP3niueg2UAukXYF0E2AAoc82ZSSf9J0WQBinzHA==}
+ engines: {node: '>=12.20'}
+
typed-array-buffer@1.0.3:
resolution: {integrity: sha512-nAYYwfY3qnzX30IkA6AQZjVbtK6duGontcQm1WSG1MD94YLqK0515GNApXkoxKOWMusVssAHWLh9SeaoefYFGw==}
engines: {node: '>= 0.4'}
@@ -1897,6 +2350,10 @@ packages:
undici-types@6.21.0:
resolution: {integrity: sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==}
+ universalify@2.0.1:
+ resolution: {integrity: sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==}
+ engines: {node: '>= 10.0.0'}
+
unrs-resolver@1.11.1:
resolution: {integrity: sha512-bSjt9pjaEBnNiGgc9rUiHGKv5l4/TGzDmYw3RhnkJGtLhbnnA/5qJj7x3dNDCRx/PJxu774LlH8lCOlB4hEfKg==}
@@ -1906,9 +2363,16 @@ packages:
peerDependencies:
browserslist: '>= 4.21.0'
+ update-check@1.5.4:
+ resolution: {integrity: sha512-5YHsflzHP4t1G+8WGPlvKbJEbAJGCgw+Em+dGR1KmBUbr1J36SJBqlHLjR7oob7sco5hWHGQVcr9B2poIVDDTQ==}
+
uri-js@4.4.1:
resolution: {integrity: sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==}
+ vary@1.1.2:
+ resolution: {integrity: sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==}
+ engines: {node: '>= 0.8'}
+
which-boxed-primitive@1.1.1:
resolution: {integrity: sha512-TbX3mj8n0odCBFVlY8AxkqcHASw3L60jIuF8jFP78az3C2YhmGvqbHBpAjTRH2/xqYunrJ9g1jSyjCjpoWzIAA==}
engines: {node: '>= 0.4'}
@@ -1930,10 +2394,18 @@ packages:
engines: {node: '>= 8'}
hasBin: true
+ widest-line@4.0.1:
+ resolution: {integrity: sha512-o0cyEG0e8GPzT4iGHphIOh0cJOV8fivsXxddQasHPHfoZf1ZexrfeA21w2NaEN1RHE+fXlfISmOE8R9N3u3Qig==}
+ engines: {node: '>=12'}
+
word-wrap@1.2.5:
resolution: {integrity: sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==}
engines: {node: '>=0.10.0'}
+ wrap-ansi@8.1.0:
+ resolution: {integrity: sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==}
+ engines: {node: '>=12'}
+
yallist@3.1.1:
resolution: {integrity: sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==}
@@ -2070,6 +2542,84 @@ snapshots:
tslib: 2.8.1
optional: true
+ '@esbuild/aix-ppc64@0.27.7':
+ optional: true
+
+ '@esbuild/android-arm64@0.27.7':
+ optional: true
+
+ '@esbuild/android-arm@0.27.7':
+ optional: true
+
+ '@esbuild/android-x64@0.27.7':
+ optional: true
+
+ '@esbuild/darwin-arm64@0.27.7':
+ optional: true
+
+ '@esbuild/darwin-x64@0.27.7':
+ optional: true
+
+ '@esbuild/freebsd-arm64@0.27.7':
+ optional: true
+
+ '@esbuild/freebsd-x64@0.27.7':
+ optional: true
+
+ '@esbuild/linux-arm64@0.27.7':
+ optional: true
+
+ '@esbuild/linux-arm@0.27.7':
+ optional: true
+
+ '@esbuild/linux-ia32@0.27.7':
+ optional: true
+
+ '@esbuild/linux-loong64@0.27.7':
+ optional: true
+
+ '@esbuild/linux-mips64el@0.27.7':
+ optional: true
+
+ '@esbuild/linux-ppc64@0.27.7':
+ optional: true
+
+ '@esbuild/linux-riscv64@0.27.7':
+ optional: true
+
+ '@esbuild/linux-s390x@0.27.7':
+ optional: true
+
+ '@esbuild/linux-x64@0.27.7':
+ optional: true
+
+ '@esbuild/netbsd-arm64@0.27.7':
+ optional: true
+
+ '@esbuild/netbsd-x64@0.27.7':
+ optional: true
+
+ '@esbuild/openbsd-arm64@0.27.7':
+ optional: true
+
+ '@esbuild/openbsd-x64@0.27.7':
+ optional: true
+
+ '@esbuild/openharmony-arm64@0.27.7':
+ optional: true
+
+ '@esbuild/sunos-x64@0.27.7':
+ optional: true
+
+ '@esbuild/win32-arm64@0.27.7':
+ optional: true
+
+ '@esbuild/win32-ia32@0.27.7':
+ optional: true
+
+ '@esbuild/win32-x64@0.27.7':
+ optional: true
+
'@eslint-community/eslint-utils@4.9.1(eslint@9.39.4(jiti@2.6.1))':
dependencies:
eslint: 9.39.4(jiti@2.6.1)
@@ -2376,10 +2926,19 @@ snapshots:
'@types/estree@1.0.8': {}
+ '@types/fs-extra@11.0.4':
+ dependencies:
+ '@types/jsonfile': 6.1.4
+ '@types/node': 20.19.39
+
'@types/json-schema@7.0.15': {}
'@types/json5@0.0.29': {}
+ '@types/jsonfile@6.1.4':
+ dependencies:
+ '@types/node': 20.19.39
+
'@types/node@20.19.39':
dependencies:
undici-types: 6.21.0
@@ -2542,6 +3101,8 @@ snapshots:
'@unrs/resolver-binding-win32-x64-msvc@1.11.1':
optional: true
+ '@zeit/schemas@2.36.0': {}
+
acorn-jsx@5.3.2(acorn@8.16.0):
dependencies:
acorn: 8.16.0
@@ -2555,10 +3116,31 @@ snapshots:
json-schema-traverse: 0.4.1
uri-js: 4.4.1
+ ajv@8.18.0:
+ dependencies:
+ fast-deep-equal: 3.1.3
+ fast-uri: 3.1.0
+ json-schema-traverse: 1.0.0
+ require-from-string: 2.0.2
+
+ ansi-align@3.0.1:
+ dependencies:
+ string-width: 4.2.3
+
+ ansi-regex@5.0.1: {}
+
+ ansi-regex@6.2.2: {}
+
ansi-styles@4.3.0:
dependencies:
color-convert: 2.0.1
+ ansi-styles@6.2.3: {}
+
+ arch@2.2.0: {}
+
+ arg@5.0.2: {}
+
argparse@2.0.1: {}
aria-query@5.3.2: {}
@@ -2648,6 +3230,17 @@ snapshots:
baseline-browser-mapping@2.10.19: {}
+ boxen@7.0.0:
+ dependencies:
+ ansi-align: 3.0.1
+ camelcase: 7.0.1
+ chalk: 5.0.1
+ cli-boxes: 3.0.0
+ string-width: 5.1.2
+ type-fest: 2.19.0
+ widest-line: 4.0.1
+ wrap-ansi: 8.1.0
+
brace-expansion@1.1.14:
dependencies:
balanced-match: 1.0.2
@@ -2669,6 +3262,10 @@ snapshots:
node-releases: 2.0.37
update-browserslist-db: 1.2.3(browserslist@4.28.2)
+ bytes@3.0.0: {}
+
+ bytes@3.1.2: {}
+
call-bind-apply-helpers@1.0.2:
dependencies:
es-errors: 1.3.0
@@ -2688,23 +3285,57 @@ snapshots:
callsites@3.1.0: {}
+ camelcase@7.0.1: {}
+
caniuse-lite@1.0.30001788: {}
+ chalk-template@0.4.0:
+ dependencies:
+ chalk: 4.1.2
+
chalk@4.1.2:
dependencies:
ansi-styles: 4.3.0
supports-color: 7.2.0
+ chalk@5.0.1: {}
+
+ cli-boxes@3.0.0: {}
+
client-only@0.0.1: {}
+ clipboardy@3.0.0:
+ dependencies:
+ arch: 2.2.0
+ execa: 5.1.1
+ is-wsl: 2.2.0
+
color-convert@2.0.1:
dependencies:
color-name: 1.1.4
color-name@1.1.4: {}
+ compressible@2.0.18:
+ dependencies:
+ mime-db: 1.54.0
+
+ compression@1.8.1:
+ dependencies:
+ bytes: 3.1.2
+ compressible: 2.0.18
+ debug: 2.6.9
+ negotiator: 0.6.4
+ on-headers: 1.1.0
+ safe-buffer: 5.2.1
+ vary: 1.1.2
+ transitivePeerDependencies:
+ - supports-color
+
concat-map@0.0.1: {}
+ content-disposition@0.5.2: {}
+
convert-source-map@2.0.0: {}
cross-spawn@7.0.6:
@@ -2735,6 +3366,10 @@ snapshots:
es-errors: 1.3.0
is-data-view: 1.0.2
+ debug@2.6.9:
+ dependencies:
+ ms: 2.0.0
+
debug@3.2.7:
dependencies:
ms: 2.1.3
@@ -2743,8 +3378,12 @@ snapshots:
dependencies:
ms: 2.1.3
+ deep-extend@0.6.0: {}
+
deep-is@0.1.4: {}
+ deepmerge@4.3.1: {}
+
define-data-property@1.1.4:
dependencies:
es-define-property: 1.0.1
@@ -2769,8 +3408,12 @@ snapshots:
es-errors: 1.3.0
gopd: 1.2.0
+ eastasianwidth@0.2.0: {}
+
electron-to-chromium@1.5.336: {}
+ emoji-regex@8.0.0: {}
+
emoji-regex@9.2.2: {}
enhanced-resolve@5.20.1:
@@ -2879,6 +3522,35 @@ snapshots:
is-date-object: 1.1.0
is-symbol: 1.1.1
+ esbuild@0.27.7:
+ optionalDependencies:
+ '@esbuild/aix-ppc64': 0.27.7
+ '@esbuild/android-arm': 0.27.7
+ '@esbuild/android-arm64': 0.27.7
+ '@esbuild/android-x64': 0.27.7
+ '@esbuild/darwin-arm64': 0.27.7
+ '@esbuild/darwin-x64': 0.27.7
+ '@esbuild/freebsd-arm64': 0.27.7
+ '@esbuild/freebsd-x64': 0.27.7
+ '@esbuild/linux-arm': 0.27.7
+ '@esbuild/linux-arm64': 0.27.7
+ '@esbuild/linux-ia32': 0.27.7
+ '@esbuild/linux-loong64': 0.27.7
+ '@esbuild/linux-mips64el': 0.27.7
+ '@esbuild/linux-ppc64': 0.27.7
+ '@esbuild/linux-riscv64': 0.27.7
+ '@esbuild/linux-s390x': 0.27.7
+ '@esbuild/linux-x64': 0.27.7
+ '@esbuild/netbsd-arm64': 0.27.7
+ '@esbuild/netbsd-x64': 0.27.7
+ '@esbuild/openbsd-arm64': 0.27.7
+ '@esbuild/openbsd-x64': 0.27.7
+ '@esbuild/openharmony-arm64': 0.27.7
+ '@esbuild/sunos-x64': 0.27.7
+ '@esbuild/win32-arm64': 0.27.7
+ '@esbuild/win32-ia32': 0.27.7
+ '@esbuild/win32-x64': 0.27.7
+
escalade@3.2.0: {}
escape-string-regexp@4.0.0: {}
@@ -3088,6 +3760,18 @@ snapshots:
esutils@2.0.3: {}
+ execa@5.1.1:
+ dependencies:
+ cross-spawn: 7.0.6
+ get-stream: 6.0.1
+ human-signals: 2.1.0
+ is-stream: 2.0.1
+ merge-stream: 2.0.0
+ npm-run-path: 4.0.1
+ onetime: 5.1.2
+ signal-exit: 3.0.7
+ strip-final-newline: 2.0.0
+
fast-deep-equal@3.1.3: {}
fast-glob@3.3.1:
@@ -3102,6 +3786,8 @@ snapshots:
fast-levenshtein@2.0.6: {}
+ fast-uri@3.1.0: {}
+
fastq@1.20.1:
dependencies:
reusify: 1.1.0
@@ -3134,6 +3820,15 @@ snapshots:
dependencies:
is-callable: 1.2.7
+ fs-extra@11.3.4:
+ dependencies:
+ graceful-fs: 4.2.11
+ jsonfile: 6.2.0
+ universalify: 2.0.1
+
+ fsevents@2.3.3:
+ optional: true
+
function-bind@1.1.2: {}
function.prototype.name@1.1.8:
@@ -3169,6 +3864,8 @@ snapshots:
dunder-proto: 1.0.1
es-object-atoms: 1.1.1
+ get-stream@6.0.1: {}
+
get-symbol-description@1.1.0:
dependencies:
call-bound: 1.0.4
@@ -3228,6 +3925,8 @@ snapshots:
dependencies:
hermes-estree: 0.25.1
+ human-signals@2.1.0: {}
+
ignore@5.3.2: {}
ignore@7.0.5: {}
@@ -3239,6 +3938,8 @@ snapshots:
imurmurhash@0.1.4: {}
+ ini@1.3.8: {}
+
internal-slot@1.1.0:
dependencies:
es-errors: 1.3.0
@@ -3289,12 +3990,16 @@ snapshots:
call-bound: 1.0.4
has-tostringtag: 1.0.2
+ is-docker@2.2.1: {}
+
is-extglob@2.1.1: {}
is-finalizationregistry@1.1.1:
dependencies:
call-bound: 1.0.4
+ is-fullwidth-code-point@3.0.0: {}
+
is-generator-function@1.1.2:
dependencies:
call-bound: 1.0.4
@@ -3318,6 +4023,8 @@ snapshots:
is-number@7.0.0: {}
+ is-port-reachable@4.0.0: {}
+
is-regex@1.2.1:
dependencies:
call-bound: 1.0.4
@@ -3331,6 +4038,8 @@ snapshots:
dependencies:
call-bound: 1.0.4
+ is-stream@2.0.1: {}
+
is-string@1.1.1:
dependencies:
call-bound: 1.0.4
@@ -3357,6 +4066,10 @@ snapshots:
call-bound: 1.0.4
get-intrinsic: 1.3.0
+ is-wsl@2.2.0:
+ dependencies:
+ is-docker: 2.2.1
+
isarray@2.0.5: {}
isexe@2.0.0: {}
@@ -3384,6 +4097,8 @@ snapshots:
json-schema-traverse@0.4.1: {}
+ json-schema-traverse@1.0.0: {}
+
json-stable-stringify-without-jsonify@1.0.1: {}
json5@1.0.2:
@@ -3392,6 +4107,12 @@ snapshots:
json5@2.2.3: {}
+ jsonfile@6.2.0:
+ dependencies:
+ universalify: 2.0.1
+ optionalDependencies:
+ graceful-fs: 4.2.11
+
jsx-ast-utils@3.3.5:
dependencies:
array-includes: 3.1.9
@@ -3463,6 +4184,8 @@ snapshots:
lightningcss-win32-arm64-msvc: 1.32.0
lightningcss-win32-x64-msvc: 1.32.0
+ load-script@1.0.0: {}
+
locate-path@6.0.0:
dependencies:
p-locate: 5.0.0
@@ -3483,6 +4206,10 @@ snapshots:
math-intrinsics@1.1.0: {}
+ memoize-one@5.2.1: {}
+
+ merge-stream@2.0.0: {}
+
merge2@1.4.1: {}
micromatch@4.0.8:
@@ -3490,6 +4217,16 @@ snapshots:
braces: 3.0.3
picomatch: 2.3.2
+ mime-db@1.33.0: {}
+
+ mime-db@1.54.0: {}
+
+ mime-types@2.1.18:
+ dependencies:
+ mime-db: 1.33.0
+
+ mimic-fn@2.1.0: {}
+
minimatch@10.2.5:
dependencies:
brace-expansion: 5.0.5
@@ -3500,6 +4237,8 @@ snapshots:
minimist@1.2.8: {}
+ ms@2.0.0: {}
+
ms@2.1.3: {}
nanoid@3.3.11: {}
@@ -3508,6 +4247,8 @@ snapshots:
natural-compare@1.4.0: {}
+ negotiator@0.6.4: {}
+
next@16.2.3(@babel/core@7.29.0)(react-dom@19.2.4(react@19.2.4))(react@19.2.4):
dependencies:
'@next/env': 16.2.3
@@ -3541,6 +4282,10 @@ snapshots:
node-releases@2.0.37: {}
+ npm-run-path@4.0.1:
+ dependencies:
+ path-key: 3.1.1
+
object-assign@4.1.1: {}
object-inspect@1.13.4: {}
@@ -3583,6 +4328,12 @@ snapshots:
define-properties: 1.2.1
es-object-atoms: 1.1.1
+ on-headers@1.1.0: {}
+
+ onetime@5.1.2:
+ dependencies:
+ mimic-fn: 2.1.0
+
optionator@0.9.4:
dependencies:
deep-is: 0.1.4
@@ -3612,10 +4363,14 @@ snapshots:
path-exists@4.0.0: {}
+ path-is-inside@1.0.2: {}
+
path-key@3.1.1: {}
path-parse@1.0.7: {}
+ path-to-regexp@3.3.0: {}
+
picocolors@1.1.1: {}
picomatch@2.3.2: {}
@@ -3648,13 +4403,33 @@ snapshots:
queue-microtask@1.2.3: {}
+ range-parser@1.2.0: {}
+
+ rc@1.2.8:
+ dependencies:
+ deep-extend: 0.6.0
+ ini: 1.3.8
+ minimist: 1.2.8
+ strip-json-comments: 2.0.1
+
react-dom@19.2.4(react@19.2.4):
dependencies:
react: 19.2.4
scheduler: 0.27.0
+ react-fast-compare@3.2.2: {}
+
react-is@16.13.1: {}
+ react-player@2.16.1(react@19.2.4):
+ dependencies:
+ deepmerge: 4.3.1
+ load-script: 1.0.0
+ memoize-one: 5.2.1
+ prop-types: 15.8.1
+ react: 19.2.4
+ react-fast-compare: 3.2.2
+
react@19.2.4: {}
reflect.getprototypeof@1.0.10:
@@ -3677,6 +4452,17 @@ snapshots:
gopd: 1.2.0
set-function-name: 2.0.2
+ registry-auth-token@3.3.2:
+ dependencies:
+ rc: 1.2.8
+ safe-buffer: 5.2.1
+
+ registry-url@3.1.0:
+ dependencies:
+ rc: 1.2.8
+
+ require-from-string@2.0.2: {}
+
resolve-from@4.0.0: {}
resolve-pkg-maps@1.0.0: {}
@@ -3704,6 +4490,8 @@ snapshots:
has-symbols: 1.1.0
isarray: 2.0.5
+ safe-buffer@5.2.1: {}
+
safe-push-apply@1.0.0:
dependencies:
es-errors: 1.3.0
@@ -3721,6 +4509,32 @@ snapshots:
semver@7.7.4: {}
+ serve-handler@6.1.7:
+ dependencies:
+ bytes: 3.0.0
+ content-disposition: 0.5.2
+ mime-types: 2.1.18
+ minimatch: 3.1.5
+ path-is-inside: 1.0.2
+ path-to-regexp: 3.3.0
+ range-parser: 1.2.0
+
+ serve@14.2.6:
+ dependencies:
+ '@zeit/schemas': 2.36.0
+ ajv: 8.18.0
+ arg: 5.0.2
+ boxen: 7.0.0
+ chalk: 5.0.1
+ chalk-template: 0.4.0
+ clipboardy: 3.0.0
+ compression: 1.8.1
+ is-port-reachable: 4.0.0
+ serve-handler: 6.1.7
+ update-check: 1.5.4
+ transitivePeerDependencies:
+ - supports-color
+
set-function-length@1.2.2:
dependencies:
define-data-property: 1.1.4
@@ -3809,6 +4623,8 @@ snapshots:
side-channel-map: 1.0.1
side-channel-weakmap: 1.0.2
+ signal-exit@3.0.7: {}
+
source-map-js@1.2.1: {}
stable-hash@0.0.5: {}
@@ -3818,6 +4634,18 @@ snapshots:
es-errors: 1.3.0
internal-slot: 1.1.0
+ string-width@4.2.3:
+ dependencies:
+ emoji-regex: 8.0.0
+ is-fullwidth-code-point: 3.0.0
+ strip-ansi: 6.0.1
+
+ string-width@5.1.2:
+ dependencies:
+ eastasianwidth: 0.2.0
+ emoji-regex: 9.2.2
+ strip-ansi: 7.2.0
+
string.prototype.includes@2.0.1:
dependencies:
call-bind: 1.0.9
@@ -3868,8 +4696,20 @@ snapshots:
define-properties: 1.2.1
es-object-atoms: 1.1.1
+ strip-ansi@6.0.1:
+ dependencies:
+ ansi-regex: 5.0.1
+
+ strip-ansi@7.2.0:
+ dependencies:
+ ansi-regex: 6.2.2
+
strip-bom@3.0.0: {}
+ strip-final-newline@2.0.0: {}
+
+ strip-json-comments@2.0.1: {}
+
strip-json-comments@3.1.1: {}
styled-jsx@5.1.6(@babel/core@7.29.0)(react@19.2.4):
@@ -3911,10 +4751,19 @@ snapshots:
tslib@2.8.1: {}
+ tsx@4.21.0:
+ dependencies:
+ esbuild: 0.27.7
+ get-tsconfig: 4.13.7
+ optionalDependencies:
+ fsevents: 2.3.3
+
type-check@0.4.0:
dependencies:
prelude-ls: 1.2.1
+ type-fest@2.19.0: {}
+
typed-array-buffer@1.0.3:
dependencies:
call-bound: 1.0.4
@@ -3970,6 +4819,8 @@ snapshots:
undici-types@6.21.0: {}
+ universalify@2.0.1: {}
+
unrs-resolver@1.11.1:
dependencies:
napi-postinstall: 0.3.4
@@ -4000,10 +4851,17 @@ snapshots:
escalade: 3.2.0
picocolors: 1.1.1
+ update-check@1.5.4:
+ dependencies:
+ registry-auth-token: 3.3.2
+ registry-url: 3.1.0
+
uri-js@4.4.1:
dependencies:
punycode: 2.3.1
+ vary@1.1.2: {}
+
which-boxed-primitive@1.1.1:
dependencies:
is-bigint: 1.1.0
@@ -4049,8 +4907,18 @@ snapshots:
dependencies:
isexe: 2.0.0
+ widest-line@4.0.1:
+ dependencies:
+ string-width: 5.1.2
+
word-wrap@1.2.5: {}
+ wrap-ansi@8.1.0:
+ dependencies:
+ ansi-styles: 6.2.3
+ string-width: 5.1.2
+ strip-ansi: 7.2.0
+
yallist@3.1.1: {}
yocto-queue@0.1.0: {}
diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml
@@ -1,3 +1,5 @@
ignoredBuiltDependencies:
- sharp
- unrs-resolver
+onlyBuiltDependencies:
+ - esbuild
diff --git a/settings.json b/settings.json
@@ -0,0 +1,5 @@
+{
+ "siteTitle": "Jeralyzer",
+ "siteDescription": "Browse and search Quartering's video transcripts",
+ "headerTitle": "Jeralyzer"
+}