docs-ready 0.2.0 → 0.4.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,338 @@
1
+ #!/usr/bin/env node
2
+
3
+ // src/generate/key-pages.ts
4
+ var PATH_KEYWORDS = [
5
+ "getting-started",
6
+ "quickstart",
7
+ "quick-start",
8
+ "setup",
9
+ "install",
10
+ "tutorial",
11
+ "overview",
12
+ "introduction",
13
+ "api",
14
+ "sdk",
15
+ "reference",
16
+ "config",
17
+ "configuration",
18
+ "network",
19
+ "deploy",
20
+ "migration"
21
+ ];
22
+ var TITLE_KEYWORDS = [
23
+ "getting started",
24
+ "quickstart",
25
+ "quick start",
26
+ "setup",
27
+ "install",
28
+ "tutorial",
29
+ "overview",
30
+ "introduction",
31
+ "api reference",
32
+ "sdk",
33
+ "reference",
34
+ "config",
35
+ "configuration",
36
+ "network",
37
+ "deploy",
38
+ "migration"
39
+ ];
40
+ var CONTENT_PATTERNS = [
41
+ { pattern: /0x[a-fA-F0-9]{40}/, label: "contract-address" },
42
+ { pattern: /chain\s*id/i, label: "chain-id" },
43
+ { pattern: /rpc\s*(url|endpoint)/i, label: "rpc-url" },
44
+ { pattern: /npm install\s+\S+/, label: "npm-install" },
45
+ { pattern: /go get\s+\S+/, label: "go-get" },
46
+ { pattern: /pip install\s+\S+/, label: "pip-install" }
47
+ ];
48
+ function detectKeyPages(pages) {
49
+ const results = [];
50
+ for (const page of pages) {
51
+ const reasons = [];
52
+ const pathLower = page.relativePath.toLowerCase();
53
+ const titleLower = page.title.toLowerCase();
54
+ if (PATH_KEYWORDS.some((kw) => pathLower.includes(kw))) {
55
+ reasons.push("path-keyword");
56
+ }
57
+ if (TITLE_KEYWORDS.some((kw) => titleLower.includes(kw))) {
58
+ reasons.push("title-keyword");
59
+ }
60
+ if (CONTENT_PATTERNS.some((cp) => cp.pattern.test(page.content))) {
61
+ reasons.push("content-pattern");
62
+ }
63
+ if (reasons.length > 0) {
64
+ results.push({ page, reasons });
65
+ }
66
+ }
67
+ return results;
68
+ }
69
+
70
+ // src/generate/extractors.ts
71
+ function extractContractAddresses(content) {
72
+ const regex = /0x[a-fA-F0-9]{40}/g;
73
+ const matches = content.match(regex);
74
+ return matches ?? [];
75
+ }
76
+ function extractCodeBlocks(content) {
77
+ const blocks = /* @__PURE__ */ new Map();
78
+ const regex = /```(\w+)\n([\s\S]*?)```/g;
79
+ let match;
80
+ while ((match = regex.exec(content)) !== null) {
81
+ const lang = match[1];
82
+ const code = match[2].trim();
83
+ if (!blocks.has(lang)) {
84
+ blocks.set(lang, []);
85
+ }
86
+ blocks.get(lang).push(code);
87
+ }
88
+ return blocks;
89
+ }
90
+ function extractTables(content) {
91
+ const tables = [];
92
+ const lines = content.split("\n");
93
+ let current = [];
94
+ let inTable = false;
95
+ for (const line of lines) {
96
+ const trimmed = line.trim();
97
+ if (trimmed.startsWith("|") && trimmed.endsWith("|")) {
98
+ inTable = true;
99
+ current.push(line);
100
+ } else {
101
+ if (inTable && current.length >= 2) {
102
+ tables.push(current.join("\n"));
103
+ }
104
+ current = [];
105
+ inTable = false;
106
+ }
107
+ }
108
+ if (inTable && current.length >= 2) {
109
+ tables.push(current.join("\n"));
110
+ }
111
+ return tables;
112
+ }
113
+ function extractLinks(content) {
114
+ const links = [];
115
+ const regex = /\[([^\]]+)\]\((https?:\/\/[^)]+)\)/g;
116
+ let match;
117
+ while ((match = regex.exec(content)) !== null) {
118
+ const text = match[1];
119
+ const url = match[2];
120
+ let category = "external";
121
+ if (url.includes("github.com")) {
122
+ category = "github";
123
+ } else if (url.includes("npmjs.com") || url.includes("npm.im")) {
124
+ category = "npm";
125
+ }
126
+ links.push({ text, url, category });
127
+ }
128
+ return links;
129
+ }
130
+ function deduplicateBlocks(blocks) {
131
+ const result = /* @__PURE__ */ new Map();
132
+ for (const [lang, codeList] of blocks) {
133
+ result.set(lang, [...new Set(codeList)]);
134
+ }
135
+ return result;
136
+ }
137
+
138
+ // src/generate/ai-context.ts
139
+ var NETWORK_PATTERNS = [/chain\s*id/i, /rpc\s*(url|endpoint)/i];
140
+ var QUICKSTART_PATH_KEYWORDS = [
141
+ "getting-started",
142
+ "quickstart",
143
+ "quick-start",
144
+ "setup",
145
+ "install",
146
+ "sdk"
147
+ ];
148
+ function generateAiContext(pages, config) {
149
+ const keyPages = resolveKeyPages(pages, config);
150
+ const sections = [];
151
+ sections.push(
152
+ "<!-- Auto-generated by docs-ready. Review and customize. -->"
153
+ );
154
+ sections.push("");
155
+ sections.push(`# ${config.title} \u2014 AI Context`);
156
+ sections.push("");
157
+ sections.push("## Overview");
158
+ sections.push("");
159
+ sections.push(config.description);
160
+ const networkSection = buildNetworkSection(keyPages);
161
+ if (networkSection) {
162
+ sections.push("");
163
+ sections.push("## Network Configuration");
164
+ sections.push("");
165
+ sections.push(networkSection);
166
+ }
167
+ const contractSection = buildContractSection(keyPages);
168
+ if (contractSection) {
169
+ sections.push("");
170
+ sections.push("## Contract Addresses");
171
+ sections.push("");
172
+ sections.push(contractSection);
173
+ }
174
+ const quickStartSection = buildQuickStartSection(keyPages);
175
+ if (quickStartSection) {
176
+ sections.push("");
177
+ sections.push("## Quick Start");
178
+ sections.push("");
179
+ sections.push(quickStartSection);
180
+ }
181
+ const linksSection = buildLinksSection(keyPages);
182
+ if (linksSection) {
183
+ sections.push("");
184
+ sections.push("## Links & Resources");
185
+ sections.push("");
186
+ sections.push(linksSection);
187
+ }
188
+ if (config.aiContextConfig?.extra_sections) {
189
+ for (const extra of config.aiContextConfig.extra_sections) {
190
+ sections.push("");
191
+ sections.push(`## ${extra.title}`);
192
+ sections.push("");
193
+ sections.push(extra.content);
194
+ }
195
+ }
196
+ return sections.join("\n") + "\n";
197
+ }
198
+ function resolveKeyPages(pages, config) {
199
+ if (config.aiContextConfig?.key_pages?.length) {
200
+ const manual = [];
201
+ for (const kp of config.aiContextConfig.key_pages) {
202
+ const match = pages.find(
203
+ (p) => p.relativePath === kp.path || p.relativePath === kp.path.replace(/\\/g, "/")
204
+ );
205
+ if (match) {
206
+ manual.push(match);
207
+ }
208
+ }
209
+ if (manual.length > 0) {
210
+ return manual;
211
+ }
212
+ }
213
+ const detected = detectKeyPages(pages);
214
+ if (detected.length > 0) {
215
+ return detected.map((d) => d.page);
216
+ }
217
+ return pages;
218
+ }
219
+ function buildNetworkSection(pages) {
220
+ const networkTables = [];
221
+ for (const page of pages) {
222
+ const hasNetworkContent = NETWORK_PATTERNS.some(
223
+ (p) => p.test(page.content)
224
+ );
225
+ if (!hasNetworkContent) continue;
226
+ const tables = extractTables(page.content);
227
+ for (const table of tables) {
228
+ if (NETWORK_PATTERNS.some((p) => p.test(table))) {
229
+ networkTables.push(`### ${page.title}
230
+
231
+ ${table}`);
232
+ }
233
+ }
234
+ }
235
+ if (networkTables.length === 0) return null;
236
+ return networkTables.join("\n\n");
237
+ }
238
+ function buildContractSection(pages) {
239
+ const addressTables = [];
240
+ const looseAddresses = [];
241
+ for (const page of pages) {
242
+ const addresses = extractContractAddresses(page.content);
243
+ if (addresses.length === 0) continue;
244
+ const tables = extractTables(page.content);
245
+ const tablesWithAddresses = tables.filter(
246
+ (t) => addresses.some((addr) => t.includes(addr))
247
+ );
248
+ if (tablesWithAddresses.length > 0) {
249
+ for (const table of tablesWithAddresses) {
250
+ addressTables.push(`### ${page.title}
251
+
252
+ ${table}`);
253
+ }
254
+ } else {
255
+ for (const addr of addresses) {
256
+ looseAddresses.push(`- \`${addr}\``);
257
+ }
258
+ }
259
+ }
260
+ if (addressTables.length === 0 && looseAddresses.length === 0) return null;
261
+ const parts = [];
262
+ if (addressTables.length > 0) {
263
+ parts.push(addressTables.join("\n\n"));
264
+ }
265
+ if (looseAddresses.length > 0) {
266
+ parts.push([...new Set(looseAddresses)].join("\n"));
267
+ }
268
+ return parts.join("\n\n");
269
+ }
270
+ function buildQuickStartSection(pages) {
271
+ const allBlocks = /* @__PURE__ */ new Map();
272
+ for (const page of pages) {
273
+ const pathLower = page.relativePath.toLowerCase();
274
+ const titleLower = page.title.toLowerCase();
275
+ const isQuickstartPage = QUICKSTART_PATH_KEYWORDS.some((kw) => pathLower.includes(kw)) || QUICKSTART_PATH_KEYWORDS.some((kw) => titleLower.includes(kw));
276
+ if (!isQuickstartPage) continue;
277
+ const blocks = extractCodeBlocks(page.content);
278
+ for (const [lang, codeList] of blocks) {
279
+ if (!allBlocks.has(lang)) {
280
+ allBlocks.set(lang, []);
281
+ }
282
+ allBlocks.get(lang).push(...codeList);
283
+ }
284
+ }
285
+ if (allBlocks.size === 0) return null;
286
+ const deduplicated = deduplicateBlocks(allBlocks);
287
+ const parts = [];
288
+ for (const [lang, codeList] of deduplicated) {
289
+ for (const code of codeList) {
290
+ parts.push(`\`\`\`${lang}
291
+ ${code}
292
+ \`\`\``);
293
+ }
294
+ }
295
+ return parts.join("\n\n");
296
+ }
297
+ function buildLinksSection(pages) {
298
+ const allLinks = [];
299
+ for (const page of pages) {
300
+ const links = extractLinks(page.content);
301
+ allLinks.push(...links);
302
+ }
303
+ if (allLinks.length === 0) return null;
304
+ const seen = /* @__PURE__ */ new Set();
305
+ const unique = [];
306
+ for (const link of allLinks) {
307
+ if (!seen.has(link.url)) {
308
+ seen.add(link.url);
309
+ unique.push(link);
310
+ }
311
+ }
312
+ const github = unique.filter((l) => l.category === "github");
313
+ const npm = unique.filter((l) => l.category === "npm");
314
+ const external = unique.filter((l) => l.category === "external");
315
+ const parts = [];
316
+ if (github.length > 0) {
317
+ parts.push("### GitHub");
318
+ parts.push("");
319
+ parts.push(...github.map((l) => `- [${l.text}](${l.url})`));
320
+ }
321
+ if (npm.length > 0) {
322
+ parts.push("");
323
+ parts.push("### npm");
324
+ parts.push("");
325
+ parts.push(...npm.map((l) => `- [${l.text}](${l.url})`));
326
+ }
327
+ if (external.length > 0) {
328
+ parts.push("");
329
+ parts.push("### External");
330
+ parts.push("");
331
+ parts.push(...external.map((l) => `- [${l.text}](${l.url})`));
332
+ }
333
+ return parts.join("\n");
334
+ }
335
+ export {
336
+ generateAiContext
337
+ };
338
+ //# sourceMappingURL=ai-context-QVGQK3GD.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/generate/key-pages.ts","../src/generate/extractors.ts","../src/generate/ai-context.ts"],"sourcesContent":["import type { ScannedPage } from \"../core/scanner.js\";\n\nexport interface KeyPageResult {\n page: ScannedPage;\n reasons: string[];\n}\n\nconst PATH_KEYWORDS = [\n \"getting-started\", \"quickstart\", \"quick-start\", \"setup\", \"install\",\n \"tutorial\", \"overview\", \"introduction\", \"api\", \"sdk\", \"reference\",\n \"config\", \"configuration\", \"network\", \"deploy\", \"migration\",\n];\n\nconst TITLE_KEYWORDS = [\n \"getting started\", \"quickstart\", \"quick start\", \"setup\", \"install\",\n \"tutorial\", \"overview\", \"introduction\", \"api reference\", \"sdk\",\n \"reference\", \"config\", \"configuration\", \"network\", \"deploy\", \"migration\",\n];\n\nconst CONTENT_PATTERNS: Array<{ pattern: RegExp; label: string }> = [\n { pattern: /0x[a-fA-F0-9]{40}/, label: \"contract-address\" },\n { pattern: /chain\\s*id/i, label: \"chain-id\" },\n { pattern: /rpc\\s*(url|endpoint)/i, label: \"rpc-url\" },\n { pattern: /npm install\\s+\\S+/, label: \"npm-install\" },\n { pattern: /go get\\s+\\S+/, label: \"go-get\" },\n { pattern: /pip install\\s+\\S+/, label: \"pip-install\" },\n];\n\nexport function detectKeyPages(pages: ScannedPage[]): KeyPageResult[] {\n const results: KeyPageResult[] = [];\n\n for (const page of pages) {\n const reasons: string[] = [];\n const pathLower = page.relativePath.toLowerCase();\n const titleLower = page.title.toLowerCase();\n\n if (PATH_KEYWORDS.some((kw) => pathLower.includes(kw))) {\n reasons.push(\"path-keyword\");\n }\n\n if (TITLE_KEYWORDS.some((kw) => titleLower.includes(kw))) {\n reasons.push(\"title-keyword\");\n }\n\n if (CONTENT_PATTERNS.some((cp) => cp.pattern.test(page.content))) {\n reasons.push(\"content-pattern\");\n }\n\n if (reasons.length > 0) {\n results.push({ page, reasons });\n }\n }\n\n return results;\n}\n","export function extractContractAddresses(content: string): string[] {\n const regex = /0x[a-fA-F0-9]{40}/g;\n const matches = content.match(regex);\n return matches ?? [];\n}\n\nexport function extractCodeBlocks(content: string): Map<string, string[]> {\n const blocks = new Map<string, string[]>();\n const regex = /```(\\w+)\\n([\\s\\S]*?)```/g;\n let match;\n\n while ((match = regex.exec(content)) !== null) {\n const lang = match[1];\n const code = match[2].trim();\n if (!blocks.has(lang)) {\n blocks.set(lang, []);\n }\n blocks.get(lang)!.push(code);\n }\n\n return blocks;\n}\n\nexport function extractTables(content: string): string[] {\n const tables: string[] = [];\n const lines = content.split(\"\\n\");\n let current: string[] = [];\n let inTable = false;\n\n for (const line of lines) {\n const trimmed = line.trim();\n if (trimmed.startsWith(\"|\") && trimmed.endsWith(\"|\")) {\n inTable = true;\n current.push(line);\n } else {\n if (inTable && current.length >= 2) {\n tables.push(current.join(\"\\n\"));\n }\n current = [];\n inTable = false;\n }\n }\n\n if (inTable && current.length >= 2) {\n tables.push(current.join(\"\\n\"));\n }\n\n return tables;\n}\n\nexport interface ExtractedLink {\n text: string;\n url: string;\n category: \"github\" | \"npm\" | \"external\";\n}\n\nexport function extractLinks(content: string): ExtractedLink[] {\n const links: ExtractedLink[] = [];\n const regex = /\\[([^\\]]+)\\]\\((https?:\\/\\/[^)]+)\\)/g;\n let match;\n\n while ((match = regex.exec(content)) !== null) {\n const text = match[1];\n const url = match[2];\n let category: ExtractedLink[\"category\"] = \"external\";\n if (url.includes(\"github.com\")) {\n category = \"github\";\n } else if (url.includes(\"npmjs.com\") || url.includes(\"npm.im\")) {\n category = \"npm\";\n }\n links.push({ text, url, category });\n }\n\n return links;\n}\n\nexport function deduplicateBlocks(blocks: Map<string, string[]>): Map<string, string[]> {\n const result = new Map<string, string[]>();\n for (const [lang, codeList] of blocks) {\n result.set(lang, [...new Set(codeList)]);\n }\n return result;\n}\n","import type { ScannedPage } from \"../core/scanner.js\";\nimport { detectKeyPages } from \"./key-pages.js\";\nimport {\n extractContractAddresses,\n extractCodeBlocks,\n extractTables,\n extractLinks,\n deduplicateBlocks,\n type ExtractedLink,\n} from \"./extractors.js\";\n\nexport interface AiContextConfig {\n title: string;\n description: string;\n aiContextConfig?: {\n key_pages?: Array<{ path: string; section: string }>;\n extra_sections?: Array<{ title: string; content: string }>;\n };\n}\n\nconst NETWORK_PATTERNS = [/chain\\s*id/i, /rpc\\s*(url|endpoint)/i];\nconst QUICKSTART_PATH_KEYWORDS = [\n \"getting-started\",\n \"quickstart\",\n \"quick-start\",\n \"setup\",\n \"install\",\n \"sdk\",\n];\n\nexport function generateAiContext(\n pages: ScannedPage[],\n config: AiContextConfig,\n): string {\n const keyPages = resolveKeyPages(pages, config);\n const sections: string[] = [];\n\n // Header\n sections.push(\n \"<!-- Auto-generated by docs-ready. Review and customize. -->\",\n );\n sections.push(\"\");\n sections.push(`# ${config.title} — AI Context`);\n\n // Overview — always present\n sections.push(\"\");\n sections.push(\"## Overview\");\n sections.push(\"\");\n sections.push(config.description);\n\n // Network Configuration\n const networkSection = buildNetworkSection(keyPages);\n if (networkSection) {\n sections.push(\"\");\n sections.push(\"## Network Configuration\");\n sections.push(\"\");\n sections.push(networkSection);\n }\n\n // Contract Addresses\n const contractSection = buildContractSection(keyPages);\n if (contractSection) {\n sections.push(\"\");\n sections.push(\"## Contract Addresses\");\n sections.push(\"\");\n sections.push(contractSection);\n }\n\n // Quick Start\n const quickStartSection = buildQuickStartSection(keyPages);\n if (quickStartSection) {\n sections.push(\"\");\n sections.push(\"## Quick Start\");\n sections.push(\"\");\n sections.push(quickStartSection);\n }\n\n // Links & Resources\n const linksSection = buildLinksSection(keyPages);\n if (linksSection) {\n sections.push(\"\");\n sections.push(\"## Links & Resources\");\n sections.push(\"\");\n sections.push(linksSection);\n }\n\n // Extra sections from config\n if (config.aiContextConfig?.extra_sections) {\n for (const extra of config.aiContextConfig.extra_sections) {\n sections.push(\"\");\n sections.push(`## ${extra.title}`);\n sections.push(\"\");\n sections.push(extra.content);\n }\n }\n\n return sections.join(\"\\n\") + \"\\n\";\n}\n\nfunction resolveKeyPages(\n pages: ScannedPage[],\n config: AiContextConfig,\n): ScannedPage[] {\n // Manual override\n if (config.aiContextConfig?.key_pages?.length) {\n const manual: ScannedPage[] = [];\n for (const kp of config.aiContextConfig.key_pages) {\n const match = pages.find(\n (p) =>\n p.relativePath === kp.path ||\n p.relativePath === kp.path.replace(/\\\\/g, \"/\"),\n );\n if (match) {\n manual.push(match);\n }\n }\n // If manual pages resolved, return them; otherwise fall through\n if (manual.length > 0) {\n return manual;\n }\n }\n\n // Auto-detect\n const detected = detectKeyPages(pages);\n if (detected.length > 0) {\n return detected.map((d) => d.page);\n }\n\n // Fallback: all pages\n return pages;\n}\n\nfunction buildNetworkSection(pages: ScannedPage[]): string | null {\n const networkTables: string[] = [];\n\n for (const page of pages) {\n const hasNetworkContent = NETWORK_PATTERNS.some((p) =>\n p.test(page.content),\n );\n if (!hasNetworkContent) continue;\n\n const tables = extractTables(page.content);\n // Only include tables that contain network-related content\n for (const table of tables) {\n if (NETWORK_PATTERNS.some((p) => p.test(table))) {\n networkTables.push(`### ${page.title}\\n\\n${table}`);\n }\n }\n }\n\n if (networkTables.length === 0) return null;\n return networkTables.join(\"\\n\\n\");\n}\n\nfunction buildContractSection(pages: ScannedPage[]): string | null {\n const addressTables: string[] = [];\n const looseAddresses: string[] = [];\n\n for (const page of pages) {\n const addresses = extractContractAddresses(page.content);\n if (addresses.length === 0) continue;\n\n // Check if the addresses appear inside tables\n const tables = extractTables(page.content);\n const tablesWithAddresses = tables.filter((t) =>\n addresses.some((addr) => t.includes(addr)),\n );\n\n if (tablesWithAddresses.length > 0) {\n for (const table of tablesWithAddresses) {\n addressTables.push(`### ${page.title}\\n\\n${table}`);\n }\n } else {\n // Fallback: bullet list\n for (const addr of addresses) {\n looseAddresses.push(`- \\`${addr}\\``);\n }\n }\n }\n\n if (addressTables.length === 0 && looseAddresses.length === 0) return null;\n\n const parts: string[] = [];\n if (addressTables.length > 0) {\n parts.push(addressTables.join(\"\\n\\n\"));\n }\n if (looseAddresses.length > 0) {\n parts.push([...new Set(looseAddresses)].join(\"\\n\"));\n }\n return parts.join(\"\\n\\n\");\n}\n\nfunction buildQuickStartSection(pages: ScannedPage[]): string | null {\n const allBlocks = new Map<string, string[]>();\n\n for (const page of pages) {\n const pathLower = page.relativePath.toLowerCase();\n const titleLower = page.title.toLowerCase();\n const isQuickstartPage =\n QUICKSTART_PATH_KEYWORDS.some((kw) => pathLower.includes(kw)) ||\n QUICKSTART_PATH_KEYWORDS.some((kw) => titleLower.includes(kw));\n\n if (!isQuickstartPage) continue;\n\n const blocks = extractCodeBlocks(page.content);\n for (const [lang, codeList] of blocks) {\n if (!allBlocks.has(lang)) {\n allBlocks.set(lang, []);\n }\n allBlocks.get(lang)!.push(...codeList);\n }\n }\n\n if (allBlocks.size === 0) return null;\n\n const deduplicated = deduplicateBlocks(allBlocks);\n const parts: string[] = [];\n\n for (const [lang, codeList] of deduplicated) {\n for (const code of codeList) {\n parts.push(`\\`\\`\\`${lang}\\n${code}\\n\\`\\`\\``);\n }\n }\n\n return parts.join(\"\\n\\n\");\n}\n\nfunction buildLinksSection(pages: ScannedPage[]): string | null {\n const allLinks: ExtractedLink[] = [];\n\n for (const page of pages) {\n const links = extractLinks(page.content);\n allLinks.push(...links);\n }\n\n if (allLinks.length === 0) return null;\n\n // Deduplicate by URL\n const seen = new Set<string>();\n const unique: ExtractedLink[] = [];\n for (const link of allLinks) {\n if (!seen.has(link.url)) {\n seen.add(link.url);\n unique.push(link);\n }\n }\n\n // Categorize\n const github = unique.filter((l) => l.category === \"github\");\n const npm = unique.filter((l) => l.category === \"npm\");\n const external = unique.filter((l) => l.category === \"external\");\n\n const parts: string[] = [];\n\n if (github.length > 0) {\n parts.push(\"### GitHub\");\n parts.push(\"\");\n parts.push(...github.map((l) => `- [${l.text}](${l.url})`));\n }\n\n if (npm.length > 0) {\n parts.push(\"\");\n parts.push(\"### npm\");\n parts.push(\"\");\n parts.push(...npm.map((l) => `- [${l.text}](${l.url})`));\n }\n\n if (external.length > 0) {\n parts.push(\"\");\n parts.push(\"### External\");\n parts.push(\"\");\n parts.push(...external.map((l) => `- [${l.text}](${l.url})`));\n }\n\n return parts.join(\"\\n\");\n}\n"],"mappings":"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","names":[]}
@@ -0,0 +1,153 @@
1
+ #!/usr/bin/env node
2
+
3
+ // src/core/config.ts
4
+ import fs from "fs/promises";
5
+ import path from "path";
6
+ import YAML from "yaml";
7
+ var DEFAULTS = {
8
+ docs: {
9
+ dir: "./docs",
10
+ include: ["**/*.md", "**/*.mdx"],
11
+ exclude: ["**/node_modules/**", "**/_*"]
12
+ },
13
+ generate: {
14
+ llms_txt: true,
15
+ llms_full_txt: true,
16
+ ai_context: true,
17
+ output_dir: "./build"
18
+ },
19
+ guard: {
20
+ npm_packages: [],
21
+ github_releases: [],
22
+ endpoints: [],
23
+ readme_scans: [],
24
+ workflow: {
25
+ enabled: true,
26
+ schedule: "0 9 */3 * *",
27
+ create_issues: true,
28
+ labels: ["ai-context-review", "documentation"]
29
+ }
30
+ },
31
+ deploy: {
32
+ platform: "none",
33
+ cors: { enabled: true, origins: ["*"] }
34
+ },
35
+ validate: {
36
+ max_tokens: 15e4,
37
+ check_links: true,
38
+ check_coverage: true,
39
+ coverage_threshold: 0.95
40
+ }
41
+ };
42
+ var CONFIG_FILES = [
43
+ { name: ".docs-ready.yaml", parser: "yaml" },
44
+ { name: ".docs-ready.yml", parser: "yaml" },
45
+ { name: ".docs-ready.json", parser: "json" },
46
+ { name: ".docs-ready.toml", parser: "toml" }
47
+ ];
48
+ async function loadConfig(dir, configPath) {
49
+ let raw;
50
+ if (configPath) {
51
+ const content = await fs.readFile(configPath, "utf-8");
52
+ raw = parseConfig(content, configPath);
53
+ } else {
54
+ for (const { name, parser } of CONFIG_FILES) {
55
+ const fullPath = path.join(dir, name);
56
+ try {
57
+ const content = await fs.readFile(fullPath, "utf-8");
58
+ if (parser === "toml") {
59
+ throw new Error("TOML support requires a TOML parser. Use .yaml or .json instead.");
60
+ }
61
+ raw = parseConfig(content, fullPath);
62
+ break;
63
+ } catch (err) {
64
+ if (err.code === "ENOENT") continue;
65
+ throw err;
66
+ }
67
+ }
68
+ }
69
+ if (!raw) {
70
+ throw new Error(
71
+ "No config file found. Run `docs-ready init` to create one, or create .docs-ready.yaml manually."
72
+ );
73
+ }
74
+ validate(raw);
75
+ return applyDefaults(raw);
76
+ }
77
+ function parseConfig(content, filePath) {
78
+ if (filePath.endsWith(".json")) {
79
+ return JSON.parse(content);
80
+ }
81
+ const parsed = YAML.parse(content);
82
+ if (parsed === null || typeof parsed !== "object") {
83
+ throw new Error(`Failed to parse config file: ${filePath}`);
84
+ }
85
+ return parsed;
86
+ }
87
+ function validate(raw) {
88
+ const missing = [];
89
+ if (!raw.title || typeof raw.title !== "string") missing.push("title");
90
+ if (!raw.description || typeof raw.description !== "string") missing.push("description");
91
+ if (!raw.url || typeof raw.url !== "string") missing.push("url");
92
+ if (missing.length > 0) {
93
+ throw new Error(
94
+ `Missing required config fields: ${missing.join(", ")}. Add them to your .docs-ready.yaml file.`
95
+ );
96
+ }
97
+ }
98
+ function applyDefaults(raw) {
99
+ const docs = raw.docs;
100
+ const generate = raw.generate;
101
+ const guard = raw.guard;
102
+ const deploy = raw.deploy;
103
+ const validateConf = raw.validate;
104
+ const guardWorkflow = guard?.workflow ?? {};
105
+ return {
106
+ title: raw.title,
107
+ description: raw.description,
108
+ url: raw.url,
109
+ docs: {
110
+ dir: docs?.dir ?? DEFAULTS.docs.dir,
111
+ include: docs?.include ?? DEFAULTS.docs.include,
112
+ exclude: docs?.exclude ?? DEFAULTS.docs.exclude
113
+ },
114
+ generate: {
115
+ llms_txt: generate?.llms_txt ?? DEFAULTS.generate.llms_txt,
116
+ llms_full_txt: generate?.llms_full_txt ?? DEFAULTS.generate.llms_full_txt,
117
+ ai_context: generate?.ai_context ?? DEFAULTS.generate.ai_context,
118
+ output_dir: generate?.output_dir ?? DEFAULTS.generate.output_dir,
119
+ sections: generate?.sections,
120
+ ai_context_config: generate?.ai_context_config
121
+ },
122
+ guard: {
123
+ npm_packages: guard?.npm_packages ?? DEFAULTS.guard.npm_packages,
124
+ github_releases: guard?.github_releases ?? DEFAULTS.guard.github_releases,
125
+ endpoints: guard?.endpoints ?? DEFAULTS.guard.endpoints,
126
+ readme_scans: guard?.readme_scans ?? DEFAULTS.guard.readme_scans,
127
+ workflow: {
128
+ enabled: guardWorkflow.enabled ?? DEFAULTS.guard.workflow.enabled,
129
+ schedule: guardWorkflow.schedule ?? DEFAULTS.guard.workflow.schedule,
130
+ create_issues: guardWorkflow.create_issues ?? DEFAULTS.guard.workflow.create_issues,
131
+ labels: guardWorkflow.labels ?? DEFAULTS.guard.workflow.labels
132
+ }
133
+ },
134
+ deploy: {
135
+ platform: deploy?.platform ?? DEFAULTS.deploy.platform,
136
+ cors: {
137
+ enabled: deploy?.cors?.enabled ?? DEFAULTS.deploy.cors.enabled,
138
+ origins: deploy?.cors?.origins ?? DEFAULTS.deploy.cors.origins
139
+ }
140
+ },
141
+ validate: {
142
+ max_tokens: validateConf?.max_tokens ?? DEFAULTS.validate.max_tokens,
143
+ check_links: validateConf?.check_links ?? DEFAULTS.validate.check_links,
144
+ check_coverage: validateConf?.check_coverage ?? DEFAULTS.validate.check_coverage,
145
+ coverage_threshold: validateConf?.coverage_threshold ?? DEFAULTS.validate.coverage_threshold
146
+ }
147
+ };
148
+ }
149
+
150
+ export {
151
+ loadConfig
152
+ };
153
+ //# sourceMappingURL=chunk-QI2AROM3.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/core/config.ts"],"sourcesContent":["import fs from \"node:fs/promises\";\nimport path from \"node:path\";\nimport YAML from \"yaml\";\n\nexport interface DocsReadyConfig {\n title: string;\n description: string;\n url: string;\n docs: {\n dir: string;\n include: string[];\n exclude: string[];\n };\n generate: {\n llms_txt: boolean;\n llms_full_txt: boolean;\n ai_context: boolean;\n output_dir: string;\n sections?: Array<{ title: string; patterns: string[] }>;\n ai_context_config?: {\n key_pages?: Array<{ path: string; section: string }>;\n extra_sections?: Array<{ title: string; source?: string; auto_extract?: boolean }>;\n };\n };\n guard: {\n npm_packages: Array<{ name: string; label: string }>;\n github_releases: Array<{ repo: string; label: string }>;\n endpoints: Array<{ url: string; label: string; expected_status?: number[] }>;\n readme_scans: Array<{ repo: string; keywords: string[] }>;\n workflow: {\n enabled: boolean;\n schedule: string;\n create_issues: boolean;\n labels: string[];\n };\n };\n deploy: {\n platform: \"vercel\" | \"netlify\" | \"cloudflare\" | \"none\";\n cors: { enabled: boolean; origins: string[] };\n };\n validate: {\n max_tokens: number;\n check_links: boolean;\n check_coverage: boolean;\n coverage_threshold: number;\n };\n}\n\nconst DEFAULTS: Omit<DocsReadyConfig, \"title\" | \"description\" | \"url\"> = {\n docs: {\n dir: \"./docs\",\n include: [\"**/*.md\", \"**/*.mdx\"],\n exclude: [\"**/node_modules/**\", \"**/_*\"],\n },\n generate: {\n llms_txt: true,\n llms_full_txt: true,\n ai_context: true,\n output_dir: \"./build\",\n },\n guard: {\n npm_packages: [],\n github_releases: [],\n endpoints: [],\n readme_scans: [],\n workflow: {\n enabled: true,\n schedule: \"0 9 */3 * *\",\n create_issues: true,\n labels: [\"ai-context-review\", \"documentation\"],\n },\n },\n deploy: {\n platform: \"none\",\n cors: { enabled: true, origins: [\"*\"] },\n },\n validate: {\n max_tokens: 150000,\n check_links: true,\n check_coverage: true,\n coverage_threshold: 0.95,\n },\n};\n\nconst CONFIG_FILES = [\n { name: \".docs-ready.yaml\", parser: \"yaml\" as const },\n { name: \".docs-ready.yml\", parser: \"yaml\" as const },\n { name: \".docs-ready.json\", parser: \"json\" as const },\n { name: \".docs-ready.toml\", parser: \"toml\" as const },\n];\n\nexport async function loadConfig(dir: string, configPath?: string): Promise<DocsReadyConfig> {\n let raw: Record<string, unknown> | undefined;\n\n if (configPath) {\n const content = await fs.readFile(configPath, \"utf-8\");\n raw = parseConfig(content, configPath);\n } else {\n for (const { name, parser } of CONFIG_FILES) {\n const fullPath = path.join(dir, name);\n try {\n const content = await fs.readFile(fullPath, \"utf-8\");\n if (parser === \"toml\") {\n throw new Error(\"TOML support requires a TOML parser. Use .yaml or .json instead.\");\n }\n raw = parseConfig(content, fullPath);\n break;\n } catch (err) {\n if ((err as NodeJS.ErrnoException).code === \"ENOENT\") continue;\n throw err;\n }\n }\n }\n\n if (!raw) {\n throw new Error(\n \"No config file found. Run `docs-ready init` to create one, or create .docs-ready.yaml manually.\"\n );\n }\n\n validate(raw);\n return applyDefaults(raw);\n}\n\nfunction parseConfig(content: string, filePath: string): Record<string, unknown> {\n if (filePath.endsWith(\".json\")) {\n return JSON.parse(content);\n }\n const parsed = YAML.parse(content);\n if (parsed === null || typeof parsed !== \"object\") {\n throw new Error(`Failed to parse config file: ${filePath}`);\n }\n return parsed;\n}\n\nfunction validate(raw: Record<string, unknown>): void {\n const missing: string[] = [];\n if (!raw.title || typeof raw.title !== \"string\") missing.push(\"title\");\n if (!raw.description || typeof raw.description !== \"string\") missing.push(\"description\");\n if (!raw.url || typeof raw.url !== \"string\") missing.push(\"url\");\n\n if (missing.length > 0) {\n throw new Error(\n `Missing required config fields: ${missing.join(\", \")}. ` +\n `Add them to your .docs-ready.yaml file.`\n );\n }\n}\n\nfunction applyDefaults(raw: Record<string, unknown>): DocsReadyConfig {\n const docs = raw.docs as Record<string, unknown> | undefined;\n const generate = raw.generate as Record<string, unknown> | undefined;\n const guard = raw.guard as Record<string, unknown> | undefined;\n const deploy = raw.deploy as Record<string, unknown> | undefined;\n const validateConf = raw.validate as Record<string, unknown> | undefined;\n const guardWorkflow = (guard?.workflow as Record<string, unknown>) ?? {};\n\n return {\n title: raw.title as string,\n description: raw.description as string,\n url: raw.url as string,\n docs: {\n dir: (docs?.dir as string) ?? DEFAULTS.docs.dir,\n include: (docs?.include as string[]) ?? DEFAULTS.docs.include,\n exclude: (docs?.exclude as string[]) ?? DEFAULTS.docs.exclude,\n },\n generate: {\n llms_txt: (generate?.llms_txt as boolean) ?? DEFAULTS.generate.llms_txt,\n llms_full_txt: (generate?.llms_full_txt as boolean) ?? DEFAULTS.generate.llms_full_txt,\n ai_context: (generate?.ai_context as boolean) ?? DEFAULTS.generate.ai_context,\n output_dir: (generate?.output_dir as string) ?? DEFAULTS.generate.output_dir,\n sections: generate?.sections as DocsReadyConfig[\"generate\"][\"sections\"],\n ai_context_config: generate?.ai_context_config as DocsReadyConfig[\"generate\"][\"ai_context_config\"],\n },\n guard: {\n npm_packages: (guard?.npm_packages as DocsReadyConfig[\"guard\"][\"npm_packages\"]) ?? DEFAULTS.guard.npm_packages,\n github_releases: (guard?.github_releases as DocsReadyConfig[\"guard\"][\"github_releases\"]) ?? DEFAULTS.guard.github_releases,\n endpoints: (guard?.endpoints as DocsReadyConfig[\"guard\"][\"endpoints\"]) ?? DEFAULTS.guard.endpoints,\n readme_scans: (guard?.readme_scans as DocsReadyConfig[\"guard\"][\"readme_scans\"]) ?? DEFAULTS.guard.readme_scans,\n workflow: {\n enabled: (guardWorkflow.enabled as boolean) ?? DEFAULTS.guard.workflow.enabled,\n schedule: (guardWorkflow.schedule as string) ?? DEFAULTS.guard.workflow.schedule,\n create_issues: (guardWorkflow.create_issues as boolean) ?? DEFAULTS.guard.workflow.create_issues,\n labels: (guardWorkflow.labels as string[]) ?? DEFAULTS.guard.workflow.labels,\n },\n },\n deploy: {\n platform: (deploy?.platform as DocsReadyConfig[\"deploy\"][\"platform\"]) ?? DEFAULTS.deploy.platform,\n cors: {\n enabled: ((deploy?.cors as Record<string, unknown>)?.enabled as boolean) ?? DEFAULTS.deploy.cors.enabled,\n origins: ((deploy?.cors as Record<string, unknown>)?.origins as string[]) ?? DEFAULTS.deploy.cors.origins,\n },\n },\n validate: {\n max_tokens: (validateConf?.max_tokens as number) ?? DEFAULTS.validate.max_tokens,\n check_links: (validateConf?.check_links as boolean) ?? DEFAULTS.validate.check_links,\n check_coverage: (validateConf?.check_coverage as boolean) ?? DEFAULTS.validate.check_coverage,\n coverage_threshold: (validateConf?.coverage_threshold as number) ?? DEFAULTS.validate.coverage_threshold,\n },\n };\n}\n"],"mappings":"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","names":[]}