mirror of
https://github.com/jakejarvis/jarv.is.git
synced 2026-04-17 10:28:46 -04:00
fix: don't pre-render view and comment count components
- Introduced a new PostStats component to handle view and comment counts, replacing the previous async implementation with a client-side approach. - Updated CommentCount component to use client-side state management for fetching comment counts. - Removed unnecessary caching logic from view and comment fetching functions. - Simplified date formatting by moving it inline, enhancing performance and readability.
This commit is contained in:
@@ -15,4 +15,3 @@ NEXT_PUBLIC_BASE_URL=
|
||||
NEXT_PUBLIC_GITHUB_REPO=
|
||||
NEXT_PUBLIC_ONION_DOMAIN=
|
||||
NEXT_PUBLIC_SITE_LOCALE=
|
||||
NEXT_PUBLIC_SITE_TZ=
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { NextResponse } from "next/server";
|
||||
import { getViewCounts } from "@/lib/views";
|
||||
import { getAllViewCounts } from "@/lib/server/views";
|
||||
|
||||
export const GET = async (): Promise<
|
||||
NextResponse<{
|
||||
@@ -13,7 +13,7 @@ export const GET = async (): Promise<
|
||||
}>
|
||||
> => {
|
||||
// note: while hits have been renamed to views in most places, this API shouldn't change due to it being snapshotted
|
||||
const views = await getViewCounts();
|
||||
const views = await getAllViewCounts();
|
||||
|
||||
const total = {
|
||||
hits: Object.values(views).reduce((acc, curr) => acc + curr, 0),
|
||||
|
||||
@@ -1,9 +1,7 @@
|
||||
import { env } from "@/lib/env";
|
||||
import { Suspense } from "react";
|
||||
import { cacheLife } from "next/cache";
|
||||
import Link from "next/link";
|
||||
import { JsonLd } from "react-schemaorg";
|
||||
import { formatDate, formatDateISO } from "@/lib/date";
|
||||
import { CalendarDaysIcon, TagIcon, SquarePenIcon, EyeIcon, MessagesSquareIcon } from "lucide-react";
|
||||
import ViewCounter from "@/components/view-counter";
|
||||
import CommentCount from "@/components/comment-count";
|
||||
@@ -47,22 +45,23 @@ export const generateMetadata = async ({ params }: { params: Promise<{ slug: str
|
||||
});
|
||||
};
|
||||
|
||||
// Cached helper to format dates - needed for Cache Components compatibility
|
||||
const getFormattedDates = async (date: string) => {
|
||||
"use cache";
|
||||
cacheLife("max");
|
||||
|
||||
return {
|
||||
dateISO: formatDateISO(date),
|
||||
dateTitle: formatDate(date, "MMM d, y, h:mm a O"),
|
||||
dateDisplay: formatDate(date, "MMMM d, y"),
|
||||
};
|
||||
};
|
||||
|
||||
const Page = async ({ params }: { params: Promise<{ slug: string }> }) => {
|
||||
const { slug } = await params;
|
||||
const frontmatter = await getFrontMatter(slug);
|
||||
const formattedDates = await getFormattedDates(frontmatter!.date);
|
||||
const d = new Date(frontmatter!.date);
|
||||
|
||||
const formattedDates = {
|
||||
dateISO: d.toISOString(),
|
||||
dateTitle: d.toLocaleString("en-US", {
|
||||
month: "short",
|
||||
day: "numeric",
|
||||
year: "numeric",
|
||||
hour: "numeric",
|
||||
minute: "2-digit",
|
||||
timeZoneName: "short",
|
||||
}),
|
||||
dateDisplay: d.toLocaleDateString("en-US", { month: "long", day: "numeric", year: "numeric" }),
|
||||
};
|
||||
|
||||
const { default: MDXContent } = await import(`../../../${POSTS_DIR}/${slug}/index.mdx`);
|
||||
|
||||
@@ -99,7 +98,7 @@ const Page = async ({ params }: { params: Promise<{ slug: string }> }) => {
|
||||
className={"text-foreground/70 flex flex-nowrap items-center gap-1.5 whitespace-nowrap hover:no-underline"}
|
||||
>
|
||||
<CalendarDaysIcon className="inline size-3 shrink-0" aria-hidden="true" />
|
||||
<time dateTime={formattedDates.dateISO} title={formattedDates.dateTitle}>
|
||||
<time dateTime={formattedDates.dateISO} title={formattedDates.dateTitle} suppressHydrationWarning>
|
||||
{formattedDates.dateDisplay}
|
||||
</time>
|
||||
</Link>
|
||||
@@ -134,16 +133,12 @@ const Page = async ({ params }: { params: Promise<{ slug: string }> }) => {
|
||||
className="text-foreground/70 flex flex-nowrap items-center gap-1.5 whitespace-nowrap hover:no-underline"
|
||||
>
|
||||
<MessagesSquareIcon className="inline size-3 shrink-0" aria-hidden="true" />
|
||||
<Suspense fallback={<span className="motion-safe:animate-pulse">0</span>}>
|
||||
<CommentCount slug={`${POSTS_DIR}/${frontmatter!.slug}`} />
|
||||
</Suspense>
|
||||
<CommentCount slug={`${POSTS_DIR}/${frontmatter!.slug}`} />
|
||||
</Link>
|
||||
|
||||
<div className="flex min-w-14 flex-nowrap items-center gap-1.5 whitespace-nowrap">
|
||||
<EyeIcon className="inline size-3 shrink-0" aria-hidden="true" />
|
||||
<Suspense fallback={<span className="motion-safe:animate-pulse">0</span>}>
|
||||
<ViewCounter slug={`${POSTS_DIR}/${frontmatter!.slug}`} />
|
||||
</Suspense>
|
||||
<ViewCounter slug={`${POSTS_DIR}/${frontmatter!.slug}`} />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -1,15 +1,9 @@
|
||||
import { env } from "@/lib/env";
|
||||
import { Suspense } from "react";
|
||||
import { cacheLife } from "next/cache";
|
||||
import Link from "next/link";
|
||||
import { EyeIcon, MessagesSquareIcon } from "lucide-react";
|
||||
import PageTitle from "@/components/layout/page-title";
|
||||
import PostStats from "@/components/post-stats";
|
||||
import { getFrontMatter, POSTS_DIR, type FrontMatter } from "@/lib/posts";
|
||||
import { createMetadata } from "@/lib/metadata";
|
||||
import { formatDate, formatDateISO } from "@/lib/date";
|
||||
import authorConfig from "@/lib/config/author";
|
||||
import { getViewCounts } from "@/lib/views";
|
||||
import { getCommentCounts } from "@/lib/server/comments";
|
||||
|
||||
export const metadata = createMetadata({
|
||||
title: "Notes",
|
||||
@@ -17,61 +11,26 @@ export const metadata = createMetadata({
|
||||
canonical: `/${POSTS_DIR}`,
|
||||
});
|
||||
|
||||
// Hoist number formatter to avoid re-creating on every render
|
||||
const numberFormatter = new Intl.NumberFormat(env.NEXT_PUBLIC_SITE_LOCALE);
|
||||
|
||||
// Async component that fetches and displays stats for a single post
|
||||
const PostStats = async ({ slug }: { slug: string }) => {
|
||||
const [views, comments] = await Promise.all([getViewCounts(slug), getCommentCounts(slug)]);
|
||||
|
||||
return (
|
||||
<>
|
||||
{views > 0 && (
|
||||
<span className="bg-muted text-foreground/65 inline-flex h-5 flex-nowrap items-center gap-1 rounded-md px-1.5 align-text-top text-xs font-semibold text-nowrap shadow select-none">
|
||||
<EyeIcon className="inline-block size-4 shrink-0" aria-hidden="true" />
|
||||
<span className="inline-block leading-none tabular-nums">{numberFormatter.format(views)}</span>
|
||||
</span>
|
||||
)}
|
||||
|
||||
{comments > 0 && (
|
||||
<Link
|
||||
href={`/${slug}#comments`}
|
||||
title={`${numberFormatter.format(comments)} ${comments === 1 ? "comment" : "comments"}`}
|
||||
className="inline-flex hover:no-underline"
|
||||
>
|
||||
<span className="bg-muted text-foreground/65 inline-flex h-5 flex-nowrap items-center gap-1 rounded-md px-1.5 align-text-top text-xs font-semibold text-nowrap shadow select-none">
|
||||
<MessagesSquareIcon className="inline-block size-3 shrink-0" aria-hidden="true" />
|
||||
<span className="inline-block leading-none tabular-nums">{numberFormatter.format(comments)}</span>
|
||||
</span>
|
||||
</Link>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
// Cached helper to format dates for posts - needed for Cache Components compatibility
|
||||
const getFormattedPostDates = async (posts: FrontMatter[]) => {
|
||||
"use cache";
|
||||
cacheLife("max");
|
||||
|
||||
return posts.map((post) => {
|
||||
const year = new Date(post.date).getUTCFullYear();
|
||||
return {
|
||||
...post,
|
||||
year,
|
||||
dateISO: formatDateISO(post.date),
|
||||
dateTitle: formatDate(post.date, "MMM d, y, h:mm a O"),
|
||||
dateDisplay: formatDate(post.date, "MMM d"),
|
||||
};
|
||||
});
|
||||
};
|
||||
|
||||
// Renders the posts list with static content, deferring stats to runtime via Suspense
|
||||
const PostsList = async () => {
|
||||
const posts = await getFrontMatter();
|
||||
|
||||
// Format dates in a cached function to avoid date-fns using new Date() during render
|
||||
const formattedPosts = await getFormattedPostDates(posts);
|
||||
const formattedPosts = posts.map((post) => {
|
||||
const d = new Date(post.date);
|
||||
return {
|
||||
...post,
|
||||
year: d.getUTCFullYear(),
|
||||
dateISO: d.toISOString(),
|
||||
dateTitle: d.toLocaleString("en-US", {
|
||||
month: "short",
|
||||
day: "numeric",
|
||||
year: "numeric",
|
||||
hour: "numeric",
|
||||
minute: "2-digit",
|
||||
timeZoneName: "short",
|
||||
}),
|
||||
dateDisplay: d.toLocaleDateString("en-US", { month: "short", day: "numeric" }),
|
||||
};
|
||||
});
|
||||
|
||||
const postsByYear: {
|
||||
[year: string]: (FrontMatter & {
|
||||
@@ -98,7 +57,7 @@ const PostsList = async () => {
|
||||
{posts.map(({ slug, dateISO, dateTitle, dateDisplay, title, htmlTitle }) => (
|
||||
<li className="flex text-base leading-relaxed" key={slug}>
|
||||
<span className="text-muted-foreground w-18 shrink-0 md:w-22">
|
||||
<time dateTime={dateISO} title={dateTitle}>
|
||||
<time dateTime={dateISO} title={dateTitle} suppressHydrationWarning>
|
||||
{dateDisplay}
|
||||
</time>
|
||||
</span>
|
||||
@@ -111,9 +70,7 @@ const PostsList = async () => {
|
||||
style={{ viewTransitionName: `note-title-${slug}` }}
|
||||
/>
|
||||
|
||||
<Suspense>
|
||||
<PostStats slug={`${POSTS_DIR}/${slug}`} />
|
||||
</Suspense>
|
||||
<PostStats slug={`${POSTS_DIR}/${slug}`} />
|
||||
</div>
|
||||
</li>
|
||||
))}
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
"use client";
|
||||
|
||||
import { ActivityCalendar, type Activity } from "react-activity-calendar";
|
||||
import { formatDate } from "@/lib/date";
|
||||
import { Tooltip, TooltipTrigger, TooltipContent } from "@/components/ui/tooltip";
|
||||
import { cn } from "@/lib/utils";
|
||||
|
||||
@@ -46,7 +45,7 @@ const Calendar = ({
|
||||
<Tooltip>
|
||||
<TooltipTrigger asChild>{block}</TooltipTrigger>
|
||||
<TooltipContent>
|
||||
<span className="text-[0.825rem] font-medium">{`${activity.count === 0 ? "No" : activity.count} ${noun}${activity.count === 1 ? "" : "s"} on ${formatDate(activity.date, "MMMM do")}`}</span>
|
||||
<span className="text-[0.825rem] font-medium">{`${activity.count === 0 ? "No" : activity.count} ${noun}${activity.count === 1 ? "" : "s"} on ${new Date(activity.date).toLocaleDateString("en-US", { month: "short", day: "numeric" })}`}</span>
|
||||
</TooltipContent>
|
||||
</Tooltip>
|
||||
)}
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
import { codeToHtml } from "shiki";
|
||||
import { cacheLife } from "next/cache";
|
||||
import reactToText from "react-to-text";
|
||||
import CopyButton from "@/components/copy-button";
|
||||
import { cn } from "@/lib/utils";
|
||||
import { cn, getTextContent } from "@/lib/utils";
|
||||
|
||||
interface CodeBlockProps extends React.ComponentProps<"pre"> {
|
||||
showLineNumbers?: boolean;
|
||||
@@ -29,7 +28,7 @@ const CodeBlock = async ({ children, className, showLineNumbers = true, ...props
|
||||
}
|
||||
|
||||
const codeProps = children.props as React.ComponentProps<"code">;
|
||||
const codeString = reactToText(codeProps.children).trim();
|
||||
const codeString = getTextContent(codeProps.children).trim();
|
||||
const lang = codeProps.className?.split("language-")[1] ?? "text";
|
||||
|
||||
const html = await renderCode(codeString, lang);
|
||||
|
||||
@@ -1,8 +1,31 @@
|
||||
import { env } from "@/lib/env";
|
||||
import { getCommentCounts } from "@/lib/server/comments";
|
||||
"use client";
|
||||
|
||||
const CommentCount = async ({ slug }: { slug: string }) => {
|
||||
const count = await getCommentCounts(slug);
|
||||
import { useEffect, useState } from "react";
|
||||
import { env } from "@/lib/env";
|
||||
import { getCommentCount } from "@/lib/server/comments";
|
||||
|
||||
const CommentCount = ({ slug }: { slug: string }) => {
|
||||
const [count, setCount] = useState<number | null>(null);
|
||||
const [error, setError] = useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
getCommentCount(slug)
|
||||
.then((result: number) => {
|
||||
setCount(result);
|
||||
})
|
||||
.catch((err: unknown) => {
|
||||
console.error("[comment-count] error:", err);
|
||||
setError(true);
|
||||
});
|
||||
}, [slug]);
|
||||
|
||||
if (error) {
|
||||
return <span title="Error getting comments">?</span>;
|
||||
}
|
||||
|
||||
if (count === null) {
|
||||
return <span className="motion-safe:animate-pulse">0</span>;
|
||||
}
|
||||
|
||||
return (
|
||||
<span
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
"use client";
|
||||
|
||||
import { useActionState, useState } from "react";
|
||||
import { useDebounce } from "react-use";
|
||||
import { useActionState, useState, useEffect } from "react";
|
||||
import { useDebouncedCallback } from "use-debounce";
|
||||
import { SendIcon, Loader2Icon, CheckIcon, XIcon } from "lucide-react";
|
||||
import Form from "next/form";
|
||||
import Input from "@/components/ui/input";
|
||||
@@ -35,14 +35,14 @@ const ContactForm = () => {
|
||||
// client-side validation using shared schema
|
||||
const [clientErrors, setClientErrors] = useState<Partial<Record<keyof ContactInput, string[]>>>({});
|
||||
|
||||
useDebounce(
|
||||
() => {
|
||||
const result = ContactSchema.safeParse(formFields);
|
||||
setClientErrors(result.success ? {} : result.error.flatten().fieldErrors);
|
||||
},
|
||||
150,
|
||||
[formFields]
|
||||
);
|
||||
const debouncedValidate = useDebouncedCallback(() => {
|
||||
const result = ContactSchema.safeParse(formFields);
|
||||
setClientErrors(result.success ? {} : result.error.flatten().fieldErrors);
|
||||
}, 150);
|
||||
|
||||
useEffect(() => {
|
||||
debouncedValidate();
|
||||
}, [formFields, debouncedValidate]);
|
||||
|
||||
const hasClientErrors = Object.values(clientErrors).some((errs) => (errs?.length || 0) > 0);
|
||||
|
||||
@@ -59,7 +59,7 @@ const ContactForm = () => {
|
||||
|
||||
return (
|
||||
<Form action={formAction} className="my-6 space-y-4">
|
||||
<div>
|
||||
<div className="not-prose">
|
||||
<Input
|
||||
type="text"
|
||||
name="name"
|
||||
@@ -111,7 +111,8 @@ const ContactForm = () => {
|
||||
{messageError && <span className="text-destructive text-[0.8rem] font-semibold">{messageError}</span>}
|
||||
|
||||
<div className="text-foreground/85 my-2 text-[0.8rem] leading-relaxed">
|
||||
<MarkdownIcon className="mr-1.5 inline-block size-4 align-text-top" /> Basic{" "}
|
||||
<MarkdownIcon className="mr-1.5 inline-block size-4 align-text-top" />
|
||||
Basic{" "}
|
||||
<a
|
||||
href="https://commonmark.org/help/"
|
||||
target="_blank"
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
import reactToText from "react-to-text";
|
||||
import { LinkIcon } from "lucide-react";
|
||||
import { cn } from "@/lib/utils";
|
||||
|
||||
@@ -14,7 +13,7 @@ const HeadingAnchor = ({ id, title, className }: { id: string; title: string; cl
|
||||
tabIndex={-1}
|
||||
>
|
||||
<LinkIcon className="inline-block size-[0.75em] align-baseline" />
|
||||
<span className="sr-only">Permalink to “{reactToText(title)}”</span>
|
||||
<span className="sr-only">Permalink to “{title}”</span>
|
||||
</a>
|
||||
);
|
||||
};
|
||||
|
||||
55
components/post-stats.tsx
Normal file
55
components/post-stats.tsx
Normal file
@@ -0,0 +1,55 @@
|
||||
"use client";
|
||||
|
||||
import { useEffect, useState } from "react";
|
||||
import Link from "next/link";
|
||||
import { EyeIcon, MessagesSquareIcon } from "lucide-react";
|
||||
import { env } from "@/lib/env";
|
||||
import { getViewCount } from "@/lib/server/views";
|
||||
import { getCommentCount } from "@/lib/server/comments";
|
||||
|
||||
const numberFormatter = new Intl.NumberFormat(env.NEXT_PUBLIC_SITE_LOCALE);
|
||||
|
||||
const PostStats = ({ slug }: { slug: string }) => {
|
||||
const [stats, setStats] = useState<{ views: number; comments: number } | null>(null);
|
||||
|
||||
useEffect(() => {
|
||||
Promise.all([getViewCount(slug), getCommentCount(slug)])
|
||||
.then(([views, comments]) => {
|
||||
setStats({ views, comments });
|
||||
})
|
||||
.catch((err) => {
|
||||
console.error("[post-stats] error:", err);
|
||||
// Silently fail - just don't show stats
|
||||
});
|
||||
}, [slug]);
|
||||
|
||||
if (!stats) {
|
||||
return null; // No loading state - badges just appear when ready
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
{stats.views > 0 && (
|
||||
<span className="bg-muted text-foreground/65 inline-flex h-5 flex-nowrap items-center gap-1 rounded-md px-1.5 align-text-top text-xs font-semibold text-nowrap shadow select-none">
|
||||
<EyeIcon className="inline-block size-4 shrink-0" aria-hidden="true" />
|
||||
<span className="inline-block leading-none tabular-nums">{numberFormatter.format(stats.views)}</span>
|
||||
</span>
|
||||
)}
|
||||
|
||||
{stats.comments > 0 && (
|
||||
<Link
|
||||
href={`/${slug}#comments`}
|
||||
title={`${numberFormatter.format(stats.comments)} ${stats.comments === 1 ? "comment" : "comments"}`}
|
||||
className="inline-flex hover:no-underline"
|
||||
>
|
||||
<span className="bg-muted text-foreground/65 inline-flex h-5 flex-nowrap items-center gap-1 rounded-md px-1.5 align-text-top text-xs font-semibold text-nowrap shadow select-none">
|
||||
<MessagesSquareIcon className="inline-block size-3 shrink-0" aria-hidden="true" />
|
||||
<span className="inline-block leading-none tabular-nums">{numberFormatter.format(stats.comments)}</span>
|
||||
</span>
|
||||
</Link>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default PostStats;
|
||||
@@ -1,11 +1,18 @@
|
||||
"use client";
|
||||
|
||||
import TimeAgo from "react-timeago";
|
||||
import { makeIntlFormatter } from "react-timeago/defaultFormatter";
|
||||
|
||||
const intlFormatter = makeIntlFormatter({
|
||||
locale: "en",
|
||||
style: "long",
|
||||
numeric: "auto",
|
||||
});
|
||||
|
||||
const RelativeTime = ({ ...rest }: React.ComponentProps<typeof TimeAgo>) => {
|
||||
return (
|
||||
<span suppressHydrationWarning>
|
||||
<TimeAgo {...rest} />
|
||||
<TimeAgo formatter={intlFormatter} {...rest} />
|
||||
</span>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -3,7 +3,7 @@ import { cva, type VariantProps } from "class-variance-authority";
|
||||
import { cn } from "@/lib/utils";
|
||||
|
||||
export const buttonVariants = cva(
|
||||
"focus-visible:border-ring focus-visible:ring-ring/50 aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive inline-flex shrink-0 cursor-pointer items-center justify-center gap-2 rounded-md text-sm font-medium whitespace-nowrap outline-none focus-visible:ring-[3px] disabled:pointer-events-none disabled:cursor-not-allowed disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
|
||||
"focus-visible:border-ring focus-visible:ring-ring/50 aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive inline-flex shrink-0 cursor-pointer items-center justify-center gap-2 rounded-md text-sm font-medium whitespace-nowrap outline-none focus-visible:ring-[3px] disabled:cursor-not-allowed disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
|
||||
{
|
||||
variants: {
|
||||
variant: {
|
||||
|
||||
@@ -55,7 +55,7 @@ const DropdownMenuItem = ({
|
||||
data-inset={inset}
|
||||
data-variant={variant}
|
||||
className={cn(
|
||||
"focus:bg-accent focus:text-accent-foreground data-[variant=destructive]:text-destructive data-[variant=destructive]:focus:bg-destructive/10 dark:data-[variant=destructive]:focus:bg-destructive/20 data-[variant=destructive]:focus:text-destructive data-[variant=destructive]:*:[svg]:!text-destructive [&_svg:not([class*='text-'])]:text-muted-foreground relative flex cursor-pointer items-center gap-2 rounded-sm px-2 py-1.5 text-sm outline-hidden select-none data-[disabled]:pointer-events-none data-[disabled]:opacity-50 data-[inset]:pl-8 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
|
||||
"focus:bg-accent focus:text-accent-foreground data-[variant=destructive]:text-destructive data-[variant=destructive]:focus:bg-destructive/10 dark:data-[variant=destructive]:focus:bg-destructive/20 data-[variant=destructive]:focus:text-destructive data-[variant=destructive]:*:[svg]:!text-destructive [&_svg:not([class*='text-'])]:text-muted-foreground relative flex cursor-pointer items-center gap-2 rounded-sm px-2 py-1.5 text-sm outline-hidden select-none data-[disabled]:opacity-50 data-[inset]:pl-8 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
|
||||
className
|
||||
)}
|
||||
{...rest}
|
||||
@@ -73,7 +73,7 @@ const DropdownMenuCheckboxItem = ({
|
||||
<DropdownMenuPrimitive.CheckboxItem
|
||||
data-slot="dropdown-menu-checkbox-item"
|
||||
className={cn(
|
||||
"focus:bg-accent focus:text-accent-foreground relative flex cursor-default items-center gap-2 rounded-sm py-1.5 pr-2 pl-8 text-sm outline-hidden select-none data-[disabled]:pointer-events-none data-[disabled]:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
|
||||
"focus:bg-accent focus:text-accent-foreground relative flex cursor-default items-center gap-2 rounded-sm py-1.5 pr-2 pl-8 text-sm outline-hidden select-none data-[disabled]:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
|
||||
className
|
||||
)}
|
||||
checked={checked}
|
||||
@@ -102,7 +102,7 @@ const DropdownMenuRadioItem = ({
|
||||
<DropdownMenuPrimitive.RadioItem
|
||||
data-slot="dropdown-menu-radio-item"
|
||||
className={cn(
|
||||
"focus:bg-accent focus:text-accent-foreground relative flex cursor-default items-center gap-2 rounded-sm py-1.5 pr-2 pl-8 text-sm outline-hidden select-none data-[disabled]:pointer-events-none data-[disabled]:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
|
||||
"focus:bg-accent focus:text-accent-foreground relative flex cursor-default items-center gap-2 rounded-sm py-1.5 pr-2 pl-8 text-sm outline-hidden select-none data-[disabled]:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
|
||||
className
|
||||
)}
|
||||
{...rest}
|
||||
|
||||
13
lib/date.ts
13
lib/date.ts
@@ -1,13 +0,0 @@
|
||||
import { env } from "@/lib/env";
|
||||
import { format, formatISO } from "date-fns";
|
||||
import { enUS } from "date-fns/locale";
|
||||
import { tz } from "@date-fns/tz";
|
||||
import { utc } from "@date-fns/utc";
|
||||
|
||||
export const formatDate = (date: string | number | Date, formatStr = "PPpp") => {
|
||||
return format(date, formatStr, { in: tz(env.NEXT_PUBLIC_SITE_TZ), locale: enUS });
|
||||
};
|
||||
|
||||
export const formatDateISO = (date: string | number | Date) => {
|
||||
return formatISO(date, { in: utc });
|
||||
};
|
||||
@@ -128,14 +128,6 @@ export const env = createEnv({
|
||||
* @see https://www.loc.gov/standards/iso639-2/php/code_list.php
|
||||
*/
|
||||
NEXT_PUBLIC_SITE_LOCALE: z.string().default("en-US"),
|
||||
|
||||
/**
|
||||
* Optional. Consistent timezone for the site. Doesn't really matter what it is, as long as it's the same everywhere
|
||||
* to avoid hydration complaints. Defaults to `America/New_York`.
|
||||
*
|
||||
* @see https://en.wikipedia.org/wiki/List_of_tz_database_time_zones#List
|
||||
*/
|
||||
NEXT_PUBLIC_SITE_TZ: z.string().default("America/New_York"),
|
||||
},
|
||||
experimental__runtimeEnv: {
|
||||
NEXT_PUBLIC_BASE_URL: process.env.NEXT_PUBLIC_BASE_URL,
|
||||
@@ -144,7 +136,6 @@ export const env = createEnv({
|
||||
NEXT_PUBLIC_GITHUB_USERNAME: process.env.NEXT_PUBLIC_GITHUB_USERNAME,
|
||||
NEXT_PUBLIC_ONION_DOMAIN: process.env.NEXT_PUBLIC_ONION_DOMAIN,
|
||||
NEXT_PUBLIC_SITE_LOCALE: process.env.NEXT_PUBLIC_SITE_LOCALE,
|
||||
NEXT_PUBLIC_SITE_TZ: process.env.NEXT_PUBLIC_SITE_TZ,
|
||||
},
|
||||
emptyStringAsUndefined: true,
|
||||
skipValidation: !!process.env.SKIP_ENV_VALIDATION,
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
"use server";
|
||||
|
||||
import { headers } from "next/headers";
|
||||
import { revalidatePath, revalidateTag, cacheTag } from "next/cache";
|
||||
import { revalidatePath } from "next/cache";
|
||||
import { eq, desc, inArray, sql } from "drizzle-orm";
|
||||
import { checkBotId } from "botid/server";
|
||||
import { db } from "@/lib/db";
|
||||
@@ -13,9 +13,6 @@ export type CommentWithUser = typeof schema.comment.$inferSelect & {
|
||||
};
|
||||
|
||||
export const getComments = async (pageSlug: string): Promise<CommentWithUser[]> => {
|
||||
"use cache";
|
||||
cacheTag("comments", `comments-${pageSlug}`);
|
||||
|
||||
try {
|
||||
// Fetch all comments for the page with user details
|
||||
const commentsWithUsers = await db
|
||||
@@ -41,59 +38,55 @@ export const getComments = async (pageSlug: string): Promise<CommentWithUser[]>
|
||||
}
|
||||
};
|
||||
|
||||
export const getCommentCounts: {
|
||||
/**
|
||||
* Retrieves the number of comments for a given slug
|
||||
*/
|
||||
(slug: string): Promise<number>;
|
||||
/**
|
||||
* Retrieves the numbers of comments for an array of slugs
|
||||
*/
|
||||
(slug: string[]): Promise<Record<string, number>>;
|
||||
/**
|
||||
* Retrieves the numbers of comments for ALL slugs
|
||||
*/
|
||||
(): Promise<Record<string, number>>;
|
||||
} = async (
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
slug?: any
|
||||
): // eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
Promise<any> => {
|
||||
"use cache";
|
||||
cacheTag("comments");
|
||||
|
||||
/**
|
||||
* Retrieves the number of comments for a given slug
|
||||
*/
|
||||
export const getCommentCount = async (slug: string): Promise<number> => {
|
||||
try {
|
||||
// return one page
|
||||
if (typeof slug === "string") {
|
||||
const result = await db
|
||||
.select({
|
||||
count: sql<number>`cast(count(${schema.comment.id}) as int)`,
|
||||
})
|
||||
.from(schema.comment)
|
||||
.where(eq(schema.comment.pageSlug, slug));
|
||||
const result = await db
|
||||
.select({
|
||||
count: sql<number>`cast(count(${schema.comment.id}) as int)`,
|
||||
})
|
||||
.from(schema.comment)
|
||||
.where(eq(schema.comment.pageSlug, slug));
|
||||
|
||||
return Number(result[0]?.count ?? 0);
|
||||
return Number(result[0]?.count ?? 0);
|
||||
} catch (error) {
|
||||
console.error("[server/comments] error fetching comment count:", error);
|
||||
return 0;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Retrieves the numbers of comments for an array of slugs
|
||||
*/
|
||||
export const getCommentCountsForSlugs = async (slugs: string[]): Promise<Record<string, number>> => {
|
||||
try {
|
||||
const rows = await db
|
||||
.select({
|
||||
pageSlug: schema.comment.pageSlug,
|
||||
count: sql<number>`cast(count(${schema.comment.id}) as int)`,
|
||||
})
|
||||
.from(schema.comment)
|
||||
.where(inArray(schema.comment.pageSlug, slugs))
|
||||
.groupBy(schema.comment.pageSlug);
|
||||
|
||||
const map: Record<string, number> = Object.fromEntries(slugs.map((s) => [s, 0]));
|
||||
for (const row of rows) {
|
||||
map[row.pageSlug] = Number(row.count ?? 0);
|
||||
}
|
||||
return map;
|
||||
} catch (error) {
|
||||
console.error("[server/comments] error fetching comment counts:", error);
|
||||
return Object.fromEntries(slugs.map((s) => [s, 0]));
|
||||
}
|
||||
};
|
||||
|
||||
// return multiple pages
|
||||
if (Array.isArray(slug)) {
|
||||
const rows = await db
|
||||
.select({
|
||||
pageSlug: schema.comment.pageSlug,
|
||||
count: sql<number>`cast(count(${schema.comment.id}) as int)`,
|
||||
})
|
||||
.from(schema.comment)
|
||||
.where(inArray(schema.comment.pageSlug, slug))
|
||||
.groupBy(schema.comment.pageSlug);
|
||||
|
||||
const map: Record<string, number> = Object.fromEntries(slug.map((s: string) => [s, 0]));
|
||||
for (const row of rows) {
|
||||
map[row.pageSlug] = Number(row.count ?? 0);
|
||||
}
|
||||
return map;
|
||||
}
|
||||
|
||||
// return ALL pages
|
||||
/**
|
||||
* Retrieves the numbers of comments for ALL slugs
|
||||
*/
|
||||
export const getAllCommentCounts = async (): Promise<Record<string, number>> => {
|
||||
try {
|
||||
const rows = await db
|
||||
.select({
|
||||
pageSlug: schema.comment.pageSlug,
|
||||
@@ -109,9 +102,6 @@ Promise<any> => {
|
||||
return map;
|
||||
} catch (error) {
|
||||
console.error("[server/comments] error fetching comment counts:", error);
|
||||
// Return sensible defaults instead of throwing during prerendering
|
||||
if (typeof slug === "string") return 0;
|
||||
if (Array.isArray(slug)) return Object.fromEntries(slug.map((s: string) => [s, 0]));
|
||||
return {};
|
||||
}
|
||||
};
|
||||
@@ -141,12 +131,8 @@ export const createComment = async (data: { content: string; pageSlug: string; p
|
||||
userId: session.user.id,
|
||||
});
|
||||
|
||||
// Revalidate caches and paths
|
||||
revalidateTag("comments", "max");
|
||||
revalidateTag(`comments-${data.pageSlug}`, "max");
|
||||
// Revalidate page
|
||||
revalidatePath(`/${data.pageSlug}`);
|
||||
// Also revalidate the notes listing to update comment count badges
|
||||
revalidatePath("/notes");
|
||||
} catch (error) {
|
||||
console.error("[server/comments] error creating comment:", error);
|
||||
throw new Error("Failed to create comment");
|
||||
@@ -195,13 +181,8 @@ export const updateComment = async (commentId: string, content: string) => {
|
||||
})
|
||||
.where(eq(schema.comment.id, commentId));
|
||||
|
||||
// Revalidate caches and paths
|
||||
revalidateTag("comments", "max");
|
||||
revalidateTag(`comments-${comment.pageSlug}`, "max");
|
||||
// Revalidate page
|
||||
revalidatePath(`/${comment.pageSlug}`);
|
||||
// Also revalidate the notes listing to update comment count badges
|
||||
// TODO: make this more generic in case we want to add comments to non-note pages
|
||||
revalidatePath("/notes");
|
||||
} catch (error) {
|
||||
console.error("[server/comments] error updating comment:", error);
|
||||
throw new Error("Failed to update comment");
|
||||
@@ -244,13 +225,8 @@ export const deleteComment = async (commentId: string) => {
|
||||
// Delete the comment
|
||||
await db.delete(schema.comment).where(eq(schema.comment.id, commentId));
|
||||
|
||||
// Revalidate caches and paths
|
||||
revalidateTag("comments", "max");
|
||||
revalidateTag(`comments-${comment.pageSlug}`, "max");
|
||||
// Revalidate page
|
||||
revalidatePath(`/${comment.pageSlug}`);
|
||||
// Also revalidate the notes listing to update comment count badges
|
||||
// TODO: make this more generic in case we want to add comments to non-note pages
|
||||
revalidatePath("/notes");
|
||||
} catch (error) {
|
||||
console.error("[server/comments] error deleting comment:", error);
|
||||
throw new Error("Failed to delete comment");
|
||||
|
||||
@@ -1,10 +1,62 @@
|
||||
"use server";
|
||||
|
||||
import { sql } from "drizzle-orm";
|
||||
import { eq, inArray, sql } from "drizzle-orm";
|
||||
import { revalidateTag } from "next/cache";
|
||||
import { db } from "@/lib/db";
|
||||
import { page } from "@/lib/db/schema";
|
||||
|
||||
/**
|
||||
* Retrieves the number of views for a given slug, or 0 if the slug does not exist or on error
|
||||
*/
|
||||
export const getViewCount = async (slug: string): Promise<number> => {
|
||||
try {
|
||||
const pages = await db.select().from(page).where(eq(page.slug, slug)).limit(1);
|
||||
return pages[0]?.views ?? 0;
|
||||
} catch (error) {
|
||||
console.error("[server/views] fatal error:", error);
|
||||
return 0;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Retrieves the numbers of views for an array of slugs, returning 0 for any that don't exist
|
||||
*/
|
||||
export const getViewCountsForSlugs = async (slugs: string[]): Promise<Record<string, number>> => {
|
||||
try {
|
||||
const pages = await db.select().from(page).where(inArray(page.slug, slugs));
|
||||
const viewMap: Record<string, number> = Object.fromEntries(slugs.map((s) => [s, 0]));
|
||||
for (const p of pages) {
|
||||
viewMap[p.slug] = p.views;
|
||||
}
|
||||
return viewMap;
|
||||
} catch (error) {
|
||||
console.error("[server/views] fatal error:", error);
|
||||
return Object.fromEntries(slugs.map((s) => [s, 0]));
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Retrieves the numbers of views for ALL slugs
|
||||
*/
|
||||
export const getAllViewCounts = async (): Promise<Record<string, number>> => {
|
||||
try {
|
||||
const pages = await db.select().from(page);
|
||||
return pages.reduce(
|
||||
(acc, p) => {
|
||||
acc[p.slug] = p.views;
|
||||
return acc;
|
||||
},
|
||||
{} as Record<string, number>
|
||||
);
|
||||
} catch (error) {
|
||||
console.error("[server/views] fatal error:", error);
|
||||
return {};
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Increments the view count for a given slug (upserts if doesn't exist)
|
||||
*/
|
||||
export const incrementViews = async (slug: string): Promise<number> => {
|
||||
try {
|
||||
// Atomic upsert: insert new row with views=1, or increment existing row
|
||||
@@ -17,13 +69,12 @@ export const incrementViews = async (slug: string): Promise<number> => {
|
||||
})
|
||||
.returning({ views: page.views });
|
||||
|
||||
// Invalidate the views cache so getViewCounts returns fresh data
|
||||
// This is safe here because this entire file is marked "use server"
|
||||
// Invalidate the views cache so getViewCount returns fresh data
|
||||
revalidateTag("views", "max");
|
||||
|
||||
return result.views;
|
||||
} catch (error) {
|
||||
console.error("[actions/views] error incrementing views:", error);
|
||||
console.error("[server/views] error incrementing views:", error);
|
||||
throw new Error("Failed to increment views");
|
||||
}
|
||||
};
|
||||
|
||||
14
lib/utils.ts
14
lib/utils.ts
@@ -4,3 +4,17 @@ import { twMerge } from "tailwind-merge";
|
||||
export const cn = (...inputs: ClassValue[]) => {
|
||||
return twMerge(clsx(inputs));
|
||||
};
|
||||
|
||||
/**
|
||||
* Recursively extracts plain text content from React nodes.
|
||||
* Replacement for the `react-to-text` package.
|
||||
*/
|
||||
export const getTextContent = (node: React.ReactNode): string => {
|
||||
if (node == null || typeof node === "boolean") return "";
|
||||
if (typeof node === "string" || typeof node === "number") return String(node);
|
||||
if (Array.isArray(node)) return node.map(getTextContent).join("");
|
||||
if (typeof node === "object" && "props" in node) {
|
||||
return getTextContent((node as React.ReactElement<{ children?: React.ReactNode }>).props.children);
|
||||
}
|
||||
return "";
|
||||
};
|
||||
|
||||
58
lib/views.ts
58
lib/views.ts
@@ -1,58 +0,0 @@
|
||||
import "server-only";
|
||||
|
||||
import { eq, inArray } from "drizzle-orm";
|
||||
import { cacheTag } from "next/cache";
|
||||
import { db } from "@/lib/db";
|
||||
import { page } from "@/lib/db/schema";
|
||||
|
||||
export const getViewCounts: {
|
||||
/**
|
||||
* Retrieves the number of views for a given slug, or 0 if the slug does not exist or on error
|
||||
*/
|
||||
(slug: string): Promise<number>;
|
||||
/**
|
||||
* Retrieves the numbers of views for an array of slugs, returning 0 for any that don't exist
|
||||
*/
|
||||
(slug: string[]): Promise<Record<string, number>>;
|
||||
/**
|
||||
* Retrieves the numbers of views for ALL slugs
|
||||
*/
|
||||
(): Promise<Record<string, number>>;
|
||||
} = (async (slug?: string | string[]) => {
|
||||
"use cache";
|
||||
cacheTag("views");
|
||||
|
||||
try {
|
||||
// return one page
|
||||
if (typeof slug === "string") {
|
||||
const pages = await db.select().from(page).where(eq(page.slug, slug)).limit(1);
|
||||
return pages[0]?.views ?? 0; // Return 0 if no row found
|
||||
}
|
||||
|
||||
// return multiple pages
|
||||
if (Array.isArray(slug)) {
|
||||
const pages = await db.select().from(page).where(inArray(page.slug, slug));
|
||||
const viewMap: Record<string, number> = Object.fromEntries(slug.map((s) => [s, 0]));
|
||||
for (const p of pages) {
|
||||
viewMap[p.slug] = p.views;
|
||||
}
|
||||
return viewMap;
|
||||
}
|
||||
|
||||
// return ALL pages
|
||||
const pages = await db.select().from(page);
|
||||
return pages.reduce(
|
||||
(acc, page) => {
|
||||
acc[page.slug] = page.views;
|
||||
return acc;
|
||||
},
|
||||
{} as Record<string, number>
|
||||
);
|
||||
} catch (error) {
|
||||
console.error("[server/views] fatal error:", error);
|
||||
// Return sensible defaults instead of throwing during prerendering
|
||||
if (typeof slug === "string") return 0;
|
||||
if (Array.isArray(slug)) return Object.fromEntries(slug.map((s) => [s, 0]));
|
||||
return {};
|
||||
}
|
||||
}) as typeof getViewCounts;
|
||||
15
package.json
15
package.json
@@ -20,8 +20,6 @@
|
||||
"prepare": "test -d node_modules/husky && husky || echo \"skipping husky\""
|
||||
},
|
||||
"dependencies": {
|
||||
"@date-fns/tz": "^1.4.1",
|
||||
"@date-fns/utc": "^2.1.1",
|
||||
"@mdx-js/loader": "^3.1.1",
|
||||
"@mdx-js/react": "^3.1.1",
|
||||
"@next/mdx": "16.1.6",
|
||||
@@ -49,7 +47,6 @@
|
||||
"class-variance-authority": "^0.7.1",
|
||||
"clsx": "^2.1.1",
|
||||
"copy-to-clipboard": "^3.3.3",
|
||||
"date-fns": "^4.1.0",
|
||||
"drizzle-orm": "^0.45.1",
|
||||
"fast-glob": "^3.3.3",
|
||||
"feed": "^5.2.0",
|
||||
@@ -67,9 +64,7 @@
|
||||
"react-markdown": "^10.1.0",
|
||||
"react-schemaorg": "^2.0.0",
|
||||
"react-timeago": "^8.3.0",
|
||||
"react-to-text": "^2.0.1",
|
||||
"react-tweet": "^3.3.0",
|
||||
"react-use": "^17.6.0",
|
||||
"rehype-external-links": "^3.0.0",
|
||||
"rehype-mdx-code-props": "^3.0.1",
|
||||
"rehype-mdx-import-media": "^1.2.0",
|
||||
@@ -94,6 +89,7 @@
|
||||
"tailwindcss": "^4.1.18",
|
||||
"tw-animate-css": "^1.4.0",
|
||||
"unified": "^11.0.5",
|
||||
"use-debounce": "^10.0.4",
|
||||
"zod": "^4.3.6"
|
||||
},
|
||||
"devDependencies": {
|
||||
@@ -104,7 +100,7 @@
|
||||
"@tailwindcss/typography": "^0.5.19",
|
||||
"@types/hast": "^3.0.4",
|
||||
"@types/mdx": "^2.0.13",
|
||||
"@types/node": "^25.0.10",
|
||||
"@types/node": "^25.1.0",
|
||||
"@types/pg": "^8.16.0",
|
||||
"@types/react": "19.2.10",
|
||||
"@types/react-dom": "19.2.3",
|
||||
@@ -144,5 +140,12 @@
|
||||
"*.{js,jsx,ts,tsx,md,mdx}": [
|
||||
"eslint"
|
||||
]
|
||||
},
|
||||
"pnpm": {
|
||||
"onlyBuiltDependencies": [
|
||||
"esbuild",
|
||||
"sharp",
|
||||
"unrs-resolver"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
264
pnpm-lock.yaml
generated
264
pnpm-lock.yaml
generated
@@ -8,12 +8,6 @@ importers:
|
||||
|
||||
.:
|
||||
dependencies:
|
||||
'@date-fns/tz':
|
||||
specifier: ^1.4.1
|
||||
version: 1.4.1
|
||||
'@date-fns/utc':
|
||||
specifier: ^2.1.1
|
||||
version: 2.1.1
|
||||
'@mdx-js/loader':
|
||||
specifier: ^3.1.1
|
||||
version: 3.1.1
|
||||
@@ -95,9 +89,6 @@ importers:
|
||||
copy-to-clipboard:
|
||||
specifier: ^3.3.3
|
||||
version: 3.3.3
|
||||
date-fns:
|
||||
specifier: ^4.1.0
|
||||
version: 4.1.0
|
||||
drizzle-orm:
|
||||
specifier: ^0.45.1
|
||||
version: 0.45.1(@types/pg@8.16.0)(kysely@0.28.10)(pg@8.17.2)
|
||||
@@ -149,15 +140,9 @@ importers:
|
||||
react-timeago:
|
||||
specifier: ^8.3.0
|
||||
version: 8.3.0(react@19.2.4)
|
||||
react-to-text:
|
||||
specifier: ^2.0.1
|
||||
version: 2.0.1(react-dom@19.2.4(react@19.2.4))(react@19.2.4)
|
||||
react-tweet:
|
||||
specifier: ^3.3.0
|
||||
version: 3.3.0(react-dom@19.2.4(react@19.2.4))(react@19.2.4)
|
||||
react-use:
|
||||
specifier: ^17.6.0
|
||||
version: 17.6.0(react-dom@19.2.4(react@19.2.4))(react@19.2.4)
|
||||
rehype-external-links:
|
||||
specifier: ^3.0.0
|
||||
version: 3.0.0
|
||||
@@ -230,6 +215,9 @@ importers:
|
||||
unified:
|
||||
specifier: ^11.0.5
|
||||
version: 11.0.5
|
||||
use-debounce:
|
||||
specifier: ^10.0.4
|
||||
version: 10.1.0(react@19.2.4)
|
||||
zod:
|
||||
specifier: ^4.3.6
|
||||
version: 4.3.6
|
||||
@@ -256,8 +244,8 @@ importers:
|
||||
specifier: ^2.0.13
|
||||
version: 2.0.13
|
||||
'@types/node':
|
||||
specifier: ^25.0.10
|
||||
version: 25.0.10
|
||||
specifier: ^25.1.0
|
||||
version: 25.1.0
|
||||
'@types/pg':
|
||||
specifier: ^8.16.0
|
||||
version: 8.16.0
|
||||
@@ -437,10 +425,6 @@ packages:
|
||||
peerDependencies:
|
||||
'@babel/core': ^7.0.0-0
|
||||
|
||||
'@babel/runtime@7.28.6':
|
||||
resolution: {integrity: sha512-05WQkdpL9COIMz4LjTxGpPNCdlpyimKppYNoJ5Di5EUObifl8t4tuLuUBBZEpoLYOmfvIWrsp9fCl0HoPRVTdA==}
|
||||
engines: {node: '>=6.9.0'}
|
||||
|
||||
'@babel/template@7.28.6':
|
||||
resolution: {integrity: sha512-YA6Ma2KsCdGb+WC6UpBVFJGXL58MDA6oyONbjyF/+5sBgxY/dwkhLogbMT2GXXyU84/IhRw/2D1Os1B/giz+BQ==}
|
||||
engines: {node: '>=6.9.0'}
|
||||
@@ -474,12 +458,6 @@ packages:
|
||||
'@better-fetch/fetch@1.1.21':
|
||||
resolution: {integrity: sha512-/ImESw0sskqlVR94jB+5+Pxjf+xBwDZF/N5+y2/q4EqD7IARUTSpPfIo8uf39SYpCxyOCtbyYpUrZ3F/k0zT4A==}
|
||||
|
||||
'@date-fns/tz@1.4.1':
|
||||
resolution: {integrity: sha512-P5LUNhtbj6YfI3iJjw5EL9eUAG6OitD0W3fWQcpQjDRc/QIsL0tRNuO1PcDvPccWL1fSTXXdE1ds+l95DV/OFA==}
|
||||
|
||||
'@date-fns/utc@2.1.1':
|
||||
resolution: {integrity: sha512-SlJDfG6RPeEX8wEVv6ZB3kak4MmbtyiI2qX/5zuKdordbrhB/iaJ58GVMZgJ6P1sJaM1gMgENFYYeg1JWrCFrA==}
|
||||
|
||||
'@drizzle-team/brocli@0.10.2':
|
||||
resolution: {integrity: sha512-z33Il7l5dKjUgGULTqBsQBQwckHh5AbIuxhdsIxDDiZAzBOrZO6q9ogcWC65kU382AfynTfgNumVcNIjuIua6w==}
|
||||
|
||||
@@ -1857,9 +1835,6 @@ packages:
|
||||
'@types/is-empty@1.2.3':
|
||||
resolution: {integrity: sha512-4J1l5d79hoIvsrKh5VUKVRA1aIdsOb10Hu5j3J2VfP/msDnfTdGPmNp2E1Wg+vs97Bktzo+MZePFFXSGoykYJw==}
|
||||
|
||||
'@types/js-cookie@2.2.7':
|
||||
resolution: {integrity: sha512-aLkWa0C0vO5b4Sr798E26QgOkss68Un0bLjs7u9qxzPT5CG+8DuNTffWES58YzJs3hrVAOs1wonycqEBqNJubA==}
|
||||
|
||||
'@types/json-schema@7.0.15':
|
||||
resolution: {integrity: sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==}
|
||||
|
||||
@@ -1881,8 +1856,8 @@ packages:
|
||||
'@types/node@22.19.7':
|
||||
resolution: {integrity: sha512-MciR4AKGHWl7xwxkBa6xUGxQJ4VBOmPTF7sL+iGzuahOFaO0jHCsuEfS80pan1ef4gWId1oWOweIhrDEYLuaOw==}
|
||||
|
||||
'@types/node@25.0.10':
|
||||
resolution: {integrity: sha512-zWW5KPngR/yvakJgGOmZ5vTBemDoSqF3AcV/LrO5u5wTWyEAVVh+IT39G4gtyAkh3CtTZs8aX/yRM82OfzHJRg==}
|
||||
'@types/node@25.1.0':
|
||||
resolution: {integrity: sha512-t7frlewr6+cbx+9Ohpl0NOTKXZNV9xHRmNOvql47BFJKcEG1CxtxlPEEe+gR9uhVWM4DwhnvTF110mIL4yP9RA==}
|
||||
|
||||
'@types/pg@8.16.0':
|
||||
resolution: {integrity: sha512-RmhMd/wD+CF8Dfo+cVIy3RR5cl8CyfXQ0tGgW6XBL8L4LM/UTEbNXYRbLwU6w+CgrKBNbrQWt4FUtTfaU5jSYQ==}
|
||||
@@ -2131,9 +2106,6 @@ packages:
|
||||
vue-router:
|
||||
optional: true
|
||||
|
||||
'@xobotyi/scrollbar-width@1.9.5':
|
||||
resolution: {integrity: sha512-N8tkAACJx2ww8vFMneJmaAgmjAG1tnVBZJRLRcx061tmsLRZHSEZSLuGWnwPtunsSLvSqXQ2wfp7Mgqg1I+2dQ==}
|
||||
|
||||
'@zone-eu/mailsplit@5.4.8':
|
||||
resolution: {integrity: sha512-eEyACj4JZ7sjzRvy26QhLgKEMWwQbsw1+QZnlLX+/gihcNH07lVPOcnwf5U6UAL7gkc//J3jVd76o/WS+taUiA==}
|
||||
|
||||
@@ -2470,16 +2442,9 @@ packages:
|
||||
resolution: {integrity: sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==}
|
||||
engines: {node: '>= 8'}
|
||||
|
||||
css-in-js-utils@3.1.0:
|
||||
resolution: {integrity: sha512-fJAcud6B3rRu+KHYk+Bwf+WFL2MDCJJ1XG9x137tJQ0xYxor7XziQtuGFbWNdqrvF4Tk26O3H73nfVqXt/fW1A==}
|
||||
|
||||
css-select@5.2.2:
|
||||
resolution: {integrity: sha512-TizTzUddG/xYLA3NXodFM0fSbNizXjOKhqiQQwvhlspadZokn1KDy0NZFS0wuEubIYAV5/c1/lAr0TaaFXEXzw==}
|
||||
|
||||
css-tree@1.1.3:
|
||||
resolution: {integrity: sha512-tRpdppF7TRazZrjJ6v3stzv93qxRcSsFmW6cX0Zm2NVKpxE1WV1HblnghVv9TreireHkqI/VDEsfolRF1p6y7Q==}
|
||||
engines: {node: '>=8.0.0'}
|
||||
|
||||
css-what@6.2.2:
|
||||
resolution: {integrity: sha512-u/O3vwbptzhMs3L1fQE82ZSLHQQfto5gyZzwteVIEyeaY5Fc7R4dapF/BvRoSYFeqfBk4m0V1Vafq5Pjv25wvA==}
|
||||
engines: {node: '>= 6'}
|
||||
@@ -2735,9 +2700,6 @@ packages:
|
||||
error-ex@1.3.4:
|
||||
resolution: {integrity: sha512-sqQamAnR14VgCr1A618A3sGrygcpK+HEbenA/HiEAkkUwcZIIB/tgWqHFxWgOyDh4nB4JCRimh79dR5Ywc9MDQ==}
|
||||
|
||||
error-stack-parser@2.1.4:
|
||||
resolution: {integrity: sha512-Sk5V6wVazPhq5MhpO+AUxJn5x7XSXGl1R93Vn7i+zS15KDVxQijejNCrz8340/2bgLBjR9GtEG8ZVKONDjcqGQ==}
|
||||
|
||||
es-abstract@1.24.1:
|
||||
resolution: {integrity: sha512-zHXBLhP+QehSSbsS9Pt23Gg964240DPd6QCf8WpkqEXxQ7fhdZzYsocOr5u7apWonsS5EjZDmTF+/slGMyasvw==}
|
||||
engines: {node: '>= 0.4'}
|
||||
@@ -3028,12 +2990,6 @@ packages:
|
||||
fast-sha256@1.3.0:
|
||||
resolution: {integrity: sha512-n11RGP/lrWEFI/bWdygLxhI+pVeo1ZYIVwvvPkW7azl/rOy+F3HYRZ2K5zeE9mmkhQppyv9sQFx0JM9UabnpPQ==}
|
||||
|
||||
fast-shallow-equal@1.0.0:
|
||||
resolution: {integrity: sha512-HPtaa38cPgWvaCFmRNhlc6NG7pv6NUHqjPgVAkWGoB9mQMwYB27/K0CvOM5Czy+qpT3e8XJ6Q4aPAnzpNpzNaw==}
|
||||
|
||||
fastest-stable-stringify@2.0.2:
|
||||
resolution: {integrity: sha512-bijHueCGd0LqqNK9b5oCMHc0MluJAx0cwqASgbWMvkO01lCYgIhacVRLcaDz3QnyYIRNJRDwMb41VuT6pHJ91Q==}
|
||||
|
||||
fastq@1.20.1:
|
||||
resolution: {integrity: sha512-GGToxJ/w1x32s/D2EKND7kTil4n8OVk/9mycTc4VDza13lOvpUZTGX3mFSCtV9ksdGBVzvsyAVLM6mHFThxXxw==}
|
||||
|
||||
@@ -3277,9 +3233,6 @@ packages:
|
||||
engines: {node: '>=18'}
|
||||
hasBin: true
|
||||
|
||||
hyphenate-style-name@1.1.0:
|
||||
resolution: {integrity: sha512-WDC/ui2VVRrz3jOVi+XtjqkDjiVjTtFaAGiW37k6b+ohyQ5wYDOGkvCZa8+H0nx3gyvv0+BST9xuOgIyGQ00gw==}
|
||||
|
||||
iconv-lite@0.6.3:
|
||||
resolution: {integrity: sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==}
|
||||
engines: {node: '>=0.10.0'}
|
||||
@@ -3321,9 +3274,6 @@ packages:
|
||||
inline-style-parser@0.2.7:
|
||||
resolution: {integrity: sha512-Nb2ctOyNR8DqQoR0OwRG95uNWIC0C1lCgf5Naz5H6Ji72KZ8OcFZLz2P5sNgwlyoJ8Yif11oMuYs5pBQa86csA==}
|
||||
|
||||
inline-style-prefixer@7.0.1:
|
||||
resolution: {integrity: sha512-lhYo5qNTQp3EvSSp3sRvXMbVQTLrvGV6DycRMJ5dm2BLMiJ30wpXKdDdgX+GmJZ5uQMucwRKHamXSst3Sj/Giw==}
|
||||
|
||||
internal-slot@1.1.0:
|
||||
resolution: {integrity: sha512-4gd7VpWNQNB4UKKCFFVcp1AVv+FMOgs9NKzjHKusc8jTMhd5eL1NqQqOpE0KzMds804/yHlglp3uxgluOqAPLw==}
|
||||
engines: {node: '>= 0.4'}
|
||||
@@ -3489,9 +3439,6 @@ packages:
|
||||
jose@6.1.3:
|
||||
resolution: {integrity: sha512-0TpaTfihd4QMNwrz/ob2Bp7X04yuxJkjRGi4aKmOqwhov54i6u79oCv7T+C7lo70MKH6BesI3vscD1yb/yzKXQ==}
|
||||
|
||||
js-cookie@2.2.1:
|
||||
resolution: {integrity: sha512-HvdH2LzI/EAZcUwA8+0nKNtWHqS+ZmijLA30RwZA0bo7ToCckjK5MkGhjED9KoRcXO6BaGI3I9UIzSA1FKFPOQ==}
|
||||
|
||||
js-tokens@4.0.0:
|
||||
resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==}
|
||||
|
||||
@@ -3757,9 +3704,6 @@ packages:
|
||||
mdast-util-to-string@4.0.0:
|
||||
resolution: {integrity: sha512-0H44vDimn51F0YwvxSJSm0eCDOJTRlmN0R1yBh4HLj9wiV1Dn0QoXGbvFAWj2hSItVTlCmBF1hqKlIyUBVFLPg==}
|
||||
|
||||
mdn-data@2.0.14:
|
||||
resolution: {integrity: sha512-dn6wd0uw5GsdswPFfsgMp5NSB0/aDe6fK94YJV/AJDYXL6HVLWBsxeq7js7Ad+mU2K9LAlwpk6kN2D5mwCPVow==}
|
||||
|
||||
merge2@1.4.1:
|
||||
resolution: {integrity: sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==}
|
||||
engines: {node: '>= 8'}
|
||||
@@ -3901,12 +3845,6 @@ packages:
|
||||
ms@2.1.3:
|
||||
resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==}
|
||||
|
||||
nano-css@5.6.2:
|
||||
resolution: {integrity: sha512-+6bHaC8dSDGALM1HJjOHVXpuastdu2xFoZlC77Jh4cg+33Zcgm+Gxd+1xsnpZK14eyHObSp82+ll5y3SX75liw==}
|
||||
peerDependencies:
|
||||
react: '*'
|
||||
react-dom: '*'
|
||||
|
||||
nano-spawn@2.0.0:
|
||||
resolution: {integrity: sha512-tacvGzUY5o2D8CBh2rrwxyNojUsZNU2zjNTzKQrkgGJQTbGAfArVWXSKMBokBeeg6C7OLRGUEyoFlYbfeWQIqw==}
|
||||
engines: {node: '>=20.17'}
|
||||
@@ -4357,30 +4295,12 @@ packages:
|
||||
peerDependencies:
|
||||
react: ^16.0.0 || ^17.0.0 || ^18.0.0 || ^19.0.0
|
||||
|
||||
react-to-text@2.0.1:
|
||||
resolution: {integrity: sha512-4lGuxY+/PqASinxrv7/ydE8wGdnZCGsTNIFiO6DKykzy703jI5pc0/fyyB6BNrBWmtYJ7H9dGY80PJ3V1/8UIg==}
|
||||
peerDependencies:
|
||||
react: ^18.2.0
|
||||
react-dom: ^18.2.0
|
||||
|
||||
react-tweet@3.3.0:
|
||||
resolution: {integrity: sha512-gSIG2169ZK7UH6rBzuU+j1xnQbH3IlOTLEkuGrRiJJTMgETik+h+26yHyyVKrLkzwrOaYPk4K3OtEKycqKgNLw==}
|
||||
peerDependencies:
|
||||
react: ^18.0.0 || ^19.0.0
|
||||
react-dom: ^18.0.0 || ^19.0.0
|
||||
|
||||
react-universal-interface@0.6.2:
|
||||
resolution: {integrity: sha512-dg8yXdcQmvgR13RIlZbTRQOoUrDciFVoSBZILwjE2LFISxZZ8loVJKAkuzswl5js8BHda79bIb2b84ehU8IjXw==}
|
||||
peerDependencies:
|
||||
react: '*'
|
||||
tslib: '*'
|
||||
|
||||
react-use@17.6.0:
|
||||
resolution: {integrity: sha512-OmedEScUMKFfzn1Ir8dBxiLLSOzhKe/dPZwVxcujweSj45aNM7BEGPb9BEVIgVEqEXx6f3/TsXzwIktNgUR02g==}
|
||||
peerDependencies:
|
||||
react: '*'
|
||||
react-dom: '*'
|
||||
|
||||
react@19.2.4:
|
||||
resolution: {integrity: sha512-9nfp2hYpCwOjAN+8TZFGhtWEwgvWHXqESH8qT89AT/lWklpLON22Lc8pEtnpsZz7VmawabSU0gCjnj8aC0euHQ==}
|
||||
engines: {node: '>=0.10.0'}
|
||||
@@ -4488,9 +4408,6 @@ packages:
|
||||
'@react-email/render':
|
||||
optional: true
|
||||
|
||||
resize-observer-polyfill@1.5.1:
|
||||
resolution: {integrity: sha512-LwZrotdHOo12nQuZlHEmtuXdqGoOD0OhaxopaNFxWzInpEgaLWoVuAMbTzixuosCx2nEG58ngzW3vxdWoxIgdg==}
|
||||
|
||||
resolve-from@4.0.0:
|
||||
resolution: {integrity: sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==}
|
||||
engines: {node: '>=4'}
|
||||
@@ -4537,9 +4454,6 @@ packages:
|
||||
rou3@0.7.12:
|
||||
resolution: {integrity: sha512-iFE4hLDuloSWcD7mjdCDhx2bKcIsYbtOTpfH5MHHLSKMOUyjqQXTeZVa289uuwEGEKFoE/BAPbhaU4B774nceg==}
|
||||
|
||||
rtl-css-js@1.16.1:
|
||||
resolution: {integrity: sha512-lRQgou1mu19e+Ya0LsTvKrVJ5TYUbqCVPAiImX3UfLTenarvPUl1QFdvu5Z3PYmHT9RCcwIfbjRQBntExyj3Zg==}
|
||||
|
||||
run-parallel@1.2.0:
|
||||
resolution: {integrity: sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==}
|
||||
|
||||
@@ -4575,10 +4489,6 @@ packages:
|
||||
schema-dts@1.1.5:
|
||||
resolution: {integrity: sha512-RJr9EaCmsLzBX2NDiO5Z3ux2BVosNZN5jo0gWgsyKvxKIUL5R3swNvoorulAeL9kLB0iTSX7V6aokhla2m7xbg==}
|
||||
|
||||
screenfull@5.2.0:
|
||||
resolution: {integrity: sha512-9BakfsO2aUQN2K9Fdbj87RJIEZ82Q9IGim7FqM5OsebfoFC6ZHXgDq/KvniuLTPdeM8wY2o6Dj3WQ7KeQCj3cA==}
|
||||
engines: {node: '>=0.10.0'}
|
||||
|
||||
selderee@0.11.0:
|
||||
resolution: {integrity: sha512-5TF+l7p4+OsnP8BCCvSyZiSPc4x4//p5uPwK8TCnVPJYRmU2aYKMpOXvw8zM5a5JvuuCGN1jmsMwuU2W02ukfA==}
|
||||
|
||||
@@ -4605,10 +4515,6 @@ packages:
|
||||
resolution: {integrity: sha512-7PGFlmtwsEADb0WYyvCMa1t+yke6daIG4Wirafur5kcf+MhUnPms1UeR0CKQdTZD81yESwMHbtn+TR+dMviakQ==}
|
||||
engines: {node: '>= 0.4'}
|
||||
|
||||
set-harmonic-interval@1.0.1:
|
||||
resolution: {integrity: sha512-AhICkFV84tBP1aWqPwLZqFvAwqEoVA9kxNMniGEUvzOlm4vLmOFLiTT3UZ6bziJTy4bOVpzWGTfSCbmaayGx8g==}
|
||||
engines: {node: '>=6.9'}
|
||||
|
||||
set-proto@1.0.0:
|
||||
resolution: {integrity: sha512-RJRdvCo6IAnPdsvP/7m6bsQqNnn1FCBX5ZNtFL98MmFF/4xAIJTIg1YbHW5DC2W5SKZanrC6i4HsJqlajw/dZw==}
|
||||
engines: {node: '>= 0.4'}
|
||||
@@ -4665,10 +4571,6 @@ packages:
|
||||
source-map-support@0.5.21:
|
||||
resolution: {integrity: sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==}
|
||||
|
||||
source-map@0.5.6:
|
||||
resolution: {integrity: sha512-MjZkVp0NHr5+TPihLcadqnlVoGIoWo4IBHptutGh9wI3ttUYvCG26HkSuDi+K6lsZ25syXJXcctwgyVCt//xqA==}
|
||||
engines: {node: '>=0.10.0'}
|
||||
|
||||
source-map@0.6.1:
|
||||
resolution: {integrity: sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==}
|
||||
engines: {node: '>=0.10.0'}
|
||||
@@ -4699,18 +4601,6 @@ packages:
|
||||
stable-hash@0.0.5:
|
||||
resolution: {integrity: sha512-+L3ccpzibovGXFK+Ap/f8LOS0ahMrHTf3xu7mMLSpEGU0EO9ucaysSylKo9eRDFNhWve/y275iPmIZ4z39a9iA==}
|
||||
|
||||
stack-generator@2.0.10:
|
||||
resolution: {integrity: sha512-mwnua/hkqM6pF4k8SnmZ2zfETsRUpWXREfA/goT8SLCV4iOFa4bzOX2nDipWAZFPTjLvQB82f5yaodMVhK0yJQ==}
|
||||
|
||||
stackframe@1.3.4:
|
||||
resolution: {integrity: sha512-oeVtt7eWQS+Na6F//S4kJ2K2VbRlS9D43mAlMyVpVWovy9o+jfgH8O9agzANzaiLjclA0oYzUXEM4PurhSUChw==}
|
||||
|
||||
stacktrace-gps@3.1.2:
|
||||
resolution: {integrity: sha512-GcUgbO4Jsqqg6RxfyTHFiPxdPqF+3LFmQhm7MgCuYQOYuWyqxo5pwRPz5d/u6/WYJdEnWfK4r+jGbyD8TSggXQ==}
|
||||
|
||||
stacktrace-js@2.0.2:
|
||||
resolution: {integrity: sha512-Je5vBeY4S1r/RnLydLl0TBTi3F2qdfWmYsGvtfZgEI+SCprPppaIhQf5nGcal4gI4cGpCV/duLcAzT1np6sQqg==}
|
||||
|
||||
standardwebhooks@1.0.0:
|
||||
resolution: {integrity: sha512-BbHGOQK9olHPMvQNHWul6MYlrRTAOKn03rOe4A8O3CLWhNf4YHBqq2HJKKC+sfqpxiBY52pNeesD6jIiLDz8jg==}
|
||||
|
||||
@@ -4806,9 +4696,6 @@ packages:
|
||||
babel-plugin-macros:
|
||||
optional: true
|
||||
|
||||
stylis@4.3.6:
|
||||
resolution: {integrity: sha512-yQ3rwFWRfwNUY7H5vpU0wfdkNSnvnJinhF9830Swlaxl03zsOjCfmX0ugac+3LtK0lYSgwL/KXc8oYL3mG4YFQ==}
|
||||
|
||||
supports-color@7.2.0:
|
||||
resolution: {integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==}
|
||||
engines: {node: '>=8'}
|
||||
@@ -4846,10 +4733,6 @@ packages:
|
||||
resolution: {integrity: sha512-g9ljZiwki/LfxmQADO3dEY1CbpmXT5Hm2fJ+QaGKwSXUylMybePR7/67YW7jOrrvjEgL1Fmz5kzyAjWVWLlucg==}
|
||||
engines: {node: '>=6'}
|
||||
|
||||
throttle-debounce@3.0.1:
|
||||
resolution: {integrity: sha512-dTEWWNu6JmeVXY0ZYoPuH5cRIwc0MeGbJwah9KUNYSJwommQpCzTySTpEe8Gs1J23aeWEuAobe4Ag7EHVt/LOg==}
|
||||
engines: {node: '>=10'}
|
||||
|
||||
tinyglobby@0.2.15:
|
||||
resolution: {integrity: sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==}
|
||||
engines: {node: '>=12.0.0'}
|
||||
@@ -4880,9 +4763,6 @@ packages:
|
||||
peerDependencies:
|
||||
typescript: '>=4.8.4'
|
||||
|
||||
ts-easing@0.2.0:
|
||||
resolution: {integrity: sha512-Z86EW+fFFh/IFB1fqQ3/+7Zpf9t2ebOAxNI/V6Wo7r5gqiqtxmgTlQ1qbqQcjLKYeSHPTsEmvlJUDg/EuL0uHQ==}
|
||||
|
||||
tsconfig-paths@3.15.0:
|
||||
resolution: {integrity: sha512-2Ac2RgzDe/cn48GvOe3M+o82pEFewD3UPbyoUHHdKasHwJKjds4fLXWf/Ux5kATBKN20oaFGu+jbElp1pos0mg==}
|
||||
|
||||
@@ -5009,6 +4889,12 @@ packages:
|
||||
'@types/react':
|
||||
optional: true
|
||||
|
||||
use-debounce@10.1.0:
|
||||
resolution: {integrity: sha512-lu87Za35V3n/MyMoEpD5zJv0k7hCn0p+V/fK2kWD+3k2u3kOCwO593UArbczg1fhfs2rqPEnHpULJ3KmGdDzvg==}
|
||||
engines: {node: '>= 16.0.0'}
|
||||
peerDependencies:
|
||||
react: '*'
|
||||
|
||||
use-sidecar@1.1.3:
|
||||
resolution: {integrity: sha512-Fedw0aZvkhynoPYlA5WXrMCAMm+nSWdZt6lzJQ7Ok8S6Q+VsHmHpRWndVRJ8Be0ZbkfPc5LRYH+5XrzXcEeLRQ==}
|
||||
engines: {node: '>=10'}
|
||||
@@ -5288,8 +5174,6 @@ snapshots:
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
||||
'@babel/runtime@7.28.6': {}
|
||||
|
||||
'@babel/template@7.28.6':
|
||||
dependencies:
|
||||
'@babel/code-frame': 7.28.6
|
||||
@@ -5334,10 +5218,6 @@ snapshots:
|
||||
|
||||
'@better-fetch/fetch@1.1.21': {}
|
||||
|
||||
'@date-fns/tz@1.4.1': {}
|
||||
|
||||
'@date-fns/utc@2.1.1': {}
|
||||
|
||||
'@drizzle-team/brocli@0.10.2': {}
|
||||
|
||||
'@emnapi/core@1.8.1':
|
||||
@@ -6500,7 +6380,7 @@ snapshots:
|
||||
|
||||
'@types/concat-stream@2.0.3':
|
||||
dependencies:
|
||||
'@types/node': 25.0.10
|
||||
'@types/node': 25.1.0
|
||||
|
||||
'@types/debug@4.1.12':
|
||||
dependencies:
|
||||
@@ -6518,8 +6398,6 @@ snapshots:
|
||||
|
||||
'@types/is-empty@1.2.3': {}
|
||||
|
||||
'@types/js-cookie@2.2.7': {}
|
||||
|
||||
'@types/json-schema@7.0.15': {}
|
||||
|
||||
'@types/json5@0.0.29': {}
|
||||
@@ -6540,13 +6418,13 @@ snapshots:
|
||||
dependencies:
|
||||
undici-types: 6.21.0
|
||||
|
||||
'@types/node@25.0.10':
|
||||
'@types/node@25.1.0':
|
||||
dependencies:
|
||||
undici-types: 7.16.0
|
||||
|
||||
'@types/pg@8.16.0':
|
||||
dependencies:
|
||||
'@types/node': 25.0.10
|
||||
'@types/node': 25.1.0
|
||||
pg-protocol: 1.11.0
|
||||
pg-types: 2.2.0
|
||||
|
||||
@@ -6732,8 +6610,6 @@ snapshots:
|
||||
next: 16.1.6(@babel/core@7.28.6)(babel-plugin-react-compiler@19.1.0-rc.3)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)
|
||||
react: 19.2.4
|
||||
|
||||
'@xobotyi/scrollbar-width@1.9.5': {}
|
||||
|
||||
'@zone-eu/mailsplit@5.4.8':
|
||||
dependencies:
|
||||
libbase64: 1.3.0
|
||||
@@ -7051,10 +6927,6 @@ snapshots:
|
||||
shebang-command: 2.0.0
|
||||
which: 2.0.2
|
||||
|
||||
css-in-js-utils@3.1.0:
|
||||
dependencies:
|
||||
hyphenate-style-name: 1.1.0
|
||||
|
||||
css-select@5.2.2:
|
||||
dependencies:
|
||||
boolbase: 1.0.0
|
||||
@@ -7063,11 +6935,6 @@ snapshots:
|
||||
domutils: 3.2.2
|
||||
nth-check: 2.1.1
|
||||
|
||||
css-tree@1.1.3:
|
||||
dependencies:
|
||||
mdn-data: 2.0.14
|
||||
source-map: 0.6.1
|
||||
|
||||
css-what@6.2.2: {}
|
||||
|
||||
cssesc@3.0.0: {}
|
||||
@@ -7219,10 +7086,6 @@ snapshots:
|
||||
dependencies:
|
||||
is-arrayish: 0.2.1
|
||||
|
||||
error-stack-parser@2.1.4:
|
||||
dependencies:
|
||||
stackframe: 1.3.4
|
||||
|
||||
es-abstract@1.24.1:
|
||||
dependencies:
|
||||
array-buffer-byte-length: 1.0.2
|
||||
@@ -7750,10 +7613,6 @@ snapshots:
|
||||
|
||||
fast-sha256@1.3.0: {}
|
||||
|
||||
fast-shallow-equal@1.0.0: {}
|
||||
|
||||
fastest-stable-stringify@2.0.2: {}
|
||||
|
||||
fastq@1.20.1:
|
||||
dependencies:
|
||||
reusify: 1.1.0
|
||||
@@ -8060,8 +7919,6 @@ snapshots:
|
||||
|
||||
husky@9.1.7: {}
|
||||
|
||||
hyphenate-style-name@1.1.0: {}
|
||||
|
||||
iconv-lite@0.6.3:
|
||||
dependencies:
|
||||
safer-buffer: 2.1.2
|
||||
@@ -8091,10 +7948,6 @@ snapshots:
|
||||
|
||||
inline-style-parser@0.2.7: {}
|
||||
|
||||
inline-style-prefixer@7.0.1:
|
||||
dependencies:
|
||||
css-in-js-utils: 3.1.0
|
||||
|
||||
internal-slot@1.1.0:
|
||||
dependencies:
|
||||
es-errors: 1.3.0
|
||||
@@ -8263,8 +8116,6 @@ snapshots:
|
||||
|
||||
jose@6.1.3: {}
|
||||
|
||||
js-cookie@2.2.1: {}
|
||||
|
||||
js-tokens@4.0.0: {}
|
||||
|
||||
js-yaml@4.1.1:
|
||||
@@ -8636,8 +8487,6 @@ snapshots:
|
||||
dependencies:
|
||||
'@types/mdast': 4.0.4
|
||||
|
||||
mdn-data@2.0.14: {}
|
||||
|
||||
merge2@1.4.1: {}
|
||||
|
||||
micromark-core-commonmark@2.0.3:
|
||||
@@ -8934,19 +8783,6 @@ snapshots:
|
||||
|
||||
ms@2.1.3: {}
|
||||
|
||||
nano-css@5.6.2(react-dom@19.2.4(react@19.2.4))(react@19.2.4):
|
||||
dependencies:
|
||||
'@jridgewell/sourcemap-codec': 1.5.5
|
||||
css-tree: 1.1.3
|
||||
csstype: 3.2.3
|
||||
fastest-stable-stringify: 2.0.2
|
||||
inline-style-prefixer: 7.0.1
|
||||
react: 19.2.4
|
||||
react-dom: 19.2.4(react@19.2.4)
|
||||
rtl-css-js: 1.16.1
|
||||
stacktrace-js: 2.0.2
|
||||
stylis: 4.3.6
|
||||
|
||||
nano-spawn@2.0.0: {}
|
||||
|
||||
nanoid@3.3.11: {}
|
||||
@@ -9359,11 +9195,6 @@ snapshots:
|
||||
dependencies:
|
||||
react: 19.2.4
|
||||
|
||||
react-to-text@2.0.1(react-dom@19.2.4(react@19.2.4))(react@19.2.4):
|
||||
dependencies:
|
||||
react: 19.2.4
|
||||
react-dom: 19.2.4(react@19.2.4)
|
||||
|
||||
react-tweet@3.3.0(react-dom@19.2.4(react@19.2.4))(react@19.2.4):
|
||||
dependencies:
|
||||
'@swc/helpers': 0.5.18
|
||||
@@ -9372,30 +9203,6 @@ snapshots:
|
||||
react-dom: 19.2.4(react@19.2.4)
|
||||
swr: 2.3.8(react@19.2.4)
|
||||
|
||||
react-universal-interface@0.6.2(react@19.2.4)(tslib@2.8.1):
|
||||
dependencies:
|
||||
react: 19.2.4
|
||||
tslib: 2.8.1
|
||||
|
||||
react-use@17.6.0(react-dom@19.2.4(react@19.2.4))(react@19.2.4):
|
||||
dependencies:
|
||||
'@types/js-cookie': 2.2.7
|
||||
'@xobotyi/scrollbar-width': 1.9.5
|
||||
copy-to-clipboard: 3.3.3
|
||||
fast-deep-equal: 3.1.3
|
||||
fast-shallow-equal: 1.0.0
|
||||
js-cookie: 2.2.1
|
||||
nano-css: 5.6.2(react-dom@19.2.4(react@19.2.4))(react@19.2.4)
|
||||
react: 19.2.4
|
||||
react-dom: 19.2.4(react@19.2.4)
|
||||
react-universal-interface: 0.6.2(react@19.2.4)(tslib@2.8.1)
|
||||
resize-observer-polyfill: 1.5.1
|
||||
screenfull: 5.2.0
|
||||
set-harmonic-interval: 1.0.1
|
||||
throttle-debounce: 3.0.1
|
||||
ts-easing: 0.2.0
|
||||
tslib: 2.8.1
|
||||
|
||||
react@19.2.4: {}
|
||||
|
||||
read-package-json-fast@3.0.2:
|
||||
@@ -9608,8 +9415,6 @@ snapshots:
|
||||
mailparser: 3.9.1
|
||||
svix: 1.84.1
|
||||
|
||||
resize-observer-polyfill@1.5.1: {}
|
||||
|
||||
resolve-from@4.0.0: {}
|
||||
|
||||
resolve-pkg-maps@1.0.0: {}
|
||||
@@ -9664,10 +9469,6 @@ snapshots:
|
||||
|
||||
rou3@0.7.12: {}
|
||||
|
||||
rtl-css-js@1.16.1:
|
||||
dependencies:
|
||||
'@babel/runtime': 7.28.6
|
||||
|
||||
run-parallel@1.2.0:
|
||||
dependencies:
|
||||
queue-microtask: 1.2.3
|
||||
@@ -9705,8 +9506,6 @@ snapshots:
|
||||
|
||||
schema-dts@1.1.5: {}
|
||||
|
||||
screenfull@5.2.0: {}
|
||||
|
||||
selderee@0.11.0:
|
||||
dependencies:
|
||||
parseley: 0.12.1
|
||||
@@ -9735,8 +9534,6 @@ snapshots:
|
||||
functions-have-names: 1.2.3
|
||||
has-property-descriptors: 1.0.2
|
||||
|
||||
set-harmonic-interval@1.0.1: {}
|
||||
|
||||
set-proto@1.0.0:
|
||||
dependencies:
|
||||
dunder-proto: 1.0.1
|
||||
@@ -9839,8 +9636,6 @@ snapshots:
|
||||
buffer-from: 1.1.2
|
||||
source-map: 0.6.1
|
||||
|
||||
source-map@0.5.6: {}
|
||||
|
||||
source-map@0.6.1: {}
|
||||
|
||||
source-map@0.7.6: {}
|
||||
@@ -9865,23 +9660,6 @@ snapshots:
|
||||
|
||||
stable-hash@0.0.5: {}
|
||||
|
||||
stack-generator@2.0.10:
|
||||
dependencies:
|
||||
stackframe: 1.3.4
|
||||
|
||||
stackframe@1.3.4: {}
|
||||
|
||||
stacktrace-gps@3.1.2:
|
||||
dependencies:
|
||||
source-map: 0.5.6
|
||||
stackframe: 1.3.4
|
||||
|
||||
stacktrace-js@2.0.2:
|
||||
dependencies:
|
||||
error-stack-parser: 2.1.4
|
||||
stack-generator: 2.0.10
|
||||
stacktrace-gps: 3.1.2
|
||||
|
||||
standardwebhooks@1.0.0:
|
||||
dependencies:
|
||||
'@stablelib/base64': 1.0.1
|
||||
@@ -10009,8 +9787,6 @@ snapshots:
|
||||
optionalDependencies:
|
||||
'@babel/core': 7.28.6
|
||||
|
||||
stylis@4.3.6: {}
|
||||
|
||||
supports-color@7.2.0:
|
||||
dependencies:
|
||||
has-flag: 4.0.0
|
||||
@@ -10042,8 +9818,6 @@ snapshots:
|
||||
|
||||
tapable@2.3.0: {}
|
||||
|
||||
throttle-debounce@3.0.1: {}
|
||||
|
||||
tinyglobby@0.2.15:
|
||||
dependencies:
|
||||
fdir: 6.5.0(picomatch@4.0.3)
|
||||
@@ -10067,8 +9841,6 @@ snapshots:
|
||||
dependencies:
|
||||
typescript: 5.9.3
|
||||
|
||||
ts-easing@0.2.0: {}
|
||||
|
||||
tsconfig-paths@3.15.0:
|
||||
dependencies:
|
||||
'@types/json5': 0.0.29
|
||||
@@ -10279,6 +10051,10 @@ snapshots:
|
||||
optionalDependencies:
|
||||
'@types/react': 19.2.10
|
||||
|
||||
use-debounce@10.1.0(react@19.2.4):
|
||||
dependencies:
|
||||
react: 19.2.4
|
||||
|
||||
use-sidecar@1.1.3(@types/react@19.2.10)(react@19.2.4):
|
||||
dependencies:
|
||||
detect-node-es: 1.1.0
|
||||
|
||||
@@ -1,9 +0,0 @@
|
||||
onlyBuiltDependencies:
|
||||
- esbuild
|
||||
- sharp
|
||||
- unrs-resolver
|
||||
|
||||
peerDependencyRules:
|
||||
allowAny:
|
||||
- react
|
||||
- react-dom
|
||||
Reference in New Issue
Block a user