chore: import upstream snapshot with attribution
CI / Deep Native Runtime Cases (1/6) (push) Has been skipped
CI / Native Preflight (push) Failing after 1s
CI / Native Runtime Cases (1/2) (push) Failing after 0s
CI / Native Runtime Cases (2/2) (push) Failing after 1s
CI / Native Metadata Reports (push) Failing after 0s
CI / Native Direct Backend Artifacts (push) Failing after 0s
CI / Native Sanitizer Smoke (push) Failing after 1s
CI / Command Contract Snapshots (push) Failing after 1s
CI / Deep Conformance Suite (push) Has been skipped
CI / Graph Build Perf (push) Failing after 1s
CI / Deep Native Preflight (push) Has been skipped
CI / Deep Native Runtime Cases (2/6) (push) Has been skipped
CI / Deep Native Runtime Cases (3/6) (push) Has been skipped
CI / Conformance Suite (push) Failing after 1s
CI / Workspace Checks (push) Failing after 0s
CI / Deep Native Runtime Cases (5/6) (push) Has been skipped
CI / Deep Native Runtime Cases (6/6) (push) Has been skipped
CI / Deep Native Runtime Cases (4/6) (push) Has been skipped
CI / Deep Graph Build Perf (push) Has been skipped

This commit is contained in:
wehub-resource-sync
2026-07-13 12:29:30 +08:00
commit e7738de6d2
1723 changed files with 216139 additions and 0 deletions
+55
View File
@@ -0,0 +1,55 @@
import { readFile } from "node:fs/promises";
import { join } from "node:path";
import { docs, findDocBySlug } from "./docs";
import type { Article, Heading } from "./types";
const ARTICLES_ROOT = join(process.cwd(), "articles");
export async function readArticleBySlug(slug: string): Promise<string | null> {
const doc = findDocBySlug(slug);
if (!doc) return null;
const relative = doc.sourcePath.replace(/^\/articles\//, "");
const filePath = join(ARTICLES_ROOT, relative);
return readFile(filePath, "utf8");
}
export async function readArticleByPath(routePath: string): Promise<Article | null> {
const doc = docs.find((d) => d.path === routePath);
if (!doc) return null;
const source = await readArticleBySlug(doc.slug);
if (source === null) return null;
return { doc, source };
}
export function extractHeadings(markdown: string): Heading[] {
const lines = markdown.replace(/\r\n/g, "\n").split("\n");
const headings: Heading[] = [];
let inCodeBlock = false;
for (const line of lines) {
if (line.trim().startsWith("```")) {
inCodeBlock = !inCodeBlock;
continue;
}
if (inCodeBlock) continue;
const match = /^(#{2,4})\s+(.+)$/.exec(line.trim());
if (match) {
const text = match[2].replace(/`([^`]+)`/g, "$1");
headings.push({
level: match[1].length as Heading["level"],
text,
id: slugify(match[2]),
});
}
}
return headings;
}
function slugify(value: string): string {
return value
.toLowerCase()
.replace(/`([^`]+)`/g, "$1")
.replace(/[^a-z0-9]+/g, "-")
.replace(/^-+|-+$/g, "");
}
+506
View File
@@ -0,0 +1,506 @@
import type { Doc, DocsGroup } from "./types";
const SECTION_ORDER = [
"Start Here",
"Concepts",
"Reference",
"Agent Workflow",
"Language Pieces",
"Standard Library",
"Core",
"Text And Data",
"Programs",
"Runtime And Web",
"Build And Runtime",
];
export const docs: Doc[] = [
{
slug: "getting-started",
title: "Getting Started",
description: "Install Zerolang and ask an agent to create a graph-first program you can review.",
path: "/getting-started",
sourcePath: "/articles/getting-started.md",
section: "Start Here",
},
{
slug: "install",
title: "Install",
description: "Install the latest compiler release and validate your environment.",
path: "/install",
sourcePath: "/articles/install.md",
section: "Start Here",
},
{
slug: "learn-zero",
title: "Learn Zerolang",
description: "A human tour of Zerolang's graph-first workflow, projections, and agent conversations.",
path: "/learn",
sourcePath: "/articles/learn-zero.md",
section: "Start Here",
},
{
slug: "examples",
title: "Examples",
description: "Runnable graph examples in learning order with copyable commands.",
path: "/examples",
sourcePath: "/articles/examples.md",
section: "Start Here",
},
{
slug: "graph-architecture",
title: "Graph Architecture",
description: "How Zerolang makes the semantic graph the program instead of treating text as the primary interface.",
path: "/concepts/graph-architecture",
sourcePath: "/articles/concepts/graph-architecture.md",
section: "Concepts",
},
{
slug: "semantic-vs-text",
title: "Semantic Graph Vs Text",
description: "How semantic graph edits differ from source-text edits, and why that matters for agents.",
path: "/concepts/semantic-vs-text",
sourcePath: "/articles/concepts/semantic-vs-text.md",
section: "Concepts",
},
{
slug: "compile-path",
title: "Compile Path",
description: "How Zerolang's graph-native compile path compares with traditional parse-first compilers.",
path: "/concepts/compile-path",
sourcePath: "/articles/concepts/compile-path.md",
section: "Concepts",
},
{
slug: "projections",
title: "Projections And Round Trips",
description: "How .0 projections support human review, manual edits, import/export, and no silent divergence.",
path: "/concepts/projections",
sourcePath: "/articles/concepts/projections.md",
section: "Concepts",
},
{
slug: "cli-reference",
title: "CLI Reference",
description: "Commands for creating, inspecting, patching, validating, running, and building Zero programs.",
path: "/cli",
sourcePath: "/articles/cli-reference.md",
section: "Reference",
},
{
slug: "diagnostics",
title: "Diagnostics And Repair",
description: "How humans and agents read errors, structured diagnostics, and repair plans.",
path: "/diagnostics",
sourcePath: "/articles/diagnostics.md",
section: "Agent Workflow",
},
{
slug: "testing",
title: "Testing And Reliability",
description: "Graph-backed zero test workflows, package tests, snapshots, fuzzing, and hardening gates.",
path: "/testing",
sourcePath: "/articles/testing.md",
section: "Agent Workflow",
},
{
slug: "primitives",
title: "Primitives And Types",
description: "The graph-visible language pieces behind both graph facts and projection syntax.",
path: "/primitives",
sourcePath: "/articles/primitives.md",
section: "Language Pieces",
},
{
slug: "language-reference",
title: "Language Model Reference",
description: "Declarations, functions, control flow, capabilities, ownership, packages, and projections.",
path: "/reference",
sourcePath: "/articles/language-reference.md",
section: "Language Pieces",
},
{
slug: "package-manifest",
title: "Package And Manifest Reference",
description: "zero.toml package manifests, imports, targets, dependencies, and profiles.",
path: "/package-manifest",
sourcePath: "/articles/package-manifest.md",
section: "Language Pieces",
},
{
slug: "standard-library",
title: "Standard Library",
description: "Graph-backed modules, capabilities, allocation behavior, and helper metadata.",
path: "/standard-library",
sourcePath: "/articles/standard-library.md",
section: "Standard Library",
},
{
slug: "target-capabilities",
title: "Target Capabilities",
description: "Current host and target-neutral capability boundaries.",
path: "/target-capabilities",
sourcePath: "/articles/target-capabilities.md",
section: "Build And Runtime",
},
{
slug: "cross-compilation",
title: "Cross-Compilation",
description: "Targets, capability denial, direct artifacts, and target facts.",
path: "/cross-compilation",
sourcePath: "/articles/cross-compilation.md",
section: "Build And Runtime",
},
{
slug: "optimization",
title: "Optimization And Size Profiles",
description: "Profile contracts, size breakdowns, retention reasons, optimization hints, and benchmark trends.",
path: "/optimization",
sourcePath: "/articles/optimization.md",
section: "Build And Runtime",
},
{
slug: "benchmarks",
title: "Benchmarks",
description: "Benchmark methodology, cases, and metrics.",
path: "/benchmarks",
sourcePath: "/articles/benchmarks.md",
section: "Build And Runtime",
},
{
slug: "c-interop",
title: "C Interop",
description: "Graph-backed C ABI export support and target library audit facts.",
path: "/c-interop",
sourcePath: "/articles/c-interop.md",
section: "Build And Runtime",
},
{
slug: "building-from-source",
title: "Building From Source",
description: "Build, use, and validate the local compiler checkout.",
path: "/native-compiler",
sourcePath: "/articles/building-from-source.md",
section: "Build And Runtime",
},
{
slug: "module-ascii",
title: "std.ascii",
description: "ASCII byte predicates, case conversion, and digit value helpers.",
path: "/modules/ascii",
sourcePath: "/articles/modules/ascii.md",
section: "Text And Data",
},
{
slug: "module-parse",
title: "std.parse",
description: "Allocation-free byte scanners and integer/bool parsers.",
path: "/modules/parse",
sourcePath: "/articles/modules/parse.md",
section: "Text And Data",
},
{
slug: "module-regex",
title: "std.regex",
description: "Compile-once regular expression matching for a documented ECMA-262-leaning subset.",
path: "/modules/regex",
sourcePath: "/articles/modules/regex.md",
section: "Text And Data",
},
{
slug: "module-inet",
title: "std.inet",
description: "Target-neutral IPv4, IPv6, and RFC 1123 hostname literal validation and parsing.",
path: "/modules/inet",
sourcePath: "/articles/modules/inet.md",
section: "Text And Data",
},
{
slug: "module-codec",
title: "std.codec",
description: "Little-endian integer helpers, unsigned varints, and CRC-32 primitives.",
path: "/modules/codec",
sourcePath: "/articles/modules/codec.md",
section: "Text And Data",
},
{
slug: "module-csv",
title: "std.csv",
description: "Allocation-free CSV validation, record scanning, field decoding, and small writers.",
path: "/modules/csv",
sourcePath: "/articles/modules/csv.md",
section: "Text And Data",
},
{
slug: "module-mem",
title: "std.mem",
description: "Span metadata, copy and equality helpers, and the allocator surface.",
path: "/modules/mem",
sourcePath: "/articles/modules/mem.md",
section: "Core",
},
{
slug: "module-collections",
title: "std.collections",
description: "Fixed-capacity collection operations over caller-owned storage.",
path: "/modules/collections",
sourcePath: "/articles/modules/collections.md",
section: "Core",
},
{
slug: "module-search",
title: "std.search",
description: "Scalar span search and binary-search helpers.",
path: "/modules/search",
sourcePath: "/articles/modules/search.md",
section: "Core",
},
{
slug: "module-sort",
title: "std.sort",
description: "In-place sort and sortedness helpers over caller-owned storage.",
path: "/modules/sort",
sourcePath: "/articles/modules/sort.md",
section: "Core",
},
{
slug: "module-args",
title: "std.args",
description: "Process argument count and indexed lookup for hosted command-line programs.",
path: "/modules/args",
sourcePath: "/articles/modules/args.md",
section: "Programs",
},
{
slug: "module-cli",
title: "std.cli",
description: "Hosted flag and option helpers for command-line programs.",
path: "/modules/cli",
sourcePath: "/articles/modules/cli.md",
section: "Programs",
},
{
slug: "module-diag",
title: "std.diag",
description: "Source offsets, line spans, and diagnostic location formatting.",
path: "/modules/diag",
sourcePath: "/articles/modules/diag.md",
section: "Text And Data",
},
{
slug: "module-fmt",
title: "std.fmt",
description: "Caller-buffer formatting for booleans and integer text.",
path: "/modules/fmt",
sourcePath: "/articles/modules/fmt.md",
section: "Text And Data",
},
{
slug: "module-math",
title: "std.math",
description: "Pure fixed-width integer helpers and small number-theory routines.",
path: "/modules/math",
sourcePath: "/articles/modules/math.md",
section: "Core",
},
{
slug: "module-path",
title: "std.path",
description: "Fixed-buffer path helpers with explicit storage and target-aware limits.",
path: "/modules/path",
sourcePath: "/articles/modules/path.md",
section: "Programs",
},
{
slug: "module-str",
title: "std.str",
description: "Allocation-free byte-string helpers over spans and caller-owned storage.",
path: "/modules/str",
sourcePath: "/articles/modules/str.md",
section: "Text And Data",
},
{
slug: "module-testing",
title: "std.testing",
description: "Bool-returning helpers for test blocks and output checks.",
path: "/modules/testing",
sourcePath: "/articles/modules/testing.md",
section: "Programs",
},
{
slug: "module-term",
title: "std.term",
description: "ANSI terminal sequences, key-byte decoding, hosted terminal metadata, and raw mode.",
path: "/modules/term",
sourcePath: "/articles/modules/term.md",
section: "Programs",
},
{
slug: "module-text",
title: "std.text",
description: "ASCII and UTF-8 byte-backed text validation.",
path: "/modules/text",
sourcePath: "/articles/modules/text.md",
section: "Text And Data",
},
{
slug: "module-unicode",
title: "std.unicode",
description: "Strict UTF-8 codepoint decode/encode iteration and codepoint-class helpers.",
path: "/modules/unicode",
sourcePath: "/articles/modules/unicode.md",
section: "Text And Data",
},
{
slug: "module-io",
title: "std.io",
description: "Buffered reader/writer helpers over caller-owned storage.",
path: "/modules/io",
sourcePath: "/articles/modules/io.md",
section: "Programs",
},
{
slug: "module-fs",
title: "std.fs",
description: "Hosted file reads, writes, and existence checks for CLI programs.",
path: "/modules/fs",
sourcePath: "/articles/modules/fs.md",
section: "Programs",
},
{
slug: "module-json",
title: "std.json",
description: "Validation, field lookup, explicit-allocator parsing, and caller-buffer writing.",
path: "/modules/json",
sourcePath: "/articles/modules/json.md",
section: "Text And Data",
},
{
slug: "module-toml",
title: "std.toml",
description: "TOML validation, shallow field lookup, and typed scalar decode helpers.",
path: "/modules/toml",
sourcePath: "/articles/modules/toml.md",
section: "Text And Data",
},
{
slug: "module-log",
title: "std.log",
description: "Explicit-buffer structured log record formatting.",
path: "/modules/log",
sourcePath: "/articles/modules/log.md",
section: "Text And Data",
},
{
slug: "module-url",
title: "std.url",
description: "Lexical URL splitting, percent/query encoding, and query helpers.",
path: "/modules/url",
sourcePath: "/articles/modules/url.md",
section: "Text And Data",
},
{
slug: "module-env",
title: "std.env",
description: "Hosted environment variable lookup.",
path: "/modules/env",
sourcePath: "/articles/modules/env.md",
section: "Programs",
},
{
slug: "module-time",
title: "std.time",
description: "Duration math, hosted sleep, and target-gated monotonic and wall-clock helpers.",
path: "/modules/time",
sourcePath: "/articles/modules/time.md",
section: "Runtime And Web",
},
{
slug: "module-rand",
title: "std.rand",
description: "Explicit deterministic random sources and target entropy helpers.",
path: "/modules/rand",
sourcePath: "/articles/modules/rand.md",
section: "Runtime And Web",
},
{
slug: "module-proc",
title: "std.proc",
description: "Host process status helpers behind explicit process capability boundaries.",
path: "/modules/proc",
sourcePath: "/articles/modules/proc.md",
section: "Runtime And Web",
},
{
slug: "module-pty",
title: "std.pty",
description: "Hosted pseudoterminal child processes for interactive CLIs and terminal programs.",
path: "/modules/pty",
sourcePath: "/articles/modules/pty.md",
section: "Runtime And Web",
},
{
slug: "module-crypto",
title: "std.crypto",
description: "Hash, keyed hash, constant-time equality, and target entropy helpers.",
path: "/modules/crypto",
sourcePath: "/articles/modules/crypto.md",
section: "Runtime And Web",
},
{
slug: "module-net",
title: "std.net",
description: "Network capability metadata, address builders, timeouts, and bootstrap handles.",
path: "/modules/net",
sourcePath: "/articles/modules/net.md",
section: "Runtime And Web",
},
{
slug: "module-http",
title: "std.http",
description: "HTTP envelope writing, request parsing, hosted fetch, and response metadata helpers.",
path: "/modules/http",
sourcePath: "/articles/modules/http.md",
section: "Runtime And Web",
},
];
export function findDocBySlug(slug: string): Doc | null {
return docs.find((doc) => doc.slug === slug) ?? null;
}
export function findDocByPath(path: string): Doc | null {
return docs.find((doc) => doc.path === path) ?? null;
}
export function groupBySection(items: Doc[]): DocsGroup[] {
const groups: DocsGroup[] = [];
const seen = new Set<string>();
for (const item of items) {
const section = item.section ?? "Other";
if (!seen.has(section)) {
seen.add(section);
groups.push({ section, items: [] });
}
const group = groups.find((g) => g.section === section);
if (group) group.items.push(item);
}
return groups.sort((a, b) => {
const ai = SECTION_ORDER.indexOf(a.section);
const bi = SECTION_ORDER.indexOf(b.section);
if (ai === -1 && bi === -1) return 0;
if (ai === -1) return 1;
if (bi === -1) return -1;
return ai - bi;
});
}
export function getAdjacentDocs(slug: string): { prev: Doc | null; next: Doc | null } {
const index = docs.findIndex((d) => d.slug === slug);
if (index === -1) return { prev: null, next: null };
return {
prev: index > 0 ? docs[index - 1] : null,
next: index < docs.length - 1 ? docs[index + 1] : null,
};
}
+19
View File
@@ -0,0 +1,19 @@
const REPO = process.env.NEXT_PUBLIC_GITHUB_REPO || "vercel-labs/zerolang";
const REVALIDATE = 86400;
export async function getStarCount(): Promise<string> {
try {
const res = await fetch(`https://api.github.com/repos/${REPO}`, {
headers: { Accept: "application/vnd.github.v3+json" },
next: { revalidate: REVALIDATE },
});
if (!res.ok) return "";
const data = (await res.json()) as { stargazers_count?: unknown };
const count = data.stargazers_count;
if (typeof count !== "number") return "";
if (count >= 1000) return `${(count / 1000).toFixed(count >= 10000 ? 0 : 1)}k`;
return String(count);
} catch {
return "";
}
}
+135
View File
@@ -0,0 +1,135 @@
type TokenName =
| "comment"
| "string"
| "char"
| "keyword"
| "type"
| "function"
| "number"
| "variable"
| "id"
| "key"
| "boolean"
| "punctuation"
| "operator";
type Grammar = ReadonlyArray<readonly [TokenName, RegExp]>;
function escapeHtml(value: string): string {
return String(value)
.replaceAll("&", "&amp;")
.replaceAll("<", "&lt;")
.replaceAll(">", "&gt;")
.replaceAll('"', "&quot;")
.replaceAll("'", "&#039;");
}
const GRAMMARS: Record<string, Grammar> = {
bash: [
["string", /'(?:[^'\\]|\\.)*'|"(?:[^"\\]|\\.)*"/],
["comment", /#.*/],
["variable", /\$\{?[A-Za-z_][A-Za-z0-9_]*\}?/],
["key", /--?[A-Za-z0-9][A-Za-z0-9-]*(?:=[^\s\\]+)?/],
["keyword", /\b(?:if|then|else|elif|fi|for|while|do|done|case|esac|function|export|local|readonly|return|exit|set|unset)\b/],
["function", /(?<![A-Za-z0-9_./-])(?:zero|pnpm|npm|git|curl|make|node|npx|bash|sh|zsh|cd|mkdir|rm|cp|mv|cat|sed|rg)(?=\s|$)/],
["number", /\b\d+\b/],
["operator", /&&|\|\||\\|[|<>;&=]/],
["punctuation", /[{}()[\]]/],
],
zero: [
["comment", /\/\/.*/],
["string", /"(?:[^"\\]|\\.)*"/],
["char", /'(?:[^'\\\n]|\\(?:[nrt0'"\\]|x[0-9A-Fa-f]{2}))'/],
["keyword", /\b(?:pub|fn|let|var|return|raises|if|else|while|for|in|match|check|rescue|raise|use|interface|enum|choice|type|alias|const|as|break|continue|defer|export|extern|packed|static|meta|mut|test|and|or|not|true|false|null)\b/],
["type", /\b(?:Void|World|Self|Bool|bool|char|u4|u8|u16|u32|u64|i8|i16|i32|i64|usize|isize|f16|f32|f64|Span|MutSpan|Maybe|Alloc|Arena|FixedBufAlloc|GeneralAlloc|NullAlloc|PageAlloc|ref|mutref|owned|Slice|Array|String|Error|Io|Fs|Net|Env|Args|Clock|Rand|Proc|Sync|Cancel|Reader|Writer|File|Path|Conn|Listener|Address|Config|Type|Field|c_int|c_long|c_size|cstr|[A-Z][A-Za-z0-9_]*)\b/],
["function", /\b[a-z_]\w*(?=\s*\()/],
["number", /\b(?:\d+\.\d+(?:[eE][+-]?\d+)?|0x[0-9a-fA-F_]+|0b[01_]+|0o[0-7_]+|\d[\d_]*(?:_[A-Za-z][A-Za-z0-9_]*)?)\b/],
["variable", /\b[a-z_]\w*(?=\.)/],
["punctuation", /[{}()\[\];,.<>]/],
["operator", /:|[-+*/%=!&|^~@?]+/],
],
"zero-graph": [
["comment", /\/\/.*/],
["string", /"(?:[^"\\]|\\.)*"/],
["id", /#[A-Za-z_][A-Za-z0-9_]*/],
["keyword", /\b(?:zero-graph|zero-program-graph-patch|origin|module|hash|node|edge|expect|graphHash|set|insert|insertEdge|replace|delete|rename|addFunction|addMain|addParam|addReturnBinary|addLetLiteral|addLetBinary|addReturnValue|addCheckWrite|addCheckWriteValue|addTest|replaceFunctionBody|replaceBlockBody|end|let|var|return|if|else|while|for|in|match|check|rescue|raise|use|test|and|or|not)\b/],
["type", /\b(?:Function|Param|Block|Identifier|Literal|MethodCall|Call|FieldAccess|Let|Var|Return|Check|If|While|For|Match|TypeRef|ArrayLiteral|Unary|Binary|Void|World|Self|Bool|bool|char|u4|u8|u16|u32|u64|i8|i16|i32|i64|usize|isize|f16|f32|f64|Span|MutSpan|Maybe|Alloc|Arena|FixedBufAlloc|GeneralAlloc|NullAlloc|PageAlloc|ref|mutref|owned|Slice|Array|String|Error|[A-Z][A-Za-z0-9_]*)\b/],
["boolean", /\b(?:true|false|null)\b/],
["key", /\b[A-Za-z_][A-Za-z0-9_-]*(?=\s*(?::|=))/],
["function", /\b(?:std|[a-z_][A-Za-z0-9_]*)\.[A-Za-z_][A-Za-z0-9_.]*(?=\s|$)/],
["number", /\b(?:v\d+|graph:[0-9a-fA-F]+|nodehash:[0-9a-fA-F]+|0x[0-9a-fA-F_]+|0b[01_]+|0o[0-7_]+|\d[\d_]*)\b/],
["variable", /\b[a-z_][A-Za-z0-9_]*(?=\.)/],
["punctuation", /[{}()\[\];,.<>]/],
["operator", /:|=|[-+*/%!&|^~@?]+/],
],
};
const LANGUAGE_ALIASES: Record<string, string> = {
sh: "bash",
shell: "bash",
zsh: "bash",
graph: "zero-graph",
"program-graph": "zero-graph",
"zero-program-graph": "zero-graph",
"graph-patch": "zero-graph",
"zero-graph-patch": "zero-graph",
"program-graph-patch": "zero-graph",
"zero-program-graph-patch": "zero-graph",
};
function normalizeLanguage(language: string): string {
const key = language.trim().toLowerCase();
return LANGUAGE_ALIASES[key] ?? key;
}
function looksLikeGraph(code: string): boolean {
const text = code.trimStart();
return (
/^zero-graph\s+v\d+\b/.test(text) ||
/^zero-program-graph-patch\s+v\d+\b/.test(text) ||
/^(?:node|edge)\s+#[A-Za-z_][A-Za-z0-9_]*\b/m.test(text) ||
/^(?:expect\s+graphHash|set\s+node=|insert\s+node=|insertEdge\s+|replace\s+node=|rename\s+node=|delete\s+node=|replaceFunctionBody\s+|replaceBlockBody\s+)/m.test(text)
);
}
export function highlightLanguage(language: string, code: string): string | null {
const normalized = normalizeLanguage(language);
if (GRAMMARS[normalized]) return normalized;
if ((normalized === "" || normalized === "text" || normalized === "txt") && looksLikeGraph(code)) {
return "zero-graph";
}
return null;
}
export function highlight(code: string, language: string): string {
const normalized = highlightLanguage(language, code);
const grammar = normalized ? GRAMMARS[normalized] : null;
if (!grammar) return escapeHtml(code);
const combined = new RegExp(
grammar.map(([, pattern]) => `(${pattern.source})`).join("|"),
"gm",
);
const names = grammar.map(([name]) => name);
let result = "";
let lastIndex = 0;
for (const match of code.matchAll(combined)) {
const matchIndex = match.index ?? 0;
if (matchIndex > lastIndex) {
result += escapeHtml(code.slice(lastIndex, matchIndex));
}
const groupIndex = match.slice(1).findIndex((g) => g !== undefined);
if (groupIndex === -1) continue;
const tokenType = names[groupIndex];
const tokenText = match[0];
result += `<span class="hl-${tokenType} hl-${normalized}-${tokenType}">${escapeHtml(tokenText)}</span>`;
lastIndex = matchIndex + tokenText.length;
}
if (lastIndex < code.length) {
result += escapeHtml(code.slice(lastIndex));
}
return result;
}
+45
View File
@@ -0,0 +1,45 @@
import type { Metadata } from "next";
import { PAGE_TITLES } from "./page-titles";
import { findDocByPath } from "./docs";
const SITE_NAME = "Zero";
const DESCRIPTION =
"zerolang is an experimental graph-first programming language where agents work with semantic program structure instead of raw source text.";
export function pageMetadata(slug: string): Metadata {
const title = PAGE_TITLES[slug];
if (title === undefined) return {};
const displayTitle = title.replace(/\n/g, " ");
const fullTitle = slug === "" ? `${SITE_NAME} | ${displayTitle}` : `${displayTitle} | ${SITE_NAME}`;
const ogImageUrl = slug ? `/og/${slug}` : "/og";
const doc = slug ? findDocByPath(`/${slug}`) : null;
const description = doc?.description ?? DESCRIPTION;
return {
title: slug === "" ? fullTitle : displayTitle,
description,
openGraph: {
type: "website",
locale: "en_US",
siteName: SITE_NAME,
title: fullTitle,
description,
images: [
{
url: ogImageUrl,
width: 1200,
height: 630,
alt: `${displayTitle}${SITE_NAME}`,
},
],
},
twitter: {
card: "summary_large_image",
title: fullTitle,
description,
images: [ogImageUrl],
},
};
}
+14
View File
@@ -0,0 +1,14 @@
import { docs } from "./docs";
const DOCS_TITLES: Record<string, string> = Object.fromEntries(
docs.map((doc) => [doc.path.replace(/^\//, ""), doc.title]),
);
export const PAGE_TITLES: Record<string, string> = {
"": "An agent-first language experiment.",
...DOCS_TITLES,
};
export function getPageTitle(slug: string): string | null {
return slug in PAGE_TITLES ? PAGE_TITLES[slug] : null;
}
+56
View File
@@ -0,0 +1,56 @@
import { Ratelimit } from "@upstash/ratelimit";
import { Redis } from "@upstash/redis";
let minuteRateLimit: Ratelimit | null = null;
let dailyRateLimit: Ratelimit | null = null;
export function isDocsChatRateLimitConfigured() {
return Boolean(process.env.KV_REST_API_URL && process.env.KV_REST_API_TOKEN);
}
function getRedis() {
const url = process.env.KV_REST_API_URL;
const token = process.env.KV_REST_API_TOKEN;
if (!url || !token) {
throw new Error("Docs chat rate limiting requires KV_REST_API_URL and KV_REST_API_TOKEN");
}
return new Redis({ url, token });
}
function readPositiveInt(name: string, fallback: number): number {
const value = Number(process.env[name]);
return Number.isInteger(value) && value > 0 ? value : fallback;
}
const MINUTE_LIMIT = readPositiveInt("RATE_LIMIT_PER_MINUTE", 10);
const DAILY_LIMIT = readPositiveInt("RATE_LIMIT_PER_DAY", 100);
export const docsChatMinuteRateLimit = {
limit: async (identifier: string) => {
if (!minuteRateLimit) {
const redis = getRedis();
minuteRateLimit = new Ratelimit({
redis,
limiter: Ratelimit.slidingWindow(MINUTE_LIMIT, "1 m"),
prefix: "ratelimit:docs-chat:minute",
});
}
return minuteRateLimit.limit(identifier);
},
};
export const docsChatDailyRateLimit = {
limit: async (identifier: string) => {
if (!dailyRateLimit) {
const redis = getRedis();
dailyRateLimit = new Ratelimit({
redis,
limiter: Ratelimit.fixedWindow(DAILY_LIMIT, "1 d"),
prefix: "ratelimit:docs-chat:daily",
});
}
return dailyRateLimit.limit(identifier);
},
};
+62
View File
@@ -0,0 +1,62 @@
// Rewrites ```json-render fenced code blocks into custom elements carrying
// base64-encoded JSON, so the docs can embed structured React components
// without switching the pipeline to MDX. Runs before rehype-pretty-code so the
// block is never treated as a normal code fence.
type HastNode = {
type?: string;
tagName?: string;
value?: string;
properties?: Record<string, unknown>;
children?: HastNode[];
};
function getNodeText(node?: HastNode): string {
if (!node) return "";
if (node.type === "text") return node.value ?? "";
if (!Array.isArray(node.children)) return "";
return node.children.map(getNodeText).join("");
}
function getCodeLanguage(node: HastNode): string {
const className = node.properties?.className;
const classes = Array.isArray(className)
? className
: typeof className === "string"
? className.split(/\s+/)
: [];
const languageClass = classes.find(
(value) => typeof value === "string" && value.startsWith("language-"),
);
return typeof languageClass === "string" ? languageClass.slice("language-".length) : "";
}
function visit(node: HastNode | undefined, callback: (node: HastNode) => void): void {
if (!node) return;
callback(node);
if (Array.isArray(node.children)) {
for (const child of node.children) visit(child, callback);
}
}
export function rehypeJsonRender(): (tree: HastNode) => void {
return (tree: HastNode) => {
visit(tree, (node) => {
if (node.type !== "element" || node.tagName !== "pre") return;
const code = node.children?.find(
(child) => child.type === "element" && child.tagName === "code",
);
if (!code || getCodeLanguage(code) !== "json-render") return;
const raw = getNodeText(code).replace(/\n$/, "");
let tagName = "agentchat";
try {
const parsed = JSON.parse(raw) as { type?: string };
if (parsed.type === "flow") tagName = "flowchart";
} catch {}
node.tagName = tagName;
node.properties = { value: Buffer.from(raw, "utf8").toString("base64") };
node.children = [];
});
};
}
+116
View File
@@ -0,0 +1,116 @@
import { highlight, highlightLanguage } from "./highlight";
type HastNode = {
type?: string;
tagName?: string;
value?: string;
properties?: Record<string, unknown>;
children?: HastNode[];
};
type NodePredicate = (node: HastNode) => boolean;
type NodeCallback = (node: HastNode) => void;
const ZERO_TOKEN_COLORS: Record<string, { light: string; dark: string }> = {
comment: { light: "#6A737D", dark: "#6A737D" },
string: { light: "#032F62", dark: "#9ECBFF" },
char: { light: "#032F62", dark: "#9ECBFF" },
keyword: { light: "#D73A49", dark: "#F97583" },
type: { light: "#005CC5", dark: "#79B8FF" },
function: { light: "#6F42C1", dark: "#B392F0" },
number: { light: "#005CC5", dark: "#79B8FF" },
variable: { light: "#24292E", dark: "#E1E4E8" },
id: { light: "#6F42C1", dark: "#B392F0" },
key: { light: "#D73A49", dark: "#F97583" },
boolean: { light: "#005CC5", dark: "#79B8FF" },
punctuation: { light: "#24292E", dark: "#E1E4E8" },
operator: { light: "#D73A49", dark: "#F97583" },
};
function visit(node: HastNode | undefined, predicate: NodePredicate, callback: NodeCallback): void {
if (!node) return;
if (predicate(node)) callback(node);
if (Array.isArray(node.children)) {
for (const child of node.children) visit(child, predicate, callback);
}
}
function highlightToHast(code: string, language: string): HastNode[] {
const parts = highlight(code, language).split("\n");
const lines = [];
for (let li = 0; li < parts.length; li++) {
const lineHtml = parts[li];
const tokens = [];
const tokenRegex = /<span class="hl-([A-Za-z0-9_-]+)(?:\s+[^"]*)?">([\s\S]*?)<\/span>|([^<]+)/g;
let match: RegExpExecArray | null;
while ((match = tokenRegex.exec(lineHtml)) !== null) {
if (match[1]) {
const color = ZERO_TOKEN_COLORS[match[1]];
const text = decodeEntities(match[2]);
tokens.push({
type: "element",
tagName: "span",
properties: color
? { style: `--shiki-light:${color.light};--shiki-dark:${color.dark}` }
: {},
children: [{ type: "text", value: text }],
});
} else if (match[3]) {
tokens.push({ type: "text", value: decodeEntities(match[3]) });
}
}
lines.push({
type: "element",
tagName: "span",
properties: { "data-line": "" },
children: tokens.length > 0 ? tokens : [{ type: "text", value: "" }],
});
}
return lines;
}
function decodeEntities(value: string): string {
return value
.replaceAll("&amp;", "&")
.replaceAll("&lt;", "<")
.replaceAll("&gt;", ">")
.replaceAll("&quot;", '"')
.replaceAll("&#039;", "'");
}
function getNodeText(node: HastNode | undefined): string {
if (!node) return "";
if (node.type === "text") return node.value ?? "";
if (!Array.isArray(node.children)) return "";
return node.children.map(getNodeText).join("");
}
function getCodeLanguage(node: HastNode): string {
const dataLanguage = node.properties?.dataLanguage ?? node.properties?.["data-language"];
if (typeof dataLanguage === "string") return dataLanguage;
const className = node.properties?.className;
const classes = Array.isArray(className) ? className : typeof className === "string" ? className.split(/\s+/) : [];
const languageClass = classes.find((value) => typeof value === "string" && value.startsWith("language-"));
return typeof languageClass === "string" ? languageClass.slice("language-".length) : "";
}
export function rehypeZeroHighlight(): (tree: HastNode) => void {
return (tree: HastNode) => {
visit(
tree,
(node) =>
node.type === "element" &&
node.tagName === "code",
(codeNode) => {
const text = getNodeText(codeNode).replace(/\n$/, "");
const language = highlightLanguage(getCodeLanguage(codeNode), text);
if (!language || language === "bash") return;
codeNode.children = highlightToHast(text, language);
codeNode.properties = {
...codeNode.properties,
style: "display:grid",
};
},
);
};
}
+45
View File
@@ -0,0 +1,45 @@
import { readArticleBySlug } from "./articles";
import { docs } from "./docs";
import type { SearchIndexEntry } from "./types";
let cached: SearchIndexEntry[] | null = null;
function stripMarkdown(md: string): string {
return md
.replace(/```[\s\S]*?```/g, "")
.replace(/`[^`]+`/g, "")
.replace(/\[([^\]]+)\]\([^)]+\)/g, "$1")
.replace(/^#{1,6}\s+/gm, "")
.replace(/\*{1,3}([^*]+)\*{1,3}/g, "$1")
.replace(/<[^>]+>/g, "")
.replace(/\n{3,}/g, "\n\n")
.trim();
}
export async function getSearchIndex(): Promise<SearchIndexEntry[]> {
if (cached) return cached;
const entries: SearchIndexEntry[] = [];
for (const doc of docs) {
try {
const raw = await readArticleBySlug(doc.slug);
const content = raw ? stripMarkdown(raw) : "";
entries.push({
title: doc.title,
href: doc.path,
section: doc.section ?? "",
content,
});
} catch {
entries.push({
title: doc.title,
href: doc.path,
section: doc.section ?? "",
content: "",
});
}
}
cached = entries;
return entries;
}
+38
View File
@@ -0,0 +1,38 @@
export type Doc = {
slug: string;
title: string;
description: string;
path: `/${string}`;
sourcePath: `/articles/${string}.md`;
section: string;
};
export type DocsGroup = {
section: string;
items: Doc[];
};
export type Heading = {
level: 2 | 3 | 4;
text: string;
id: string;
};
export type Article = {
doc: Doc;
source: string;
};
export type SearchIndexEntry = {
title: string;
href: string;
section: string;
content: string;
};
export type SearchResult = {
title: string;
href: string;
section: string;
snippet: string;
};
+7
View File
@@ -0,0 +1,7 @@
import { clsx } from "clsx";
import type { ClassValue } from "clsx";
import { twMerge } from "tailwind-merge";
export function cn(...inputs: ClassValue[]): string {
return twMerge(clsx(inputs));
}