sourcey 3.5.10 → 3.6.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 (100) hide show
  1. package/README.md +24 -97
  2. package/dist/adapters/index.d.ts +12 -0
  3. package/dist/adapters/index.d.ts.map +1 -0
  4. package/dist/adapters/index.js +130 -0
  5. package/dist/adapters/mkdocs.d.ts +5 -0
  6. package/dist/adapters/mkdocs.d.ts.map +1 -0
  7. package/dist/adapters/mkdocs.js +367 -0
  8. package/dist/adapters/shared.d.ts +12 -0
  9. package/dist/adapters/shared.d.ts.map +1 -0
  10. package/dist/adapters/shared.js +134 -0
  11. package/dist/adapters/types.d.ts +74 -0
  12. package/dist/adapters/types.d.ts.map +1 -0
  13. package/dist/adapters/types.js +1 -0
  14. package/dist/cli.js +28 -34
  15. package/dist/client/search.js +213 -82
  16. package/dist/client/tabs.js +23 -0
  17. package/dist/components/layout/Head.js +17 -4
  18. package/dist/components/layout/TableOfContents.d.ts.map +1 -1
  19. package/dist/components/layout/TableOfContents.js +4 -2
  20. package/dist/components/openapi/Introduction.d.ts.map +1 -1
  21. package/dist/components/openapi/Introduction.js +16 -1
  22. package/dist/components/openapi/Security.d.ts.map +1 -1
  23. package/dist/components/openapi/Security.js +18 -2
  24. package/dist/config.d.ts +101 -0
  25. package/dist/config.d.ts.map +1 -1
  26. package/dist/config.js +95 -143
  27. package/dist/core/api-rendering.d.ts +47 -0
  28. package/dist/core/api-rendering.d.ts.map +1 -0
  29. package/dist/core/api-rendering.js +76 -0
  30. package/dist/core/doxygen-loader.d.ts.map +1 -1
  31. package/dist/core/doxygen-loader.js +36 -17
  32. package/dist/core/godoc-loader.d.ts.map +1 -1
  33. package/dist/core/godoc-loader.js +17 -6
  34. package/dist/core/markdown-loader.d.ts +14 -0
  35. package/dist/core/markdown-loader.d.ts.map +1 -1
  36. package/dist/core/markdown-loader.js +70 -26
  37. package/dist/core/mcp-normalizer.d.ts.map +1 -1
  38. package/dist/core/mcp-normalizer.js +48 -34
  39. package/dist/core/navigation.d.ts.map +1 -1
  40. package/dist/core/navigation.js +8 -7
  41. package/dist/core/parser.d.ts.map +1 -1
  42. package/dist/core/parser.js +75 -4
  43. package/dist/core/rustdoc-introspector.d.ts +29 -0
  44. package/dist/core/rustdoc-introspector.d.ts.map +1 -0
  45. package/dist/core/rustdoc-introspector.js +236 -0
  46. package/dist/core/rustdoc-loader.d.ts +25 -0
  47. package/dist/core/rustdoc-loader.d.ts.map +1 -0
  48. package/dist/core/rustdoc-loader.js +469 -0
  49. package/dist/core/rustdoc-render.d.ts +76 -0
  50. package/dist/core/rustdoc-render.d.ts.map +1 -0
  51. package/dist/core/rustdoc-render.js +785 -0
  52. package/dist/core/rustdoc-types.d.ts +314 -0
  53. package/dist/core/rustdoc-types.d.ts.map +1 -0
  54. package/dist/core/rustdoc-types.js +18 -0
  55. package/dist/core/search-indexer.d.ts +10 -0
  56. package/dist/core/search-indexer.d.ts.map +1 -1
  57. package/dist/core/search-indexer.js +6 -1
  58. package/dist/core/sourcey-rustdoc/Cargo.lock +705 -0
  59. package/dist/core/sourcey-rustdoc/Cargo.toml +38 -0
  60. package/dist/core/sourcey-rustdoc/README.md +59 -0
  61. package/dist/core/sourcey-rustdoc/src/diagnostics.rs +42 -0
  62. package/dist/core/sourcey-rustdoc/src/doctest.rs +268 -0
  63. package/dist/core/sourcey-rustdoc/src/extract.rs +492 -0
  64. package/dist/core/sourcey-rustdoc/src/lib.rs +58 -0
  65. package/dist/core/sourcey-rustdoc/src/links.rs +126 -0
  66. package/dist/core/sourcey-rustdoc/src/main.rs +256 -0
  67. package/dist/core/sourcey-rustdoc/src/signature.rs +415 -0
  68. package/dist/core/sourcey-rustdoc/src/spec.rs +390 -0
  69. package/dist/core/sourcey-rustdoc/tests/format_version.rs +45 -0
  70. package/dist/dev-server.d.ts.map +1 -1
  71. package/dist/dev-server.js +109 -33
  72. package/dist/index.d.ts +4 -0
  73. package/dist/index.d.ts.map +1 -1
  74. package/dist/index.js +26 -14
  75. package/dist/init.d.ts.map +1 -1
  76. package/dist/init.js +21 -16
  77. package/dist/renderer/changelog-feed.d.ts.map +1 -1
  78. package/dist/renderer/changelog-feed.js +27 -19
  79. package/dist/renderer/html-builder.d.ts.map +1 -1
  80. package/dist/renderer/html-builder.js +4 -3
  81. package/dist/renderer/llms.js +57 -2
  82. package/dist/site-assembly.d.ts +5 -0
  83. package/dist/site-assembly.d.ts.map +1 -1
  84. package/dist/site-assembly.js +161 -25
  85. package/dist/themes/default/sourcey.css +300 -0
  86. package/dist/utils/code-samples.d.ts +1 -0
  87. package/dist/utils/code-samples.d.ts.map +1 -1
  88. package/dist/utils/code-samples.js +30 -22
  89. package/dist/utils/html.d.ts +10 -0
  90. package/dist/utils/html.d.ts.map +1 -0
  91. package/dist/utils/html.js +152 -0
  92. package/dist/utils/http.d.ts +0 -2
  93. package/dist/utils/http.d.ts.map +1 -1
  94. package/dist/utils/http.js +19 -34
  95. package/dist/utils/markdown.d.ts.map +1 -1
  96. package/dist/utils/markdown.js +115 -39
  97. package/dist/vite-plugin.d.ts +2 -0
  98. package/dist/vite-plugin.d.ts.map +1 -1
  99. package/dist/vite-plugin.js +71 -1
  100. package/package.json +11 -5
@@ -0,0 +1,76 @@
1
+ /**
2
+ * Shared API-documentation rendering primitives.
3
+ *
4
+ * Introduced by the rustdoc adapter and designed to be reusable by the godoc
5
+ * and doxygen adapters in a follow-on retrofit. Class names mirror rustdoc's
6
+ * DOM so deep-links from a rustdoc URL line up with sourcey output.
7
+ */
8
+ import { escapeAttr as sharedEscapeAttr, escapeHtml as sharedEscapeHtml } from "../utils/html.js";
9
+ export function apiStabilityCallout(input) {
10
+ switch (input.kind) {
11
+ case "unstable": {
12
+ const issue = input.issueId
13
+ ? ` (<a href="${escapeAttr((input.issueBaseUrl ?? "https://github.com/rust-lang/rust/issues/") +
14
+ String(input.issueId))}">#${input.issueId}</a>)`
15
+ : "";
16
+ const feature = input.featureName ? ` <code>${escapeHtml(input.featureName)}</code>` : "";
17
+ return `<div class="stab unstable api-stab api-stab-unstable">🔬 This is a nightly-only experimental API.${feature}${issue}</div>`;
18
+ }
19
+ case "deprecated": {
20
+ const since = input.since ? ` since ${escapeHtml(input.since)}` : "";
21
+ const reason = input.reason ? `: ${escapeHtml(input.reason)}` : "";
22
+ return `<div class="stab deprecated api-stab api-stab-deprecated">👎 Deprecated${since}${reason}</div>`;
23
+ }
24
+ case "portability": {
25
+ const feature = input.featureName
26
+ ? ` crate feature <code>${escapeHtml(input.featureName)}</code>`
27
+ : "";
28
+ return `<div class="stab portability api-stab api-stab-portability">Available on${feature} only.</div>`;
29
+ }
30
+ case "non_exhaustive":
31
+ return `<div class="stab api-stab api-stab-non-exhaustive">This is marked <code>#[non_exhaustive]</code>; new variants or fields may be added in future versions.</div>`;
32
+ case "must_use": {
33
+ const reason = input.reason ? `: ${escapeHtml(input.reason)}` : "";
34
+ return `<div class="stab api-stab api-stab-must-use"><code>#[must_use]</code>${reason}</div>`;
35
+ }
36
+ }
37
+ }
38
+ export function apiItemInfoRow(input) {
39
+ const parts = [];
40
+ if (input.since) {
41
+ parts.push(`<span class="since api-since">${escapeHtml(input.since)}</span>`);
42
+ }
43
+ if (input.sourceHref) {
44
+ parts.push(`<a class="srclink api-srclink" href="${escapeAttr(input.sourceHref)}">${escapeHtml(input.sourceLabel ?? "Source")}</a>`);
45
+ }
46
+ if (parts.length === 0)
47
+ return "";
48
+ return `<div class="rightside api-rightside">${parts.join(" · ")}</div>`;
49
+ }
50
+ export function apiSymbolLink(input) {
51
+ const title = input.title ? ` title="${escapeAttr(input.title)}"` : "";
52
+ return `<a class="${escapeAttr(input.kind)} api-symbol-link"${title} href="${escapeAttr(input.href)}">${escapeHtml(input.text)}</a>`;
53
+ }
54
+ export function apiImplToggle(input) {
55
+ return apiToggle({
56
+ ...input,
57
+ cssClass: "api-toggle api-impl-toggle",
58
+ });
59
+ }
60
+ function apiToggle(input) {
61
+ const openAttr = input.open === false ? "" : " open";
62
+ return `<details class="${escapeAttr(input.cssClass)}"${openAttr}><summary class="api-toggle-summary">${input.summary}</summary><div class="api-toggle-body">${input.body}</div></details>`;
63
+ }
64
+ export function apiSectionAnchor(input) {
65
+ const cls = input.className ? ` ${input.className}` : "";
66
+ return (`<h${input.level} id="${escapeAttr(input.id)}" class="section-header api-section-header${cls}">` +
67
+ `${escapeHtml(input.text)}` +
68
+ ` <a class="anchor api-anchor" href="#${escapeAttr(input.id)}">§</a>` +
69
+ `</h${input.level}>`);
70
+ }
71
+ export function escapeHtml(value) {
72
+ return sharedEscapeHtml(value);
73
+ }
74
+ export function escapeAttr(value) {
75
+ return sharedEscapeAttr(value);
76
+ }
@@ -1 +1 @@
1
- {"version":3,"file":"doxygen-loader.d.ts","sourceRoot":"","sources":["../../src/core/doxygen-loader.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAqB,qBAAqB,EAAE,MAAM,cAAc,CAAC;AAE7E,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,sBAAsB,CAAC;AACzD,OAAO,KAAK,EAAE,OAAO,EAA6B,MAAM,iBAAiB,CAAC;AAM1E,MAAM,WAAW,aAAa;IAC5B,KAAK,EAAE,GAAG,CAAC,MAAM,EAAE,YAAY,CAAC,CAAC;IACjC,MAAM,EAAE,OAAO,CAAC;CACjB;AAED,wBAAgB,2BAA2B,CAAC,WAAW,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,MAAM,CAMzF;AAED,wBAAgB,kCAAkC,CAAC,WAAW,EAAE,MAAM,GAAG,MAAM,CAoB9E;AAED,wBAAgB,+BAA+B,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAIxE;AAED,wBAAgB,2BAA2B,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAShE;AAED,wBAAgB,gCAAgC,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAcrE;AAED,wBAAsB,cAAc,CAClC,MAAM,EAAE,qBAAqB,EAC7B,OAAO,EAAE,MAAM,EACf,QAAQ,EAAE,MAAM,GACf,OAAO,CAAC,aAAa,CAAC,CAkGxB"}
1
+ {"version":3,"file":"doxygen-loader.d.ts","sourceRoot":"","sources":["../../src/core/doxygen-loader.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAqB,qBAAqB,EAAE,MAAM,cAAc,CAAC;AAO7E,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,sBAAsB,CAAC;AACzD,OAAO,KAAK,EAAE,OAAO,EAA6B,MAAM,iBAAiB,CAAC;AAM1E,MAAM,WAAW,aAAa;IAC5B,KAAK,EAAE,GAAG,CAAC,MAAM,EAAE,YAAY,CAAC,CAAC;IACjC,MAAM,EAAE,OAAO,CAAC;CACjB;AAED,wBAAgB,2BAA2B,CAAC,WAAW,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,MAAM,CAMzF;AAED,wBAAgB,kCAAkC,CAAC,WAAW,EAAE,MAAM,GAAG,MAAM,CAoB9E;AAED,wBAAgB,+BAA+B,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAOxE;AAED,wBAAgB,2BAA2B,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAOhE;AAED,wBAAgB,gCAAgC,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAoBrE;AAED,wBAAsB,cAAc,CAClC,MAAM,EAAE,qBAAqB,EAC7B,OAAO,EAAE,MAAM,EACf,QAAQ,EAAE,MAAM,GACf,OAAO,CAAC,aAAa,CAAC,CA4GxB"}
@@ -1,5 +1,5 @@
1
1
  import { generate } from "moxygen";
2
- import { renderMarkdown, extractHeadings, extractFirstParagraph, stripMarkdownLinks } from "../utils/markdown.js";
2
+ import { renderMarkdown, extractHeadings, extractFirstParagraph, stripMarkdownLinks, } from "../utils/markdown.js";
3
3
  export function normalizeDoxygenDescription(description, markdown) {
4
4
  if (!description)
5
5
  return "";
@@ -35,9 +35,7 @@ export function rewriteGeneratedDoxygenHref(href) {
35
35
  const [path, hash] = href.split("#", 2);
36
36
  if (!path.startsWith("api_") || !path.endsWith(".md"))
37
37
  return href;
38
- const slug = path
39
- .slice("api_".length, -".md".length)
40
- .replace(/--/g, "-");
38
+ const slug = path.slice("api_".length, -".md".length).replace(/--/g, "-");
41
39
  return `${slug}.html${hash ? `#${hash}` : ""}`;
42
40
  }
43
41
  export function rewriteGeneratedDoxygenHtmlLinks(html) {
@@ -56,11 +54,19 @@ export function rewriteGeneratedDoxygenHtmlLinks(html) {
56
54
  });
57
55
  }
58
56
  export async function loadDoxygenTab(config, tabSlug, tabLabel) {
59
- const generated = await generate({
60
- directory: config.xml,
61
- language: config.language,
62
- quiet: true,
63
- });
57
+ let generated;
58
+ try {
59
+ generated = await generate({
60
+ directory: config.xml,
61
+ language: config.language,
62
+ sourceUrl: config.sourceUrl,
63
+ quiet: true,
64
+ });
65
+ }
66
+ catch (error) {
67
+ const message = error instanceof Error ? error.message : String(error);
68
+ throw new Error(`Failed to load Doxygen XML from ${config.xml}: ${message}`);
69
+ }
64
70
  const pages = new Map();
65
71
  const groupMap = new Map();
66
72
  for (const page of generated) {
@@ -79,6 +85,7 @@ export async function loadDoxygenTab(config, tabSlug, tabLabel) {
79
85
  headings,
80
86
  sourcePath: `api/${page.slug}.md`,
81
87
  editPath: page.kind === "group" ? `api/${page.slug}.md` : null,
88
+ searchEntries: page.searchEntries,
82
89
  });
83
90
  // Group by the last segment of namespace (e.g. "icy::http" -> "http")
84
91
  const groupKey = page.module ?? lastSegment(page.namespace) ?? "API";
@@ -198,7 +205,9 @@ function buildRichIndex(groupMap, pages) {
198
205
  cards.push(`<a href="${href}" class="card-item">` +
199
206
  `<div class="card-item-inner">` +
200
207
  `<h3 class="card-item-title">${escHtml(page.title)}</h3>` +
201
- (desc ? `<div class="card-item-content"><p>${escHtml(desc)}</p>${meta}</div>` : `<div class="card-item-content">${meta}</div>`) +
208
+ (desc
209
+ ? `<div class="card-item-content"><p>${escHtml(desc)}</p>${meta}</div>`
210
+ : `<div class="card-item-content">${meta}</div>`) +
202
211
  `</div></a>`);
203
212
  }
204
213
  // Also include groups without a group page (orphan namespaces)
@@ -278,15 +287,25 @@ function lastSegment(name) {
278
287
  return parts[parts.length - 1] || name;
279
288
  }
280
289
  function escHtml(s) {
281
- return s.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;");
290
+ return s
291
+ .replace(/&/g, "&amp;")
292
+ .replace(/</g, "&lt;")
293
+ .replace(/>/g, "&gt;")
294
+ .replace(/"/g, "&quot;");
282
295
  }
283
296
  function kindLabel(kind) {
284
297
  switch (kind) {
285
- case "class": return "Classes";
286
- case "struct": return "Structs";
287
- case "enum": return "Enums";
288
- case "union": return "Unions";
289
- case "typedef": return "Type Aliases";
290
- default: return kind.charAt(0).toUpperCase() + kind.slice(1) + "s";
298
+ case "class":
299
+ return "Classes";
300
+ case "struct":
301
+ return "Structs";
302
+ case "enum":
303
+ return "Enums";
304
+ case "union":
305
+ return "Unions";
306
+ case "typedef":
307
+ return "Type Aliases";
308
+ default:
309
+ return kind.charAt(0).toUpperCase() + kind.slice(1) + "s";
291
310
  }
292
311
  }
@@ -1 +1 @@
1
- {"version":3,"file":"godoc-loader.d.ts","sourceRoot":"","sources":["../../src/core/godoc-loader.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,cAAc,CAAC;AAiBxD,OAAO,KAAK,EAAE,YAAY,EAAmB,MAAM,sBAAsB,CAAC;AAC1E,OAAO,KAAK,EAAE,OAAO,EAA6B,MAAM,iBAAiB,CAAC;AAE1E,MAAM,WAAW,iBAAiB;IAChC,KAAK,EAAE,GAAG,CAAC,MAAM,EAAE,YAAY,CAAC,CAAC;IACjC,MAAM,EAAE,OAAO,CAAC;IAChB,WAAW,EAAE,qBAAqB,EAAE,CAAC;CACtC;AAED,MAAM,WAAW,qBAAqB;IACpC,QAAQ,EAAE,OAAO,GAAG,SAAS,GAAG,MAAM,CAAC;IACvC,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,sBAAsB;IACrC,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;AAED;;;;;;;;;;GAUG;AACH,wBAAsB,YAAY,CAChC,MAAM,EAAE,mBAAmB,EAC3B,OAAO,EAAE,MAAM,EACf,QAAQ,EAAE,MAAM,EAChB,WAAW,GAAE,sBAA2B,GACvC,OAAO,CAAC,iBAAiB,CAAC,CAG5B"}
1
+ {"version":3,"file":"godoc-loader.d.ts","sourceRoot":"","sources":["../../src/core/godoc-loader.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,cAAc,CAAC;AAaxD,OAAO,KAAK,EAAE,YAAY,EAAmB,MAAM,sBAAsB,CAAC;AAC1E,OAAO,KAAK,EAAE,OAAO,EAA6B,MAAM,iBAAiB,CAAC;AAE1E,MAAM,WAAW,iBAAiB;IAChC,KAAK,EAAE,GAAG,CAAC,MAAM,EAAE,YAAY,CAAC,CAAC;IACjC,MAAM,EAAE,OAAO,CAAC;IAChB,WAAW,EAAE,qBAAqB,EAAE,CAAC;CACtC;AAED,MAAM,WAAW,qBAAqB;IACpC,QAAQ,EAAE,OAAO,GAAG,SAAS,GAAG,MAAM,CAAC;IACvC,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,sBAAsB;IACrC,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;AAED;;;;;;;;;;GAUG;AACH,wBAAsB,YAAY,CAChC,MAAM,EAAE,mBAAmB,EAC3B,OAAO,EAAE,MAAM,EACf,QAAQ,EAAE,MAAM,EAChB,WAAW,GAAE,sBAA2B,GACvC,OAAO,CAAC,iBAAiB,CAAC,CAG5B"}
@@ -2,7 +2,7 @@ import { readFile } from "node:fs/promises";
2
2
  import { spawnSync } from "node:child_process";
3
3
  import { GODOC_SCHEMA_VERSION } from "./godoc-types.js";
4
4
  import { runIntrospector, GodocIntrospectorError } from "./godoc-introspector.js";
5
- import { renderCodeBlock, renderMarkdown, } from "../utils/markdown.js";
5
+ import { renderCodeBlock, renderMarkdown } from "../utils/markdown.js";
6
6
  /**
7
7
  * Resolve a configured godoc tab into pre-rendered Sourcey pages plus a
8
8
  * navigation tab. Honours `mode: "live" | "snapshot" | "auto"`:
@@ -32,7 +32,18 @@ async function loadSpec(config) {
32
32
  return runIntrospector({ config });
33
33
  }
34
34
  if (config.snapshot) {
35
- return loadSnapshot(config.snapshot, config.module);
35
+ const spec = await loadSnapshot(config.snapshot, config.module);
36
+ return {
37
+ ...spec,
38
+ diagnostics: [
39
+ {
40
+ severity: "info",
41
+ code: "GODOC_AUTO_FELL_BACK_TO_SNAPSHOT",
42
+ message: "godoc auto mode did not find Go on PATH and used the configured snapshot.",
43
+ },
44
+ ...spec.diagnostics,
45
+ ],
46
+ };
36
47
  }
37
48
  throw new GodocIntrospectorError("GO_NOT_FOUND", "godoc mode is 'auto' and Go is not on PATH. Install Go or set " +
38
49
  "mode: 'snapshot' with a committed godoc.json.");
@@ -455,7 +466,9 @@ function sourceURL(position, sourceLinks) {
455
466
  if (!position || !sourceLinks.repo || !sourceLinks.editBranch)
456
467
  return undefined;
457
468
  const repoBase = sourceLinks.repo.replace(/\/$/, "");
458
- const basePath = sourceLinks.editBasePath ? `${sourceLinks.editBasePath.replace(/^\/|\/$/g, "")}/` : "";
469
+ const basePath = sourceLinks.editBasePath
470
+ ? `${sourceLinks.editBasePath.replace(/^\/|\/$/g, "")}/`
471
+ : "";
459
472
  return `${repoBase}/blob/${sourceLinks.editBranch}/${basePath}${position.file}#L${position.line}`;
460
473
  }
461
474
  function renderExample(ex) {
@@ -472,9 +485,7 @@ function renderExample(ex) {
472
485
  return parts.join("\n");
473
486
  }
474
487
  function humaniseSuffix(suffix) {
475
- return suffix
476
- .replace(/_/g, " ")
477
- .replace(/\b\w/g, (c) => c.toUpperCase());
488
+ return suffix.replace(/_/g, " ").replace(/\b\w/g, (c) => c.toUpperCase());
478
489
  }
479
490
  function renderDoc(input) {
480
491
  return renderMarkdown(input).trim();
@@ -20,12 +20,19 @@ export interface MarkdownPage {
20
20
  editBasePath?: string | null;
21
21
  /** Optional page-local entries that should be indexed in addition to headings. */
22
22
  searchEntries?: PageSearchEntry[];
23
+ /** Optional source root used to rewrite source-format-relative assets. */
24
+ sourceRoot?: string;
23
25
  }
24
26
  export interface PageSearchEntry {
25
27
  title: string;
26
28
  content: string;
27
29
  anchor?: string;
28
30
  category: string;
31
+ symbolKind?: string;
32
+ owner?: string;
33
+ ownerKind?: string;
34
+ namespace?: string;
35
+ qualifiedName?: string;
29
36
  }
30
37
  export interface ChangelogPage {
31
38
  kind: "changelog";
@@ -45,7 +52,14 @@ export type { PageHeading } from "../utils/markdown.js";
45
52
  export interface LoadDocsPageOptions {
46
53
  changelog?: boolean;
47
54
  repoUrl?: string;
55
+ sourceRoot?: string;
56
+ preprocess?: MarkdownPreprocessor[];
48
57
  }
58
+ export interface MarkdownPreprocessContext {
59
+ filePath: string;
60
+ sourceRoot?: string;
61
+ }
62
+ export type MarkdownPreprocessor = (body: string, context: MarkdownPreprocessContext) => string;
49
63
  /**
50
64
  * Load a single markdown file and return a MarkdownPage.
51
65
  */
@@ -1 +1 @@
1
- {"version":3,"file":"markdown-loader.d.ts","sourceRoot":"","sources":["../../src/core/markdown-loader.ts"],"names":[],"mappings":"AAMA,OAAO,EAKL,KAAK,WAAW,EACjB,MAAM,sBAAsB,CAAC;AAC9B,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,YAAY,CAAC;AAMtD,MAAM,WAAW,YAAY;IAC3B,IAAI,EAAE,UAAU,CAAC;IACjB,kCAAkC;IAClC,KAAK,EAAE,MAAM,CAAC;IACd,wCAAwC;IACxC,WAAW,EAAE,MAAM,CAAC;IACpB,qDAAqD;IACrD,IAAI,EAAE,MAAM,CAAC;IACb,yBAAyB;IACzB,IAAI,EAAE,MAAM,CAAC;IACb,qDAAqD;IACrD,QAAQ,EAAE,WAAW,EAAE,CAAC;IACxB,sEAAsE;IACtE,UAAU,EAAE,MAAM,CAAC;IACnB,2FAA2F;IAC3F,QAAQ,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,8FAA8F;IAC9F,YAAY,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,kFAAkF;IAClF,aAAa,CAAC,EAAE,eAAe,EAAE,CAAC;CACnC;AAED,MAAM,WAAW,eAAe;IAC9B,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,aAAa;IAC5B,IAAI,EAAE,WAAW,CAAC;IAClB,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,EAAE,MAAM,CAAC;IACpB,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,WAAW,EAAE,CAAC;IACxB,UAAU,EAAE,MAAM,CAAC;IACnB,QAAQ,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,SAAS,EAAE,mBAAmB,CAAC;IAC/B,OAAO,EAAE,MAAM,CAAC;IAChB,iFAAiF;IACjF,kBAAkB,CAAC,EAAE,MAAM,CAAC;CAC7B;AAED,MAAM,MAAM,QAAQ,GAAG,YAAY,GAAG,aAAa,CAAC;AAEpD,YAAY,EAAE,WAAW,EAAE,MAAM,sBAAsB,CAAC;AAUxD,MAAM,WAAW,mBAAmB;IAClC,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAgnCD;;GAEG;AACH,wBAAsB,gBAAgB,CACpC,QAAQ,EAAE,MAAM,EAChB,IAAI,EAAE,MAAM,GACX,OAAO,CAAC,YAAY,CAAC,CAKvB;AAED,wBAAsB,YAAY,CAChC,QAAQ,EAAE,MAAM,EAChB,IAAI,EAAE,MAAM,EACZ,OAAO,GAAE,mBAAwB,GAChC,OAAO,CAAC,QAAQ,CAAC,CAiCnB;AAuDD;;;;;;GAMG;AACH,wBAAgB,YAAY,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAIrD"}
1
+ {"version":3,"file":"markdown-loader.d.ts","sourceRoot":"","sources":["../../src/core/markdown-loader.ts"],"names":[],"mappings":"AAOA,OAAO,EAOL,KAAK,WAAW,EACjB,MAAM,sBAAsB,CAAC;AAC9B,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,YAAY,CAAC;AAMtD,MAAM,WAAW,YAAY;IAC3B,IAAI,EAAE,UAAU,CAAC;IACjB,kCAAkC;IAClC,KAAK,EAAE,MAAM,CAAC;IACd,wCAAwC;IACxC,WAAW,EAAE,MAAM,CAAC;IACpB,qDAAqD;IACrD,IAAI,EAAE,MAAM,CAAC;IACb,yBAAyB;IACzB,IAAI,EAAE,MAAM,CAAC;IACb,qDAAqD;IACrD,QAAQ,EAAE,WAAW,EAAE,CAAC;IACxB,sEAAsE;IACtE,UAAU,EAAE,MAAM,CAAC;IACnB,2FAA2F;IAC3F,QAAQ,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,8FAA8F;IAC9F,YAAY,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,kFAAkF;IAClF,aAAa,CAAC,EAAE,eAAe,EAAE,CAAC;IAClC,0EAA0E;IAC1E,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,WAAW,eAAe;IAC9B,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,MAAM,CAAC;IACjB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB;AAED,MAAM,WAAW,aAAa;IAC5B,IAAI,EAAE,WAAW,CAAC;IAClB,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,EAAE,MAAM,CAAC;IACpB,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,WAAW,EAAE,CAAC;IACxB,UAAU,EAAE,MAAM,CAAC;IACnB,QAAQ,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,SAAS,EAAE,mBAAmB,CAAC;IAC/B,OAAO,EAAE,MAAM,CAAC;IAChB,iFAAiF;IACjF,kBAAkB,CAAC,EAAE,MAAM,CAAC;CAC7B;AAED,MAAM,MAAM,QAAQ,GAAG,YAAY,GAAG,aAAa,CAAC;AAEpD,YAAY,EAAE,WAAW,EAAE,MAAM,sBAAsB,CAAC;AAUxD,MAAM,WAAW,mBAAmB;IAClC,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,UAAU,CAAC,EAAE,oBAAoB,EAAE,CAAC;CACrC;AAED,MAAM,WAAW,yBAAyB;IACxC,QAAQ,EAAE,MAAM,CAAC;IACjB,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,MAAM,oBAAoB,GAAG,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,yBAAyB,KAAK,MAAM,CAAC;AAonChG;;GAEG;AACH,wBAAsB,gBAAgB,CAAC,QAAQ,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,YAAY,CAAC,CAK5F;AAED,wBAAsB,YAAY,CAChC,QAAQ,EAAE,MAAM,EAChB,IAAI,EAAE,MAAM,EACZ,OAAO,GAAE,mBAAwB,GAChC,OAAO,CAAC,QAAQ,CAAC,CAgCnB;AA+DD;;;;;;GAMG;AACH,wBAAgB,YAAY,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAOrD"}
@@ -1,10 +1,11 @@
1
- import { readFile } from "node:fs/promises";
1
+ import { readFile as readFileAsync } from "node:fs/promises";
2
2
  import { basename, extname, relative } from "node:path";
3
3
  import { load as parseYaml } from "js-yaml";
4
4
  import { normalizeChangelog } from "./changelog-normalizer.js";
5
5
  import { htmlId } from "../utils/html-id.js";
6
+ import { escapeAttr, safeUrl } from "../utils/html.js";
6
7
  import { renderIcon } from "../utils/icons.js";
7
- import { renderCodeBlock, renderMarkdown, renderMarkdownInline, extractHeadings, } from "../utils/markdown.js";
8
+ import { renderCodeBlock, renderMarkdown, renderMarkdownInline, extractHeadings, extractFirstParagraph, stripMarkdownLinks, } from "../utils/markdown.js";
8
9
  // ---------------------------------------------------------------------------
9
10
  // Frontmatter parsing
10
11
  // ---------------------------------------------------------------------------
@@ -453,8 +454,9 @@ ${body}
453
454
  const cols = node.attrs.cols || "2";
454
455
  const cardHtml = cards
455
456
  .map((card) => {
456
- const tag = card.attrs.href ? "a" : "div";
457
- const href = card.attrs.href ? ` href="${escapeHtmlAttr(card.attrs.href)}"` : "";
457
+ const safeHref = card.attrs.href ? safeUrl(card.attrs.href) : null;
458
+ const tag = safeHref ? "a" : "div";
459
+ const href = safeHref ? ` href="${escapeHtmlAttr(safeHref)}"` : "";
458
460
  const iconHtml = renderIcon(card.attrs.icon || "");
459
461
  const title = renderMarkdownInline(card.attrs.title || "").trim();
460
462
  const body = renderComponentChildrenToHtml(card.children);
@@ -502,7 +504,10 @@ ${body}
502
504
  return null;
503
505
  return `\n\n${buildTabbedHtml(codeBlocks, nextId("cg"), "directive-code-group")}\n`;
504
506
  }
505
- if (node.name === "Note" || node.name === "Warning" || node.name === "Tip" || node.name === "Info") {
507
+ if (node.name === "Note" ||
508
+ node.name === "Warning" ||
509
+ node.name === "Tip" ||
510
+ node.name === "Info") {
506
511
  const type = node.name.toLowerCase();
507
512
  const label = renderMarkdownInline(node.attrs.title?.trim() || node.name.charAt(0).toUpperCase() + node.name.slice(1)).trim();
508
513
  const body = renderComponentChildrenToHtml(node.children);
@@ -562,10 +567,7 @@ function escapeRegExp(value) {
562
567
  return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
563
568
  }
564
569
  function escapeHtmlAttr(value) {
565
- return value
566
- .replace(/&/g, "&amp;")
567
- .replace(/"/g, "&quot;")
568
- .replace(/</g, "&lt;");
570
+ return escapeAttr(value);
569
571
  }
570
572
  function stripDirectiveIndent(line) {
571
573
  return line.replace(/^ {0,3}/, "");
@@ -585,9 +587,7 @@ function closesFence(line, fence) {
585
587
  }
586
588
  /** Build tabbed UI HTML (shared by :::tabs and :::code-group). */
587
589
  function buildTabbedHtml(tabs, id, extraClass) {
588
- const cls = extraClass
589
- ? `directive-tabs ${extraClass} not-prose`
590
- : "directive-tabs not-prose";
590
+ const cls = extraClass ? `directive-tabs ${extraClass} not-prose` : "directive-tabs not-prose";
591
591
  const buttons = tabs
592
592
  .map((t, i) => `<button class="directive-tab${i === 0 ? " active" : ""}" data-tab-group="${id}" data-tab-index="${i}">${t.title}</button>`)
593
593
  .join("\n");
@@ -652,7 +652,10 @@ function splitChildren(content, marker) {
652
652
  }
653
653
  children.push({
654
654
  attrs: parseAttrs(start[1] ?? ""),
655
- body: lines.slice(i + 1, j).join("\n").trim(),
655
+ body: lines
656
+ .slice(i + 1, j)
657
+ .join("\n")
658
+ .trim(),
656
659
  });
657
660
  i = j + 1;
658
661
  }
@@ -696,7 +699,9 @@ const SUPPORTED_DIRECTIVES = new Set([
696
699
  "accordion",
697
700
  ]);
698
701
  function matchDirectiveStart(line) {
699
- const match = stripDirectiveIndent(line).trimEnd().match(/^:::(\w[\w-]*)(.*)$/);
702
+ const match = stripDirectiveIndent(line)
703
+ .trimEnd()
704
+ .match(/^:::(\w[\w-]*)(.*)$/);
700
705
  if (!match || !SUPPORTED_DIRECTIVES.has(match[1]))
701
706
  return null;
702
707
  return {
@@ -821,8 +826,9 @@ ${body}
821
826
  if (type === "card-group") {
822
827
  const cols = parseAttrs(meta.match(/^\{([^}]*)\}$/)?.[1] ?? "").cols || "2";
823
828
  const cards = splitChildren(content, "card").map((child) => {
824
- const tag = child.attrs.href ? "a" : "div";
825
- const href = child.attrs.href ? ` href="${escapeHtmlAttr(child.attrs.href)}"` : "";
829
+ const safeHref = child.attrs.href ? safeUrl(child.attrs.href) : null;
830
+ const tag = safeHref ? "a" : "div";
831
+ const href = safeHref ? ` href="${escapeHtmlAttr(safeHref)}"` : "";
826
832
  const iconHtml = renderIcon(child.attrs.icon || "");
827
833
  const title = renderMarkdownInline(child.attrs.title || "").trim();
828
834
  const body = renderDirectiveMarkdown(child.body);
@@ -927,12 +933,12 @@ function preprocessDirectives(body) {
927
933
  * Load a single markdown file and return a MarkdownPage.
928
934
  */
929
935
  export async function loadMarkdownPage(filePath, slug) {
930
- const raw = await readFile(filePath, "utf-8");
936
+ const raw = await readFileAsync(filePath, "utf-8");
931
937
  const { meta, body } = parseFrontmatter(raw);
932
938
  return loadMarkdownPageFromBody(filePath, slug, body, meta);
933
939
  }
934
940
  export async function loadDocsPage(filePath, slug, options = {}) {
935
- const raw = await readFile(filePath, "utf-8");
941
+ const raw = await readFileAsync(filePath, "utf-8");
936
942
  const { meta, body } = parseFrontmatter(raw);
937
943
  if (shouldTreatAsChangelog(filePath, meta, options)) {
938
944
  const sourcePath = relative(process.cwd(), filePath);
@@ -941,9 +947,7 @@ export async function loadDocsPage(filePath, slug, options = {}) {
941
947
  description: typeof meta.description === "string" ? meta.description : undefined,
942
948
  repoUrl: options.repoUrl,
943
949
  });
944
- const description = typeof meta.description === "string"
945
- ? meta.description
946
- : changelog.description ?? "";
950
+ const description = typeof meta.description === "string" ? meta.description : (changelog.description ?? "");
947
951
  return {
948
952
  kind: "changelog",
949
953
  title: changelog.title,
@@ -960,7 +964,7 @@ export async function loadDocsPage(filePath, slug, options = {}) {
960
964
  rawBody: body,
961
965
  };
962
966
  }
963
- return loadMarkdownPageFromBody(filePath, slug, body, meta);
967
+ return loadMarkdownPageFromBody(filePath, slug, body, meta, options);
964
968
  }
965
969
  function shouldTreatAsChangelog(filePath, meta, options) {
966
970
  if (options.changelog === false)
@@ -969,14 +973,15 @@ function shouldTreatAsChangelog(filePath, meta, options) {
969
973
  return true;
970
974
  return basename(filePath).toLowerCase() === "changelog.md";
971
975
  }
972
- function loadMarkdownPageFromBody(filePath, slug, body, meta) {
976
+ function loadMarkdownPageFromBody(filePath, slug, body, meta, options = {}) {
973
977
  resetDirectiveCounter();
974
- const componentPreprocessed = preprocessComponents(body);
978
+ const sourcePreprocessed = preprocessSourceMarkdown(body, filePath, options);
979
+ const componentPreprocessed = preprocessComponents(sourcePreprocessed);
975
980
  const preprocessed = preprocessDirectives(componentPreprocessed);
976
981
  const headings = extractHeadings(componentPreprocessed);
977
982
  const html = renderMarkdown(preprocessed);
978
983
  const title = meta.title ?? extractFirstHeading(componentPreprocessed) ?? slug;
979
- const description = meta.description ?? "";
984
+ const description = normalizeMarkdownDescription(meta.description, componentPreprocessed);
980
985
  // Strip leading h1 from rendered HTML when it duplicates the page title
981
986
  const cleanHtml = html.replace(/^\s*<h1[^>]*>(.*?)<\/h1>\s*/i, (_m, inner) => inner.replace(/<[^>]+>/g, "").trim() === title ? "" : _m);
982
987
  const sourcePath = relative(process.cwd(), filePath);
@@ -989,8 +994,15 @@ function loadMarkdownPageFromBody(filePath, slug, body, meta) {
989
994
  headings,
990
995
  sourcePath,
991
996
  editPath: sourcePath,
997
+ sourceRoot: options.sourceRoot ? relative(process.cwd(), options.sourceRoot) : undefined,
992
998
  };
993
999
  }
1000
+ function normalizeMarkdownDescription(description, markdown) {
1001
+ if (typeof description === "string" && description.trim()) {
1002
+ return description;
1003
+ }
1004
+ return stripMarkdownLinks(extractFirstParagraph(markdown)).replace(/\s+/g, " ").trim();
1005
+ }
994
1006
  /**
995
1007
  * Extract the first # heading from markdown as a fallback title.
996
1008
  */
@@ -1008,5 +1020,37 @@ function extractFirstHeading(md) {
1008
1020
  export function slugFromPath(filePath) {
1009
1021
  const ext = extname(filePath);
1010
1022
  const stripped = ext ? filePath.slice(0, -ext.length) : filePath;
1011
- return stripped.split("/").map((s) => htmlId(s)).join("/");
1023
+ return stripped
1024
+ .split("/")
1025
+ .map((s) => slugSegmentFromPath(s))
1026
+ .join("/");
1027
+ }
1028
+ function slugSegmentFromPath(segment) {
1029
+ const normalized = segment
1030
+ .replace(/^operator=$/, "operator-assign")
1031
+ .replace(/^~(.+)$/, "destructor-$1")
1032
+ .replace(/<=>/g, "-spaceship")
1033
+ .replace(/<=/g, "-le")
1034
+ .replace(/>=/g, "-ge")
1035
+ .replace(/==/g, "-eq")
1036
+ .replace(/!=/g, "-ne")
1037
+ .replace(/\[\]/g, "-array")
1038
+ .replace(/\+=/g, "-plus-eq")
1039
+ .replace(/\/=/g, "-slash-eq")
1040
+ .replace(/\//g, "-slash")
1041
+ .replace(/=/g, "-eq")
1042
+ .replace(/\+/g, "-plus")
1043
+ .replace(/</g, "-lt")
1044
+ .replace(/>/g, "-gt");
1045
+ return htmlId(normalized);
1046
+ }
1047
+ function preprocessSourceMarkdown(body, filePath, options) {
1048
+ let current = body;
1049
+ for (const preprocess of options.preprocess ?? []) {
1050
+ current = preprocess(current, {
1051
+ filePath,
1052
+ sourceRoot: options.sourceRoot,
1053
+ });
1054
+ }
1055
+ return current;
1012
1056
  }
@@ -1 +1 @@
1
- {"version":3,"file":"mcp-normalizer.d.ts","sourceRoot":"","sources":["../../src/core/mcp-normalizer.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,KAAK,EAAE,OAAO,EAAoE,MAAM,YAAY,CAAC;AAC5G,OAAO,KAAK,EACV,cAAc,EASf,MAAM,YAAY,CAAC;AAKpB,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,OAAO,GAAG,cAAc,CA+C9D"}
1
+ {"version":3,"file":"mcp-normalizer.d.ts","sourceRoot":"","sources":["../../src/core/mcp-normalizer.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,KAAK,EACV,OAAO,EAMR,MAAM,YAAY,CAAC;AACpB,OAAO,KAAK,EACV,cAAc,EASf,MAAM,YAAY,CAAC;AAKpB,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,OAAO,GAAG,cAAc,CAwD9D"}
@@ -9,12 +9,14 @@ import { generateExample } from "../utils/example-generator.js";
9
9
  // ── Public API ────────────────────────────────────────────────────────
10
10
  export function normalizeMcpSpec(spec) {
11
11
  const connectionInfo = {
12
- transport: spec.transport ? {
13
- type: spec.transport.type,
14
- command: spec.transport.command,
15
- args: spec.transport.args,
16
- url: spec.transport.url,
17
- } : undefined,
12
+ transport: spec.transport
13
+ ? {
14
+ type: spec.transport.type,
15
+ command: spec.transport.command,
16
+ args: spec.transport.args,
17
+ url: spec.transport.url,
18
+ }
19
+ : undefined,
18
20
  serverName: spec.server.name,
19
21
  mcpVersion: spec.mcpVersion,
20
22
  capabilities: spec.capabilities,
@@ -58,13 +60,14 @@ function toolToOperation(tool, connection) {
58
60
  const group = tool["x-sourcey-group"];
59
61
  const inputSchema = convertSchema(tool.inputSchema);
60
62
  const outputSchema = tool.outputSchema ? convertSchema(tool.outputSchema) : undefined;
61
- // Flat parameters from top-level inputSchema properties
62
- const parameters = flattenInputSchema(tool.inputSchema);
63
- // Only include requestBody for nested/complex schemas
64
- const requestBody = hasNestedProperties(tool.inputSchema) ? {
65
- required: true,
66
- content: { "application/json": { schema: inputSchema } },
67
- } : undefined;
63
+ const emitsRequestBody = hasNestedProperties(tool.inputSchema);
64
+ const parameters = emitsRequestBody ? [] : flattenInputSchema(tool.inputSchema);
65
+ const requestBody = emitsRequestBody
66
+ ? {
67
+ required: true,
68
+ content: { "application/json": { schema: inputSchema } },
69
+ }
70
+ : undefined;
68
71
  return {
69
72
  operationId: tool.name,
70
73
  summary: tool.title ?? tool.name,
@@ -80,12 +83,14 @@ function toolToOperation(tool, connection) {
80
83
  codeSamples: generateToolSamples(tool, inputSchema),
81
84
  mcpExtras: {
82
85
  type: "tool",
83
- annotations: tool.annotations ? {
84
- readOnlyHint: tool.annotations.readOnlyHint,
85
- destructiveHint: tool.annotations.destructiveHint,
86
- idempotentHint: tool.annotations.idempotentHint,
87
- openWorldHint: tool.annotations.openWorldHint,
88
- } : undefined,
86
+ annotations: tool.annotations
87
+ ? {
88
+ readOnlyHint: tool.annotations.readOnlyHint,
89
+ destructiveHint: tool.annotations.destructiveHint,
90
+ idempotentHint: tool.annotations.idempotentHint,
91
+ openWorldHint: tool.annotations.openWorldHint,
92
+ }
93
+ : undefined,
89
94
  outputSchema,
90
95
  connection,
91
96
  },
@@ -136,7 +141,7 @@ function resourceTemplateToOperation(template, connection) {
136
141
  // ── Prompt → Operation ────────────────────────────────────────────────
137
142
  function promptToOperation(prompt, connection) {
138
143
  const group = prompt["x-sourcey-group"];
139
- const parameters = (prompt.arguments ?? []).map(arg => ({
144
+ const parameters = (prompt.arguments ?? []).map((arg) => ({
140
145
  name: arg.name,
141
146
  in: "argument",
142
147
  description: arg.description,
@@ -189,18 +194,20 @@ function convertSchema(schema, name) {
189
194
  }
190
195
  }
191
196
  if (schema.additionalProperties !== undefined) {
192
- result.additionalProperties = typeof schema.additionalProperties === "boolean"
193
- ? schema.additionalProperties
194
- : convertSchema(schema.additionalProperties);
197
+ result.additionalProperties =
198
+ typeof schema.additionalProperties === "boolean"
199
+ ? schema.additionalProperties
200
+ : convertSchema(schema.additionalProperties);
195
201
  }
196
202
  if (schema.required)
197
203
  result.required = schema.required;
198
204
  // Array — handle JsonSchema.items being JsonSchema | JsonSchema[]
199
205
  if (schema.items) {
200
206
  if (Array.isArray(schema.items)) {
201
- result.items = schema.items.length === 1
202
- ? convertSchema(schema.items[0])
203
- : { oneOf: schema.items.map(s => convertSchema(s)) };
207
+ result.items =
208
+ schema.items.length === 1
209
+ ? convertSchema(schema.items[0])
210
+ : { oneOf: schema.items.map((s) => convertSchema(s)) };
204
211
  }
205
212
  else {
206
213
  result.items = convertSchema(schema.items);
@@ -265,7 +272,10 @@ function hasNestedProperties(schema) {
265
272
  return Object.values(schema.properties).some((prop) => {
266
273
  if (prop.type === "object" && prop.properties)
267
274
  return true;
268
- if (prop.type === "array" && prop.items && !Array.isArray(prop.items) && prop.items.type === "object")
275
+ if (prop.type === "array" &&
276
+ prop.items &&
277
+ !Array.isArray(prop.items) &&
278
+ prop.items.type === "object")
269
279
  return true;
270
280
  if (prop.allOf || prop.anyOf || prop.oneOf)
271
281
  return true;
@@ -276,7 +286,7 @@ function extractUriTemplateParams(uriTemplate) {
276
286
  const matches = uriTemplate.match(/\{([^}]+)\}/g);
277
287
  if (!matches)
278
288
  return [];
279
- return matches.map(m => ({
289
+ return matches.map((m) => ({
280
290
  name: m.slice(1, -1),
281
291
  in: "path",
282
292
  required: true,
@@ -289,11 +299,12 @@ function slugify(name) {
289
299
  }
290
300
  // ── Code sample generation ────────────────────────────────────────────
291
301
  function generateToolSamples(tool, inputSchema) {
292
- const example = inputSchema.properties
293
- ? generateExample(inputSchema)
294
- : {};
302
+ const example = inputSchema.properties ? generateExample(inputSchema) : {};
295
303
  const exampleJson = JSON.stringify(example, null, 2);
296
- const indented = exampleJson.split("\n").map((line, i) => i === 0 ? line : " " + line).join("\n");
304
+ const indented = exampleJson
305
+ .split("\n")
306
+ .map((line, i) => (i === 0 ? line : " " + line))
307
+ .join("\n");
297
308
  return [
298
309
  {
299
310
  lang: "json",
@@ -345,10 +356,13 @@ function generateResourceSamples(resource) {
345
356
  ];
346
357
  }
347
358
  function generatePromptSamples(prompt) {
348
- const args = (prompt.arguments ?? []);
359
+ const args = prompt.arguments ?? [];
349
360
  const exampleArgs = Object.fromEntries(args.map((a) => [a.name, `<${a.name}>`]));
350
361
  const argsJson = JSON.stringify(exampleArgs, null, 2);
351
- const indented = argsJson.split("\n").map((line, i) => i === 0 ? line : " " + line).join("\n");
362
+ const indented = argsJson
363
+ .split("\n")
364
+ .map((line, i) => (i === 0 ? line : " " + line))
365
+ .join("\n");
352
366
  const tsArgs = args.map((a) => ` ${a.name}: "<${a.name}>",`).join("\n");
353
367
  const pyArgs = args.map((a) => ` "${a.name}": "<${a.name}>",`).join("\n");
354
368
  return [
@@ -1 +1 @@
1
- {"version":3,"file":"navigation.d.ts","sourceRoot":"","sources":["../../src/core/navigation.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAGjD,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAChD,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,sBAAsB,CAAC;AAErD,OAAO,EAAgB,KAAK,UAAU,EAAE,MAAM,gBAAgB,CAAC;AAgB/D,MAAM,WAAW,cAAc;IAC7B,IAAI,EAAE,OAAO,EAAE,CAAC;IAChB,aAAa,EAAE,MAAM,CAAC;IACtB,cAAc,EAAE,MAAM,CAAC;CACxB;AAED,MAAM,WAAW,OAAO;IACtB,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,CAAC;IACb,6DAA6D;IAC7D,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,GAAG,MAAM,CAAC;IACtB,MAAM,EAAE,YAAY,EAAE,CAAC;CACxB;AAED,MAAM,WAAW,YAAY;IAC3B,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,WAAW,EAAE,CAAC;CACtB;AAED,MAAM,WAAW,WAAW;IAC1B,KAAK,EAAE,MAAM,CAAC;IACd,iDAAiD;IACjD,IAAI,EAAE,MAAM,CAAC;IACb,0CAA0C;IAC1C,EAAE,EAAE,MAAM,CAAC;IACX,kEAAkE;IAClE,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAMD;;;;;GAKG;AACH,wBAAgB,gBAAgB,CAC9B,IAAI,EAAE,cAAc,EACpB,OAAO,EAAE,MAAM,EACf,UAAU,GAAE,UAAkB,GAC7B,OAAO,CAiDT;AAED;;;GAGG;AACH,wBAAgB,iBAAiB,CAC/B,GAAG,EAAE,WAAW,EAChB,WAAW,EAAE,GAAG,CAAC,MAAM,EAAE,QAAQ,CAAC,EAClC,UAAU,GAAE,UAAkB,GAC7B,OAAO,CA6BT;AAED;;;GAGG;AACH,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,OAAO,EAAE,GAAG,cAAc,CASnE;AAED;;GAEG;AACH,wBAAgB,cAAc,CAC5B,GAAG,EAAE,cAAc,EACnB,OAAO,EAAE,MAAM,EACf,QAAQ,EAAE,MAAM,GACf,cAAc,CAEhB"}
1
+ {"version":3,"file":"navigation.d.ts","sourceRoot":"","sources":["../../src/core/navigation.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAGjD,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAChD,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,sBAAsB,CAAC;AAErD,OAAO,EAAgB,KAAK,UAAU,EAAE,MAAM,gBAAgB,CAAC;AAgB/D,MAAM,WAAW,cAAc;IAC7B,IAAI,EAAE,OAAO,EAAE,CAAC;IAChB,aAAa,EAAE,MAAM,CAAC;IACtB,cAAc,EAAE,MAAM,CAAC;CACxB;AAED,MAAM,WAAW,OAAO;IACtB,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,CAAC;IACb,6DAA6D;IAC7D,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,GAAG,MAAM,CAAC;IACtB,MAAM,EAAE,YAAY,EAAE,CAAC;CACxB;AAED,MAAM,WAAW,YAAY;IAC3B,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,WAAW,EAAE,CAAC;CACtB;AAED,MAAM,WAAW,WAAW;IAC1B,KAAK,EAAE,MAAM,CAAC;IACd,iDAAiD;IACjD,IAAI,EAAE,MAAM,CAAC;IACb,0CAA0C;IAC1C,EAAE,EAAE,MAAM,CAAC;IACX,kEAAkE;IAClE,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAMD;;;;;GAKG;AACH,wBAAgB,gBAAgB,CAC9B,IAAI,EAAE,cAAc,EACpB,OAAO,EAAE,MAAM,EACf,UAAU,GAAE,UAAkB,GAC7B,OAAO,CAiDT;AAED;;;GAGG;AACH,wBAAgB,iBAAiB,CAC/B,GAAG,EAAE,WAAW,EAChB,WAAW,EAAE,GAAG,CAAC,MAAM,EAAE,QAAQ,CAAC,EAClC,UAAU,GAAE,UAAkB,GAC7B,OAAO,CA8BT;AAED;;;GAGG;AACH,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,OAAO,EAAE,GAAG,cAAc,CASnE;AAED;;GAEG;AACH,wBAAgB,cAAc,CAC5B,GAAG,EAAE,cAAc,EACnB,OAAO,EAAE,MAAM,EACf,QAAQ,EAAE,MAAM,GACf,cAAc,CAEhB"}