Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
37 changes: 37 additions & 0 deletions apps/content/blume.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,26 @@ export default defineConfig({
],

seo: {
/** The publisher of every page's JSON-LD: the team behind oRPC, not the product itself. */
organization: {
name: 'middleapi',
url: 'https://github.com/middleapi',
sameAs: [
'https://x.com/middleapi',
'https://opencollective.com/middleapi',
],
},
/** The homepage's SoftwareApplication JSON-LD node, which tells search engines and agents what oRPC is. */
software: {
license: 'https://github.com/middleapi/orpc/blob/main/LICENSE',
operatingSystem: 'Cross-platform',
price: 0,
sameAs: [
'https://github.com/middleapi/orpc',
'https://www.npmjs.com/package/@orpc/server',
'https://www.npmjs.com/package/@orpc/client',
],
},
x: { creator: '@middleapi', handle: '@middleapi' },
og: {
titles: {
Expand Down Expand Up @@ -218,6 +238,23 @@ export default defineConfig({
},
],
agents: {
llmsTxt: {
// The llms.txt "details" block, right after the title and summary: what
// oRPC is, when to reach for it, and how to install it. No headings, the
// spec reserves them for the page sections that follow.
details: [
'oRPC is an open-source (MIT) TypeScript library for end-to-end typesafe APIs. You write procedures, plain functions with optional input and output schemas (Zod, Valibot, ArkType, or any Standard Schema library), group them into a router, and serve it over HTTP. From that one router you get a fully typed client with no code generation, a REST API with a generated OpenAPI document, MCP tools for AI agents, and typed realtime streams.',
'',
'Reach for oRPC when:',
'',
'- A TypeScript frontend, mobile app, or service calls a TypeScript backend and both sides should share types without a build step.',
'- You want tRPC-style RPC but also need a public REST API or an OpenAPI spec from the same code.',
'- You want to design the API as a contract first, then implement it and call it from clients that only import the contract.',
'- The API runs on Node.js, Bun, Deno, or Cloudflare Workers, inside a framework like Next.js, Nuxt, SvelteKit, or Hono, or feeds TanStack Query.',
'',
'Install the server and client with `npm install @orpc/server@beta @orpc/client@beta zod`, then follow [Getting Started](https://orpc.dev/docs/getting-started). These docs cover oRPC v2, currently in beta. The v1 docs live at https://v1.orpc.dev, and [Migrating from oRPC v1](https://orpc.dev/docs/migrations/from-v1) lists what changed.',
].join('\n'),
},
mcp: {
enabled: true,
},
Expand Down
3 changes: 3 additions & 0 deletions apps/content/pages/blog/[...slug].astro
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,8 @@ const minutes = Math.max(1, Math.round(words / 200))
const dateFormat = new Intl.DateTimeFormat('en-US', { dateStyle: 'long' })

// schema.org JSON-LD, matching what Blume emits for generated blog pages.
// PageLayout's own graph would describe the post as a second, TechArticle
// node, so it is turned off below.
const siteBase = (config.site ?? '').replace(/\/$/, '')
const postJsonLd = {
'@context': 'https://schema.org',
Expand Down Expand Up @@ -106,6 +108,7 @@ const postJsonLd = {
siteUrl={config.site}
ogEnabled={config.og.enabled}
page={{ title: `${entry.data.title} - ${config.title}`, description: entry.data.description, route: route.path }}
structuredDataEnabled={false}
Comment thread
dinwwwh marked this conversation as resolved.
x={config.x}
>
<script
Expand Down
2 changes: 2 additions & 0 deletions apps/content/pages/blog/index.astro
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@ const feed = data.feeds.find(f => f.href.startsWith('/blog'))

// schema.org JSON-LD: the index as a Blog with its posts, absolute when the
// site URL is known (production); crawlers read JSON-LD from the body fine.
// PageLayout's own graph would call the index a TechArticle, so it is off.
const siteBase = (config.site ?? '').replace(/\/$/, '')
const blogJsonLd = {
'@context': 'https://schema.org',
Expand Down Expand Up @@ -95,6 +96,7 @@ const blogJsonLd = {
siteUrl={config.site}
ogEnabled={config.og.enabled}
page={{ title: `Blog - ${config.title}`, description: 'News, release announcements, tutorials, and engineering articles from the oRPC team about building typesafe APIs with TypeScript.', route: '/blog' }}
structuredDataEnabled={false}
x={config.x}
>
<script
Expand Down
39 changes: 2 additions & 37 deletions apps/content/pages/index.astro
Original file line number Diff line number Diff line change
Expand Up @@ -20,37 +20,8 @@ const description
= 'Build end-to-end typesafe APIs in TypeScript. One oRPC router gives you a typed client, '
+ 'a REST API with an OpenAPI document, and MCP tools for AI agents.'

// schema.org JSON-LD, absolute when the site URL is known (production).
// PageLayout carries no structured data of its own — Blume only emits it for
// generated content pages — so the home page declares its own graph: the site
// identity node plus the project as a free developer application.
const siteBase = (config.site ?? '').replace(/\/$/, '')
const jsonLd = {
'@context': 'https://schema.org',
'@graph': [
{
'@type': 'WebSite',
'@id': `${siteBase}/#website`,
'url': `${siteBase}/`,
'name': config.title,
'description': description,
'inLanguage': 'en',
},
{
'@type': 'SoftwareApplication',
'@id': `${siteBase}/#software`,
'name': 'oRPC',
'applicationCategory': 'DeveloperApplication',
'operatingSystem': 'Cross-platform',
'url': siteBase,
'description': description,
'license': 'https://github.com/middleapi/orpc/blob/main/LICENSE',
'codeRepository': 'https://github.com/middleapi/orpc',
'isAccessibleForFree': true,
'offers': { '@type': 'Offer', 'price': '0', 'priceCurrency': 'USD' },
},
],
}
// PageLayout emits the page's schema.org JSON-LD: the WebSite and WebPage
// nodes, plus the SoftwareApplication from `seo.software` in blume.config.ts.
---

<PageLayout
Expand All @@ -68,12 +39,6 @@ const jsonLd = {
page={{ title: 'oRPC - Typesafe APIs Made Simple 🪄', description, route: '/' }}
x={config.x}
>
<script
is:inline
type="application/ld+json"
set:html={JSON.stringify(jsonLd).replaceAll('<', '\\u003c')}
/>

<Hero />
<Runtimes />
<Features />
Expand Down
Loading