feat(site): improve motion, responsive UX, and agent readiness (#426)

This commit is contained in:
Rohit Ghumare
2026-08-23 19:21:54 +01:00
committed by GitHub
parent 60aa04f31e
commit 6571f5430a
12 changed files with 377 additions and 4 deletions
+83
View File
@@ -0,0 +1,83 @@
const fs = require('fs');
const path = require('path');
const SITE_ROOT = path.join(__dirname, '..', 'site');
const HTML_BY_PATH = {
'/': 'index.html',
'/about': 'about.html',
'/catalog': 'catalog.html',
'/glossary': 'glossary.html',
'/path': 'prereqs.html',
'/roadmap': 'prereqs.html',
'/developer': 'developer.html',
'/docs': 'developer.html',
'/contact': 'contact.html',
'/privacy': 'privacy.html',
};
function parseAccept(header) {
if (!header) return [{ type: 'text/html', q: 1 }];
return header.split(',').map((part, index) => {
const [rawType, ...params] = part.trim().toLowerCase().split(';');
const qParam = params.find((param) => param.trim().startsWith('q='));
const q = qParam ? Number.parseFloat(qParam.trim().slice(2)) : 1;
return { type: rawType.trim(), q: Number.isFinite(q) ? Math.max(0, Math.min(1, q)) : 0, index };
}).filter((entry) => entry.type && entry.q > 0).sort((a, b) => {
if (b.q !== a.q) return b.q - a.q;
const specificity = (type) => type === '*/*' ? 0 : type.endsWith('/*') ? 1 : 2;
return specificity(b.type) - specificity(a.type) || a.index - b.index;
});
}
function qualityFor(accepted, mediaType) {
const exact = accepted.find((entry) => entry.type === mediaType);
if (exact) return exact.q;
const wildcard = accepted.find((entry) => entry.type === '*/*' || entry.type === `${mediaType.split('/')[0]}/*`);
return wildcard ? wildcard.q : 0;
}
function markdownFor(requestPath) {
const llms = fs.readFileSync(path.join(SITE_ROOT, 'llms.txt'), 'utf8');
if (requestPath === '/') return llms;
return `# AI Engineering from Scratch\n\nCanonical page: https://aiengineeringfromscratch.com${requestPath}\n\nThe agent-oriented curriculum index is available at https://aiengineeringfromscratch.com/llms.txt.\n\n${llms}`;
}
module.exports = (req, res) => {
const requestPath = String((req.query && req.query.path) || '/').split('?')[0] || '/';
const accepted = parseAccept(req.headers.accept || '');
const markdownQ = qualityFor(accepted, 'text/markdown');
const htmlQ = qualityFor(accepted, 'text/html');
res.setHeader('Vary', 'Accept, Accept-Encoding');
res.setHeader('Cache-Control', 'public, max-age=300, s-maxage=86400, stale-while-revalidate=604800');
const file = HTML_BY_PATH[requestPath];
if (!file) {
res.statusCode = 404;
if (markdownQ >= htmlQ && markdownQ > 0) {
res.setHeader('Content-Type', 'text/markdown; charset=utf-8');
res.end('# AI Engineering from Scratch\n\nThis path does not exist.\n\nTry /llms.txt or /sitemap.xml.\n');
} else {
res.setHeader('Content-Type', 'text/html; charset=utf-8');
res.end(fs.readFileSync(path.join(SITE_ROOT, '404.html'), 'utf8'));
}
return;
}
if (!markdownQ && !htmlQ && accepted.length && !accepted.some((entry) => entry.type === '*/*')) {
res.statusCode = 406;
res.setHeader('Content-Type', 'text/plain; charset=utf-8');
res.end('Not Acceptable\n\nAvailable representations: text/html, text/markdown\n');
return;
}
if (markdownQ >= htmlQ && markdownQ > 0) {
res.statusCode = 200;
res.setHeader('Content-Type', 'text/markdown; charset=utf-8');
res.end(markdownFor(requestPath));
return;
}
res.statusCode = 200;
res.setHeader('Content-Type', 'text/html; charset=utf-8');
res.end(fs.readFileSync(path.join(SITE_ROOT, file), 'utf8'));
};
+43
View File
@@ -0,0 +1,43 @@
const assert = require('node:assert/strict');
const test = require('node:test');
const handler = require('../api/markdown.js');
function request(accept, requestPath = '/') {
const headers = {};
let body = '';
const res = {
statusCode: 200,
setHeader(name, value) { headers[name.toLowerCase()] = value; },
end(value) { body = value || ''; },
};
handler({ headers: { accept }, query: { path: requestPath } }, res);
return { res, headers, body };
}
test('serves Markdown for the canonical homepage when preferred', () => {
const result = request('text/markdown, text/html;q=0.8');
assert.equal(result.res.statusCode, 200);
assert.match(result.headers['content-type'], /^text\/markdown/);
assert.equal(result.headers.vary, 'Accept, Accept-Encoding');
assert.match(result.body, /^# AI Engineering from Scratch/);
});
test('keeps HTML when HTML has the higher quality value', () => {
const result = request('text/html, text/markdown;q=0.5');
assert.equal(result.res.statusCode, 200);
assert.match(result.headers['content-type'], /^text\/html/);
assert.match(result.body, /<html/i);
});
test('returns 406 when the client rejects both supported types', () => {
const result = request('application/json');
assert.equal(result.res.statusCode, 406);
assert.match(result.body, /Not Acceptable/);
});
test('returns an agent-readable 404 for an unknown negotiated route', () => {
const result = request('text/markdown', '/does-not-exist');
assert.equal(result.res.statusCode, 404);
assert.match(result.headers['content-type'], /^text\/markdown/);
assert.match(result.body, /llms\.txt/);
});
+53
View File
@@ -0,0 +1,53 @@
#!/usr/bin/env python3
"""Contract checks for the site's agent-facing discovery surfaces."""
import json
from pathlib import Path
ROOT = Path(__file__).resolve().parents[1]
SITE = ROOT / "site"
def main() -> None:
config = json.loads((ROOT / "vercel.json").read_text())
rewrites = config["rewrites"]
markdown_rewrites = [r for r in rewrites if "has" in r and r["destination"] == "/llms.txt"]
negotiator_rewrites = [r for r in rewrites if r.get("destination", "").startswith("/api/markdown")]
assert negotiator_rewrites, "markdown negotiation rewrite is missing"
assert all("accept" in h["key"].lower() for r in negotiator_rewrites for h in r["has"])
headers = config["headers"]
llms_header = next(h for h in headers if h["source"] == "/llms.txt")
values = {h["key"].lower(): h["value"] for h in llms_header["headers"]}
assert values["content-type"].startswith("text/markdown")
assert values["vary"] == "Accept, Accept-Encoding"
for name in ("404.html", "developer.html", "contact.html", "privacy.html", "openapi.json"):
assert (SITE / name).is_file(), f"missing {name}"
for name in ("developer.html", "contact.html", "privacy.html"):
text = (SITE / name).read_text()
assert "AI Engineering from Scratch" in text
assert len(" ".join(text.split())) > 500, f"{name} is too thin to be a trust page"
openapi = json.loads((SITE / "openapi.json").read_text())
assert openapi["openapi"].startswith("3.")
assert "https://aiengineeringfromscratch.com" in openapi["servers"][0]["url"]
assert {"/llms.txt", "/sitemap.xml", "/lesson.html"} <= set(openapi["paths"])
home = (SITE / "index.html").read_text()
assert '"@type": "Organization"' in home
assert '"url": "https://aiengineeringfromscratch.com"' in home
assert '"sameAs"' in home
assert '"contactPoint"' in home
not_found = (SITE / "404.html").read_text()
assert "/llms.txt" in not_found and "/sitemap.xml" in not_found
assert (ROOT / "api" / "markdown.js").is_file()
assert "Vary" in (ROOT / "api" / "markdown.js").read_text()
print("agent readiness contracts: ok")
if __name__ == "__main__":
main()
+10
View File
@@ -0,0 +1,10 @@
<!DOCTYPE html>
<html lang="en"><head><meta charset="UTF-8"><meta name="robots" content="noindex"><title>404 · AI Engineering from Scratch</title><link rel="stylesheet" href="style.css?v=20260802a"></head>
<body><main class="container" style="max-width:760px;padding-top:18vh"><p class="about-eyebrow">404 · AI Engineering from Scratch</p><h1>Page not found</h1><pre style="white-space:pre-wrap;line-height:1.7"># AI Engineering from Scratch
This path does not exist.
Try the sitemap: /sitemap.xml
Try the agent index: /llms.txt
Try the catalog: /catalog.html
Try the developer resources: /developer.html</pre></main></body></html>
+2
View File
@@ -7,6 +7,7 @@
<link rel="icon" href="data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 32 32'><rect width='32' height='32' fill='%23fafaf5'/><rect x='2' y='2' width='28' height='28' fill='none' stroke='%233553ff' stroke-width='1.2'/><text x='6' y='22' font-size='14' font-family='monospace' fill='%233553ff'>AI</text></svg>">
<meta name="description" content="What AI Engineering from Scratch is, why it exists, who builds it, and how the site is made. A free, open-source, MIT-licensed curriculum.">
<link rel="canonical" href="https://aiengineeringfromscratch.com/about.html">
<link rel="alternate" type="text/markdown" href="https://aiengineeringfromscratch.com/llms.txt">
<meta property="og:title" content="About · AI Engineering from Scratch">
<meta property="og:description" content="Why this curriculum exists, who builds it, and how the site is made. Free, open source, MIT.">
<meta property="og:image" content="https://aiengineeringfromscratch.com/og-image.png?v=3">
@@ -157,6 +158,7 @@
"name": "AI Engineering from Scratch",
"url": "https://aiengineeringfromscratch.com",
"sameAs": ["https://github.com/rohitg00/ai-engineering-from-scratch", "https://github.com/rohitg00"],
"contactPoint": { "@type": "ContactPoint", "contactType": "project support", "url": "https://github.com/rohitg00/ai-engineering-from-scratch/issues" },
"founder": { "@type": "Person", "name": "Rohit Ghumare", "url": "https://github.com/rohitg00" }
}
}
+10
View File
@@ -1607,6 +1607,10 @@ function writeSitemap(phases, glossaryCount, certifications) {
{ loc: '/', priority: '1.0', freq: 'weekly' },
{ loc: '/catalog.html', priority: '0.8', freq: 'weekly' },
{ loc: '/prereqs.html', priority: '0.7', freq: 'monthly' },
{ loc: '/about.html', priority: '0.5', freq: 'yearly' },
{ loc: '/developer.html', priority: '0.6', freq: 'monthly' },
{ loc: '/contact.html', priority: '0.3', freq: 'yearly' },
{ loc: '/privacy.html', priority: '0.3', freq: 'yearly' },
];
if (glossaryCount > 0) urls.push({ loc: '/glossary.html', priority: '0.6', freq: 'monthly' });
if (certifications && certifications.program) {
@@ -1647,6 +1651,12 @@ function writeLlms(phases, glossaryCount, artifactCount, certifications) {
out += `Canonical site: ${SITE_ORIGIN}\n`;
out += `Source: https://github.com/rohitg00/ai-engineering-from-scratch\n`;
out += `Glossary terms: ${glossaryCount} · Reusable outputs (prompts/skills/agents): ${artifactCount}\n\n`;
out += `## Developer resources\n`;
out += `- [Developer documentation](${SITE_ORIGIN}/developer.html) — machine-readable site contracts and integration notes\n`;
out += `- [OpenAPI description](${SITE_ORIGIN}/openapi.json) — read-only public resource inventory\n`;
out += `- [Sitemap](${SITE_ORIGIN}/sitemap.xml) — canonical URL inventory\n`;
out += `- [Contact](${SITE_ORIGIN}/contact.html) — maintainer and project contact route\n`;
out += `- [Privacy](${SITE_ORIGIN}/privacy.html) — data and analytics policy\n\n`;
out += `Lesson pages render client-side. Agents: fetch each lesson's raw markdown link; it is the full text. Lesson directories may also include code/ (runnable implementation) and quiz.json.\n\n`;
for (const phase of phases) {
out += `## Phase ${phase.id}: ${phase.name}\n`;
+31
View File
@@ -0,0 +1,31 @@
<!DOCTYPE html>
<html lang="en" data-theme="light">
<head>
<meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Contact · AI Engineering from Scratch</title>
<meta name="description" content="Contact the maintainer and contributors of AI Engineering from Scratch through the public project channels.">
<link rel="canonical" href="https://aiengineeringfromscratch.com/contact.html"><link rel="stylesheet" href="style.css?v=20260802a">
<style>
.trust-page { max-width: 780px; margin: 0 auto; padding: 100px 0 80px; }
.trust-page h1, .trust-page h2 { font-family: var(--font-display); color: var(--blueprint); text-transform: uppercase; }
.trust-page h1 { font-size: clamp(2.4rem, 6vw, 3.8rem); line-height: 1; }
.trust-page h2 { font-family: var(--font-mono); font-size: 1rem; letter-spacing: .12em; margin-top: 42px; }
.trust-page p, .trust-page li { font-family: var(--font-body); font-size: 1.05rem; line-height: 1.7; color: var(--ink); }
.trust-page a { color: var(--blueprint); }
</style>
</head>
<body>
<header class="site-header"><div class="header-inner"><a href="index.html" class="logo"><span class="logo-icon" aria-hidden="true"></span> AI / FROM SCRATCH</a><nav class="header-nav"><a href="index.html#contents">Contents</a><a href="catalog.html">Catalog</a><a href="prereqs.html">Roadmap</a><a href="glossary.html">Glossary</a><a href="about.html">About</a></nav></div></header>
<main class="container"><article class="trust-page">
<p class="about-eyebrow">AI Engineering from Scratch</p>
<h1>Contact</h1>
<p>AI Engineering from Scratch is maintained in public. The fastest way to reach the maintainer and contributors is through the project repository, where questions stay attached to the lesson or behavior they describe and can be answered in context.</p>
<h2>Project support</h2>
<ul><li><a href="https://github.com/rohitg00/ai-engineering-from-scratch/issues/new/choose">Report a bug or propose a lesson</a>.</li><li><a href="https://github.com/rohitg00/ai-engineering-from-scratch/discussions">Start a discussion</a> for learning questions, ideas, and community help.</li><li><a href="https://github.com/rohitg00">Contact the maintainer profile</a> for public project matters.</li></ul>
<h2>Responsible reports</h2>
<p>Do not publish secrets, private learner data, or credentials in an issue. For a security-sensitive report, open a private GitHub security advisory for the repository when the report involves a vulnerability. For ordinary content, accessibility, curriculum, or site issues, include the URL, expected behavior, actual behavior, and a minimal reproduction.</p>
</article></main>
<footer class="site-footer"><div class="container footer-inner"><p>© 2026 · open source · free forever</p><div class="footer-links"><a href="about.html">About</a><a href="developer.html">Developer resources</a><a href="privacy.html">Privacy</a></div></div></footer>
<script type="application/ld+json">{"@context":"https://schema.org","@type":"ContactPage","name":"Contact AI Engineering from Scratch","url":"https://aiengineeringfromscratch.com/contact.html","isPartOf":{"@type":"WebSite","name":"AI Engineering from Scratch","url":"https://aiengineeringfromscratch.com"}}</script>
</body>
</html>
+44
View File
@@ -0,0 +1,44 @@
<!DOCTYPE html>
<html lang="en" data-theme="light">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Developer resources · AI Engineering from Scratch</title>
<meta name="description" content="Machine-readable developer resources, public URLs, OpenAPI description, sitemap, and agent integration notes for AI Engineering from Scratch.">
<link rel="canonical" href="https://aiengineeringfromscratch.com/developer.html">
<link rel="stylesheet" href="style.css?v=20260802a">
<style>
.trust-page { max-width: 780px; margin: 0 auto; padding: 100px 0 80px; }
.trust-page h1, .trust-page h2 { font-family: var(--font-display); color: var(--blueprint); text-transform: uppercase; }
.trust-page h1 { font-size: clamp(2.4rem, 6vw, 3.8rem); line-height: 1; }
.trust-page h2 { font-family: var(--font-mono); font-size: 1rem; letter-spacing: .12em; margin-top: 42px; }
.trust-page p, .trust-page li { font-family: var(--font-body); font-size: 1.05rem; line-height: 1.7; color: var(--ink); }
.trust-page a { color: var(--blueprint); }
.trust-page code { font-family: var(--font-mono); background: var(--bg-surface); border: 1px solid var(--rule-soft); padding: 1px 5px; }
</style>
</head>
<body>
<header class="site-header"><div class="header-inner">
<a href="index.html" class="logo"><span class="logo-icon" aria-hidden="true"></span> AI / FROM SCRATCH</a>
<nav class="header-nav"><a href="index.html#contents">Contents</a><a href="catalog.html">Catalog</a><a href="prereqs.html">Roadmap</a><a href="glossary.html">Glossary</a><a href="about.html">About</a></nav>
</div></header>
<main class="container"><article class="trust-page">
<p class="about-eyebrow">AI Engineering from Scratch</p>
<h1>Developer resources</h1>
<p>This is the machine-readable entry point for AI Engineering from Scratch. The project is a static, open-source curriculum rather than a hosted application API. Agents and developer tools should start with <a href="llms.txt">llms.txt</a>, which maps the lessons to their canonical pages and raw Markdown sources.</p>
<h2>Stable resources</h2>
<ul>
<li><a href="llms.txt"><code>/llms.txt</code></a> is the agent-oriented curriculum map.</li>
<li><a href="sitemap.xml"><code>/sitemap.xml</code></a> lists canonical public URLs.</li>
<li><a href="openapi.json"><code>/openapi.json</code></a> describes the read-only resource surface.</li>
<li><a href="catalog.html"><code>/catalog.html</code></a> is the searchable human catalog.</li>
<li><a href="https://github.com/rohitg00/ai-engineering-from-scratch"><code>GitHub</code></a> is the source of truth for lessons, code, quizzes, and reusable artifacts.</li>
</ul>
<h2>Markdown negotiation</h2>
<p>Requests for the homepage or major navigation routes with <code>Accept: text/markdown</code> receive the agent map with <code>Content-Type: text/markdown</code>. Responses vary on <code>Accept</code> so a cache cannot mix the HTML and Markdown representations.</p>
<h2>MCP status</h2>
<p>The website does not currently expose a hosted MCP server or authenticated product API. The curriculum does teach how to build and operate MCP servers, and those lesson artifacts live in the repository. A remote Streamable HTTP MCP endpoint would require a separate product decision, authentication policy, and operational owner.</p>
</article></main>
<footer class="site-footer"><div class="container footer-inner"><p>© 2026 · open source · free forever</p><div class="footer-links"><a href="about.html">About</a><a href="contact.html">Contact</a><a href="privacy.html">Privacy</a><a href="llms.txt">Agents</a></div></div></footer>
</body>
</html>
+6
View File
@@ -7,6 +7,7 @@
<link rel="icon" href="data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 32 32'><rect width='32' height='32' fill='%23fafaf5'/><rect x='2' y='2' width='28' height='28' fill='none' stroke='%233553ff' stroke-width='1.2'/><text x='6' y='22' font-size='14' font-family='monospace' fill='%233553ff'>AI</text></svg>">
<meta name="description" content="AI Engineering from Scratch is a free, open-source curriculum that builds every core AI algorithm by hand. 511 lessons, 20 phases, four languages. The math, the model, the tokenizer, and the agent loop. Once, by hand.">
<link rel="canonical" href="https://aiengineeringfromscratch.com/">
<link rel="alternate" type="text/markdown" href="https://aiengineeringfromscratch.com/llms.txt">
<meta property="og:title" content="AI Engineering from Scratch">
<meta property="og:description" content="511 lessons. 20 phases. Write the backprop, the tokenizer, the attention mechanism, and the agent loop by hand before any framework gets imported. Python, TypeScript, Rust, Julia.">
<meta property="og:image" content="https://aiengineeringfromscratch.com/og-image.png?v=3">
@@ -31,6 +32,11 @@
"url": "https://aiengineeringfromscratch.com",
"logo": "https://aiengineeringfromscratch.com/og-image.png",
"sameAs": ["https://github.com/rohitg00/ai-engineering-from-scratch", "https://github.com/rohitg00"],
"contactPoint": {
"@type": "ContactPoint",
"contactType": "project support",
"url": "https://github.com/rohitg00/ai-engineering-from-scratch/issues"
},
"founder": { "@type": "Person", "name": "Rohit Ghumare", "url": "https://github.com/rohitg00" }
},
{
+33
View File
@@ -0,0 +1,33 @@
{
"openapi": "3.1.0",
"info": {
"title": "AI Engineering from Scratch public resources",
"version": "1.0.0",
"description": "Read-only, public machine-readable resources for the AI Engineering from Scratch curriculum. Lesson source remains canonical in the GitHub repository."
},
"servers": [{"url": "https://aiengineeringfromscratch.com"}],
"paths": {
"/llms.txt": {
"get": {
"summary": "Read the agent-oriented curriculum map",
"operationId": "getAgentIndex",
"responses": {"200": {"description": "Markdown lesson and resource index", "content": {"text/markdown": {"schema": {"type": "string"}}}}}
}
},
"/sitemap.xml": {
"get": {
"summary": "Read canonical public URLs",
"operationId": "getSitemap",
"responses": {"200": {"description": "XML sitemap", "content": {"application/xml": {"schema": {"type": "string"}}}}}
}
},
"/lesson.html": {
"get": {
"summary": "Open a rendered lesson",
"operationId": "getLesson",
"parameters": [{"name": "path", "in": "query", "required": true, "schema": {"type": "string"}, "description": "Repository-relative lesson path, for example phases/01-math-foundations/01-linear-algebra-intuition"}],
"responses": {"200": {"description": "Rendered lesson page"}, "404": {"description": "Unknown lesson path"}}
}
}
}
}
+33
View File
@@ -0,0 +1,33 @@
<!DOCTYPE html>
<html lang="en" data-theme="light">
<head>
<meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Privacy · AI Engineering from Scratch</title>
<meta name="description" content="Privacy information for the AI Engineering from Scratch curriculum website.">
<link rel="canonical" href="https://aiengineeringfromscratch.com/privacy.html"><link rel="stylesheet" href="style.css?v=20260802a">
<style>
.trust-page { max-width: 780px; margin: 0 auto; padding: 100px 0 80px; }
.trust-page h1, .trust-page h2 { font-family: var(--font-display); color: var(--blueprint); text-transform: uppercase; }
.trust-page h1 { font-size: clamp(2.4rem, 6vw, 3.8rem); line-height: 1; }
.trust-page h2 { font-family: var(--font-mono); font-size: 1rem; letter-spacing: .12em; margin-top: 42px; }
.trust-page p { font-family: var(--font-body); font-size: 1.05rem; line-height: 1.7; color: var(--ink); }
.trust-page a { color: var(--blueprint); }
</style>
</head>
<body>
<header class="site-header"><div class="header-inner"><a href="index.html" class="logo"><span class="logo-icon" aria-hidden="true"></span> AI / FROM SCRATCH</a><nav class="header-nav"><a href="index.html#contents">Contents</a><a href="catalog.html">Catalog</a><a href="prereqs.html">Roadmap</a><a href="glossary.html">Glossary</a><a href="about.html">About</a></nav></div></header>
<main class="container"><article class="trust-page">
<p class="about-eyebrow">AI Engineering from Scratch</p>
<h1>Privacy</h1>
<p>AI Engineering from Scratch is a public, open-source curriculum. The lesson content is available without an account, payment, or learner profile. The site does not ask for passwords, API keys, or private project files.</p>
<h2>What the site receives</h2>
<p>Hosting and analytics providers may receive ordinary request data such as an IP address, browser user agent, referrer, requested URL, and approximate timing. This information is used for delivery, security, aggregate traffic measurement, and diagnosing broken pages. The project does not sell learner data or use lesson activity to build advertising profiles.</p>
<h2>Local progress</h2>
<p>Optional reading progress, theme preferences, and quiz state are stored in the browser when the site offers those controls. They are not required to read the curriculum and are not submitted as an account record by the static site. Clearing site storage removes that local state.</p>
<h2>Third-party links</h2>
<p>Lessons link to specifications, papers, repositories, and other resources. Those destinations have their own policies. Review them before submitting information. Questions about this policy can be raised through the <a href="contact.html">contact page</a> or the public repository.</p>
</article></main>
<footer class="site-footer"><div class="container footer-inner"><p>© 2026 · open source · free forever</p><div class="footer-links"><a href="about.html">About</a><a href="contact.html">Contact</a><a href="developer.html">Developer resources</a></div></div></footer>
<script type="application/ld+json">{"@context":"https://schema.org","@type":"WebPage","name":"Privacy · AI Engineering from Scratch","url":"https://aiengineeringfromscratch.com/privacy.html","isPartOf":{"@type":"WebSite","name":"AI Engineering from Scratch","url":"https://aiengineeringfromscratch.com"}}</script>
</body>
</html>
+29 -4
View File
@@ -4,6 +4,16 @@
"framework": null,
"installCommand": "echo skip",
"rewrites": [
{ "source": "/", "has": [{ "type": "header", "key": "accept", "value": ".*" }], "destination": "/api/markdown?path=/" },
{ "source": "/about", "has": [{ "type": "header", "key": "accept", "value": ".*" }], "destination": "/api/markdown?path=/about" },
{ "source": "/catalog", "has": [{ "type": "header", "key": "accept", "value": ".*" }], "destination": "/api/markdown?path=/catalog" },
{ "source": "/glossary", "has": [{ "type": "header", "key": "accept", "value": ".*" }], "destination": "/api/markdown?path=/glossary" },
{ "source": "/path", "has": [{ "type": "header", "key": "accept", "value": ".*" }], "destination": "/api/markdown?path=/path" },
{ "source": "/roadmap", "has": [{ "type": "header", "key": "accept", "value": ".*" }], "destination": "/api/markdown?path=/roadmap" },
{ "source": "/developer", "has": [{ "type": "header", "key": "accept", "value": ".*" }], "destination": "/api/markdown?path=/developer" },
{ "source": "/docs", "has": [{ "type": "header", "key": "accept", "value": ".*" }], "destination": "/api/markdown?path=/docs" },
{ "source": "/contact", "has": [{ "type": "header", "key": "accept", "value": ".*" }], "destination": "/api/markdown?path=/contact" },
{ "source": "/privacy", "has": [{ "type": "header", "key": "accept", "value": ".*" }], "destination": "/api/markdown?path=/privacy" },
{ "source": "/glossary", "destination": "/glossary.html" },
{ "source": "/catalog", "destination": "/catalog.html" },
{ "source": "/certifications", "destination": "/certifications.html" },
@@ -11,7 +21,11 @@
{ "source": "/assessment", "destination": "/assessment.html" },
{ "source": "/path", "destination": "/prereqs.html" },
{ "source": "/roadmap", "destination": "/prereqs.html" },
{ "source": "/about", "destination": "/about.html" }
{ "source": "/about", "destination": "/about.html" },
{ "source": "/developer", "destination": "/developer.html" },
{ "source": "/docs", "destination": "/developer.html" },
{ "source": "/contact", "destination": "/contact.html" },
{ "source": "/privacy", "destination": "/privacy.html" }
],
"headers": [
{
@@ -35,18 +49,29 @@
{
"source": "/(.*)\\.html",
"headers": [
{ "key": "Cache-Control", "value": "public, max-age=300, s-maxage=86400, stale-while-revalidate=604800" }
{ "key": "Cache-Control", "value": "public, max-age=300, s-maxage=86400, stale-while-revalidate=604800" },
{ "key": "Vary", "value": "Accept, Accept-Encoding" }
]
},
{
"source": "/",
"headers": [
{ "key": "Cache-Control", "value": "public, max-age=300, s-maxage=86400, stale-while-revalidate=604800" }
{ "key": "Cache-Control", "value": "public, max-age=300, s-maxage=86400, stale-while-revalidate=604800" },
{ "key": "Vary", "value": "Accept, Accept-Encoding" }
]
},
{
"source": "/(glossary|catalog|certifications|certification|assessment|path|roadmap)",
"source": "/(glossary|catalog|certifications|certification|assessment|path|roadmap|about|developer|docs|contact|privacy)",
"headers": [
{ "key": "Cache-Control", "value": "public, max-age=300, s-maxage=86400, stale-while-revalidate=604800" },
{ "key": "Vary", "value": "Accept, Accept-Encoding" }
]
},
{
"source": "/llms.txt",
"headers": [
{ "key": "Content-Type", "value": "text/markdown; charset=utf-8" },
{ "key": "Vary", "value": "Accept, Accept-Encoding" },
{ "key": "Cache-Control", "value": "public, max-age=300, s-maxage=86400, stale-while-revalidate=604800" }
]
}