diff --git a/package.json b/package.json
index c0171cd480..80a2f9c0d0 100644
--- a/package.json
+++ b/package.json
@@ -43,7 +43,7 @@
"elliptic": "6.6.0",
"esbuild": "0.24.2",
"eslint": "9.17.0",
- "eslint-config-next": "15.1.2",
+ "eslint-config-next": "14.2.23",
"instantsearch.js": "4.75.4",
"jsonpath-plus": "10.0.7",
"markdown-to-jsx": "7.4.0",
@@ -84,7 +84,7 @@
"depcheck": "^1.4.7",
"dotenv": "^16.4.5",
"eslint": "^9",
- "eslint-config-next": "15.1.2",
+ "eslint-config-next": "14.2.23",
"eslint-config-prettier": "^9.1.0",
"eslint-config-turbo": "^2.3.3",
"eslint-plugin-deprecation": "^3.0.0",
diff --git a/packages/commons/core-utils/src/withDefaultProtocol.ts b/packages/commons/core-utils/src/withDefaultProtocol.ts
index 901fd18ebb..fa2a75b11e 100644
--- a/packages/commons/core-utils/src/withDefaultProtocol.ts
+++ b/packages/commons/core-utils/src/withDefaultProtocol.ts
@@ -21,6 +21,10 @@ export function withDefaultProtocol(
return undefined;
}
+ if (endpoint === "") {
+ throw new Error(`URL is empty`);
+ }
+
// matches any protocol scheme at the beginning of the string (e.g., "http://", "https://", "ftp://")
const protocolRegex = /^[a-z]+:\/\//i;
diff --git a/packages/fern-docs/bundle/package.json b/packages/fern-docs/bundle/package.json
index 2e4762c909..5ee190d816 100644
--- a/packages/fern-docs/bundle/package.json
+++ b/packages/fern-docs/bundle/package.json
@@ -17,7 +17,8 @@
"chromatic": "pnpx chromatic --project-token=chpt_48b3c560025e978",
"depcheck": "depcheck",
"docs:build": "next build",
- "docs:dev": "NODE_OPTIONS='--inspect' next dev",
+ "docs:dev": "next dev",
+ "docs:dev:inspect": "NODE_OPTIONS='--inspect' next dev",
"docs:start": "next start",
"format": "prettier --write --ignore-unknown \"**\"",
"format:check": "prettier --check --ignore-unknown \"**\"",
@@ -79,6 +80,7 @@
"@segment/snippet": "^5.2.1",
"@types/qs": "6.9.14",
"@upstash/qstash": "^2.7.16",
+ "@vercel/functions": "^2.0.0",
"@vercel/kv": "^2.0.0",
"@vercel/otel": "^1.10.0",
"@workos-inc/node": "^7.31.0",
diff --git a/packages/fern-docs/bundle/src/app/[[...slug]]/page.tsx b/packages/fern-docs/bundle/src/app/[domain]/docs-page.tsx
similarity index 91%
rename from packages/fern-docs/bundle/src/app/[[...slug]]/page.tsx
rename to packages/fern-docs/bundle/src/app/[domain]/docs-page.tsx
index 763f1580e0..d5def4a1eb 100644
--- a/packages/fern-docs/bundle/src/app/[[...slug]]/page.tsx
+++ b/packages/fern-docs/bundle/src/app/[domain]/docs-page.tsx
@@ -9,8 +9,10 @@ import FeedbackPopover from "@/components/feedback/FeedbackPopover";
import { serializeMdx } from "@/components/mdx/bundlers/mdx-bundler";
import { DocsContent } from "@/components/resolver/DocsContent";
import { renderThemeStylesheet } from "@/components/themes/stylesheet/renderThemeStylesheet";
+import { ThemedDocs } from "@/components/themes/ThemedDocs";
import { getApiRouteSupplier } from "@/components/util/getApiRouteSupplier";
import { getGitHubInfo, getGitHubRepo } from "@/components/util/github";
+import { getOrigin } from "@/server/auth/origin";
import { getReturnToQueryParam } from "@/server/auth/return-to";
import { createCachedDocsLoader, DocsLoader } from "@/server/docs-loader";
import { createFileResolver } from "@/server/file-resolver";
@@ -44,13 +46,15 @@ import React from "react";
import urlJoin from "url-join";
import { toImageDescriptor } from "../seo";
-export default async function Page({
+export async function DocsPageComponent({
params,
+ fern_token,
}: {
- params: { slug?: string[] };
+ params: { slug?: string[]; domain: string };
+ fern_token?: string;
}) {
const slug = FernNavigation.slugjoin(params.slug);
- const loader = await createCachedDocsLoader();
+ const loader = await createCachedDocsLoader(params.domain, fern_token);
const [baseUrl, config, authState, edgeFlags, files, colors] =
await Promise.all([
loader.getBaseUrl(),
@@ -187,7 +191,7 @@ export default async function Page({
const redirect = new URL(withDefaultProtocol(loader.authConfig.redirect));
redirect.searchParams.set(
getReturnToQueryParam(loader.authConfig),
- urlJoin(withDefaultProtocol(loader.host), slug)
+ urlJoin(getOrigin(), slug)
);
navbarLinks.push({
@@ -206,11 +210,11 @@ export default async function Page({
if (authState.authed) {
const logout = new URL(
getApiRoute("/api/fern-docs/auth/logout"),
- withDefaultProtocol(loader.host)
+ withDefaultProtocol(getOrigin())
);
logout.searchParams.set(
getReturnToQueryParam(loader.authConfig),
- urlJoin(withDefaultProtocol(loader.host), slug)
+ urlJoin(withDefaultProtocol(getOrigin()), slug)
);
navbarLinks.push({
@@ -369,38 +373,43 @@ export default async function Page({
return (
-
-
-
-
+
+
+
+
+
+
);
}
-export async function generateMetadata({
+export async function generateDocsPageMetadata({
params,
+ fern_token,
}: {
- params: { slug?: string[] };
+ params: { slug?: string[]; domain: string };
+ fern_token?: string;
}): Promise {
const slug = FernNavigation.slugjoin(params.slug);
- const docsLoader = await createCachedDocsLoader();
+ const docsLoader = await createCachedDocsLoader(params.domain, fern_token);
const findNode = createFindNode(docsLoader);
const [files, node, config, isSeoDisabled] = await Promise.all([
docsLoader.getFiles(),
diff --git a/packages/fern-docs/bundle/src/app/[domain]/layout.tsx b/packages/fern-docs/bundle/src/app/[domain]/layout.tsx
new file mode 100644
index 0000000000..2ff5513b56
--- /dev/null
+++ b/packages/fern-docs/bundle/src/app/[domain]/layout.tsx
@@ -0,0 +1,190 @@
+"use server";
+
+import { CustomerAnalytics } from "@/components/analytics/CustomerAnalytics";
+import Preload, { PreloadHref } from "@/components/preload";
+import { ThemeScript } from "@/components/themes/ThemeScript";
+import { createCachedDocsLoader } from "@/server/docs-loader";
+import { RgbaColor } from "@/server/types";
+import { getDocsDomainApp } from "@/server/xfernhost/app";
+import { DocsV2Read } from "@fern-api/fdr-sdk/client/types";
+import { withDefaultProtocol } from "@fern-api/ui-core-utils";
+import { Toaster } from "@fern-docs/components";
+import { getEdgeFlags } from "@fern-docs/edge-config";
+import { EdgeFlags } from "@fern-docs/utils";
+import { compact, uniqBy } from "es-toolkit/array";
+import { Metadata, Viewport } from "next/types";
+import tinycolor from "tinycolor2";
+import { toImageDescriptor } from "../seo";
+
+export default async function Layout({
+ children,
+ params,
+}: {
+ children: React.ReactNode;
+ params: {
+ domain: string;
+ };
+}) {
+ const docsLoader = await createCachedDocsLoader(params.domain);
+ const [config, edgeFlags, files, colors] = await Promise.all([
+ docsLoader.getConfig(),
+ getEdgeFlags(params.domain),
+ docsLoader.getFiles(),
+ docsLoader.getColors(),
+ ]);
+ const preloadHrefs = generatePreloadHrefs(
+ config?.typographyV2,
+ files,
+ edgeFlags
+ );
+ return (
+ <>
+ {preloadHrefs.map((href) => (
+
+ ))}
+
+
+ {children}
+
+ >
+ );
+}
+
+export async function generateViewport(): Promise {
+ const domain = getDocsDomainApp();
+ const docsLoader = await createCachedDocsLoader(domain);
+ const colors = await docsLoader.getColors();
+ const dark = maybeToHex(
+ colors.dark?.background ?? colors.dark?.accentPrimary
+ );
+ const light = maybeToHex(
+ colors.light?.background ?? colors.light?.accentPrimary
+ );
+ return {
+ themeColor: compact([
+ dark ? { color: dark, media: "(prefers-color-scheme: dark)" } : undefined,
+ light
+ ? { color: light, media: "(prefers-color-scheme: light)" }
+ : undefined,
+ ]),
+ };
+}
+
+function maybeToHex(color: RgbaColor | undefined): string | undefined {
+ if (color == null) {
+ return undefined;
+ }
+ return tinycolor(color).toHexString();
+}
+
+export async function generateMetadata({
+ params,
+}: {
+ params: {
+ domain: string;
+ };
+}): Promise {
+ const docsLoader = await createCachedDocsLoader(params.domain, params.domain);
+ const [files, config, baseUrl] = await Promise.all([
+ docsLoader.getFiles(),
+ docsLoader.getConfig(),
+ docsLoader.getBaseUrl(),
+ ]);
+ const index = config?.metadata?.noindex ? false : undefined;
+ const follow = config?.metadata?.nofollow ? false : undefined;
+
+ return {
+ metadataBase: new URL(
+ baseUrl.basePath || "/",
+ withDefaultProtocol(docsLoader.domain)
+ ),
+ applicationName: config?.title,
+ title: {
+ template: config?.title ? "%s | " + config?.title : "%s",
+ default: config?.title ?? "Documentation",
+ },
+ robots: { index, follow },
+ openGraph: {
+ title: config?.metadata?.["og:title"],
+ description: config?.metadata?.["og:description"],
+ locale: config?.metadata?.["og:locale"],
+ url: config?.metadata?.["og:url"],
+ siteName: config?.metadata?.["og:site_name"],
+ images: toImageDescriptor(
+ files,
+ config?.metadata?.["og:image"],
+ config?.metadata?.["og:image:width"],
+ config?.metadata?.["og:image:height"]
+ ),
+ },
+ twitter: {
+ site: config?.metadata?.["twitter:site"],
+ creator: config?.metadata?.["twitter:handle"],
+ title: config?.metadata?.["twitter:title"],
+ description: config?.metadata?.["twitter:description"],
+ images: toImageDescriptor(files, config?.metadata?.["twitter:image"]),
+ },
+ icons: {
+ icon: config?.favicon
+ ? toImageDescriptor(files, {
+ type: "fileId",
+ value: config.favicon,
+ })?.url
+ : undefined,
+ },
+ };
+}
+
+function generatePreloadHrefs(
+ typography: DocsV2Read.LoadDocsForUrlResponse["definition"]["config"]["typographyV2"],
+ files: Record,
+ edgeFlags: EdgeFlags
+): PreloadHref[] {
+ const toReturn: PreloadHref[] = [];
+
+ const fontVariants = compact([
+ typography?.bodyFont?.variants,
+ typography?.headingsFont?.variants,
+ typography?.codeFont?.variants,
+ ]).flat();
+
+ fontVariants.forEach((variant) => {
+ try {
+ const file = files[variant.fontFile];
+ if (file != null) {
+ toReturn.push({
+ href: file.src,
+ options: {
+ as: "font",
+ crossOrigin: "anonymous",
+ type: `font/${getFontExtension(file.src)}`,
+ },
+ });
+ }
+ } catch {}
+ });
+
+ if (edgeFlags.isApiPlaygroundEnabled) {
+ toReturn.push({
+ href: "/api/fern-docs/auth/api-key-injection",
+ options: { as: "fetch" },
+ });
+ }
+
+ toReturn.push({
+ href: edgeFlags.isSearchV2Enabled
+ ? "/api/fern-docs/search/v2/key"
+ : "/api/fern-docs/search/v1/key",
+ options: { as: "fetch" },
+ });
+
+ return uniqBy(toReturn, (href) => href.href);
+}
+
+function getFontExtension(url: string): string {
+ const ext = new URL(url).pathname.split(".").pop();
+ if (ext == null) {
+ throw new Error("No extension found for font: " + url);
+ }
+ return ext;
+}
diff --git a/packages/fern-docs/bundle/src/app/[domain]/~dynamic/[[...slug]]/page.tsx b/packages/fern-docs/bundle/src/app/[domain]/~dynamic/[[...slug]]/page.tsx
new file mode 100644
index 0000000000..80a1e08866
--- /dev/null
+++ b/packages/fern-docs/bundle/src/app/[domain]/~dynamic/[[...slug]]/page.tsx
@@ -0,0 +1,23 @@
+"use server";
+
+import { cookies } from "next/headers";
+import { Metadata } from "next/types";
+import { DocsPageComponent, generateDocsPageMetadata } from "../../docs-page";
+
+export default async function Page({
+ params,
+}: {
+ params: { slug?: string[]; domain: string };
+}) {
+ const fern_token = cookies().get("fern_token")?.value;
+ return ;
+}
+
+export async function generateMetadata({
+ params,
+}: {
+ params: { slug?: string[]; domain: string };
+}): Promise {
+ const fern_token = cookies().get("fern_token")?.value;
+ return generateDocsPageMetadata({ params, fern_token });
+}
diff --git a/packages/fern-docs/bundle/src/app/[domain]/~static/[[...slug]]/page.tsx b/packages/fern-docs/bundle/src/app/[domain]/~static/[[...slug]]/page.tsx
new file mode 100644
index 0000000000..f041ea7dd6
--- /dev/null
+++ b/packages/fern-docs/bundle/src/app/[domain]/~static/[[...slug]]/page.tsx
@@ -0,0 +1,20 @@
+"use server";
+
+import { Metadata } from "next/types";
+import { DocsPageComponent, generateDocsPageMetadata } from "../../docs-page";
+
+export default async function Page({
+ params,
+}: {
+ params: { slug?: string[]; domain: string };
+}) {
+ return ;
+}
+
+export async function generateMetadata({
+ params,
+}: {
+ params: { slug?: string[]; domain: string };
+}): Promise {
+ return generateDocsPageMetadata({ params });
+}
diff --git a/packages/fern-docs/bundle/src/app/api/fern-docs/api-definition/[api]/endpoint/[endpoint]/route.ts b/packages/fern-docs/bundle/src/app/api/fern-docs/api-definition/[api]/endpoint/[endpoint]/route.ts
index 35632f8e32..9b79adc260 100644
--- a/packages/fern-docs/bundle/src/app/api/fern-docs/api-definition/[api]/endpoint/[endpoint]/route.ts
+++ b/packages/fern-docs/bundle/src/app/api/fern-docs/api-definition/[api]/endpoint/[endpoint]/route.ts
@@ -1,5 +1,5 @@
import { serializeMdx } from "@/components/mdx/bundlers/mdx-bundler";
-import { getAuthStateEdge } from "@/server/auth/getAuthStateEdge";
+import { createGetAuthStateEdge } from "@/server/auth/getAuthStateEdge";
import * as ApiDefinition from "@fern-api/fdr-sdk/api-definition";
import { ApiDefinitionLoader } from "@fern-docs/cache";
import { getEdgeFlags } from "@fern-docs/edge-config";
@@ -11,7 +11,11 @@ export async function GET(
): Promise {
const { api, endpoint } = params;
- const authState = await getAuthStateEdge(req);
+ const { getAuthState, domain } = await createGetAuthStateEdge(req);
+ const [authState, flags] = await Promise.all([
+ getAuthState(),
+ getEdgeFlags(domain),
+ ]);
if (!authState.ok) {
return NextResponse.json(
@@ -20,10 +24,8 @@ export async function GET(
);
}
- const flags = await getEdgeFlags(authState.domain);
-
const apiDefinition = await ApiDefinitionLoader.create(
- authState.domain,
+ domain,
ApiDefinition.ApiDefinitionId(api)
)
.withEdgeFlags(flags)
diff --git a/packages/fern-docs/bundle/src/app/api/fern-docs/api-definition/[api]/webhook/[webhook]/route.ts b/packages/fern-docs/bundle/src/app/api/fern-docs/api-definition/[api]/webhook/[webhook]/route.ts
index ab7614f193..8bddedb2e0 100644
--- a/packages/fern-docs/bundle/src/app/api/fern-docs/api-definition/[api]/webhook/[webhook]/route.ts
+++ b/packages/fern-docs/bundle/src/app/api/fern-docs/api-definition/[api]/webhook/[webhook]/route.ts
@@ -1,5 +1,5 @@
import { serializeMdx } from "@/components/mdx/bundlers/mdx-bundler";
-import { getAuthStateEdge } from "@/server/auth/getAuthStateEdge";
+import { createGetAuthStateEdge } from "@/server/auth/getAuthStateEdge";
import * as ApiDefinition from "@fern-api/fdr-sdk/api-definition";
import { ApiDefinitionLoader } from "@fern-docs/cache";
import { getEdgeFlags } from "@fern-docs/edge-config";
@@ -11,7 +11,11 @@ export async function GET(
): Promise {
const { api, webhook } = params;
- const authState = await getAuthStateEdge(req);
+ const { getAuthState, domain } = await createGetAuthStateEdge(req);
+ const [authState, flags] = await Promise.all([
+ getAuthState(),
+ getEdgeFlags(domain),
+ ]);
if (!authState.ok) {
return NextResponse.json(
@@ -20,10 +24,8 @@ export async function GET(
);
}
- const flags = await getEdgeFlags(authState.domain);
-
const apiDefinition = await ApiDefinitionLoader.create(
- authState.domain,
+ domain,
ApiDefinition.ApiDefinitionId(api)
)
.withEdgeFlags(flags)
diff --git a/packages/fern-docs/bundle/src/app/api/fern-docs/api-definition/[api]/websocket/[websocket]/route.ts b/packages/fern-docs/bundle/src/app/api/fern-docs/api-definition/[api]/websocket/[websocket]/route.ts
index 078d1cb448..f802f65e16 100644
--- a/packages/fern-docs/bundle/src/app/api/fern-docs/api-definition/[api]/websocket/[websocket]/route.ts
+++ b/packages/fern-docs/bundle/src/app/api/fern-docs/api-definition/[api]/websocket/[websocket]/route.ts
@@ -1,5 +1,5 @@
import { serializeMdx } from "@/components/mdx/bundlers/mdx-bundler";
-import { getAuthStateEdge } from "@/server/auth/getAuthStateEdge";
+import { createGetAuthStateEdge } from "@/server/auth/getAuthStateEdge";
import * as ApiDefinition from "@fern-api/fdr-sdk/api-definition";
import { ApiDefinitionLoader } from "@fern-docs/cache";
import { getEdgeFlags } from "@fern-docs/edge-config";
@@ -11,7 +11,11 @@ export async function GET(
): Promise {
const { api, websocket } = params;
- const authState = await getAuthStateEdge(req);
+ const { getAuthState, domain } = await createGetAuthStateEdge(req);
+ const [authState, flags] = await Promise.all([
+ getAuthState(),
+ getEdgeFlags(domain),
+ ]);
if (!authState.ok) {
return NextResponse.json(
@@ -20,10 +24,8 @@ export async function GET(
);
}
- const flags = await getEdgeFlags(authState.domain);
-
const apiDefinition = await ApiDefinitionLoader.create(
- authState.domain,
+ domain,
ApiDefinition.ApiDefinitionId(api)
)
.withEdgeFlags(flags)
diff --git a/packages/fern-docs/bundle/src/app/api/fern-docs/auth/api-key-injection/route.ts b/packages/fern-docs/bundle/src/app/api/fern-docs/auth/api-key-injection/route.ts
index 351f157d9b..683646c8d3 100644
--- a/packages/fern-docs/bundle/src/app/api/fern-docs/auth/api-key-injection/route.ts
+++ b/packages/fern-docs/bundle/src/app/api/fern-docs/auth/api-key-injection/route.ts
@@ -2,7 +2,7 @@ import { safeVerifyFernJWTConfig } from "@/server/auth/FernJWT";
import { OryOAuth2Client, getOryAuthorizationUrl } from "@/server/auth/ory";
import { getReturnToQueryParam } from "@/server/auth/return-to";
import { withSecureCookie } from "@/server/auth/with-secure-cookie";
-import { fernToken } from "@/server/env-variables";
+import { fernToken_admin } from "@/server/env-variables";
import { getDocsDomainEdge, getHostEdge } from "@/server/xfernhost/edge";
import { withDefaultProtocol } from "@fern-api/ui-core-utils";
import { APIKeyInjectionConfig, OryAccessTokenSchema } from "@fern-docs/auth";
@@ -27,7 +27,7 @@ export async function GET(
const returnToQueryParam = getReturnToQueryParam(edgeConfig);
- const fern_token = fernToken();
+ const fern_token = fernToken_admin();
const access_token = cookieJar.get("access_token")?.value;
const refresh_token = cookieJar.get("refresh_token")?.value;
const fernUser = await safeVerifyFernJWTConfig(fern_token, edgeConfig);
diff --git a/packages/fern-docs/bundle/src/app/api/fern-docs/revalidate/route.ts b/packages/fern-docs/bundle/src/app/api/fern-docs/revalidate/route.ts
index c49eba32a1..934bbb58af 100644
--- a/packages/fern-docs/bundle/src/app/api/fern-docs/revalidate/route.ts
+++ b/packages/fern-docs/bundle/src/app/api/fern-docs/revalidate/route.ts
@@ -16,7 +16,7 @@ export async function GET(req: NextRequest) {
revalidateTag(domain);
if (req.nextUrl.searchParams.get("regenerate") === "true") {
- const docs = await createCachedDocsLoader();
+ const docs = await createCachedDocsLoader(domain);
const root = await docs.unsafe_getFullRoot();
if (!root) {
notFound();
diff --git a/packages/fern-docs/bundle/src/app/api/fern-docs/search/v1/key/route.ts b/packages/fern-docs/bundle/src/app/api/fern-docs/search/v1/key/route.ts
index 941533ff40..9cb148117d 100644
--- a/packages/fern-docs/bundle/src/app/api/fern-docs/search/v1/key/route.ts
+++ b/packages/fern-docs/bundle/src/app/api/fern-docs/search/v1/key/route.ts
@@ -1,4 +1,4 @@
-import { getAuthStateEdge } from "@/server/auth/getAuthStateEdge";
+import { createGetAuthStateEdge } from "@/server/auth/getAuthStateEdge";
import { loadWithUrl } from "@/server/loadWithUrl";
import { provideRegistryService } from "@/server/registry";
import { getInkeepSettings } from "@fern-docs/edge-config";
@@ -10,7 +10,8 @@ export const runtime = "nodejs";
export async function GET(
req: NextRequest
): Promise> {
- const authState = await getAuthStateEdge(req, req.nextUrl.pathname);
+ const { getAuthState, domain } = await createGetAuthStateEdge(req);
+ const authState = await getAuthState();
if (!authState.ok) {
return NextResponse.json(
@@ -19,13 +20,13 @@ export async function GET(
);
}
- const docs = await loadWithUrl(authState.domain);
+ const docs = await loadWithUrl(domain);
if (!docs.ok) {
return NextResponse.json({ isAvailable: false }, { status: 503 });
}
- const inkeepSettings = await getInkeepSettings(authState.domain);
+ const inkeepSettings = await getInkeepSettings(domain);
const searchInfo = docs.body.definition.search;
const config = await getSearchConfig(
provideRegistryService(),
diff --git a/packages/fern-docs/bundle/src/app/api/fern-docs/search/v2/key/route.ts b/packages/fern-docs/bundle/src/app/api/fern-docs/search/v2/key/route.ts
index aca73704dc..2e66869c5c 100644
--- a/packages/fern-docs/bundle/src/app/api/fern-docs/search/v2/key/route.ts
+++ b/packages/fern-docs/bundle/src/app/api/fern-docs/search/v2/key/route.ts
@@ -3,7 +3,7 @@ import { getOrgMetadataForDomain } from "@/server/auth/metadata-for-url";
import {
algoliaAppId,
algoliaSearchApikey,
- fernToken,
+ fernToken_admin,
} from "@/server/env-variables";
import { selectFirst } from "@/server/utils/selectFirst";
import { getDocsDomainEdge } from "@/server/xfernhost/edge";
@@ -34,7 +34,7 @@ export async function GET(req: NextRequest): Promise {
});
}
- const fern_token = await fernToken();
+ const fern_token = await fernToken_admin();
const user = await safeVerifyFernJWTConfig(
fern_token,
await getAuthEdgeConfig(domain)
diff --git a/packages/fern-docs/bundle/src/app/layout.tsx b/packages/fern-docs/bundle/src/app/layout.tsx
index bf9173d64f..f17a5ed78c 100644
--- a/packages/fern-docs/bundle/src/app/layout.tsx
+++ b/packages/fern-docs/bundle/src/app/layout.tsx
@@ -1,29 +1,19 @@
"use server";
-import Preload, { PreloadHref } from "@/components/preload";
-import { createCachedDocsLoader } from "@/server/docs-loader";
-import { RgbaColor } from "@/server/types";
-import { DocsV2Read } from "@fern-api/fdr-sdk/client/types";
+import { getDocsDomainApp } from "@/server/xfernhost/app";
import { withDefaultProtocol } from "@fern-api/ui-core-utils";
import { FernTooltipProvider } from "@fern-docs/components";
-import { getEdgeFlags, getSeoDisabled } from "@fern-docs/edge-config";
-import { EdgeFlags } from "@fern-docs/utils";
-import { compact } from "es-toolkit/array";
+import { getSeoDisabled } from "@fern-docs/edge-config";
import { Provider as JotaiProvider } from "jotai/react";
import { Metadata, Viewport } from "next/types";
-import tinycolor from "tinycolor2";
import "./globals.scss";
import StyledJsxRegistry from "./registry";
-import { toImageDescriptor } from "./seo";
export default async function DashboardLayout({
children,
}: {
children: React.ReactNode;
}) {
- const docsLoader = await createCachedDocsLoader();
- const config = await docsLoader.getConfig();
- const edgeFlags = await getEdgeFlags(docsLoader.domain);
return (
@@ -32,13 +22,6 @@ export default async function DashboardLayout({
rel="stylesheet"
fetchPriority="low"
/>
-
@@ -52,14 +35,6 @@ export default async function DashboardLayout({
}
export async function generateViewport(): Promise {
- const docsLoader = await createCachedDocsLoader();
- const colors = await docsLoader.getColors();
- const dark = maybeToHex(
- colors.dark?.background ?? colors.dark?.accentPrimary
- );
- const light = maybeToHex(
- colors.light?.background ?? colors.light?.accentPrimary
- );
return {
width: "device-width",
height: "device-height",
@@ -67,129 +42,16 @@ export async function generateViewport(): Promise {
maximumScale: 1,
minimumScale: 1,
userScalable: true,
- themeColor: compact([
- dark ? { color: dark, media: "(prefers-color-scheme: dark)" } : undefined,
- light
- ? { color: light, media: "(prefers-color-scheme: light)" }
- : undefined,
- ]),
};
}
-function maybeToHex(color: RgbaColor | undefined): string | undefined {
- if (color == null) {
- return undefined;
- }
- return tinycolor(color).toHexString();
-}
-
export async function generateMetadata(): Promise {
- const docsLoader = await createCachedDocsLoader();
- const [files, config, baseUrl, isSeoDisabled] = await Promise.all([
- docsLoader.getFiles(),
- docsLoader.getConfig(),
- docsLoader.getBaseUrl(),
- getSeoDisabled(docsLoader.domain),
- ]);
- const noindex = isSeoDisabled || config?.metadata?.noindex || false;
- const nofollow = isSeoDisabled || config?.metadata?.nofollow || false;
+ const domain = getDocsDomainApp();
+ const seoEnabled = !getSeoDisabled(domain);
return {
generator: "https://buildwithfern.com",
- metadataBase: new URL(
- baseUrl.basePath || "/",
- withDefaultProtocol(docsLoader.domain)
- ),
- applicationName: config?.title,
- title: {
- template: config?.title ? "%s | " + config?.title : "%s",
- default: config?.title ?? "Documentation",
- },
- robots: {
- index: !noindex,
- follow: !nofollow,
- },
- openGraph: {
- title: config?.metadata?.["og:title"],
- description: config?.metadata?.["og:description"],
- locale: config?.metadata?.["og:locale"],
- url: config?.metadata?.["og:url"],
- siteName: config?.metadata?.["og:site_name"],
- images: toImageDescriptor(
- files,
- config?.metadata?.["og:image"],
- config?.metadata?.["og:image:width"],
- config?.metadata?.["og:image:height"]
- ),
- },
- twitter: {
- site: config?.metadata?.["twitter:site"],
- creator: config?.metadata?.["twitter:handle"],
- title: config?.metadata?.["twitter:title"],
- description: config?.metadata?.["twitter:description"],
- images: toImageDescriptor(files, config?.metadata?.["twitter:image"]),
- },
- icons: {
- icon: config?.favicon
- ? toImageDescriptor(files, {
- type: "fileId",
- value: config.favicon,
- })?.url
- : undefined,
- },
+ metadataBase: new URL("/", withDefaultProtocol(domain)),
+ robots: { index: seoEnabled, follow: seoEnabled },
};
}
-
-function generatePreloadHrefs(
- typography: DocsV2Read.LoadDocsForUrlResponse["definition"]["config"]["typographyV2"],
- files: Record,
- edgeFlags: EdgeFlags
-): PreloadHref[] {
- const toReturn: PreloadHref[] = [];
-
- const fontVariants = compact([
- typography?.bodyFont?.variants,
- typography?.headingsFont?.variants,
- typography?.codeFont?.variants,
- ]).flat();
-
- fontVariants.forEach((variant) => {
- try {
- const file = files[variant.fontFile];
- if (file != null) {
- toReturn.push({
- href: file.src,
- options: {
- as: "font",
- crossOrigin: "anonymous",
- type: `font/${getFontExtension(file.src)}`,
- },
- });
- }
- } catch {}
- });
-
- if (edgeFlags.isApiPlaygroundEnabled) {
- toReturn.push({
- href: "/api/fern-docs/auth/api-key-injection",
- options: { as: "fetch" },
- });
- }
-
- toReturn.push({
- href: edgeFlags.isSearchV2Enabled
- ? "/api/fern-docs/search/v2/key"
- : "/api/fern-docs/search/v1/key",
- options: { as: "fetch" },
- });
-
- return toReturn;
-}
-
-function getFontExtension(url: string): string {
- const ext = new URL(url).pathname.split(".").pop();
- if (ext == null) {
- throw new Error("No extension found for font: " + url);
- }
- return ext;
-}
diff --git a/packages/fern-docs/bundle/src/components/analytics/CustomerAnalytics.tsx b/packages/fern-docs/bundle/src/components/analytics/CustomerAnalytics.tsx
index 91a62e2fd4..a4151d3156 100644
--- a/packages/fern-docs/bundle/src/components/analytics/CustomerAnalytics.tsx
+++ b/packages/fern-docs/bundle/src/components/analytics/CustomerAnalytics.tsx
@@ -1,3 +1,5 @@
+"use client";
+
import { DocsV1Read } from "@fern-api/fdr-sdk";
import { isEqual } from "es-toolkit/predicate";
import { useAtomValue } from "jotai";
diff --git a/packages/fern-docs/bundle/src/components/analytics/use-track.ts b/packages/fern-docs/bundle/src/components/analytics/use-track.ts
index 365316d2f3..8f5f85f0e7 100644
--- a/packages/fern-docs/bundle/src/components/analytics/use-track.ts
+++ b/packages/fern-docs/bundle/src/components/analytics/use-track.ts
@@ -51,7 +51,7 @@ export function useSafeListenTrackEvents(
console.warn("Error emitting track event", error, event);
}
};
- window.addEventListener(TRACK_EVENT_NAME, handler);
+ window.addEventListener(TRACK_EVENT_NAME, handler, { passive: true });
return () => window.removeEventListener(TRACK_EVENT_NAME, handler);
}, [allowInternal]);
}
diff --git a/packages/fern-docs/bundle/src/components/atoms/apis.ts b/packages/fern-docs/bundle/src/components/atoms/apis.ts
index 14d9b71bf3..7f8581e84d 100644
--- a/packages/fern-docs/bundle/src/components/atoms/apis.ts
+++ b/packages/fern-docs/bundle/src/components/atoms/apis.ts
@@ -1,13 +1,10 @@
import type * as ApiDefinition from "@fern-api/fdr-sdk/api-definition";
import { joiner } from "@fern-api/fdr-sdk/api-definition";
import type * as FernNavigation from "@fern-api/fdr-sdk/navigation";
-import { atom, useAtomValue, useSetAtom } from "jotai";
+import { atom, useSetAtom } from "jotai";
import { atomFamily } from "jotai/utils";
import { useEffect } from "react";
-import { useMemoOne } from "use-memo-one";
import { useIsLocalPreview } from "../contexts/local-preview";
-import { DOCS_ATOM } from "./docs";
-import { RESOLVED_API_DEFINITION_ATOM } from "./navigation";
const SETTABLE_APIS_ATOM = atom<
Record
@@ -72,27 +69,17 @@ export function useIsApiReferencePaginated(): boolean {
}
export function useIsApiReferenceShallowLink(
- node: FernNavigation.WithApiDefinitionId
+ _node: FernNavigation.WithApiDefinitionId
): boolean {
- return useAtomValue(
- useMemoOne(
- () =>
- atom((get) => {
- const isPaginated = true;
- const resolvedApi = get(RESOLVED_API_DEFINITION_ATOM);
- return !isPaginated && resolvedApi?.id === node.apiDefinitionId;
- }),
- [node.apiDefinitionId]
- )
- );
+ return false;
}
export const ENDPOINT_ID_TO_SLUG_ATOM = atom<
Record
->((get) => {
- const { content } = get(DOCS_ATOM);
- if (content.type === "markdown-page") {
- return content.endpointIdsToSlugs;
- }
+>((_get) => {
+ // const { content } = get(DOCS_ATOM);
+ // if (content.type === "markdown-page") {
+ // return content.endpointIdsToSlugs;
+ // }
return {};
});
diff --git a/packages/fern-docs/bundle/src/components/atoms/navigation.ts b/packages/fern-docs/bundle/src/components/atoms/navigation.ts
index 8bf29ae293..4100ea9401 100644
--- a/packages/fern-docs/bundle/src/components/atoms/navigation.ts
+++ b/packages/fern-docs/bundle/src/components/atoms/navigation.ts
@@ -100,7 +100,7 @@ export const CURRENT_NODE_ATOM = atom((get) => {
// this sets the document title to the current node's title when shallow routing
// (this will use the navigation node title rather than the page's actual title)
if (node && typeof window !== "undefined") {
- window.document.title = node.title;
+ // window.document.title = node.title;
}
return node;
});
diff --git a/packages/fern-docs/bundle/src/components/atoms/viewport.ts b/packages/fern-docs/bundle/src/components/atoms/viewport.ts
index 121fd3fcde..73f82d0c06 100644
--- a/packages/fern-docs/bundle/src/components/atoms/viewport.ts
+++ b/packages/fern-docs/bundle/src/components/atoms/viewport.ts
@@ -42,7 +42,7 @@ VIEWPORT_SIZE_ATOM.onMount = (set) => {
set([window.innerWidth, window.innerHeight]);
});
};
- window.addEventListener("resize", handleResize);
+ window.addEventListener("resize", handleResize, { passive: true });
return () => {
window.removeEventListener("resize", handleResize);
};
diff --git a/packages/fern-docs/bundle/src/components/docs/DocsMainContent.tsx b/packages/fern-docs/bundle/src/components/docs/DocsMainContent.tsx
index 247b8443ea..c4b91d6872 100644
--- a/packages/fern-docs/bundle/src/components/docs/DocsMainContent.tsx
+++ b/packages/fern-docs/bundle/src/components/docs/DocsMainContent.tsx
@@ -8,13 +8,13 @@ import { notFound } from "next/navigation";
// import ApiReferencePage from "../api-reference/ApiReferencePage";
import ChangelogEntryPage from "../changelog/ChangelogEntryPage";
// import ChangelogPage from "../changelog/ChangelogPage";
-import React from "react";
import { LayoutEvaluator } from "../layouts/LayoutEvaluator";
import { serializeMdx } from "../mdx/bundlers/mdx-bundler";
import { FernSerializeMdxOptions } from "../mdx/types";
import type { DocsContent } from "../resolver/DocsContent";
export async function DocsMainContent({
+ domain,
node,
parents,
neighbors,
@@ -22,6 +22,7 @@ export async function DocsMainContent({
// apiReferenceNodes,
scope,
}: {
+ domain: string;
node: FernNavigation.NavigationNodePage;
// apiReference: FernNavigation.ApiReferenceNode | undefined;
parents: readonly FernNavigation.NavigationNodeParent[];
@@ -29,7 +30,7 @@ export async function DocsMainContent({
breadcrumb: readonly FernNavigation.BreadcrumbItem[];
scope?: Record;
}) {
- const docsLoader = await createCachedDocsLoader();
+ const docsLoader = await createCachedDocsLoader(domain);
const mdxBundlerFiles = await docsLoader.getMdxBundlerFiles();
const fileResolver = await createFileResolver(docsLoader);
const mdxOptions: FernSerializeMdxOptions = {
@@ -88,14 +89,12 @@ export async function DocsMainContent({
const mdx = await serializeMdx(content.markdown, mdxOptions);
return (
- Loading...}>
-
-
+
);
}
diff --git a/packages/fern-docs/bundle/src/components/docs/DocsPage.tsx b/packages/fern-docs/bundle/src/components/docs/DocsPage.tsx
index 7f9bff2b52..eb375632ab 100644
--- a/packages/fern-docs/bundle/src/components/docs/DocsPage.tsx
+++ b/packages/fern-docs/bundle/src/components/docs/DocsPage.tsx
@@ -3,15 +3,13 @@
import dynamic from "next/dynamic";
import { usePathname } from "next/navigation";
import { ReactElement, useEffect } from "react";
-import { useEdgeFlag, useMessageHandler, useSetJustNavigated } from "../atoms";
+import { useMessageHandler, useSetJustNavigated } from "../atoms";
import { BgImageGradient } from "../components/BgImageGradient";
import { FernErrorBoundary } from "../components/FernErrorBoundary";
import { JavascriptProvider } from "../components/JavascriptProvider";
-import { LinkPreloadApiRoute } from "../components/LinkPreload";
import { useConsoleMessage } from "../hooks/useConsoleMessage";
import { PlaygroundContextProvider } from "../playground/PlaygroundContext";
import { InitializeTheme } from "../themes";
-import { FernTheme, ThemedDocs } from "../themes/ThemedDocs";
import { scrollToRoute } from "../util/anchor";
const SearchDialog = dynamic(
@@ -26,10 +24,8 @@ let timer: number;
export function DocsPage({
children,
- theme,
}: {
children: React.ReactNode;
- theme: FernTheme;
}): ReactElement | null {
useConsoleMessage();
useMessageHandler();
@@ -54,27 +50,12 @@ export function DocsPage({
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [pathname]);
- const isSearchV2Enabled = useEdgeFlag("isSearchV2Enabled");
- const isApiPlaygroundEnabled = useEdgeFlag("isApiPlaygroundEnabled");
-
return (
<>
-
- {isApiPlaygroundEnabled && (
-
- )}
-
- {children}
-
+ {children}
>
diff --git a/packages/fern-docs/bundle/src/components/docs/NextApp.tsx b/packages/fern-docs/bundle/src/components/docs/NextApp.tsx
index 1e89438509..1cb40493f6 100644
--- a/packages/fern-docs/bundle/src/components/docs/NextApp.tsx
+++ b/packages/fern-docs/bundle/src/components/docs/NextApp.tsx
@@ -1,14 +1,10 @@
"use client";
-import { Toaster } from "@fern-docs/components";
import { SyntaxHighlighterEdgeFlagsProvider } from "@fern-docs/syntax-highlighter";
import { ReactElement, ReactNode } from "react";
import { SWRConfig } from "swr";
-import { CustomerAnalytics } from "../analytics/CustomerAnalytics";
import { DocsProps, HydrateAtoms } from "../atoms";
-import { FernErrorBoundary } from "../components/FernErrorBoundary";
import { FeatureFlagProvider } from "../feature-flags/FeatureFlagProvider";
-import { ThemeScript } from "../themes/ThemeScript";
export function NextApp({
children,
@@ -20,19 +16,12 @@ export function NextApp({
return (
-
-
-
-
-
- {children}
-
-
+
+ {children}
+
diff --git a/packages/fern-docs/bundle/src/components/hooks/useIsScrolled.ts b/packages/fern-docs/bundle/src/components/hooks/useIsScrolled.ts
index f11d98156a..19079705bf 100644
--- a/packages/fern-docs/bundle/src/components/hooks/useIsScrolled.ts
+++ b/packages/fern-docs/bundle/src/components/hooks/useIsScrolled.ts
@@ -21,13 +21,13 @@ export function useIsScrolled(ref?: RefObject): boolean {
if (ref?.current) {
const element = ref.current;
- ref.current.addEventListener("scroll", handleScroll);
+ ref.current.addEventListener("scroll", handleScroll, { passive: true });
return () => {
element.removeEventListener("scroll", handleScroll);
};
} else if (typeof window !== "undefined") {
- window.addEventListener("scroll", handleScroll);
+ window.addEventListener("scroll", handleScroll, { passive: true });
return () => {
window.removeEventListener("scroll", handleScroll);
diff --git a/packages/fern-docs/bundle/src/components/hooks/useViewportSize.ts b/packages/fern-docs/bundle/src/components/hooks/useViewportSize.ts
index 25ada19f97..a5f17970cd 100644
--- a/packages/fern-docs/bundle/src/components/hooks/useViewportSize.ts
+++ b/packages/fern-docs/bundle/src/components/hooks/useViewportSize.ts
@@ -18,7 +18,7 @@ export function useViewportSize(): { width: number; height: number } {
handleResize();
- window.addEventListener("resize", handleResize);
+ window.addEventListener("resize", handleResize, { passive: true });
return () => {
window.removeEventListener("resize", handleResize);
};
diff --git a/packages/fern-docs/bundle/src/components/preload.tsx b/packages/fern-docs/bundle/src/components/preload.tsx
index 3476072edb..c4bea71340 100644
--- a/packages/fern-docs/bundle/src/components/preload.tsx
+++ b/packages/fern-docs/bundle/src/components/preload.tsx
@@ -1,5 +1,6 @@
"use client";
+import { useEffect } from "react";
import ReactDOM from "react-dom";
export interface PreloadHref {
@@ -7,10 +8,11 @@ export interface PreloadHref {
options: ReactDOM.PreloadOptions;
}
-export default function Preload({ hrefs }: { hrefs: PreloadHref[] }) {
- hrefs.forEach((href) => {
- ReactDOM.preload(href.href, href.options);
- });
+export default function Preload({ href, options }: PreloadHref) {
+ useEffect(() => {
+ ReactDOM.preload(href, options);
+ // eslint-disable-next-line react-hooks/exhaustive-deps
+ }, [href]);
return false;
}
diff --git a/packages/fern-docs/bundle/src/components/themes/ThemeScript.tsx b/packages/fern-docs/bundle/src/components/themes/ThemeScript.tsx
index 5df8341af7..d5d3037f02 100644
--- a/packages/fern-docs/bundle/src/components/themes/ThemeScript.tsx
+++ b/packages/fern-docs/bundle/src/components/themes/ThemeScript.tsx
@@ -1,3 +1,5 @@
+"use client";
+
import { ColorsThemeConfig } from "@/server/types";
import Script from "next/script";
import type { ReactElement } from "react";
diff --git a/packages/fern-docs/bundle/src/components/themes/ThemedDocs.tsx b/packages/fern-docs/bundle/src/components/themes/ThemedDocs.tsx
index 97587ab744..5f81e48963 100644
--- a/packages/fern-docs/bundle/src/components/themes/ThemedDocs.tsx
+++ b/packages/fern-docs/bundle/src/components/themes/ThemedDocs.tsx
@@ -1,24 +1,20 @@
-import dynamic from "next/dynamic";
+"use server";
+
+import { CohereDocs } from "./cohere/CohereDocs";
+import { DefaultDocs } from "./default/DefaultDocs";
const THEMES = {
- default: dynamic(
- () =>
- import("./default/DefaultDocs").then(({ DefaultDocs }) => DefaultDocs),
- { ssr: true }
- ),
- cohere: dynamic(
- () => import("./cohere/CohereDocs").then(({ CohereDocs }) => CohereDocs),
- { ssr: true }
- ),
+ default: DefaultDocs,
+ cohere: CohereDocs,
};
export type FernTheme = keyof typeof THEMES;
-export function ThemedDocs({
- theme,
+export async function ThemedDocs({
+ theme = "default",
children,
}: {
- theme: FernTheme;
+ theme: FernTheme | undefined;
children: React.ReactNode;
}) {
const Docs = THEMES[theme];
diff --git a/packages/fern-docs/bundle/src/middleware.ts b/packages/fern-docs/bundle/src/middleware.ts
index 61c0fb9512..8c4efd68d9 100644
--- a/packages/fern-docs/bundle/src/middleware.ts
+++ b/packages/fern-docs/bundle/src/middleware.ts
@@ -3,10 +3,13 @@ import { removeLeadingSlash } from "@fern-docs/utils";
import { NextResponse, type NextMiddleware } from "next/server";
import { MARKDOWN_PATTERN, RSS_PATTERN } from "./server/patterns";
import { withPathname } from "./server/withPathname";
+import { getDocsDomainEdge } from "./server/xfernhost/edge";
const API_FERN_DOCS_PATTERN = /^(?!\/api\/fern-docs\/).*(\/api\/fern-docs\/)/;
export const middleware: NextMiddleware = async (request) => {
+ const domain = getDocsDomainEdge(request);
+
let pathname = request.nextUrl.pathname;
/**
@@ -143,7 +146,10 @@ export const middleware: NextMiddleware = async (request) => {
);
}
- return NextResponse.next({ request: { headers } });
+ return NextResponse.rewrite(
+ withPathname(request, `/${domain}/~static${pathname}`),
+ { request: { headers } }
+ );
};
export const config = {
diff --git a/packages/fern-docs/bundle/src/server/DocsLoader.ts b/packages/fern-docs/bundle/src/server/DocsLoader.ts
index c44a445a48..471e61d0a0 100644
--- a/packages/fern-docs/bundle/src/server/DocsLoader.ts
+++ b/packages/fern-docs/bundle/src/server/DocsLoader.ts
@@ -7,7 +7,7 @@ import * as FernNavigation from "@fern-api/fdr-sdk/navigation";
import type { AuthEdgeConfig } from "@fern-docs/auth";
import { ApiDefinitionLoader } from "@fern-docs/cache";
import { getAuthEdgeConfig } from "@fern-docs/edge-config";
-import { getAuthState, type AuthState } from "./auth/getAuthState";
+import { createGetAuthState, type AuthState } from "./auth/getAuthState";
import { loadWithUrl } from "./loadWithUrl";
import { pruneWithAuthState } from "./withRbac";
@@ -65,13 +65,12 @@ export class DocsLoader {
return [this.authState, this.authConfig];
}
return [
- await getAuthState(
+ await createGetAuthState(
this.domain,
this.host,
this.fernToken,
- undefined,
this.authConfig
- ),
+ ).then(({ getAuthState }) => getAuthState()),
this.authConfig,
];
}
diff --git a/packages/fern-docs/bundle/src/server/auth/getAuthState.ts b/packages/fern-docs/bundle/src/server/auth/getAuthState.ts
index 7917100fe2..70c7df6004 100644
--- a/packages/fern-docs/bundle/src/server/auth/getAuthState.ts
+++ b/packages/fern-docs/bundle/src/server/auth/getAuthState.ts
@@ -11,6 +11,7 @@ import urlJoin from "url-join";
import { safeVerifyFernJWTConfig } from "./FernJWT";
import { getAllowedRedirectUrls } from "./allowed-redirects";
import { getOrgMetadataForDomain } from "./metadata-for-url";
+import { getOrigin } from "./origin";
import { getOryAuthorizationUrl } from "./ory";
import { getReturnToQueryParam } from "./return-to";
import { getWebflowAuthorizationUrl } from "./webflow";
@@ -25,11 +26,6 @@ export interface DomainAndHost {
*/
domain: string;
- /**
- * The host of the request
- */
- host: string;
-
/**
* allowed destinations for redirects
*/
@@ -79,13 +75,11 @@ export type AuthState = NotLoggedIn | IsLoggedIn;
* @internal visible for testing
*/
export async function getAuthStateInternal({
- host,
fernToken,
authConfig,
previewAuthConfig,
setFernToken,
}: {
- host: string;
fernToken: string | undefined;
authConfig?: AuthEdgeConfig;
previewAuthConfig?: PreviewUrlAuth;
@@ -99,7 +93,6 @@ export async function getAuthStateInternal({
handleWorkosAuth({
fernToken,
organization: previewAuthConfig.org,
- host,
pathname,
setFernToken,
});
@@ -127,7 +120,7 @@ export async function getAuthStateInternal({
return (pathname) => ({
authed: false,
ok: true,
- authorizationUrl: getAuthorizationUrl(authConfig, host, pathname),
+ authorizationUrl: getAuthorizationUrl(authConfig, pathname),
partner,
});
}
@@ -139,7 +132,6 @@ export async function getAuthStateInternal({
handleWorkosAuth({
fernToken,
organization: authConfig.organization,
- host,
pathname,
setFernToken,
authorizationUrl: {
@@ -170,7 +162,6 @@ export async function getAuthStateInternal({
*/
export async function createGetAuthState(
domain: string,
- host: string,
fernToken: string | undefined,
authConfig?: AuthEdgeConfig,
setFernToken?: (token: string) => void
@@ -187,7 +178,6 @@ export async function createGetAuthState(
: undefined;
const getAuthState = await getAuthStateInternal({
- host,
fernToken,
authConfig,
setFernToken,
@@ -201,7 +191,6 @@ export async function createGetAuthState(
return {
domain,
- host,
allowedDestinations,
getAuthState,
};
@@ -209,13 +198,13 @@ export async function createGetAuthState(
function getAuthorizationUrl(
authConfig: AuthEdgeConfig,
- host: string,
pathname?: string
): string | undefined {
+ const origin = getOrigin();
// TODO: this is currently not a correct implementation of the state parameter b/c it should be signed w/ the jwt secret
// however, we should not break existing customers who are consuming the state as a `return_to` param in their auth flows.
const state = urlJoin(
- removeTrailingSlash(withDefaultProtocol(host)),
+ removeTrailingSlash(withDefaultProtocol(origin)),
pathname ?? ""
);
@@ -225,7 +214,7 @@ function getAuthorizationUrl(
// note: `redirect` is allowed to override the default redirect uri, and the `return_to` param
if (!destination.searchParams.has("redirect_uri")) {
const redirectUri = urlJoin(
- removeTrailingSlash(withDefaultProtocol(host)),
+ removeTrailingSlash(origin),
"/api/fern-docs/auth/jwt/callback"
);
@@ -237,7 +226,7 @@ function getAuthorizationUrl(
return destination.toString();
} else if (authConfig.type === "sso" && authConfig.partner === "workos") {
const redirectUri = urlJoin(
- removeTrailingSlash(withDefaultProtocol(host)),
+ removeTrailingSlash(origin),
"/api/fern-docs/auth/sso/callback"
);
return getWorkosSSOAuthorizationUrl({
@@ -254,7 +243,7 @@ function getAuthorizationUrl(
return getWebflowAuthorizationUrl(authConfig, {
state,
redirectUri: urlJoin(
- removeTrailingSlash(withDefaultProtocol(host)),
+ removeTrailingSlash(origin),
"/api/fern-docs/oauth/webflow/callback"
),
});
@@ -262,7 +251,7 @@ function getAuthorizationUrl(
return getOryAuthorizationUrl(authConfig, {
state,
redirectUri: urlJoin(
- removeTrailingSlash(withDefaultProtocol(host)),
+ removeTrailingSlash(origin),
"/api/fern-docs/oauth/ory/callback"
),
});
diff --git a/packages/fern-docs/bundle/src/server/auth/getAuthStateEdge.ts b/packages/fern-docs/bundle/src/server/auth/getAuthStateEdge.ts
index 72033bb654..d99e9fee96 100644
--- a/packages/fern-docs/bundle/src/server/auth/getAuthStateEdge.ts
+++ b/packages/fern-docs/bundle/src/server/auth/getAuthStateEdge.ts
@@ -1,26 +1,18 @@
import { COOKIE_FERN_TOKEN } from "@fern-docs/utils";
import { NextRequest } from "next/server";
import { getDocsDomainEdge, getHostEdge } from "../xfernhost/edge";
-import { getAuthState } from "./getAuthState";
+import { createGetAuthState } from "./getAuthState";
/**
* @param request - the request to check the headers / cookies
* @param pathname - the pathname to check the auth config against. The pathname MUST be provided in the middleware.
*/
-export async function getAuthStateEdge(
+export async function createGetAuthStateEdge(
request: NextRequest,
- pathname?: string,
setFernToken?: (token: string) => void
-): ReturnType {
+): ReturnType {
const domain = getDocsDomainEdge(request);
const host = getHostEdge(request);
const fern_token = request.cookies.get(COOKIE_FERN_TOKEN)?.value;
- return getAuthState(
- domain,
- host,
- fern_token,
- pathname,
- undefined,
- setFernToken
- );
+ return createGetAuthState(domain, host, fern_token, undefined, setFernToken);
}
diff --git a/packages/fern-docs/bundle/src/server/auth/getAuthStateNode.ts b/packages/fern-docs/bundle/src/server/auth/getAuthStateNode.ts
deleted file mode 100644
index 922a5d9ce6..0000000000
--- a/packages/fern-docs/bundle/src/server/auth/getAuthStateNode.ts
+++ /dev/null
@@ -1,18 +0,0 @@
-import { COOKIE_FERN_TOKEN } from "@fern-docs/utils";
-import { NextApiRequest } from "next";
-import { getDocsDomainNode, getHostNode } from "../xfernhost/node";
-import { getAuthState } from "./getAuthState";
-
-/**
- * @param request - the request to check the headers / cookies
- * @param pathname - the pathname to check the auth config against.
- */
-export async function getAuthStateNode(
- request: NextApiRequest,
- pathname?: string
-): ReturnType {
- const domain = getDocsDomainNode(request);
- const host = getHostNode(request) ?? domain;
- const fern_token = request.cookies[COOKIE_FERN_TOKEN];
- return getAuthState(domain, host, fern_token, pathname);
-}
diff --git a/packages/fern-docs/bundle/src/server/auth/origin.ts b/packages/fern-docs/bundle/src/server/auth/origin.ts
new file mode 100644
index 0000000000..0649f744a3
--- /dev/null
+++ b/packages/fern-docs/bundle/src/server/auth/origin.ts
@@ -0,0 +1,13 @@
+import { withDefaultProtocol } from "@fern-api/ui-core-utils";
+import { getEnv } from "@vercel/functions";
+import { headers } from "next/headers";
+
+export function getOrigin() {
+ const { VERCEL_ENV } = getEnv();
+ return withDefaultProtocol(
+ (VERCEL_ENV === "preview" ? headers().get("host") : undefined) ||
+ headers().get("x-fern-host") ||
+ headers().get("host") ||
+ ""
+ );
+}
diff --git a/packages/fern-docs/bundle/src/server/auth/workos-handler.ts b/packages/fern-docs/bundle/src/server/auth/workos-handler.ts
index f57fa1ab55..52955f3518 100644
--- a/packages/fern-docs/bundle/src/server/auth/workos-handler.ts
+++ b/packages/fern-docs/bundle/src/server/auth/workos-handler.ts
@@ -1,7 +1,7 @@
-import { withDefaultProtocol } from "@fern-api/ui-core-utils";
import { removeTrailingSlash } from "@fern-docs/utils";
import urlJoin from "url-join";
import { AuthState, getWorkosRbacRoles } from "./getAuthState";
+import { getOrigin } from "./origin";
import { getWorkosSSOAuthorizationUrl } from "./workos";
import {
encryptSession,
@@ -14,7 +14,6 @@ import { toFernUser } from "./workos-user-to-fern-user";
interface WorkosAuthParams {
fernToken: string | undefined;
organization: string;
- host: string;
pathname?: string;
setFernToken?: (token: string) => void;
authorizationUrl?: {
@@ -28,15 +27,12 @@ interface WorkosAuthParams {
export async function handleWorkosAuth({
fernToken,
organization,
- host,
pathname,
setFernToken,
authorizationUrl,
}: WorkosAuthParams): Promise {
- const state = urlJoin(
- removeTrailingSlash(withDefaultProtocol(host)),
- pathname ?? ""
- );
+ const origin = getOrigin();
+ const state = urlJoin(removeTrailingSlash(origin), pathname ?? "");
const session =
fernToken != null ? await getSessionFromToken(fernToken) : undefined;
const workosUserInfo = await toSessionUserInfo(session);
@@ -74,10 +70,7 @@ export async function handleWorkosAuth({
}
const redirectUri = String(
- new URL(
- "/api/fern-docs/auth/sso/callback",
- withDefaultProtocol(process.env.NEXT_PUBLIC_CDN_URI ?? host)
- )
+ new URL("/api/fern-docs/auth/sso/callback", origin)
);
const authorizationUrlParams = getWorkosSSOAuthorizationUrl({
redirectUri,
diff --git a/packages/fern-docs/bundle/src/server/docs-loader.ts b/packages/fern-docs/bundle/src/server/docs-loader.ts
index b32a28f025..77e355e681 100644
--- a/packages/fern-docs/bundle/src/server/docs-loader.ts
+++ b/packages/fern-docs/bundle/src/server/docs-loader.ts
@@ -17,10 +17,8 @@ import { AuthState, createGetAuthState } from "./auth/getAuthState";
import { loadWithUrl } from "./loadWithUrl";
import { ColorsThemeConfig, FileData, RgbaColor } from "./types";
import { pruneWithAuthState } from "./withRbac";
-import { withServerProps } from "./withServerProps";
export interface DocsLoader {
- host: string;
domain: string;
fern_token: string | undefined;
authConfig: AuthEdgeConfig | undefined;
@@ -101,28 +99,22 @@ export interface DocsLoader {
/**
* Force cache the loadWithUrl call, so that `JSON.parse()` is called only once.
*/
-export const createCachedDocsLoader = async (): Promise => {
- const { domain, host, fern_token } = withServerProps();
+export const createCachedDocsLoader = async (
+ domain: string,
+ fern_token?: string
+): Promise => {
const authConfig = await getAuthEdgeConfig(domain);
const { getAuthState } = await createGetAuthState(
domain,
- host,
fern_token,
authConfig
);
- return new CachedDocsLoaderImpl(
- domain,
- host,
- fern_token,
- authConfig,
- getAuthState
- );
+ return new CachedDocsLoaderImpl(domain, fern_token, authConfig, getAuthState);
};
class CachedDocsLoaderImpl implements DocsLoader {
constructor(
private _domain: string,
- private _host: string,
private _fern_token: string | undefined,
private _authConfig: AuthEdgeConfig | undefined,
private _getAuthState: (pathname?: string) => AsyncOrSync
@@ -132,10 +124,6 @@ class CachedDocsLoaderImpl implements DocsLoader {
return this._domain;
}
- public get host() {
- return this._host;
- }
-
public get fern_token() {
return this._fern_token;
}
diff --git a/packages/fern-docs/bundle/src/server/env-variables.ts b/packages/fern-docs/bundle/src/server/env-variables.ts
index b832731f50..93ed837b9d 100644
--- a/packages/fern-docs/bundle/src/server/env-variables.ts
+++ b/packages/fern-docs/bundle/src/server/env-variables.ts
@@ -10,7 +10,7 @@ export function algoliaSearchApikey(): string {
return getEnvVariable("ALGOLIA_SEARCH_API_KEY");
}
-export function fernToken(): string {
+export function fernToken_admin(): string {
return getEnvVariable("FERN_TOKEN");
}
diff --git a/packages/fern-docs/bundle/src/server/registry.ts b/packages/fern-docs/bundle/src/server/registry.ts
index 017689a0ab..78787617f5 100644
--- a/packages/fern-docs/bundle/src/server/registry.ts
+++ b/packages/fern-docs/bundle/src/server/registry.ts
@@ -1,6 +1,6 @@
import { FdrClient } from "@fern-api/fdr-sdk/client";
import { once } from "es-toolkit/function";
-import { fernToken } from "./env-variables";
+import { fernToken_admin } from "./env-variables";
function getEnvironment() {
return (
@@ -9,5 +9,6 @@ function getEnvironment() {
}
export const provideRegistryService = once(
- () => new FdrClient({ environment: getEnvironment(), token: fernToken() })
+ () =>
+ new FdrClient({ environment: getEnvironment(), token: fernToken_admin() })
);
diff --git a/packages/fern-docs/bundle/src/server/withMiddlewareAuth.ts b/packages/fern-docs/bundle/src/server/withMiddlewareAuth.ts
index edf15d3d1a..de937590b2 100644
--- a/packages/fern-docs/bundle/src/server/withMiddlewareAuth.ts
+++ b/packages/fern-docs/bundle/src/server/withMiddlewareAuth.ts
@@ -3,7 +3,7 @@ import { FernUser } from "@fern-docs/auth";
import { COOKIE_FERN_TOKEN } from "@fern-docs/utils";
import { NextRequest, NextResponse } from "next/server";
import { FernNextResponse } from "./FernNextResponse";
-import { getAuthStateEdge } from "./auth/getAuthStateEdge";
+import { createGetAuthStateEdge } from "./auth/getAuthStateEdge";
import { withSecureCookie } from "./auth/with-secure-cookie";
import { getHostEdge } from "./xfernhost/edge";
@@ -18,9 +18,14 @@ export async function withMiddlewareAuth(
next: (isLoggedIn: boolean, user: FernUser | undefined) => NextResponse
): Promise {
let fernToken: string | undefined;
- const res = await getAuthStateEdge(request, pathname, (token: string) => {
- fernToken = token;
- });
+ const { getAuthState, allowedDestinations } = await createGetAuthStateEdge(
+ request,
+ (token: string) => {
+ fernToken = token;
+ }
+ );
+
+ const res = await getAuthState(pathname);
if (res.authed) {
const response = next(true, res.user);
@@ -44,7 +49,7 @@ export async function withMiddlewareAuth(
if (res.authorizationUrl) {
return FernNextResponse.redirect(request, {
destination: res.authorizationUrl,
- allowedDestinations: res.allowedDestinations,
+ allowedDestinations,
});
}
diff --git a/packages/fern-docs/components/src/FernToast.tsx b/packages/fern-docs/components/src/FernToast.tsx
index 56739f5a46..ec9ae1af96 100644
--- a/packages/fern-docs/components/src/FernToast.tsx
+++ b/packages/fern-docs/components/src/FernToast.tsx
@@ -1,3 +1,5 @@
+"use client";
+
import {
CheckCircle,
InfoCircle,
diff --git a/packages/fern-docs/search-ui/src/server/env-variables.ts b/packages/fern-docs/search-ui/src/server/env-variables.ts
index b817efafdf..48ceb74e48 100644
--- a/packages/fern-docs/search-ui/src/server/env-variables.ts
+++ b/packages/fern-docs/search-ui/src/server/env-variables.ts
@@ -12,7 +12,7 @@ export function algoliaSearchApikey(): string {
return getEnvVariable("ALGOLIA_SEARCH_API_KEY");
}
-export function fernToken(): string {
+export function fernToken_admin(): string {
return getEnvVariable("FERN_TOKEN");
}
diff --git a/packages/fern-docs/search-ui/src/server/run-reindex-algolia.ts b/packages/fern-docs/search-ui/src/server/run-reindex-algolia.ts
index 1f85de6c2c..adb243e72f 100644
--- a/packages/fern-docs/search-ui/src/server/run-reindex-algolia.ts
+++ b/packages/fern-docs/search-ui/src/server/run-reindex-algolia.ts
@@ -8,13 +8,12 @@ import {
algoliaAppId,
algoliaWriteApiKey,
fdrEnvironment,
- fernToken,
+ fernToken_admin,
} from "./env-variables";
export const runReindexAlgolia = async (
domain: string
): Promise => {
- // eslint-disable-next-line no-console
console.time("reindexing");
await algoliaIndexSettingsTask({
@@ -28,11 +27,10 @@ export const runReindexAlgolia = async (
writeApiKey: algoliaWriteApiKey(),
indexName: SEARCH_INDEX,
environment: fdrEnvironment(),
- fernToken: fernToken(),
+ fernToken: fernToken_admin(),
domain,
});
- // eslint-disable-next-line no-console
console.timeEnd("reindexing");
return response;
diff --git a/packages/fern-docs/search-ui/src/server/run-reindex-turbopuffer.ts b/packages/fern-docs/search-ui/src/server/run-reindex-turbopuffer.ts
index f9f57b6416..8a8a2cc1b1 100644
--- a/packages/fern-docs/search-ui/src/server/run-reindex-turbopuffer.ts
+++ b/packages/fern-docs/search-ui/src/server/run-reindex-turbopuffer.ts
@@ -5,7 +5,11 @@ import {
turbopufferUpsertTask,
} from "@fern-docs/search-server/turbopuffer";
import { embed, embedMany } from "ai";
-import { fdrEnvironment, fernToken, turbopufferApiKey } from "./env-variables";
+import {
+ fdrEnvironment,
+ fernToken_admin,
+ turbopufferApiKey,
+} from "./env-variables";
const openai = createOpenAI({
apiKey: process.env.OPENAI_API_KEY,
@@ -21,7 +25,7 @@ export const runReindexTurbopuffer = async (
namespace: `${domain}_${model.modelId}`,
payload: {
environment: fdrEnvironment(),
- fernToken: fernToken(),
+ fernToken: fernToken_admin(),
domain,
},
vectorizer: async (chunks) => {
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index d25b917a09..1bc63a63c4 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -13,7 +13,7 @@ overrides:
elliptic: 6.6.0
esbuild: 0.24.2
eslint: 9.17.0
- eslint-config-next: 15.1.2
+ eslint-config-next: 14.2.23
instantsearch.js: 4.75.4
jsonpath-plus: 10.0.7
markdown-to-jsx: 7.4.0
@@ -109,8 +109,8 @@ importers:
specifier: 9.17.0
version: 9.17.0(jiti@1.21.7)
eslint-config-next:
- specifier: 15.1.2
- version: 15.1.2(eslint@9.17.0(jiti@1.21.7))(typescript@5.7.2)
+ specifier: 14.2.23
+ version: 14.2.23(eslint@9.17.0(jiti@1.21.7))(typescript@5.7.2)
eslint-config-prettier:
specifier: ^9.1.0
version: 9.1.0(eslint@9.17.0(jiti@1.21.7))
@@ -834,6 +834,9 @@ importers:
'@upstash/qstash':
specifier: ^2.7.16
version: 2.7.16
+ '@vercel/functions':
+ specifier: ^2.0.0
+ version: 2.0.0(@aws-sdk/credential-provider-web-identity@3.723.0(@aws-sdk/client-sts@3.682.0))
'@vercel/kv':
specifier: ^2.0.0
version: 2.0.0
@@ -5153,8 +5156,8 @@ packages:
'@next/env@15.1.2':
resolution: {integrity: sha512-Hm3jIGsoUl6RLB1vzY+dZeqb+/kWPZ+h34yiWxW0dV87l8Im/eMOwpOA+a0L78U0HM04syEjXuRlCozqpwuojQ==}
- '@next/eslint-plugin-next@15.1.2':
- resolution: {integrity: sha512-sgfw3+WdaYOGPKCvM1L+UucBmRfh8V2Ygefp7ELON0+0vY7uohQwXXnVWg3rY7mXDKharQR3o7uedpfvnU2hlQ==}
+ '@next/eslint-plugin-next@14.2.23':
+ resolution: {integrity: sha512-efRC7m39GoiU1fXZRgGySqYbQi6ZyLkuGlvGst7IwkTTczehQTJA/7PoMg4MMjUZvZEGpiSEu+oJBAjPawiC3Q==}
'@next/swc-darwin-arm64@14.2.23':
resolution: {integrity: sha512-WhtEntt6NcbABA8ypEoFd3uzq5iAnrl9AnZt9dXdO+PZLACE32z3a3qA5OoV20JrbJfSJ6Sd6EqGZTrlRnGxQQ==}
@@ -7945,6 +7948,15 @@ packages:
'@aws-sdk/credential-provider-web-identity':
optional: true
+ '@vercel/functions@2.0.0':
+ resolution: {integrity: sha512-BSwIihLHoV18gerKZJyGuqd3rtaYM6rJvET1kOwKktshucyaHXTJel7Cxegs+sdX0NZqsX4LO2MFnMU2jG01Cw==}
+ engines: {node: '>= 18'}
+ peerDependencies:
+ '@aws-sdk/credential-provider-web-identity': '*'
+ peerDependenciesMeta:
+ '@aws-sdk/credential-provider-web-identity':
+ optional: true
+
'@vercel/kv@2.0.0':
resolution: {integrity: sha512-zdVrhbzZBYo5d1Hfn4bKtqCeKf0FuzW8rSHauzQVMUgv1+1JOwof2mWcBuI+YMJy8s0G0oqAUfQ7HgUDzb8EbA==}
engines: {node: '>=14.6'}
@@ -10435,8 +10447,8 @@ packages:
resolution: {integrity: sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw==}
engines: {node: '>=12'}
- eslint-config-next@15.1.2:
- resolution: {integrity: sha512-PrMm1/4zWSJ689wd/ypWIR5ZF1uvmp3EkgpgBV1Yu6PhEobBjXMGgT8bVNelwl17LXojO8D5ePFRiI4qXjsPRA==}
+ eslint-config-next@14.2.23:
+ resolution: {integrity: sha512-qtWJzOsDZxnLtXLNtnVjbutHmnEp6QTTSZBTlTCge/Wy0AsUaq8nwR91dBcZZvFg3eY3zKFPBhUkLMHu3Qpauw==}
peerDependencies:
eslint: 9.17.0
typescript: 5.7.2
@@ -10514,6 +10526,12 @@ packages:
peerDependencies:
eslint: 9.17.0
+ eslint-plugin-react-hooks@5.0.0-canary-7118f5dd7-20230705:
+ resolution: {integrity: sha512-AZYbMo/NW9chdL7vk6HQzQhT+PvTAEVqWk9ziruUoW2kAOcN5qNyelv70e0F1VNQAbvutOC9oc+xfWycI9FxDw==}
+ engines: {node: '>=10'}
+ peerDependencies:
+ eslint: 9.17.0
+
eslint-plugin-react-hooks@5.1.0:
resolution: {integrity: sha512-mpJRtPgHN2tNAvZ35AMfqeB3Xqeo273QxrHJsbBEPWODRM4r0yB6jfoROqKEYrOn27UtRPpcpHc2UqyBSuUNTw==}
engines: {node: '>=10'}
@@ -10765,10 +10783,6 @@ packages:
fast-fifo@1.3.2:
resolution: {integrity: sha512-/d9sfos4yxzpwkDkuN7k2SqFKtYNmCTzgfEpz82x34IM9/zc8KGxQoXg1liNC/izpRM/MBdt44Nmx41ZWqk+FQ==}
- fast-glob@3.3.1:
- resolution: {integrity: sha512-kNFPyjhh5cKjrUltxs+wFx+ZkbRaxxmZ+X0ZU31SOsxCEtP9VPgtq2teZw1DebupL5GmDaNQ6yKMMVcM41iqDg==}
- engines: {node: '>=8.6.0'}
-
fast-glob@3.3.2:
resolution: {integrity: sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow==}
engines: {node: '>=8.6.0'}
@@ -11187,6 +11201,11 @@ packages:
glob-to-regexp@0.4.1:
resolution: {integrity: sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw==}
+ glob@10.3.10:
+ resolution: {integrity: sha512-fa46+tv1Ak0UPK1TOy/pZrIybNNt4HCv7SDzwyfiOZkvZLEbjsZkJBPtDHVshZjbecAoAGSC20MjLDG/qr679g==}
+ engines: {node: '>=16 || 14 >=14.17'}
+ hasBin: true
+
glob@10.3.14:
resolution: {integrity: sha512-4fkAqu93xe9Mk7le9v0y3VrPDqLKHarNi2s4Pv7f2yOvfhWfhc7hRPHC/JyqMqb8B/Dt/eGS4n7ykwf3fOsl8g==}
engines: {node: '>=16 || 14 >=14.17'}
@@ -20869,9 +20888,9 @@ snapshots:
'@next/env@15.1.2': {}
- '@next/eslint-plugin-next@15.1.2':
+ '@next/eslint-plugin-next@14.2.23':
dependencies:
- fast-glob: 3.3.1
+ glob: 10.3.10
'@next/swc-darwin-arm64@14.2.23':
optional: true
@@ -24506,6 +24525,10 @@ snapshots:
optionalDependencies:
'@aws-sdk/credential-provider-web-identity': 3.723.0(@aws-sdk/client-sts@3.682.0)
+ '@vercel/functions@2.0.0(@aws-sdk/credential-provider-web-identity@3.723.0(@aws-sdk/client-sts@3.682.0))':
+ optionalDependencies:
+ '@aws-sdk/credential-provider-web-identity': 3.723.0(@aws-sdk/client-sts@3.682.0)
+
'@vercel/kv@2.0.0':
dependencies:
'@upstash/redis': 1.34.0
@@ -28101,9 +28124,9 @@ snapshots:
escape-string-regexp@5.0.0: {}
- eslint-config-next@15.1.2(eslint@9.17.0(jiti@1.21.7))(typescript@5.7.2):
+ eslint-config-next@14.2.23(eslint@9.17.0(jiti@1.21.7))(typescript@5.7.2):
dependencies:
- '@next/eslint-plugin-next': 15.1.2
+ '@next/eslint-plugin-next': 14.2.23
'@rushstack/eslint-patch': 1.10.4
'@typescript-eslint/eslint-plugin': 8.18.1(@typescript-eslint/parser@8.18.1(eslint@9.17.0(jiti@1.21.7))(typescript@5.7.2))(eslint@9.17.0(jiti@1.21.7))(typescript@5.7.2)
'@typescript-eslint/parser': 8.18.1(eslint@9.17.0(jiti@1.21.7))(typescript@5.7.2)
@@ -28113,7 +28136,7 @@ snapshots:
eslint-plugin-import: 2.31.0(@typescript-eslint/parser@8.18.1(eslint@9.17.0(jiti@1.21.7))(typescript@5.7.2))(eslint-import-resolver-typescript@3.7.0)(eslint@9.17.0(jiti@1.21.7))
eslint-plugin-jsx-a11y: 6.10.2(eslint@9.17.0(jiti@1.21.7))
eslint-plugin-react: 7.37.2(eslint@9.17.0(jiti@1.21.7))
- eslint-plugin-react-hooks: 5.1.0(eslint@9.17.0(jiti@1.21.7))
+ eslint-plugin-react-hooks: 5.0.0-canary-7118f5dd7-20230705(eslint@9.17.0(jiti@1.21.7))
optionalDependencies:
typescript: 5.7.2
transitivePeerDependencies:
@@ -28133,7 +28156,7 @@ snapshots:
eslint-import-resolver-node@0.3.9:
dependencies:
debug: 3.2.7
- is-core-module: 2.13.1
+ is-core-module: 2.16.0
resolve: 1.22.8
transitivePeerDependencies:
- supports-color
@@ -28223,6 +28246,10 @@ snapshots:
safe-regex-test: 1.1.0
string.prototype.includes: 2.0.1
+ eslint-plugin-react-hooks@5.0.0-canary-7118f5dd7-20230705(eslint@9.17.0(jiti@1.21.7)):
+ dependencies:
+ eslint: 9.17.0(jiti@1.21.7)
+
eslint-plugin-react-hooks@5.1.0(eslint@9.17.0(jiti@1.21.7)):
dependencies:
eslint: 9.17.0(jiti@1.21.7)
@@ -28589,14 +28616,6 @@ snapshots:
fast-fifo@1.3.2: {}
- fast-glob@3.3.1:
- dependencies:
- '@nodelib/fs.stat': 2.0.5
- '@nodelib/fs.walk': 1.2.8
- glob-parent: 5.1.2
- merge2: 1.4.1
- micromatch: 4.0.8
-
fast-glob@3.3.2:
dependencies:
'@nodelib/fs.stat': 2.0.5
@@ -29036,6 +29055,14 @@ snapshots:
glob-to-regexp@0.4.1: {}
+ glob@10.3.10:
+ dependencies:
+ foreground-child: 3.1.1
+ jackspeak: 2.3.6
+ minimatch: 9.0.4
+ minipass: 7.1.1
+ path-scurry: 1.11.0
+
glob@10.3.14:
dependencies:
foreground-child: 3.1.1