const ORIGIN = 'https://developers.cloudflare.com' const MARKDOWN_ACCEPT = 'text/markdown, text/plain;q=0.9, text/html;q=0.8' const PREFIXES = [ '/api/', '/fundamentals/', '/workers/', '/workers-ai/', '/ai-gateway/', '/d1/', '/r2/', '/kv/', '/durable-objects/', '/queues/', '/vectorize/', '/pages/', ] function assertAllowedPath(path) { const value = String(path || '').trim() if (!value.startsWith('/')) { throw new Error('path must start with `/` and must not include a host.') } if (!PREFIXES.some((prefix) => value.startsWith(prefix))) { throw new Error('path must start with an allowlisted Cloudflare docs prefix.') } if (value.includes('..')) { throw new Error('path must not contain `..`.') } if (/\s|#/.test(value)) { throw new Error('path contains disallowed characters.') } if (value.length > 2048) { throw new Error('path exceeds maximum length.') } return value } export type CloudflareDocsInput = { path?: string } export type CloudflareDocsResult = { status: number path: string contentType: string | null markdownTokenEstimate: string | null body: string } /** * Fetch an allowlisted Cloudflare developer docs page as markdown-oriented text. * @param params.path - Docs path under an allowlisted prefix such as `/workers/`. * @returns HTTP status, content type, and truncated page body. * @example * import docs from 'kody:@kentcdodds/cloudflare/docs' * const result = await docs({ path: '/workers/' }) * // => { status: 200, path: '/workers/', body: '...' } */ export default async function cloudflareDocs(params: CloudflareDocsInput = {}): Promise { const path = assertAllowedPath(params.path || '/workers/') const response = await fetch(new URL(path, ORIGIN).toString(), { headers: { Accept: MARKDOWN_ACCEPT }, }) const body = await response.text() return { status: response.status, path, contentType: response.headers.get('content-type'), markdownTokenEstimate: response.headers.get('x-markdown-tokens'), body: body.slice(0, 500000), } }