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
2 changes: 1 addition & 1 deletion packages/cloudflare-api-site/nimbus.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"$schema": "https://nimbus-docs.com/schema/nimbus.json",
"version": "0.7.12",
"lastReviewedNimbusVersion": "0.18.0",
"lastReviewedNimbusVersion": "0.19.0",
"templatesTag": "templates-v0.7.12",
"variant": "empty",
"registry": "https://nimbus-docs.com/registry",
Expand Down
2 changes: 1 addition & 1 deletion packages/cloudflare-api-site/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
"dependencies": {
"@astrojs/cloudflare": "^15.0.0-beta.2",
"@astrojs/prism": "^4.0.2",
"@cloudflare/nimbus-docs": "^0.18.0",
"@cloudflare/nimbus-docs": "^0.19.0",
"@fontsource-variable/inter": "5.3.0",
"@fontsource-variable/jetbrains-mono": "5.3.0",
"@iconify-json/ph": "^1.2.2",
Expand Down
16 changes: 8 additions & 8 deletions packages/cloudflare-api-site/src/description-markdown.test.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import assert from 'node:assert/strict';
import { test } from 'node:test';
import { keepPlaceholders, renderDescription } from './description-markdown.ts';
import { keepDroppedHtml, renderDescription } from './description-markdown.ts';

const ASTRO_FERN_PROBE = '<i data-astro-fern-probe></i> [probe](javascript:probe)';

Expand All @@ -19,13 +19,13 @@ test('placeholders inside code are left alone', () => {
'~~~\nmetadata.<key>\n~~~',
'Para\n\n metadata.<key>\n\nafter',
]) {
assert.equal(keepPlaceholders(markdown), markdown);
assert.equal(keepDroppedHtml(markdown), markdown);
}
});

test('a leading indent or blank lines are trimmed, as Nimbus does, so the placeholder is escaped', () => {
assert.equal(keepPlaceholders(' metadata.<key> leading indent'), 'metadata.&lt;key> leading indent');
assert.equal(keepPlaceholders('\n\nmetadata.<key> after blank lines'), 'metadata.&lt;key> after blank lines');
test('a leading indent or blank lines are trimmed, as Nimbus does, so the comment is escaped', () => {
assert.equal(keepDroppedHtml(' <!--sse--> leading indent'), '&lt;!--sse--> leading indent');
assert.equal(keepDroppedHtml('\n\n<!--sse--> after blank lines'), '&lt;!--sse--> after blank lines');
});

test('escaped backticks around a placeholder still escape it', () => {
Expand All @@ -38,7 +38,7 @@ test('already-escaped placeholders, autolinks and angle-bracket links are unchan
'Autolink <https://www.cloudflare.com/learning/dns/dns-records/dns-txt-record/> ok',
'Link [a](<https://x>) ok',
]) {
assert.equal(keepPlaceholders(markdown), markdown);
assert.equal(keepDroppedHtml(markdown), markdown);
}
assert.match(
renderDescription('<https://www.cloudflare.com/learning/dns/dns-records/dns-txt-record/>'),
Expand All @@ -62,8 +62,8 @@ test('allowed tags still render, including a placeholder named like one', () =>
assert.equal(renderDescription('metadata.<b>bold</b>'), '<p>metadata.<b>bold</b></p>\n');
});

test('offsets count string characters, so text before a placeholder can be non-ASCII', () => {
assert.equal(keepPlaceholders('Ünïcödé — “quotes” then metadata.<key>'), 'Ünïcödé — “quotes” then metadata.&lt;key>');
test('offsets count string characters, so text before a comment can be non-ASCII', () => {
assert.equal(keepDroppedHtml('Ünïcödé — “quotes” then <!--sse-->'), 'Ünïcödé — “quotes” then &lt;!--sse-->');
});

test('unsafe HTML and links stay inert', () => {
Expand Down
17 changes: 7 additions & 10 deletions packages/cloudflare-api-site/src/description-markdown.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ interface MdastNode {
children?: MdastNode[];
}

const ALLOWED_TAGS = new Set((defaultSchema.tagNames ?? []).map((name) => name.toLowerCase()));
const DROPPED_TAGS = new Set((defaultSchema.strip ?? []).map((name) => name.toLowerCase()));
const TAG_NAME = /^<\/?([A-Za-z][A-Za-z0-9-]*)/;

function htmlRanges(node: MdastNode, ranges: [number, number][]): [number, number][] {
Expand All @@ -19,18 +19,15 @@ function htmlRanges(node: MdastNode, ranges: [number, number][]): [number, numbe
return ranges;
}

function escapeUnknownTags(html: string): string {
function escapeDroppedTags(html: string): string {
return html.replace(/</g, (lt, offset: number) => {
const name = TAG_NAME.exec(html.slice(offset))?.[1]?.toLowerCase();
return name !== undefined && ALLOWED_TAGS.has(name) ? lt : '&lt;';
return name === undefined || DROPPED_TAGS.has(name) ? '&lt;' : lt;
});
}

/**
* Escapes raw HTML the sanitizer would drop, such as `metadata.<key>` placeholders and comments, so
* it shows as text. Trims like Nimbus's renderer, so both parse the same block structure.
*/
export function keepPlaceholders(markdown: string): string {
// Nimbus keeps unknown tags such as `metadata.<key>` as text but drops comments and `<script>`, which some descriptions use as text.
export function keepDroppedHtml(markdown: string): string {
const trimmed = markdown.trim();
let tree: MdastNode;
try {
Expand All @@ -40,9 +37,9 @@ export function keepPlaceholders(markdown: string): string {
}
let result = trimmed;
for (const [start, end] of htmlRanges(tree, []).sort((a, b) => b[0] - a[0])) {
result = result.slice(0, start) + escapeUnknownTags(result.slice(start, end)) + result.slice(end);
result = result.slice(0, start) + escapeDroppedTags(result.slice(start, end)) + result.slice(end);
}
return result;
}

export const renderDescription = (markdown: string): string => renderMarkdown(keepPlaceholders(markdown));
export const renderDescription = (markdown: string): string => renderMarkdown(keepDroppedHtml(markdown));
6 changes: 0 additions & 6 deletions packages/cloudflare-api-site/src/pages/[...slug].astro
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
---
import DocsLayout from "../layouts/DocsLayout.astro";
import {
getDocsStaticPaths,
getDocsPage,
getRouteFlags,
getBreadcrumbs,
Expand All @@ -14,11 +13,6 @@ import { components } from "../components";
import { getApiRouter } from "../api-server.ts";
import { getPageSidebar } from "../page-sidebar.ts";

// Nimbus's `rendering: { default: "request" }` turns this route into on-demand
// rendering; Nimbus requires canonical content routes to keep these exports.
export const prerender = true;
export const getStaticPaths = getDocsStaticPaths;

// The overview honours the site-wide `?version=` / `?lang=` selection; reject
// empty, duplicate, or unknown values like the API routes do.
const router = await getApiRouter();
Expand Down
3 changes: 1 addition & 2 deletions packages/cloudflare-api-site/src/pages/[...slug]/index.md.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import { markdownRoute } from '@cloudflare/nimbus-docs/agent-endpoints';

export const prerender = true;
export const { GET, getStaticPaths } = markdownRoute();
export const { GET } = markdownRoute();
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import { markdownSourceRoute } from '@cloudflare/nimbus-docs/agent-endpoints';

export const prerender = true;
export const { GET, getStaticPaths } = markdownSourceRoute();
export const { GET } = markdownSourceRoute();
1 change: 0 additions & 1 deletion packages/cloudflare-api-site/src/pages/llms-full.txt.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import { llmsFullRoute } from '@cloudflare/nimbus-docs/agent-endpoints';

export const prerender = true;
export const { GET } = llmsFullRoute();
85 changes: 32 additions & 53 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions pnpm-workspace.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -10,3 +10,4 @@ minimumReleaseAgeExclude:
- astro@7.4.1
- cf@1.0.0-beta.19
- miniflare@5.20261019.0-beta
- '@cloudflare/nimbus-docs@0.19.0'
Loading