diff --git a/next.config.mjs b/next.config.mjs
index cd0f948..70b3b31 100644
--- a/next.config.mjs
+++ b/next.config.mjs
@@ -7,6 +7,11 @@ const nextConfig = {
hostname: "pbs.twimg.com",
pathname: "**",
},
+ {
+ protocol: "https",
+ hostname: "images.ctfassets.net",
+ pathname: "**",
+ }
],
},
};
diff --git a/package.json b/package.json
index e041d31..1d767a9 100644
--- a/package.json
+++ b/package.json
@@ -13,20 +13,31 @@
},
"dependencies": {
"@amplitude/analytics-browser": "^2.4.1",
+ "@contentful/rich-text-plain-text-renderer": "^16.2.8",
+ "@contentful/rich-text-react-renderer": "^15.22.9",
+ "@contentful/rich-text-types": "^16.8.3",
"@headlessui/react": "^1.7.18",
"@next/third-parties": "^14.2.5",
"@typeform/embed-react": "^3.17.0",
+ "contentful": "^10.13.1",
"embla-carousel-auto-scroll": "^8.1.5",
"embla-carousel-autoplay": "^8.1.5",
"embla-carousel-react": "^8.1.5",
+ "lodash.words": "^4.2.0",
+ "luxon": "^3.5.0",
"next": "14.1.0",
"numeral": "^2.0.6",
"react": "^18",
"react-dom": "^18",
+ "sharp": "^0.33.5",
"tailwind-merge": "^2.2.1",
+ "tailwind-scrollbar-hide": "^1.1.7",
+ "use-debounce": "^10.0.3",
"zod": "^3.22.4"
},
"devDependencies": {
+ "@types/lodash.words": "^4.2.9",
+ "@types/luxon": "^3.4.2",
"@types/node": "^20",
"@types/numeral": "^2.0.5",
"@types/react": "^18",
diff --git a/src/app/(routes)/blog/[slug]/_components/article-content.tsx b/src/app/(routes)/blog/[slug]/_components/article-content.tsx
new file mode 100644
index 0000000..7eb1935
--- /dev/null
+++ b/src/app/(routes)/blog/[slug]/_components/article-content.tsx
@@ -0,0 +1,147 @@
+import {
+ documentToReactComponents,
+ RenderMark,
+ RenderNode,
+} from "@contentful/rich-text-react-renderer";
+import { BLOCKS, Document, INLINES, MARKS } from "@contentful/rich-text-types";
+import Link from "next/link";
+import { isExternal } from "util/types";
+import Divider from "./divider";
+import { IframeContainer } from "./iframe-container";
+import { Text } from "@/app/_components/text";
+import { Asset } from "contentful";
+import ContentfulImage from "./contentful-image";
+
+// Map text-format types to custom components
+
+const markRenderers: RenderMark = {
+ [MARKS.BOLD]: (text) => {text},
+ [MARKS.ITALIC]: (text) => {text},
+ [MARKS.UNDERLINE]: (text) => {text},
+ [MARKS.CODE]: (text) => {text}
,
+ [MARKS.SUPERSCRIPT]: (text) => {text},
+ [MARKS.SUBSCRIPT]: (text) => {text},
+};
+
+const nodeRenderers: RenderNode = {
+ [INLINES.HYPERLINK]: (node, children) => {
+ const href = node.data.uri as string;
+ if (
+ href.includes("youtube.com/embed") ||
+ href.includes("player.vimeo.com") ||
+ children?.toString().toLowerCase().includes("iframe") // to handle uncommon cases, creator can set the text to "iframe"
+ ) {
+ return (
+
+
+
+ );
+ }
+ return (
+
+ {children}
+
+ );
+ },
+ [BLOCKS.DOCUMENT]: (_, children) => children,
+ [BLOCKS.PARAGRAPH]: (_, children) => (
+
+ {children}
+
+ ),
+ [BLOCKS.HEADING_1]: (_, children) => (
+
+ {children}
+
+ ),
+ [BLOCKS.HEADING_2]: (_, children) => (
+
+ {children}
+
+ ),
+ [BLOCKS.HEADING_3]: (_, children) => (
+
+ {children}
+
+ ),
+ [BLOCKS.HEADING_4]: (_, children) => (
+
+ {children}
+
+ ),
+ [BLOCKS.HEADING_5]: (_, children) => (
+
+ {children}
+
+ ),
+ [BLOCKS.HEADING_6]: (_, children) => (
+
+ {children}
+
+ ),
+ [BLOCKS.EMBEDDED_RESOURCE]: (_, children) =>
{children}
,
+ [BLOCKS.UL_LIST]: (_, children) => ,
+ [BLOCKS.OL_LIST]: (_, children) => {children}
,
+ [BLOCKS.LIST_ITEM]: (_, children) => {children},
+ [BLOCKS.QUOTE]: (_, children) => {children}
,
+ [BLOCKS.HR]: () => ,
+ [BLOCKS.TABLE]: (_, children) => (
+
+ ),
+ [BLOCKS.TABLE_ROW]: (_, children) => {children}
,
+ [BLOCKS.TABLE_HEADER_CELL]: (_, children) => {children} | ,
+ [BLOCKS.TABLE_CELL]: (_, children) => {children} | ,
+ [BLOCKS.EMBEDDED_ASSET]: (node) => {
+ const data = node.data.target as Asset<"WITHOUT_UNRESOLVABLE_LINKS", string>;
+ const { file, description, title } = data.fields;
+ const mimeGroup = file?.contentType.split("/")[0]; // image / video etc
+ switch (mimeGroup) {
+ case "image":
+ return ;
+ // TODO: test this, make custom component if necessary
+ case "video":
+ return (
+
+ );
+ // TODO: add other asset types, handle them
+ default:
+ return unknown file type
;
+ }
+ },
+};
+
+const options = {
+ renderNode: nodeRenderers,
+ renderMark: markRenderers,
+ preserveWhitespace: true,
+};
+
+export default function ArticleContent({ content }: { content: Document }) {
+ return (
+
+ {documentToReactComponents(content, options)}
+
+ );
+}
diff --git a/src/app/(routes)/blog/[slug]/_components/breadcrumb.tsx b/src/app/(routes)/blog/[slug]/_components/breadcrumb.tsx
new file mode 100644
index 0000000..8e80d8e
--- /dev/null
+++ b/src/app/(routes)/blog/[slug]/_components/breadcrumb.tsx
@@ -0,0 +1,16 @@
+import Link from "next/link";
+import { ChevronDownIcon } from "@/app/_components/icons";
+
+export default function Breadcrumb({ fullTitle }: { fullTitle: string }) {
+ // Max title to 40 characters
+ const title = fullTitle.length > 40 ? fullTitle.slice(0, 40) + "..." : fullTitle;
+ return (
+
+ );
+}
diff --git a/src/app/(routes)/blog/[slug]/_components/contentful-image.tsx b/src/app/(routes)/blog/[slug]/_components/contentful-image.tsx
new file mode 100644
index 0000000..9a4eef2
--- /dev/null
+++ b/src/app/(routes)/blog/[slug]/_components/contentful-image.tsx
@@ -0,0 +1,49 @@
+import { Asset } from "contentful";
+import Image from "next/image";
+import { object } from "zod";
+
+export default function ContentfulImage({
+ image,
+ borderless,
+ displayDescription,
+ fillDisplay,
+}: {
+ image?: Asset<"WITHOUT_UNRESOLVABLE_LINKS", string>;
+ borderless?: boolean;
+ displayDescription?: boolean;
+ fillDisplay?: boolean;
+}) {
+ if (!image) {
+ return null;
+ }
+
+ const { file, description, title } = image.fields;
+ const url = file?.url;
+ if (!url) {
+ return null;
+ }
+ const urlWithProtocol = `https:${url}`;
+
+ const classes = borderless ? "" : "rounded-3xl border border-white-translucent";
+
+ const props = fillDisplay
+ ? { fill: true, objectFit: "cover" }
+ : {
+ height: file.details.image?.height,
+ width: file.details.image?.width,
+ };
+
+ return (
+
+
+ {description && displayDescription &&
{description}
}
+
+ );
+}
diff --git a/src/app/(routes)/blog/[slug]/_components/divider.tsx b/src/app/(routes)/blog/[slug]/_components/divider.tsx
new file mode 100644
index 0000000..6c06851
--- /dev/null
+++ b/src/app/(routes)/blog/[slug]/_components/divider.tsx
@@ -0,0 +1,9 @@
+import { twMerge } from "@/app/_lib/tw-merge";
+
+export default function Divider({ className }: { className?: string }) {
+ return (
+
+ );
+}
diff --git a/src/app/(routes)/blog/[slug]/_components/iframe-container.tsx b/src/app/(routes)/blog/[slug]/_components/iframe-container.tsx
new file mode 100644
index 0000000..a1e4489
--- /dev/null
+++ b/src/app/(routes)/blog/[slug]/_components/iframe-container.tsx
@@ -0,0 +1,13 @@
+import { twMerge } from "@/app/_lib/tw-merge";
+
+type Props = {
+ className?: string;
+};
+
+export function IframeContainer({ className, children }: React.PropsWithChildren) {
+ return (
+
+ {children}
+
+ );
+}
diff --git a/src/app/(routes)/blog/[slug]/_components/meta-info.tsx b/src/app/(routes)/blog/[slug]/_components/meta-info.tsx
new file mode 100644
index 0000000..1bf1a11
--- /dev/null
+++ b/src/app/(routes)/blog/[slug]/_components/meta-info.tsx
@@ -0,0 +1,33 @@
+import { Text } from "@/app/_components/text";
+import { getReadingTime } from "@/app/_lib/contentful";
+import { Document } from "@contentful/rich-text-types";
+import { DateTime } from "luxon";
+import { twMerge } from "tailwind-merge";
+
+export function MetaInfo({
+ isoCreatedDate,
+ content,
+ preventCenter,
+ compact,
+}: {
+ isoCreatedDate: string;
+ content: Document;
+ preventCenter?: boolean;
+ compact?: boolean;
+}) {
+ const dateString = DateTime.fromISO(isoCreatedDate).toFormat("MMM dd, yyyy");
+ const minutesToRead = getReadingTime(content);
+ return (
+
+ {dateString}•
+
+ {minutesToRead} min read
+
+
+ );
+}
diff --git a/src/app/(routes)/blog/[slug]/_components/share-link.tsx b/src/app/(routes)/blog/[slug]/_components/share-link.tsx
new file mode 100644
index 0000000..e192702
--- /dev/null
+++ b/src/app/(routes)/blog/[slug]/_components/share-link.tsx
@@ -0,0 +1,85 @@
+"use client";
+
+import { Text } from "@/app/_components/text";
+import { BlogPostType } from "@/app/_lib/contentful";
+import { TwitterIcon, LinkIcon } from "@/app/_components/icons";
+import { useEffect, useState } from "react";
+import { twMerge } from "@/app/_lib/tw-merge";
+import { useRouter } from "next/navigation";
+
+function LinkButton({
+ onClick,
+ icon: Icon,
+}: {
+ onClick: () => void;
+ icon: typeof TwitterIcon;
+}) {
+ const [copyClicked, setCopyClicked] = useState(false);
+ useEffect(() => {
+ if (copyClicked) {
+ setTimeout(() => {
+ setCopyClicked(false);
+ }, 1000);
+ }
+ }, [copyClicked]);
+
+ const borderColor = copyClicked ? "border-aqua-100" : "border-white-translucent";
+ const textColor = copyClicked ? "text-aqua-100" : "text-white";
+ return (
+
+ );
+}
+
+export default function ShareLink({
+ entry,
+ collapsed,
+}: {
+ entry: BlogPostType;
+ collapsed?: boolean;
+}) {
+ const url = `https://across.to/blog/${entry.fields.slug}`;
+ const twitterUrl = `https://twitter.com/intent/tweet?url=${url}`;
+
+ const buttons = [
+ {
+ icon: LinkIcon,
+ onClick: () => {
+ navigator.clipboard.writeText(url);
+ },
+ },
+ {
+ icon: TwitterIcon,
+ onClick: () => {
+ window.open(twitterUrl, "_blank")?.focus();
+ },
+ },
+ ];
+
+ return (
+
+
Share this article
+
+ {buttons.map((button, index) => (
+
+ ))}
+
+
+ );
+}
diff --git a/src/app/(routes)/blog/[slug]/page.tsx b/src/app/(routes)/blog/[slug]/page.tsx
new file mode 100644
index 0000000..8ea6e75
--- /dev/null
+++ b/src/app/(routes)/blog/[slug]/page.tsx
@@ -0,0 +1,134 @@
+import {
+ resolvePublishDateToIsoDate,
+ retrieveContentfulEntry,
+ retrieveContentfulPublishedSlugs,
+} from "@/app/_lib/contentful";
+import { redirect } from "next/navigation";
+import { ReactNode } from "react";
+import { twMerge } from "tailwind-merge";
+import Divider from "./_components/divider";
+import ArticleContent from "./_components/article-content";
+import BackgroundBanner from "../_components/background-banner";
+import Breadcrumb from "./_components/breadcrumb";
+import { MetaInfo } from "./_components/meta-info";
+import BackToTopButton from "../_components/back-to-top-button";
+import ContentfulImage from "./_components/contentful-image";
+import ShareLink from "./_components/share-link";
+import ArticleSnippetCard from "../_components/article-snippet-card";
+import { Metadata } from "next";
+import { documentToPlainTextString } from "@contentful/rich-text-plain-text-renderer";
+import { SITE_BASE_URL } from "@/app/_constants/links";
+
+type SpecificBlogPageProps = { params: { slug: string } };
+
+export async function generateMetadata({
+ params,
+}: SpecificBlogPageProps): Promise {
+ const entry = await retrieveContentfulEntry(params.slug);
+ if (!entry) {
+ redirect("/404");
+ }
+ const title = entry.fields.title;
+ const description =
+ documentToPlainTextString(entry.fields.content).substring(0, 50) + "...";
+ const imageUrl = `https:${entry.fields.featuredImage?.fields.file?.url}`;
+
+ return {
+ keywords: entry.fields.tag ?? [],
+ metadataBase: new URL(SITE_BASE_URL),
+ publisher: "Across Protocol",
+ alternates: {
+ canonical: `/blog/${params.slug}`,
+ },
+ title,
+ description,
+ icons: {
+ icon: ["/favicon-32x32.png", "/favicon-16x16.png"],
+ },
+ twitter: {
+ card: "summary_large_image",
+ site: "@AcrossProtocol",
+ title,
+ images: [imageUrl],
+ },
+ openGraph: {
+ siteName: "Across Protocol",
+ title,
+ description,
+ images: [imageUrl],
+ url: `/blog/${params.slug}`,
+ },
+ };
+}
+
+export async function generateStaticParams() {
+ // Grab all relevant slugs and pipe them into the SSG function
+ // so that we can generate static blog pages without needing to
+ // use SSR at runtime as much as possible.
+ const { slugsForQuery } = await retrieveContentfulPublishedSlugs();
+ return slugsForQuery.map((slug) => ({
+ slug,
+ }));
+}
+
+const SubStack = ({
+ children,
+ className,
+}: {
+ children?: ReactNode;
+ className?: string;
+}) =>
+ children && (
+
+ {children}
+
+ );
+
+export default async function SpecificBlogPage({ params }: SpecificBlogPageProps) {
+ const entry = await retrieveContentfulEntry(params.slug);
+ if (!entry) {
+ redirect("/404");
+ }
+ const fullTitle = entry.fields.title;
+ const content = entry.fields.content;
+ return (
+ <>
+
+
+
+
+
+
+
+
+ {fullTitle}
+
+ {/** Block for Share Links */}
+
+
+
+
+
+
+
+
+
+
+
+ {entry.relevantEntries.map((entry) => (
+
+ ))}
+
+
+
+
+ >
+ );
+}
diff --git a/src/app/(routes)/blog/_components/article-full-card.tsx b/src/app/(routes)/blog/_components/article-full-card.tsx
new file mode 100644
index 0000000..edd9e13
--- /dev/null
+++ b/src/app/(routes)/blog/_components/article-full-card.tsx
@@ -0,0 +1,41 @@
+import {
+ resolvePublishDateToIsoDate,
+ retrieveContentfulEntry,
+} from "@/app/_lib/contentful";
+import ContentfulImage from "../[slug]/_components/contentful-image";
+import { MetaInfo } from "../[slug]/_components/meta-info";
+import { documentToPlainTextString } from "@contentful/rich-text-plain-text-renderer";
+import { Text } from "@/app/_components/text";
+import Link from "next/link";
+
+export default async function ArticleFullCard({ slug }: { slug: string }) {
+ const article = await retrieveContentfulEntry(slug);
+ if (!article) {
+ return null;
+ }
+ const description =
+ article.fields.description ?? documentToPlainTextString(article.fields.content);
+
+ return (
+
+
+
+
+ {article.fields.title}
+
+
+ {description}
+
+
+
+
+
+
+ );
+}
diff --git a/src/app/(routes)/blog/_components/article-snippet-card.tsx b/src/app/(routes)/blog/_components/article-snippet-card.tsx
new file mode 100644
index 0000000..db2501c
--- /dev/null
+++ b/src/app/(routes)/blog/_components/article-snippet-card.tsx
@@ -0,0 +1,31 @@
+import { BlogPostType, resolvePublishDateToIsoDate } from "@/app/_lib/contentful";
+import { Text } from "@/app/_components/text";
+import Link from "next/link";
+import ContentfulImage from "../[slug]/_components/contentful-image";
+import { MetaInfo } from "../[slug]/_components/meta-info";
+
+export default function ArticleSnippetCard({
+ article,
+}: {
+ article: BlogPostType;
+ expandedOnDesktop?: boolean;
+}) {
+ return (
+
+
+
+
+
+ {article.fields.title}
+
+
+
+ );
+}
diff --git a/src/app/(routes)/blog/_components/back-to-top-button.tsx b/src/app/(routes)/blog/_components/back-to-top-button.tsx
new file mode 100644
index 0000000..90665c8
--- /dev/null
+++ b/src/app/(routes)/blog/_components/back-to-top-button.tsx
@@ -0,0 +1,16 @@
+"use client";
+
+import { Text } from "@/app/_components/text";
+
+export default function BackToTopButton() {
+ const onClickHandler = () => {
+ window.scrollTo({ top: 0, behavior: "smooth" });
+ };
+ return (
+
+
+ Back to top
+
+
+ );
+}
diff --git a/src/app/(routes)/blog/_components/background-banner.tsx b/src/app/(routes)/blog/_components/background-banner.tsx
new file mode 100644
index 0000000..ee860b7
--- /dev/null
+++ b/src/app/(routes)/blog/_components/background-banner.tsx
@@ -0,0 +1,14 @@
+import Image from "next/image";
+import backgroundBanner from "@/app/_assets/blog-background.png";
+
+export default function BackgroundBanner() {
+ return (
+
+ );
+}
diff --git a/src/app/(routes)/blog/_components/filter.tsx b/src/app/(routes)/blog/_components/filter.tsx
new file mode 100644
index 0000000..7e0c5f2
--- /dev/null
+++ b/src/app/(routes)/blog/_components/filter.tsx
@@ -0,0 +1,23 @@
+"use client";
+
+import { SearchIcon } from "@/app/_components/icons";
+import { useFilter } from "@/app/_hooks/useFilter";
+
+export default function Filter() {
+ const { text, handleTextChange } = useFilter();
+
+ return (
+
+
+
+ handleTextChange(e.target.value)}
+ className="w-80 bg-transparent outline-none"
+ />
+
+
+ );
+}
diff --git a/src/app/(routes)/blog/_components/pagination.tsx b/src/app/(routes)/blog/_components/pagination.tsx
new file mode 100644
index 0000000..4012af6
--- /dev/null
+++ b/src/app/(routes)/blog/_components/pagination.tsx
@@ -0,0 +1,76 @@
+"use client";
+
+import { useFilter } from "@/app/_hooks/useFilter";
+import { twMerge } from "@/app/_lib/tw-merge";
+
+export default function Pagination({
+ pageLength,
+ currentPage,
+ totalCount,
+}: {
+ pageLength: number;
+ currentPage: number;
+ totalCount: number;
+}) {
+ const { handlePageChange } = useFilter();
+
+ const pageChangeHandler = (page: number) => {
+ handlePageChange(page);
+ window.scrollTo({ top: 0, behavior: "smooth" });
+ };
+
+ const totalPages = Math.ceil(totalCount / pageLength);
+ const hasPrevious = currentPage > 1;
+ const hasNext = currentPage < totalPages;
+
+ return (
+
+
pageChangeHandler(currentPage - 1)}
+ />
+ {Array.from({ length: totalPages }).map((_, index) => (
+ pageChangeHandler(index + 1)}
+ />
+ ))}
+ pageChangeHandler(currentPage + 1)}
+ />
+
+ );
+}
+
+const PageButton = ({
+ text,
+ isDisabled,
+ onClick,
+ isAqua,
+}: {
+ text: string;
+ isDisabled: boolean;
+ isAqua: boolean;
+ onClick?: () => void;
+}) => (
+
+ {text}
+
+);
diff --git a/src/app/(routes)/blog/_components/posts.tsx b/src/app/(routes)/blog/_components/posts.tsx
new file mode 100644
index 0000000..387c268
--- /dev/null
+++ b/src/app/(routes)/blog/_components/posts.tsx
@@ -0,0 +1,15 @@
+import { Text } from "@/app/_components/text";
+import ArticleFullCard from "./article-full-card";
+
+export async function Posts({ isSearch, slugs }: { isSearch: boolean; slugs: string[] }) {
+ return (
+
+
+ {isSearch ? "Search results" : "Most recent articles"}
+
+ {slugs.map((slug) => (
+
+ ))}
+
+ );
+}
diff --git a/src/app/(routes)/blog/page.tsx b/src/app/(routes)/blog/page.tsx
new file mode 100644
index 0000000..2a74e91
--- /dev/null
+++ b/src/app/(routes)/blog/page.tsx
@@ -0,0 +1,95 @@
+import { Text } from "@/app/_components/text";
+import BackToTopButton from "./_components/back-to-top-button";
+import BackgroundBanner from "./_components/background-banner";
+import Filter from "./_components/filter";
+
+import { Suspense } from "react";
+import { Posts } from "./_components/posts";
+import { createCacheKey } from "@/app/_lib/cache";
+import { Metadata } from "next";
+import { SITE_BASE_URL } from "@/app/_constants/links";
+import Pagination from "./_components/pagination";
+import { retrieveContentfulPublishedSlugs } from "@/app/_lib/contentful";
+
+export type SearchParams = Record;
+
+type PageProps = {
+ searchParams: SearchParams;
+};
+
+export default async function BlogHomePage({ searchParams }: PageProps) {
+ const key = createCacheKey({
+ searchParams,
+ });
+
+ const search = searchParams["search"];
+ const isSearch = Boolean(!!search);
+ const page = Number(searchParams["page"]);
+
+ const pageNumber = isNaN(page) || !Number.isInteger(page) || page < 1 ? 1 : page;
+ const pageLength = 16;
+
+ const { slugsForQuery, totalCount } = await retrieveContentfulPublishedSlugs({
+ query: search,
+ sortByRecent: true,
+ limit: pageLength,
+ skip: (pageNumber - 1) * pageLength,
+ });
+
+ return (
+ <>
+
+
+
+ Across Blog
+
+
+
+
+ Searching...
+ }
+ >
+
+
+
+
+
+ >
+ );
+}
+
+export async function generateMetadata(): Promise {
+ const title = "Across Blog";
+ const description =
+ "Explore the latest in cross-chain solutions with the Across blog. Dive into tutorials, updates and announcements that help you leverage our protocol for fast and secure cross-chain transactions.";
+
+ return {
+ metadataBase: new URL(SITE_BASE_URL),
+ publisher: "Across Protocol",
+ alternates: {
+ canonical: `/blog`,
+ },
+ title,
+ description,
+ icons: {
+ icon: ["/favicon-32x32.png", "/favicon-16x16.png"],
+ },
+ twitter: {
+ card: "summary_large_image",
+ site: "@AcrossProtocol",
+ title,
+ },
+ openGraph: {
+ siteName: "Across Protocol",
+ title,
+ description,
+ },
+ };
+}
diff --git a/src/app/_amplitude/index.ts b/src/app/_amplitude/index.ts
index 90dc5b6..1c3fbb7 100644
--- a/src/app/_amplitude/index.ts
+++ b/src/app/_amplitude/index.ts
@@ -177,11 +177,37 @@ export interface IdentifyProperties {
WethVolumeUsd?: any;
}
+export interface BlogSearchProperties {
+ /**
+ * | Rule | Value |
+ * |---|---|
+ * | Enum Values | splashPage, bridgePage, poolPage, rewardsPage, transactionsPage, stakingPage, referralPage, airdropPage, 404Page, marketingHomePage, marketingBridgePage, marketingAcrossPlusPage, marketingSettlementPage, depositStatusPage, marketingBlogSpecificPage, marketingBlogHomePage |
+ */
+ page:
+ | "splashPage"
+ | "bridgePage"
+ | "poolPage"
+ | "rewardsPage"
+ | "transactionsPage"
+ | "stakingPage"
+ | "referralPage"
+ | "airdropPage"
+ | "404Page"
+ | "marketingHomePage"
+ | "marketingBridgePage"
+ | "marketingAcrossPlusPage"
+ | "marketingSettlementPage"
+ | "depositStatusPage"
+ | "marketingBlogSpecificPage"
+ | "marketingBlogHomePage";
+ search: string;
+}
+
export interface BridgeButtonClickedProperties {
/**
* | Rule | Value |
* |---|---|
- * | Enum Values | splashPage, bridgePage, poolPage, rewardsPage, transactionsPage, stakingPage, referralPage, airdropPage, 404Page, marketingHomePage, marketingBridgePage, marketingAcrossPlusPage, marketingSettlementPage, depositStatusPage |
+ * | Enum Values | splashPage, bridgePage, poolPage, rewardsPage, transactionsPage, stakingPage, referralPage, airdropPage, 404Page, marketingHomePage, marketingBridgePage, marketingAcrossPlusPage, marketingSettlementPage, depositStatusPage, marketingBlogSpecificPage, marketingBlogHomePage |
*/
page:
| "splashPage"
@@ -197,7 +223,9 @@ export interface BridgeButtonClickedProperties {
| "marketingBridgePage"
| "marketingAcrossPlusPage"
| "marketingSettlementPage"
- | "depositStatusPage";
+ | "depositStatusPage"
+ | "marketingBlogSpecificPage"
+ | "marketingBlogHomePage";
/**
* | Rule | Value |
* |---|---|
@@ -224,7 +252,7 @@ export interface CtaButtonClickedProperties {
/**
* | Rule | Value |
* |---|---|
- * | Enum Values | splashPage, bridgePage, poolPage, rewardsPage, transactionsPage, stakingPage, referralPage, airdropPage, 404Page, marketingHomePage, marketingBridgePage, marketingAcrossPlusPage, marketingSettlementPage, depositStatusPage |
+ * | Enum Values | splashPage, bridgePage, poolPage, rewardsPage, transactionsPage, stakingPage, referralPage, airdropPage, 404Page, marketingHomePage, marketingBridgePage, marketingAcrossPlusPage, marketingSettlementPage, depositStatusPage, marketingBlogSpecificPage, marketingBlogHomePage |
*/
page:
| "splashPage"
@@ -240,7 +268,9 @@ export interface CtaButtonClickedProperties {
| "marketingBridgePage"
| "marketingAcrossPlusPage"
| "marketingSettlementPage"
- | "depositStatusPage";
+ | "depositStatusPage"
+ | "marketingBlogSpecificPage"
+ | "marketingBlogHomePage";
/**
* | Rule | Value |
* |---|---|
@@ -276,7 +306,7 @@ export interface PageViewedProperties {
/**
* | Rule | Value |
* |---|---|
- * | Enum Values | splashPage, bridgePage, poolPage, rewardsPage, transactionsPage, stakingPage, referralPage, airdropPage, 404Page, marketingHomePage, marketingBridgePage, marketingAcrossPlusPage, marketingSettlementPage, depositStatusPage |
+ * | Enum Values | splashPage, bridgePage, poolPage, rewardsPage, transactionsPage, stakingPage, referralPage, airdropPage, 404Page, marketingHomePage, marketingBridgePage, marketingAcrossPlusPage, marketingSettlementPage, depositStatusPage, marketingBlogSpecificPage, marketingBlogHomePage |
*/
page:
| "splashPage"
@@ -292,7 +322,9 @@ export interface PageViewedProperties {
| "marketingBridgePage"
| "marketingAcrossPlusPage"
| "marketingSettlementPage"
- | "depositStatusPage";
+ | "depositStatusPage"
+ | "marketingBlogSpecificPage"
+ | "marketingBlogHomePage";
path: string;
/**
* Address of referee, null if no referral used
@@ -320,6 +352,14 @@ export class ApplicationLoaded implements BaseEvent {
event_type = "ApplicationLoaded";
}
+export class BlogSearch implements BaseEvent {
+ event_type = "BlogSearch";
+
+ constructor(public event_properties: BlogSearchProperties) {
+ this.event_properties = event_properties;
+ }
+}
+
export class BridgeButtonClicked implements BaseEvent {
event_type = "BridgeButtonClicked";
@@ -478,6 +518,23 @@ export class Ampli {
return this.track(new ApplicationLoaded(), options);
}
+ /**
+ * BlogSearch
+ *
+ * [View in Tracking Plan](https://data.amplitude.com/risklabs/Risk%20Labs/events/main/latest/BlogSearch)
+ *
+ * Event has no description in tracking plan.
+ *
+ * @param properties The event's properties (e.g. page)
+ * @param options Amplitude event options.
+ */
+ blogSearch(
+ properties: BlogSearchProperties,
+ options?: EventOptions,
+ ) {
+ return this.track(new BlogSearch(properties), options);
+ }
+
/**
* BridgeButtonClicked
*
diff --git a/src/app/_assets/blog-background.png b/src/app/_assets/blog-background.png
new file mode 100644
index 0000000..af79ab3
Binary files /dev/null and b/src/app/_assets/blog-background.png differ
diff --git a/src/app/_components/footer.tsx b/src/app/_components/footer.tsx
index 319b346..582eb94 100644
--- a/src/app/_components/footer.tsx
+++ b/src/app/_components/footer.tsx
@@ -12,6 +12,7 @@ import {
DocumentIcon,
DiscourseIcon,
GitHubIcon,
+ NewspaperIcon,
} from "./icons";
import { IconBox } from "./icon-box";
import { PRODUCT_LINKS, SOCIAL_LINKS, INFORMATION_LINKS } from "@/app/_constants";
@@ -69,6 +70,12 @@ const socials = [
];
const information = [
+ {
+ ...INFORMATION_LINKS.blog,
+ Icon: NewspaperIcon,
+ iconClassName: "h-5 w-5",
+ iconContainerClassName: "bg-light-100/[.05]",
+ },
{
...INFORMATION_LINKS.docs,
Icon: DocumentIcon,
@@ -123,7 +130,7 @@ function FooterBox(props: {
{props.items.map((item) => (
- {props.useExternalLinks ? (
+ {props.useExternalLinks && !item.href.startsWith("/") ? (
diff --git a/src/app/_components/header-nav/index.tsx b/src/app/_components/header-nav/index.tsx
index a648655..44b387d 100644
--- a/src/app/_components/header-nav/index.tsx
+++ b/src/app/_components/header-nav/index.tsx
@@ -5,7 +5,7 @@ import { usePathname } from "next/navigation";
import { useState } from "react";
import { twMerge } from "@/app/_lib/tw-merge";
-import { PRODUCT_LINKS, SOCIAL_LINKS } from "@/app/_constants";
+import { INFORMATION_LINKS, PRODUCT_LINKS, SOCIAL_LINKS } from "@/app/_constants";
import {
AcrossIcon,
@@ -18,6 +18,7 @@ import {
TwitterIcon,
MediumIcon,
DiscourseIcon,
+ NewspaperIcon,
} from "../icons";
import { Button } from "../button";
import { Text } from "../text";
@@ -80,6 +81,14 @@ const communityNavigationItems = [
iconContainerClassName: "bg-light-100/[.05]",
containerClassName: "group-hover:bg-light-100/[.05]",
},
+ {
+ ...INFORMATION_LINKS.blog,
+ description: "Across Blog",
+ Icon: NewspaperIcon,
+ iconClassName: "h-4 w-4",
+ iconContainerClassName: "bg-light-100/[.05]",
+ containerClassName: "group-hover:bg-light-100/[.05]",
+ },
];
export function HeaderNav() {
@@ -109,6 +118,7 @@ export function HeaderNav() {
Home
+
Blog
) {
+ return (
+
+ );
+}
diff --git a/src/app/_components/icons/newspaper.tsx b/src/app/_components/icons/newspaper.tsx
new file mode 100644
index 0000000..3479249
--- /dev/null
+++ b/src/app/_components/icons/newspaper.tsx
@@ -0,0 +1,38 @@
+import { SVGProps } from "react";
+
+export function NewspaperIcon(props: SVGProps) {
+ return (
+
+ );
+}
diff --git a/src/app/_components/icons/search.tsx b/src/app/_components/icons/search.tsx
new file mode 100644
index 0000000..2fa94ad
--- /dev/null
+++ b/src/app/_components/icons/search.tsx
@@ -0,0 +1,22 @@
+import { SVGProps } from "react";
+
+export function SearchIcon(props: SVGProps) {
+ return (
+
+ );
+}
diff --git a/src/app/_components/link.tsx b/src/app/_components/link.tsx
index be3d43a..2c44251 100644
--- a/src/app/_components/link.tsx
+++ b/src/app/_components/link.tsx
@@ -17,7 +17,7 @@ type CustomLinkProps = LinkProps & {
*/
function CustomLink({ href, preserveQueryParams, ...props }: CustomLinkProps) {
const params = useSearchParams();
- if (preserveQueryParams && !href.toString().includes("?")) {
+ if (preserveQueryParams && params.size > 0 && !href.toString().includes("?")) {
href = `${href.toString()}?${params.toString()}`;
}
return ;
diff --git a/src/app/_components/text.tsx b/src/app/_components/text.tsx
index c48811b..1420ad4 100644
--- a/src/app/_components/text.tsx
+++ b/src/app/_components/text.tsx
@@ -1,5 +1,5 @@
-import { ComponentProps } from "react";
import { twMerge } from "@/app/_lib/tw-merge";
+import { ComponentProps } from "react";
type TextVariant = keyof typeof textBaseClasses;
@@ -9,8 +9,12 @@ type Props = ComponentProps<"div"> & {
const textBaseClasses = {
body: "text-md",
+ "cap-case-md":
+ "text-medium text-medium uppercase lining-nums tabular-nums tracking-wide-4",
"cap-case-sm":
"text-medium text-xs uppercase lining-nums tabular-nums tracking-wide-4 sm:text-sm",
+ "cap-case-xs":
+ "text-medium text-xs uppercase lining-nums tabular-nums tracking-wide-4 xs:text-xs",
"cap-case": "text-medium text-xs uppercase lining-nums tabular-nums tracking-wide-4",
"heading-1":
"text-heading-3 font-lighter lining-nums tabular-nums tracking-tight-5 sm:text-heading-2 md:text-heading-1",
@@ -20,6 +24,8 @@ const textBaseClasses = {
"text-heading-3 font-lighter lining-nums tabular-nums tracking-tight-5 sm:text-heading-3",
"heading-4":
"text-heading-4 font-lighter lining-nums tabular-nums tracking-tight-5 sm:text-heading-4",
+ "heading-5":
+ "text-heading-5 font-lighter lining-nums tabular-nums tracking-tight-5 sm:text-heading-5",
"body-nums": "text-md lining-nums tabular-nums sm:text-lg",
"body-nums-sm": "text-sm lining-nums tabular-nums",
};
diff --git a/src/app/_constants/amplitude.ts b/src/app/_constants/amplitude.ts
index 27f0896..803a316 100644
--- a/src/app/_constants/amplitude.ts
+++ b/src/app/_constants/amplitude.ts
@@ -3,11 +3,14 @@ export type AMPLITUDE_PAGE =
| "marketingHomePage"
| "marketingBridgePage"
| "marketingAcrossPlusPage"
- | "marketingSettlementPage";
+ | "marketingSettlementPage"
+ | "marketingBlogSpecificPage"
+ | "marketingBlogHomePage";
export const AMPLITUDE_PAGE_LOOKUP: Record = {
"/": "marketingHomePage",
"/across-bridge": "marketingBridgePage",
"/across-plus": "marketingAcrossPlusPage",
"/across-settlement": "marketingSettlementPage",
+ "/blog": "marketingBlogHomePage",
};
diff --git a/src/app/_constants/environment.ts b/src/app/_constants/environment.ts
index 59fb24d..cf9cc09 100644
--- a/src/app/_constants/environment.ts
+++ b/src/app/_constants/environment.ts
@@ -5,3 +5,7 @@ export const AMPLITUDE_LOGGING =
process.env.NEXT_PUBLIC_AMPLITUDE_DEBUG_LOGGING === "true";
export const GIT_COMMIT_HASH = process.env.NEXT_PUBLIC_VERCEL_GIT_COMMIT_SHA ?? "";
export const GOOGLE_ANALYTICS_TAG_ID = process.env.NEXT_PUBLIC_GOOGLE_ANALYTICS_ID;
+
+// Server side environment variables
+export const CONTENTFUL_SPACE_ID = process.env.CONTENTFUL_SPACE_ID;
+export const CONTENTFUL_ACCESS_TOKEN = process.env.CONTENTFUL_ACCESS_TOKEN;
diff --git a/src/app/_constants/links.ts b/src/app/_constants/links.ts
index 6eac5d5..825c81f 100644
--- a/src/app/_constants/links.ts
+++ b/src/app/_constants/links.ts
@@ -48,6 +48,10 @@ export const INFORMATION_LINKS = {
label: "Docs",
href: "https://docs.across.to/v/v3-developer-docs/introduction/what-is-across",
},
+ blog: {
+ label: "Blog",
+ href: "/blog",
+ },
};
export const INTEGRATION_LINKS = {
@@ -57,4 +61,6 @@ export const INTEGRATION_LINKS = {
"https://docs.across.to/v/v3-developer-docs/concepts/intents-architecture-in-across",
};
+export const SITE_BASE_URL = "https://across.to" as const;
+
export const TERMS_OF_SERVICE = "/terms-of-service";
diff --git a/src/app/_hooks/useFilter.ts b/src/app/_hooks/useFilter.ts
new file mode 100644
index 0000000..2e4d737
--- /dev/null
+++ b/src/app/_hooks/useFilter.ts
@@ -0,0 +1,47 @@
+import { useSetQueryParams } from "./useSetQueryParams";
+import { useState, useCallback } from "react";
+import { useDebouncedCallback } from "use-debounce";
+import { ampli } from "../_amplitude";
+
+export function useFilter() {
+ const { params, setParams, removeParams } = useSetQueryParams(["page", "search"]);
+ const [text, setText] = useState(params.search ?? "");
+
+ const debouncedSetParam = useDebouncedCallback((value: string) => {
+ setParams({
+ search: value,
+ page: "1", // Reset page to 1 when searching
+ });
+ if (!!value) {
+ ampli.blogSearch({ search: value, page: "marketingBlogHomePage" });
+ }
+ }, 300);
+
+ function handleTextChange(value: string) {
+ setText(value);
+ debouncedSetParam(value);
+ }
+
+ function handlePageChange(value: number) {
+ setParams({
+ page: String(value),
+ });
+ }
+
+ const clearAll = useCallback(() => {
+ setText("");
+ removeParams(["page", "search"]);
+ }, [removeParams]);
+
+ const hasParams = text || params.tag ? true : false;
+
+ return {
+ text,
+ handleTextChange,
+ productParam: params.product,
+ tag: params.tag,
+ handlePageChange,
+ clearAll,
+ hasParams,
+ };
+}
diff --git a/src/app/_hooks/useSetQueryParams.ts b/src/app/_hooks/useSetQueryParams.ts
new file mode 100644
index 0000000..70714fe
--- /dev/null
+++ b/src/app/_hooks/useSetQueryParams.ts
@@ -0,0 +1,70 @@
+"use client";
+import { usePathname, useRouter, useSearchParams } from "next/navigation";
+import { useCallback, useState } from "react";
+
+type QueryParams = Record;
+type QueryParamKeys = (keyof QueryParams)[];
+
+export function useSetQueryParams(paramKeys: QueryParamKeys) {
+ const router = useRouter();
+ const pathname = usePathname();
+ const searchParams = useSearchParams();
+
+ const [paramValues, setParamValues] = useState(() => {
+ const initialValues: QueryParams = {};
+ paramKeys.forEach((key) => {
+ const fromUrl = searchParams.get(key);
+ Object.assign(initialValues, {
+ [key]: fromUrl ? decodeURIComponent(fromUrl) : undefined,
+ });
+ });
+ return initialValues;
+ });
+
+ const setParams = useCallback(
+ (params: QueryParams) => {
+ const newParams = new URLSearchParams(searchParams.toString());
+ Object.keys(params).forEach((key) => {
+ const value = params[key];
+ if (value === undefined || value === "") {
+ newParams.delete(key);
+ } else {
+ newParams.set(key, encodeURIComponent(value));
+ }
+ });
+ router.push(`${pathname}?${newParams.toString()}`, {
+ scroll: false,
+ });
+ setParamValues((prev) => ({ ...prev, ...params }));
+ },
+ [pathname, router, searchParams],
+ );
+
+ const removeParams = useCallback(
+ (keys: string[]) => {
+ const newParams = new URLSearchParams(searchParams.toString());
+ keys.forEach((key) => {
+ newParams.delete(key);
+ });
+ const queryString = newParams.toString();
+ const path = queryString ? `${pathname}?${queryString}` : pathname;
+ router.push(path, {
+ scroll: false,
+ });
+ setParamValues((prev) => {
+ const updatedValues = { ...prev };
+ keys.forEach((key) => {
+ delete updatedValues[key];
+ });
+ return updatedValues;
+ });
+ },
+ [pathname, router, searchParams],
+ );
+
+ return {
+ params: paramValues,
+ setParams,
+ removeParams,
+ };
+}
diff --git a/src/app/_lib/amplitude.ts b/src/app/_lib/amplitude.ts
index 8ab13fe..a5c8238 100644
--- a/src/app/_lib/amplitude.ts
+++ b/src/app/_lib/amplitude.ts
@@ -39,5 +39,8 @@ export async function initializeAmplitude(setLoaded: (loaded: boolean) => void)
}
export function pageLookup(pathname: string) {
- return AMPLITUDE_PAGE_LOOKUP[pathname] ?? "404Page";
+ const isSpecificBlogPage = /^\/blog\/[a-zA-Z0-9-]+$/.test(pathname);
+ return isSpecificBlogPage
+ ? "marketingBlogSpecificPage"
+ : AMPLITUDE_PAGE_LOOKUP[pathname] ?? "404Page";
}
diff --git a/src/app/_lib/cache.ts b/src/app/_lib/cache.ts
new file mode 100644
index 0000000..548de9d
--- /dev/null
+++ b/src/app/_lib/cache.ts
@@ -0,0 +1,13 @@
+import { SearchParams } from "../(routes)/blog/page";
+
+export function createCacheKey(options: { searchParams: SearchParams }) {
+ const { searchParams } = options;
+ const newParamString = new URLSearchParams();
+ Object.entries(searchParams).forEach(([key, value]) => {
+ if (typeof value === "string") {
+ newParamString.set(key, value);
+ }
+ });
+
+ return newParamString.toString();
+}
diff --git a/src/app/_lib/contentful.ts b/src/app/_lib/contentful.ts
new file mode 100644
index 0000000..7bb3275
--- /dev/null
+++ b/src/app/_lib/contentful.ts
@@ -0,0 +1,141 @@
+import type {
+ ChainModifiers,
+ Entry,
+ EntryFieldTypes,
+ EntrySkeletonType,
+ LocaleCode,
+} from "contentful";
+import { createClient } from "contentful";
+import { CONTENTFUL_ACCESS_TOKEN, CONTENTFUL_SPACE_ID } from "../_constants";
+import { documentToPlainTextString } from "@contentful/rich-text-plain-text-renderer";
+import { Document } from "@contentful/rich-text-types";
+import words from "lodash.words";
+
+const contentType = "acrossBlogPost";
+const averageReadingSpeed = 238; // words per minute
+
+type TypeAcrossBlogPostFields = {
+ title: EntryFieldTypes.Symbol;
+ slug: EntryFieldTypes.Symbol;
+ content: EntryFieldTypes.RichText;
+ tag: EntryFieldTypes.Array;
+ featuredImage: EntryFieldTypes.AssetLink;
+ publishDate: EntryFieldTypes.Date;
+ description: EntryFieldTypes.Symbol;
+};
+
+type TypeAcrossBlogPostSkeleton = EntrySkeletonType<
+ TypeAcrossBlogPostFields,
+ "acrossBlogPost"
+>;
+export type BlogPostType = Entry<
+ TypeAcrossBlogPostSkeleton,
+ "WITHOUT_UNRESOLVABLE_LINKS",
+ string
+>;
+
+export type BlogPostWithRelevantEntries = BlogPostType & {
+ relevantEntries: BlogPostType[];
+};
+
+function getProductionClient() {
+ return createClient({
+ space: CONTENTFUL_SPACE_ID ?? "",
+ accessToken: CONTENTFUL_ACCESS_TOKEN ?? "",
+ });
+}
+
+export async function retrieveContentfulPublishedSlugs({
+ query,
+ limit,
+ avoidTags,
+ includeTags,
+ sortByRecent,
+ skip,
+}: {
+ query?: string;
+ limit?: number;
+ avoidTags?: string[];
+ includeTags?: string[];
+ sortByRecent?: boolean;
+ skip?: number;
+} = {}): Promise<{ slugsForQuery: string[]; totalCount: number }> {
+ const client = getProductionClient();
+ const options = {
+ content_type: contentType,
+ select: "fields.slug",
+ "fields.content[exists]": true,
+ "fields.slug[exists]": true,
+ ...(limit ? { limit } : {}),
+ ...(query ? { query: decodeURI(query) } : {}),
+ ...(avoidTags ? { "fields.tag[nin]": avoidTags.join(",").toLowerCase() } : {}),
+ ...(includeTags ? { "fields.tag[in]": includeTags.join(",").toLowerCase() } : {}),
+ ...(sortByRecent ? { order: "-fields.publishDate" } : {}),
+ ...(skip ? { skip } : {}),
+ } as const;
+ const entries =
+ await client.withoutUnresolvableLinks.getEntries(options);
+
+ return {
+ slugsForQuery: entries.items.map((item) => item.fields.slug),
+ totalCount: entries.total,
+ };
+}
+
+export async function retrieveContentfulEntry(
+ entrySlugId: string,
+ relevantEntryCount = 4,
+): Promise {
+ const client = getProductionClient();
+ const options = {
+ content_type: contentType,
+ limit: 1,
+ "fields.slug": entrySlugId,
+ } as const;
+ const entries =
+ await client.withoutUnresolvableLinks.getEntries(options);
+ const entry = entries.items[0];
+ if (!entry) {
+ return undefined;
+ }
+ const relevantEntries = await retrieveRelevantContentfulEntries(
+ entrySlugId,
+ entry.fields.tag ?? [],
+ relevantEntryCount,
+ );
+ return {
+ ...entry,
+ relevantEntries,
+ };
+}
+
+export async function retrieveRelevantContentfulEntries(
+ entrySlugId: string,
+ tags: string[],
+ limit: number,
+): Promise {
+ const client = getProductionClient();
+ const options = {
+ content_type: contentType,
+ limit,
+ "fields.content[exists]": true, // no empty posts
+ "fields.tag[in]": tags.join(",").toLowerCase(), // get posts with same tags
+ "fields.slug[nin]": entrySlugId, // don't include current post
+ "fields.slug[exists]": true, // no empty slugs
+ "fields.publishDate[exists]": true, // no empty dates
+ order: "-fields.publishDate", // sorted latest first
+ } as const;
+ const entries =
+ await client.withoutUnresolvableLinks.getEntries(options);
+ return entries.items;
+}
+
+export function getReadingTime(content: Document): number {
+ const rawText = documentToPlainTextString(content);
+ const wordCount = words(rawText).length;
+ return Math.round(wordCount / averageReadingSpeed);
+}
+
+export function resolvePublishDateToIsoDate(entry: BlogPostType): string {
+ return entry.fields.publishDate ?? entry.sys.createdAt;
+}
diff --git a/tailwind.config.ts b/tailwind.config.ts
index cc2a4bc..a8d4870 100644
--- a/tailwind.config.ts
+++ b/tailwind.config.ts
@@ -9,6 +9,7 @@ const config: Config = {
theme: {
colors: {
transparent: "transparent",
+ "white-translucent": "#ffffff08",
light: {
100: "#fff",
200: "#F0FFFB",
@@ -46,6 +47,7 @@ const config: Config = {
"heading-2": ["3rem", "3.3rem"], // [48px, 52.8px]
"heading-3": ["2rem", "2.2rem"], // [32px, 35.2px]
"heading-4": ["1.5rem", "1.65rem"], // [24px, 26.4px]
+ "heading-5": ["1.25rem", "1.375rem"], // [20px, 22px]
},
letterSpacing: {
"tight-1": "-0.28rem", // -4.48px
@@ -117,6 +119,6 @@ const config: Config = {
},
},
},
- plugins: [],
+ plugins: [require("tailwind-scrollbar-hide")],
};
export default config;
diff --git a/yarn.lock b/yarn.lock
index ee5ff4c..8d37d10 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -78,6 +78,40 @@
dependencies:
regenerator-runtime "^0.14.0"
+"@contentful/content-source-maps@^0.6.0":
+ version "0.6.1"
+ resolved "https://registry.yarnpkg.com/@contentful/content-source-maps/-/content-source-maps-0.6.1.tgz#a12828d287bdcd9f31c132f2a230bd1ac3005b77"
+ integrity sha512-IjsyhakG17OC5xtIa5agVRsnjjxiJf9HZjpDIV6Ix036Pd5YHJrbyyiF4KNEmLlIqe3hQ0kHGWEs9S/HfECmRQ==
+ dependencies:
+ "@vercel/stega" "^0.1.2"
+ json-pointer "^0.6.2"
+
+"@contentful/rich-text-plain-text-renderer@^16.2.8":
+ version "16.2.8"
+ resolved "https://registry.yarnpkg.com/@contentful/rich-text-plain-text-renderer/-/rich-text-plain-text-renderer-16.2.8.tgz#5ba85e7a12ab706eccfeb3de551b0467c4332403"
+ integrity sha512-5YQPg1rERTTla1XpQqvLYUtmyz7WPZnNX6o66SYQ7kF8FSjK4HKAVJEpzs8qMlyQCKdlzXUVbu9ItrlStORh1g==
+ dependencies:
+ "@contentful/rich-text-types" "^16.8.3"
+
+"@contentful/rich-text-react-renderer@^15.22.9":
+ version "15.22.9"
+ resolved "https://registry.yarnpkg.com/@contentful/rich-text-react-renderer/-/rich-text-react-renderer-15.22.9.tgz#aee71e5d0c923038041c5f4000e752864ce56aee"
+ integrity sha512-ubzuQKaIwB7AeUi1zHQ6EMvpLOMJ9p5LGxZznkcHG1Sn91LNZorQXfMt9K4tSoOR9Cn8KxN6ca8gMNNh5zGbig==
+ dependencies:
+ "@contentful/rich-text-types" "^16.8.3"
+
+"@contentful/rich-text-types@^16.0.2", "@contentful/rich-text-types@^16.8.3":
+ version "16.8.3"
+ resolved "https://registry.yarnpkg.com/@contentful/rich-text-types/-/rich-text-types-16.8.3.tgz#f0effea5d0ed23f9abfd992c9b1be8a00f4d63e9"
+ integrity sha512-vXwXDQMDbqITCWfTkU5R/q+uvXWCc1eYNvdZyjtrs0YDIYr4L7QJ2s1r4ZheIs3iVf3AFucKIHgDSpwCAm2wKA==
+
+"@emnapi/runtime@^1.2.0":
+ version "1.2.0"
+ resolved "https://registry.yarnpkg.com/@emnapi/runtime/-/runtime-1.2.0.tgz#71d018546c3a91f3b51106530edbc056b9f2f2e3"
+ integrity sha512-bV21/9LQmcQeCPEg3BDFtvwL6cwiTMksYNWQQ4KOxCZikEGalWtenoZ0wCiukJINlGCIi2KXx01g4FoH/LxpzQ==
+ dependencies:
+ tslib "^2.4.0"
+
"@eslint-community/eslint-utils@^4.2.0":
version "4.4.0"
resolved "https://registry.yarnpkg.com/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz#a23514e8fb9af1269d5f7788aa556798d61c6b59"
@@ -137,6 +171,119 @@
resolved "https://registry.yarnpkg.com/@humanwhocodes/object-schema/-/object-schema-2.0.2.tgz#d9fae00a2d5cb40f92cfe64b47ad749fbc38f917"
integrity sha512-6EwiSjwWYP7pTckG6I5eyFANjPhmPjUX9JRLUSfNPC7FX7zK9gyZAfUEaECL6ALTpGX5AjnBq3C9XmVWPitNpw==
+"@img/sharp-darwin-arm64@0.33.5":
+ version "0.33.5"
+ resolved "https://registry.yarnpkg.com/@img/sharp-darwin-arm64/-/sharp-darwin-arm64-0.33.5.tgz#ef5b5a07862805f1e8145a377c8ba6e98813ca08"
+ integrity sha512-UT4p+iz/2H4twwAoLCqfA9UH5pI6DggwKEGuaPy7nCVQ8ZsiY5PIcrRvD1DzuY3qYL07NtIQcWnBSY/heikIFQ==
+ optionalDependencies:
+ "@img/sharp-libvips-darwin-arm64" "1.0.4"
+
+"@img/sharp-darwin-x64@0.33.5":
+ version "0.33.5"
+ resolved "https://registry.yarnpkg.com/@img/sharp-darwin-x64/-/sharp-darwin-x64-0.33.5.tgz#e03d3451cd9e664faa72948cc70a403ea4063d61"
+ integrity sha512-fyHac4jIc1ANYGRDxtiqelIbdWkIuQaI84Mv45KvGRRxSAa7o7d1ZKAOBaYbnepLC1WqxfpimdeWfvqqSGwR2Q==
+ optionalDependencies:
+ "@img/sharp-libvips-darwin-x64" "1.0.4"
+
+"@img/sharp-libvips-darwin-arm64@1.0.4":
+ version "1.0.4"
+ resolved "https://registry.yarnpkg.com/@img/sharp-libvips-darwin-arm64/-/sharp-libvips-darwin-arm64-1.0.4.tgz#447c5026700c01a993c7804eb8af5f6e9868c07f"
+ integrity sha512-XblONe153h0O2zuFfTAbQYAX2JhYmDHeWikp1LM9Hul9gVPjFY427k6dFEcOL72O01QxQsWi761svJ/ev9xEDg==
+
+"@img/sharp-libvips-darwin-x64@1.0.4":
+ version "1.0.4"
+ resolved "https://registry.yarnpkg.com/@img/sharp-libvips-darwin-x64/-/sharp-libvips-darwin-x64-1.0.4.tgz#e0456f8f7c623f9dbfbdc77383caa72281d86062"
+ integrity sha512-xnGR8YuZYfJGmWPvmlunFaWJsb9T/AO2ykoP3Fz/0X5XV2aoYBPkX6xqCQvUTKKiLddarLaxpzNe+b1hjeWHAQ==
+
+"@img/sharp-libvips-linux-arm64@1.0.4":
+ version "1.0.4"
+ resolved "https://registry.yarnpkg.com/@img/sharp-libvips-linux-arm64/-/sharp-libvips-linux-arm64-1.0.4.tgz#979b1c66c9a91f7ff2893556ef267f90ebe51704"
+ integrity sha512-9B+taZ8DlyyqzZQnoeIvDVR/2F4EbMepXMc/NdVbkzsJbzkUjhXv/70GQJ7tdLA4YJgNP25zukcxpX2/SueNrA==
+
+"@img/sharp-libvips-linux-arm@1.0.5":
+ version "1.0.5"
+ resolved "https://registry.yarnpkg.com/@img/sharp-libvips-linux-arm/-/sharp-libvips-linux-arm-1.0.5.tgz#99f922d4e15216ec205dcb6891b721bfd2884197"
+ integrity sha512-gvcC4ACAOPRNATg/ov8/MnbxFDJqf/pDePbBnuBDcjsI8PssmjoKMAz4LtLaVi+OnSb5FK/yIOamqDwGmXW32g==
+
+"@img/sharp-libvips-linux-s390x@1.0.4":
+ version "1.0.4"
+ resolved "https://registry.yarnpkg.com/@img/sharp-libvips-linux-s390x/-/sharp-libvips-linux-s390x-1.0.4.tgz#f8a5eb1f374a082f72b3f45e2fb25b8118a8a5ce"
+ integrity sha512-u7Wz6ntiSSgGSGcjZ55im6uvTrOxSIS8/dgoVMoiGE9I6JAfU50yH5BoDlYA1tcuGS7g/QNtetJnxA6QEsCVTA==
+
+"@img/sharp-libvips-linux-x64@1.0.4":
+ version "1.0.4"
+ resolved "https://registry.yarnpkg.com/@img/sharp-libvips-linux-x64/-/sharp-libvips-linux-x64-1.0.4.tgz#d4c4619cdd157774906e15770ee119931c7ef5e0"
+ integrity sha512-MmWmQ3iPFZr0Iev+BAgVMb3ZyC4KeFc3jFxnNbEPas60e1cIfevbtuyf9nDGIzOaW9PdnDciJm+wFFaTlj5xYw==
+
+"@img/sharp-libvips-linuxmusl-arm64@1.0.4":
+ version "1.0.4"
+ resolved "https://registry.yarnpkg.com/@img/sharp-libvips-linuxmusl-arm64/-/sharp-libvips-linuxmusl-arm64-1.0.4.tgz#166778da0f48dd2bded1fa3033cee6b588f0d5d5"
+ integrity sha512-9Ti+BbTYDcsbp4wfYib8Ctm1ilkugkA/uscUn6UXK1ldpC1JjiXbLfFZtRlBhjPZ5o1NCLiDbg8fhUPKStHoTA==
+
+"@img/sharp-libvips-linuxmusl-x64@1.0.4":
+ version "1.0.4"
+ resolved "https://registry.yarnpkg.com/@img/sharp-libvips-linuxmusl-x64/-/sharp-libvips-linuxmusl-x64-1.0.4.tgz#93794e4d7720b077fcad3e02982f2f1c246751ff"
+ integrity sha512-viYN1KX9m+/hGkJtvYYp+CCLgnJXwiQB39damAO7WMdKWlIhmYTfHjwSbQeUK/20vY154mwezd9HflVFM1wVSw==
+
+"@img/sharp-linux-arm64@0.33.5":
+ version "0.33.5"
+ resolved "https://registry.yarnpkg.com/@img/sharp-linux-arm64/-/sharp-linux-arm64-0.33.5.tgz#edb0697e7a8279c9fc829a60fc35644c4839bb22"
+ integrity sha512-JMVv+AMRyGOHtO1RFBiJy/MBsgz0x4AWrT6QoEVVTyh1E39TrCUpTRI7mx9VksGX4awWASxqCYLCV4wBZHAYxA==
+ optionalDependencies:
+ "@img/sharp-libvips-linux-arm64" "1.0.4"
+
+"@img/sharp-linux-arm@0.33.5":
+ version "0.33.5"
+ resolved "https://registry.yarnpkg.com/@img/sharp-linux-arm/-/sharp-linux-arm-0.33.5.tgz#422c1a352e7b5832842577dc51602bcd5b6f5eff"
+ integrity sha512-JTS1eldqZbJxjvKaAkxhZmBqPRGmxgu+qFKSInv8moZ2AmT5Yib3EQ1c6gp493HvrvV8QgdOXdyaIBrhvFhBMQ==
+ optionalDependencies:
+ "@img/sharp-libvips-linux-arm" "1.0.5"
+
+"@img/sharp-linux-s390x@0.33.5":
+ version "0.33.5"
+ resolved "https://registry.yarnpkg.com/@img/sharp-linux-s390x/-/sharp-linux-s390x-0.33.5.tgz#f5c077926b48e97e4a04d004dfaf175972059667"
+ integrity sha512-y/5PCd+mP4CA/sPDKl2961b+C9d+vPAveS33s6Z3zfASk2j5upL6fXVPZi7ztePZ5CuH+1kW8JtvxgbuXHRa4Q==
+ optionalDependencies:
+ "@img/sharp-libvips-linux-s390x" "1.0.4"
+
+"@img/sharp-linux-x64@0.33.5":
+ version "0.33.5"
+ resolved "https://registry.yarnpkg.com/@img/sharp-linux-x64/-/sharp-linux-x64-0.33.5.tgz#d806e0afd71ae6775cc87f0da8f2d03a7c2209cb"
+ integrity sha512-opC+Ok5pRNAzuvq1AG0ar+1owsu842/Ab+4qvU879ippJBHvyY5n2mxF1izXqkPYlGuP/M556uh53jRLJmzTWA==
+ optionalDependencies:
+ "@img/sharp-libvips-linux-x64" "1.0.4"
+
+"@img/sharp-linuxmusl-arm64@0.33.5":
+ version "0.33.5"
+ resolved "https://registry.yarnpkg.com/@img/sharp-linuxmusl-arm64/-/sharp-linuxmusl-arm64-0.33.5.tgz#252975b915894fb315af5deea174651e208d3d6b"
+ integrity sha512-XrHMZwGQGvJg2V/oRSUfSAfjfPxO+4DkiRh6p2AFjLQztWUuY/o8Mq0eMQVIY7HJ1CDQUJlxGGZRw1a5bqmd1g==
+ optionalDependencies:
+ "@img/sharp-libvips-linuxmusl-arm64" "1.0.4"
+
+"@img/sharp-linuxmusl-x64@0.33.5":
+ version "0.33.5"
+ resolved "https://registry.yarnpkg.com/@img/sharp-linuxmusl-x64/-/sharp-linuxmusl-x64-0.33.5.tgz#3f4609ac5d8ef8ec7dadee80b560961a60fd4f48"
+ integrity sha512-WT+d/cgqKkkKySYmqoZ8y3pxx7lx9vVejxW/W4DOFMYVSkErR+w7mf2u8m/y4+xHe7yY9DAXQMWQhpnMuFfScw==
+ optionalDependencies:
+ "@img/sharp-libvips-linuxmusl-x64" "1.0.4"
+
+"@img/sharp-wasm32@0.33.5":
+ version "0.33.5"
+ resolved "https://registry.yarnpkg.com/@img/sharp-wasm32/-/sharp-wasm32-0.33.5.tgz#6f44f3283069d935bb5ca5813153572f3e6f61a1"
+ integrity sha512-ykUW4LVGaMcU9lu9thv85CbRMAwfeadCJHRsg2GmeRa/cJxsVY9Rbd57JcMxBkKHag5U/x7TSBpScF4U8ElVzg==
+ dependencies:
+ "@emnapi/runtime" "^1.2.0"
+
+"@img/sharp-win32-ia32@0.33.5":
+ version "0.33.5"
+ resolved "https://registry.yarnpkg.com/@img/sharp-win32-ia32/-/sharp-win32-ia32-0.33.5.tgz#1a0c839a40c5351e9885628c85f2e5dfd02b52a9"
+ integrity sha512-T36PblLaTwuVJ/zw/LaH0PdZkRz5rd3SmMHX8GSmR7vtNSP5Z6bQkExdSK7xGWyxLw4sUknBuugTelgw2faBbQ==
+
+"@img/sharp-win32-x64@0.33.5":
+ version "0.33.5"
+ resolved "https://registry.yarnpkg.com/@img/sharp-win32-x64/-/sharp-win32-x64-0.33.5.tgz#56f00962ff0c4e0eb93d34a047d29fa995e3e342"
+ integrity sha512-MpY/o8/8kj+EcnxwvrP4aTJSWw/aZ7JIGR4aBeZkZw5B7/Jn+tY9/VNwtcoGmdT7GfggGIU4kygOMSbYnOrAbg==
+
"@isaacs/cliui@^8.0.2":
version "8.0.2"
resolved "https://registry.yarnpkg.com/@isaacs/cliui/-/cliui-8.0.2.tgz#b37667b7bc181c168782259bab42474fbf52b550"
@@ -313,6 +460,23 @@
resolved "https://registry.yarnpkg.com/@types/json5/-/json5-0.0.29.tgz#ee28707ae94e11d2b827bcbe5270bcea7f3e71ee"
integrity sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==
+"@types/lodash.words@^4.2.9":
+ version "4.2.9"
+ resolved "https://registry.yarnpkg.com/@types/lodash.words/-/lodash.words-4.2.9.tgz#06f10d82241371186dd7219b29f708c451f55182"
+ integrity sha512-t7jYEhzm/JJxYZzU4BNeySF9zXOQwtd5t1pJBcK9ZZf62HUbcFuiLN69G5N9A/QUmE44OsBVrmZcAxbnWm0IXg==
+ dependencies:
+ "@types/lodash" "*"
+
+"@types/lodash@*":
+ version "4.17.7"
+ resolved "https://registry.yarnpkg.com/@types/lodash/-/lodash-4.17.7.tgz#2f776bcb53adc9e13b2c0dfd493dfcbd7de43612"
+ integrity sha512-8wTvZawATi/lsmNu10/j2hk1KEP0IvjubqPE3cu1Xz7xfXXt5oCq3SNUz4fMIP4XGF9Ky+Ue2tBA3hcS7LSBlA==
+
+"@types/luxon@^3.4.2":
+ version "3.4.2"
+ resolved "https://registry.yarnpkg.com/@types/luxon/-/luxon-3.4.2.tgz#e4fc7214a420173cea47739c33cdf10874694db7"
+ integrity sha512-TifLZlFudklWlMBfhubvgqTXRzLDI5pCbGa4P8a3wPyUQSW+1xQ5eDsreP9DWHX3tjq1ke96uYG/nwundroWcA==
+
"@types/node@^20":
version "20.11.16"
resolved "https://registry.yarnpkg.com/@types/node/-/node-20.11.16.tgz#4411f79411514eb8e2926f036c86c9f0e4ec6708"
@@ -402,6 +566,11 @@
resolved "https://registry.yarnpkg.com/@ungap/structured-clone/-/structured-clone-1.2.0.tgz#756641adb587851b5ccb3e095daf27ae581c8406"
integrity sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ==
+"@vercel/stega@^0.1.2":
+ version "0.1.2"
+ resolved "https://registry.yarnpkg.com/@vercel/stega/-/stega-0.1.2.tgz#0c20c5c9419c4288b1de58a64b5f9f26c763b25f"
+ integrity sha512-P7mafQXjkrsoyTRppnt0N21udKS9wUmLXHRyP9saLXLHw32j/FgUJ3FscSWgvSqRs4cj7wKZtwqJEvWJ2jbGmA==
+
acorn-jsx@^5.3.2:
version "5.3.2"
resolved "https://registry.yarnpkg.com/acorn-jsx/-/acorn-jsx-5.3.2.tgz#7ed5bb55908b3b2f1bc55c6af1653bada7f07937"
@@ -565,6 +734,11 @@ asynciterator.prototype@^1.0.0:
dependencies:
has-symbols "^1.0.3"
+asynckit@^0.4.0:
+ version "0.4.0"
+ resolved "https://registry.yarnpkg.com/asynckit/-/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79"
+ integrity sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==
+
autoprefixer@^10.0.1:
version "10.4.17"
resolved "https://registry.yarnpkg.com/autoprefixer/-/autoprefixer-10.4.17.tgz#35cd5695cbbe82f536a50fa025d561b01fdec8be"
@@ -587,6 +761,15 @@ axe-core@=4.7.0:
resolved "https://registry.yarnpkg.com/axe-core/-/axe-core-4.7.0.tgz#34ba5a48a8b564f67e103f0aa5768d76e15bbbbf"
integrity sha512-M0JtH+hlOL5pLQwHOLNYZaXuhqmvS8oExsqB1SBYgA4Dk7u/xx+YdGHXaK5pyUfed5mYXdlYiphWq3G8cRi5JQ==
+axios@~1.6.8:
+ version "1.6.8"
+ resolved "https://registry.yarnpkg.com/axios/-/axios-1.6.8.tgz#66d294951f5d988a00e87a0ffb955316a619ea66"
+ integrity sha512-v/ZHtJDU39mDpyBoFVkETcd/uNdxrWRrg3bKpOKzXFA6Bvqopts6ALSMU3y6ijYxbw2B+wPrIv46egTzJXCLGQ==
+ dependencies:
+ follow-redirects "^1.15.6"
+ form-data "^4.0.0"
+ proxy-from-env "^1.1.0"
+
axobject-query@^3.2.1:
version "3.2.1"
resolved "https://registry.yarnpkg.com/axobject-query/-/axobject-query-3.2.1.tgz#39c378a6e3b06ca679f29138151e45b2b32da62a"
@@ -652,6 +835,17 @@ call-bind@^1.0.0, call-bind@^1.0.2, call-bind@^1.0.5:
get-intrinsic "^1.2.1"
set-function-length "^1.1.1"
+call-bind@^1.0.7:
+ version "1.0.7"
+ resolved "https://registry.yarnpkg.com/call-bind/-/call-bind-1.0.7.tgz#06016599c40c56498c18769d2730be242b6fa3b9"
+ integrity sha512-GHTSNSYICQ7scH7sZ+M2rFopRoLh8t2bLSW6BbgrtLsahOIB5iyAVJf9GjWK3cYTDaMj4XdBpM1cA6pIS0Kv2w==
+ dependencies:
+ es-define-property "^1.0.0"
+ es-errors "^1.3.0"
+ function-bind "^1.1.2"
+ get-intrinsic "^1.2.4"
+ set-function-length "^1.2.1"
+
callsites@^3.0.0:
version "3.1.0"
resolved "https://registry.yarnpkg.com/callsites/-/callsites-3.1.0.tgz#b3630abd8943432f54b3f0519238e33cd7df2f73"
@@ -702,11 +896,34 @@ color-convert@^2.0.1:
dependencies:
color-name "~1.1.4"
-color-name@~1.1.4:
+color-name@^1.0.0, color-name@~1.1.4:
version "1.1.4"
resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.4.tgz#c2a09a87acbde69543de6f63fa3995c826c536a2"
integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==
+color-string@^1.9.0:
+ version "1.9.1"
+ resolved "https://registry.yarnpkg.com/color-string/-/color-string-1.9.1.tgz#4467f9146f036f855b764dfb5bf8582bf342c7a4"
+ integrity sha512-shrVawQFojnZv6xM40anx4CkoDP+fZsw/ZerEMsW/pyzsRbElpsL/DBVW7q3ExxwusdNXI3lXpuhEZkzs8p5Eg==
+ dependencies:
+ color-name "^1.0.0"
+ simple-swizzle "^0.2.2"
+
+color@^4.2.3:
+ version "4.2.3"
+ resolved "https://registry.yarnpkg.com/color/-/color-4.2.3.tgz#d781ecb5e57224ee43ea9627560107c0e0c6463a"
+ integrity sha512-1rXeuUUiGGrykh+CeBdu5Ie7OJwinCgQY0bc7GCRxy5xVHy+moaqkpL/jqQq0MtQOeYcrqEz4abc5f0KtU7W4A==
+ dependencies:
+ color-convert "^2.0.1"
+ color-string "^1.9.0"
+
+combined-stream@^1.0.8:
+ version "1.0.8"
+ resolved "https://registry.yarnpkg.com/combined-stream/-/combined-stream-1.0.8.tgz#c3d45a8b34fd730631a110a8a2520682b31d5a7f"
+ integrity sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==
+ dependencies:
+ delayed-stream "~1.0.0"
+
commander@^4.0.0:
version "4.1.1"
resolved "https://registry.yarnpkg.com/commander/-/commander-4.1.1.tgz#9fd602bd936294e9e9ef46a3f4d6964044b18068"
@@ -717,6 +934,37 @@ concat-map@0.0.1:
resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b"
integrity sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==
+contentful-resolve-response@^1.9.0:
+ version "1.9.0"
+ resolved "https://registry.yarnpkg.com/contentful-resolve-response/-/contentful-resolve-response-1.9.0.tgz#7a89e6f332d32b98c6e98af9406ce7aa64707711"
+ integrity sha512-LtgPx/eREpHXOX82od48zFZbFhXzYw/NfUoYK4Qf1OaKpLzmYPE4cAY4aD+rxVgnMM5JN/mQaPCsofUlJRYEUA==
+ dependencies:
+ fast-copy "^2.1.7"
+
+contentful-sdk-core@^8.1.0:
+ version "8.3.1"
+ resolved "https://registry.yarnpkg.com/contentful-sdk-core/-/contentful-sdk-core-8.3.1.tgz#773d59f286d1bd8e50a3ffd9e3f4f410da6fb929"
+ integrity sha512-HYy4ecFA76ERxz7P0jW7hgDcL8jH+bRckv2QfAwQ4k1yPP9TvxpZwrKnlLM69JOStxVkCXP37HvbjbFnjcoWdg==
+ dependencies:
+ fast-copy "^2.1.7"
+ lodash.isplainobject "^4.0.6"
+ lodash.isstring "^4.0.1"
+ p-throttle "^4.1.1"
+ qs "^6.11.2"
+
+contentful@^10.13.1:
+ version "10.13.1"
+ resolved "https://registry.yarnpkg.com/contentful/-/contentful-10.13.1.tgz#8b14ea44f2816e7a109894145ae6fd161b3a26c6"
+ integrity sha512-GczP0vSWzZGKllzUOsUnCHTKuW92E9oR44uGrLdA7cpel75gJqSB2KpVBV1KT4oaUQn773MAU/J6tWNqCYadfw==
+ dependencies:
+ "@contentful/content-source-maps" "^0.6.0"
+ "@contentful/rich-text-types" "^16.0.2"
+ axios "~1.6.8"
+ contentful-resolve-response "^1.9.0"
+ contentful-sdk-core "^8.1.0"
+ json-stringify-safe "^5.0.1"
+ type-fest "^4.0.0"
+
cross-spawn@^7.0.0, cross-spawn@^7.0.2:
version "7.0.3"
resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.3.tgz#f73a85b9d5d41d045551c177e2882d4ac85728a6"
@@ -769,6 +1017,15 @@ define-data-property@^1.0.1, define-data-property@^1.1.1:
gopd "^1.0.1"
has-property-descriptors "^1.0.0"
+define-data-property@^1.1.4:
+ version "1.1.4"
+ resolved "https://registry.yarnpkg.com/define-data-property/-/define-data-property-1.1.4.tgz#894dc141bb7d3060ae4366f6a0107e68fbe48c5e"
+ integrity sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==
+ dependencies:
+ es-define-property "^1.0.0"
+ es-errors "^1.3.0"
+ gopd "^1.0.1"
+
define-properties@^1.1.3, define-properties@^1.2.0, define-properties@^1.2.1:
version "1.2.1"
resolved "https://registry.yarnpkg.com/define-properties/-/define-properties-1.2.1.tgz#10781cc616eb951a80a034bafcaa7377f6af2b6c"
@@ -778,11 +1035,21 @@ define-properties@^1.1.3, define-properties@^1.2.0, define-properties@^1.2.1:
has-property-descriptors "^1.0.0"
object-keys "^1.1.1"
+delayed-stream@~1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/delayed-stream/-/delayed-stream-1.0.0.tgz#df3ae199acadfb7d440aaae0b29e2272b24ec619"
+ integrity sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==
+
dequal@^2.0.3:
version "2.0.3"
resolved "https://registry.yarnpkg.com/dequal/-/dequal-2.0.3.tgz#2644214f1997d39ed0ee0ece72335490a7ac67be"
integrity sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==
+detect-libc@^2.0.3:
+ version "2.0.3"
+ resolved "https://registry.yarnpkg.com/detect-libc/-/detect-libc-2.0.3.tgz#f0cd503b40f9939b894697d19ad50895e30cf700"
+ integrity sha512-bwy0MGW55bG41VqxxypOsdSdGqLwXPI/focwgTYCFMbdUiBAxLg9CFzG08sz2aqzknwiX7Hkl0bQENjg8iLByw==
+
didyoumean@^1.2.2:
version "1.2.2"
resolved "https://registry.yarnpkg.com/didyoumean/-/didyoumean-1.2.2.tgz#989346ffe9e839b4555ecf5666edea0d3e8ad037"
@@ -915,6 +1182,18 @@ es-abstract@^1.22.1:
unbox-primitive "^1.0.2"
which-typed-array "^1.1.13"
+es-define-property@^1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/es-define-property/-/es-define-property-1.0.0.tgz#c7faefbdff8b2696cf5f46921edfb77cc4ba3845"
+ integrity sha512-jxayLKShrEqqzJ0eumQbVhTYQM27CfT1T35+gCgDFoL82JLsXqTJ76zv6A0YLOgEnLUMvLzsDsGIrl8NFpT2gQ==
+ dependencies:
+ get-intrinsic "^1.2.4"
+
+es-errors@^1.3.0:
+ version "1.3.0"
+ resolved "https://registry.yarnpkg.com/es-errors/-/es-errors-1.3.0.tgz#05f75a25dab98e4fb1dcd5e1472c0546d5057c8f"
+ integrity sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==
+
es-iterator-helpers@^1.0.12, es-iterator-helpers@^1.0.15:
version "1.0.15"
resolved "https://registry.yarnpkg.com/es-iterator-helpers/-/es-iterator-helpers-1.0.15.tgz#bd81d275ac766431d19305923707c3efd9f1ae40"
@@ -1181,6 +1460,11 @@ esutils@^2.0.2:
resolved "https://registry.yarnpkg.com/esutils/-/esutils-2.0.3.tgz#74d2eb4de0b8da1293711910d50775b9b710ef64"
integrity sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==
+fast-copy@^2.1.7:
+ version "2.1.7"
+ resolved "https://registry.yarnpkg.com/fast-copy/-/fast-copy-2.1.7.tgz#affc9475cb4b555fb488572b2a44231d0c9fa39e"
+ integrity sha512-ozrGwyuCTAy7YgFCua8rmqmytECYk/JYAMXcswOcm0qvGoE3tPb7ivBeIHTOK2DiapBhDZgacIhzhQIKU5TCfA==
+
fast-deep-equal@^3.1.1, fast-deep-equal@^3.1.3:
version "3.1.3"
resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz#3a7d56b559d6cbc3eb512325244e619a65c6c525"
@@ -1250,6 +1534,11 @@ flatted@^3.2.9:
resolved "https://registry.yarnpkg.com/flatted/-/flatted-3.2.9.tgz#7eb4c67ca1ba34232ca9d2d93e9886e611ad7daf"
integrity sha512-36yxDn5H7OFZQla0/jFJmbIKTdZAQHngCedGxiMmpNfEZM0sdEeT+WczLQrjK6D7o2aiyLYDnkw0R3JK0Qv1RQ==
+follow-redirects@^1.15.6:
+ version "1.15.6"
+ resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.15.6.tgz#7f815c0cda4249c74ff09e95ef97c23b5fd0399b"
+ integrity sha512-wWN62YITEaOpSK584EZXJafH1AGpO8RVgElfkuXbTOrPX4fIfOyEpW/CsiNd8JdYrAoOvafRTOEnvsO++qCqFA==
+
for-each@^0.3.3:
version "0.3.3"
resolved "https://registry.yarnpkg.com/for-each/-/for-each-0.3.3.tgz#69b447e88a0a5d32c3e7084f3f1710034b21376e"
@@ -1257,6 +1546,11 @@ for-each@^0.3.3:
dependencies:
is-callable "^1.1.3"
+foreach@^2.0.4:
+ version "2.0.6"
+ resolved "https://registry.yarnpkg.com/foreach/-/foreach-2.0.6.tgz#87bcc8a1a0e74000ff2bf9802110708cfb02eb6e"
+ integrity sha512-k6GAGDyqLe9JaebCsFCoudPPWfihKu8pylYXRlqP1J7ms39iPoTtk2fviNglIeQEwdh0bQeKJ01ZPyuyQvKzwg==
+
foreground-child@^3.1.0:
version "3.1.1"
resolved "https://registry.yarnpkg.com/foreground-child/-/foreground-child-3.1.1.tgz#1d173e776d75d2772fed08efe4a0de1ea1b12d0d"
@@ -1265,6 +1559,15 @@ foreground-child@^3.1.0:
cross-spawn "^7.0.0"
signal-exit "^4.0.1"
+form-data@^4.0.0:
+ version "4.0.0"
+ resolved "https://registry.yarnpkg.com/form-data/-/form-data-4.0.0.tgz#93919daeaf361ee529584b9b31664dc12c9fa452"
+ integrity sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==
+ dependencies:
+ asynckit "^0.4.0"
+ combined-stream "^1.0.8"
+ mime-types "^2.1.12"
+
fraction.js@^4.3.7:
version "4.3.7"
resolved "https://registry.yarnpkg.com/fraction.js/-/fraction.js-4.3.7.tgz#06ca0085157e42fda7f9e726e79fefc4068840f7"
@@ -1310,6 +1613,17 @@ get-intrinsic@^1.0.2, get-intrinsic@^1.1.1, get-intrinsic@^1.1.3, get-intrinsic@
has-symbols "^1.0.3"
hasown "^2.0.0"
+get-intrinsic@^1.2.4:
+ version "1.2.4"
+ resolved "https://registry.yarnpkg.com/get-intrinsic/-/get-intrinsic-1.2.4.tgz#e385f5a4b5227d449c3eabbad05494ef0abbeadd"
+ integrity sha512-5uYhsJH8VJBTv7oslg4BznJYhDoRI6waYCxMmCdnTrcCrHA/fCFKoTFz2JKKE0HdDFUF7/oQuhzumXJK7paBRQ==
+ dependencies:
+ es-errors "^1.3.0"
+ function-bind "^1.1.2"
+ has-proto "^1.0.1"
+ has-symbols "^1.0.3"
+ hasown "^2.0.0"
+
get-symbol-description@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/get-symbol-description/-/get-symbol-description-1.0.0.tgz#7fdb81c900101fbd564dd5f1a30af5aadc1e58d6"
@@ -1422,6 +1736,13 @@ has-property-descriptors@^1.0.0, has-property-descriptors@^1.0.1:
dependencies:
get-intrinsic "^1.2.2"
+has-property-descriptors@^1.0.2:
+ version "1.0.2"
+ resolved "https://registry.yarnpkg.com/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz#963ed7d071dc7bf5f084c5bfbe0d1b6222586854"
+ integrity sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==
+ dependencies:
+ es-define-property "^1.0.0"
+
has-proto@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/has-proto/-/has-proto-1.0.1.tgz#1885c1305538958aff469fef37937c22795408e0"
@@ -1495,6 +1816,11 @@ is-array-buffer@^3.0.1, is-array-buffer@^3.0.2:
get-intrinsic "^1.2.0"
is-typed-array "^1.1.10"
+is-arrayish@^0.3.1:
+ version "0.3.2"
+ resolved "https://registry.yarnpkg.com/is-arrayish/-/is-arrayish-0.3.2.tgz#4574a2ae56f7ab206896fb431eaeed066fdf8f03"
+ integrity sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ==
+
is-async-function@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/is-async-function/-/is-async-function-2.0.0.tgz#8e4418efd3e5d3a6ebb0164c05ef5afb69aa9646"
@@ -1714,6 +2040,13 @@ json-buffer@3.0.1:
resolved "https://registry.yarnpkg.com/json-buffer/-/json-buffer-3.0.1.tgz#9338802a30d3b6605fbe0613e094008ca8c05a13"
integrity sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==
+json-pointer@^0.6.2:
+ version "0.6.2"
+ resolved "https://registry.yarnpkg.com/json-pointer/-/json-pointer-0.6.2.tgz#f97bd7550be5e9ea901f8c9264c9d436a22a93cd"
+ integrity sha512-vLWcKbOaXlO+jvRy4qNd+TI1QUPZzfJj1tpJ3vAXDych5XJf93ftpUKe5pKCrzyIIwgBJcOcCVRUfqQP25afBw==
+ dependencies:
+ foreach "^2.0.4"
+
json-schema-traverse@^0.4.1:
version "0.4.1"
resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz#69f6a87d9513ab8bb8fe63bdb0979c448e684660"
@@ -1724,6 +2057,11 @@ json-stable-stringify-without-jsonify@^1.0.1:
resolved "https://registry.yarnpkg.com/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz#9db7b59496ad3f3cfef30a75142d2d930ad72651"
integrity sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==
+json-stringify-safe@^5.0.1:
+ version "5.0.1"
+ resolved "https://registry.yarnpkg.com/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz#1296a2d58fd45f19a0f6ce01d65701e2c735b6eb"
+ integrity sha512-ZClg6AaYvamvYEE82d3Iyd3vSSIjQ+odgjaTzRuO3s7toCdFKczob2i0zCh7JE8kWn17yvAWhUVxvqGwUalsRA==
+
json5@^1.0.2:
version "1.0.2"
resolved "https://registry.yarnpkg.com/json5/-/json5-1.0.2.tgz#63d98d60f21b313b77c4d6da18bfa69d80e1d593"
@@ -1790,11 +2128,26 @@ locate-path@^6.0.0:
dependencies:
p-locate "^5.0.0"
+lodash.isplainobject@^4.0.6:
+ version "4.0.6"
+ resolved "https://registry.yarnpkg.com/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz#7c526a52d89b45c45cc690b88163be0497f550cb"
+ integrity sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA==
+
+lodash.isstring@^4.0.1:
+ version "4.0.1"
+ resolved "https://registry.yarnpkg.com/lodash.isstring/-/lodash.isstring-4.0.1.tgz#d527dfb5456eca7cc9bb95d5daeaf88ba54a5451"
+ integrity sha512-0wJxfxH1wgO3GrbuP+dTTk7op+6L41QCXbGINEmD+ny/G/eCqGzxyCsh7159S+mgDDcoarnBw6PC1PS5+wUGgw==
+
lodash.merge@^4.6.2:
version "4.6.2"
resolved "https://registry.yarnpkg.com/lodash.merge/-/lodash.merge-4.6.2.tgz#558aa53b43b661e1925a0afdfa36a9a1085fe57a"
integrity sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==
+lodash.words@^4.2.0:
+ version "4.2.0"
+ resolved "https://registry.yarnpkg.com/lodash.words/-/lodash.words-4.2.0.tgz#5ecfeaf8ecf8acaa8e0c8386295f1993c9cf4036"
+ integrity sha512-mXxqd8Yx9BGPij3lZKFSdOsjOTbL4krbCCp9slEozaN4EMppA2dFmK/f8HeohodprY6W0vOdiQ5WFgPaTI75xQ==
+
loose-envify@^1.1.0, loose-envify@^1.4.0:
version "1.4.0"
resolved "https://registry.yarnpkg.com/loose-envify/-/loose-envify-1.4.0.tgz#71ee51fa7be4caec1a63839f7e682d8132d30caf"
@@ -1814,6 +2167,11 @@ lru-cache@^6.0.0:
resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-10.2.0.tgz#0bd445ca57363465900f4d1f9bd8db343a4d95c3"
integrity sha512-2bIM8x+VAf6JT4bKAljS1qUWgMsqZRPGJS6FSahIMPVvctcNhyVp7AJu7quxOW9jwkryBReKZY5tY5JYv2n/7Q==
+luxon@^3.5.0:
+ version "3.5.0"
+ resolved "https://registry.yarnpkg.com/luxon/-/luxon-3.5.0.tgz#6b6f65c5cd1d61d1fd19dbf07ee87a50bf4b8e20"
+ integrity sha512-rh+Zjr6DNfUYR3bPwJEnuwDdqMbxZW7LOQfUN4B54+Cl+0o5zaU9RJ6bcidfDtC1cWCZXQ+nvX8bf6bAji37QQ==
+
merge2@^1.3.0, merge2@^1.4.1:
version "1.4.1"
resolved "https://registry.yarnpkg.com/merge2/-/merge2-1.4.1.tgz#4368892f885e907455a6fd7dc55c0c9d404990ae"
@@ -1827,6 +2185,18 @@ micromatch@^4.0.4, micromatch@^4.0.5:
braces "^3.0.2"
picomatch "^2.3.1"
+mime-db@1.52.0:
+ version "1.52.0"
+ resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.52.0.tgz#bbabcdc02859f4987301c856e3387ce5ec43bf70"
+ integrity sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==
+
+mime-types@^2.1.12:
+ version "2.1.35"
+ resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.35.tgz#381a871b62a734450660ae3deee44813f70d959a"
+ integrity sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==
+ dependencies:
+ mime-db "1.52.0"
+
minimatch@9.0.3, minimatch@^9.0.1:
version "9.0.3"
resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-9.0.3.tgz#a6e00c3de44c3a542bfaae70abfc22420a6da825"
@@ -2031,6 +2401,11 @@ p-locate@^5.0.0:
dependencies:
p-limit "^3.0.2"
+p-throttle@^4.1.1:
+ version "4.1.1"
+ resolved "https://registry.yarnpkg.com/p-throttle/-/p-throttle-4.1.1.tgz#80b1fbd358af40a8bfa1667f9dc8b72b714ad692"
+ integrity sha512-TuU8Ato+pRTPJoDzYD4s7ocJYcNSEZRvlxoq3hcPI2kZDZ49IQ1Wkj7/gDJc3X7XiEAAvRGtDzdXJI0tC3IL1g==
+
parent-module@^1.0.0:
version "1.0.1"
resolved "https://registry.yarnpkg.com/parent-module/-/parent-module-1.0.1.tgz#691d2709e78c79fae3a156622452d00762caaaa2"
@@ -2177,11 +2552,23 @@ prop-types@^15.8.1:
object-assign "^4.1.1"
react-is "^16.13.1"
+proxy-from-env@^1.1.0:
+ version "1.1.0"
+ resolved "https://registry.yarnpkg.com/proxy-from-env/-/proxy-from-env-1.1.0.tgz#e102f16ca355424865755d2c9e8ea4f24d58c3e2"
+ integrity sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==
+
punycode@^2.1.0:
version "2.3.1"
resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.3.1.tgz#027422e2faec0b25e1549c3e1bd8309b9133b6e5"
integrity sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==
+qs@^6.11.2:
+ version "6.13.0"
+ resolved "https://registry.yarnpkg.com/qs/-/qs-6.13.0.tgz#6ca3bd58439f7e245655798997787b0d88a51906"
+ integrity sha512-+38qI9SOr8tfZ4QmJNplMUxqjbe7LKvvZgWdExBOmd+egZTtjLB67Gu0HRX3u/XOq7UU2Nx6nsjvS16Z9uwfpg==
+ dependencies:
+ side-channel "^1.0.6"
+
queue-microtask@^1.2.2:
version "1.2.3"
resolved "https://registry.yarnpkg.com/queue-microtask/-/queue-microtask-1.2.3.tgz#4929228bbc724dfac43e0efb058caf7b6cfb6243"
@@ -2332,6 +2719,11 @@ semver@^7.5.4:
dependencies:
lru-cache "^6.0.0"
+semver@^7.6.3:
+ version "7.6.3"
+ resolved "https://registry.yarnpkg.com/semver/-/semver-7.6.3.tgz#980f7b5550bc175fb4dc09403085627f9eb33143"
+ integrity sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==
+
set-function-length@^1.1.1:
version "1.2.0"
resolved "https://registry.yarnpkg.com/set-function-length/-/set-function-length-1.2.0.tgz#2f81dc6c16c7059bda5ab7c82c11f03a515ed8e1"
@@ -2343,6 +2735,18 @@ set-function-length@^1.1.1:
gopd "^1.0.1"
has-property-descriptors "^1.0.1"
+set-function-length@^1.2.1:
+ version "1.2.2"
+ resolved "https://registry.yarnpkg.com/set-function-length/-/set-function-length-1.2.2.tgz#aac72314198eaed975cf77b2c3b6b880695e5449"
+ integrity sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==
+ dependencies:
+ define-data-property "^1.1.4"
+ es-errors "^1.3.0"
+ function-bind "^1.1.2"
+ get-intrinsic "^1.2.4"
+ gopd "^1.0.1"
+ has-property-descriptors "^1.0.2"
+
set-function-name@^2.0.0, set-function-name@^2.0.1:
version "2.0.1"
resolved "https://registry.yarnpkg.com/set-function-name/-/set-function-name-2.0.1.tgz#12ce38b7954310b9f61faa12701620a0c882793a"
@@ -2352,6 +2756,35 @@ set-function-name@^2.0.0, set-function-name@^2.0.1:
functions-have-names "^1.2.3"
has-property-descriptors "^1.0.0"
+sharp@^0.33.5:
+ version "0.33.5"
+ resolved "https://registry.yarnpkg.com/sharp/-/sharp-0.33.5.tgz#13e0e4130cc309d6a9497596715240b2ec0c594e"
+ integrity sha512-haPVm1EkS9pgvHrQ/F3Xy+hgcuMV0Wm9vfIBSiwZ05k+xgb0PkBQpGsAA/oWdDobNaZTH5ppvHtzCFbnSEwHVw==
+ dependencies:
+ color "^4.2.3"
+ detect-libc "^2.0.3"
+ semver "^7.6.3"
+ optionalDependencies:
+ "@img/sharp-darwin-arm64" "0.33.5"
+ "@img/sharp-darwin-x64" "0.33.5"
+ "@img/sharp-libvips-darwin-arm64" "1.0.4"
+ "@img/sharp-libvips-darwin-x64" "1.0.4"
+ "@img/sharp-libvips-linux-arm" "1.0.5"
+ "@img/sharp-libvips-linux-arm64" "1.0.4"
+ "@img/sharp-libvips-linux-s390x" "1.0.4"
+ "@img/sharp-libvips-linux-x64" "1.0.4"
+ "@img/sharp-libvips-linuxmusl-arm64" "1.0.4"
+ "@img/sharp-libvips-linuxmusl-x64" "1.0.4"
+ "@img/sharp-linux-arm" "0.33.5"
+ "@img/sharp-linux-arm64" "0.33.5"
+ "@img/sharp-linux-s390x" "0.33.5"
+ "@img/sharp-linux-x64" "0.33.5"
+ "@img/sharp-linuxmusl-arm64" "0.33.5"
+ "@img/sharp-linuxmusl-x64" "0.33.5"
+ "@img/sharp-wasm32" "0.33.5"
+ "@img/sharp-win32-ia32" "0.33.5"
+ "@img/sharp-win32-x64" "0.33.5"
+
shebang-command@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/shebang-command/-/shebang-command-2.0.0.tgz#ccd0af4f8835fbdc265b82461aaf0c36663f34ea"
@@ -2373,11 +2806,28 @@ side-channel@^1.0.4:
get-intrinsic "^1.0.2"
object-inspect "^1.9.0"
+side-channel@^1.0.6:
+ version "1.0.6"
+ resolved "https://registry.yarnpkg.com/side-channel/-/side-channel-1.0.6.tgz#abd25fb7cd24baf45466406b1096b7831c9215f2"
+ integrity sha512-fDW/EZ6Q9RiO8eFG8Hj+7u/oW+XrPTIChwCOM2+th2A6OblDtYYIpve9m+KvI9Z4C9qSEXlaGR6bTEYHReuglA==
+ dependencies:
+ call-bind "^1.0.7"
+ es-errors "^1.3.0"
+ get-intrinsic "^1.2.4"
+ object-inspect "^1.13.1"
+
signal-exit@^4.0.1:
version "4.1.0"
resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-4.1.0.tgz#952188c1cbd546070e2dd20d0f41c0ae0530cb04"
integrity sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==
+simple-swizzle@^0.2.2:
+ version "0.2.2"
+ resolved "https://registry.yarnpkg.com/simple-swizzle/-/simple-swizzle-0.2.2.tgz#a4da6b635ffcccca33f70d17cb92592de95e557a"
+ integrity sha512-JA//kQgZtbuY83m+xT+tXJkmJncGMTFT+C+g2h2R9uxkYIrE2yy9sgmcLhCnw57/WSD+Eh3J97FPEDFnbXnDUg==
+ dependencies:
+ is-arrayish "^0.3.1"
+
slash@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/slash/-/slash-3.0.0.tgz#6539be870c165adbd5240220dbe361f1bc4d4634"
@@ -2393,7 +2843,16 @@ streamsearch@^1.1.0:
resolved "https://registry.yarnpkg.com/streamsearch/-/streamsearch-1.1.0.tgz#404dd1e2247ca94af554e841a8ef0eaa238da764"
integrity sha512-Mcc5wHehp9aXz1ax6bZUyY5afg9u2rv5cqQI3mRrYkGC8rW2hM02jWuwjtL++LS5qinSyhj2QfLyNsuc+VsExg==
-"string-width-cjs@npm:string-width@^4.2.0", string-width@^4.1.0:
+"string-width-cjs@npm:string-width@^4.2.0":
+ version "4.2.3"
+ resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010"
+ integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==
+ dependencies:
+ emoji-regex "^8.0.0"
+ is-fullwidth-code-point "^3.0.0"
+ strip-ansi "^6.0.1"
+
+string-width@^4.1.0:
version "4.2.3"
resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010"
integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==
@@ -2453,7 +2912,14 @@ string.prototype.trimstart@^1.0.7:
define-properties "^1.2.0"
es-abstract "^1.22.1"
-"strip-ansi-cjs@npm:strip-ansi@^6.0.1", strip-ansi@^6.0.0, strip-ansi@^6.0.1:
+"strip-ansi-cjs@npm:strip-ansi@^6.0.1":
+ version "6.0.1"
+ resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9"
+ integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==
+ dependencies:
+ ansi-regex "^5.0.1"
+
+strip-ansi@^6.0.0, strip-ansi@^6.0.1:
version "6.0.1"
resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9"
integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==
@@ -2516,6 +2982,11 @@ tailwind-merge@^2.2.1:
dependencies:
"@babel/runtime" "^7.23.7"
+tailwind-scrollbar-hide@^1.1.7:
+ version "1.1.7"
+ resolved "https://registry.yarnpkg.com/tailwind-scrollbar-hide/-/tailwind-scrollbar-hide-1.1.7.tgz#90b481fb2e204030e3919427416650c54f56f847"
+ integrity sha512-X324n9OtpTmOMqEgDUEA/RgLrNfBF/jwJdctaPZDzB3mppxJk7TLIDmOreEDm1Bq4R9LSPu4Epf8VSdovNU+iA==
+
tailwindcss@^3.3.0:
version "3.4.1"
resolved "https://registry.yarnpkg.com/tailwindcss/-/tailwindcss-3.4.1.tgz#f512ca5d1dd4c9503c7d3d28a968f1ad8f5c839d"
@@ -2617,6 +3088,11 @@ type-fest@^0.20.2:
resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.20.2.tgz#1bf207f4b28f91583666cb5fbd327887301cd5f4"
integrity sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==
+type-fest@^4.0.0:
+ version "4.23.0"
+ resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-4.23.0.tgz#8196561a6b835175473be744f3e41e2dece1496b"
+ integrity sha512-ZiBujro2ohr5+Z/hZWHESLz3g08BBdrdLMieYFULJO+tWc437sn8kQsWLJoZErY8alNhxre9K4p3GURAG11n+w==
+
typed-array-buffer@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/typed-array-buffer/-/typed-array-buffer-1.0.0.tgz#18de3e7ed7974b0a729d3feecb94338d1472cd60"
@@ -2691,6 +3167,11 @@ uri-js@^4.2.2:
dependencies:
punycode "^2.1.0"
+use-debounce@^10.0.3:
+ version "10.0.3"
+ resolved "https://registry.yarnpkg.com/use-debounce/-/use-debounce-10.0.3.tgz#636094a37f7aa2bcc77b26b961481a0b571bf7ea"
+ integrity sha512-DxQSI9ZKso689WM1mjgGU3ozcxU1TJElBJ3X6S4SMzMNcm2lVH0AHmyXB+K7ewjz2BSUKJTDqTcwtSMRfB89dg==
+
util-deprecate@^1.0.2:
version "1.0.2"
resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf"