diff --git a/apps/web/src/app/(home)/layout.tsx b/apps/web/src/app/(home)/layout.tsx
index 77379fa..31e120b 100644
--- a/apps/web/src/app/(home)/layout.tsx
+++ b/apps/web/src/app/(home)/layout.tsx
@@ -1,5 +1,25 @@
import { HomeLayout } from 'fumadocs-ui/layouts/home';
import { baseOptions } from '@/lib/layout.shared';
+import type { Metadata } from 'next';
+
+export const metadata: Metadata = {
+ title: '@deessejs/errors',
+ description:
+ 'A TypeScript library that brings Python-inspired error handling to JavaScript. Exception chaining, hierarchical inheritance, and rich error semantics through a function-based API.',
+ openGraph: {
+ title: '@deessejs/errors',
+ description: 'TypeScript error handling reimagined.',
+ url: '/',
+ siteName: 'DeesseJS Errors',
+ locale: 'en_US',
+ type: 'website',
+ },
+ twitter: {
+ card: 'summary_large_image',
+ title: '@deessejs/errors',
+ description: 'TypeScript error handling reimagined.',
+ },
+};
export default function Layout({ children }: LayoutProps<'/'>) {
return {children};
diff --git a/apps/web/src/app/(home)/page.tsx b/apps/web/src/app/(home)/page.tsx
index 318a443..6d9f9d1 100644
--- a/apps/web/src/app/(home)/page.tsx
+++ b/apps/web/src/app/(home)/page.tsx
@@ -3,6 +3,7 @@ import type { Metadata } from 'next';
import { CodeBlock } from '@/components/code-block';
import { CtaCard } from '@/components/cta-card';
import { Footer } from '@/components/footer';
+import { baseUrl } from '@/lib/shared';
export const metadata: Metadata = {
title: '@deessejs/errors — Error Handling, Reimagined',
@@ -123,9 +124,34 @@ catch (err) {
}`;
export default function HomePage() {
+ const softwareJsonLd = {
+ '@context': 'https://schema.org',
+ '@type': 'SoftwareApplication',
+ name: '@deessejs/errors',
+ description:
+ 'TypeScript error handling library with exception chaining, hierarchical inheritance, and rich error semantics through a function-based API.',
+ url: baseUrl,
+ applicationCategory: 'DeveloperApplication',
+ operatingSystem: 'Node.js 18+',
+ programmingLanguage: {
+ '@type': 'ComputerLanguage',
+ name: 'TypeScript',
+ },
+ license: 'MIT',
+ offers: {
+ '@type': 'Offer',
+ price: '0',
+ priceCurrency: 'USD',
+ },
+ downloadUrl: 'https://www.npmjs.com/package/@deessejs/errors',
+ };
+
return (
<>
- {/* Blueprint grid background */}
+
) {
const params = await props.params;
@@ -21,26 +21,99 @@ export default async function Page(props: PageProps<'/docs/[[...slug]]'>) {
const MDX = page.data.body;
const markdownUrl = getPageMarkdownUrl(page).url;
+ // Generate page-specific JSON-LD
+ const breadcrumbJsonLd = {
+ '@context': 'https://schema.org',
+ '@type': 'BreadcrumbList',
+ itemListElement: [
+ {
+ '@type': 'ListItem',
+ position: 1,
+ name: 'Home',
+ item: baseUrl,
+ },
+ {
+ '@type': 'ListItem',
+ position: 2,
+ name: 'Docs',
+ item: `${baseUrl}/docs`,
+ },
+ {
+ '@type': 'ListItem',
+ position: 3,
+ name: page.data.title,
+ item: `${baseUrl}${page.url}`,
+ },
+ ],
+ };
+
+ const techArticleJsonLd = {
+ '@context': 'https://schema.org',
+ '@type': 'TechArticle',
+ name: page.data.title,
+ description: page.data.description,
+ about: {
+ '@type': 'SoftwareApplication',
+ name: '@deessejs/errors',
+ },
+ author: {
+ '@type': 'Organization',
+ name: 'Nesalia Inc',
+ },
+ datePublished: new Date().toISOString(),
+ keywords: ['TypeScript', 'error handling', 'exceptions'],
+ };
+
+ // Add APIReference schema for API reference page
+ const isApiReference = params.slug?.join('/') === 'api-reference';
+ const apiReferenceJsonLd = isApiReference
+ ? {
+ '@context': 'https://schema.org',
+ '@type': 'APIReference',
+ name: '@deessejs/errors API Reference',
+ description: 'Complete API reference for @deessejs/errors',
+ assemblyVersion: '1.0.0',
+ targetPlatform: 'Node.js',
+ about: {
+ '@type': 'SoftwareApplication',
+ name: '@deessejs/errors',
+ },
+ author: {
+ '@type': 'Organization',
+ name: 'Nesalia Inc',
+ },
+ }
+ : null;
+
+ const schemas = isApiReference
+ ? [breadcrumbJsonLd, techArticleJsonLd, apiReferenceJsonLd]
+ : [breadcrumbJsonLd, techArticleJsonLd];
+
return (
-
- {page.data.title}
- {page.data.description}
-
-
-
-
-
-
-
-
+ <>
+
+
+ {page.data.title}
+ {page.data.description}
+
+
+
+
+
+
+
+
+ >
);
}
@@ -57,7 +130,7 @@ export async function generateMetadata(props: PageProps<'/docs/[[...slug]]'>): P
title: page.data.title,
description: page.data.description,
alternates: {
- canonical: `${siteUrl}${page.url}`,
+ canonical: `${baseUrl}${page.url}`,
},
openGraph: {
images: getPageImage(page).url,
diff --git a/apps/web/src/app/layout.tsx b/apps/web/src/app/layout.tsx
index 3330cd2..ce223f5 100644
--- a/apps/web/src/app/layout.tsx
+++ b/apps/web/src/app/layout.tsx
@@ -1,4 +1,6 @@
import { RootProvider } from 'fumadocs-ui/provider/next';
+import type { Metadata } from 'next';
+import { baseUrl, gitConfig } from '@/lib/shared';
import './global.css';
import { Inter } from 'next/font/google';
import { Analytics } from '@vercel/analytics/next';
@@ -8,11 +10,58 @@ const inter = Inter({
display: 'swap',
});
+export const metadata: Metadata = {
+ metadataBase: new URL(process.env.NEXT_PUBLIC_SITE_URL || baseUrl),
+ title: {
+ template: '%s | @deessejs/errors',
+ default: '@deessejs/errors — TypeScript Error Handling',
+ },
+};
+
+function JsonLd() {
+ const websiteJsonLd = {
+ '@context': 'https://schema.org',
+ '@type': 'WebSite',
+ name: '@deessejs/errors',
+ url: baseUrl,
+ description: 'TypeScript error handling library with exception chaining and hierarchical inheritance',
+ potentialAction: {
+ '@type': 'SearchAction',
+ target: {
+ '@type': 'EntryPoint',
+ urlTemplate: `${baseUrl}/docs?q={search_term_string}`,
+ },
+ 'query-input': 'required name=search_term_string',
+ },
+ };
+
+ const organizationJsonLd = {
+ '@context': 'https://schema.org',
+ '@type': 'Organization',
+ name: 'Nesalia Inc',
+ url: 'https://nesalia.com',
+ logo: `${baseUrl}/icon.svg`,
+ sameAs: [
+ `https://github.com/${gitConfig.user}`,
+ ],
+ };
+
+ return (
+
+ );
+}
+
export default function Layout({ children }: LayoutProps<'/'>) {
return (
+
{children}
@@ -20,4 +69,4 @@ export default function Layout({ children }: LayoutProps<'/'>) {
);
-}
\ No newline at end of file
+}
diff --git a/apps/web/src/app/og/docs/[...slug]/route.tsx b/apps/web/src/app/og/docs/[...slug]/route.tsx
index 518a98c..755fe6f 100644
--- a/apps/web/src/app/og/docs/[...slug]/route.tsx
+++ b/apps/web/src/app/og/docs/[...slug]/route.tsx
@@ -1,8 +1,9 @@
import { getPageImage, source } from '@/lib/source';
import { notFound } from 'next/navigation';
import { ImageResponse } from 'next/og';
-import { generate as DefaultImage } from 'fumadocs-ui/og';
-import { appName } from '@/lib/shared';
+import { appName, baseUrl } from '@/lib/shared';
+import { readFile } from 'node:fs/promises';
+import { join } from 'node:path';
export const revalidate = false;
@@ -20,8 +21,134 @@ export async function GET(_req: Request, { params }: RouteContext<'/og/docs/[...
const page = source.getPage(cleanSlug);
if (!page) notFound();
+ // Load banner image
+ const bannerData = await readFile(
+ join(process.cwd(), 'src/public/banner.jpg'),
+ );
+ const bannerBase64 = `data:image/jpeg;base64,${bannerData.toString('base64')}`;
+
return new ImageResponse(
-
,
+ (
+
+ {/* Banner Image */}
+
+

+
+
+ {/* Content Section */}
+
+ {/* Documentation Badge */}
+
+
+ {/* Page Title */}
+
+ {page.data.title}
+
+
+ {/* Description */}
+ {page.data.description && (
+
+ {page.data.description.substring(0, 120)}
+ {page.data.description.length > 120 ? '...' : ''}
+
+ )}
+
+ {/* Footer */}
+
+
+ {appName}
+
+
+ {baseUrl.replace('https://', '')}
+
+
+
+
+ ),
{
width: 1200,
height: 630,
diff --git a/apps/web/src/app/opengraph-image.tsx b/apps/web/src/app/opengraph-image.tsx
new file mode 100644
index 0000000..23c45c4
--- /dev/null
+++ b/apps/web/src/app/opengraph-image.tsx
@@ -0,0 +1,125 @@
+import { ImageResponse } from 'next/og';
+import { appName, baseUrl } from '@/lib/shared';
+import { readFile } from 'node:fs/promises';
+import { join } from 'node:path';
+
+export const size = {
+ width: 1200,
+ height: 630,
+};
+
+export const contentType = 'image/png';
+
+export const alt = '@deessejs/errors - TypeScript Error Handling Reimagined';
+
+export default async function Image() {
+ // Load banner image
+ const bannerData = await readFile(
+ join(process.cwd(), 'src/public/banner.jpg'),
+ );
+ const bannerBase64 = `data:image/jpeg;base64,${bannerData.toString('base64')}`;
+
+ return new ImageResponse(
+ (
+
+ {/* Banner Image */}
+
+

+
+
+ {/* Content Overlay at Bottom */}
+
+ {/* Library Name */}
+
+
+ {appName}
+
+
+
+ {/* Tagline */}
+
+ TypeScript Error Handling Reimagined
+
+
+ {/* NPM Badge */}
+
+
+ npm install @deessejs/errors
+
+
+
+
+ ),
+ {
+ ...size,
+ },
+ );
+}
\ No newline at end of file
diff --git a/apps/web/src/app/robots.ts b/apps/web/src/app/robots.ts
index ca31f53..a1c0a6c 100644
--- a/apps/web/src/app/robots.ts
+++ b/apps/web/src/app/robots.ts
@@ -1,3 +1,4 @@
+import { baseUrl } from '@/lib/shared';
import type { MetadataRoute } from 'next';
export default function robots(): MetadataRoute.Robots {
@@ -6,9 +7,9 @@ export default function robots(): MetadataRoute.Robots {
{
userAgent: '*',
allow: '/',
- disallow: ['/api/search', '/llms.mdx'],
+ disallow: ['/api/', '/llms.txt', '/llms.mdx/'],
},
],
- sitemap: 'https://errors.deessejs.com/sitemap.xml',
+ sitemap: `${baseUrl}/sitemap.xml`,
};
}
diff --git a/apps/web/src/app/sitemap.ts b/apps/web/src/app/sitemap.ts
index f0ea468..5115dfa 100644
--- a/apps/web/src/app/sitemap.ts
+++ b/apps/web/src/app/sitemap.ts
@@ -1,27 +1,35 @@
-import type { MetadataRoute } from 'next';
import { source } from '@/lib/source';
-import { siteUrl } from '@/lib/shared';
+import { baseUrl } from '@/lib/shared';
import { getPageImage } from '@/lib/source';
+import type { MetadataRoute } from 'next';
export default function sitemap(): MetadataRoute.Sitemap {
const pages = source.getPages();
- const docPages = pages.map((page) => ({
- url: `${siteUrl}${page.url}`,
- lastModified: new Date(),
- changeFrequency: 'weekly' as const,
- priority: page.url === '/docs' ? 1.0 : 0.8,
- images: [`${siteUrl}${getPageImage(page).url}`],
- }));
-
- const staticPages: MetadataRoute.Sitemap = [
+ // Static routes
+ const staticRoutes: MetadataRoute.Sitemap = [
{
- url: siteUrl,
+ url: baseUrl,
lastModified: new Date(),
- changeFrequency: 'monthly',
+ changeFrequency: 'weekly',
+ priority: 1.0,
+ },
+ {
+ url: `${baseUrl}/docs`,
+ lastModified: new Date(),
+ changeFrequency: 'weekly',
priority: 0.9,
},
];
- return [...staticPages, ...docPages];
+ // Dynamic doc pages with image sitemap
+ const docPages: MetadataRoute.Sitemap = pages.map((page) => ({
+ url: `${baseUrl}${page.url}`,
+ lastModified: new Date(),
+ changeFrequency: 'monthly' as const,
+ priority: 0.8,
+ images: [`${baseUrl}${getPageImage(page).url}`],
+ }));
+
+ return [...staticRoutes, ...docPages];
}
diff --git a/apps/web/src/app/twitter-image.tsx b/apps/web/src/app/twitter-image.tsx
new file mode 100644
index 0000000..a54486d
--- /dev/null
+++ b/apps/web/src/app/twitter-image.tsx
@@ -0,0 +1,110 @@
+import { ImageResponse } from 'next/og';
+import { appName } from '@/lib/shared';
+import { readFile } from 'node:fs/promises';
+import { join } from 'node:path';
+
+export const size = {
+ width: 1200,
+ height: 630,
+};
+
+export const contentType = 'image/png';
+
+export const alt = '@deessejs/errors - TypeScript Error Handling Library';
+
+export default async function Image() {
+ // Load banner image
+ const bannerData = await readFile(
+ join(process.cwd(), 'src/public/banner.jpg'),
+ );
+ const bannerBase64 = `data:image/jpeg;base64,${bannerData.toString('base64')}`;
+
+ return new ImageResponse(
+ (
+
+ {/* Banner Image */}
+
+

+
+
+ {/* Bottom Section */}
+
+ {/* Library Name */}
+
+ {appName}
+
+
+ {/* Short Description */}
+
+ Exception chaining • Hierarchical inheritance • Rich error semantics
+
+
+ {/* Hashtags */}
+
+ #TypeScript #ErrorHandling #NodeJS
+
+
+
+ ),
+ {
+ ...size,
+ },
+ );
+}
\ No newline at end of file
diff --git a/apps/web/src/lib/shared.ts b/apps/web/src/lib/shared.ts
index c562b06..96460b6 100644
--- a/apps/web/src/lib/shared.ts
+++ b/apps/web/src/lib/shared.ts
@@ -1,4 +1,5 @@
export const appName = 'DeesseJS Errors';
+export const baseUrl = 'https://errors.deessejs.com';
export const docsRoute = '/docs';
export const docsImageRoute = '/og/docs';
export const docsContentRoute = '/llms.mdx/docs';
diff --git a/apps/web/src/public/banner.jpg b/apps/web/src/public/banner.jpg
new file mode 100644
index 0000000..9935b3a
Binary files /dev/null and b/apps/web/src/public/banner.jpg differ