Skip to content
Draft
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
31 changes: 9 additions & 22 deletions astro.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,14 +12,8 @@ import { readdir, readFile } from "fs/promises";
import { join } from "path";
import { fileURLToPath } from "url";

import remarkValidateImages from "./src/plugins/remark/validate-images";

import rehypeTitleFigure from "rehype-title-figure";
import rehypeMermaid from "./src/plugins/rehype/mermaid.ts";
import rehypeAutolinkHeadings from "./src/plugins/rehype/autolink-headings.ts";
import rehypeExternalLinks from "./src/plugins/rehype/external-links.ts";
import rehypeHeadingSlugs from "./src/plugins/rehype/heading-slugs.ts";
import rehypeShiftHeadings from "./src/plugins/rehype/shift-headings.ts";
import { satteri } from "@astrojs/markdown-satteri";
import { mdastPlugins, hastPlugins } from "./src/plugins/satteri/index.ts";
import { createSitemapLastmodSerializer } from "./sitemap.serializer.ts";

import skills from "astro-skills";
Expand Down Expand Up @@ -93,26 +87,19 @@ const sidebar = await autogenSections();
const customCss = await autogenStyles();
const externalLinkPaths = await getExternalLinkPaths("src/content/docs");

const RUN_LINK_CHECK =
process.env.RUN_LINK_CHECK?.toLowerCase() === "true" || false;
// Temporarily disabled during the satteri migration; re-enable once link validation is green.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Add a tracking prefix so this does not get lost:

Suggested change
// Temporarily disabled during the satteri migration; re-enable once link validation is green.
// TODO(satteri): Temporarily disabled during the satteri migration; re-enable once link validation is green.

const RUN_LINK_CHECK = false;

// https://astro.build/config
export default defineConfig({
site: "https://developers.cloudflare.com",
cacheDir: ".astro-cache",
markdown: {
gfm: true,
smartypants: false,
remarkPlugins: [remarkValidateImages],
rehypePlugins: [
rehypeMermaid,
rehypeExternalLinks,
rehypeHeadingSlugs,
rehypeAutolinkHeadings,
// @ts-expect-error plugins types are outdated but functional
rehypeTitleFigure,
rehypeShiftHeadings,
],
processor: satteri({
mdastPlugins,
hastPlugins,
features: { gfm: true, smartPunctuation: false },
}),
},
image: {
service: {
Expand Down
10 changes: 10 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@
"@apidevtools/swagger-parser": "12.1.0",
"@astrojs/check": "0.9.9",
"@astrojs/markdown-remark": "7.2.0",
"@astrojs/markdown-satteri": "0.3.0",
"@astrojs/mdx": "6.0.3",
"@astrojs/react": "5.0.7",
"@astrojs/rss": "4.0.18",
Expand Down Expand Up @@ -114,6 +115,7 @@
"mdast-util-from-markdown": "2.0.3",
"mdast-util-mdx": "3.0.0",
"mdast-util-mdx-expression": "2.0.1",
"mdast-util-to-string": "4.0.0",
"mermaid": "11.15.0",
"micromark-extension-mdxjs": "3.0.0",
"nanostores": "1.3.0",
Expand Down Expand Up @@ -141,6 +143,7 @@
"remark": "15.0.1",
"remark-gfm": "4.0.1",
"remark-stringify": "11.0.0",
"satteri": "0.8.1",
"sharp": "0.35.1",
"solarflare-theme": "0.0.6",
"space-separated-tokens": "2.0.2",
Expand Down Expand Up @@ -178,5 +181,12 @@
"version": ">=24.0.0",
"onFail": "error"
}
},
"pnpm": {
"peerDependencyRules": {
"allowedVersions": {
"@astrojs/starlight>@astrojs/markdown-satteri": "0.3.0"
}
}
}
}
871 changes: 504 additions & 367 deletions pnpm-lock.yaml

Large diffs are not rendered by default.

34 changes: 24 additions & 10 deletions src/components/AnchorHeading.astro
Original file line number Diff line number Diff line change
@@ -1,10 +1,9 @@
---
import { z } from "astro/zod";
import { marked } from "marked";
import { slug as GithubSlug } from "github-slugger";
import { defineHastPlugin, markdownToHtml } from "satteri";

import { process } from "~/util/rehype";
import rehypeAutoLinkHeadings from "~/plugins/rehype/autolink-headings";
import autolinkHeadings from "~/plugins/satteri/autolink-headings";

type Props = z.infer<typeof props>;

Expand All @@ -16,14 +15,29 @@ const props = z.object({

const { title, slug, depth } = props.parse(Astro.props);

const slugified = GithubSlug(slug ?? title);

const tag = `h${depth}` as "h1" | "h2" | "h3" | "h4" | "h5" | "h6";
/**
* An inline plugin that adds an ID to the header so it can be linked by `autolinkHeadings`.
* The syntax used for the `headingSlugs` plugin can't be used here because satteri does not have a simple way to go from mdx to html.
* This one-off plugin allows us to do the same thing via pure md.
**/
const setHeadingId = defineHastPlugin({
name: "set-heading-id",
element: {
filter: ["h1", "h2", "h3", "h4", "h5", "h6"],
visit(node, ctx) {
const id = slug ?? ctx.textContent(node);
if (id) {
ctx.setProperty(node, "id", GithubSlug(id));
}
},
},
});

const html = await process(
`<${tag} id=${slugified}>${marked.parseInline(title)}</${tag}>`,
[rehypeAutoLinkHeadings],
);
const md = `${"#".repeat(depth)} ${title}`;
const { html } = markdownToHtml(md, {
features: { gfm: true, smartPunctuation: false },
hastPlugins: [setHeadingId, autolinkHeadings],
});
---

<Fragment set:html={html} />
31 changes: 9 additions & 22 deletions src/components/ListTutorials.astro
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,6 @@
import { getCollection, getEntry, type CollectionEntry } from "astro:content";
import { formatDistance } from "date-fns";

import { process } from "~/util/rehype";
import rehypeExternalLinks from "~/plugins/rehype/external-links";

type DocsEntry = CollectionEntry<"docs">;

const tutorials: Array<DocsEntry> = [];
Expand Down Expand Up @@ -51,25 +48,15 @@ const timeAgo = (date?: Date) => {
</thead>
<tbody>
{
tutorials.map(async (tutorial) => {
const title = tutorial.data.title;

const href = `/${tutorial.id}/`;

const anchor = await process(`<a href=${href}>${title}</a>`, [
rehypeExternalLinks,
]);

return (
<tr>
<td>
<Fragment set:html={anchor} />
</td>
<td>{timeAgo(tutorial.data.reviewed)}</td>
<td>{tutorial.data.difficulty}</td>
</tr>
);
})
tutorials.map((tutorial) => (
<tr>
<td>
<a href={`/${tutorial.id}/`}>{tutorial.data.title}</a>
</td>
<td>{timeAgo(tutorial.data.reviewed)}</td>
<td>{tutorial.data.difficulty}</td>
</tr>
))
}
</tbody>
</table>
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ title: New Confidence Intervals in GraphQL Analytics API
description: Confidence intervals are now supported in the GraphQL Analytics API to show statistical ranges for sampled data results
date: 2025-10-01
---

The GraphQL Analytics API now supports confidence intervals for `sum` and `count` fields on adaptive (sampled) datasets. Confidence intervals provide a statistical range around sampled results, helping verify accuracy and quantify uncertainty.

- **Supported datasets**: Adaptive (sampled) datasets only.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ We overhauled the Invite Members UI to simplify inviting users and assigning per


**Refreshed Members Overview Page**

We've updated the Members Overview Page to clearly display:
- Member 2FA status
- Which members hold Super Admin privileges
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,9 @@ title: Scam domain category introduced under Security Threats
description: You can now create policies specifically targeting the Scam content
date: 2025-07-28
---

We have introduced a new Security Threat category called **Scam**. Relevant domains are marked with the Scam category. Scam typically refers to fraudulent websites and schemes designed to trick victims into giving away money or personal information.

**New category added**


Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,9 @@ title: New APIs for Brand Protection setup
description: You can now use the Brand Protection API endpoints to manage your Brand Protection queries
date: 2025-07-18
---

The Brand Protection API is now available, allowing users to create new queries and delete existing ones, fetch matches and more!

What you can do:
- **create new string or logo query**
- **delete string or logo queries**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,25 +3,25 @@ title: Real-time alerts and daily digests for Threat Events
description: You can now subscribe to automated notifications for your saved views in Threat Events
date: 2026-04-08
---

You can now automate your threat monitoring by setting up custom alerts in your saved views. Instead of manually checking the dashboard for updates, you can subscribe to notifications that trigger whenever new data matches your specific filter sets, like new activity associated to a particular threat actor or spikes in activity within your industry.

### Stay ahead of emerging threats

By linking your saved views to the Cloudflare Notifications Center, you can ensure the right information reaches your team at the right time.

- **Immediate Alerts**: receive real-time notifications the moment a critical event is detected that matches your saved criteria. This is essential for high-priority monitoring, such as tracking active campaigns from specific APT groups.

- **Daily Digests**: opt for a summarized report delivered once a day. This is ideal for maintaining situational awareness of broader trends, like regional activity shifts or industry-wide threat landscapes, without cluttering your inbox.

![Threat Events notifications](~/assets/images/changelog/security-center/threat-events-notifications.png)

### How to get started

To set up an alert, go to **Application Security** > **Threat Intelligence** > **Threat Events**. From there:

1. Choose your datasets and apply your desired filters and select **Save View** (or select an existing one).
2. Open the **Manage Saved Views** menu.
3. Select **Add Alert** next to your chosen view to configure your notification preferences in the Cloudflare dashboard.

For more technical details on configuring notifications, refer to the [Threat Events documentation](/security-center/cloudforce-one/).
18 changes: 9 additions & 9 deletions src/content/changelog/waf/2026-05-04-waf-release.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -3,21 +3,21 @@ title: "WAF Release - 2026-05-04"
description: Cloudflare WAF managed rulesets 2026-05-04 release
date: 2026-05-04
---

import { RuleID } from "~/components";

This week's release focuses on new detections to expand coverage across command injection, SQL injection, PHP object injection, remote code execution, and XSS attack vectors.

**Key Findings**

- Existing rule enhancements have been deployed to improve detection resilience against broad classes of web attacks and strengthen behavioral coverage.


**Continuous Rule Improvements**

We are continuously refining our managed rules to provide more resilient protection and deeper insights into attack patterns. To ensure an optimal security posture, we recommend consistently monitoring the Security Events dashboard and adjusting rule actions as these enhancements are deployed.


<table style="width: 100%">
<thead>
<tr>
Expand Down
20 changes: 10 additions & 10 deletions src/content/changelog/workers-ai/2025-10-02-deepgram-flux.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -31,53 +31,53 @@ export default {
},
} satisfies ExportedHandler<Env>;
```

2. Deploy your worker
```bash
npx wrangler deploy
```

3. Write a client script to connect to your worker and start sending random audio bytes to it
```js
const ws = new WebSocket('wss://<your-worker-url.com>');

ws.onopen = () => {
console.log('Connected to WebSocket');

// Generate and send random audio bytes
// You can replace this part with a function
// that reads from your mic or other audio source
const audioData = generateRandomAudio();
ws.send(audioData);
console.log('Audio data sent');
};

ws.onmessage = (event) => {
// Transcription will be received here
// Add your custom logic to parse the data
console.log('Received:', event.data);
};

ws.onerror = (error) => {
console.error('WebSocket error:', error);
};

ws.onclose = () => {
console.log('WebSocket closed');
};

// Generate random audio data (1 second of noise at 44.1kHz, mono)
function generateRandomAudio() {
const sampleRate = 44100;
const duration = 1;
const numSamples = sampleRate * duration;
const buffer = new ArrayBuffer(numSamples * 2);
const view = new Int16Array(buffer);

for (let i = 0; i < numSamples; i++) {
view[i] = Math.floor(Math.random() * 65536 - 32768);
}

return buffer;
}
```
Original file line number Diff line number Diff line change
Expand Up @@ -106,20 +106,20 @@ Through Workers AI Binding:
async function streamToBlob(stream: ReadableStream, contentType: string): Promise<Blob> {
const reader = stream.getReader();
const chunks = [];

while (true) {
const { done, value } = await reader.read();
if (done) break;
chunks.push(value);
}

return new Blob(chunks, { type: contentType });
}

const image0 = await fetch("http://image-url");
const image1 = await fetch("http://image-url");
const form = new FormData();

const image_blob0 = await streamToBlob(image0.body, "image/png");
const image_blob1 = await streamToBlob(image1.body, "image/png");
form.append('input_image_0', image_blob0)
Expand All @@ -134,7 +134,7 @@ const formRequest = new Request('http://dummy', {
});
const formStream = formRequest.body;
const formContentType = formRequest.headers.get('content-type') || 'multipart/form-data';

const resp = await env.AI.run("@cf/black-forest-labs/flux-2-dev", {
multipart: {
body: form,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ title: 'Workers Static Assets: Corrected handling of double slashes in redirect
description: Corrected handling of double slashes in redirect rule paths
date: 2025-08-15
---

[Static Assets](/workers/static-assets/): Fixed a bug in how [redirect rules](https://developers.cloudflare.com/workers/static-assets/redirects/) defined in your Worker's `_redirects` file are processed.

If you're serving Static Assets with a `_redirects` file containing a rule like `/ja/* /:splat`, paths with double slashes were previously misinterpreted as external URLs. For example, visiting `/ja//example.com` would incorrectly redirect to `https://example.com` instead of `/example.com` on your domain. This has been fixed and double slashes now correctly resolve as local paths. Note: [Cloudflare Pages](/pages/) was not affected by this issue.
Loading