diff --git a/apps/api/package.json b/apps/api/package.json
index 1589cb6fe..3e58cb7dd 100644
--- a/apps/api/package.json
+++ b/apps/api/package.json
@@ -29,7 +29,7 @@
"drizzle-kit": "1.0.0-rc.4",
"drizzle-orm": "1.0.0-rc.4",
"hono": "^4.12.31",
- "next-intl": "^4.13.3",
+ "next-intl": "^4.13.7",
"react": "^19.2.8",
"react-dom": "^19.2.8",
"ws": "^8.21.1",
diff --git a/apps/api/src/vitnode.api.config.ts b/apps/api/src/vitnode.api.config.ts
index 2e6f4a5fe..559bbe765 100644
--- a/apps/api/src/vitnode.api.config.ts
+++ b/apps/api/src/vitnode.api.config.ts
@@ -1,5 +1,6 @@
import { google } from "@ai-sdk/google";
import { blogApiPlugin } from "@vitnode/blog/config.api";
+import { coreRelations } from "@vitnode/core/database/relations";
// import { LocalStorageAdapter } from "@vitnode/core/api/adapters/storage/local";
import { buildApiConfig } from "@vitnode/core/vitnode.config";
import { exampleApiPlugin } from "@vitnode/example/config.api";
@@ -8,7 +9,6 @@ import { NodemailerEmailAdapter } from "@vitnode/nodemailer";
// import { S3StorageAdapter } from "@vitnode/s3";
import { SupabaseStorageAdapter } from "@vitnode/supabase-storage";
import { config } from "dotenv";
-import { coreRelations } from "@vitnode/core/database/relations";
import { drizzle } from "drizzle-orm/postgres-js";
config({
diff --git a/apps/docs/next.config.ts b/apps/docs/next.config.ts
index 7cbc1bd40..e16d6c775 100644
--- a/apps/docs/next.config.ts
+++ b/apps/docs/next.config.ts
@@ -3,16 +3,36 @@ import { vitNodeNextConfig } from "@vitnode/core/config/next.config";
import { createMDX } from "fumadocs-mdx/next";
import type { NextConfig } from "next";
+import { i18n } from "./src/i18n";
+
const withMDX = createMDX();
const withBundleAnalyzer = nextAnalyzer({
enabled: process.env.ANALYZE === "true",
});
+/**
+ * `/docs` has no section of its own, so it forwards to the first one.
+ *
+ * Handled here rather than in the page: the page reads its slug inside a
+ * `` boundary now, so a redirect decided there would stream after a
+ * 200 instead of being a real HTTP redirect. `localePrefix` is `as-needed`, so
+ * the default locale is unprefixed and every other locale gets its own entry.
+ */
+const docsIndexRedirects = [
+ "",
+ ...i18n.locales
+ .filter(locale => locale.code !== i18n.defaultLocale)
+ .map(locale => `/${locale.code}`),
+].map(prefix => ({
+ source: `${prefix}/docs`,
+ destination: `${prefix}/docs/dev`,
+ permanent: false,
+}));
+
const nextConfig: NextConfig = {
reactCompiler: true,
- // cacheComponents: true,
- // partialPrefetching: true,
+ redirects: async () => docsIndexRedirects,
};
export default withBundleAnalyzer(withMDX(vitNodeNextConfig(nextConfig)));
diff --git a/apps/docs/package.json b/apps/docs/package.json
index 91e2c6dc5..fbddada73 100644
--- a/apps/docs/package.json
+++ b/apps/docs/package.json
@@ -40,8 +40,8 @@
"hono": "^4.12.31",
"lucide-react": "^1.25.0",
"motion": "^12.42.2",
- "next": "16.3.0-preview.9",
- "next-intl": "^4.13.3",
+ "next": "16.3.1",
+ "next-intl": "^4.13.7",
"react": "^19.2.8",
"react-dom": "^19.2.8",
"react-hook-form": "^7.82.0",
@@ -50,7 +50,7 @@
"sonner": "^2.0.7"
},
"devDependencies": {
- "@next/bundle-analyzer": "^16.2.11",
+ "@next/bundle-analyzer": "^16.3.1",
"@playwright/test": "^1.61.1",
"@react-email/ui": "^6.9.0",
"@tailwindcss/postcss": "^4.3.3",
diff --git a/apps/docs/src/app/[locale]/(docs)/docs/[[...slug]]/page.tsx b/apps/docs/src/app/[locale]/(docs)/docs/[[...slug]]/page.tsx
index 542e0d724..c91d08121 100644
--- a/apps/docs/src/app/[locale]/(docs)/docs/[[...slug]]/page.tsx
+++ b/apps/docs/src/app/[locale]/(docs)/docs/[[...slug]]/page.tsx
@@ -1,25 +1,31 @@
import type { Metadata } from "next";
-import { redirect } from "@vitnode/core/lib/navigation";
+import { Skeleton } from "@vitnode/core/components/ui/skeleton";
import { getBreadcrumbItems } from "fumadocs-core/breadcrumb";
import { Step, Steps } from "fumadocs-ui/components/steps";
import defaultMdxComponents from "fumadocs-ui/mdx";
import { DocsBody, DocsPage } from "fumadocs-ui/page";
import { notFound } from "next/navigation";
+import { Suspense } from "react";
import { Preview } from "@/components/fumadocs/preview";
import { source } from "@/lib/source";
import { ViewOptions } from "./page.client";
-export default async function Page(
- props: PageProps<"/[locale]/docs/[[...slug]]">,
-) {
- const params = await props.params;
- if (!params.slug) {
- await redirect("/docs/dev");
- }
- const page = source.getPage(params.slug);
+/**
+ * Everything on a docs page comes from the slug - title, description, table of
+ * contents and body - so this is where `params` is read.
+ *
+ * Reading it here instead of in the page body is what keeps the fumadocs
+ * sidebar and nav in the App Shell: they are identical for every docs URL, so
+ * one prefetch is shared across every link and only this region streams.
+ */
+async function DocsArticle({
+ params,
+}: Pick, "params">) {
+ const { slug } = await params;
+ const page = source.getPage(slug);
if (!page) notFound();
const MDX = page.data.body;
@@ -53,9 +59,44 @@ export default async function Page(
);
}
-// export function generateStaticParams() {
-// return source.generateParams();
-// }
+/**
+ * Mirrors the article's own layout - heading, description, then body copy - so
+ * the swap to real content doesn't shift the page.
+ */
+const DocsArticleSkeleton = () => (
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+);
+
+export default function Page(props: PageProps<"/[locale]/docs/[[...slug]]">) {
+ return (
+ }>
+
+
+ );
+}
+
+export function generateStaticParams() {
+ return source.generateParams();
+}
export async function generateMetadata(props: {
params: Promise<{ slug?: string[] }>;
diff --git a/apps/docs/src/app/[locale]/(main)/(plugins)/(vitnode-core)/files/page.tsx b/apps/docs/src/app/[locale]/(main)/(plugins)/(vitnode-core)/files/page.tsx
index 9c8e2a64e..f0bdf1474 100644
--- a/apps/docs/src/app/[locale]/(main)/(plugins)/(vitnode-core)/files/page.tsx
+++ b/apps/docs/src/app/[locale]/(main)/(plugins)/(vitnode-core)/files/page.tsx
@@ -14,6 +14,8 @@ const MyFilesTableView = dynamic(async () =>
})),
);
+export const instant = false;
+
export const generateMetadata = async () => {
const t = await getTranslations("core.files");
@@ -41,7 +43,7 @@ export default async function Page(
- }>
+ }>
diff --git a/apps/docs/src/app/[locale]/(main)/(plugins)/(vitnode-core)/login/page.tsx b/apps/docs/src/app/[locale]/(main)/(plugins)/(vitnode-core)/login/page.tsx
index 7c076125f..d334b05f7 100644
--- a/apps/docs/src/app/[locale]/(main)/(plugins)/(vitnode-core)/login/page.tsx
+++ b/apps/docs/src/app/[locale]/(main)/(plugins)/(vitnode-core)/login/page.tsx
@@ -4,13 +4,8 @@ import { getTranslations } from "next-intl/server";
import { SignInView } from "@vitnode/core/views/auth/sign-in/sign-in-view";
-export const generateMetadata = async ({
- params,
-}: {
- params: Promise<{ locale: string }>;
-}): Promise => {
- const { locale } = await params;
- const t = await getTranslations({ locale, namespace: "core.global" });
+export const generateMetadata = async (): Promise => {
+ const t = await getTranslations("core.global");
return {
title: t("login"),
diff --git a/apps/docs/src/app/[locale]/(main)/(plugins)/(vitnode-core)/login/reset-password/page.tsx b/apps/docs/src/app/[locale]/(main)/(plugins)/(vitnode-core)/login/reset-password/page.tsx
index ebb16b44e..02f57b3b6 100644
--- a/apps/docs/src/app/[locale]/(main)/(plugins)/(vitnode-core)/login/reset-password/page.tsx
+++ b/apps/docs/src/app/[locale]/(main)/(plugins)/(vitnode-core)/login/reset-password/page.tsx
@@ -4,16 +4,8 @@ import { getTranslations } from "next-intl/server";
import { PasswordResetView } from "@vitnode/core/views/auth/password-reset/password-reset-view";
-export const generateMetadata = async ({
- params,
-}: {
- params: Promise<{ locale: string }>;
-}): Promise => {
- const { locale } = await params;
- const t = await getTranslations({
- locale,
- namespace: "core.auth.reset_password",
- });
+export const generateMetadata = async (): Promise => {
+ const t = await getTranslations("core.auth.reset_password");
return {
title: t("title"),
diff --git a/apps/docs/src/app/[locale]/(main)/(plugins)/(vitnode-core)/login/sso/[providerId]/page.tsx b/apps/docs/src/app/[locale]/(main)/(plugins)/(vitnode-core)/login/sso/[providerId]/page.tsx
index e52b56cbb..167fe488e 100644
--- a/apps/docs/src/app/[locale]/(main)/(plugins)/(vitnode-core)/login/sso/[providerId]/page.tsx
+++ b/apps/docs/src/app/[locale]/(main)/(plugins)/(vitnode-core)/login/sso/[providerId]/page.tsx
@@ -1,5 +1,7 @@
import { CallbackSSOView } from "@vitnode/core/views/auth/sso/callback/callback-sso-view";
+export const instant = false;
+
export default async function Page({
params,
searchParams,
diff --git a/apps/docs/src/app/[locale]/(main)/(plugins)/(vitnode-core)/register/page.tsx b/apps/docs/src/app/[locale]/(main)/(plugins)/(vitnode-core)/register/page.tsx
index 999d87e2f..7878f8a7b 100644
--- a/apps/docs/src/app/[locale]/(main)/(plugins)/(vitnode-core)/register/page.tsx
+++ b/apps/docs/src/app/[locale]/(main)/(plugins)/(vitnode-core)/register/page.tsx
@@ -4,13 +4,8 @@ import { getTranslations } from "next-intl/server";
import { SignUpView } from "@vitnode/core/views/auth/sign-up/sign-up-view";
-export const generateMetadata = async ({
- params,
-}: {
- params: Promise<{ locale: string }>;
-}): Promise => {
- const { locale } = await params;
- const t = await getTranslations({ locale, namespace: "core.global" });
+export const generateMetadata = async (): Promise => {
+ const t = await getTranslations("core.global");
return {
title: t("register"),
diff --git a/apps/docs/src/app/[locale]/(main)/(plugins)/(vitnode-core)/settings/layout.tsx b/apps/docs/src/app/[locale]/(main)/(plugins)/(vitnode-core)/settings/layout.tsx
index 175467cc1..0e3b8c17b 100644
--- a/apps/docs/src/app/[locale]/(main)/(plugins)/(vitnode-core)/settings/layout.tsx
+++ b/apps/docs/src/app/[locale]/(main)/(plugins)/(vitnode-core)/settings/layout.tsx
@@ -9,6 +9,13 @@ export const metadata: Metadata = {
},
};
+// instant = false: kept on purpose. Every route under this layout is gated on a
+// signed-in user in `LayoutSettings`, which calls `notFound()` when there isn't
+// one. Moving that read inside `` would turn a real 404 into a 200
+// shell that later swaps to a not-found body, so the gate stays where it is and
+// the segment is allowed to block.
+export const instant = false;
+
export default function Layout(
props: React.ComponentProps,
) {
diff --git a/apps/docs/src/app/[locale]/(main)/@breadcrumb/[...rest]/page.tsx b/apps/docs/src/app/[locale]/(main)/@breadcrumb/[...rest]/page.tsx
index e66aa3f96..88eab8cc1 100644
--- a/apps/docs/src/app/[locale]/(main)/@breadcrumb/[...rest]/page.tsx
+++ b/apps/docs/src/app/[locale]/(main)/@breadcrumb/[...rest]/page.tsx
@@ -1,3 +1,5 @@
+export const instant = false;
+
export default function BreadcrumbSlot() {
return null;
}
diff --git a/apps/docs/src/app/[locale]/(main)/[...rest]/page.tsx b/apps/docs/src/app/[locale]/(main)/[...rest]/page.tsx
index 64011c694..e1670efef 100644
--- a/apps/docs/src/app/[locale]/(main)/[...rest]/page.tsx
+++ b/apps/docs/src/app/[locale]/(main)/[...rest]/page.tsx
@@ -1,5 +1,7 @@
import { notFound } from "next/navigation";
+export const instant = false;
+
export default function RestPage() {
notFound();
}
diff --git a/apps/docs/src/app/[locale]/admin/(auth)/(plugins)/(vitnode-core)/content/[...slug]/page.tsx b/apps/docs/src/app/[locale]/admin/(auth)/(plugins)/(vitnode-core)/content/[...slug]/page.tsx
index c30d6692d..2c7b84233 100644
--- a/apps/docs/src/app/[locale]/admin/(auth)/(plugins)/(vitnode-core)/content/[...slug]/page.tsx
+++ b/apps/docs/src/app/[locale]/admin/(auth)/(plugins)/(vitnode-core)/content/[...slug]/page.tsx
@@ -1,8 +1,11 @@
import type { Metadata } from "next/dist/types";
+import React from "react";
+
import {
ContentAdminView,
type ContentAdminViewProps,
+ ContentAdminViewSkeleton,
getContentLabels,
resolveContentType,
} from "@vitnode/core/views/admin/views/content/content-admin-view";
@@ -19,5 +22,9 @@ export const generateMetadata = async ({
};
export default function ContentAdminPage(props: ContentAdminViewProps) {
- return ;
+ return (
+ }>
+
+
+ );
}
diff --git a/apps/docs/src/app/[locale]/admin/(auth)/(plugins)/(vitnode-core)/core/advanced/queue/page.tsx b/apps/docs/src/app/[locale]/admin/(auth)/(plugins)/(vitnode-core)/core/advanced/queue/page.tsx
index c9bc0b9d4..ca28b8415 100644
--- a/apps/docs/src/app/[locale]/admin/(auth)/(plugins)/(vitnode-core)/core/advanced/queue/page.tsx
+++ b/apps/docs/src/app/[locale]/admin/(auth)/(plugins)/(vitnode-core)/core/advanced/queue/page.tsx
@@ -42,7 +42,7 @@ export default async function Page(
- }>
+ }>
diff --git a/apps/docs/src/app/[locale]/admin/(auth)/(plugins)/(vitnode-core)/core/system/files/page.tsx b/apps/docs/src/app/[locale]/admin/(auth)/(plugins)/(vitnode-core)/core/system/files/page.tsx
index d89bc8204..51c4b4c22 100644
--- a/apps/docs/src/app/[locale]/admin/(auth)/(plugins)/(vitnode-core)/core/system/files/page.tsx
+++ b/apps/docs/src/app/[locale]/admin/(auth)/(plugins)/(vitnode-core)/core/system/files/page.tsx
@@ -42,7 +42,7 @@ export default async function Page(
- }>
+ }>
diff --git a/apps/docs/src/app/[locale]/admin/(auth)/(plugins)/(vitnode-core)/core/users/page.tsx b/apps/docs/src/app/[locale]/admin/(auth)/(plugins)/(vitnode-core)/core/users/page.tsx
index 3dd4691f9..6d16ba707 100644
--- a/apps/docs/src/app/[locale]/admin/(auth)/(plugins)/(vitnode-core)/core/users/page.tsx
+++ b/apps/docs/src/app/[locale]/admin/(auth)/(plugins)/(vitnode-core)/core/users/page.tsx
@@ -46,7 +46,7 @@ export default async function Page(
{canCreate && }
- }>
+ }>
diff --git a/apps/docs/src/app/[locale]/admin/(auth)/(plugins)/(vitnode-core)/core/users/roles/page.tsx b/apps/docs/src/app/[locale]/admin/(auth)/(plugins)/(vitnode-core)/core/users/roles/page.tsx
index c486e462a..d393da3f8 100644
--- a/apps/docs/src/app/[locale]/admin/(auth)/(plugins)/(vitnode-core)/core/users/roles/page.tsx
+++ b/apps/docs/src/app/[locale]/admin/(auth)/(plugins)/(vitnode-core)/core/users/roles/page.tsx
@@ -48,7 +48,7 @@ export default async function Page(
{canCreate && }
- }>
+ }>
diff --git a/apps/docs/src/app/[locale]/admin/(auth)/@breadcrumb/content/[...slug]/page.tsx b/apps/docs/src/app/[locale]/admin/(auth)/@breadcrumb/content/[...slug]/page.tsx
index 830a2d0ea..49c2334d3 100644
--- a/apps/docs/src/app/[locale]/admin/(auth)/@breadcrumb/content/[...slug]/page.tsx
+++ b/apps/docs/src/app/[locale]/admin/(auth)/@breadcrumb/content/[...slug]/page.tsx
@@ -1,71 +1,16 @@
-import { getTranslations } from "next-intl/server";
+import React from "react";
-import { contentTypeName } from "@vitnode/core/content/admin/labels";
-import {
- CONTENT_ADMIN_CREATE_SEGMENT,
- CONTENT_ADMIN_EDIT_SEGMENT,
-} from "@vitnode/core/content/const";
-import { contentAdminHref } from "@vitnode/core/content/registry";
-import { BreadcrumbAdmin } from "@vitnode/core/views/admin/layouts/breadcrumb/breadcrumb-admin";
-import {
- getContentLabels,
- resolveContentRoute,
-} from "@vitnode/core/views/admin/views/content/content-admin-view";
+import { BreadcrumbContentAdmin } from "@vitnode/core/views/admin/layouts/breadcrumb/breadcrumb-content-admin";
+import { BreadcrumbSkeleton } from "@vitnode/core/views/breadcrumb/breadcrumb-render";
-/**
- * The breadcrumb of every generated Content Engine screen.
- *
- * The list keeps the trail it always had. A create or an edit **page** appends
- * one more crumb, labelled from `core.content` with the content type's own
- * singular - so it reads "Blog / Articles / Create article" in whatever language
- * the AdminCP is in, and "Articles" becomes a link back to the list.
- *
- * The record id is deliberately **not** a crumb of its own: `/42/` would render
- * as a dead "42" between two words, and the page it would point at is the one
- * being read.
- */
-export default async function BreadcrumbSlot({
+export default function BreadcrumbSlot({
params,
}: {
params: Promise<{ slug: string[] }>;
}) {
- const { slug } = await params;
- const route = await resolveContentRoute(params);
- const labels = route ? await getContentLabels(route.entry) : undefined;
-
- if (!route || route.action === "list") {
- return (
-
- );
- }
-
- const t = await getTranslations("core.content");
- const { definition } = route.entry;
-
return (
-
+ }>
+
+
);
}
diff --git a/apps/docs/src/app/[locale]/admin/(auth)/layout.tsx b/apps/docs/src/app/[locale]/admin/(auth)/layout.tsx
index 83191e110..a6e0ca312 100644
--- a/apps/docs/src/app/[locale]/admin/(auth)/layout.tsx
+++ b/apps/docs/src/app/[locale]/admin/(auth)/layout.tsx
@@ -5,6 +5,8 @@ import {
import { vitNodeConfig } from "@/vitnode.config";
+export const instant = false;
+
export default function Layout(props: AdminLayoutProps) {
return ;
}
diff --git a/apps/docs/src/app/[locale]/layout.client.tsx b/apps/docs/src/app/[locale]/layout.client.tsx
index 4dbd83965..8ae133f18 100644
--- a/apps/docs/src/app/[locale]/layout.client.tsx
+++ b/apps/docs/src/app/[locale]/layout.client.tsx
@@ -1,22 +1,31 @@
"use client";
-import { cn } from "@vitnode/core/lib/utils";
import { useParams } from "next/navigation";
+import { useLayoutEffect } from "react";
-export function useMode(): string | undefined {
+/**
+ * Keeps the docs section class (`dev`, `guides`, `plugins`, `ui`) on `` in
+ * sync with the route. The class sets `--color-fd-primary`, so it has to live on
+ * `` - an element that cannot be wrapped in ``.
+ *
+ * First paint is handled by the inline script in the root layout, which derives
+ * the same class from `location.pathname` before anything renders. This takes
+ * over from there, so client navigations between sections repaint correctly.
+ * Renders nothing.
+ */
+export function DocsModeSync(): null {
const { slug } = useParams();
+ const mode = Array.isArray(slug) && slug.length > 0 ? slug[0] : undefined;
- return Array.isArray(slug) && slug.length > 0 ? slug[0] : undefined;
-}
+ useLayoutEffect(() => {
+ if (typeof mode !== "string" || mode.length === 0) return;
+
+ document.body.classList.add(mode);
-export function Body({
- children,
- className,
-}: {
- children: React.ReactNode;
- className?: string;
-}): React.ReactElement {
- const mode = useMode();
+ return () => {
+ document.body.classList.remove(mode);
+ };
+ }, [mode]);
- return {children};
+ return null;
}
diff --git a/apps/docs/src/app/[locale]/layout.tsx b/apps/docs/src/app/[locale]/layout.tsx
index 8d52d99de..99391552e 100644
--- a/apps/docs/src/app/[locale]/layout.tsx
+++ b/apps/docs/src/app/[locale]/layout.tsx
@@ -8,12 +8,14 @@ import {
type RootLayoutProps,
} from "@vitnode/core/views/layouts/root-layout";
import { RootProvider } from "fumadocs-ui/provider/next";
+import { getLocale } from "next-intl/server";
import { Geist } from "next/font/google";
+import { Suspense } from "react";
import { vitNodeConfig } from "@/vitnode.config";
import SearchDialogFumadocs from "../../components/fumadocs/search-dialog";
-import { Body } from "./layout.client";
+import { DocsModeSync } from "./layout.client";
export const generateMetadata = (): Metadata =>
generateMetadataRootLayout(vitNodeConfig);
@@ -25,12 +27,31 @@ const geist = Geist({
subsets: ["latin"],
});
+/**
+ * Paints the docs section class on `` before first paint.
+ *
+ * `` cannot be wrapped in ``, so the class cannot come from a
+ * `useParams()` read during prerendering without making every route block.
+ * Deriving it from `location.pathname` here keeps the shell static; the
+ * `DocsModeSync` component below takes over for client navigations.
+ */
+const docsModeScript = `(function(){var p=location.pathname.split("/"),i=p.indexOf("docs"),m=i<0?"":p[i+1];if(m)document.body.classList.add(m)})()`;
+
export default async function Layout(props: RootLayoutProps) {
- const { locale } = await props.params;
+ const locale = await getLocale();
return (
-
+
+ {/* eslint-disable-next-line @eslint-react/dom-no-dangerously-set-innerhtml */}
+
+
+
+
+
-
+
);
}
diff --git a/apps/docs/src/app/llms-full.txt/route.ts b/apps/docs/src/app/llms-full.txt/route.ts
index fcccaeefa..0cea3563a 100644
--- a/apps/docs/src/app/llms-full.txt/route.ts
+++ b/apps/docs/src/app/llms-full.txt/route.ts
@@ -1,10 +1,17 @@
+import { cacheLife } from "next/cache";
+
import { getLLMText, source } from "@/lib/source";
-export const revalidate = false;
+async function getLLMFullText() {
+ "use cache";
+ cacheLife("max");
-export async function GET() {
const scan = source.getPages().map(getLLMText);
const scanned = await Promise.all(scan);
- return new Response(scanned.join("\n\n"));
+ return scanned.join("\n\n");
+}
+
+export async function GET() {
+ return new Response(await getLLMFullText());
}
diff --git a/apps/docs/src/proxy.ts b/apps/docs/src/proxy.ts
index dbc211b22..d1b29eeda 100644
--- a/apps/docs/src/proxy.ts
+++ b/apps/docs/src/proxy.ts
@@ -1,12 +1,8 @@
-import createMiddleware from "next-intl/middleware";
+import { createVitNodeProxy } from "@vitnode/core/lib/i18n/proxy";
-import { vitNodeConfig } from "./vitnode.config";
+import { i18n } from "./i18n";
-export default createMiddleware({
- locales: vitNodeConfig.i18n.locales.map(locale => locale.code),
- defaultLocale: vitNodeConfig.i18n.defaultLocale,
- localePrefix: vitNodeConfig.i18n.localePrefix,
-});
+export default createVitNodeProxy(i18n);
export const config = {
matcher: [
diff --git a/apps/docs/src/vitnode.api.config.ts b/apps/docs/src/vitnode.api.config.ts
index 50a9740be..3be7cc8ea 100644
--- a/apps/docs/src/vitnode.api.config.ts
+++ b/apps/docs/src/vitnode.api.config.ts
@@ -3,13 +3,13 @@ import { DiscordSSOApiPlugin } from "@vitnode/core/api/adapters/sso/discord";
// import { ResendEmailAdapter } from "@vitnode/resend";
import { FacebookSSOApiPlugin } from "@vitnode/core/api/adapters/sso/facebook";
import { GoogleSSOApiPlugin } from "@vitnode/core/api/adapters/sso/google";
+import { coreRelations } from "@vitnode/core/database/relations";
import { buildApiConfig } from "@vitnode/core/vitnode.config";
import { exampleApiPlugin } from "@vitnode/example/config.api";
import { NodeCronAdapter } from "@vitnode/node-cron";
import { NodemailerEmailAdapter } from "@vitnode/nodemailer";
// import { LocalStorageAdapter } from "@vitnode/core/api/adapters/storage/local";
import { SupabaseStorageAdapter } from "@vitnode/supabase-storage";
-import { coreRelations } from "@vitnode/core/database/relations";
import { drizzle } from "drizzle-orm/postgres-js";
import { i18n } from "./i18n";
diff --git a/apps/docs/src/vitnode.config.ts b/apps/docs/src/vitnode.config.ts
index 0c360d790..930a96117 100644
--- a/apps/docs/src/vitnode.config.ts
+++ b/apps/docs/src/vitnode.config.ts
@@ -1,5 +1,6 @@
import { blogPlugin } from "@vitnode/blog/config";
-import { buildConfig, handleRequestConfig } from "@vitnode/core/vitnode.config";
+import { handleRequestConfig } from "@vitnode/core/lib/i18n/request-config";
+import { buildConfig } from "@vitnode/core/vitnode.config";
import { examplePlugin } from "@vitnode/example/config";
import { getRequestConfig } from "next-intl/server";
@@ -20,9 +21,9 @@ export const vitNodeConfig = buildConfig({
// This is the request config for the app. It will be used in the app router.
export default getRequestConfig(
- async ({ requestLocale }) =>
+ async params =>
await handleRequestConfig({
- requestLocale,
+ params,
vitNodeConfig,
}),
);
diff --git a/packages/create-vitnode-app/copy-of-vitnode-app/root/src/app/[locale]/(main)/@breadcrumb/[...rest]/page.tsx b/packages/create-vitnode-app/copy-of-vitnode-app/root/src/app/[locale]/(main)/@breadcrumb/[...rest]/page.tsx
index e66aa3f96..88eab8cc1 100644
--- a/packages/create-vitnode-app/copy-of-vitnode-app/root/src/app/[locale]/(main)/@breadcrumb/[...rest]/page.tsx
+++ b/packages/create-vitnode-app/copy-of-vitnode-app/root/src/app/[locale]/(main)/@breadcrumb/[...rest]/page.tsx
@@ -1,3 +1,5 @@
+export const instant = false;
+
export default function BreadcrumbSlot() {
return null;
}
diff --git a/packages/create-vitnode-app/copy-of-vitnode-app/root/src/app/[locale]/(main)/[...rest]/page.tsx b/packages/create-vitnode-app/copy-of-vitnode-app/root/src/app/[locale]/(main)/[...rest]/page.tsx
index 64011c694..e1670efef 100644
--- a/packages/create-vitnode-app/copy-of-vitnode-app/root/src/app/[locale]/(main)/[...rest]/page.tsx
+++ b/packages/create-vitnode-app/copy-of-vitnode-app/root/src/app/[locale]/(main)/[...rest]/page.tsx
@@ -1,5 +1,7 @@
import { notFound } from "next/navigation";
+export const instant = false;
+
export default function RestPage() {
notFound();
}
diff --git a/packages/create-vitnode-app/copy-of-vitnode-app/root/src/app/[locale]/admin/(auth)/layout.tsx b/packages/create-vitnode-app/copy-of-vitnode-app/root/src/app/[locale]/admin/(auth)/layout.tsx
index 83191e110..a6e0ca312 100644
--- a/packages/create-vitnode-app/copy-of-vitnode-app/root/src/app/[locale]/admin/(auth)/layout.tsx
+++ b/packages/create-vitnode-app/copy-of-vitnode-app/root/src/app/[locale]/admin/(auth)/layout.tsx
@@ -5,6 +5,8 @@ import {
import { vitNodeConfig } from "@/vitnode.config";
+export const instant = false;
+
export default function Layout(props: AdminLayoutProps) {
return ;
}
diff --git a/packages/create-vitnode-app/copy-of-vitnode-app/root/src/app/[locale]/layout.tsx b/packages/create-vitnode-app/copy-of-vitnode-app/root/src/app/[locale]/layout.tsx
index 662c9e154..2c8e2dcf8 100644
--- a/packages/create-vitnode-app/copy-of-vitnode-app/root/src/app/[locale]/layout.tsx
+++ b/packages/create-vitnode-app/copy-of-vitnode-app/root/src/app/[locale]/layout.tsx
@@ -7,6 +7,7 @@ import {
RootLayout,
type RootLayoutProps,
} from "@vitnode/core/views/layouts/root-layout";
+import { getLocale } from "next-intl/server";
import { Geist } from "next/font/google";
import { vitNodeConfig } from "@/vitnode.config";
@@ -22,7 +23,7 @@ const geist = Geist({
});
export default async function Layout(props: RootLayoutProps) {
- const { locale } = await props.params;
+ const locale = await getLocale();
return (
diff --git a/packages/create-vitnode-app/copy-of-vitnode-app/root/src/i18n.ts b/packages/create-vitnode-app/copy-of-vitnode-app/root/src/i18n.ts
new file mode 100644
index 000000000..b45fd7ae2
--- /dev/null
+++ b/packages/create-vitnode-app/copy-of-vitnode-app/root/src/i18n.ts
@@ -0,0 +1,22 @@
+import type { VitNodeI18nConfig } from "@vitnode/core/lib/i18n/types";
+
+/**
+ * Shared by `vitnode.config.ts` and `proxy.ts` so rendering and routing agree
+ * on which languages exist.
+ *
+ * Kept apart from `vitnode.config.ts` on purpose: the Proxy runs outside the App
+ * Directory and must not pull the request config - and with it
+ * `next/root-params` - into its module graph.
+ *
+ * Packages ship their own languages - only what this app adds or reworks needs a
+ * file here. Anything a file leaves out falls back to `defaultLocale`.
+ */
+export const i18n = {
+ defaultLocale: "en",
+ locales: [
+ {
+ code: "en",
+ name: "English",
+ },
+ ],
+} satisfies VitNodeI18nConfig;
diff --git a/packages/create-vitnode-app/copy-of-vitnode-app/root/src/proxy.ts b/packages/create-vitnode-app/copy-of-vitnode-app/root/src/proxy.ts
index dbc211b22..d1b29eeda 100644
--- a/packages/create-vitnode-app/copy-of-vitnode-app/root/src/proxy.ts
+++ b/packages/create-vitnode-app/copy-of-vitnode-app/root/src/proxy.ts
@@ -1,12 +1,8 @@
-import createMiddleware from "next-intl/middleware";
+import { createVitNodeProxy } from "@vitnode/core/lib/i18n/proxy";
-import { vitNodeConfig } from "./vitnode.config";
+import { i18n } from "./i18n";
-export default createMiddleware({
- locales: vitNodeConfig.i18n.locales.map(locale => locale.code),
- defaultLocale: vitNodeConfig.i18n.defaultLocale,
- localePrefix: vitNodeConfig.i18n.localePrefix,
-});
+export default createVitNodeProxy(i18n);
export const config = {
matcher: [
diff --git a/packages/create-vitnode-app/copy-of-vitnode-app/root/src/vitnode.config.ts b/packages/create-vitnode-app/copy-of-vitnode-app/root/src/vitnode.config.ts
index bf7569899..96d03aba2 100644
--- a/packages/create-vitnode-app/copy-of-vitnode-app/root/src/vitnode.config.ts
+++ b/packages/create-vitnode-app/copy-of-vitnode-app/root/src/vitnode.config.ts
@@ -1,21 +1,16 @@
-import { buildConfig, handleRequestConfig } from "@vitnode/core/vitnode.config";
+import { handleRequestConfig } from "@vitnode/core/lib/i18n/request-config";
+import { buildConfig } from "@vitnode/core/vitnode.config";
import { getRequestConfig } from "next-intl/server";
+import { i18n } from "./i18n";
+
export const vitNodeConfig = buildConfig({
metadata: {
title: "VitNode",
shortTitle: "VitNode",
},
plugins: [],
- i18n: {
- locales: [
- {
- code: "en",
- name: "English",
- },
- ],
- defaultLocale: "en",
- },
+ i18n,
theme: {
defaultTheme: "light",
},
@@ -23,9 +18,9 @@ export const vitNodeConfig = buildConfig({
// This is the request config for the app. It will be used in the app router.
export default getRequestConfig(
- async ({ requestLocale }) =>
+ async params =>
await handleRequestConfig({
- requestLocale,
+ params,
vitNodeConfig,
}),
);
diff --git a/packages/create-vitnode-app/src/create/package-versions.ts b/packages/create-vitnode-app/src/create/package-versions.ts
index 84b232aec..491f7bb02 100644
--- a/packages/create-vitnode-app/src/create/package-versions.ts
+++ b/packages/create-vitnode-app/src/create/package-versions.ts
@@ -18,8 +18,8 @@ export const versionsPackageJson = {
react: "^19.2",
reactDom: "^19.2",
- nextSingle: "16.3.0-preview.9",
- nextIntl: "^4.13",
+ nextSingle: "16.3.1",
+ nextIntl: "^4.13.7",
rhf: "^7",
rhfResolvers: "^5.1.1",
lucide: "^1.21",
diff --git a/packages/vitnode/config/next.config.ts b/packages/vitnode/config/next.config.ts
index 9317acb61..4a2816cb3 100644
--- a/packages/vitnode/config/next.config.ts
+++ b/packages/vitnode/config/next.config.ts
@@ -5,6 +5,8 @@ const withNextIntl = createNextIntlPlugin("./src/vitnode.config.ts");
export const vitNodeNextConfig = (config: NextConfig): NextConfig =>
withNextIntl({
+ cacheComponents: true,
+ partialPrefetching: true,
...config,
serverExternalPackages: [
...(config.serverExternalPackages ?? []),
diff --git a/packages/vitnode/package.json b/packages/vitnode/package.json
index ff965bcc2..790fe4e72 100644
--- a/packages/vitnode/package.json
+++ b/packages/vitnode/package.json
@@ -28,7 +28,7 @@
"drizzle-orm": "^1.0.0-rc.4",
"hono": "^4.9.x",
"motion": "^12.x.x",
- "next": "^16.0.x",
+ "next": "^16.3.x",
"next-intl": "^4.x.x",
"react": "^19.2.x",
"react-dom": "^19.2.x",
@@ -58,8 +58,8 @@
"jiti": "^2.7.0",
"jsdom": "^29.1.1",
"lucide-react": "^1.25.0",
- "next": "16.3.0-preview.9",
- "next-intl": "^4.13.3",
+ "next": "16.3.1",
+ "next-intl": "^4.13.7",
"react": "^19.2.8",
"react-dom": "^19.2.8",
"react-email": "^6.9.0",
diff --git a/packages/vitnode/src/components/switchers/langs/language-switcher.tsx b/packages/vitnode/src/components/switchers/langs/language-switcher.tsx
index 5de6ada9e..a2c25bcb9 100644
--- a/packages/vitnode/src/components/switchers/langs/language-switcher.tsx
+++ b/packages/vitnode/src/components/switchers/langs/language-switcher.tsx
@@ -16,13 +16,48 @@ import {
DropdownMenuTrigger,
} from "../../ui/dropdown-menu";
-export const LanguageSwitcher = ({ locales }: { locales: LocaleConfig[] }) => {
+/**
+ * The switchable locales. Split out because it is the only part that reads
+ * `usePathname()` - switching locale means re-navigating to the current path.
+ *
+ * This component sits in the header of every themed route, so reading the
+ * pathname in the switcher body kept the whole header out of the static shell on
+ * any route with dynamic params. Keeping the read down here leaves the trigger
+ * button prerendered and suspends only the menu items, which are behind a closed
+ * dropdown anyway.
+ */
+const LanguageSwitcherItems = ({
+ locales,
+ startTransition,
+}: {
+ locales: LocaleConfig[];
+ startTransition: React.TransitionStartFunction;
+}) => {
const currentLocale = useLocale();
- const [isPending, startTransition] = React.useTransition();
const { replace } = useRouter();
- const t = useTranslations("core.global");
const pathname = usePathname();
+ return locales.map(locale => (
+ {
+ startTransition(() => {
+ replace(pathname, {
+ locale: locale.code,
+ });
+ });
+ }}
+ >
+ {locale.name}
+ {locale.code === currentLocale && }
+
+ ));
+};
+
+export const LanguageSwitcher = ({ locales }: { locales: LocaleConfig[] }) => {
+ const [isPending, startTransition] = React.useTransition();
+ const t = useTranslations("core.global");
+
return (
{
- {locales.map(locale => (
- {
- startTransition(() => {
- replace(pathname, {
- locale: locale.code,
- });
- });
- }}
- >
- {locale.name}
- {locale.code === currentLocale && }
-
- ))}
+ {/* The fallback holds the menu's final size, so opening it mid-stream
+ doesn't resize the popup under the pointer. */}
+ (
+
+ {locale.name}
+
+ ))}
+ >
+
+
);
diff --git a/packages/vitnode/src/components/table/data-table.tsx b/packages/vitnode/src/components/table/data-table.tsx
index e73883c18..ae74f0986 100644
--- a/packages/vitnode/src/components/table/data-table.tsx
+++ b/packages/vitnode/src/components/table/data-table.tsx
@@ -4,11 +4,13 @@ import type { FilterDataTable } from "./filters";
import type { PaginationDataTable } from "./pagination";
import type { SearchDataTable } from "./search";
+import { cn } from "../../lib/utils";
import { ErrorView } from "../../views/error/error-view";
import { Skeleton } from "../ui/skeleton";
import {
Table,
TableBody,
+ TableCell,
TableHead,
TableHeader,
TableRow,
@@ -49,58 +51,90 @@ interface DisplayColumnDef extends ColumnDefBase {
export type ColumnDef =
AccessorColumnDef | DisplayColumnDef;
-export const DataTableSkeleton = ({ columns }: { columns: number }) => {
- const headerIds = React.useMemo(
- () =>
- Array.from({ length: columns }).map(
- // eslint-disable-next-line react-hooks/purity
- () => `s-head-${Math.random().toString(36).slice(2, 9)}`,
- ),
- [columns],
- );
-
- const rowIds = React.useMemo(
- () =>
- Array.from({ length: 6 }).map(
- // eslint-disable-next-line react-hooks/purity
- () => `s-row-${Math.random().toString(36).slice(2, 9)}`,
- ),
- [],
- );
+/** Widths the placeholder cells cycle through, so a loading table does not read as one grey block. */
+const SKELETON_HEAD_WIDTHS = ["w-24", "w-16", "w-20", "w-14"];
+const SKELETON_CELL_WIDTHS = ["w-full", "w-3/4", "w-1/2", "w-5/6", "w-2/3"];
+
+/**
+ * Placeholder for a `DataTable` that has not streamed in yet.
+ *
+ * Mirrors `ContentDataTable`'s markup - the same toolbar row, the same bordered
+ * table with a `bg-card` head, the same pagination footer - so the real table
+ * replaces it in place instead of shifting the page around it.
+ */
+export const DataTableSkeleton = ({
+ columns,
+ rows = 6,
+ toolbar = false,
+}: {
+ columns: number;
+ /** Rows to draw. Defaults to a page that is neither empty nor full. */
+ rows?: number;
+ /** Draw the search / filters row, for the tables that render one. */
+ toolbar?: boolean;
+}) => {
+ const headerIds = Array.from({ length: columns }, (_, i) => `s-head-${i}`);
+ const rowIds = Array.from({ length: rows }, (_, i) => `s-row-${i}`);
return (
+ {toolbar && (
+
+ )}
+
-
+
- {headerIds.map(hid => (
-
-
+ {headerIds.map((hid, i) => (
+
+
))}
- {rowIds.map(rid => (
-
- {headerIds.map((_, j) => {
- const cellId = `s-cell-${rid}-${j}`;
-
- return (
- |
-
- |
- );
- })}
+ {rowIds.map((rid, i) => (
+ // `h-9` is a floor, not a cap: it holds the row at the height a
+ // row of text-sm cells settles on once the data arrives.
+
+ {headerIds.map((hid, j) => (
+
+
+
+ ))}
))}
-
-
+ {/* The shape `PaginationDataTable` takes: page size, then prev / next. */}
+
);
diff --git a/packages/vitnode/src/content/server/diagnostics.ts b/packages/vitnode/src/content/server/diagnostics.ts
index 37745fbb9..91a3fbcc2 100644
--- a/packages/vitnode/src/content/server/diagnostics.ts
+++ b/packages/vitnode/src/content/server/diagnostics.ts
@@ -5,12 +5,11 @@ import { and, count, eq, inArray, sql } from "drizzle-orm";
import type { RegisteredContentModel } from "./model";
-import { contentDefinitionOf } from "./model";
-
import { core_content_schedules } from "../../database/content";
import { core_search_index } from "../../database/search";
import { normalizeContentLocale } from "../locale";
import { listContentLanguages } from "./language-resolver";
+import { contentDefinitionOf } from "./model";
import {
contentTranslationPublicationColumns,
publicationColumns,
diff --git a/packages/vitnode/src/content/server/module.ts b/packages/vitnode/src/content/server/module.ts
index 6d32f6f80..4e6807fbd 100644
--- a/packages/vitnode/src/content/server/module.ts
+++ b/packages/vitnode/src/content/server/module.ts
@@ -1,5 +1,4 @@
import type { BuildModuleReturn } from "../../api/lib/module";
-import type { AnyContentTypeDefinition } from "../types";
import type { AnyContentModel } from "./model";
import { buildModule } from "../../api/lib/module";
diff --git a/packages/vitnode/src/content/server/public-module.ts b/packages/vitnode/src/content/server/public-module.ts
index a9b729f04..2fcc81c40 100644
--- a/packages/vitnode/src/content/server/public-module.ts
+++ b/packages/vitnode/src/content/server/public-module.ts
@@ -1,5 +1,4 @@
import type { BuildModuleReturn } from "../../api/lib/module";
-import type { AnyContentTypeDefinition } from "../types";
import type { AnyContentModel } from "./model";
import { buildModule } from "../../api/lib/module";
diff --git a/packages/vitnode/src/content/server/search-sync.ts b/packages/vitnode/src/content/server/search-sync.ts
index be86d1119..6affc7c23 100644
--- a/packages/vitnode/src/content/server/search-sync.ts
+++ b/packages/vitnode/src/content/server/search-sync.ts
@@ -6,8 +6,6 @@ import { eq } from "drizzle-orm";
import type { AnyContentTypeDefinition } from "../types";
import type { AnyContentModel } from "./model";
-import { contentDefinitionOf } from "./model";
-
import { partitionContentFields } from "../localization";
import { contentColumnsToValues, contentStorageColumns } from "../paths";
import {
@@ -16,6 +14,7 @@ import {
contentSearchIndexedPaths,
} from "../search";
import { listContentLanguages } from "./language-resolver";
+import { contentDefinitionOf } from "./model";
import {
contentSearchDocument,
contentTranslationSearchDocument,
diff --git a/packages/vitnode/src/lib/api/get-middleware-api.ts b/packages/vitnode/src/lib/api/get-middleware-api.ts
index e448cf355..8460394bd 100644
--- a/packages/vitnode/src/lib/api/get-middleware-api.ts
+++ b/packages/vitnode/src/lib/api/get-middleware-api.ts
@@ -1,15 +1,53 @@
+import { cacheLife } from "next/cache";
+import { connection } from "next/server";
+
import { middlewareModule } from "@/api/modules/middleware/middleware.module";
-import { fetcher } from "@/lib/fetcher";
+import { coreFetcher } from "@/lib/fetcher/core";
-export const getMiddlewareApi = async () => {
- const res = await fetcher(middlewareModule, {
+/**
+ * Deployment configuration for the auth screens: which SSO adapters are
+ * registered, whether an email adapter exists, and the public captcha key.
+ *
+ * Every field is derived from `vitnode.api.config.ts`, so the response is the
+ * same for every visitor and only changes on deploy - hence `cacheLife("max")`.
+ *
+ * Goes through `coreFetcher` rather than `fetcher` deliberately: `fetcher`
+ * forwards the request's cookies and headers, and `use cache` cannot enclose a
+ * runtime read. Nothing on this route is per-user, so there is nothing to
+ * forward.
+ */
+const fetchMiddlewareApi = async () => {
+ "use cache";
+ cacheLife("max");
+
+ const res = await coreFetcher(middlewareModule, {
path: "/",
method: "get",
module: "middleware",
- options: {
- cache: "force-cache",
- },
});
return await res.json();
};
+
+/**
+ * `connection()` first, so the entry above is filled by the first real request
+ * instead of during `next build`.
+ *
+ * Cache Components fills a `use cache` entry while prerendering, and filling
+ * this one means an HTTP request to `NEXT_PUBLIC_API_URL` from the build itself.
+ * In the single-app deployment that origin *is* the app being built, so nothing
+ * is listening and the prerender dies with `ECONNREFUSED`; in the split
+ * deployment it is a separate service a build machine - CI, a Docker image, a
+ * first deploy - has no reason to reach. Neither is a misconfiguration that
+ * could be fixed, so the read waits for a request that can actually answer it.
+ *
+ * The cache still does its job either way: one API call per deploy, not one per
+ * visitor. What it gives up is a place in the static shell, so every caller has
+ * to sit inside a `
` boundary - or its route has to be a documented
+ * `instant = false`.
+ */
+export const getMiddlewareApi = async () => {
+ await connection();
+
+ return await fetchMiddlewareApi();
+};
diff --git a/packages/vitnode/src/lib/i18n/proxy.ts b/packages/vitnode/src/lib/i18n/proxy.ts
new file mode 100644
index 000000000..30e85c443
--- /dev/null
+++ b/packages/vitnode/src/lib/i18n/proxy.ts
@@ -0,0 +1,19 @@
+import createMiddleware from "next-intl/middleware";
+
+import type { VitNodeI18nConfig } from "./types";
+
+/**
+ * The next-intl Proxy (middleware), built from the app's i18n config alone.
+ *
+ * Deliberately takes the i18n block rather than the whole `VitNodeConfig`: the
+ * Proxy runs outside the App Directory, where `next/root-params` - reached
+ * through the request config - cannot be imported. Keeping `vitnode.config.ts`
+ * out of the Proxy's module graph is what lets the request config read root
+ * params at all.
+ */
+export const createVitNodeProxy = (i18n: VitNodeI18nConfig) =>
+ createMiddleware({
+ locales: i18n.locales.map(locale => locale.code),
+ defaultLocale: i18n.defaultLocale,
+ localePrefix: i18n.localePrefix ?? "as-needed",
+ });
diff --git a/packages/vitnode/src/lib/i18n/request-config.ts b/packages/vitnode/src/lib/i18n/request-config.ts
new file mode 100644
index 000000000..ca0f6d056
--- /dev/null
+++ b/packages/vitnode/src/lib/i18n/request-config.ts
@@ -0,0 +1,109 @@
+import type { VitNodeConfig } from "@/vitnode.config";
+
+import { CONFIG_PLUGIN } from "@/config";
+import { loadMessages } from "@/lib/i18n/load-messages";
+import { buildMessagesSources } from "@/lib/i18n/sources";
+
+/**
+ * The next-intl request config, kept in its own module rather than alongside
+ * `buildConfig` in `vitnode.config.ts`.
+ *
+ * `next/root-params` is only importable from the App Directory graph, and
+ * `vitnode.config.ts` is reached from the Proxy (middleware) and from
+ * `vitnode.api.config.ts` inside the catch-all Route Handler. Importing root
+ * params there fails the build outright, so the read lives here, behind the one
+ * entry point only a Server Component reaches.
+ */
+
+/**
+ * The root `[locale]` segment, read through `next/root-params`.
+ *
+ * Root params are known at prerender time, which is what makes a localized
+ * route statically renderable - unlike next-intl's header-derived
+ * `requestLocale`, reading them does not pull the render into request time.
+ *
+ * Returns `undefined` rather than throwing when root params are unavailable, so
+ * callers fall through to the next source. That covers Server Actions, where
+ * `next/root-params` is not supported.
+ */
+const readRootParamsLocale = async (): Promise => {
+ try {
+ const { locale } = await import("next/root-params");
+
+ return await locale();
+ } catch {
+ return undefined;
+ }
+};
+
+export const handleRequestConfig = async ({
+ params,
+ vitNodeConfig,
+}: {
+ /**
+ * next-intl's `getRequestConfig` argument, passed through whole rather than
+ * destructured by the caller.
+ *
+ * `requestLocale` is a getter that calls `headers()` the moment it is read, so
+ * destructuring it - even without awaiting - would touch request data on
+ * routes that are otherwise fully static. Taking the object lets the read stay
+ * behind the root-params check below.
+ */
+ params: {
+ /**
+ * A locale named by the caller, e.g. `getTranslations({ locale })`. Wins
+ * over everything else - it is the one unambiguously deliberate source.
+ */
+ locale?: string;
+ /**
+ * next-intl's header-derived locale. Deprecated upstream in favour of root
+ * params, and kept only as the last resort: it is still the sole source in
+ * Server Actions, where `next/root-params` is unavailable and no explicit
+ * locale is passed.
+ */
+ requestLocale?: Promise;
+ };
+ vitNodeConfig: VitNodeConfig;
+}) => {
+ const localeCodes = vitNodeConfig.i18n.locales.map(locale => locale.code);
+ const isSupported = (value: string | undefined): value is string =>
+ value !== undefined && localeCodes.includes(value);
+
+ // Ordered and short-circuiting: each source is consulted only when the ones
+ // before it miss, so a statically rendered route never reaches
+ // `requestLocale` and stays out of the request-time path.
+ const resolveLocale = async (): Promise => {
+ if (isSupported(params.locale)) return params.locale;
+
+ const fromRootParams = await readRootParamsLocale();
+ if (isSupported(fromRootParams)) return fromRootParams;
+
+ // Root params cover every App Router render, so reaching here means the
+ // Server Action path, which `next/root-params` does not support.
+ const fromRequest = await params.requestLocale;
+ if (isSupported(fromRequest)) return fromRequest;
+
+ return vitNodeConfig.i18n.defaultLocale;
+ };
+
+ const locale = await resolveLocale();
+
+ const sources = buildMessagesSources({
+ appMessages: vitNodeConfig.i18n.messages,
+ plugins: vitNodeConfig.plugins,
+ });
+
+ return {
+ locale,
+ messages: await loadMessages({
+ defaultLocale: vitNodeConfig.i18n.defaultLocale,
+ locale,
+ sources,
+ }),
+ pluginIds: [
+ CONFIG_PLUGIN.pluginId,
+ ...vitNodeConfig.plugins.map(plugin => plugin.pluginId),
+ ],
+ timeZone: vitNodeConfig.i18n.timeZone,
+ };
+};
diff --git a/packages/vitnode/src/routes/admin/content/[...slug]/page.tsx b/packages/vitnode/src/routes/admin/content/[...slug]/page.tsx
index ad75e5bab..c77ceb7e8 100644
--- a/packages/vitnode/src/routes/admin/content/[...slug]/page.tsx
+++ b/packages/vitnode/src/routes/admin/content/[...slug]/page.tsx
@@ -1,8 +1,11 @@
import type { Metadata } from "next/dist/types";
+import React from "react";
+
import {
ContentAdminView,
type ContentAdminViewProps,
+ ContentAdminViewSkeleton,
getContentLabels,
resolveContentType,
} from "@/views/admin/views/content/content-admin-view";
@@ -19,5 +22,9 @@ export const generateMetadata = async ({
};
export default function ContentAdminPage(props: ContentAdminViewProps) {
- return ;
+ return (
+ }>
+
+
+ );
}
diff --git a/packages/vitnode/src/routes/admin/core/advanced/queue/page.tsx b/packages/vitnode/src/routes/admin/core/advanced/queue/page.tsx
index 1451b8db6..a81ab2a0f 100644
--- a/packages/vitnode/src/routes/admin/core/advanced/queue/page.tsx
+++ b/packages/vitnode/src/routes/admin/core/advanced/queue/page.tsx
@@ -42,7 +42,7 @@ export default async function Page(
- }>
+ }>
diff --git a/packages/vitnode/src/routes/admin/core/system/files/page.tsx b/packages/vitnode/src/routes/admin/core/system/files/page.tsx
index 003bba1f8..459f3da9b 100644
--- a/packages/vitnode/src/routes/admin/core/system/files/page.tsx
+++ b/packages/vitnode/src/routes/admin/core/system/files/page.tsx
@@ -42,7 +42,7 @@ export default async function Page(
- }>
+ }>
diff --git a/packages/vitnode/src/routes/admin/core/users/page.tsx b/packages/vitnode/src/routes/admin/core/users/page.tsx
index 37d5801ee..6869eccf2 100644
--- a/packages/vitnode/src/routes/admin/core/users/page.tsx
+++ b/packages/vitnode/src/routes/admin/core/users/page.tsx
@@ -46,7 +46,7 @@ export default async function Page(
{canCreate && }
- }>
+ }>
diff --git a/packages/vitnode/src/routes/admin/core/users/roles/page.tsx b/packages/vitnode/src/routes/admin/core/users/roles/page.tsx
index 03c0cfdbc..322e7aec5 100644
--- a/packages/vitnode/src/routes/admin/core/users/roles/page.tsx
+++ b/packages/vitnode/src/routes/admin/core/users/roles/page.tsx
@@ -48,7 +48,7 @@ export default async function Page(
{canCreate && }
- }>
+ }>
diff --git a/packages/vitnode/src/routes/breadcrumb/admin/content/[...slug]/page.tsx b/packages/vitnode/src/routes/breadcrumb/admin/content/[...slug]/page.tsx
index 4602753f3..765b8e3dd 100644
--- a/packages/vitnode/src/routes/breadcrumb/admin/content/[...slug]/page.tsx
+++ b/packages/vitnode/src/routes/breadcrumb/admin/content/[...slug]/page.tsx
@@ -1,71 +1,16 @@
-import { getTranslations } from "next-intl/server";
+import React from "react";
-import { contentTypeName } from "@/content/admin/labels";
-import {
- CONTENT_ADMIN_CREATE_SEGMENT,
- CONTENT_ADMIN_EDIT_SEGMENT,
-} from "@/content/const";
-import { contentAdminHref } from "@/content/registry";
-import { BreadcrumbAdmin } from "@/views/admin/layouts/breadcrumb/breadcrumb-admin";
-import {
- getContentLabels,
- resolveContentRoute,
-} from "@/views/admin/views/content/content-admin-view";
+import { BreadcrumbContentAdmin } from "@/views/admin/layouts/breadcrumb/breadcrumb-content-admin";
+import { BreadcrumbSkeleton } from "@/views/breadcrumb/breadcrumb-render";
-/**
- * The breadcrumb of every generated Content Engine screen.
- *
- * The list keeps the trail it always had. A create or an edit **page** appends
- * one more crumb, labelled from `core.content` with the content type's own
- * singular - so it reads "Blog / Articles / Create article" in whatever language
- * the AdminCP is in, and "Articles" becomes a link back to the list.
- *
- * The record id is deliberately **not** a crumb of its own: `/42/` would render
- * as a dead "42" between two words, and the page it would point at is the one
- * being read.
- */
-export default async function BreadcrumbSlot({
+export default function BreadcrumbSlot({
params,
}: {
params: Promise<{ slug: string[] }>;
}) {
- const { slug } = await params;
- const route = await resolveContentRoute(params);
- const labels = route ? await getContentLabels(route.entry) : undefined;
-
- if (!route || route.action === "list") {
- return (
-
- );
- }
-
- const t = await getTranslations("core.content");
- const { definition } = route.entry;
-
return (
-
+ }>
+
+
);
}
diff --git a/packages/vitnode/src/routes/breadcrumb/main/[...rest]/page.tsx b/packages/vitnode/src/routes/breadcrumb/main/[...rest]/page.tsx
index e66aa3f96..88eab8cc1 100644
--- a/packages/vitnode/src/routes/breadcrumb/main/[...rest]/page.tsx
+++ b/packages/vitnode/src/routes/breadcrumb/main/[...rest]/page.tsx
@@ -1,3 +1,5 @@
+export const instant = false;
+
export default function BreadcrumbSlot() {
return null;
}
diff --git a/packages/vitnode/src/routes/main/files/page.tsx b/packages/vitnode/src/routes/main/files/page.tsx
index 2c61f1b63..14b981e1c 100644
--- a/packages/vitnode/src/routes/main/files/page.tsx
+++ b/packages/vitnode/src/routes/main/files/page.tsx
@@ -14,6 +14,8 @@ const MyFilesTableView = dynamic(async () =>
})),
);
+export const instant = false;
+
export const generateMetadata = async () => {
const t = await getTranslations("core.files");
@@ -41,7 +43,7 @@ export default async function Page(
- }>
+ }>
diff --git a/packages/vitnode/src/routes/main/login/page.tsx b/packages/vitnode/src/routes/main/login/page.tsx
index bccf3d75d..a71ef5373 100644
--- a/packages/vitnode/src/routes/main/login/page.tsx
+++ b/packages/vitnode/src/routes/main/login/page.tsx
@@ -4,13 +4,8 @@ import { getTranslations } from "next-intl/server";
import { SignInView } from "@/views/auth/sign-in/sign-in-view";
-export const generateMetadata = async ({
- params,
-}: {
- params: Promise<{ locale: string }>;
-}): Promise => {
- const { locale } = await params;
- const t = await getTranslations({ locale, namespace: "core.global" });
+export const generateMetadata = async (): Promise => {
+ const t = await getTranslations("core.global");
return {
title: t("login"),
diff --git a/packages/vitnode/src/routes/main/login/reset-password/page.tsx b/packages/vitnode/src/routes/main/login/reset-password/page.tsx
index e9f49711e..5aba343d6 100644
--- a/packages/vitnode/src/routes/main/login/reset-password/page.tsx
+++ b/packages/vitnode/src/routes/main/login/reset-password/page.tsx
@@ -4,16 +4,8 @@ import { getTranslations } from "next-intl/server";
import { PasswordResetView } from "@/views/auth/password-reset/password-reset-view";
-export const generateMetadata = async ({
- params,
-}: {
- params: Promise<{ locale: string }>;
-}): Promise => {
- const { locale } = await params;
- const t = await getTranslations({
- locale,
- namespace: "core.auth.reset_password",
- });
+export const generateMetadata = async (): Promise => {
+ const t = await getTranslations("core.auth.reset_password");
return {
title: t("title"),
diff --git a/packages/vitnode/src/routes/main/login/sso/[providerId]/page.tsx b/packages/vitnode/src/routes/main/login/sso/[providerId]/page.tsx
index b9d1b7895..130c90443 100644
--- a/packages/vitnode/src/routes/main/login/sso/[providerId]/page.tsx
+++ b/packages/vitnode/src/routes/main/login/sso/[providerId]/page.tsx
@@ -1,5 +1,7 @@
import { CallbackSSOView } from "@/views/auth/sso/callback/callback-sso-view";
+export const instant = false;
+
export default async function Page({
params,
searchParams,
diff --git a/packages/vitnode/src/routes/main/register/page.tsx b/packages/vitnode/src/routes/main/register/page.tsx
index 3c92bd555..0e352dbf6 100644
--- a/packages/vitnode/src/routes/main/register/page.tsx
+++ b/packages/vitnode/src/routes/main/register/page.tsx
@@ -4,13 +4,8 @@ import { getTranslations } from "next-intl/server";
import { SignUpView } from "@/views/auth/sign-up/sign-up-view";
-export const generateMetadata = async ({
- params,
-}: {
- params: Promise<{ locale: string }>;
-}): Promise => {
- const { locale } = await params;
- const t = await getTranslations({ locale, namespace: "core.global" });
+export const generateMetadata = async (): Promise => {
+ const t = await getTranslations("core.global");
return {
title: t("register"),
diff --git a/packages/vitnode/src/routes/main/settings/layout.tsx b/packages/vitnode/src/routes/main/settings/layout.tsx
index 68695a6e5..903da51c4 100644
--- a/packages/vitnode/src/routes/main/settings/layout.tsx
+++ b/packages/vitnode/src/routes/main/settings/layout.tsx
@@ -9,6 +9,8 @@ export const metadata: Metadata = {
},
};
+export const instant = false;
+
export default function Layout(
props: React.ComponentProps,
) {
diff --git a/packages/vitnode/src/views/admin/layouts/breadcrumb/breadcrumb-content-admin.tsx b/packages/vitnode/src/views/admin/layouts/breadcrumb/breadcrumb-content-admin.tsx
new file mode 100644
index 000000000..7fe522fc2
--- /dev/null
+++ b/packages/vitnode/src/views/admin/layouts/breadcrumb/breadcrumb-content-admin.tsx
@@ -0,0 +1,76 @@
+import { getTranslations } from "next-intl/server";
+
+import { contentTypeName } from "@/content/admin/labels";
+import {
+ CONTENT_ADMIN_CREATE_SEGMENT,
+ CONTENT_ADMIN_EDIT_SEGMENT,
+} from "@/content/const";
+import { contentAdminHref } from "@/content/registry";
+import {
+ getContentLabels,
+ resolveContentRoute,
+} from "@/views/admin/views/content/content-admin-view";
+
+import { BreadcrumbAdmin } from "./breadcrumb-admin";
+
+/**
+ * The breadcrumb of every generated Content Engine screen.
+ *
+ * The list keeps the trail it always had. A create or an edit **page** appends
+ * one more crumb, labelled from `core.content` with the content type's own
+ * singular - so it reads "Blog / Articles / Create article" in whatever language
+ * the AdminCP is in, and "Articles" becomes a link back to the list.
+ *
+ * The record id is deliberately **not** a crumb of its own: `/42/` would render
+ * as a dead "42" between two words, and the page it would point at is the one
+ * being read.
+ *
+ * Takes `params` as the promise the slot was handed rather than the resolved
+ * slug: awaiting it above the slot's `` would pull the URL back into
+ * the AdminCP's shared App Shell.
+ */
+export const BreadcrumbContentAdmin = async ({
+ params,
+}: {
+ params: Promise<{ slug: string[] }>;
+}) => {
+ const { slug } = await params;
+ const route = await resolveContentRoute(params);
+ const labels = route ? await getContentLabels(route.entry) : undefined;
+
+ if (!route || route.action === "list") {
+ return (
+
+ );
+ }
+
+ const t = await getTranslations("core.content");
+ const { definition } = route.entry;
+
+ return (
+
+ );
+};
diff --git a/packages/vitnode/src/views/admin/layouts/search/search-dialog.tsx b/packages/vitnode/src/views/admin/layouts/search/search-dialog.tsx
index 013d31026..2c157c4c1 100644
--- a/packages/vitnode/src/views/admin/layouts/search/search-dialog.tsx
+++ b/packages/vitnode/src/views/admin/layouts/search/search-dialog.tsx
@@ -90,6 +90,7 @@ export const SearchAdminDialog = ({
const [query, setQuery] = React.useState("");
const [usersQuery, setUsersQuery] = React.useState("");
+ const isOpen = React.useDeferredValue(open, false);
const debounceUsersQuery = useDebouncedCallback(
setUsersQuery,
@@ -106,7 +107,9 @@ export const SearchAdminDialog = ({
const trimmedUsersQuery = usersQuery.trim();
const canSearchUsers =
- open && canViewUsers && trimmedUsersQuery.length >= MIN_USERS_QUERY_LENGTH;
+ isOpen &&
+ canViewUsers &&
+ trimmedUsersQuery.length >= MIN_USERS_QUERY_LENGTH;
const { data: users, isFetching: isFetchingUsers } = useQuery({
queryKey: ["admin-search-users", trimmedUsersQuery],
@@ -185,7 +188,7 @@ export const SearchAdminDialog = ({
push(href);
}
}}
- open={open}
+ open={isOpen}
title={t("search.title")}
>
diff --git a/packages/vitnode/src/views/admin/views/content/content-admin-view.tsx b/packages/vitnode/src/views/admin/views/content/content-admin-view.tsx
index 51661db95..0040154de 100644
--- a/packages/vitnode/src/views/admin/views/content/content-admin-view.tsx
+++ b/packages/vitnode/src/views/admin/views/content/content-admin-view.tsx
@@ -8,6 +8,7 @@ import type { ContentAdminRoute } from "@/content/admin/route";
import { I18nProvider } from "@/components/i18n-provider";
import { DataTableSkeleton } from "@/components/table/data-table";
import { HeaderContent } from "@/components/ui/header-content";
+import { Skeleton } from "@/components/ui/skeleton";
import {
findFrontendContentType,
findFrontendContentTypeByAdminPath,
@@ -36,13 +37,6 @@ export interface ContentAdminViewProps {
searchParams: Promise>;
}
-/**
- * Resolves what the catch-all slug was asking for: which content type, and
- * whether it wants the list, the create page or an edit page.
- *
- * Shared with `generateMetadata` and the breadcrumb slot, so all three agree
- * about a URL rather than each parsing it their own way.
- */
export const resolveContentRoute = async (
params: ContentAdminViewProps["params"],
): Promise<
@@ -183,7 +177,12 @@ const ContentListView = async ({
}
+ fallback={
+ 0}
+ />
+ }
>
);
};
+
+export const ContentAdminViewSkeleton = () => (
+
+
+
+
+ }
+ h1={
+
+
+
+ }
+ >
+
+
+
+
+
+);
diff --git a/packages/vitnode/src/views/admin/views/core/advanced/search/search-view.tsx b/packages/vitnode/src/views/admin/views/core/advanced/search/search-view.tsx
index 7319d2275..d41186640 100644
--- a/packages/vitnode/src/views/admin/views/core/advanced/search/search-view.tsx
+++ b/packages/vitnode/src/views/admin/views/core/advanced/search/search-view.tsx
@@ -165,7 +165,7 @@ export const SearchAdminViewSkeleton = () => (
-
+
diff --git a/packages/vitnode/src/views/admin/views/core/dashboard/dashboard-admin-view.tsx b/packages/vitnode/src/views/admin/views/core/dashboard/dashboard-admin-view.tsx
index 1e8ed20bb..79f551144 100644
--- a/packages/vitnode/src/views/admin/views/core/dashboard/dashboard-admin-view.tsx
+++ b/packages/vitnode/src/views/admin/views/core/dashboard/dashboard-admin-view.tsx
@@ -1,22 +1,16 @@
import { AlertTriangleIcon } from "lucide-react";
import { getTranslations } from "next-intl/server";
+import React from "react";
-import { adminModule } from "@/api/modules/admin/admin.module";
import { I18nProvider } from "@/components/i18n-provider";
import { Badge } from "@/components/ui/badge";
-import { HeaderContent } from "@/components/ui/header-content";
import { getSessionAdminApi } from "@/lib/api/get-session-admin-api";
import { CONFIG } from "@/lib/config";
-import { fetcher } from "@/lib/fetcher";
-import type { DashboardWidgetCatalogEntry } from "./widgets/types";
+import type { DashboardHeaderContent } from "./widgets/types";
-import { DashboardBoardProvider } from "./grid/board-provider";
-import { DashboardGrid } from "./grid/dashboard-grid";
-import { DashboardEditActions } from "./grid/edit-actions";
-import { getDashboardWidgets } from "./widgets/get-dashboard-widgets";
-import { widgetIdOf } from "./widgets/instance-id";
-import { normalizeLayout } from "./widgets/normalize-layout";
+import { DashboardBoard } from "./dashboard-board";
+import { DashboardBoardSkeleton } from "./dashboard-board-skeleton";
export const DashboardAdminView = async () => {
const session = await getSessionAdminApi();
@@ -24,95 +18,29 @@ export const DashboardAdminView = async () => {
if (!session) return null;
const { vitnode_version } = session;
- const [widgets, res] = await Promise.all([
- getDashboardWidgets(),
- fetcher(adminModule, {
- path: "/",
- method: "get",
- module: "admin/dashboard",
- }),
- ]);
-
- const saved = res.ok ? (await res.json()).widgets : [];
- const layout = normalizeLayout({ saved, widgets });
-
- const widgetIds = new Set(widgets.map(({ id }) => id));
- const managedIds = [
- ...new Set([
- ...saved
- .filter(item => widgetIds.has(widgetIdOf(item.id)))
- .map(item => item.id),
- ...layout.map(item => item.id),
- ]),
- ];
-
- const placedWidgetIds = new Set(layout.map(item => widgetIdOf(item.id)));
- const content: Record = {};
-
- for (const item of layout) {
- const widget = widgets.find(({ id }) => id === widgetIdOf(item.id));
- if (!widget) continue;
-
- const Widget = widget.component;
- content[item.id] = (
-
- );
- }
-
- const catalog: DashboardWidgetCatalogEntry[] = widgets.map(widget => {
- const Widget = widget.component;
- const needsStandIn =
- !!widget.allowMultiple || !placedWidgetIds.has(widget.id);
-
- return {
- id: widget.id,
- title: widget.title,
- desc: widget.desc,
- icon: widget.icon,
- category: widget.category,
- allowMultiple: widget.allowMultiple,
- minSpan: widget.minSpan,
- defaultSpan: widget.defaultSpan,
- defaultRows: widget.defaultRows,
-
- content: needsStandIn ? (
-
- ) : null,
-
- hasSettings: !!widget.settingsComponent,
- };
- });
+ const header: DashboardHeaderContent = {
+ desc: t("version", { version: vitnode_version }),
+ h1: (
+ <>
+ VitNode
+ {CONFIG.node_development && (
+
+ {t("dev_mode")}
+
+ )}
+ >
+ ),
+ };
return (
-
-
- VitNode
- {CONFIG.node_development && (
-
- {t("dev_mode")}
-
- )}
- >
- }
- >
-
-
-
-
-
+ }>
+
+
);
diff --git a/packages/vitnode/src/views/admin/views/core/dashboard/dashboard-board-skeleton.tsx b/packages/vitnode/src/views/admin/views/core/dashboard/dashboard-board-skeleton.tsx
new file mode 100644
index 000000000..259e5372c
--- /dev/null
+++ b/packages/vitnode/src/views/admin/views/core/dashboard/dashboard-board-skeleton.tsx
@@ -0,0 +1,47 @@
+import { Card, CardContent, CardHeader } from "@/components/ui/card";
+import { HeaderContent } from "@/components/ui/header-content";
+import { Skeleton } from "@/components/ui/skeleton";
+import { cn } from "@/lib/utils";
+
+import type { DashboardHeaderContent } from "./widgets/types";
+
+import { gridClasses, rowsClasses, spanClasses } from "./grid/span-classes";
+import { WidgetContentSkeleton } from "./grid/widget-skeleton";
+
+/**
+ * Sized after the widgets core ships enabled, so the board that streams in
+ * lands close to the placeholder on the common first load. An admin who has
+ * arranged their own is the case this cannot predict.
+ */
+const PLACEHOLDER_CARDS = [
+ { id: "s-wide", rows: 2, span: 2 },
+ { id: "s-short", rows: 1, span: 2 },
+] as const;
+
+export const DashboardBoardSkeleton = ({
+ header,
+}: {
+ header: DashboardHeaderContent;
+}) => (
+ <>
+
+
+
+
+
+ {PLACEHOLDER_CARDS.map(({ id, rows, span }) => (
+
+
+
+
+
+
+
+
+ ))}
+
+ >
+);
diff --git a/packages/vitnode/src/views/admin/views/core/dashboard/dashboard-board.tsx b/packages/vitnode/src/views/admin/views/core/dashboard/dashboard-board.tsx
new file mode 100644
index 000000000..27838e8a9
--- /dev/null
+++ b/packages/vitnode/src/views/admin/views/core/dashboard/dashboard-board.tsx
@@ -0,0 +1,95 @@
+import { adminModule } from "@/api/modules/admin/admin.module";
+import { HeaderContent } from "@/components/ui/header-content";
+import { fetcher } from "@/lib/fetcher";
+
+import type {
+ DashboardHeaderContent,
+ DashboardWidgetCatalogEntry,
+} from "./widgets/types";
+
+import { DashboardBoardProvider } from "./grid/board-provider";
+import { DashboardGrid } from "./grid/dashboard-grid";
+import { DashboardEditActions } from "./grid/edit-actions";
+import { getDashboardWidgets } from "./widgets/get-dashboard-widgets";
+import { widgetIdOf } from "./widgets/instance-id";
+import { normalizeLayout } from "./widgets/normalize-layout";
+
+export const DashboardBoard = async ({
+ header,
+}: {
+ header: DashboardHeaderContent;
+}) => {
+ const [widgets, res] = await Promise.all([
+ getDashboardWidgets(),
+ fetcher(adminModule, {
+ path: "/",
+ method: "get",
+ module: "admin/dashboard",
+ }),
+ ]);
+
+ const saved = res.ok ? (await res.json()).widgets : [];
+ const layout = normalizeLayout({ saved, widgets });
+
+ const widgetIds = new Set(widgets.map(({ id }) => id));
+ const managedIds = [
+ ...new Set([
+ ...saved
+ .filter(item => widgetIds.has(widgetIdOf(item.id)))
+ .map(item => item.id),
+ ...layout.map(item => item.id),
+ ]),
+ ];
+
+ const placedWidgetIds = new Set(layout.map(item => widgetIdOf(item.id)));
+ const content: Record = {};
+
+ for (const item of layout) {
+ const widget = widgets.find(({ id }) => id === widgetIdOf(item.id));
+ if (!widget) continue;
+
+ const Widget = widget.component;
+ content[item.id] = (
+
+ );
+ }
+
+ const catalog: DashboardWidgetCatalogEntry[] = widgets.map(widget => {
+ const Widget = widget.component;
+ const needsStandIn =
+ !!widget.allowMultiple || !placedWidgetIds.has(widget.id);
+
+ return {
+ id: widget.id,
+ title: widget.title,
+ desc: widget.desc,
+ icon: widget.icon,
+ category: widget.category,
+ allowMultiple: widget.allowMultiple,
+ minSpan: widget.minSpan,
+ defaultSpan: widget.defaultSpan,
+ defaultRows: widget.defaultRows,
+
+ content: needsStandIn ? (
+
+ ) : null,
+
+ hasSettings: !!widget.settingsComponent,
+ };
+ });
+
+ return (
+
+
+
+
+
+
+
+ );
+};
diff --git a/packages/vitnode/src/views/admin/views/core/dashboard/widgets/types.ts b/packages/vitnode/src/views/admin/views/core/dashboard/widgets/types.ts
index ed942ce89..5ee0af2de 100644
--- a/packages/vitnode/src/views/admin/views/core/dashboard/widgets/types.ts
+++ b/packages/vitnode/src/views/admin/views/core/dashboard/widgets/types.ts
@@ -20,6 +20,17 @@ export interface DashboardLayoutItem extends AdminDashboardWidgetLayoutItem {
span: AdminDashboardWidgetSpan;
}
+/**
+ * The dashboard's own heading, resolved above the board's ``
+ * boundary so the fallback and the board render the same one. The board cannot
+ * own it: `DashboardEditActions` sits in its action slot and reads the board's
+ * context, so a fallback rendering the header alone would throw.
+ */
+export interface DashboardHeaderContent {
+ desc: React.ReactNode;
+ h1: React.ReactNode;
+}
+
/**
* The heading a widget sits under in the panel: either a category its own
* plugin defined, or - by default - the plugin itself.
diff --git a/packages/vitnode/src/views/auth/password-reset/password-reset-view.tsx b/packages/vitnode/src/views/auth/password-reset/password-reset-view.tsx
index bfb08cd52..2efb2158f 100644
--- a/packages/vitnode/src/views/auth/password-reset/password-reset-view.tsx
+++ b/packages/vitnode/src/views/auth/password-reset/password-reset-view.tsx
@@ -1,13 +1,15 @@
import { notFound } from "next/navigation";
+import React from "react";
import { I18nProvider } from "@/components/i18n-provider";
-import { Card } from "@/components/ui/card";
+import { Card, CardContent, CardHeader } from "@/components/ui/card";
+import { Skeleton } from "@/components/ui/skeleton";
import { getMiddlewareApi } from "@/lib/api/get-middleware-api";
import { ChangePasswordForm } from "./change-password-form/form";
import { PasswordResetForm } from "./form/form";
-export const PasswordResetView = async ({
+const PasswordResetContent = async ({
searchParams,
}: {
searchParams: Promise<{ token: string; userId: string }>;
@@ -16,25 +18,53 @@ export const PasswordResetView = async ({
getMiddlewareApi(),
searchParams,
]);
+
if (!isEmail) notFound();
+ if (token && userId) {
+ return (
+
+
+
+ );
+ }
+
return (
-
-
- {token && userId ? (
-
-
-
- ) : (
-
-
-
- )}
-
-
+
+
+
);
};
+
+const PasswordResetContentSkeleton = () => (
+ <>
+
+
+
+
+
+
+
+
+
+
+ >
+);
+
+export const PasswordResetView = ({
+ searchParams,
+}: {
+ searchParams: Promise<{ token: string; userId: string }>;
+}) => (
+
+);
diff --git a/packages/vitnode/src/views/auth/settings/devices/devices-list-skeleton.tsx b/packages/vitnode/src/views/auth/settings/devices/devices-list-skeleton.tsx
new file mode 100644
index 000000000..4c8884bdd
--- /dev/null
+++ b/packages/vitnode/src/views/auth/settings/devices/devices-list-skeleton.tsx
@@ -0,0 +1,28 @@
+import { Separator } from "@/components/ui/separator";
+import { Skeleton } from "@/components/ui/skeleton";
+
+const DETAIL_IDS = Array.from({ length: 3 }, (_, i) => `s-detail-${i}`);
+
+export const DevicesListSkeleton = () => (
+
+
+
+
+
+
+ {DETAIL_IDS.map(id => (
+
+
+
+
+ ))}
+
+
+);
diff --git a/packages/vitnode/src/views/auth/settings/devices/devices-list.tsx b/packages/vitnode/src/views/auth/settings/devices/devices-list.tsx
new file mode 100644
index 000000000..3a1e7c825
--- /dev/null
+++ b/packages/vitnode/src/views/auth/settings/devices/devices-list.tsx
@@ -0,0 +1,24 @@
+import { getTranslations } from "next-intl/server";
+
+import { getDevicesApi } from "@/lib/api/get-devices-api";
+
+import { DeviceItem } from "./device-item";
+
+export const DevicesList = async () => {
+ const [t, { devices }] = await Promise.all([
+ getTranslations("core.auth.settings.devices"),
+ getDevicesApi(),
+ ]);
+
+ if (devices.length === 0) {
+ return {t("empty")}
;
+ }
+
+ return (
+
+ {devices.map(device => (
+
+ ))}
+
+ );
+};
diff --git a/packages/vitnode/src/views/auth/settings/devices/devices.tsx b/packages/vitnode/src/views/auth/settings/devices/devices.tsx
index 1395ef6ba..551f32e7f 100644
--- a/packages/vitnode/src/views/auth/settings/devices/devices.tsx
+++ b/packages/vitnode/src/views/auth/settings/devices/devices.tsx
@@ -1,27 +1,21 @@
import { getTranslations } from "next-intl/server";
+import React from "react";
import { HeaderContent } from "@/components/ui/header-content";
-import { getDevicesApi } from "@/lib/api/get-devices-api";
-import { DeviceItem } from "./device-item";
+import { DevicesList } from "./devices-list";
+import { DevicesListSkeleton } from "./devices-list-skeleton";
export const DevicesSettings = async () => {
const t = await getTranslations("core.auth.settings.devices");
- const { devices } = await getDevicesApi();
return (
<>
- {devices.length === 0 ? (
- {t("empty")}
- ) : (
-
- {devices.map(device => (
-
- ))}
-
- )}
+ }>
+
+
>
);
};
diff --git a/packages/vitnode/src/views/auth/sign-in/sign-in-view.tsx b/packages/vitnode/src/views/auth/sign-in/sign-in-view.tsx
index 221f8ae59..7ce4e2048 100644
--- a/packages/vitnode/src/views/auth/sign-in/sign-in-view.tsx
+++ b/packages/vitnode/src/views/auth/sign-in/sign-in-view.tsx
@@ -2,6 +2,7 @@ import { getTranslations } from "next-intl/server";
import React from "react";
import { Card, CardDescription } from "@/components/ui/card";
+import { Skeleton } from "@/components/ui/skeleton";
import { getMiddlewareApi } from "@/lib/api/get-middleware-api";
import { Link } from "@/lib/navigation";
@@ -9,11 +10,32 @@ import { I18nProvider } from "../../../components/i18n-provider";
import { SSOButtons, SSOButtonsSkeleton } from "../sso/buttons/sso-buttons";
import { FormSignIn } from "./form/form";
+const SignInForm = async () => {
+ const { isEmail } = await getMiddlewareApi();
+
+ return ;
+};
+
+const SignInFormSkeleton = () => (
+
+);
+
export const SignInView = async () => {
- const [t, tGlobal, { isEmail }] = await Promise.all([
+ const [t, tGlobal] = await Promise.all([
getTranslations("core.auth.sign_in"),
getTranslations("core.global"),
- getMiddlewareApi(),
]);
return (
@@ -27,7 +49,10 @@ export const SignInView = async () => {
{t("desc")}
-
+
+ }>
+
+
}>
diff --git a/packages/vitnode/src/views/auth/sign-up/sign-up-view.tsx b/packages/vitnode/src/views/auth/sign-up/sign-up-view.tsx
index d3e316527..ee5c2933a 100644
--- a/packages/vitnode/src/views/auth/sign-up/sign-up-view.tsx
+++ b/packages/vitnode/src/views/auth/sign-up/sign-up-view.tsx
@@ -2,6 +2,7 @@ import { getTranslations } from "next-intl/server";
import React from "react";
import { Card, CardDescription } from "@/components/ui/card";
+import { Skeleton } from "@/components/ui/skeleton";
import { getMiddlewareApi } from "@/lib/api/get-middleware-api";
import { Link } from "@/lib/navigation";
@@ -10,11 +11,34 @@ import { SSOButtons, SSOButtonsSkeleton } from "../sso/buttons/sso-buttons";
import { FormSignUp } from "./form/form";
import { WrapperSignUp } from "./wrapper";
+const SignUpForm = async () => {
+ const { isEmail, captcha } = await getMiddlewareApi();
+
+ return ;
+};
+
+const SignUpFormSkeleton = () => (
+
+ {[0, 1, 2].map(field => (
+
+
+
+
+ ))}
+
+
+
+
+
+
+
+
+);
+
export const SignUpView = async () => {
- const [t, tGlobal, { isEmail, captcha }] = await Promise.all([
+ const [t, tGlobal] = await Promise.all([
getTranslations("core.auth.sign_up"),
getTranslations("core.global"),
- getMiddlewareApi(),
]);
return (
@@ -29,7 +53,10 @@ export const SignUpView = async () => {
{t("desc")}
-
+
+ }>
+
+
}>
diff --git a/packages/vitnode/src/views/breadcrumb/breadcrumb-render.tsx b/packages/vitnode/src/views/breadcrumb/breadcrumb-render.tsx
index 899fa9ef0..59b6ac588 100644
--- a/packages/vitnode/src/views/breadcrumb/breadcrumb-render.tsx
+++ b/packages/vitnode/src/views/breadcrumb/breadcrumb-render.tsx
@@ -8,6 +8,7 @@ import {
BreadcrumbPage,
BreadcrumbSeparator,
} from "@/components/ui/breadcrumb";
+import { Skeleton } from "@/components/ui/skeleton";
import { Link } from "@/lib/navigation";
import { cn } from "@/lib/utils";
@@ -56,3 +57,25 @@ export const BreadcrumbRender = ({
);
};
+
+/**
+ * Placeholder trail for a breadcrumb that resolves its labels from the URL.
+ *
+ * Built from the same list and separators as {@link BreadcrumbRender} so the
+ * crumbs land on the row they will occupy once they arrive, rather than moving
+ * the header's contents when the boundary resolves.
+ */
+export const BreadcrumbSkeleton = ({ crumbs = 2 }: { crumbs?: number }) => (
+
+
+ {Array.from({ length: crumbs }, (_, index) => (
+
+ {index > 0 && }
+
+
+
+
+ ))}
+
+
+);
diff --git a/packages/vitnode/src/views/layouts/root-layout.tsx b/packages/vitnode/src/views/layouts/root-layout.tsx
index 6f3e89d9c..4d5fbbd4e 100644
--- a/packages/vitnode/src/views/layouts/root-layout.tsx
+++ b/packages/vitnode/src/views/layouts/root-layout.tsx
@@ -1,8 +1,6 @@
import type { Metadata } from "next/dist/types";
import type React from "react";
-import { setRequestLocale } from "next-intl/server";
-
import type { VitNodeConfig } from "@/vitnode.config";
import { I18nProvider } from "@/components/i18n-provider";
@@ -12,7 +10,13 @@ import { RootProvider } from "./provider";
export interface RootLayoutProps {
children: React.ReactNode;
- params: Promise<{
+ /**
+ * Still handed over by Next, and still spread through by app layouts, but no
+ * longer read here: the locale now comes from `next/root-params` via the
+ * next-intl request config. Optional so a layout that has stopped destructuring
+ * it type-checks.
+ */
+ params?: Promise<{
locale: string;
}>;
}
@@ -28,16 +32,12 @@ export const generateMetadataRootLayout = ({
};
};
-export const RootLayout = async ({
+export const RootLayout = ({
children,
- params,
config,
}: RootLayoutProps & {
config: VitNodeConfig;
}) => {
- const { locale } = await params;
- setRequestLocale(locale);
-
return (
`:
+ * awaiting it in the layout body pulled every themed route into request time,
+ * and nothing above this point needs the user.
+ */
+const WebSocketAuthSyncSession = async () => {
+ const session = await getSessionApi();
+
+ return ;
+};
+
+export const ThemeLayout = ({
children,
logo,
vitNodeConfig,
@@ -16,13 +30,13 @@ export const ThemeLayout = async ({
children: React.ReactNode;
vitNodeConfig: VitNodeConfig;
}) => {
- const session = await getSessionApi();
- const user = session?.user ?? null;
-
return (
<>
-
+ {/* Renders nothing, so an empty fallback costs no layout shift. */}
+
+
+
{breadcrumb}
{children}
diff --git a/packages/vitnode/src/vitnode.config.ts b/packages/vitnode/src/vitnode.config.ts
index 25f471457..6166f6756 100644
--- a/packages/vitnode/src/vitnode.config.ts
+++ b/packages/vitnode/src/vitnode.config.ts
@@ -21,10 +21,6 @@ import type {
} from "./lib/i18n/types";
import type { BuildPluginReturn } from "./lib/plugin";
-import { CONFIG_PLUGIN } from "./config";
-import { loadMessages } from "./lib/i18n/load-messages";
-import { buildMessagesSources } from "./lib/i18n/sources";
-
export type { LocaleConfig };
export interface VitNodeConfig<
@@ -191,37 +187,3 @@ export const getVitNodeConfig = (): VitNodeConfig => {
export function buildApiConfig(args: VitNodeApiConfig): VitNodeApiConfig {
return args;
}
-
-export const handleRequestConfig = async ({
- requestLocale,
- vitNodeConfig,
-}: {
- requestLocale: Promise;
- vitNodeConfig: VitNodeConfig;
-}) => {
- const reqLocale = await requestLocale;
- const localeCodes = vitNodeConfig.i18n.locales.map(locale => locale.code);
- const locale =
- reqLocale && localeCodes.includes(reqLocale)
- ? reqLocale
- : vitNodeConfig.i18n.defaultLocale;
-
- const sources = buildMessagesSources({
- appMessages: vitNodeConfig.i18n.messages,
- plugins: vitNodeConfig.plugins,
- });
-
- return {
- locale,
- messages: await loadMessages({
- defaultLocale: vitNodeConfig.i18n.defaultLocale,
- locale,
- sources,
- }),
- pluginIds: [
- CONFIG_PLUGIN.pluginId,
- ...vitNodeConfig.plugins.map(plugin => plugin.pluginId),
- ],
- timeZone: vitNodeConfig.i18n.timeZone,
- };
-};
diff --git a/plugins/blog/package.json b/plugins/blog/package.json
index 217b366d7..29d7e807f 100644
--- a/plugins/blog/package.json
+++ b/plugins/blog/package.json
@@ -45,8 +45,8 @@
"drizzle-orm": "1.0.0-rc.4",
"hono": "^4.12.31",
"lucide-react": "^1.25.0",
- "next": "16.3.0-preview.9",
- "next-intl": "^4.13.3",
+ "next": "16.3.1",
+ "next-intl": "^4.13.7",
"react": "^19.2.8",
"react-dom": "^19.2.8",
"react-email": "^6.9.0",
diff --git a/plugins/example/package.json b/plugins/example/package.json
index 0d69c1cbf..b4b508d9f 100644
--- a/plugins/example/package.json
+++ b/plugins/example/package.json
@@ -28,8 +28,8 @@
"drizzle-orm": "1.0.0-rc.4",
"hono": "^4.12.31",
"lucide-react": "^1.25.0",
- "next": "16.3.0-preview.9",
- "next-intl": "^4.13.3",
+ "next": "16.3.1",
+ "next-intl": "^4.13.7",
"react": "^19.2.8",
"react-dom": "^19.2.8",
"react-hook-form": "^7.82.0",
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index c2cb3da3a..b87d0178f 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -66,8 +66,8 @@ importers:
specifier: ^4.12.31
version: 4.12.31
next-intl:
- specifier: ^4.13.3
- version: 4.13.3(next@16.3.0-preview.9(@opentelemetry/api@1.9.1)(@playwright/test@1.61.1)(@types/node@26.1.1)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.8(react@19.2.8))(react@19.2.8))(react@19.2.8)(typescript@6.0.3)
+ specifier: ^4.13.7
+ version: 4.13.7(@swc/helpers@0.5.23)(next@16.3.1(@opentelemetry/api@1.9.1)(@playwright/test@1.61.1)(@types/node@26.1.1)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.8(react@19.2.8))(react@19.2.8))(react@19.2.8)(typescript@6.0.3)
react:
specifier: ^19.2.8
version: 19.2.8
@@ -155,13 +155,13 @@ importers:
version: 1.0.0-rc.4(@opentelemetry/api@1.9.1)(postgres@3.4.9)(zod@4.4.3)
fumadocs-core:
specifier: ^16.11.5
- version: 16.11.5(@mdx-js/mdx@3.1.1)(@types/estree-jsx@1.0.5)(@types/hast@3.0.5)(@types/mdast@4.0.4)(@types/react@19.2.17)(lucide-react@1.25.0(react@19.2.8))(next@16.3.0-preview.9(@babel/core@7.29.7)(@opentelemetry/api@1.9.1)(@playwright/test@1.61.1)(@types/node@26.1.1)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.8(react@19.2.8))(react@19.2.8))(react-dom@19.2.8(react@19.2.8))(react@19.2.8)(zod@4.4.3)
+ version: 16.11.5(@mdx-js/mdx@3.1.1)(@types/estree-jsx@1.0.5)(@types/hast@3.0.5)(@types/mdast@4.0.4)(@types/react@19.2.17)(lucide-react@1.25.0(react@19.2.8))(next@16.3.1(@babel/core@7.29.7)(@opentelemetry/api@1.9.1)(@playwright/test@1.61.1)(@types/node@26.1.1)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.8(react@19.2.8))(react@19.2.8))(react-dom@19.2.8(react@19.2.8))(react@19.2.8)(zod@4.4.3)
fumadocs-mdx:
specifier: ^15.2.0
- version: 15.2.0(@types/mdast@4.0.4)(@types/mdx@2.0.14)(@types/react@19.2.17)(fumadocs-core@16.11.5(@mdx-js/mdx@3.1.1)(@types/estree-jsx@1.0.5)(@types/hast@3.0.5)(@types/mdast@4.0.4)(@types/react@19.2.17)(lucide-react@1.25.0(react@19.2.8))(next@16.3.0-preview.9(@babel/core@7.29.7)(@opentelemetry/api@1.9.1)(@playwright/test@1.61.1)(@types/node@26.1.1)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.8(react@19.2.8))(react@19.2.8))(react-dom@19.2.8(react@19.2.8))(react@19.2.8)(zod@4.4.3))(next@16.3.0-preview.9(@babel/core@7.29.7)(@opentelemetry/api@1.9.1)(@playwright/test@1.61.1)(@types/node@26.1.1)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.8(react@19.2.8))(react@19.2.8))(react@19.2.8)(rolldown@1.1.5)(vite@8.1.5(@types/node@26.1.1)(esbuild@0.28.1)(jiti@2.7.0)(tsx@4.23.1)(yaml@2.9.0))
+ version: 15.2.0(@types/mdast@4.0.4)(@types/mdx@2.0.14)(@types/react@19.2.17)(fumadocs-core@16.11.5(@mdx-js/mdx@3.1.1)(@types/estree-jsx@1.0.5)(@types/hast@3.0.5)(@types/mdast@4.0.4)(@types/react@19.2.17)(lucide-react@1.25.0(react@19.2.8))(next@16.3.1(@babel/core@7.29.7)(@opentelemetry/api@1.9.1)(@playwright/test@1.61.1)(@types/node@26.1.1)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.8(react@19.2.8))(react@19.2.8))(react-dom@19.2.8(react@19.2.8))(react@19.2.8)(zod@4.4.3))(next@16.3.1(@babel/core@7.29.7)(@opentelemetry/api@1.9.1)(@playwright/test@1.61.1)(@types/node@26.1.1)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.8(react@19.2.8))(react@19.2.8))(react@19.2.8)(rolldown@1.1.5)(vite@8.1.5(@types/node@26.1.1)(esbuild@0.28.1)(jiti@2.7.0)(tsx@4.23.1)(yaml@2.9.0))
fumadocs-ui:
specifier: ^16.11.5
- version: 16.11.5(@types/mdx@2.0.14)(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(fumadocs-core@16.11.5(@mdx-js/mdx@3.1.1)(@types/estree-jsx@1.0.5)(@types/hast@3.0.5)(@types/mdast@4.0.4)(@types/react@19.2.17)(lucide-react@1.25.0(react@19.2.8))(next@16.3.0-preview.9(@babel/core@7.29.7)(@opentelemetry/api@1.9.1)(@playwright/test@1.61.1)(@types/node@26.1.1)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.8(react@19.2.8))(react@19.2.8))(react-dom@19.2.8(react@19.2.8))(react@19.2.8)(zod@4.4.3))(next@16.3.0-preview.9(@babel/core@7.29.7)(@opentelemetry/api@1.9.1)(@playwright/test@1.61.1)(@types/node@26.1.1)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.8(react@19.2.8))(react@19.2.8))(react-dom@19.2.8(react@19.2.8))(react@19.2.8)(tailwindcss@4.3.3)
+ version: 16.11.5(@types/mdx@2.0.14)(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(fumadocs-core@16.11.5(@mdx-js/mdx@3.1.1)(@types/estree-jsx@1.0.5)(@types/hast@3.0.5)(@types/mdast@4.0.4)(@types/react@19.2.17)(lucide-react@1.25.0(react@19.2.8))(next@16.3.1(@babel/core@7.29.7)(@opentelemetry/api@1.9.1)(@playwright/test@1.61.1)(@types/node@26.1.1)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.8(react@19.2.8))(react@19.2.8))(react-dom@19.2.8(react@19.2.8))(react@19.2.8)(zod@4.4.3))(next@16.3.1(@babel/core@7.29.7)(@opentelemetry/api@1.9.1)(@playwright/test@1.61.1)(@types/node@26.1.1)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.8(react@19.2.8))(react@19.2.8))(react-dom@19.2.8(react@19.2.8))(react@19.2.8)(tailwindcss@4.3.3)
hono:
specifier: ^4.12.31
version: 4.12.31
@@ -172,11 +172,11 @@ importers:
specifier: ^12.42.2
version: 12.42.2(react-dom@19.2.8(react@19.2.8))(react@19.2.8)
next:
- specifier: 16.3.0-preview.9
- version: 16.3.0-preview.9(@babel/core@7.29.7)(@opentelemetry/api@1.9.1)(@playwright/test@1.61.1)(@types/node@26.1.1)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.8(react@19.2.8))(react@19.2.8)
+ specifier: 16.3.1
+ version: 16.3.1(@babel/core@7.29.7)(@opentelemetry/api@1.9.1)(@playwright/test@1.61.1)(@types/node@26.1.1)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.8(react@19.2.8))(react@19.2.8)
next-intl:
- specifier: ^4.13.3
- version: 4.13.3(next@16.3.0-preview.9(@opentelemetry/api@1.9.1)(@playwright/test@1.61.1)(@types/node@26.1.1)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.8(react@19.2.8))(react@19.2.8))(react@19.2.8)(typescript@6.0.3)
+ specifier: ^4.13.7
+ version: 4.13.7(@swc/helpers@0.5.23)(next@16.3.1(@opentelemetry/api@1.9.1)(@playwright/test@1.61.1)(@types/node@26.1.1)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.8(react@19.2.8))(react@19.2.8))(react@19.2.8)(typescript@6.0.3)
react:
specifier: ^19.2.8
version: 19.2.8
@@ -197,8 +197,8 @@ importers:
version: 2.0.7(react-dom@19.2.8(react@19.2.8))(react@19.2.8)
devDependencies:
'@next/bundle-analyzer':
- specifier: ^16.2.11
- version: 16.2.11
+ specifier: ^16.3.1
+ version: 16.3.1
'@playwright/test':
specifier: ^1.61.1
version: 1.61.1
@@ -354,10 +354,10 @@ importers:
devDependencies:
'@swc/cli':
specifier: ^0.8.1
- version: 0.8.1(@swc/core@1.15.46)(chokidar@5.0.0)
+ version: 0.8.1(@swc/core@1.15.46(@swc/helpers@0.5.23))(chokidar@5.0.0)
'@swc/core':
specifier: ^1.15.46
- version: 1.15.46
+ version: 1.15.46(@swc/helpers@0.5.23)
'@vitnode/config':
specifier: workspace:*
version: link:../config
@@ -382,10 +382,10 @@ importers:
devDependencies:
'@swc/cli':
specifier: ^0.8.1
- version: 0.8.1(@swc/core@1.15.46)(chokidar@5.0.0)
+ version: 0.8.1(@swc/core@1.15.46(@swc/helpers@0.5.23))(chokidar@5.0.0)
'@swc/core':
specifier: ^1.15.46
- version: 1.15.46
+ version: 1.15.46(@swc/helpers@0.5.23)
'@vitnode/config':
specifier: workspace:*
version: link:../config
@@ -410,10 +410,10 @@ importers:
devDependencies:
'@swc/cli':
specifier: ^0.8.1
- version: 0.8.1(@swc/core@1.15.46)(chokidar@5.0.0)
+ version: 0.8.1(@swc/core@1.15.46(@swc/helpers@0.5.23))(chokidar@5.0.0)
'@swc/core':
specifier: ^1.15.46
- version: 1.15.46
+ version: 1.15.46(@swc/helpers@0.5.23)
'@types/nodemailer':
specifier: ^8.0.1
version: 8.0.1
@@ -441,10 +441,10 @@ importers:
devDependencies:
'@swc/cli':
specifier: ^0.8.1
- version: 0.8.1(@swc/core@1.15.46)(chokidar@5.0.0)
+ version: 0.8.1(@swc/core@1.15.46(@swc/helpers@0.5.23))(chokidar@5.0.0)
'@swc/core':
specifier: ^1.15.46
- version: 1.15.46
+ version: 1.15.46(@swc/helpers@0.5.23)
'@vitnode/config':
specifier: workspace:*
version: link:../config
@@ -469,10 +469,10 @@ importers:
devDependencies:
'@swc/cli':
specifier: ^0.8.1
- version: 0.8.1(@swc/core@1.15.46)(chokidar@5.0.0)
+ version: 0.8.1(@swc/core@1.15.46(@swc/helpers@0.5.23))(chokidar@5.0.0)
'@swc/core':
specifier: ^1.15.46
- version: 1.15.46
+ version: 1.15.46(@swc/helpers@0.5.23)
'@vitnode/config':
specifier: workspace:*
version: link:../config
@@ -497,10 +497,10 @@ importers:
devDependencies:
'@swc/cli':
specifier: ^0.8.1
- version: 0.8.1(@swc/core@1.15.46)(chokidar@5.0.0)
+ version: 0.8.1(@swc/core@1.15.46(@swc/helpers@0.5.23))(chokidar@5.0.0)
'@swc/core':
specifier: ^1.15.46
- version: 1.15.46
+ version: 1.15.46(@swc/helpers@0.5.23)
'@vitnode/config':
specifier: workspace:*
version: link:../config
@@ -524,7 +524,7 @@ importers:
version: 1.6.0(@date-fns/tz@1.5.0)(@types/react@19.2.17)(date-fns@4.4.0)(react-dom@19.2.8(react@19.2.8))(react@19.2.8)
'@bprogress/next':
specifier: ^3.2.12
- version: 3.2.12(next@16.3.0-preview.9(@babel/core@7.29.7)(@opentelemetry/api@1.9.1)(@playwright/test@1.61.1)(@types/node@26.1.1)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.8(react@19.2.8))(react@19.2.8))(react-dom@19.2.8(react@19.2.8))(react@19.2.8)
+ version: 3.2.12(next@16.3.1(@babel/core@7.29.7)(@opentelemetry/api@1.9.1)(@playwright/test@1.61.1)(@types/node@26.1.1)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.8(react@19.2.8))(react@19.2.8))(react-dom@19.2.8(react@19.2.8))(react@19.2.8)
'@dnd-kit/core':
specifier: ^6.3.1
version: 6.3.1(react-dom@19.2.8(react@19.2.8))(react@19.2.8)
@@ -648,10 +648,10 @@ importers:
version: 6.9.0(@babel/core@7.29.7)(@opentelemetry/api@1.9.1)(@playwright/test@1.61.1)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.8(react@19.2.8))(react@19.2.8)
'@swc/cli':
specifier: ^0.8.1
- version: 0.8.1(@swc/core@1.15.46)(chokidar@5.0.0)
+ version: 0.8.1(@swc/core@1.15.46(@swc/helpers@0.5.23))(chokidar@5.0.0)
'@swc/core':
specifier: ^1.15.46
- version: 1.15.46
+ version: 1.15.46(@swc/helpers@0.5.23)
'@types/node':
specifier: ^26.1.1
version: 26.1.1
@@ -698,11 +698,11 @@ importers:
specifier: ^1.25.0
version: 1.25.0(react@19.2.8)
next:
- specifier: 16.3.0-preview.9
- version: 16.3.0-preview.9(@babel/core@7.29.7)(@opentelemetry/api@1.9.1)(@playwright/test@1.61.1)(@types/node@26.1.1)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.8(react@19.2.8))(react@19.2.8)
+ specifier: 16.3.1
+ version: 16.3.1(@babel/core@7.29.7)(@opentelemetry/api@1.9.1)(@playwright/test@1.61.1)(@types/node@26.1.1)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.8(react@19.2.8))(react@19.2.8)
next-intl:
- specifier: ^4.13.3
- version: 4.13.3(next@16.3.0-preview.9(@opentelemetry/api@1.9.1)(@playwright/test@1.61.1)(@types/node@26.1.1)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.8(react@19.2.8))(react@19.2.8))(react@19.2.8)(typescript@6.0.3)
+ specifier: ^4.13.7
+ version: 4.13.7(@swc/helpers@0.5.23)(next@16.3.1(@opentelemetry/api@1.9.1)(@playwright/test@1.61.1)(@types/node@26.1.1)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.8(react@19.2.8))(react@19.2.8))(react@19.2.8)(typescript@6.0.3)
react:
specifier: ^19.2.8
version: 19.2.8
@@ -726,7 +726,7 @@ importers:
version: 1.9.1
tsup:
specifier: ^8.5.1
- version: 8.5.1(@swc/core@1.15.46)(jiti@2.7.0)(postcss@8.5.22)(tsx@4.23.1)(typescript@6.0.3)(yaml@2.9.0)
+ version: 8.5.1(@swc/core@1.15.46(@swc/helpers@0.5.23))(jiti@2.7.0)(postcss@8.5.22)(tsx@4.23.1)(typescript@6.0.3)(yaml@2.9.0)
tsx:
specifier: ^4.23.1
version: 4.23.1
@@ -770,11 +770,11 @@ importers:
specifier: ^1.25.0
version: 1.25.0(react@19.2.8)
next:
- specifier: 16.3.0-preview.9
- version: 16.3.0-preview.9(@babel/core@7.29.7)(@opentelemetry/api@1.9.1)(@playwright/test@1.61.1)(@types/node@26.1.1)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.8(react@19.2.8))(react@19.2.8)
+ specifier: 16.3.1
+ version: 16.3.1(@babel/core@7.29.7)(@opentelemetry/api@1.9.1)(@playwright/test@1.61.1)(@types/node@26.1.1)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.8(react@19.2.8))(react@19.2.8)
next-intl:
- specifier: ^4.13.3
- version: 4.13.3(next@16.3.0-preview.9(@opentelemetry/api@1.9.1)(@playwright/test@1.61.1)(@types/node@26.1.1)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.8(react@19.2.8))(react@19.2.8))(react@19.2.8)(typescript@6.0.3)
+ specifier: ^4.13.7
+ version: 4.13.7(@swc/helpers@0.5.23)(next@16.3.1(@opentelemetry/api@1.9.1)(@playwright/test@1.61.1)(@types/node@26.1.1)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.8(react@19.2.8))(react@19.2.8))(react@19.2.8)(typescript@6.0.3)
react:
specifier: ^19.2.8
version: 19.2.8
@@ -799,10 +799,10 @@ importers:
version: 6.9.0(@babel/core@7.29.7)(@opentelemetry/api@1.9.1)(@playwright/test@1.61.1)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.8(react@19.2.8))(react@19.2.8)
'@swc/cli':
specifier: ^0.8.1
- version: 0.8.1(@swc/core@1.15.46)(chokidar@5.0.0)
+ version: 0.8.1(@swc/core@1.15.46(@swc/helpers@0.5.23))(chokidar@5.0.0)
'@swc/core':
specifier: ^1.15.46
- version: 1.15.46
+ version: 1.15.46(@swc/helpers@0.5.23)
'@types/react':
specifier: ^19.2.17
version: 19.2.17
@@ -855,11 +855,11 @@ importers:
specifier: ^1.25.0
version: 1.25.0(react@19.2.8)
next:
- specifier: 16.3.0-preview.9
- version: 16.3.0-preview.9(@babel/core@7.29.7)(@opentelemetry/api@1.9.1)(@playwright/test@1.61.1)(@types/node@26.1.1)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.8(react@19.2.8))(react@19.2.8)
+ specifier: 16.3.1
+ version: 16.3.1(@babel/core@7.29.7)(@opentelemetry/api@1.9.1)(@playwright/test@1.61.1)(@types/node@26.1.1)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.8(react@19.2.8))(react@19.2.8)
next-intl:
- specifier: ^4.13.3
- version: 4.13.3(next@16.3.0-preview.9(@opentelemetry/api@1.9.1)(@playwright/test@1.61.1)(@types/node@26.1.1)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.8(react@19.2.8))(react@19.2.8))(react@19.2.8)(typescript@6.0.3)
+ specifier: ^4.13.7
+ version: 4.13.7(@swc/helpers@0.5.23)(next@16.3.1(@opentelemetry/api@1.9.1)(@playwright/test@1.61.1)(@types/node@26.1.1)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.8(react@19.2.8))(react@19.2.8))(react@19.2.8)(typescript@6.0.3)
react:
specifier: ^19.2.8
version: 19.2.8
@@ -878,10 +878,10 @@ importers:
devDependencies:
'@swc/cli':
specifier: ^0.8.1
- version: 0.8.1(@swc/core@1.15.46)(chokidar@5.0.0)
+ version: 0.8.1(@swc/core@1.15.46(@swc/helpers@0.5.23))(chokidar@5.0.0)
'@swc/core':
specifier: ^1.15.46
- version: 1.15.46
+ version: 1.15.46(@swc/helpers@0.5.23)
'@types/react':
specifier: ^19.2.17
version: 19.2.17
@@ -2614,14 +2614,14 @@ packages:
'@emnapi/core': ^1.7.1
'@emnapi/runtime': ^1.7.1
- '@next/bundle-analyzer@16.2.11':
- resolution: {integrity: sha512-C5228wy1RC0g7uOSvmQhrZXCuEeLIPureKkqramAkySmqY2Y0yw6K+TyAxXXvtrYe4uehnZs3YMFfJcorBRwpg==}
+ '@next/bundle-analyzer@16.3.1':
+ resolution: {integrity: sha512-/6XQeYPHM6jF1gTeJ82Mu6yXOHQIFVxzAn3DkPtHDp5v6SDXoCicBMTHloN+2h4WKFCQujSLCgLZHItJ3jN1uw==}
'@next/env@16.2.6':
resolution: {integrity: sha512-gd8HoHN4ufj73WmR3JmVolrpJR47ILK6LouP5xElPglaVxir6e1a7VzvTvDWkOoPXT9rkkTzyCxBu4yeZfZwcw==}
- '@next/env@16.3.0-preview.9':
- resolution: {integrity: sha512-BcD+/sVDfoHckHcEx6e4RaLfWrUb+DpXgL9jOc6f29RTyvNwVVtnAaPljp3ok4zFQan6RhFE7q32lSXAFjjV9A==}
+ '@next/env@16.3.1':
+ resolution: {integrity: sha512-35G3xwkQUb2oETSDjFXGrVugknoayLFBh7vSE+yAcl9IP2zT9wyGwq7297AYHR11kJld807t5f8AJBs6WBzXsQ==}
'@next/swc-darwin-arm64@16.2.6':
resolution: {integrity: sha512-ZJGkkcNfYgrrMkqOdZ7zoLa1TOy0qpcMfk/z4Mh/FKUz40gVO+HNQWqmLxf67Z5WB64DRp0dhEbyHfel+6sJUg==}
@@ -2629,8 +2629,8 @@ packages:
cpu: [arm64]
os: [darwin]
- '@next/swc-darwin-arm64@16.3.0-preview.9':
- resolution: {integrity: sha512-42OV/jDhHoKuqq+kp0RYrW1uksykLhXQeX+zx4SYFEG45ec9HnLHnsGgG1vy5W+LaSDvj3BPMBPTKMCh1vl1Ew==}
+ '@next/swc-darwin-arm64@16.3.1':
+ resolution: {integrity: sha512-ABMIu2zQ7cnNIHm5ivKGwZwUrm0pAai3yiJ/gK/rF1c1VP9UOnj7XECbMKFdVKp9I9eMYq9NoDs1WXOoowxzJw==}
engines: {node: '>= 10'}
cpu: [arm64]
os: [darwin]
@@ -2641,8 +2641,8 @@ packages:
cpu: [x64]
os: [darwin]
- '@next/swc-darwin-x64@16.3.0-preview.9':
- resolution: {integrity: sha512-j6dvZHjhjB3l+uDQiBmuajzzCyCetWdQw+eouwR0mE455b+OvPY32dUNIY+D33oTOe3f0My9wSw4Um5hxywUlA==}
+ '@next/swc-darwin-x64@16.3.1':
+ resolution: {integrity: sha512-gNG21e/UnrroeScbY/QndUEdl0mF1FRibW7BBeYUz/5ABCepjqDdEdgr592vpzMtCn/m7FTjYq3TN4TpyDnutw==}
engines: {node: '>= 10'}
cpu: [x64]
os: [darwin]
@@ -2654,8 +2654,8 @@ packages:
os: [linux]
libc: [glibc]
- '@next/swc-linux-arm64-gnu@16.3.0-preview.9':
- resolution: {integrity: sha512-2+1WzP2+ngzCkXiyzBwowgBiYaUycjAB/9b5ah5TjP0icBHOHf7oaPnjSZlZ8iq6x2G6QuR9S840jkOKNInUCw==}
+ '@next/swc-linux-arm64-gnu@16.3.1':
+ resolution: {integrity: sha512-6B6Lw016iwNUQuaJoraMMTLh6TwHzFUtxipSScD1F3YyymcrRWkobodRS2ftIOkF5vrs4zNlyUrTC5YZQ9Lz5w==}
engines: {node: '>= 10'}
cpu: [arm64]
os: [linux]
@@ -2668,8 +2668,8 @@ packages:
os: [linux]
libc: [musl]
- '@next/swc-linux-arm64-musl@16.3.0-preview.9':
- resolution: {integrity: sha512-2Tq6H0LtObGQj2s7u56TYakTJ3NmDOWnNRaT2TI17oQEUfPv3O4a22jt4xIDVkxvaYfB2L4y2hl8DGAEw1WV6g==}
+ '@next/swc-linux-arm64-musl@16.3.1':
+ resolution: {integrity: sha512-JUiPXZKK9wOhjf4MgDiH29GZLxfqOesbLtHq2pDxwH/WwscTRV2ToymnOTh1egzaZf0ueUf8T2+CeYTGHjW0Iw==}
engines: {node: '>= 10'}
cpu: [arm64]
os: [linux]
@@ -2682,8 +2682,8 @@ packages:
os: [linux]
libc: [glibc]
- '@next/swc-linux-x64-gnu@16.3.0-preview.9':
- resolution: {integrity: sha512-H2bCT2w2H/W6xRLRw4oWgOEMIyWc1tn65c46u0zC60omKig9hPWj7vrhMBqwOMTmZcFZwNqBTpJTF0LuopEfHA==}
+ '@next/swc-linux-x64-gnu@16.3.1':
+ resolution: {integrity: sha512-Uog9jsrmIRIL/lfvIp9htmskSNC7JcQsMVucXL2V2YY1y/D9IUN3LPEafqy0zRJ2cIU1SQ0V6F6TlffQ+pLAGg==}
engines: {node: '>= 10'}
cpu: [x64]
os: [linux]
@@ -2696,8 +2696,8 @@ packages:
os: [linux]
libc: [musl]
- '@next/swc-linux-x64-musl@16.3.0-preview.9':
- resolution: {integrity: sha512-WK0CI8ky1oDGwqzi8oJjtC5rfMNOUx4DMBiVr1o9mvsMwkCUkVh7n6+AAx2Qxk8llRJxpaGlBZv1fhHytsENVw==}
+ '@next/swc-linux-x64-musl@16.3.1':
+ resolution: {integrity: sha512-6yy3FT13KgUFOj5H8bl8w/6nKiJwHIvbtwh1V+1acsu+7y4tJjnemSa6mhsh53BeoVrlozE+fMgZhXH46WmjMA==}
engines: {node: '>= 10'}
cpu: [x64]
os: [linux]
@@ -2709,8 +2709,8 @@ packages:
cpu: [arm64]
os: [win32]
- '@next/swc-win32-arm64-msvc@16.3.0-preview.9':
- resolution: {integrity: sha512-COFwXsMQsStyBv4qBn6E3tKHuPVifMkpVvF2k2/I0HsLAwuelgj+5uO8jS4e+sfTcdvczdvuiG8BjJz3aAa0IQ==}
+ '@next/swc-win32-arm64-msvc@16.3.1':
+ resolution: {integrity: sha512-iOoN1QecUoGNZik536U/vtK43YwgyrCsGIkth52yIkl612n+0C9MjSnJbQAikISpb+WYRooBVhaDlUW7iZoKog==}
engines: {node: '>= 10'}
cpu: [arm64]
os: [win32]
@@ -2721,8 +2721,8 @@ packages:
cpu: [x64]
os: [win32]
- '@next/swc-win32-x64-msvc@16.3.0-preview.9':
- resolution: {integrity: sha512-RG4rEpg3ynGwTP6mnsDZHLuFbKksXkQCd9A5MDkgPY9+wYawIrc23pJYHZxyBxisuV/t+dwCgbpZoclW8icE7A==}
+ '@next/swc-win32-x64-msvc@16.3.1':
+ resolution: {integrity: sha512-d/k+PpAriUPaeMJJOG7HUSdqfEX46FEPWU1p3/nm2ACmXhj9hFEWdFODUBIpkuijXYkfL90qZzTqVPRp4BW/hw==}
engines: {node: '>= 10'}
cpu: [x64]
os: [win32]
@@ -4137,12 +4137,24 @@ packages:
'@swc/helpers':
optional: true
+ '@swc/core@1.15.47':
+ resolution: {integrity: sha512-FbsO5JcfOjfH38W/rohBRBweJeERsAuIP4f377lmkmxTcq9exjtx4SkRuZY5CdfhR2CBVwDIJegBpJDffwNsOg==}
+ engines: {node: '>=10'}
+ peerDependencies:
+ '@swc/helpers': '>=0.5.17'
+ peerDependenciesMeta:
+ '@swc/helpers':
+ optional: true
+
'@swc/counter@0.1.3':
resolution: {integrity: sha512-e2BR4lsJkkRlKZ/qCHPw9ZaSxc0MVUd7gtbtaB7aMvHeJVYe8sOB8DBZkP2DtISHGSku9sCK6T6cnY0CtXrOCQ==}
'@swc/helpers@0.5.15':
resolution: {integrity: sha512-JQ5TuMi45Owi4/BIMAJBoSQoOJu12oOk/gADqlcUL9JEdHB8vyjUSsxqeNXnmXHjYKMi2WcYtezGEEhqUI/E2g==}
+ '@swc/helpers@0.5.23':
+ resolution: {integrity: sha512-5lSsMOTXURePglDfvuAQUqkGek9Hg2kksOYay2m0+XR++b2NWYL/4sWyuvVBIs8oKnJaxkdi9whaL/sqN13afw==}
+
'@swc/types@0.1.27':
resolution: {integrity: sha512-K6h3iUlqeM946U4sXFYeahefR1YBbXJvko+hv8WS8/0BNJ4OHiHRywMnQUJCqkR7Y9+hqQ1TvEpiKqUhz7NEFg==}
@@ -6540,8 +6552,8 @@ packages:
resolution: {integrity: sha512-IKXpvIzjnC9XTAUbVBcMfGS0EPaIXtW6v+zr+RRp+hqULEpo0owZax6wyRwPOJbWbzjYspQwusTsfVr0ifh4uQ==}
engines: {node: '>=0.10.0'}
- icu-minify@4.13.3:
- resolution: {integrity: sha512-dCbcvYH4YBgR+SgCtnXGIquuc6JkS7e4n+5mmgseQ5z81KJIWbs5ucBjnrE72HFct6Ddr2yWjX6o1j8DT7MI5w==}
+ icu-minify@4.13.7:
+ resolution: {integrity: sha512-X9gLFtipsP4HHbmy9urh+palImTR9P6lyhvmgbP6iym8i0IwhcsS4Z6KMjkEoCU6O16OJT5JIZkd8xfDROYo/A==}
ieee754@1.2.1:
resolution: {integrity: sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==}
@@ -7488,11 +7500,11 @@ packages:
resolution: {integrity: sha512-8Ofs/AUQh8MaEcrlq5xOX0CQ9ypTF5dl78mjlMNfOK08fzpgTHQRQPBxcPlEtIw0yRpws+Zo/3r+5WRby7u3Gg==}
engines: {node: '>= 0.6'}
- next-intl-swc-plugin-extractor@4.13.3:
- resolution: {integrity: sha512-Oy86w/VRwqOu6sHEXwthEGhgnQszS79e47i3Kkzv4r+Yfz9ilZMSen8Sp8vtAwuc1nAZPrN13IbsLC5jmc9fVQ==}
+ next-intl-swc-plugin-extractor@4.13.7:
+ resolution: {integrity: sha512-MxOUMGKncc/D6rofu0O80I6Ebr3gqlH8XiEb0Uu5TZmX7DqY3NVHs70Uy4bvtgWmHeDwsra6KU8EBtfRVGRv7Q==}
- next-intl@4.13.3:
- resolution: {integrity: sha512-+bAjmPLsRFG/YAN1ged7wVCbuYvYjEW06ApBRdM7VbtNfXYN1iBFNh28Bi1sX5xGSFvAsGCBC1K2Zjtvn1AMvQ==}
+ next-intl@4.13.7:
+ resolution: {integrity: sha512-j7KnGWt4Ih6TnW1x714R8bX3H+DYP25fqLTYTfUzAFXh0Od57WuQYM/Sf58yalvIXhE6y8sBYHlrCFmr0jPy3g==}
peerDependencies:
next: ^12.0.0 || ^13.0.0 || ^14.0.0 || ^15.0.0 || ^16.0.0
react: ^16.8.0 || ^17.0.0 || ^18.0.0 || >=19.0.0-rc <19.0.0 || ^19.0.0
@@ -7528,8 +7540,8 @@ packages:
sass:
optional: true
- next@16.3.0-preview.9:
- resolution: {integrity: sha512-ruev+K1No0Cm/05hSrgKLtzaBdUIT+2I/Bj8x+K8/vk3j/dW+9YJOqGdDEkqNBOXtQlLxuEgeTH71jgsKTfWiQ==}
+ next@16.3.1:
+ resolution: {integrity: sha512-hsAp0i7Rh+/dhe7DGIeN2YlpLM1DP4MNxti9EtDMtqcO612X81MvvEj388/oTce9U1EcEIOWDlGq0zRwrBKvuA==}
engines: {node: '>=20.9.0'}
hasBin: true
peerDependencies:
@@ -7889,14 +7901,14 @@ packages:
resolution: {integrity: sha512-PS08Iboia9mts/2ygV3eLpY5ghnUcfLV/EXTOW1E2qYxJKGGBUtNjN76FYHnMs36RmARn41bC0AZmn+rR0OVpQ==}
engines: {node: ^10 || ^12 || >=14}
- postcss@8.5.10:
- resolution: {integrity: sha512-pMMHxBOZKFU6HgAZ4eyGnwXF/EvPGGqUr0MnZ5+99485wwW41kW91A4LOGxSHhgugZmSChL5AlElNdwlNgcnLQ==}
- engines: {node: ^10 || ^12 || >=14}
-
postcss@8.5.22:
resolution: {integrity: sha512-KBDEIpLrvpv16pp3K0Fw+UCoZfopFjjgeB+0tA/aaThfEE74kKDLrgg603YvOWJyg3+WYtyq3xYsQWsIyZlPqQ==}
engines: {node: ^10 || ^12 || >=14}
+ postcss@8.5.23:
+ resolution: {integrity: sha512-g50586zr4bZmwFiTlflMu8E0bDTb5I5gertgwAKmsdUlTQIhZtunzUlD1WSzwcVWPoAVpsrA6vlfCD7oXvRwgg==}
+ engines: {node: ^10 || ^12 || >=14}
+
postgres@3.4.9:
resolution: {integrity: sha512-GD3qdB0x1z9xgFI6cdRD6xu2Sp2WCOEoe3mtnyB5Ee0XrrL5Pe+e4CCnJrRMnL1zYtRDZmQQVbvOttLnKDLnaw==}
engines: {node: '>=12'}
@@ -9068,8 +9080,8 @@ packages:
peerDependencies:
react: '*'
- use-intl@4.13.3:
- resolution: {integrity: sha512-e1qMU4PokNEU7K1TtKROSl3Sv7JLsKh7GyNjuhKZVB444Qfv0qHjet59IZF6GtYE+VwGbhgWqDWfGTnuDPe2Bg==}
+ use-intl@4.13.7:
+ resolution: {integrity: sha512-vWapep/2GESovKEmkxaG1Bkt6AWwANCWrM4kwOYbMmvQ4IsBGJFx9l66h8DNCcU0jSOzNtSFyRXFcYvgAak/Cg==}
peerDependencies:
react: ^17.0.0 || ^18.0.0 || >=19.0.0-rc <19.0.0 || ^19.0.0
@@ -9889,11 +9901,11 @@ snapshots:
'@bprogress/core@1.3.4': {}
- '@bprogress/next@3.2.12(next@16.3.0-preview.9(@babel/core@7.29.7)(@opentelemetry/api@1.9.1)(@playwright/test@1.61.1)(@types/node@26.1.1)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.8(react@19.2.8))(react@19.2.8))(react-dom@19.2.8(react@19.2.8))(react@19.2.8)':
+ '@bprogress/next@3.2.12(next@16.3.1(@babel/core@7.29.7)(@opentelemetry/api@1.9.1)(@playwright/test@1.61.1)(@types/node@26.1.1)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.8(react@19.2.8))(react@19.2.8))(react-dom@19.2.8(react@19.2.8))(react@19.2.8)':
dependencies:
'@bprogress/core': 1.3.4
'@bprogress/react': 1.2.7(react-dom@19.2.8(react@19.2.8))(react@19.2.8)
- next: 16.3.0-preview.9(@babel/core@7.29.7)(@opentelemetry/api@1.9.1)(@playwright/test@1.61.1)(@types/node@26.1.1)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.8(react@19.2.8))(react@19.2.8)
+ next: 16.3.1(@babel/core@7.29.7)(@opentelemetry/api@1.9.1)(@playwright/test@1.61.1)(@types/node@26.1.1)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.8(react@19.2.8))(react@19.2.8)
react: 19.2.8
react-dom: 19.2.8(react@19.2.8)
@@ -10947,7 +10959,7 @@ snapshots:
'@tybys/wasm-util': 0.10.3
optional: true
- '@next/bundle-analyzer@16.2.11':
+ '@next/bundle-analyzer@16.3.1':
dependencies:
webpack-bundle-analyzer: 4.10.1
transitivePeerDependencies:
@@ -10956,54 +10968,54 @@ snapshots:
'@next/env@16.2.6': {}
- '@next/env@16.3.0-preview.9': {}
+ '@next/env@16.3.1': {}
'@next/swc-darwin-arm64@16.2.6':
optional: true
- '@next/swc-darwin-arm64@16.3.0-preview.9':
+ '@next/swc-darwin-arm64@16.3.1':
optional: true
'@next/swc-darwin-x64@16.2.6':
optional: true
- '@next/swc-darwin-x64@16.3.0-preview.9':
+ '@next/swc-darwin-x64@16.3.1':
optional: true
'@next/swc-linux-arm64-gnu@16.2.6':
optional: true
- '@next/swc-linux-arm64-gnu@16.3.0-preview.9':
+ '@next/swc-linux-arm64-gnu@16.3.1':
optional: true
'@next/swc-linux-arm64-musl@16.2.6':
optional: true
- '@next/swc-linux-arm64-musl@16.3.0-preview.9':
+ '@next/swc-linux-arm64-musl@16.3.1':
optional: true
'@next/swc-linux-x64-gnu@16.2.6':
optional: true
- '@next/swc-linux-x64-gnu@16.3.0-preview.9':
+ '@next/swc-linux-x64-gnu@16.3.1':
optional: true
'@next/swc-linux-x64-musl@16.2.6':
optional: true
- '@next/swc-linux-x64-musl@16.3.0-preview.9':
+ '@next/swc-linux-x64-musl@16.3.1':
optional: true
'@next/swc-win32-arm64-msvc@16.2.6':
optional: true
- '@next/swc-win32-arm64-msvc@16.3.0-preview.9':
+ '@next/swc-win32-arm64-msvc@16.3.1':
optional: true
'@next/swc-win32-x64-msvc@16.2.6':
optional: true
- '@next/swc-win32-x64-msvc@16.3.0-preview.9':
+ '@next/swc-win32-x64-msvc@16.3.1':
optional: true
'@nodelib/fs.scandir@2.1.5':
@@ -12019,9 +12031,9 @@ snapshots:
iceberg-js: 0.8.1
tslib: 2.8.1
- '@swc/cli@0.8.1(@swc/core@1.15.46)(chokidar@5.0.0)':
+ '@swc/cli@0.8.1(@swc/core@1.15.46(@swc/helpers@0.5.23))(chokidar@5.0.0)':
dependencies:
- '@swc/core': 1.15.46
+ '@swc/core': 1.15.46(@swc/helpers@0.5.23)
'@swc/counter': 0.1.3
'@xhmikosr/bin-wrapper': 14.5.0
commander: 8.3.0
@@ -12074,7 +12086,7 @@ snapshots:
'@swc/core-win32-x64-msvc@1.15.46':
optional: true
- '@swc/core@1.15.46':
+ '@swc/core@1.15.46(@swc/helpers@0.5.23)':
dependencies:
'@swc/counter': 0.1.3
'@swc/types': 0.1.27
@@ -12091,6 +12103,14 @@ snapshots:
'@swc/core-win32-arm64-msvc': 1.15.46
'@swc/core-win32-ia32-msvc': 1.15.46
'@swc/core-win32-x64-msvc': 1.15.46
+ '@swc/helpers': 0.5.23
+
+ '@swc/core@1.15.47(@swc/helpers@0.5.23)':
+ dependencies:
+ '@swc/counter': 0.1.3
+ '@swc/types': 0.1.27
+ optionalDependencies:
+ '@swc/helpers': 0.5.23
'@swc/counter@0.1.3': {}
@@ -12098,6 +12118,10 @@ snapshots:
dependencies:
tslib: 2.8.1
+ '@swc/helpers@0.5.23':
+ dependencies:
+ tslib: 2.8.1
+
'@swc/types@0.1.27':
dependencies:
'@swc/counter': 0.1.3
@@ -14318,7 +14342,7 @@ snapshots:
fsevents@2.3.3:
optional: true
- fumadocs-core@16.11.5(@mdx-js/mdx@3.1.1)(@types/estree-jsx@1.0.5)(@types/hast@3.0.5)(@types/mdast@4.0.4)(@types/react@19.2.17)(lucide-react@1.25.0(react@19.2.8))(next@16.3.0-preview.9(@babel/core@7.29.7)(@opentelemetry/api@1.9.1)(@playwright/test@1.61.1)(@types/node@26.1.1)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.8(react@19.2.8))(react@19.2.8))(react-dom@19.2.8(react@19.2.8))(react@19.2.8)(zod@4.4.3):
+ fumadocs-core@16.11.5(@mdx-js/mdx@3.1.1)(@types/estree-jsx@1.0.5)(@types/hast@3.0.5)(@types/mdast@4.0.4)(@types/react@19.2.17)(lucide-react@1.25.0(react@19.2.8))(next@16.3.1(@babel/core@7.29.7)(@opentelemetry/api@1.9.1)(@playwright/test@1.61.1)(@types/node@26.1.1)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.8(react@19.2.8))(react@19.2.8))(react-dom@19.2.8(react@19.2.8))(react@19.2.8)(zod@4.4.3):
dependencies:
'@orama/orama': 3.1.18
estree-util-value-to-estree: 3.5.0
@@ -14344,21 +14368,21 @@ snapshots:
'@types/mdast': 4.0.4
'@types/react': 19.2.17
lucide-react: 1.25.0(react@19.2.8)
- next: 16.3.0-preview.9(@babel/core@7.29.7)(@opentelemetry/api@1.9.1)(@playwright/test@1.61.1)(@types/node@26.1.1)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.8(react@19.2.8))(react@19.2.8)
+ next: 16.3.1(@babel/core@7.29.7)(@opentelemetry/api@1.9.1)(@playwright/test@1.61.1)(@types/node@26.1.1)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.8(react@19.2.8))(react@19.2.8)
react: 19.2.8
react-dom: 19.2.8(react@19.2.8)
zod: 4.4.3
transitivePeerDependencies:
- supports-color
- fumadocs-mdx@15.2.0(@types/mdast@4.0.4)(@types/mdx@2.0.14)(@types/react@19.2.17)(fumadocs-core@16.11.5(@mdx-js/mdx@3.1.1)(@types/estree-jsx@1.0.5)(@types/hast@3.0.5)(@types/mdast@4.0.4)(@types/react@19.2.17)(lucide-react@1.25.0(react@19.2.8))(next@16.3.0-preview.9(@babel/core@7.29.7)(@opentelemetry/api@1.9.1)(@playwright/test@1.61.1)(@types/node@26.1.1)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.8(react@19.2.8))(react@19.2.8))(react-dom@19.2.8(react@19.2.8))(react@19.2.8)(zod@4.4.3))(next@16.3.0-preview.9(@babel/core@7.29.7)(@opentelemetry/api@1.9.1)(@playwright/test@1.61.1)(@types/node@26.1.1)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.8(react@19.2.8))(react@19.2.8))(react@19.2.8)(rolldown@1.1.5)(vite@8.1.5(@types/node@26.1.1)(esbuild@0.28.1)(jiti@2.7.0)(tsx@4.23.1)(yaml@2.9.0)):
+ fumadocs-mdx@15.2.0(@types/mdast@4.0.4)(@types/mdx@2.0.14)(@types/react@19.2.17)(fumadocs-core@16.11.5(@mdx-js/mdx@3.1.1)(@types/estree-jsx@1.0.5)(@types/hast@3.0.5)(@types/mdast@4.0.4)(@types/react@19.2.17)(lucide-react@1.25.0(react@19.2.8))(next@16.3.1(@babel/core@7.29.7)(@opentelemetry/api@1.9.1)(@playwright/test@1.61.1)(@types/node@26.1.1)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.8(react@19.2.8))(react@19.2.8))(react-dom@19.2.8(react@19.2.8))(react@19.2.8)(zod@4.4.3))(next@16.3.1(@babel/core@7.29.7)(@opentelemetry/api@1.9.1)(@playwright/test@1.61.1)(@types/node@26.1.1)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.8(react@19.2.8))(react@19.2.8))(react@19.2.8)(rolldown@1.1.5)(vite@8.1.5(@types/node@26.1.1)(esbuild@0.28.1)(jiti@2.7.0)(tsx@4.23.1)(yaml@2.9.0)):
dependencies:
'@mdx-js/mdx': 3.1.1
'@standard-schema/spec': 1.1.0
chokidar: 5.0.0
esbuild: 0.28.1
estree-util-value-to-estree: 3.5.0
- fumadocs-core: 16.11.5(@mdx-js/mdx@3.1.1)(@types/estree-jsx@1.0.5)(@types/hast@3.0.5)(@types/mdast@4.0.4)(@types/react@19.2.17)(lucide-react@1.25.0(react@19.2.8))(next@16.3.0-preview.9(@babel/core@7.29.7)(@opentelemetry/api@1.9.1)(@playwright/test@1.61.1)(@types/node@26.1.1)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.8(react@19.2.8))(react@19.2.8))(react-dom@19.2.8(react@19.2.8))(react@19.2.8)(zod@4.4.3)
+ fumadocs-core: 16.11.5(@mdx-js/mdx@3.1.1)(@types/estree-jsx@1.0.5)(@types/hast@3.0.5)(@types/mdast@4.0.4)(@types/react@19.2.17)(lucide-react@1.25.0(react@19.2.8))(next@16.3.1(@babel/core@7.29.7)(@opentelemetry/api@1.9.1)(@playwright/test@1.61.1)(@types/node@26.1.1)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.8(react@19.2.8))(react@19.2.8))(react-dom@19.2.8(react@19.2.8))(react@19.2.8)(zod@4.4.3)
github-slugger: 2.0.0
magic-string: 0.30.21
mdast-util-mdx: 3.0.0
@@ -14377,14 +14401,14 @@ snapshots:
'@types/mdast': 4.0.4
'@types/mdx': 2.0.14
'@types/react': 19.2.17
- next: 16.3.0-preview.9(@babel/core@7.29.7)(@opentelemetry/api@1.9.1)(@playwright/test@1.61.1)(@types/node@26.1.1)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.8(react@19.2.8))(react@19.2.8)
+ next: 16.3.1(@babel/core@7.29.7)(@opentelemetry/api@1.9.1)(@playwright/test@1.61.1)(@types/node@26.1.1)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.8(react@19.2.8))(react@19.2.8)
react: 19.2.8
rolldown: 1.1.5
vite: 8.1.5(@types/node@26.1.1)(esbuild@0.28.1)(jiti@2.7.0)(tsx@4.23.1)(yaml@2.9.0)
transitivePeerDependencies:
- supports-color
- fumadocs-ui@16.11.5(@types/mdx@2.0.14)(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(fumadocs-core@16.11.5(@mdx-js/mdx@3.1.1)(@types/estree-jsx@1.0.5)(@types/hast@3.0.5)(@types/mdast@4.0.4)(@types/react@19.2.17)(lucide-react@1.25.0(react@19.2.8))(next@16.3.0-preview.9(@babel/core@7.29.7)(@opentelemetry/api@1.9.1)(@playwright/test@1.61.1)(@types/node@26.1.1)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.8(react@19.2.8))(react@19.2.8))(react-dom@19.2.8(react@19.2.8))(react@19.2.8)(zod@4.4.3))(next@16.3.0-preview.9(@babel/core@7.29.7)(@opentelemetry/api@1.9.1)(@playwright/test@1.61.1)(@types/node@26.1.1)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.8(react@19.2.8))(react@19.2.8))(react-dom@19.2.8(react@19.2.8))(react@19.2.8)(tailwindcss@4.3.3):
+ fumadocs-ui@16.11.5(@types/mdx@2.0.14)(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(fumadocs-core@16.11.5(@mdx-js/mdx@3.1.1)(@types/estree-jsx@1.0.5)(@types/hast@3.0.5)(@types/mdast@4.0.4)(@types/react@19.2.17)(lucide-react@1.25.0(react@19.2.8))(next@16.3.1(@babel/core@7.29.7)(@opentelemetry/api@1.9.1)(@playwright/test@1.61.1)(@types/node@26.1.1)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.8(react@19.2.8))(react@19.2.8))(react-dom@19.2.8(react@19.2.8))(react@19.2.8)(zod@4.4.3))(next@16.3.1(@babel/core@7.29.7)(@opentelemetry/api@1.9.1)(@playwright/test@1.61.1)(@types/node@26.1.1)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.8(react@19.2.8))(react@19.2.8))(react-dom@19.2.8(react@19.2.8))(react@19.2.8)(tailwindcss@4.3.3):
dependencies:
'@fuma-translate/react': 1.0.2(@types/react@19.2.17)(react-dom@19.2.8(react@19.2.8))(react@19.2.8)
'@fumadocs/tailwind': 0.1.1(tailwindcss@4.3.3)
@@ -14400,7 +14424,7 @@ snapshots:
'@radix-ui/react-tabs': 1.1.18(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.8(react@19.2.8))(react@19.2.8)
class-variance-authority: 0.7.1
cnfast: 0.0.8
- fumadocs-core: 16.11.5(@mdx-js/mdx@3.1.1)(@types/estree-jsx@1.0.5)(@types/hast@3.0.5)(@types/mdast@4.0.4)(@types/react@19.2.17)(lucide-react@1.25.0(react@19.2.8))(next@16.3.0-preview.9(@babel/core@7.29.7)(@opentelemetry/api@1.9.1)(@playwright/test@1.61.1)(@types/node@26.1.1)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.8(react@19.2.8))(react@19.2.8))(react-dom@19.2.8(react@19.2.8))(react@19.2.8)(zod@4.4.3)
+ fumadocs-core: 16.11.5(@mdx-js/mdx@3.1.1)(@types/estree-jsx@1.0.5)(@types/hast@3.0.5)(@types/mdast@4.0.4)(@types/react@19.2.17)(lucide-react@1.25.0(react@19.2.8))(next@16.3.1(@babel/core@7.29.7)(@opentelemetry/api@1.9.1)(@playwright/test@1.61.1)(@types/node@26.1.1)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.8(react@19.2.8))(react@19.2.8))(react-dom@19.2.8(react@19.2.8))(react@19.2.8)(zod@4.4.3)
lucide-react: 1.25.0(react@19.2.8)
motion: 12.42.2(react-dom@19.2.8(react@19.2.8))(react@19.2.8)
next-themes: 0.4.6(react-dom@19.2.8(react@19.2.8))(react@19.2.8)
@@ -14414,7 +14438,7 @@ snapshots:
optionalDependencies:
'@types/mdx': 2.0.14
'@types/react': 19.2.17
- next: 16.3.0-preview.9(@babel/core@7.29.7)(@opentelemetry/api@1.9.1)(@playwright/test@1.61.1)(@types/node@26.1.1)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.8(react@19.2.8))(react@19.2.8)
+ next: 16.3.1(@babel/core@7.29.7)(@opentelemetry/api@1.9.1)(@playwright/test@1.61.1)(@types/node@26.1.1)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.8(react@19.2.8))(react@19.2.8)
transitivePeerDependencies:
- '@emotion/is-prop-valid'
- '@types/react-dom'
@@ -14740,7 +14764,7 @@ snapshots:
dependencies:
safer-buffer: 2.1.2
- icu-minify@4.13.3:
+ icu-minify@4.13.7:
dependencies:
'@formatjs/icu-messageformat-parser': 3.5.15
@@ -15815,20 +15839,20 @@ snapshots:
negotiator@1.0.0: {}
- next-intl-swc-plugin-extractor@4.13.3: {}
+ next-intl-swc-plugin-extractor@4.13.7: {}
- next-intl@4.13.3(next@16.3.0-preview.9(@opentelemetry/api@1.9.1)(@playwright/test@1.61.1)(@types/node@26.1.1)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.8(react@19.2.8))(react@19.2.8))(react@19.2.8)(typescript@6.0.3):
+ next-intl@4.13.7(@swc/helpers@0.5.23)(next@16.3.1(@opentelemetry/api@1.9.1)(@playwright/test@1.61.1)(@types/node@26.1.1)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.8(react@19.2.8))(react@19.2.8))(react@19.2.8)(typescript@6.0.3):
dependencies:
'@formatjs/intl-localematcher': 0.8.13
'@parcel/watcher': 2.6.0
- '@swc/core': 1.15.46
- icu-minify: 4.13.3
+ '@swc/core': 1.15.47(@swc/helpers@0.5.23)
+ icu-minify: 4.13.7
negotiator: 1.0.0
- next: 16.3.0-preview.9(@babel/core@7.29.7)(@opentelemetry/api@1.9.1)(@playwright/test@1.61.1)(@types/node@26.1.1)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.8(react@19.2.8))(react@19.2.8)
- next-intl-swc-plugin-extractor: 4.13.3
+ next: 16.3.1(@babel/core@7.29.7)(@opentelemetry/api@1.9.1)(@playwright/test@1.61.1)(@types/node@26.1.1)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.8(react@19.2.8))(react@19.2.8)
+ next-intl-swc-plugin-extractor: 4.13.7
po-parser: 2.1.1
react: 19.2.8
- use-intl: 4.13.3(react@19.2.8)
+ use-intl: 4.13.7(react@19.2.8)
optionalDependencies:
typescript: 6.0.3
transitivePeerDependencies:
@@ -15866,25 +15890,25 @@ snapshots:
- '@babel/core'
- babel-plugin-macros
- next@16.3.0-preview.9(@babel/core@7.29.7)(@opentelemetry/api@1.9.1)(@playwright/test@1.61.1)(@types/node@26.1.1)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.8(react@19.2.8))(react@19.2.8):
+ next@16.3.1(@babel/core@7.29.7)(@opentelemetry/api@1.9.1)(@playwright/test@1.61.1)(@types/node@26.1.1)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.8(react@19.2.8))(react@19.2.8):
dependencies:
- '@next/env': 16.3.0-preview.9
- '@swc/helpers': 0.5.15
+ '@next/env': 16.3.1
+ '@swc/helpers': 0.5.23
baseline-browser-mapping: 2.11.1
caniuse-lite: 1.0.30001806
- postcss: 8.5.10
+ postcss: 8.5.23
react: 19.2.8
react-dom: 19.2.8(react@19.2.8)
styled-jsx: 5.1.6(@babel/core@7.29.7)(react@19.2.8)
optionalDependencies:
- '@next/swc-darwin-arm64': 16.3.0-preview.9
- '@next/swc-darwin-x64': 16.3.0-preview.9
- '@next/swc-linux-arm64-gnu': 16.3.0-preview.9
- '@next/swc-linux-arm64-musl': 16.3.0-preview.9
- '@next/swc-linux-x64-gnu': 16.3.0-preview.9
- '@next/swc-linux-x64-musl': 16.3.0-preview.9
- '@next/swc-win32-arm64-msvc': 16.3.0-preview.9
- '@next/swc-win32-x64-msvc': 16.3.0-preview.9
+ '@next/swc-darwin-arm64': 16.3.1
+ '@next/swc-darwin-x64': 16.3.1
+ '@next/swc-linux-arm64-gnu': 16.3.1
+ '@next/swc-linux-arm64-musl': 16.3.1
+ '@next/swc-linux-x64-gnu': 16.3.1
+ '@next/swc-linux-x64-musl': 16.3.1
+ '@next/swc-win32-arm64-msvc': 16.3.1
+ '@next/swc-win32-x64-msvc': 16.3.1
'@opentelemetry/api': 1.9.1
'@playwright/test': 1.61.1
babel-plugin-react-compiler: 1.0.0
@@ -16283,13 +16307,13 @@ snapshots:
picocolors: 1.1.1
source-map-js: 1.2.1
- postcss@8.5.10:
+ postcss@8.5.22:
dependencies:
nanoid: 3.3.16
picocolors: 1.1.1
source-map-js: 1.2.1
- postcss@8.5.22:
+ postcss@8.5.23:
dependencies:
nanoid: 3.3.16
picocolors: 1.1.1
@@ -17551,7 +17575,7 @@ snapshots:
tslib@2.8.1: {}
- tsup@8.5.1(@swc/core@1.15.46)(jiti@2.7.0)(postcss@8.5.22)(tsx@4.23.1)(typescript@6.0.3)(yaml@2.9.0):
+ tsup@8.5.1(@swc/core@1.15.46(@swc/helpers@0.5.23))(jiti@2.7.0)(postcss@8.5.22)(tsx@4.23.1)(typescript@6.0.3)(yaml@2.9.0):
dependencies:
bundle-require: 5.1.0(esbuild@0.27.7)
cac: 6.7.14
@@ -17571,7 +17595,7 @@ snapshots:
tinyglobby: 0.2.17
tree-kill: 1.2.2
optionalDependencies:
- '@swc/core': 1.15.46
+ '@swc/core': 1.15.46(@swc/helpers@0.5.23)
postcss: 8.5.22
typescript: 6.0.3
transitivePeerDependencies:
@@ -17765,11 +17789,11 @@ snapshots:
dependencies:
react: 19.2.8
- use-intl@4.13.3(react@19.2.8):
+ use-intl@4.13.7(react@19.2.8):
dependencies:
'@formatjs/fast-memoize': 3.1.7
'@schummar/icu-type-parser': 1.21.5
- icu-minify: 4.13.3
+ icu-minify: 4.13.7
intl-messageformat: 11.2.12
react: 19.2.8
@@ -17838,7 +17862,7 @@ snapshots:
dependencies:
lightningcss: 1.33.0
picomatch: 4.0.5
- postcss: 8.5.22
+ postcss: 8.5.23
rolldown: 1.1.5
tinyglobby: 0.2.17
optionalDependencies:
@@ -17853,7 +17877,7 @@ snapshots:
dependencies:
lightningcss: 1.33.0
picomatch: 4.0.5
- postcss: 8.5.22
+ postcss: 8.5.23
rolldown: 1.1.5
tinyglobby: 0.2.17
optionalDependencies:
diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml
index 3bdaabddf..225da7ac4 100644
--- a/pnpm-workspace.yaml
+++ b/pnpm-workspace.yaml
@@ -62,3 +62,7 @@ minimumReleaseAgeExclude:
- '@next/swc-win32-arm64-msvc@16.3.0-preview.9'
- '@next/swc-win32-x64-msvc@16.3.0-preview.9'
- next@16.3.0-preview.9
+ - icu-minify@4.13.7
+ - next-intl-swc-plugin-extractor@4.13.7
+ - next-intl@4.13.7
+ - use-intl@4.13.7
diff --git a/skills-lock.json b/skills-lock.json
new file mode 100644
index 000000000..2b2761ba6
--- /dev/null
+++ b/skills-lock.json
@@ -0,0 +1,11 @@
+{
+ "version": 1,
+ "skills": {
+ "next-cache-components-adoption": {
+ "source": "vercel/next.js",
+ "sourceType": "github",
+ "skillPath": "skills/next-cache-components-adoption/SKILL.md",
+ "computedHash": "15dcef9cfce611ba5a9ca467ed0d4eeae8de16fadb91b66df892a5ca63c57829"
+ }
+ }
+}