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
93 changes: 51 additions & 42 deletions astro.config.mts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import {
isShallowRepository,
sitemapLastmodSerializer,
} from "./src/lib/content-dates";
import { shouldExcludeFromSitemap, variantOnlySdkAstroRedirects } from "./src/lib/sdk";
import { main as sidebar } from "./src/lib/sidebars";

/*
Expand Down Expand Up @@ -173,7 +174,10 @@ export default defineConfig({
],
plugins: [
starlightLinksValidator({
exclude: ["**/*f=*"], // exclude urls with `f` param from validation
exclude: [
"**/*f=*", // exclude urls with `f` param from validation
"**/sdk/**", // SDK-scoped routes are generated by the content loader
],
errorOnLocalLinks: false, // we use localhost in the examples
// TODO(#494) enable once we've sorted out the issue it's having with the
// troubleshooting section. Specifically
Expand Down Expand Up @@ -221,81 +225,86 @@ export default defineConfig({
* needs adding here too.
*/
sitemap({
filter: (page) => !shouldExcludeFromSitemap(new URL(page).pathname),
serialize: sitemapLastmodSerializer(),
}),
react(),
arcjet(),
],
// External redirects go in /vercel.json
redirects: {
"/get-started/bun": "/get-started?f=bun",
"/get-started/nextjs": "/get-started?f=next-js",
"/get-started/nodejs": "/get-started?f=node-js",
"/get-started/fastify": "/get-started?f=fastify",
"/get-started/sveltekit": "/get-started?f=sveltekit",
"/shield/quick-start/bun": "/shield/quick-start?f=bun",
"/shield/quick-start/nextjs": "/shield/quick-start?f=next-js",
"/shield/quick-start/nodejs": "/shield/quick-start?f=node-js",
"/shield/quick-start/sveltekit": "/shield/quick-start?f=sveltekit",
"/shield/reference/bun": "/shield/reference?f=bun",
"/shield/reference/nextjs": "/shield/reference?f=next-js",
"/shield/reference/nodejs": "/shield/reference?f=node-js",
"/shield/reference/sveltekit": "/shield/reference?f=sveltekit",
"/get-started/bun": "/sdk/bun/get-started/",
"/get-started/nextjs": "/sdk/next/get-started/",
"/get-started/nodejs": "/sdk/node/get-started/",
"/get-started/fastify": "/sdk/fastify/get-started/",
"/get-started/sveltekit": "/sdk/sveltekit/get-started/",
"/shield/quick-start/bun": "/sdk/bun/shield/quick-start/",
"/shield/quick-start/nextjs": "/sdk/next/shield/quick-start/",
"/shield/quick-start/nodejs": "/sdk/node/shield/quick-start/",
"/shield/quick-start/sveltekit": "/sdk/sveltekit/shield/quick-start/",
"/shield/reference/bun": "/sdk/bun/shield/reference/",
"/shield/reference/nextjs": "/sdk/next/shield/reference/",
"/shield/reference/nodejs": "/sdk/node/shield/reference/",
"/shield/reference/sveltekit": "/sdk/sveltekit/shield/reference/",
"/rate-limiting/concepts": "/rate-limiting",
"/rate-limiting/quick-start/bun": "/rate-limiting/quick-start?f=bun",
"/rate-limiting/quick-start/nextjs": "/rate-limiting/quick-start?f=next-js",
"/rate-limiting/quick-start/nodejs": "/rate-limiting/quick-start?f=node-js",
"/rate-limiting/quick-start/bun": "/sdk/bun/rate-limiting/quick-start/",
"/rate-limiting/quick-start/nextjs":
"/sdk/next/rate-limiting/quick-start/",
"/rate-limiting/quick-start/nodejs":
"/sdk/node/rate-limiting/quick-start/",
"/rate-limiting/quick-start/sveltekit":
"/rate-limiting/quick-start?f=sveltekit",
"/bot-protection/quick-start/bun": "/bot-protection/quick-start?f=bun",
"/sdk/sveltekit/rate-limiting/quick-start/",
"/bot-protection/quick-start/bun": "/sdk/bun/bot-protection/quick-start/",
"/bot-protection/quick-start/nextjs":
"/bot-protection/quick-start?f=next-js",
"/sdk/next/bot-protection/quick-start/",
"/bot-protection/quick-start/nodejs":
"/bot-protection/quick-start?f=node-js",
"/sdk/node/bot-protection/quick-start/",
"/bot-protection/quick-start/sveltekit":
"/bot-protection/quick-start?f=sveltekit",
"/bot-protection/reference/bun": "/bot-protection/reference?f=bun",
"/bot-protection/reference/nextjs": "/bot-protection/reference?f=next-js",
"/bot-protection/reference/nodejs": "/bot-protection/reference?f=node-js",
"/sdk/sveltekit/bot-protection/quick-start/",
"/bot-protection/reference/bun": "/sdk/bun/bot-protection/reference/",
"/bot-protection/reference/nextjs": "/sdk/next/bot-protection/reference/",
"/bot-protection/reference/nodejs": "/sdk/node/bot-protection/reference/",
"/bot-protection/reference/sveltekit":
"/bot-protection/reference?f=sveltekit",
"/email-validation/quick-start/bun": "/email-validation/quick-start?f=bun",
"/sdk/sveltekit/bot-protection/reference/",
"/email-validation/quick-start/bun":
"/sdk/bun/email-validation/quick-start/",
"/email-validation/quick-start/nextjs":
"/email-validation/quick-start?f=next-js",
"/sdk/next/email-validation/quick-start/",
"/email-validation/quick-start/nodejs":
"/email-validation/quick-start?f=node-js",
"/sdk/node/email-validation/quick-start/",
"/email-validation/quick-start/sveltekit":
"/email-validation/quick-start?f=sveltekit",
"/email-validation/reference/bun": "/email-validation/reference?f=bun",
"/sdk/sveltekit/email-validation/quick-start/",
"/email-validation/reference/bun": "/sdk/bun/email-validation/reference/",
"/email-validation/reference/nextjs":
"/email-validation/reference?f=next-js",
"/sdk/next/email-validation/reference/",
"/email-validation/reference/nodejs":
"/email-validation/reference?f=node-js",
"/sdk/node/email-validation/reference/",
"/email-validation/reference/sveltekit":
"/email-validation/reference?f=sveltekit",
"/sdk/sveltekit/email-validation/reference/",
"/filters/concepts": "/filters",
"/signup-protection/quick-start/bun":
"/signup-protection/quick-start?f=bun",
"/sdk/bun/signup-protection/quick-start/",
"/signup-protection/quick-start/nextjs":
"/signup-protection/quick-start?f=next-js",
"/sdk/next/signup-protection/quick-start/",
"/signup-protection/quick-start/nodejs":
"/signup-protection/quick-start?f=node-js",
"/sdk/node/signup-protection/quick-start/",
"/signup-protection/quick-start/sveltekit":
"/signup-protection/quick-start?f=sveltekit",
"/signup-protection/reference/bun": "/signup-protection/reference?f=bun",
"/sdk/sveltekit/signup-protection/quick-start/",
"/signup-protection/reference/bun": "/sdk/bun/signup-protection/reference/",
"/signup-protection/reference/nextjs":
"/signup-protection/reference?f=next-js",
"/sdk/next/signup-protection/reference/",
"/signup-protection/reference/nodejs":
"/signup-protection/reference?f=node-js",
"/sdk/node/signup-protection/reference/",
"/signup-protection/reference/sveltekit":
"/signup-protection/reference?f=sveltekit",
"/sdk/sveltekit/signup-protection/reference/",
"/reference/ts-js": "/reference/nodejs",
"/bot-protection/bot-types": "/bot-protection/identifying-bots",
"/mcp": "/mcp-server",
// Duplicated in vercel.json for production. This Astro redirect covers
// local preview. Keep both in sync. Real file is sitemap-index.xml;
// do not collapse /sdk/{framework}/ sitemap entries.
"/sitemap.xml": "/sitemap-index.xml",
...variantOnlySdkAstroRedirects(),
...withComparisonSdkScopes(comparisonMarketingRedirects),
},
});
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -127,7 +127,8 @@
"preview": "astro preview",
"pw:install": "playwright install --with-deps chromium --only-shell",
"pw:open": "playwright test --ui-host=0.0.0.0",
"pw:run": "playwright test"
"pw:run": "playwright test",
"generate:legacy-f-redirects": "node --experimental-strip-types scripts/generate-legacy-f-redirects.ts"
},
"type": "module",
"version": "0.0.1",
Expand Down
33 changes: 17 additions & 16 deletions public/llms-full.txt
Original file line number Diff line number Diff line change
Expand Up @@ -156,24 +156,25 @@ Go SDK: https://github.com/arcjet/arcjet-go

## Quick start – choose your framework

Each link below directs to the quick start guide with a framework-specific view:
Each link below directs to the SDK-scoped quick start guide for that framework:

- [Astro quick start](https://docs.arcjet.com/get-started?f=astro)
- [Bun quick start](https://docs.arcjet.com/get-started?f=bun)
- [Deno quick start](https://docs.arcjet.com/get-started?f=deno)
- [Fastify quick start](https://docs.arcjet.com/get-started?f=fastify)
- [Astro quick start](https://docs.arcjet.com/sdk/astro/get-started/)
- [Bun quick start](https://docs.arcjet.com/sdk/bun/get-started/)
- [Deno quick start](https://docs.arcjet.com/sdk/deno/get-started/)
- [Fastify quick start](https://docs.arcjet.com/sdk/fastify/get-started/)
- [Go SDK reference](https://docs.arcjet.com/reference/go)
- [NestJS quick start](https://docs.arcjet.com/get-started?f=nest-js)
- [Next.js quick start](https://docs.arcjet.com/get-started?f=next-js)
- [Node.js quick start](https://docs.arcjet.com/get-started?f=node-js)
- [Node.js + Express quick start](https://docs.arcjet.com/get-started?f=node-js-express)
- [Node.js + Hono quick start](https://docs.arcjet.com/get-started?f=node-js-hono)
- [Nuxt quick start](https://docs.arcjet.com/get-started?f=nuxt)
- [Python FastAPI quick start](https://docs.arcjet.com/get-started?f=python-fastapi)
- [Python Flask quick start](https://docs.arcjet.com/get-started?f=python-flask)
- [React Router quick start](https://docs.arcjet.com/get-started?f=react-router)
- [Remix quick start](https://docs.arcjet.com/get-started?f=remix)
- [SvelteKit quick start](https://docs.arcjet.com/get-started?f=sveltekit)
- [NestJS quick start](https://docs.arcjet.com/sdk/nest/get-started/)
- [Next.js quick start](https://docs.arcjet.com/sdk/next/get-started/)
- [Node.js quick start](https://docs.arcjet.com/sdk/node/get-started/)
- [Node.js + Express quick start](https://docs.arcjet.com/sdk/node/plus/express/get-started/)
- [Node.js + Hono quick start](https://docs.arcjet.com/sdk/node/plus/hono/get-started/)
- [Nuxt quick start](https://docs.arcjet.com/sdk/nuxt/get-started/)
- [Python + FastAPI quick start](https://docs.arcjet.com/sdk/python/plus/fastapi/get-started/)
- [Python + Flask quick start](https://docs.arcjet.com/sdk/python/plus/flask/get-started/)
- [React Router quick start](https://docs.arcjet.com/sdk/react-router/get-started/)
- [Remix quick start](https://docs.arcjet.com/sdk/remix/get-started/)
- [SvelteKit quick start](https://docs.arcjet.com/sdk/sveltekit/get-started/)
- [Bun + Hono quick start](https://docs.arcjet.com/sdk/bun/plus/hono/get-started/)

Full docs: https://docs.arcjet.com

Expand Down
23 changes: 22 additions & 1 deletion public/llms.txt
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,28 @@ extra layer of defense. Pricing is based on usage, see https://arcjet.com/pricin
- [MCP server](https://docs.arcjet.com/mcp-server): same management surface over MCP at `https://api.arcjet.com/mcp` (OAuth).
- [Arcjet plugin](https://docs.arcjet.com/arcjet-plugin): bundled skills + MCP + coding rules for Claude Code and Cursor.
- [Create an account](https://console.arcjet.com)
- [Quick start guides by framework](https://docs.arcjet.com/get-started)
- [Quick start guides by framework](https://docs.arcjet.com/get-started) (legacy; prefer the SDK-scoped URLs below)

### SDK-scoped quick starts

Use these stable URLs when linking to a framework-specific guide. They replace `?f=` query parameters and render the matching guide server-side.

- [Next.js](https://docs.arcjet.com/sdk/next/get-started/)
- [Astro](https://docs.arcjet.com/sdk/astro/get-started/)
- [Bun](https://docs.arcjet.com/sdk/bun/get-started/)
- [Bun + Hono](https://docs.arcjet.com/sdk/bun/plus/hono/get-started/)
- [Deno](https://docs.arcjet.com/sdk/deno/get-started/)
- [Fastify](https://docs.arcjet.com/sdk/fastify/get-started/)
- [NestJS](https://docs.arcjet.com/sdk/nest/get-started/)
- [Node.js](https://docs.arcjet.com/sdk/node/get-started/)
- [Node.js + Express](https://docs.arcjet.com/sdk/node/plus/express/get-started/)
- [Node.js + Hono](https://docs.arcjet.com/sdk/node/plus/hono/get-started/)
- [Nuxt](https://docs.arcjet.com/sdk/nuxt/get-started/)
- [Python + FastAPI](https://docs.arcjet.com/sdk/python/plus/fastapi/get-started/)
- [Python + Flask](https://docs.arcjet.com/sdk/python/plus/flask/get-started/)
- [React Router](https://docs.arcjet.com/sdk/react-router/get-started/)
- [Remix](https://docs.arcjet.com/sdk/remix/get-started/)
- [SvelteKit](https://docs.arcjet.com/sdk/sveltekit/get-started/)

## Features

Expand Down
33 changes: 33 additions & 0 deletions scripts/generate-legacy-f-redirects.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import fs from "node:fs";
import {
legacyFrameworkVercelRedirects,
variantOnlySdkVercelRedirects,
} from "../src/lib/sdk.ts";

const vercelPath = new URL("../vercel.json", import.meta.url);
const vercel = JSON.parse(fs.readFileSync(vercelPath, "utf8")) as {
redirects: Array<{ source?: string; has?: Array<{ key: string }> }>;
};

function isVariantOnlySdkRedirect(source: string | undefined): boolean {
return (
source === "/sdk/python" ||
source === "/sdk/python/:path((?!plus/).*)"
);
}

vercel.redirects = vercel.redirects.filter(
(redirect) =>
!redirect.has?.some((condition) => condition.key === "f") &&
!isVariantOnlySdkRedirect(redirect.source),
);

const variantOnly = variantOnlySdkVercelRedirects();
const generated = legacyFrameworkVercelRedirects();
vercel.redirects.unshift(...variantOnly, ...generated);

fs.writeFileSync(vercelPath, `${JSON.stringify(vercel, null, 2)}\n`);

console.log(
`Updated vercel.json with ${variantOnly.length} variant-only SDK redirects and ${generated.length} legacy ?f= redirects`,
);
23 changes: 23 additions & 0 deletions src/components/FrameworkLinks.astro
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
---
/**
* FrameworkLinks — Astro wrapper
*
* On SDK-scoped routes (`/sdk/:sdk/...`), renders nothing — the SDK switcher
* in the table of contents already provides navigation between SDK variants.
*
* On legacy (unscoped) routes, delegates to the React `FrameworkLinksReact`
* component with `client:load` so the framework link grid uses SDK routes.
*/
import FrameworkLinksReact from "@/components/FrameworkLinksReact";
import { sdkFromPathname } from "@/lib/sdk";

const sdkKey = sdkFromPathname(Astro.url.pathname);
---

{
sdkKey ? null : (
<FrameworkLinksReact client:load {...Astro.props}>
<slot />
</FrameworkLinksReact>
)
}
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import { SvelteKit as IconSvelteKit } from "@/components/icons/tech/SvelteKit";
import { VercelAi as IconVercelAi } from "@/components/icons/tech/VercelAi";
import { VercelEve as IconVercelEve } from "@/components/icons/tech/VercelEve";
import { frameworks, getStoredFramework, type FrameworkKey } from "@/lib/prefs";
import { hrefForLegacyFrameworkKey } from "@/lib/sdk";
import { queryParamFramework } from "@/store";
import { useStore } from "@nanostores/react";
import type { ForwardedRef, PropsWithChildren, ReactNode } from "react";
Expand Down Expand Up @@ -62,6 +63,14 @@ const FrameworkLinks = forwardRef(

const $queryParamFramework = useStore(queryParamFramework);

const [basePath, setBasePath] = useState(path);

useEffect(() => {
if (!path) {
setBasePath(window.location.pathname);
}
}, [path]);

useEffect(() => {
// Check if a framework is set in query params
const params = new URLSearchParams(window.location.search);
Expand Down Expand Up @@ -182,7 +191,7 @@ const FrameworkLinks = forwardRef(
key={f.key + idx}
as="link"
size="lg"
href={`${path}?f=${f.key}`}
href={hrefForLegacyFrameworkKey(f.key, basePath || path || "/")}
decoratorLeft={icon}
>
{f.label}
Expand Down
15 changes: 11 additions & 4 deletions src/components/FrameworkSwitcher.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,11 @@ import {
isValidFrameworkKey,
storeFramework,
} from "@/lib/prefs";
import {
docPathFromSdkPathname,
hrefForLegacyFrameworkKey,
sdkFromPathname,
} from "@/lib/sdk";
import {
availableFrameworks,
displayedFramework,
Expand Down Expand Up @@ -111,10 +116,12 @@ const FrameworkSwitcher = forwardRef(

storeFramework(val);

// Reload the page with the new framework in the query param
const url = new URL(window.location.toString());
url.searchParams.set("f", val);
window.location.replace(url.toString());
const currentPath = window.location.pathname;
const docPath = sdkFromPathname(currentPath)
? docPathFromSdkPathname(currentPath)
: currentPath;
const target = hrefForLegacyFrameworkKey(val as FrameworkKey, docPath);
window.location.replace(target);
};

// Sync store with current page frontmatter
Expand Down
18 changes: 18 additions & 0 deletions src/components/SdkHydration.astro
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
---
/**
* Seeds Astro island hydration on SDK routes that otherwise only contain
* static framework wrappers plus `SelectableContent` islands.
*/
import { sdkFromPathname } from "@/lib/sdk";
import FrameworkName from "@/components/FrameworkName";

const isSdkRoute = sdkFromPathname(Astro.url.pathname) !== undefined;
---

{
isSdkRoute ? (
<span hidden aria-hidden="true">
<FrameworkName client:load />
</span>
) : null
}
4 changes: 2 additions & 2 deletions src/components/SdkReferenceLinkByFramework.astro
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
---
import SlotByFramework from "@/components/SlotByFramework";
import SlotByFramework from "@/components/SlotByFramework.astro";
import { Link } from "@/components/link";
---

<SlotByFramework client:load>
<SlotByFramework>
<Link.Card
slot="bun"
title="SDK reference"
Expand Down
Loading