zreader 0.1.0 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,293 @@
1
+ import rehypeParse from 'rehype-parse';
2
+ import rehypeRemark from 'rehype-remark';
3
+ import remarkGfm from 'remark-gfm';
4
+ import remarkMdx from 'remark-mdx';
5
+ import remarkParse from 'remark-parse';
6
+ import remarkStringify from 'remark-stringify';
7
+ import { unified } from 'unified';
8
+
9
+ // src/agent/markdown.ts
10
+ function base(basePath) {
11
+ return basePath.replace(/\/+$/, "");
12
+ }
13
+ function pageUrl(ctx, book, chapter) {
14
+ let path = base(ctx.basePath);
15
+ if (book) path += `/${encodeURIComponent(book)}`;
16
+ if (book && chapter) path += `/${encodeURIComponent(chapter)}`;
17
+ return `${ctx.origin}${path || "/"}`;
18
+ }
19
+ function markdownPageUrl(ctx, book, chapter) {
20
+ if (!book && !base(ctx.basePath)) return `${ctx.origin}/index.md`;
21
+ return `${pageUrl(ctx, book, chapter)}.md`;
22
+ }
23
+ var MDX_DROP = /* @__PURE__ */ new Set(["mdxjsEsm", "mdxFlowExpression", "mdxTextExpression"]);
24
+ function jsxAttr(node, name) {
25
+ const attrs = node.attributes ?? [];
26
+ const a = attrs.find((x) => x.type === "mdxJsxAttribute" && x.name === name);
27
+ return typeof a?.value === "string" ? a.value : void 0;
28
+ }
29
+ var EMBED_SRC = { embed: "src", iframe: "src", object: "data", video: "src", audio: "src", source: "src" };
30
+ function fileLabel(url, title) {
31
+ if (title) return title;
32
+ const last = url.split(/[?#]/)[0].split("/").filter(Boolean).pop() ?? url;
33
+ try {
34
+ return decodeURIComponent(last);
35
+ } catch {
36
+ return last;
37
+ }
38
+ }
39
+ function trimTrailingSpace(out) {
40
+ const prev = out[out.length - 1];
41
+ if (prev?.type === "text") prev.value = String(prev.value).replace(/[ \t]+$/, "");
42
+ }
43
+ function stripMdx(nodes) {
44
+ const out = [];
45
+ for (let node of nodes) {
46
+ if (MDX_DROP.has(node.type)) {
47
+ if (node.type === "mdxTextExpression") trimTrailingSpace(out);
48
+ continue;
49
+ }
50
+ const only = node.type === "paragraph" && node.children?.length === 1 ? node.children[0] : null;
51
+ if (only?.type === "mdxJsxTextElement") {
52
+ node = { ...only, type: "mdxJsxFlowElement", children: only.children?.length ? [{ type: "paragraph", children: only.children }] : [] };
53
+ }
54
+ const children = node.children ? stripMdx(node.children) : void 0;
55
+ if (node.type === "mdxJsxFlowElement" || node.type === "mdxJsxTextElement") {
56
+ const name = typeof node.name === "string" ? node.name : "";
57
+ const flow = node.type === "mdxJsxFlowElement";
58
+ if (name === "Callout" && flow) {
59
+ const title = jsxAttr(node, "title");
60
+ const heading = title ? [{ type: "paragraph", children: [{ type: "strong", children: [{ type: "text", value: title }] }] }] : [];
61
+ out.push({ type: "blockquote", children: [...heading, ...children ?? []] });
62
+ } else if (name === "img") {
63
+ const url = jsxAttr(node, "src");
64
+ if (url) {
65
+ const img = { type: "image", url, alt: jsxAttr(node, "alt") ?? "", title: jsxAttr(node, "title") ?? null };
66
+ out.push(flow ? { type: "paragraph", children: [img] } : img);
67
+ }
68
+ } else if (name === "a" && jsxAttr(node, "href")) {
69
+ const link2 = { type: "link", url: jsxAttr(node, "href"), children: children ?? [] };
70
+ out.push(flow ? { type: "paragraph", children: [link2] } : link2);
71
+ } else if (name === "br") {
72
+ out.push({ type: "break" });
73
+ } else if (EMBED_SRC[name] && jsxAttr(node, EMBED_SRC[name])) {
74
+ const url = jsxAttr(node, EMBED_SRC[name]);
75
+ const link2 = { type: "link", url, children: [{ type: "text", value: fileLabel(url, jsxAttr(node, "title")) }] };
76
+ out.push(flow ? { type: "paragraph", children: [link2] } : link2);
77
+ } else if (children?.length) {
78
+ out.push(...children);
79
+ } else if (!flow) {
80
+ trimTrailingSpace(out);
81
+ }
82
+ continue;
83
+ }
84
+ out.push(children ? { ...node, children } : node);
85
+ }
86
+ return out;
87
+ }
88
+ var stringify = { bullet: "-", emphasis: "_", rule: "-", fences: true };
89
+ async function mdxToMarkdown(source) {
90
+ const processor = unified().use(remarkParse).use(remarkMdx).use(remarkGfm);
91
+ const tree = await processor.run(processor.parse(source));
92
+ tree.children = stripMdx(tree.children ?? []);
93
+ return unified().use(remarkGfm).use(remarkStringify, stringify).stringify(tree);
94
+ }
95
+ function embedHandler(_state, node) {
96
+ const attr = EMBED_SRC[node.tagName] ?? "src";
97
+ const url = node.properties?.[attr];
98
+ if (typeof url !== "string" || !url) return void 0;
99
+ const title = typeof node.properties?.title === "string" ? node.properties.title : void 0;
100
+ return { type: "link", url, children: [{ type: "text", value: fileLabel(url, title) }] };
101
+ }
102
+ async function htmlToMarkdown(html) {
103
+ const file = await unified().use(rehypeParse, { fragment: true }).use(rehypeRemark, { handlers: { embed: embedHandler, iframe: embedHandler, object: embedHandler } }).use(remarkGfm).use(remarkStringify, stringify).process(html);
104
+ return String(file);
105
+ }
106
+ function hasH1(markdown) {
107
+ const tree = unified().use(remarkParse).parse(markdown);
108
+ const walk = (n) => n.type === "heading" && n.depth === 1 || (n.children ?? []).some(walk);
109
+ return walk(tree);
110
+ }
111
+ var bodyCache = /* @__PURE__ */ new Map();
112
+ function chapterBodyToMarkdown(entry) {
113
+ const key = `${entry.file}\0${entry.mtimeMs}`;
114
+ let p = bodyCache.get(key);
115
+ if (!p) {
116
+ p = entry.format === "mdx" ? (
117
+ // Invalid MDX (e.g. an HTML comment) can't be cleaned: serve the source rather than fail.
118
+ mdxToMarkdown(entry.body).catch(() => entry.body)
119
+ ) : entry.format === "html" ? htmlToMarkdown(entry.body) : Promise.resolve(entry.body);
120
+ bodyCache.set(key, p);
121
+ p.catch(() => bodyCache.delete(key));
122
+ if (bodyCache.size > 500) bodyCache.delete(bodyCache.keys().next().value);
123
+ }
124
+ return p;
125
+ }
126
+ function escapeText(s) {
127
+ return s.replace(/([\\[\]*_`])/g, "\\$1");
128
+ }
129
+ function link(text, url) {
130
+ return `[${escapeText(text)}](${url})`;
131
+ }
132
+ function chapterName(ch) {
133
+ return ch.label ? `${ch.label}: ${ch.title}` : ch.title;
134
+ }
135
+ async function chapterToMarkdown(hit, ctx) {
136
+ const { book, chapter, prev, next } = hit;
137
+ const body = (await chapterBodyToMarkdown(chapter)).trim();
138
+ const parts = [];
139
+ if (!hasH1(body)) parts.push(`# ${chapter.title}`);
140
+ if (body) parts.push(body);
141
+ const nav = [
142
+ prev && `- Previous: ${link(chapterName(prev), markdownPageUrl(ctx, book.id, prev.id))}`,
143
+ `- Contents: ${link(book.title, markdownPageUrl(ctx, book.id))}`,
144
+ next && `- Next: ${link(chapterName(next), markdownPageUrl(ctx, book.id, next.id))}`
145
+ ].filter(Boolean);
146
+ parts.push(`---
147
+
148
+ ${nav.join("\n")}`);
149
+ return parts.join("\n\n") + "\n";
150
+ }
151
+ function chapterList(book, ctx) {
152
+ const lines = [];
153
+ let part;
154
+ for (const ch of book.chapters) {
155
+ if (ch.part && ch.part !== part) {
156
+ part = ch.part;
157
+ lines.push(`${lines.length ? "\n" : ""}### ${ch.part}
158
+ `);
159
+ }
160
+ const desc = ch.description ? `: ${ch.description}` : "";
161
+ lines.push(`- ${link(chapterName(ch), markdownPageUrl(ctx, book.id, ch.id))}${desc}`);
162
+ }
163
+ return lines.join("\n");
164
+ }
165
+ function bookToMarkdown(book, ctx) {
166
+ const parts = [`# ${book.title}`];
167
+ if (book.subtitle) parts.push(`_${escapeText(book.subtitle)}_`);
168
+ if (book.authors.length) parts.push(`By ${book.authors.join(", ")}`);
169
+ if (book.description) parts.push(book.description);
170
+ parts.push(`## Contents
171
+
172
+ ${chapterList(book, ctx)}`);
173
+ parts.push(`---
174
+
175
+ - All books: ${link("Shelf", markdownPageUrl(ctx))}`);
176
+ return parts.join("\n\n") + "\n";
177
+ }
178
+ function shelfToMarkdown(books, ctx) {
179
+ const parts = ["# Books"];
180
+ for (const book of books) {
181
+ const head = `## ${link(book.title, markdownPageUrl(ctx, book.id))}`;
182
+ const meta = [book.subtitle && `_${escapeText(book.subtitle)}_`, book.authors.length && `By ${book.authors.join(", ")}`, book.description].filter(Boolean).join("\n\n");
183
+ parts.push(meta ? `${head}
184
+
185
+ ${meta}` : head);
186
+ }
187
+ if (!books.length) parts.push("_No books yet._");
188
+ return parts.join("\n\n") + "\n";
189
+ }
190
+ function llmsTxt(books, ctx, opts = {}) {
191
+ const title = opts.title ?? (books.length === 1 ? books[0].title : "Books");
192
+ const description = opts.description ?? (books.length === 1 && books[0].description ? books[0].description : "Every chapter is available as Markdown; follow the links below for the full text.");
193
+ const parts = [`# ${title}`, `> ${description.replace(/\s*\n\s*/g, " ")}`];
194
+ for (const book of books) {
195
+ const lines = [`- ${link("Table of contents", markdownPageUrl(ctx, book.id))}`];
196
+ for (const ch of book.chapters) {
197
+ const desc = ch.description ? `: ${ch.description}` : "";
198
+ lines.push(`- ${link(chapterName(ch), markdownPageUrl(ctx, book.id, ch.id))}${desc}`);
199
+ }
200
+ parts.push(`## ${book.title}
201
+
202
+ ${lines.join("\n")}`);
203
+ }
204
+ return parts.join("\n\n") + "\n";
205
+ }
206
+
207
+ // src/agent/handler.ts
208
+ var DEFAULT_CACHE = "public, max-age=0, s-maxage=3600, stale-while-revalidate=86400";
209
+ function decode(segment) {
210
+ try {
211
+ return decodeURIComponent(segment);
212
+ } catch {
213
+ return null;
214
+ }
215
+ }
216
+ function matchAgentRoute(pathname, basePath, llms = true) {
217
+ if (llms && pathname === "/llms.txt") return { kind: "llms" };
218
+ if (!pathname.endsWith(".md")) return null;
219
+ const base2 = basePath.replace(/\/+$/, "");
220
+ if (pathname === (base2 ? `${base2}.md` : "/index.md")) return { kind: "shelf" };
221
+ if (!pathname.startsWith(`${base2}/`)) return null;
222
+ const rest = pathname.slice(base2.length + 1, -3).split("/");
223
+ const segs = rest.map(decode);
224
+ if (segs.some((s) => !s)) return null;
225
+ if (segs.length === 1) return { kind: "book", book: segs[0] };
226
+ if (segs.length === 2) return { kind: "chapter", book: segs[0], chapter: segs[1] };
227
+ return null;
228
+ }
229
+ function first(value) {
230
+ return value?.split(",")[0]?.trim() || void 0;
231
+ }
232
+ function requestOrigin(request) {
233
+ const url = new URL(request.url);
234
+ const host = first(request.headers.get("x-forwarded-host")) ?? first(request.headers.get("host"));
235
+ if (!host || !/^[\w.-]+(:\d+)?$|^\[[\da-f:]+\](:\d+)?$/i.test(host)) return url.origin;
236
+ const proto = first(request.headers.get("x-forwarded-proto")) ?? url.protocol.replace(/:$/, "");
237
+ return `${proto === "http" ? "http" : "https"}://${host}`;
238
+ }
239
+ async function defaultBasePath() {
240
+ const { getReaderConfig } = await import('zreader/content');
241
+ return (await getReaderConfig()).reader.basePath;
242
+ }
243
+ async function defaultApi() {
244
+ const { getBooks, getBook, getChapter } = await import('zreader/content');
245
+ return { getBooks, getBook, getChapter };
246
+ }
247
+ function createAgentHandler(options = {}) {
248
+ const llmsOpts = options.llms === false ? null : options.llms === true || !options.llms ? {} : options.llms;
249
+ const cacheControl = options.cacheControl ?? DEFAULT_CACHE;
250
+ let basePathP;
251
+ let apiP;
252
+ return async (request) => {
253
+ if (request.method !== "GET" && request.method !== "HEAD") return void 0;
254
+ const url = new URL(request.url);
255
+ const basePath = await (basePathP ??= options.basePath != null ? Promise.resolve(options.basePath) : defaultBasePath());
256
+ const route = matchAgentRoute(url.pathname, basePath, llmsOpts !== null);
257
+ if (!route) return void 0;
258
+ const api = await (apiP ??= options.api ? Promise.resolve(options.api) : defaultApi());
259
+ const ctx = { origin: (options.origin ?? requestOrigin(request)).replace(/\/+$/, ""), basePath };
260
+ const head = request.method === "HEAD";
261
+ const respond = (body, status = 200, canonical, type = "text/markdown") => {
262
+ const headers = new Headers({
263
+ "Content-Type": `${type}; charset=utf-8`,
264
+ "Cache-Control": status === 200 ? cacheControl : "no-store",
265
+ "X-Content-Type-Options": "nosniff"
266
+ });
267
+ if (canonical) headers.set("Link", `<${canonical}>; rel="canonical"`);
268
+ return new Response(head ? null : body, { status, headers });
269
+ };
270
+ const notFound = (what) => respond(`# Not found
271
+
272
+ No ${what} at \`${url.pathname}\`. See the [shelf](${markdownPageUrl(ctx)}).
273
+ `, 404);
274
+ switch (route.kind) {
275
+ case "llms":
276
+ return respond(llmsTxt(await api.getBooks(), ctx, llmsOpts ?? {}), 200, void 0, "text/plain");
277
+ case "shelf":
278
+ return respond(shelfToMarkdown(await api.getBooks(), ctx), 200, pageUrl(ctx));
279
+ case "book": {
280
+ const book = await api.getBook(route.book);
281
+ return book ? respond(bookToMarkdown(book, ctx), 200, pageUrl(ctx, book.id)) : notFound("book");
282
+ }
283
+ case "chapter": {
284
+ const hit = await api.getChapter(route.book, route.chapter);
285
+ return hit ? respond(await chapterToMarkdown(hit, ctx), 200, pageUrl(ctx, hit.book.id, hit.chapter.id)) : notFound("chapter");
286
+ }
287
+ }
288
+ };
289
+ }
290
+
291
+ export { bookToMarkdown, chapterBodyToMarkdown, chapterToMarkdown, createAgentHandler, htmlToMarkdown, llmsTxt, markdownPageUrl, matchAgentRoute, mdxToMarkdown, pageUrl, requestOrigin, shelfToMarkdown };
292
+ //# sourceMappingURL=index.js.map
293
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/agent/markdown.ts","../../src/agent/handler.ts"],"names":["link","base"],"mappings":"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","file":"index.js","sourcesContent":["/**\n * Chapter / book / shelf → plain Markdown for agents (LLMs, crawlers, CLIs).\n * Server-only: reads the content layer's raw chapter sources.\n */\nimport rehypeParse from \"rehype-parse\";\nimport rehypeRemark from \"rehype-remark\";\nimport remarkGfm from \"remark-gfm\";\nimport remarkMdx from \"remark-mdx\";\nimport remarkParse from \"remark-parse\";\nimport remarkStringify from \"remark-stringify\";\nimport { unified } from \"unified\";\nimport type { BookEntry, ChapterEntry, ChapterLookup } from \"zreader/content\";\nimport type { BookMeta, ChapterMeta } from \"zreader\";\n\n/** Where the reader lives: absolute origin + the reader's basePath. */\nexport interface AgentUrlContext {\n /** e.g. \"https://press.example.com\" (no trailing slash). */\n origin: string;\n /** The reader's basePath, e.g. \"/books\". */\n basePath: string;\n}\n\nexport interface LlmsTxtOptions {\n /** H1 of llms.txt. Defaults to the book title (one book) or \"Books\". */\n title?: string;\n /** Summary line of llms.txt. */\n description?: string;\n}\n\n// ---------------------------------------------------------------- URLs\n\nfunction base(basePath: string) {\n return basePath.replace(/\\/+$/, \"\");\n}\n\n/** Page URL of the shelf, a book, or a chapter. */\nexport function pageUrl(ctx: AgentUrlContext, book?: string, chapter?: string): string {\n let path = base(ctx.basePath);\n if (book) path += `/${encodeURIComponent(book)}`;\n if (book && chapter) path += `/${encodeURIComponent(chapter)}`;\n return `${ctx.origin}${path || \"/\"}`;\n}\n\n/** `.md` URL of the shelf, a book, or a chapter. */\nexport function markdownPageUrl(ctx: AgentUrlContext, book?: string, chapter?: string): string {\n if (!book && !base(ctx.basePath)) return `${ctx.origin}/index.md`;\n return `${pageUrl(ctx, book, chapter)}.md`;\n}\n\n// ---------------------------------------------------------------- conversion\n\ntype Node = { type: string; children?: Node[]; [k: string]: unknown };\ntype JsxAttribute = { type: string; name?: string; value?: unknown };\n\nconst MDX_DROP = new Set([\"mdxjsEsm\", \"mdxFlowExpression\", \"mdxTextExpression\"]);\n\nfunction jsxAttr(node: Node, name: string): string | undefined {\n const attrs = (node.attributes as JsxAttribute[] | undefined) ?? [];\n const a = attrs.find((x) => x.type === \"mdxJsxAttribute\" && x.name === name);\n return typeof a?.value === \"string\" ? a.value : undefined;\n}\n\n/** Embedded documents and media: kept as a link so agents can still fetch them (PDFs etc.). */\nconst EMBED_SRC: Record<string, string> = { embed: \"src\", iframe: \"src\", object: \"data\", video: \"src\", audio: \"src\", source: \"src\" };\n\n/** Link text for an embedded file: its title, else its file name. */\nfunction fileLabel(url: string, title?: string): string {\n if (title) return title;\n const last = url.split(/[?#]/)[0]!.split(\"/\").filter(Boolean).pop() ?? url;\n try {\n return decodeURIComponent(last);\n } catch {\n return last;\n }\n}\n\nfunction trimTrailingSpace(out: Node[]) {\n const prev = out[out.length - 1];\n if (prev?.type === \"text\") prev.value = String(prev.value).replace(/[ \\t]+$/, \"\");\n}\n\n/** Replace MDX-only nodes with plain Markdown equivalents (or their children). */\nfunction stripMdx(nodes: Node[]): Node[] {\n const out: Node[] = [];\n for (let node of nodes) {\n if (MDX_DROP.has(node.type)) {\n // `a {expr} b` → `a b`, not `a b`.\n if (node.type === \"mdxTextExpression\") trimTrailingSpace(out);\n continue;\n }\n // A paragraph holding only one JSX element (`<Callout>One line.</Callout>`) is a block.\n const only = node.type === \"paragraph\" && node.children?.length === 1 ? node.children[0]! : null;\n if (only?.type === \"mdxJsxTextElement\") {\n node = { ...only, type: \"mdxJsxFlowElement\", children: only.children?.length ? [{ type: \"paragraph\", children: only.children }] : [] };\n }\n const children = node.children ? stripMdx(node.children) : undefined;\n if (node.type === \"mdxJsxFlowElement\" || node.type === \"mdxJsxTextElement\") {\n const name = typeof node.name === \"string\" ? node.name : \"\";\n const flow = node.type === \"mdxJsxFlowElement\";\n if (name === \"Callout\" && flow) {\n const title = jsxAttr(node, \"title\");\n const heading: Node[] = title\n ? [{ type: \"paragraph\", children: [{ type: \"strong\", children: [{ type: \"text\", value: title }] }] }]\n : [];\n out.push({ type: \"blockquote\", children: [...heading, ...(children ?? [])] });\n } else if (name === \"img\") {\n const url = jsxAttr(node, \"src\");\n if (url) {\n const img: Node = { type: \"image\", url, alt: jsxAttr(node, \"alt\") ?? \"\", title: jsxAttr(node, \"title\") ?? null };\n out.push(flow ? { type: \"paragraph\", children: [img] } : img);\n }\n } else if (name === \"a\" && jsxAttr(node, \"href\")) {\n const link: Node = { type: \"link\", url: jsxAttr(node, \"href\"), children: children ?? [] };\n out.push(flow ? { type: \"paragraph\", children: [link] } : link);\n } else if (name === \"br\") {\n out.push({ type: \"break\" });\n } else if (EMBED_SRC[name] && jsxAttr(node, EMBED_SRC[name]!)) {\n const url = jsxAttr(node, EMBED_SRC[name]!)!;\n const link: Node = { type: \"link\", url, children: [{ type: \"text\", value: fileLabel(url, jsxAttr(node, \"title\")) }] };\n out.push(flow ? { type: \"paragraph\", children: [link] } : link);\n } else if (children?.length) {\n // Unknown component or fragment: keep its content.\n out.push(...children);\n } else if (!flow) {\n trimTrailingSpace(out); // `See <Badge />` → `See`\n }\n continue;\n }\n out.push(children ? { ...node, children } : node);\n }\n return out;\n}\n\nconst stringify = { bullet: \"-\", emphasis: \"_\", rule: \"-\", fences: true } as const;\n\n/** Trusted MDX → Markdown: drops imports/exports/expressions, unwraps JSX. */\nexport async function mdxToMarkdown(source: string): Promise<string> {\n const processor = unified().use(remarkParse).use(remarkMdx).use(remarkGfm);\n const tree = (await processor.run(processor.parse(source))) as unknown as Node;\n tree.children = stripMdx(tree.children ?? []);\n return unified()\n .use(remarkGfm)\n .use(remarkStringify, stringify)\n .stringify(tree as never);\n}\n\ntype HastElement = { tagName: string; properties?: Record<string, unknown>; children?: unknown[] };\n\n/** `<embed>` / `<iframe>` / `<object>` → a link to the embedded file (e.g. a PDF). */\nfunction embedHandler(_state: unknown, node: HastElement) {\n const attr = EMBED_SRC[node.tagName] ?? \"src\";\n const url = node.properties?.[attr];\n if (typeof url !== \"string\" || !url) return undefined;\n const title = typeof node.properties?.title === \"string\" ? node.properties.title : undefined;\n return { type: \"link\", url, children: [{ type: \"text\", value: fileLabel(url, title) }] };\n}\n\n/** HTML → Markdown (GFM tables, fenced code; embedded files become links). */\nexport async function htmlToMarkdown(html: string): Promise<string> {\n const file = await unified()\n .use(rehypeParse, { fragment: true })\n .use(rehypeRemark, { handlers: { embed: embedHandler, iframe: embedHandler, object: embedHandler } as never })\n .use(remarkGfm)\n .use(remarkStringify, stringify)\n .process(html);\n return String(file);\n}\n\nfunction hasH1(markdown: string): boolean {\n const tree = unified().use(remarkParse).parse(markdown) as unknown as Node;\n const walk = (n: Node): boolean =>\n (n.type === \"heading\" && n.depth === 1) || (n.children ?? []).some(walk);\n return walk(tree);\n}\n\nconst bodyCache = new Map<string, Promise<string>>();\n\n/** The chapter's source as Markdown (cached per file + mtime). */\nexport function chapterBodyToMarkdown(entry: Pick<ChapterEntry, \"file\" | \"mtimeMs\" | \"format\" | \"body\">): Promise<string> {\n const key = `${entry.file}\\0${entry.mtimeMs}`;\n let p = bodyCache.get(key);\n if (!p) {\n p =\n entry.format === \"mdx\"\n ? // Invalid MDX (e.g. an HTML comment) can't be cleaned: serve the source rather than fail.\n mdxToMarkdown(entry.body).catch(() => entry.body)\n : entry.format === \"html\"\n ? htmlToMarkdown(entry.body)\n : Promise.resolve(entry.body);\n bodyCache.set(key, p);\n p.catch(() => bodyCache.delete(key));\n if (bodyCache.size > 500) bodyCache.delete(bodyCache.keys().next().value!);\n }\n return p;\n}\n\n// ---------------------------------------------------------------- documents\n\nfunction escapeText(s: string) {\n return s.replace(/([\\\\[\\]*_`])/g, \"\\\\$1\");\n}\n\nfunction link(text: string, url: string) {\n return `[${escapeText(text)}](${url})`;\n}\n\nfunction chapterName(ch: ChapterMeta) {\n return ch.label ? `${ch.label}: ${ch.title}` : ch.title;\n}\n\n/** One chapter as a standalone Markdown document with prev / contents / next links. */\nexport async function chapterToMarkdown(hit: ChapterLookup, ctx: AgentUrlContext): Promise<string> {\n const { book, chapter, prev, next } = hit;\n const body = (await chapterBodyToMarkdown(chapter)).trim();\n const parts: string[] = [];\n if (!hasH1(body)) parts.push(`# ${chapter.title}`);\n if (body) parts.push(body);\n\n const nav = [\n prev && `- Previous: ${link(chapterName(prev), markdownPageUrl(ctx, book.id, prev.id))}`,\n `- Contents: ${link(book.title, markdownPageUrl(ctx, book.id))}`,\n next && `- Next: ${link(chapterName(next), markdownPageUrl(ctx, book.id, next.id))}`,\n ].filter(Boolean);\n parts.push(`---\\n\\n${nav.join(\"\\n\")}`);\n return parts.join(\"\\n\\n\") + \"\\n\";\n}\n\nfunction chapterList(book: BookMeta, ctx: AgentUrlContext): string {\n const lines: string[] = [];\n let part: string | undefined;\n for (const ch of book.chapters) {\n if (ch.part && ch.part !== part) {\n part = ch.part;\n lines.push(`${lines.length ? \"\\n\" : \"\"}### ${ch.part}\\n`);\n }\n const desc = ch.description ? `: ${ch.description}` : \"\";\n lines.push(`- ${link(chapterName(ch), markdownPageUrl(ctx, book.id, ch.id))}${desc}`);\n }\n return lines.join(\"\\n\");\n}\n\n/** A book's front matter and table of contents, linking every chapter's `.md`. */\nexport function bookToMarkdown(book: BookMeta | BookEntry, ctx: AgentUrlContext): string {\n const parts = [`# ${book.title}`];\n if (book.subtitle) parts.push(`_${escapeText(book.subtitle)}_`);\n if (book.authors.length) parts.push(`By ${book.authors.join(\", \")}`);\n if (book.description) parts.push(book.description);\n parts.push(`## Contents\\n\\n${chapterList(book, ctx)}`);\n parts.push(`---\\n\\n- All books: ${link(\"Shelf\", markdownPageUrl(ctx))}`);\n return parts.join(\"\\n\\n\") + \"\\n\";\n}\n\n/** Every book on the shelf, linking each book's `.md`. */\nexport function shelfToMarkdown(books: Array<BookMeta | BookEntry>, ctx: AgentUrlContext): string {\n const parts = [\"# Books\"];\n for (const book of books) {\n const head = `## ${link(book.title, markdownPageUrl(ctx, book.id))}`;\n const meta = [book.subtitle && `_${escapeText(book.subtitle)}_`, book.authors.length && `By ${book.authors.join(\", \")}`, book.description]\n .filter(Boolean)\n .join(\"\\n\\n\");\n parts.push(meta ? `${head}\\n\\n${meta}` : head);\n }\n if (!books.length) parts.push(\"_No books yet._\");\n return parts.join(\"\\n\\n\") + \"\\n\";\n}\n\n/** An llms.txt index (https://llmstxt.org) of every chapter's `.md`. */\nexport function llmsTxt(books: Array<BookMeta | BookEntry>, ctx: AgentUrlContext, opts: LlmsTxtOptions = {}): string {\n const title = opts.title ?? (books.length === 1 ? books[0]!.title : \"Books\");\n const description =\n opts.description ??\n (books.length === 1 && books[0]!.description\n ? books[0]!.description\n : \"Every chapter is available as Markdown; follow the links below for the full text.\");\n const parts = [`# ${title}`, `> ${description.replace(/\\s*\\n\\s*/g, \" \")}`];\n for (const book of books) {\n const lines = [`- ${link(\"Table of contents\", markdownPageUrl(ctx, book.id))}`];\n for (const ch of book.chapters) {\n const desc = ch.description ? `: ${ch.description}` : \"\";\n lines.push(`- ${link(chapterName(ch), markdownPageUrl(ctx, book.id, ch.id))}${desc}`);\n }\n parts.push(`## ${book.title}\\n\\n${lines.join(\"\\n\")}`);\n }\n return parts.join(\"\\n\\n\") + \"\\n\";\n}\n","/**\n * Framework-agnostic `Request → Response` handler that serves agent-readable\n * Markdown next to the reader's HTML pages:\n *\n * {basePath}.md → the shelf\n * {basePath}/:book.md → a book's table of contents\n * {basePath}/:book/:chapter.md → a chapter\n * /llms.txt → an llms.txt index\n *\n * Returns `undefined` for any other request, so it composes with your own\n * middleware / proxy / router.\n */\nimport type { BookEntry, ChapterLookup } from \"zreader/content\";\nimport { bookToMarkdown, chapterToMarkdown, llmsTxt, markdownPageUrl, pageUrl, shelfToMarkdown, type AgentUrlContext, type LlmsTxtOptions } from \"./markdown\";\n\n/** The slice of the content API the handler needs (`zreader/content` or `defineBooks()`). */\nexport interface AgentContentSource {\n getBooks(): Promise<BookEntry[]>;\n getBook(bookId: string): Promise<BookEntry | null>;\n getChapter(bookId: string, chapterId: string): Promise<ChapterLookup | null>;\n}\n\nexport interface AgentHandlerOptions {\n /** Defaults to `reader.basePath` from reader.config.ts (\"/books\"). */\n basePath?: string;\n /** Defaults to the global content layer (`zreader/content`). */\n api?: AgentContentSource;\n /**\n * Absolute origin for links, e.g. \"https://press.example.com\". Recommended in\n * production. Defaults to the request's `X-Forwarded-Host` / `Host` header.\n */\n origin?: string;\n /** Serve `/llms.txt` (default true), optionally with a custom title/summary. */\n llms?: boolean | LlmsTxtOptions;\n /** Cache-Control header for successful responses. */\n cacheControl?: string;\n}\n\nexport type AgentHandler = (request: Request) => Promise<Response | undefined>;\n\nconst DEFAULT_CACHE = \"public, max-age=0, s-maxage=3600, stale-while-revalidate=86400\";\n\nfunction decode(segment: string): string | null {\n try {\n return decodeURIComponent(segment);\n } catch {\n return null;\n }\n}\n\ntype Route =\n | { kind: \"shelf\" }\n | { kind: \"book\"; book: string }\n | { kind: \"chapter\"; book: string; chapter: string }\n | { kind: \"llms\" };\n\n/** Match a pathname against the agent routes (pure; exported for tests and custom routers). */\nexport function matchAgentRoute(pathname: string, basePath: string, llms = true): Route | null {\n if (llms && pathname === \"/llms.txt\") return { kind: \"llms\" };\n if (!pathname.endsWith(\".md\")) return null;\n const base = basePath.replace(/\\/+$/, \"\");\n if (pathname === (base ? `${base}.md` : \"/index.md\")) return { kind: \"shelf\" };\n if (!pathname.startsWith(`${base}/`)) return null;\n const rest = pathname.slice(base.length + 1, -3).split(\"/\");\n const segs = rest.map(decode);\n if (segs.some((s) => !s)) return null;\n if (segs.length === 1) return { kind: \"book\", book: segs[0]! };\n if (segs.length === 2) return { kind: \"chapter\", book: segs[0]!, chapter: segs[1]! };\n return null;\n}\n\nfunction first(value: string | null): string | undefined {\n return value?.split(\",\")[0]?.trim() || undefined;\n}\n\n/** Public origin of a request: forwarded headers first (proxies, `next start`), then its URL. */\nexport function requestOrigin(request: Request): string {\n const url = new URL(request.url);\n const host = first(request.headers.get(\"x-forwarded-host\")) ?? first(request.headers.get(\"host\"));\n if (!host || !/^[\\w.-]+(:\\d+)?$|^\\[[\\da-f:]+\\](:\\d+)?$/i.test(host)) return url.origin;\n const proto = first(request.headers.get(\"x-forwarded-proto\")) ?? url.protocol.replace(/:$/, \"\");\n return `${proto === \"http\" ? \"http\" : \"https\"}://${host}`;\n}\n\nasync function defaultBasePath(): Promise<string> {\n const { getReaderConfig } = await import(\"zreader/content\");\n return (await getReaderConfig()).reader.basePath;\n}\n\nasync function defaultApi(): Promise<AgentContentSource> {\n const { getBooks, getBook, getChapter } = await import(\"zreader/content\");\n return { getBooks, getBook, getChapter };\n}\n\nexport function createAgentHandler(options: AgentHandlerOptions = {}): AgentHandler {\n const llmsOpts = options.llms === false ? null : options.llms === true || !options.llms ? {} : options.llms;\n const cacheControl = options.cacheControl ?? DEFAULT_CACHE;\n let basePathP: Promise<string> | undefined;\n let apiP: Promise<AgentContentSource> | undefined;\n\n return async (request) => {\n if (request.method !== \"GET\" && request.method !== \"HEAD\") return undefined;\n const url = new URL(request.url);\n const basePath = await (basePathP ??= options.basePath != null ? Promise.resolve(options.basePath) : defaultBasePath());\n const route = matchAgentRoute(url.pathname, basePath, llmsOpts !== null);\n if (!route) return undefined;\n\n const api = await (apiP ??= options.api ? Promise.resolve(options.api) : defaultApi());\n const ctx: AgentUrlContext = { origin: (options.origin ?? requestOrigin(request)).replace(/\\/+$/, \"\"), basePath };\n const head = request.method === \"HEAD\";\n\n const respond = (body: string, status = 200, canonical?: string, type = \"text/markdown\") => {\n const headers = new Headers({\n \"Content-Type\": `${type}; charset=utf-8`,\n \"Cache-Control\": status === 200 ? cacheControl : \"no-store\",\n \"X-Content-Type-Options\": \"nosniff\",\n });\n if (canonical) headers.set(\"Link\", `<${canonical}>; rel=\"canonical\"`);\n return new Response(head ? null : body, { status, headers });\n };\n const notFound = (what: string) =>\n respond(`# Not found\\n\\nNo ${what} at \\`${url.pathname}\\`. See the [shelf](${markdownPageUrl(ctx)}).\\n`, 404);\n\n switch (route.kind) {\n case \"llms\":\n return respond(llmsTxt(await api.getBooks(), ctx, llmsOpts ?? {}), 200, undefined, \"text/plain\");\n case \"shelf\":\n return respond(shelfToMarkdown(await api.getBooks(), ctx), 200, pageUrl(ctx));\n case \"book\": {\n const book = await api.getBook(route.book);\n return book ? respond(bookToMarkdown(book, ctx), 200, pageUrl(ctx, book.id)) : notFound(\"book\");\n }\n case \"chapter\": {\n const hit = await api.getChapter(route.book, route.chapter);\n return hit\n ? respond(await chapterToMarkdown(hit, ctx), 200, pageUrl(ctx, hit.book.id, hit.chapter.id))\n : notFound(\"chapter\");\n }\n }\n };\n}\n"]}
@@ -0,0 +1,65 @@
1
+ import * as react from 'react';
2
+
3
+ /**
4
+ * Pure helpers for building agent references (no DOM, no React).
5
+ */
6
+ type BuiltInProvider = "chatgpt" | "claude" | "grok";
7
+ interface ProviderSpec {
8
+ id: string;
9
+ label: string;
10
+ /** URL that opens the assistant with `prompt` pre-filled. */
11
+ href(prompt: string): string;
12
+ }
13
+ declare const DEFAULT_PROMPT = "Refer the content at {url}";
14
+ declare const DEFAULT_ASK_PROMPT = "Read from {url} so I can ask questions about it.";
15
+ declare const PROVIDERS: Record<BuiltInProvider, ProviderSpec>;
16
+ declare const DEFAULT_PROVIDERS: BuiltInProvider[];
17
+ declare function resolveProvider(p: BuiltInProvider | ProviderSpec): ProviderSpec;
18
+ /**
19
+ * The `.md` twin of a page URL: drops the query and hash and any trailing
20
+ * slash, then appends `.md` (`/` becomes `/index.md`).
21
+ */
22
+ declare function markdownUrl(pageUrl: string): string;
23
+ /** Fill `{url}` in a prompt template (appends the URL when there is no placeholder). */
24
+ declare function formatPrompt(template: string, url: string): string;
25
+ /** URL that opens an assistant with the prompt pre-filled. */
26
+ declare function providerUrl(provider: BuiltInProvider | ProviderSpec, prompt: string): string;
27
+
28
+ interface CopyReferenceProps {
29
+ /**
30
+ * Reference the page's `.md` twin (served by `createAgentHandler()` from
31
+ * `zreader/agent`). `false` (default) references the page URL itself, so the
32
+ * button never points at a route your app doesn't serve. Pass a function to
33
+ * map page URLs to Markdown URLs yourself (e.g. for hash routing).
34
+ */
35
+ markdown?: boolean | ((pageUrl: string) => string);
36
+ /** Copied text. `{url}` is replaced with the reference URL. */
37
+ prompt?: string;
38
+ /** Prompt sent to the assistants in the menu. */
39
+ askPrompt?: string;
40
+ /** Assistants offered in the menu, in order. `[]` hides them. */
41
+ providers?: Array<BuiltInProvider | ProviderSpec>;
42
+ /** Button text from the `lg` breakpoint up (icon-only below). */
43
+ label?: string;
44
+ className?: string;
45
+ }
46
+ /**
47
+ * "Copy reference" split button for the reader's top bar: copies a prompt that
48
+ * points an AI agent at this chapter, with a menu to open it in ChatGPT,
49
+ * Claude or Grok.
50
+ *
51
+ * <Reader … actions={<CopyReference markdown />} />
52
+ */
53
+ declare function CopyReference({ markdown, prompt, askPrompt, providers, label, className, }: CopyReferenceProps): react.JSX.Element;
54
+
55
+ /** `.md` twin of a reader href (relative or absolute): drops the hash/query, appends `.md`. */
56
+ declare function markdownHref(href: string): string;
57
+ /**
58
+ * `<link rel="alternate" type="text/markdown">` for the current chapter, so
59
+ * agents and crawlers can find its Markdown twin. React 19 hoists it into
60
+ * <head>. Renders nothing outside <Reader>.
61
+ */
62
+ declare function MarkdownAlternate(): react.JSX.Element | null;
63
+
64
+ export { CopyReference, DEFAULT_ASK_PROMPT, DEFAULT_PROMPT, DEFAULT_PROVIDERS, MarkdownAlternate, PROVIDERS, formatPrompt, markdownHref, markdownUrl, providerUrl, resolveProvider };
65
+ export type { BuiltInProvider, CopyReferenceProps, ProviderSpec };