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
2 changes: 1 addition & 1 deletion packages/web/app/docs.md/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ function name(path: string): string {

export async function GET(): Promise<Response> {
const paths = await files()
const chunks: string[] = ["# cruel docs", ""]
const chunks: string[] = ["# Cruel Docs", ""]

for (const path of paths) {
const raw = await readFile(path, "utf8")
Expand Down
52 changes: 49 additions & 3 deletions packages/web/app/docs/layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,58 @@ export default function Layout({ children }: { children: ReactNode }) {
<DocsLayout
tree={source.getPageTree()}
nav={{
title: "cruel",
title: (
<span className="inline-flex items-center justify-center" aria-label="Cruel">
<svg
viewBox="0 0 32 32"
className="size-5 text-[#f1ecde]"
fill="none"
aria-hidden="true"
>
<line
x1="16"
y1="4"
x2="16"
y2="28"
stroke="currentColor"
strokeWidth="1.7"
strokeLinecap="round"
/>
<line
x1="4"
y1="16"
x2="28"
y2="16"
stroke="currentColor"
strokeWidth="1.7"
strokeLinecap="round"
/>
<line
x1="7.5"
y1="7.5"
x2="24.5"
y2="24.5"
stroke="currentColor"
strokeWidth="1.7"
strokeLinecap="round"
/>
<line
x1="24.5"
y1="7.5"
x2="7.5"
y2="24.5"
stroke="currentColor"
strokeWidth="1.7"
strokeLinecap="round"
/>
</svg>
</span>
),
url: "/",
}}
links={[
{ text: "story", url: "/story" },
{ text: "github", url: "https://github.com/visible/cruel" },
{ text: "Story", url: "/story" },
{ text: "GitHub", url: "https://github.com/visible/cruel" },
]}
themeSwitch={{ enabled: false }}
>
Expand Down
16 changes: 8 additions & 8 deletions packages/web/app/layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,32 +6,32 @@ import type { Metadata, Viewport } from "next"
import "./globals.css"

export const metadata: Metadata = {
title: "cruel",
description: "chaos testing with zero mercy",
title: "Cruel",
description: "Chaos testing with zero mercy",
metadataBase: new URL("https://cruel.dev"),
icons: {
icon: "/icon.svg",
apple: "/apple-icon.png",
},
openGraph: {
title: "cruel",
description: "chaos testing with zero mercy",
title: "Cruel",
description: "Chaos testing with zero mercy",
url: "https://cruel.dev",
siteName: "cruel",
siteName: "Cruel",
type: "website",
images: [
{
url: "/og.png",
width: 1200,
height: 630,
alt: "cruel",
alt: "Cruel",
},
],
},
twitter: {
card: "summary_large_image",
title: "cruel",
description: "chaos testing with zero mercy",
title: "Cruel",
description: "Chaos testing with zero mercy",
images: ["/og.png"],
},
}
Expand Down
2 changes: 1 addition & 1 deletion packages/web/app/llms.txt/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ export async function GET(): Promise<Response> {
const lines: string[] = [
"project: cruel",
"site: https://cruel.dev",
"summary: chaos engineering for ai sdk and async apis",
"summary: chaos engineering for AI SDK and async APIs",
"",
"docs_markdown: https://cruel.dev/docs.md",
"docs_root: https://cruel.dev/docs",
Expand Down
84 changes: 42 additions & 42 deletions packages/web/app/story/page.tsx

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion packages/web/components/landing/cursor-bento.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ const items: readonly item[] = [
title: "Core and AI SDK",
body: "Wrap fetch and async functions with cruel(...), or wrap providers and models with cruel/ai-sdk.",
href: "/docs/core",
label: "Read core api",
label: "Read Core API",
},
{
id: "003",
Expand Down
36 changes: 18 additions & 18 deletions packages/web/content/docs/advanced.mdx
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
---
title: advanced
description: presets, global mode, scenarios, fetch interception
title: Advanced
description: Presets, global mode, scenarios, and fetch interception
---

## presets
## Presets

```ts
cruel.enable(cruel.presets.development)
Expand All @@ -14,14 +14,14 @@ cruel.enable(cruel.presets.nightmare)
cruel.enable(cruel.presets.apocalypse)
```

## profiles
## Profiles

```ts
cruel.profile("testing", { fail: 0.2, delay: 100 })
cruel.useProfile("testing")
```

## global mode
## Global Mode

```ts
cruel.enable({ fail: 0.1, delay: [100, 500] })
Expand All @@ -30,15 +30,15 @@ cruel.toggle()
cruel.isEnabled()
```

## scoped chaos
## Scoped Chaos

```ts
await cruel.scope(async () => {
await api("...")
}, { fail: 0.2 })
```

## fluent api
## Fluent API

```ts
cruel.wrap(fn).fail(0.1)
Expand All @@ -48,15 +48,15 @@ cruel.wrap(fn).flaky()
cruel.wrap(fn).nightmare()
```

## factory
## Factory

```ts
import { createCruel } from "cruel"

const myCruel = createCruel({ delay: 100 })
```

## scenarios
## Scenarios

```ts
cruel.scenario("outage", {
Expand All @@ -73,7 +73,7 @@ await cruel.play("degraded")
await cruel.play("recovery")
```

## fetch interception
## Fetch Interception

```ts
cruel.patchFetch()
Expand All @@ -91,13 +91,13 @@ cruel.intercept(/api\.anthropic\.com/, {
cruel.unpatchFetch()
```

## production rollout checklist
## Production Rollout Checklist

for production-like chaos drills, keep blast radius controlled:
For production-like chaos drills, keep blast radius controlled:

1. start with low rates (`0.01` - `0.05`) and increase gradually
2. run chaos in a scoped block or dedicated test path first
3. attach `onChaos` and record event types, model ids, and latency
4. keep retry, fallback, and circuit-breaker logic enabled during tests
5. run deterministic passes with `cruel.seed(...)` before random passes
6. stop chaos immediately if error budget or latency thresholds are exceeded
1. Start with low rates (`0.01` - `0.05`) and increase gradually.
2. Run chaos in a scoped block or dedicated test path first.
3. Attach `onChaos` and record event types, model IDs, and latency.
4. Keep retry, fallback, and circuit-breaker logic enabled during tests.
5. Run deterministic passes with `cruel.seed(...)` before random passes.
6. Stop chaos immediately if error budget or latency thresholds are exceeded.
32 changes: 16 additions & 16 deletions packages/web/content/docs/aisdk.mdx
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
---
title: ai sdk
description: chaos testing for ai providers, models, and tools
title: AI SDK
description: Chaos testing for AI providers, models, and tools
---

## ai failure modes
## AI Failure Modes

```ts
cruel.ai.rateLimit(fn, 0.1)
Expand All @@ -19,7 +19,7 @@ cruel.ai.realistic(fn)
cruel.ai.nightmare(fn)
```

## wrap a provider
## Wrap a Provider

```ts
import { cruelProvider } from "cruel/ai-sdk"
Expand All @@ -38,28 +38,28 @@ const result = await generateText({
})
```

## wrap a model
## Wrap a Model

```ts
import { cruelModel, presets } from "cruel/ai-sdk"

const model = cruelModel(openai("gpt-4o"), presets.realistic)
```

## model override for tests
## Model Override for Tests

set `MODEL` to swap model ids without editing code:
Set `MODEL` to swap model IDs without editing code:

```bash
MODEL=gpt-6 bun run your-script.ts
```

this works for ids with and without a provider prefix:
This works for IDs with and without a provider prefix:

- `gpt-4o` -> `gpt-6`
- `openai/gpt-4o` -> `openai/gpt-6`

## streaming with chaos
## Streaming with Chaos

```ts
import { cruelModel } from "cruel/ai-sdk"
Expand All @@ -73,7 +73,7 @@ const result = streamText({
})
```

## middleware
## Middleware

```ts
import { cruelMiddleware } from "cruel/ai-sdk"
Expand All @@ -92,7 +92,7 @@ const model = wrapLanguageModel({
})
```

## presets
## Presets

```ts
import { presets } from "cruel/ai-sdk"
Expand All @@ -104,7 +104,7 @@ presets.nightmare // extreme chaos
presets.apocalypse // everything fails
```

## tool wrapping
## Tool Wrapping

```ts
import { cruelTools } from "cruel/ai-sdk"
Expand All @@ -119,9 +119,9 @@ const tools = cruelTools({
})
```

## error handling
## Error Handling

cruel errors are fully compatible with the ai sdk's `APICallError.isInstance()` check, so retries work automatically:
Cruel errors are fully compatible with the AI SDK's `APICallError.isInstance()` check, so retries work automatically:

```ts
import { APICallError } from "ai"
Expand All @@ -136,7 +136,7 @@ try {
}
```

## embeddings
## Embeddings

```ts
import { cruelEmbeddingModel } from "cruel/ai-sdk"
Expand All @@ -151,7 +151,7 @@ const model = cruelEmbeddingModel(openai.embedding("text-embedding-3-small"), {
const { embedding } = await embed({ model, value: "hello" })
```

## images
## Images

```ts
import { cruelImageModel } from "cruel/ai-sdk"
Expand Down
10 changes: 5 additions & 5 deletions packages/web/content/docs/chaos.mdx
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
---
title: chaos injection
description: network, http, and stream failure simulation
title: Chaos Injection
description: Network, HTTP, and stream failure simulation
---

## network
## Network

```ts
cruel.network.latency(fn, [100, 500])
Expand All @@ -15,7 +15,7 @@ cruel.network.unstable(fn)
cruel.network.offline(fn)
```

## http
## HTTP

```ts
cruel.http.status(fn, 500, 0.1)
Expand All @@ -28,7 +28,7 @@ cruel.http.serviceUnavailable(fn)
cruel.http.gatewayTimeout(fn)
```

## streams
## Streams

```ts
cruel.stream.cut(fn, 0.1)
Expand Down
Loading