sourcey 3.3.10 → 3.4.1

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.
Files changed (58) hide show
  1. package/README.md +19 -3
  2. package/dist/client/scroll-tracker.js +8 -1
  3. package/dist/client/search.js +26 -7
  4. package/dist/components/layout/Head.d.ts.map +1 -1
  5. package/dist/components/layout/Head.js +1 -1
  6. package/dist/components/layout/Header.d.ts.map +1 -1
  7. package/dist/components/layout/Header.js +2 -2
  8. package/dist/components/layout/Page.d.ts.map +1 -1
  9. package/dist/components/layout/Page.js +6 -5
  10. package/dist/components/layout/Sidebar.d.ts.map +1 -1
  11. package/dist/components/layout/Sidebar.js +11 -2
  12. package/dist/components/layout/TableOfContents.d.ts.map +1 -1
  13. package/dist/components/layout/TableOfContents.js +3 -2
  14. package/dist/components/mcp/AnnotationBadges.d.ts +15 -0
  15. package/dist/components/mcp/AnnotationBadges.d.ts.map +1 -0
  16. package/dist/components/mcp/AnnotationBadges.js +10 -0
  17. package/dist/components/mcp/McpConnection.d.ts +10 -0
  18. package/dist/components/mcp/McpConnection.d.ts.map +1 -0
  19. package/dist/components/mcp/McpConnection.js +32 -0
  20. package/dist/components/mcp/McpEndpointBar.d.ts +8 -0
  21. package/dist/components/mcp/McpEndpointBar.d.ts.map +1 -0
  22. package/dist/components/mcp/McpEndpointBar.js +16 -0
  23. package/dist/components/mcp/McpReturns.d.ts +18 -0
  24. package/dist/components/mcp/McpReturns.d.ts.map +1 -0
  25. package/dist/components/mcp/McpReturns.js +33 -0
  26. package/dist/components/openapi/Operation.d.ts.map +1 -1
  27. package/dist/components/openapi/Operation.js +5 -1
  28. package/dist/config.d.ts +11 -0
  29. package/dist/config.d.ts.map +1 -1
  30. package/dist/config.js +12 -3
  31. package/dist/core/markdown-loader.d.ts.map +1 -1
  32. package/dist/core/markdown-loader.js +7 -2
  33. package/dist/core/mcp-normalizer.d.ts +11 -0
  34. package/dist/core/mcp-normalizer.d.ts.map +1 -0
  35. package/dist/core/mcp-normalizer.js +382 -0
  36. package/dist/core/search-indexer.d.ts +3 -1
  37. package/dist/core/search-indexer.d.ts.map +1 -1
  38. package/dist/core/search-indexer.js +7 -3
  39. package/dist/core/types.d.ts +26 -2
  40. package/dist/core/types.d.ts.map +1 -1
  41. package/dist/dev-server.d.ts.map +1 -1
  42. package/dist/dev-server.js +47 -9
  43. package/dist/index.d.ts.map +1 -1
  44. package/dist/index.js +22 -10
  45. package/dist/renderer/html-builder.d.ts +2 -0
  46. package/dist/renderer/html-builder.d.ts.map +1 -1
  47. package/dist/renderer/html-builder.js +15 -0
  48. package/dist/renderer/llms.d.ts +6 -0
  49. package/dist/renderer/llms.d.ts.map +1 -0
  50. package/dist/renderer/llms.js +247 -0
  51. package/dist/themes/default/main.css +3 -0
  52. package/dist/themes/default/sourcey.css +101 -23
  53. package/dist/utils/icons.d.ts +4 -0
  54. package/dist/utils/icons.d.ts.map +1 -1
  55. package/dist/utils/icons.js +6 -0
  56. package/dist/utils/markdown.d.ts.map +1 -1
  57. package/dist/utils/markdown.js +97 -1
  58. package/package.json +6 -2
@@ -7,6 +7,7 @@ import { loadSpec } from "./core/loader.js";
7
7
  import { convertToOpenApi3 } from "./core/converter.js";
8
8
  import { parseSpec } from "./core/parser.js";
9
9
  import { normalizeSpec } from "./core/normalizer.js";
10
+ import { normalizeMcpSpec } from "./core/mcp-normalizer.js";
10
11
  import { buildNavFromSpec, buildNavFromPages, buildSiteNavigation, withActivePage } from "./core/navigation.js";
11
12
  import { loadMarkdownPage, slugFromPath } from "./core/markdown-loader.js";
12
13
  import { loadDoxygenTab } from "./core/doxygen-loader.js";
@@ -40,6 +41,8 @@ export async function startDevServer(options) {
40
41
  for (const tab of config.tabs) {
41
42
  if (tab.openapi)
42
43
  watchPaths.push(tab.openapi);
44
+ if (tab.mcp)
45
+ watchPaths.push(tab.mcp);
43
46
  if (tab.doxygen)
44
47
  watchPaths.push(tab.doxygen.xml);
45
48
  if (tab.groups) {
@@ -57,6 +60,9 @@ export async function startDevServer(options) {
57
60
  if (tab.openapi) {
58
61
  map.set(resolve(tab.openapi), { kind: "openapi", tabSlug: tab.slug, specPath: tab.openapi });
59
62
  }
63
+ if (tab.mcp) {
64
+ map.set(resolve(tab.mcp), { kind: "mcp", tabSlug: tab.slug, specPath: tab.mcp });
65
+ }
60
66
  if (tab.doxygen) {
61
67
  map.set(resolve(tab.doxygen.xml), { kind: "doxygen", tabSlug: tab.slug, xmlDir: tab.doxygen.xml });
62
68
  }
@@ -183,6 +189,29 @@ export async function startDevServer(options) {
183
189
  data.siteTabs[idx] = navTab;
184
190
  }
185
191
  }
192
+ else if (content.kind === "mcp") {
193
+ log(`reparsing mcp spec ${shortPath(content.specPath)}`);
194
+ const { parse } = await import("mcp-parser");
195
+ const mcpSpec = await parse(content.specPath);
196
+ const spec = normalizeMcpSpec(mcpSpec);
197
+ if (cache !== snapshot)
198
+ return;
199
+ data.specsBySlug.set(content.tabSlug, spec);
200
+ const pageKey = tabPath(content.tabSlug, "index.html");
201
+ const existing = data.pageMap.get(pageKey);
202
+ if (existing) {
203
+ existing.currentPage = { kind: "spec", spec };
204
+ existing.spec = spec;
205
+ }
206
+ const tab = config.tabs.find((t) => t.slug === content.tabSlug);
207
+ if (tab) {
208
+ const navTab = buildNavFromSpec(spec, tab.slug);
209
+ navTab.label = tab.label;
210
+ const idx = data.siteTabs.findIndex((t) => t.slug === content.tabSlug);
211
+ if (idx !== -1)
212
+ data.siteTabs[idx] = navTab;
213
+ }
214
+ }
186
215
  const ms = Math.round(performance.now() - start);
187
216
  log(`updated \x1b[2m(${ms}ms)\x1b[0m`);
188
217
  }
@@ -257,7 +286,7 @@ export async function startDevServer(options) {
257
286
  markdownPagesByTab.set(tab.slug, tabPages);
258
287
  }
259
288
  }
260
- return buildSearchIndex(specsBySlug, markdownPagesByTab, navigation);
289
+ return buildSearchIndex(specsBySlug, markdownPagesByTab, navigation, "/", config.search.featured);
261
290
  }
262
291
  const viteConfig = {
263
292
  root: process.cwd(),
@@ -265,6 +294,9 @@ export async function startDevServer(options) {
265
294
  port,
266
295
  strictPort: true,
267
296
  hmr: true,
297
+ fs: {
298
+ allow: [process.cwd(), projectRoot],
299
+ },
268
300
  },
269
301
  plugins: [
270
302
  preact(),
@@ -330,20 +362,26 @@ async function loadSiteData(tabs) {
330
362
  const siteTabs = [];
331
363
  const pageMap = new Map();
332
364
  for (const tab of tabs) {
333
- if (!tab.openapi)
334
- continue;
335
- const loaded = await loadSpec(tab.openapi);
336
- const parsed = await parseSpec(loaded);
337
- const openapi3 = await convertToOpenApi3(parsed);
338
- const spec = normalizeSpec(openapi3);
339
- specsBySlug.set(tab.slug, spec);
365
+ if (tab.openapi) {
366
+ const loaded = await loadSpec(tab.openapi);
367
+ const parsed = await parseSpec(loaded);
368
+ const openapi3 = await convertToOpenApi3(parsed);
369
+ const spec = normalizeSpec(openapi3);
370
+ specsBySlug.set(tab.slug, spec);
371
+ }
372
+ else if (tab.mcp) {
373
+ const { parse } = await import("mcp-parser");
374
+ const mcpSpec = await parse(tab.mcp);
375
+ const spec = normalizeMcpSpec(mcpSpec);
376
+ specsBySlug.set(tab.slug, spec);
377
+ }
340
378
  }
341
379
  const primarySpec = specsBySlug.values().next().value ?? {
342
380
  info: { title: "", version: "", description: "" },
343
381
  servers: [], tags: [], operations: [], schemas: {}, securitySchemes: {}, webhooks: [],
344
382
  };
345
383
  for (const tab of tabs) {
346
- if (tab.openapi) {
384
+ if (tab.openapi || tab.mcp) {
347
385
  const spec = specsBySlug.get(tab.slug);
348
386
  const navTab = buildNavFromSpec(spec, tab.slug);
349
387
  navTab.label = tab.label;
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAQA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAC;AAEtD,OAAO,KAAK,EAAE,cAAc,EAAe,MAAM,aAAa,CAAC;AAc/D,MAAM,WAAW,YAAY;IAC3B,UAAU,EAAE,MAAM,CAAC;IACnB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,SAAS,CAAC,EAAE,OAAO,CAAC;CACrB;AAED,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,cAAc,CAAC;IACrB,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;CACnB;AAED;;;GAGG;AACH,wBAAsB,SAAS,CAAC,OAAO,EAAE,YAAY,GAAG,OAAO,CAAC,WAAW,CAAC,CAY3E;AAMD,MAAM,WAAW,gBAAgB;IAC/B,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,MAAM,CAAC,EAAE,cAAc,CAAC;IACxB,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,UAAU,CAAC,EAAE,OAAO,CAAC;CACtB;AAED,MAAM,WAAW,eAAe;IAC9B,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;IAClB,wDAAwD;IACxD,MAAM,CAAC,EAAE,GAAG,CAAC,MAAM,EAAE,cAAc,CAAC,CAAC;CACtC;AAED,wBAAsB,aAAa,CAAC,OAAO,GAAE,gBAAqB,GAAG,OAAO,CAAC,eAAe,CAAC,CA0G5F;AAgKD,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAG3C,YAAY,EACV,cAAc,EACd,mBAAmB,EACnB,aAAa,EACb,gBAAgB,EAChB,mBAAmB,EACnB,qBAAqB,EACrB,kBAAkB,GACnB,MAAM,iBAAiB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AASA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAC;AAEtD,OAAO,KAAK,EAAE,cAAc,EAAe,MAAM,aAAa,CAAC;AAe/D,MAAM,WAAW,YAAY;IAC3B,UAAU,EAAE,MAAM,CAAC;IACnB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,SAAS,CAAC,EAAE,OAAO,CAAC;CACrB;AAED,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,cAAc,CAAC;IACrB,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;CACnB;AAED;;;GAGG;AACH,wBAAsB,SAAS,CAAC,OAAO,EAAE,YAAY,GAAG,OAAO,CAAC,WAAW,CAAC,CAY3E;AAMD,MAAM,WAAW,gBAAgB;IAC/B,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,MAAM,CAAC,EAAE,cAAc,CAAC;IACxB,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,UAAU,CAAC,EAAE,OAAO,CAAC;CACtB;AAED,MAAM,WAAW,eAAe;IAC9B,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;IAClB,wDAAwD;IACxD,MAAM,CAAC,EAAE,GAAG,CAAC,MAAM,EAAE,cAAc,CAAC,CAAC;CACtC;AAED,wBAAsB,aAAa,CAAC,OAAO,GAAE,gBAAqB,GAAG,OAAO,CAAC,eAAe,CAAC,CAoH5F;AAgKD,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAG3C,YAAY,EACV,cAAc,EACd,mBAAmB,EACnB,aAAa,EACb,gBAAgB,EAChB,mBAAmB,EACnB,qBAAqB,EACrB,kBAAkB,GACnB,MAAM,iBAAiB,CAAC"}
package/dist/index.js CHANGED
@@ -4,6 +4,7 @@ import { loadSpec } from "./core/loader.js";
4
4
  import { convertToOpenApi3 } from "./core/converter.js";
5
5
  import { parseSpec } from "./core/parser.js";
6
6
  import { normalizeSpec } from "./core/normalizer.js";
7
+ import { normalizeMcpSpec } from "./core/mcp-normalizer.js";
7
8
  import { buildSite as buildSiteHtml } from "./renderer/html-builder.js";
8
9
  import { tabPath } from "./config.js";
9
10
  import { loadConfig, configFromSpec } from "./config.js";
@@ -11,6 +12,7 @@ import { loadMarkdownPage, slugFromPath } from "./core/markdown-loader.js";
11
12
  import { loadDoxygenTab } from "./core/doxygen-loader.js";
12
13
  import { buildNavFromSpec, buildNavFromPages, buildSiteNavigation } from "./core/navigation.js";
13
14
  import { buildSearchIndex } from "./core/search-indexer.js";
15
+ import { generateLlmsTxt, generateLlmsFullTxt } from "./renderer/llms.js";
14
16
  /**
15
17
  * Build API documentation from a single OpenAPI/Swagger spec.
16
18
  * Wraps the spec in a single-tab site and renders through the modern layout.
@@ -33,15 +35,21 @@ export async function buildSiteDocs(options = {}) {
33
35
  const sitePages = [];
34
36
  const siteTabs = [];
35
37
  const specsBySlug = new Map();
36
- // Load all specs
38
+ // Load all specs (OpenAPI and MCP)
37
39
  for (const tab of tabs) {
38
- if (!tab.openapi)
39
- continue;
40
- const loaded = await loadSpec(tab.openapi);
41
- const parsed = await parseSpec(loaded);
42
- const openapi3 = await convertToOpenApi3(parsed);
43
- const spec = normalizeSpec(openapi3);
44
- specsBySlug.set(tab.slug, spec);
40
+ if (tab.openapi) {
41
+ const loaded = await loadSpec(tab.openapi);
42
+ const parsed = await parseSpec(loaded);
43
+ const openapi3 = await convertToOpenApi3(parsed);
44
+ const spec = normalizeSpec(openapi3);
45
+ specsBySlug.set(tab.slug, spec);
46
+ }
47
+ else if (tab.mcp) {
48
+ const { parse } = await import("mcp-parser");
49
+ const mcpSpec = await parse(tab.mcp);
50
+ const spec = normalizeMcpSpec(mcpSpec);
51
+ specsBySlug.set(tab.slug, spec);
52
+ }
45
53
  }
46
54
  // Primary spec for SpecContext on markdown pages
47
55
  const primarySpec = specsBySlug.values().next().value ?? createMinimalSpec();
@@ -49,7 +57,7 @@ export async function buildSiteDocs(options = {}) {
49
57
  const site = await buildSiteConfig(config);
50
58
  // Process all tabs
51
59
  for (const tab of tabs) {
52
- if (tab.openapi) {
60
+ if (tab.openapi || tab.mcp) {
53
61
  const spec = specsBySlug.get(tab.slug);
54
62
  const navTab = buildNavFromSpec(spec, tab.slug);
55
63
  navTab.label = tab.label;
@@ -110,10 +118,14 @@ export async function buildSiteDocs(options = {}) {
110
118
  markdownPagesByTab.set(tab.slug, tabPages);
111
119
  }
112
120
  }
113
- const searchIndex = buildSearchIndex(specsBySlug, markdownPagesByTab, navigation);
121
+ const searchIndex = buildSearchIndex(specsBySlug, markdownPagesByTab, navigation, "/", config.search.featured);
122
+ const llmsTxt = generateLlmsTxt(sitePages, navigation, site);
123
+ const llmsFullTxt = generateLlmsFullTxt(sitePages, navigation, site);
114
124
  if (!options.skipWrite) {
115
125
  await buildSiteHtml(sitePages, navigation, outputDir, site, {
116
126
  searchIndex,
127
+ llmsTxt,
128
+ llmsFullTxt,
117
129
  embeddable: options.embeddable,
118
130
  });
119
131
  }
@@ -18,5 +18,7 @@ export interface SitePage {
18
18
  export declare function buildSite(pages: SitePage[], navigation: SiteNavigation, outputDir: string, site: SiteConfig, options?: {
19
19
  embeddable?: boolean;
20
20
  searchIndex?: string;
21
+ llmsTxt?: string;
22
+ llmsFullTxt?: string;
21
23
  }): Promise<BuildOutput>;
22
24
  //# sourceMappingURL=html-builder.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"html-builder.d.ts","sourceRoot":"","sources":["../../src/renderer/html-builder.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAC;AACvD,OAAO,KAAK,EAAiB,WAAW,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAC3E,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AA0B5D,MAAM,WAAW,WAAW;IAC1B,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,QAAQ;IACvB,UAAU,EAAE,MAAM,CAAC;IACnB,WAAW,EAAE,WAAW,CAAC;IACzB,IAAI,EAAE,cAAc,CAAC;IACrB,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED;;GAEG;AACH,wBAAsB,SAAS,CAC7B,KAAK,EAAE,QAAQ,EAAE,EACjB,UAAU,EAAE,cAAc,EAC1B,SAAS,EAAE,MAAM,EACjB,IAAI,EAAE,UAAU,EAChB,OAAO,CAAC,EAAE;IAAE,UAAU,CAAC,EAAE,OAAO,CAAC;IAAC,WAAW,CAAC,EAAE,MAAM,CAAA;CAAE,GACvD,OAAO,CAAC,WAAW,CAAC,CAoCtB"}
1
+ {"version":3,"file":"html-builder.d.ts","sourceRoot":"","sources":["../../src/renderer/html-builder.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAC;AACvD,OAAO,KAAK,EAAiB,WAAW,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAC3E,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AA0B5D,MAAM,WAAW,WAAW;IAC1B,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,QAAQ;IACvB,UAAU,EAAE,MAAM,CAAC;IACnB,WAAW,EAAE,WAAW,CAAC;IACzB,IAAI,EAAE,cAAc,CAAC;IACrB,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED;;GAEG;AACH,wBAAsB,SAAS,CAC7B,KAAK,EAAE,QAAQ,EAAE,EACjB,UAAU,EAAE,cAAc,EAC1B,SAAS,EAAE,MAAM,EACjB,IAAI,EAAE,UAAU,EAChB,OAAO,CAAC,EAAE;IAAE,UAAU,CAAC,EAAE,OAAO,CAAC;IAAC,WAAW,CAAC,EAAE,MAAM,CAAC;IAAC,OAAO,CAAC,EAAE,MAAM,CAAC;IAAC,WAAW,CAAC,EAAE,MAAM,CAAA;CAAE,GAC/F,OAAO,CAAC,WAAW,CAAC,CAsDtB"}
@@ -60,6 +60,21 @@ export async function buildSite(pages, navigation, outputDir, site, options) {
60
60
  if (options?.searchIndex) {
61
61
  await writeFile(resolve(resolvedDir, "search-index.json"), options.searchIndex, "utf-8");
62
62
  }
63
+ if (options?.llmsTxt) {
64
+ await writeFile(resolve(resolvedDir, "llms.txt"), options.llmsTxt, "utf-8");
65
+ }
66
+ if (options?.llmsFullTxt) {
67
+ await writeFile(resolve(resolvedDir, "llms-full.txt"), options.llmsFullTxt, "utf-8");
68
+ }
69
+ const urls = pages.map(p => ` <url><loc>${p.outputPath}</loc></url>`);
70
+ const sitemap = [
71
+ `<?xml version="1.0" encoding="UTF-8"?>`,
72
+ `<!-- Generated by Sourcey https://sourcey.com -->`,
73
+ `<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">`,
74
+ ...urls,
75
+ `</urlset>`,
76
+ ].join("\n");
77
+ await writeFile(resolve(resolvedDir, "sitemap.xml"), sitemap, "utf-8");
63
78
  return { htmlPath: resolve(resolvedDir, "index.html"), outputDir: resolvedDir };
64
79
  }
65
80
  /**
@@ -0,0 +1,6 @@
1
+ import type { SiteNavigation } from "../core/navigation.js";
2
+ import type { SiteConfig } from "./context.js";
3
+ import type { SitePage } from "./html-builder.js";
4
+ export declare function generateLlmsTxt(pages: SitePage[], navigation: SiteNavigation, site: SiteConfig): string;
5
+ export declare function generateLlmsFullTxt(pages: SitePage[], navigation: SiteNavigation, site: SiteConfig): string;
6
+ //# sourceMappingURL=llms.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"llms.d.ts","sourceRoot":"","sources":["../../src/renderer/llms.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AAG5D,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAC/C,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,mBAAmB,CAAC;AAElD,wBAAgB,eAAe,CAC7B,KAAK,EAAE,QAAQ,EAAE,EACjB,UAAU,EAAE,cAAc,EAC1B,IAAI,EAAE,UAAU,GACf,MAAM,CA4CR;AAED,wBAAgB,mBAAmB,CACjC,KAAK,EAAE,QAAQ,EAAE,EACjB,UAAU,EAAE,cAAc,EAC1B,IAAI,EAAE,UAAU,GACf,MAAM,CA8BR"}
@@ -0,0 +1,247 @@
1
+ import { htmlId } from "../utils/html-id.js";
2
+ export function generateLlmsTxt(pages, navigation, site) {
3
+ const lines = [];
4
+ const title = resolveSiteTitle(pages, site);
5
+ const summary = resolveSiteSummary(pages, site);
6
+ lines.push(`# ${title}`);
7
+ lines.push("");
8
+ if (summary) {
9
+ lines.push(`> ${firstLine(summary)}`);
10
+ lines.push("");
11
+ }
12
+ for (const tab of navigation.tabs) {
13
+ const tabPages = pages.filter((page) => page.tabSlug === tab.slug);
14
+ if (!tabPages.length)
15
+ continue;
16
+ lines.push(`## ${tab.label}`);
17
+ lines.push("");
18
+ for (const page of tabPages) {
19
+ if (page.currentPage.kind === "markdown" && page.currentPage.markdown) {
20
+ const doc = page.currentPage.markdown;
21
+ const desc = doc.description || excerpt(stripHtml(doc.html));
22
+ lines.push(`- [${doc.title}](${page.outputPath})${desc ? `: ${desc}` : ""}`);
23
+ continue;
24
+ }
25
+ const spec = page.currentPage.spec ?? page.spec;
26
+ const overview = spec.info.description ? firstLine(spec.info.description) : `${spec.operations.length} documented operations`;
27
+ lines.push(`- [${spec.info.title}](${page.outputPath})${overview ? `: ${overview}` : ""}`);
28
+ for (const op of spec.operations) {
29
+ if (op.hidden)
30
+ continue;
31
+ const opLabel = op.summary ?? operationDisplayName(op);
32
+ const opSummary = [operationKind(op), firstLine(op.description)].filter(Boolean).join(" — ");
33
+ lines.push(`- [${opLabel}](${page.outputPath}#${operationAnchor(op)})${opSummary ? `: ${opSummary}` : ""}`);
34
+ }
35
+ }
36
+ lines.push("");
37
+ }
38
+ return lines.join("\n");
39
+ }
40
+ export function generateLlmsFullTxt(pages, navigation, site) {
41
+ const lines = [];
42
+ const title = resolveSiteTitle(pages, site);
43
+ const summary = resolveSiteSummary(pages, site);
44
+ lines.push(`# ${title}`);
45
+ lines.push("");
46
+ if (summary) {
47
+ lines.push(summary);
48
+ lines.push("");
49
+ }
50
+ for (const tab of navigation.tabs) {
51
+ const tabPages = pages.filter((page) => page.tabSlug === tab.slug);
52
+ if (!tabPages.length)
53
+ continue;
54
+ lines.push(`## ${tab.label}`);
55
+ lines.push("");
56
+ for (const page of tabPages) {
57
+ if (page.currentPage.kind === "markdown" && page.currentPage.markdown) {
58
+ appendMarkdownPage(lines, page);
59
+ }
60
+ else {
61
+ appendSpecPage(lines, page);
62
+ }
63
+ }
64
+ }
65
+ return lines.join("\n");
66
+ }
67
+ function appendMarkdownPage(lines, page) {
68
+ const doc = page.currentPage.markdown;
69
+ lines.push(`### ${doc.title}`);
70
+ lines.push("");
71
+ lines.push(`Path: \`${page.outputPath}\``);
72
+ lines.push("");
73
+ if (doc.description) {
74
+ lines.push(doc.description);
75
+ lines.push("");
76
+ }
77
+ const body = stripHtml(doc.html);
78
+ if (body) {
79
+ lines.push(body);
80
+ lines.push("");
81
+ }
82
+ }
83
+ function appendSpecPage(lines, page) {
84
+ const spec = page.currentPage.spec ?? page.spec;
85
+ lines.push(`### ${spec.info.title}`);
86
+ lines.push("");
87
+ lines.push(`Path: \`${page.outputPath}\``);
88
+ if (spec.info.version) {
89
+ lines.push(`Version: ${spec.info.version}`);
90
+ }
91
+ lines.push("");
92
+ if (spec.info.description) {
93
+ lines.push(spec.info.description);
94
+ lines.push("");
95
+ }
96
+ if (spec.operations.length) {
97
+ lines.push("#### Operations");
98
+ lines.push("");
99
+ for (const op of spec.operations) {
100
+ if (op.hidden)
101
+ continue;
102
+ appendOperation(lines, op);
103
+ }
104
+ }
105
+ const schemas = Object.entries(spec.schemas);
106
+ if (schemas.length) {
107
+ lines.push("#### Models");
108
+ lines.push("");
109
+ for (const [name, schema] of schemas) {
110
+ const desc = schema.description ? `: ${schema.description}` : "";
111
+ lines.push(`- ${name}${desc}`);
112
+ }
113
+ lines.push("");
114
+ }
115
+ }
116
+ function appendOperation(lines, op) {
117
+ lines.push(`##### ${operationDisplayName(op)}`);
118
+ lines.push("");
119
+ if (op.summary && op.summary !== operationDisplayName(op)) {
120
+ lines.push(`Summary: ${op.summary}`);
121
+ lines.push("");
122
+ }
123
+ if (op.description) {
124
+ lines.push(op.description);
125
+ lines.push("");
126
+ }
127
+ if (op.parameters.length) {
128
+ lines.push("Parameters:");
129
+ for (const param of op.parameters) {
130
+ const req = param.required ? "required" : "optional";
131
+ const type = param.schema ? formatSchemaType(param.schema) : "unknown";
132
+ const desc = param.description ? ` — ${firstLine(param.description)}` : "";
133
+ lines.push(`- \`${param.name}\` (${param.in}, ${type}, ${req})${desc}`);
134
+ }
135
+ lines.push("");
136
+ }
137
+ if (op.requestBody) {
138
+ const mediaTypes = Object.keys(op.requestBody.content);
139
+ if (mediaTypes.length) {
140
+ lines.push(`Request body: ${mediaTypes.join(", ")}`);
141
+ lines.push("");
142
+ }
143
+ }
144
+ if (op.mcpExtras?.outputSchema) {
145
+ lines.push("Returns:");
146
+ lines.push("```json");
147
+ lines.push(JSON.stringify(op.mcpExtras.outputSchema, null, 2));
148
+ lines.push("```");
149
+ lines.push("");
150
+ }
151
+ else if (op.responses.length) {
152
+ lines.push("Responses:");
153
+ for (const response of op.responses) {
154
+ lines.push(`- ${formatResponse(response)}`);
155
+ }
156
+ lines.push("");
157
+ }
158
+ }
159
+ function resolveSiteTitle(pages, site) {
160
+ if (site.name && site.name !== "API Reference")
161
+ return site.name;
162
+ const specPages = pages.filter((page) => page.currentPage.kind === "spec");
163
+ if (specPages.length === 1) {
164
+ const spec = specPages[0].currentPage.spec ?? specPages[0].spec;
165
+ if (spec.info.title)
166
+ return spec.info.title;
167
+ }
168
+ const firstMarkdown = pages.find((page) => page.currentPage.kind === "markdown" && page.currentPage.markdown);
169
+ if (firstMarkdown?.currentPage.markdown?.title)
170
+ return firstMarkdown.currentPage.markdown.title;
171
+ return site.name || "Documentation";
172
+ }
173
+ function resolveSiteSummary(pages, site) {
174
+ const firstMarkdown = pages.find((page) => page.currentPage.kind === "markdown" && page.currentPage.markdown?.description);
175
+ if (firstMarkdown?.currentPage.markdown?.description)
176
+ return firstMarkdown.currentPage.markdown.description;
177
+ const firstSpec = pages.find((page) => page.currentPage.kind === "spec");
178
+ const spec = firstSpec ? (firstSpec.currentPage.spec ?? firstSpec.spec) : undefined;
179
+ if (spec?.info.description)
180
+ return spec.info.description;
181
+ return site.name ? `${site.name} documentation generated by Sourcey.` : undefined;
182
+ }
183
+ function operationDisplayName(op) {
184
+ if (op.mcpExtras?.type === "tool")
185
+ return `TOOL ${op.path}`;
186
+ if (op.mcpExtras?.type === "resource")
187
+ return `RESOURCE ${op.path}`;
188
+ if (op.mcpExtras?.type === "prompt")
189
+ return `PROMPT ${op.path}`;
190
+ return `${op.method.toUpperCase()} ${op.path}`;
191
+ }
192
+ function operationKind(op) {
193
+ if (op.mcpExtras?.type === "tool")
194
+ return "tool";
195
+ if (op.mcpExtras?.type === "resource")
196
+ return "resource";
197
+ if (op.mcpExtras?.type === "prompt")
198
+ return "prompt";
199
+ return `${op.method.toUpperCase()} ${op.path}`;
200
+ }
201
+ function operationAnchor(op) {
202
+ return `operation-${htmlId(op.path)}-${htmlId(op.method)}`;
203
+ }
204
+ function formatResponse(response) {
205
+ const desc = response.description ? firstLine(response.description) : "";
206
+ return desc ? `${response.statusCode}: ${desc}` : response.statusCode;
207
+ }
208
+ function formatSchemaType(schema) {
209
+ if (Array.isArray(schema.type))
210
+ return schema.type.join(" | ");
211
+ if (schema.type)
212
+ return schema.type;
213
+ if (schema.oneOf?.length)
214
+ return schema.oneOf.map(formatSchemaType).join(" | ");
215
+ if (schema.anyOf?.length)
216
+ return schema.anyOf.map(formatSchemaType).join(" | ");
217
+ if (schema.allOf?.length)
218
+ return "object";
219
+ if (schema.properties)
220
+ return "object";
221
+ return "unknown";
222
+ }
223
+ function stripHtml(html) {
224
+ return decodeEntities(html)
225
+ .replace(/<pre[\s\S]*?<\/pre>/gi, (match) => match.replace(/<[^>]+>/g, " "))
226
+ .replace(/<code[\s\S]*?<\/code>/gi, (match) => match.replace(/<[^>]+>/g, " "))
227
+ .replace(/<[^>]+>/g, " ")
228
+ .replace(/\s+/g, " ")
229
+ .trim();
230
+ }
231
+ function decodeEntities(text) {
232
+ return text
233
+ .replace(/&nbsp;/g, " ")
234
+ .replace(/&amp;/g, "&")
235
+ .replace(/&lt;/g, "<")
236
+ .replace(/&gt;/g, ">")
237
+ .replace(/&quot;/g, "\"")
238
+ .replace(/&#39;/g, "'");
239
+ }
240
+ function excerpt(text, max = 140) {
241
+ if (text.length <= max)
242
+ return text;
243
+ return `${text.slice(0, max - 1).trimEnd()}…`;
244
+ }
245
+ function firstLine(text) {
246
+ return text?.split("\n").map((line) => line.trim()).find(Boolean) ?? "";
247
+ }
@@ -65,6 +65,9 @@
65
65
  --method-put: #d97706;
66
66
  --method-delete: #dc2626;
67
67
  --method-patch: #9333ea;
68
+ --method-tool: #9333ea;
69
+ --method-resource: #16a34a;
70
+ --method-prompt: #2563eb;
68
71
 
69
72
  /* Typography */
70
73
  --font-sans: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', system-ui, sans-serif;