@lepid-labs/weft-core 0.1.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.
Files changed (266) hide show
  1. package/LICENSE +21 -0
  2. package/dist/anchors/__tests__/markdown.test.d.ts +2 -0
  3. package/dist/anchors/__tests__/markdown.test.d.ts.map +1 -0
  4. package/dist/anchors/__tests__/markdown.test.js +130 -0
  5. package/dist/anchors/__tests__/markdown.test.js.map +1 -0
  6. package/dist/anchors/__tests__/openapi.test.d.ts +2 -0
  7. package/dist/anchors/__tests__/openapi.test.d.ts.map +1 -0
  8. package/dist/anchors/__tests__/openapi.test.js +115 -0
  9. package/dist/anchors/__tests__/openapi.test.js.map +1 -0
  10. package/dist/anchors/index.d.ts +35 -0
  11. package/dist/anchors/index.d.ts.map +1 -0
  12. package/dist/anchors/index.js +68 -0
  13. package/dist/anchors/index.js.map +1 -0
  14. package/dist/anchors/markdown.d.ts +15 -0
  15. package/dist/anchors/markdown.d.ts.map +1 -0
  16. package/dist/anchors/markdown.js +71 -0
  17. package/dist/anchors/markdown.js.map +1 -0
  18. package/dist/anchors/openapi.d.ts +24 -0
  19. package/dist/anchors/openapi.d.ts.map +1 -0
  20. package/dist/anchors/openapi.js +75 -0
  21. package/dist/anchors/openapi.js.map +1 -0
  22. package/dist/browser.d.ts +10 -0
  23. package/dist/browser.d.ts.map +1 -0
  24. package/dist/browser.js +8 -0
  25. package/dist/browser.js.map +1 -0
  26. package/dist/config.d.ts +47 -0
  27. package/dist/config.d.ts.map +1 -0
  28. package/dist/config.js +393 -0
  29. package/dist/config.js.map +1 -0
  30. package/dist/config.test.d.ts +2 -0
  31. package/dist/config.test.d.ts.map +1 -0
  32. package/dist/config.test.js +454 -0
  33. package/dist/config.test.js.map +1 -0
  34. package/dist/content.d.ts +37 -0
  35. package/dist/content.d.ts.map +1 -0
  36. package/dist/content.js +57 -0
  37. package/dist/content.js.map +1 -0
  38. package/dist/content.test.d.ts +2 -0
  39. package/dist/content.test.d.ts.map +1 -0
  40. package/dist/content.test.js +100 -0
  41. package/dist/content.test.js.map +1 -0
  42. package/dist/contributions.d.ts +64 -0
  43. package/dist/contributions.d.ts.map +1 -0
  44. package/dist/contributions.js +184 -0
  45. package/dist/contributions.js.map +1 -0
  46. package/dist/contributions.test.d.ts +2 -0
  47. package/dist/contributions.test.d.ts.map +1 -0
  48. package/dist/contributions.test.js +202 -0
  49. package/dist/contributions.test.js.map +1 -0
  50. package/dist/fetch/auth.d.ts +15 -0
  51. package/dist/fetch/auth.d.ts.map +1 -0
  52. package/dist/fetch/auth.js +38 -0
  53. package/dist/fetch/auth.js.map +1 -0
  54. package/dist/fetch/cache.d.ts +25 -0
  55. package/dist/fetch/cache.d.ts.map +1 -0
  56. package/dist/fetch/cache.js +68 -0
  57. package/dist/fetch/cache.js.map +1 -0
  58. package/dist/fetch/cache.test.d.ts +2 -0
  59. package/dist/fetch/cache.test.d.ts.map +1 -0
  60. package/dist/fetch/cache.test.js +73 -0
  61. package/dist/fetch/cache.test.js.map +1 -0
  62. package/dist/fetch/clone.d.ts +26 -0
  63. package/dist/fetch/clone.d.ts.map +1 -0
  64. package/dist/fetch/clone.js +105 -0
  65. package/dist/fetch/clone.js.map +1 -0
  66. package/dist/fetch/fetch.test.d.ts +2 -0
  67. package/dist/fetch/fetch.test.d.ts.map +1 -0
  68. package/dist/fetch/fetch.test.js +167 -0
  69. package/dist/fetch/fetch.test.js.map +1 -0
  70. package/dist/fetch/resolve.d.ts +34 -0
  71. package/dist/fetch/resolve.d.ts.map +1 -0
  72. package/dist/fetch/resolve.js +59 -0
  73. package/dist/fetch/resolve.js.map +1 -0
  74. package/dist/freshness.d.ts +29 -0
  75. package/dist/freshness.d.ts.map +1 -0
  76. package/dist/freshness.js +170 -0
  77. package/dist/freshness.js.map +1 -0
  78. package/dist/freshness.test.d.ts +2 -0
  79. package/dist/freshness.test.d.ts.map +1 -0
  80. package/dist/freshness.test.js +209 -0
  81. package/dist/freshness.test.js.map +1 -0
  82. package/dist/frontmatter.d.ts +15 -0
  83. package/dist/frontmatter.d.ts.map +1 -0
  84. package/dist/frontmatter.js +24 -0
  85. package/dist/frontmatter.js.map +1 -0
  86. package/dist/frontmatter.test.d.ts +2 -0
  87. package/dist/frontmatter.test.d.ts.map +1 -0
  88. package/dist/frontmatter.test.js +54 -0
  89. package/dist/frontmatter.test.js.map +1 -0
  90. package/dist/git.d.ts +55 -0
  91. package/dist/git.d.ts.map +1 -0
  92. package/dist/git.js +127 -0
  93. package/dist/git.js.map +1 -0
  94. package/dist/git.test.d.ts +2 -0
  95. package/dist/git.test.d.ts.map +1 -0
  96. package/dist/git.test.js +198 -0
  97. package/dist/git.test.js.map +1 -0
  98. package/dist/includes.d.ts +53 -0
  99. package/dist/includes.d.ts.map +1 -0
  100. package/dist/includes.js +73 -0
  101. package/dist/includes.js.map +1 -0
  102. package/dist/includes.test.d.ts +2 -0
  103. package/dist/includes.test.d.ts.map +1 -0
  104. package/dist/includes.test.js +106 -0
  105. package/dist/includes.test.js.map +1 -0
  106. package/dist/index.d.ts +33 -0
  107. package/dist/index.d.ts.map +1 -0
  108. package/dist/index.js +23 -0
  109. package/dist/index.js.map +1 -0
  110. package/dist/links/__tests__/markdown.test.d.ts +2 -0
  111. package/dist/links/__tests__/markdown.test.d.ts.map +1 -0
  112. package/dist/links/__tests__/markdown.test.js +173 -0
  113. package/dist/links/__tests__/markdown.test.js.map +1 -0
  114. package/dist/links/__tests__/sidecar.test.d.ts +2 -0
  115. package/dist/links/__tests__/sidecar.test.d.ts.map +1 -0
  116. package/dist/links/__tests__/sidecar.test.js +238 -0
  117. package/dist/links/__tests__/sidecar.test.js.map +1 -0
  118. package/dist/links/index.d.ts +3 -0
  119. package/dist/links/index.d.ts.map +1 -0
  120. package/dist/links/index.js +3 -0
  121. package/dist/links/index.js.map +1 -0
  122. package/dist/links/markdown.d.ts +13 -0
  123. package/dist/links/markdown.d.ts.map +1 -0
  124. package/dist/links/markdown.js +93 -0
  125. package/dist/links/markdown.js.map +1 -0
  126. package/dist/links/sidecar.d.ts +12 -0
  127. package/dist/links/sidecar.d.ts.map +1 -0
  128. package/dist/links/sidecar.js +88 -0
  129. package/dist/links/sidecar.js.map +1 -0
  130. package/dist/manifest.d.ts +45 -0
  131. package/dist/manifest.d.ts.map +1 -0
  132. package/dist/manifest.js +269 -0
  133. package/dist/manifest.js.map +1 -0
  134. package/dist/manifest.test.d.ts +2 -0
  135. package/dist/manifest.test.d.ts.map +1 -0
  136. package/dist/manifest.test.js +625 -0
  137. package/dist/manifest.test.js.map +1 -0
  138. package/dist/multi-repo.test.d.ts +2 -0
  139. package/dist/multi-repo.test.d.ts.map +1 -0
  140. package/dist/multi-repo.test.js +179 -0
  141. package/dist/multi-repo.test.js.map +1 -0
  142. package/dist/node-path.d.ts +13 -0
  143. package/dist/node-path.d.ts.map +1 -0
  144. package/dist/node-path.js +21 -0
  145. package/dist/node-path.js.map +1 -0
  146. package/dist/node-path.test.d.ts +2 -0
  147. package/dist/node-path.test.d.ts.map +1 -0
  148. package/dist/node-path.test.js +29 -0
  149. package/dist/node-path.test.js.map +1 -0
  150. package/dist/published-links.d.ts +16 -0
  151. package/dist/published-links.d.ts.map +1 -0
  152. package/dist/published-links.js +59 -0
  153. package/dist/published-links.js.map +1 -0
  154. package/dist/published-links.test.d.ts +2 -0
  155. package/dist/published-links.test.d.ts.map +1 -0
  156. package/dist/published-links.test.js +82 -0
  157. package/dist/published-links.test.js.map +1 -0
  158. package/dist/repos.d.ts +32 -0
  159. package/dist/repos.d.ts.map +1 -0
  160. package/dist/repos.js +46 -0
  161. package/dist/repos.js.map +1 -0
  162. package/dist/repos.test.d.ts +2 -0
  163. package/dist/repos.test.d.ts.map +1 -0
  164. package/dist/repos.test.js +61 -0
  165. package/dist/repos.test.js.map +1 -0
  166. package/dist/scalar-source.d.ts +15 -0
  167. package/dist/scalar-source.d.ts.map +1 -0
  168. package/dist/scalar-source.js +21 -0
  169. package/dist/scalar-source.js.map +1 -0
  170. package/dist/search.d.ts +14 -0
  171. package/dist/search.d.ts.map +1 -0
  172. package/dist/search.js +59 -0
  173. package/dist/search.js.map +1 -0
  174. package/dist/service-concurrency.test.d.ts +2 -0
  175. package/dist/service-concurrency.test.d.ts.map +1 -0
  176. package/dist/service-concurrency.test.js +89 -0
  177. package/dist/service-concurrency.test.js.map +1 -0
  178. package/dist/service-history.test.d.ts +2 -0
  179. package/dist/service-history.test.d.ts.map +1 -0
  180. package/dist/service-history.test.js +76 -0
  181. package/dist/service-history.test.js.map +1 -0
  182. package/dist/service.d.ts +118 -0
  183. package/dist/service.d.ts.map +1 -0
  184. package/dist/service.js +347 -0
  185. package/dist/service.js.map +1 -0
  186. package/dist/service.test.d.ts +2 -0
  187. package/dist/service.test.d.ts.map +1 -0
  188. package/dist/service.test.js +313 -0
  189. package/dist/service.test.js.map +1 -0
  190. package/dist/types.d.ts +403 -0
  191. package/dist/types.d.ts.map +1 -0
  192. package/dist/types.js +2 -0
  193. package/dist/types.js.map +1 -0
  194. package/dist/validate/__tests__/artifacts.test.d.ts +2 -0
  195. package/dist/validate/__tests__/artifacts.test.d.ts.map +1 -0
  196. package/dist/validate/__tests__/artifacts.test.js +167 -0
  197. package/dist/validate/__tests__/artifacts.test.js.map +1 -0
  198. package/dist/validate/__tests__/assertions.test.d.ts +2 -0
  199. package/dist/validate/__tests__/assertions.test.d.ts.map +1 -0
  200. package/dist/validate/__tests__/assertions.test.js +254 -0
  201. package/dist/validate/__tests__/assertions.test.js.map +1 -0
  202. package/dist/validate/__tests__/duplicates.test.d.ts +2 -0
  203. package/dist/validate/__tests__/duplicates.test.d.ts.map +1 -0
  204. package/dist/validate/__tests__/duplicates.test.js +130 -0
  205. package/dist/validate/__tests__/duplicates.test.js.map +1 -0
  206. package/dist/validate/__tests__/edge-resolution.test.d.ts +2 -0
  207. package/dist/validate/__tests__/edge-resolution.test.d.ts.map +1 -0
  208. package/dist/validate/__tests__/edge-resolution.test.js +279 -0
  209. package/dist/validate/__tests__/edge-resolution.test.js.map +1 -0
  210. package/dist/validate/__tests__/history.test.d.ts +2 -0
  211. package/dist/validate/__tests__/history.test.d.ts.map +1 -0
  212. package/dist/validate/__tests__/history.test.js +93 -0
  213. package/dist/validate/__tests__/history.test.js.map +1 -0
  214. package/dist/validate/__tests__/includes.test.d.ts +2 -0
  215. package/dist/validate/__tests__/includes.test.d.ts.map +1 -0
  216. package/dist/validate/__tests__/includes.test.js +93 -0
  217. package/dist/validate/__tests__/includes.test.js.map +1 -0
  218. package/dist/validate/__tests__/registry.test.d.ts +2 -0
  219. package/dist/validate/__tests__/registry.test.d.ts.map +1 -0
  220. package/dist/validate/__tests__/registry.test.js +78 -0
  221. package/dist/validate/__tests__/registry.test.js.map +1 -0
  222. package/dist/validate/__tests__/run.test.d.ts +2 -0
  223. package/dist/validate/__tests__/run.test.d.ts.map +1 -0
  224. package/dist/validate/__tests__/run.test.js +215 -0
  225. package/dist/validate/__tests__/run.test.js.map +1 -0
  226. package/dist/validate/history.d.ts +23 -0
  227. package/dist/validate/history.d.ts.map +1 -0
  228. package/dist/validate/history.js +45 -0
  229. package/dist/validate/history.js.map +1 -0
  230. package/dist/validate/index.d.ts +10 -0
  231. package/dist/validate/index.d.ts.map +1 -0
  232. package/dist/validate/index.js +9 -0
  233. package/dist/validate/index.js.map +1 -0
  234. package/dist/validate/registry.d.ts +27 -0
  235. package/dist/validate/registry.d.ts.map +1 -0
  236. package/dist/validate/registry.js +67 -0
  237. package/dist/validate/registry.js.map +1 -0
  238. package/dist/validate/rules/artifacts.d.ts +25 -0
  239. package/dist/validate/rules/artifacts.d.ts.map +1 -0
  240. package/dist/validate/rules/artifacts.js +87 -0
  241. package/dist/validate/rules/artifacts.js.map +1 -0
  242. package/dist/validate/rules/assertions.d.ts +19 -0
  243. package/dist/validate/rules/assertions.d.ts.map +1 -0
  244. package/dist/validate/rules/assertions.js +149 -0
  245. package/dist/validate/rules/assertions.js.map +1 -0
  246. package/dist/validate/rules/duplicates.d.ts +20 -0
  247. package/dist/validate/rules/duplicates.d.ts.map +1 -0
  248. package/dist/validate/rules/duplicates.js +0 -0
  249. package/dist/validate/rules/duplicates.js.map +1 -0
  250. package/dist/validate/rules/edge-resolution.d.ts +15 -0
  251. package/dist/validate/rules/edge-resolution.d.ts.map +1 -0
  252. package/dist/validate/rules/edge-resolution.js +185 -0
  253. package/dist/validate/rules/edge-resolution.js.map +1 -0
  254. package/dist/validate/rules/includes.d.ts +21 -0
  255. package/dist/validate/rules/includes.d.ts.map +1 -0
  256. package/dist/validate/rules/includes.js +112 -0
  257. package/dist/validate/rules/includes.js.map +1 -0
  258. package/dist/validate/run.d.ts +20 -0
  259. package/dist/validate/run.d.ts.map +1 -0
  260. package/dist/validate/run.js +90 -0
  261. package/dist/validate/run.js.map +1 -0
  262. package/dist/validate/types.d.ts +101 -0
  263. package/dist/validate/types.d.ts.map +1 -0
  264. package/dist/validate/types.js +2 -0
  265. package/dist/validate/types.js.map +1 -0
  266. package/package.json +54 -0
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Wil Asche
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=markdown.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"markdown.test.d.ts","sourceRoot":"","sources":["../../../src/anchors/__tests__/markdown.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,130 @@
1
+ import { describe, expect, it } from "vitest";
2
+ import { extractMarkdownAnchors, extractMarkdownDescription, extractMarkdownTitle, } from "../markdown.js";
3
+ /**
4
+ * Build CRLF content explicitly rather than reading a fixture — a fixture's line
5
+ * endings depend on how the repo was checked out, so it would not reliably
6
+ * exercise this path.
7
+ */
8
+ function crlf(content) {
9
+ return content.replace(/\n/g, "\r\n");
10
+ }
11
+ /** Slugs alone, for the cases that are only about slugification. */
12
+ function slugs(content) {
13
+ return extractMarkdownAnchors(content).map((a) => a.slug);
14
+ }
15
+ describe("extractMarkdownAnchors", () => {
16
+ it("extracts heading slugs", () => {
17
+ const content = "# Title\n\n## Getting Started\n\n### API Reference\n";
18
+ expect(slugs(content)).toEqual(["#title", "#getting-started", "#api-reference"]);
19
+ });
20
+ it("handles duplicate headings with suffix", () => {
21
+ const content = "## Overview\n\n## Details\n\n## Overview\n";
22
+ expect(slugs(content)).toEqual(["#overview", "#details", "#overview-1"]);
23
+ });
24
+ it("strips special characters", () => {
25
+ expect(slugs("## What is `weft`?\n")).toEqual(["#what-is-weft"]);
26
+ });
27
+ it("returns empty array for no headings", () => {
28
+ expect(extractMarkdownAnchors("Just a paragraph.\n")).toEqual([]);
29
+ });
30
+ it("extracts headings from CRLF content", () => {
31
+ const content = crlf("# Title\n\n## Getting Started\n\n### API Reference\n");
32
+ expect(slugs(content)).toEqual(["#title", "#getting-started", "#api-reference"]);
33
+ });
34
+ it("slugs CRLF headings identically to LF", () => {
35
+ const content = "## What is `weft`?\n\n## Overview\n\n## Overview\n";
36
+ expect(extractMarkdownAnchors(crlf(content))).toEqual(extractMarkdownAnchors(content));
37
+ });
38
+ it("carries the line, level and raw text of each heading", () => {
39
+ const content = "# Title\n\nprose\n\n### Data Flow\n";
40
+ expect(extractMarkdownAnchors(content)).toEqual([
41
+ { slug: "#title", text: "Title", line: 1, level: 1 },
42
+ { slug: "#data-flow", text: "Data Flow", line: 5, level: 3 },
43
+ ]);
44
+ });
45
+ it("numbers lines from 1, and counts CRLF lines the same as LF", () => {
46
+ const content = "intro\n\n## Later\n";
47
+ expect(extractMarkdownAnchors(content)[0].line).toBe(3);
48
+ expect(extractMarkdownAnchors(crlf(content))[0].line).toBe(3);
49
+ });
50
+ it("keeps the heading's text as rendered, before slugification", () => {
51
+ const anchors = extractMarkdownAnchors("## What is `weft`?\n");
52
+ // The text a reader sees, not the source line — which is also what the
53
+ // slug is derived from, so the two cannot drift apart.
54
+ expect(anchors[0].text).toBe("What is weft?");
55
+ expect(anchors[0].slug).toBe("#what-is-weft");
56
+ });
57
+ it("resolves inline markup in the text rather than carrying its syntax", () => {
58
+ const anchors = extractMarkdownAnchors("## See [the docs](guide.md) first\n");
59
+ expect(anchors[0].text).toBe("See the docs first");
60
+ expect(anchors[0].slug).toBe("#see-the-docs-first");
61
+ });
62
+ it("finds setext headings, which the renderer also gives ids", () => {
63
+ const anchors = extractMarkdownAnchors("Setext Title\n============\n\nBody.\n");
64
+ expect(anchors).toEqual([{ slug: "#setext-title", text: "Setext Title", line: 1, level: 1 }]);
65
+ });
66
+ });
67
+ // Slugs are authored against how the document renders on GitHub (DD-2), so
68
+ // github-slugger's output is the correct answer, not an approximation of it.
69
+ describe("extractMarkdownAnchors (GitHub slug parity)", () => {
70
+ it("gives each space its own hyphen rather than collapsing runs", () => {
71
+ expect(slugs("## Layout — Presenting Mode\n")).toEqual(["#layout--presenting-mode"]);
72
+ expect(slugs("## React + Vite\n")).toEqual(["#react--vite"]);
73
+ expect(slugs("## Fastify / Express\n")).toEqual(["#fastify--express"]);
74
+ });
75
+ it("keeps accented letters instead of stripping them", () => {
76
+ expect(slugs("## Café Setup\n")).toEqual(["#café-setup"]);
77
+ expect(slugs("## Naïve Approach\n")).toEqual(["#naïve-approach"]);
78
+ });
79
+ it("keeps non-Latin headings rather than slugging them to nothing", () => {
80
+ expect(slugs("## 日本語の見出し\n")).toEqual(["#日本語の見出し"]);
81
+ });
82
+ it("does not collapse a heading of only punctuation into a colliding empty slug", () => {
83
+ // Previously every such heading became "#", then "#-1", "#-2".
84
+ const result = slugs("## 日本語\n\n## 中文\n");
85
+ expect(new Set(result).size).toBe(2);
86
+ });
87
+ });
88
+ describe("extractMarkdownAnchors (fenced code)", () => {
89
+ it("ignores a # comment inside a fenced block", () => {
90
+ const content = "# Usage\n\n```sh\n# Install globally\nnpm i -g weft\n```\n";
91
+ expect(slugs(content)).toEqual(["#usage"]);
92
+ });
93
+ it("resumes finding headings after the fence closes", () => {
94
+ const content = "```\n# not a heading\n```\n\n## Real Heading\n";
95
+ expect(slugs(content)).toEqual(["#real-heading"]);
96
+ });
97
+ it("handles tilde fences and a longer closing marker", () => {
98
+ const content = "~~~\n# nope\n~~~\n\n## Yes\n";
99
+ expect(slugs(content)).toEqual(["#yes"]);
100
+ });
101
+ it("does not let a tilde fence close a backtick fence", () => {
102
+ const content = "```\n~~~\n# still code\n```\n\n## After\n";
103
+ expect(slugs(content)).toEqual(["#after"]);
104
+ });
105
+ });
106
+ describe("extractMarkdownTitle", () => {
107
+ it("extracts the first H1", () => {
108
+ expect(extractMarkdownTitle("# My Doc\n\n## Section")).toBe("My Doc");
109
+ });
110
+ it("returns undefined when no H1", () => {
111
+ expect(extractMarkdownTitle("## Not a title\n")).toBeUndefined();
112
+ });
113
+ it("extracts the H1 from CRLF content", () => {
114
+ expect(extractMarkdownTitle(crlf("# My Doc\n\n## Section\n"))).toBe("My Doc");
115
+ });
116
+ it("does not leave a trailing carriage return on the title", () => {
117
+ expect(extractMarkdownTitle(crlf("# My Doc\n"))).not.toMatch(/\r/);
118
+ });
119
+ });
120
+ describe("extractMarkdownDescription", () => {
121
+ it("extracts the first prose paragraph", () => {
122
+ const content = "# Title\n\nThis is the opening paragraph of the document.\n";
123
+ expect(extractMarkdownDescription(content)).toBe("This is the opening paragraph of the document.");
124
+ });
125
+ it("extracts the same paragraph from CRLF content", () => {
126
+ const content = "# Title\n\nThis is the opening paragraph of the document.\n";
127
+ expect(extractMarkdownDescription(crlf(content))).toBe(extractMarkdownDescription(content));
128
+ });
129
+ });
130
+ //# sourceMappingURL=markdown.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"markdown.test.js","sourceRoot":"","sources":["../../../src/anchors/__tests__/markdown.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=openapi.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"openapi.test.d.ts","sourceRoot":"","sources":["../../../src/anchors/__tests__/openapi.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,115 @@
1
+ import { describe, expect, it } from "vitest";
2
+ import { extractOpenApiAnchors, extractOpenApiTitle, openApiOperationAnchor, openApiSchemaAnchor, } from "../openapi.js";
3
+ describe("extractOpenApiAnchors", () => {
4
+ it("extracts operation IDs", () => {
5
+ const spec = `
6
+ openapi: "3.0.0"
7
+ info:
8
+ title: Test API
9
+ paths:
10
+ /users:
11
+ get:
12
+ operationId: listUsers
13
+ post:
14
+ operationId: createUser
15
+ `;
16
+ expect(extractOpenApiAnchors(spec)).toEqual([
17
+ { slug: "#listUsers", text: "listUsers" },
18
+ { slug: "#createUser", text: "createUser" },
19
+ ]);
20
+ });
21
+ it("extracts schema names from components", () => {
22
+ const spec = `
23
+ openapi: "3.0.0"
24
+ info:
25
+ title: Test API
26
+ components:
27
+ schemas:
28
+ User:
29
+ type: object
30
+ Order:
31
+ type: object
32
+ `;
33
+ expect(extractOpenApiAnchors(spec)).toEqual([
34
+ { slug: "#/components/schemas/User", text: "User" },
35
+ { slug: "#/components/schemas/Order", text: "Order" },
36
+ ]);
37
+ });
38
+ it("carries no line or level, having no source position", () => {
39
+ const spec = `
40
+ openapi: "3.0.0"
41
+ info:
42
+ title: Test API
43
+ paths:
44
+ /users:
45
+ get:
46
+ operationId: listUsers
47
+ `;
48
+ const [anchor] = extractOpenApiAnchors(spec);
49
+ expect(anchor.line).toBeUndefined();
50
+ expect(anchor.level).toBeUndefined();
51
+ });
52
+ it("falls back to path encoding when no operationId", () => {
53
+ const spec = `
54
+ openapi: "3.0.0"
55
+ info:
56
+ title: Test API
57
+ paths:
58
+ /users/{id}:
59
+ get:
60
+ summary: Get user
61
+ `;
62
+ const anchors = extractOpenApiAnchors(spec);
63
+ expect(anchors[0].slug).toMatch(/^#\/paths/);
64
+ expect(anchors[0].text).toBe("GET /users/{id}");
65
+ });
66
+ });
67
+ // The renderer stamps these same ids onto the elements it draws, by calling
68
+ // these same functions. If it computed them separately the two would agree only
69
+ // by coincidence, and an anchor would stop resolving the moment either changed.
70
+ describe("anchor ids shared with the renderer", () => {
71
+ it("uses the operation id when there is one", () => {
72
+ expect(openApiOperationAnchor("/users", "get", "listUsers")).toBe("listUsers");
73
+ });
74
+ it("falls back to an escaped path and method", () => {
75
+ expect(openApiOperationAnchor("/users/{id}", "get")).toBe("/paths~1users~1{id}/get");
76
+ });
77
+ it("builds a schema id from the name", () => {
78
+ expect(openApiSchemaAnchor("User")).toBe("/components/schemas/User");
79
+ });
80
+ it("produces exactly the anchors the extractor records, minus the leading #", () => {
81
+ const spec = `
82
+ openapi: "3.0.0"
83
+ info:
84
+ title: Test API
85
+ paths:
86
+ /users:
87
+ get:
88
+ operationId: listUsers
89
+ /users/{id}:
90
+ delete:
91
+ summary: No operation id here
92
+ components:
93
+ schemas:
94
+ User:
95
+ type: object
96
+ `;
97
+ const extracted = extractOpenApiAnchors(spec).map((a) => a.slug);
98
+ expect(extracted).toEqual([
99
+ `#${openApiOperationAnchor("/users", "get", "listUsers")}`,
100
+ `#${openApiOperationAnchor("/users/{id}", "delete")}`,
101
+ `#${openApiSchemaAnchor("User")}`,
102
+ ]);
103
+ });
104
+ });
105
+ describe("extractOpenApiTitle", () => {
106
+ it("extracts spec title", () => {
107
+ const spec = `
108
+ openapi: "3.0.0"
109
+ info:
110
+ title: My Cool API
111
+ `;
112
+ expect(extractOpenApiTitle(spec)).toBe("My Cool API");
113
+ });
114
+ });
115
+ //# sourceMappingURL=openapi.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"openapi.test.js","sourceRoot":"","sources":["../../../src/anchors/__tests__/openapi.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,QAAQ,CAAC;AAC9C,OAAO,EACN,qBAAqB,EACrB,mBAAmB,EACnB,sBAAsB,EACtB,mBAAmB,GACnB,MAAM,eAAe,CAAC;AAEvB,QAAQ,CAAC,uBAAuB,EAAE,GAAG,EAAE;IACtC,EAAE,CAAC,wBAAwB,EAAE,GAAG,EAAE;QACjC,MAAM,IAAI,GAAG;;;;;;;;;;CAUd,CAAC;QACA,MAAM,CAAC,qBAAqB,CAAC,IAAI,CAAC,CAAC,CAAC,OAAO,CAAC;YAC3C,EAAE,IAAI,EAAE,YAAY,EAAE,IAAI,EAAE,WAAW,EAAE;YACzC,EAAE,IAAI,EAAE,aAAa,EAAE,IAAI,EAAE,YAAY,EAAE;SAC3C,CAAC,CAAC;IACJ,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,uCAAuC,EAAE,GAAG,EAAE;QAChD,MAAM,IAAI,GAAG;;;;;;;;;;CAUd,CAAC;QACA,MAAM,CAAC,qBAAqB,CAAC,IAAI,CAAC,CAAC,CAAC,OAAO,CAAC;YAC3C,EAAE,IAAI,EAAE,2BAA2B,EAAE,IAAI,EAAE,MAAM,EAAE;YACnD,EAAE,IAAI,EAAE,4BAA4B,EAAE,IAAI,EAAE,OAAO,EAAE;SACrD,CAAC,CAAC;IACJ,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,qDAAqD,EAAE,GAAG,EAAE;QAC9D,MAAM,IAAI,GAAG;;;;;;;;CAQd,CAAC;QACA,MAAM,CAAC,MAAM,CAAC,GAAG,qBAAqB,CAAC,IAAI,CAAC,CAAC;QAC7C,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,aAAa,EAAE,CAAC;QACpC,MAAM,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,aAAa,EAAE,CAAC;IACtC,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,iDAAiD,EAAE,GAAG,EAAE;QAC1D,MAAM,IAAI,GAAG;;;;;;;;CAQd,CAAC;QACA,MAAM,OAAO,GAAG,qBAAqB,CAAC,IAAI,CAAC,CAAC;QAC5C,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,OAAO,CAAC,WAAW,CAAC,CAAC;QAC7C,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,iBAAiB,CAAC,CAAC;IACjD,CAAC,CAAC,CAAC;AACJ,CAAC,CAAC,CAAC;AAEH,4EAA4E;AAC5E,gFAAgF;AAChF,gFAAgF;AAChF,QAAQ,CAAC,qCAAqC,EAAE,GAAG,EAAE;IACpD,EAAE,CAAC,yCAAyC,EAAE,GAAG,EAAE;QAClD,MAAM,CAAC,sBAAsB,CAAC,QAAQ,EAAE,KAAK,EAAE,WAAW,CAAC,CAAC,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC;IAChF,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,0CAA0C,EAAE,GAAG,EAAE;QACnD,MAAM,CAAC,sBAAsB,CAAC,aAAa,EAAE,KAAK,CAAC,CAAC,CAAC,IAAI,CAAC,yBAAyB,CAAC,CAAC;IACtF,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,kCAAkC,EAAE,GAAG,EAAE;QAC3C,MAAM,CAAC,mBAAmB,CAAC,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,0BAA0B,CAAC,CAAC;IACtE,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,yEAAyE,EAAE,GAAG,EAAE;QAClF,MAAM,IAAI,GAAG;;;;;;;;;;;;;;;CAed,CAAC;QACA,MAAM,SAAS,GAAG,qBAAqB,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC;QAEjE,MAAM,CAAC,SAAS,CAAC,CAAC,OAAO,CAAC;YACzB,IAAI,sBAAsB,CAAC,QAAQ,EAAE,KAAK,EAAE,WAAW,CAAC,EAAE;YAC1D,IAAI,sBAAsB,CAAC,aAAa,EAAE,QAAQ,CAAC,EAAE;YACrD,IAAI,mBAAmB,CAAC,MAAM,CAAC,EAAE;SACjC,CAAC,CAAC;IACJ,CAAC,CAAC,CAAC;AACJ,CAAC,CAAC,CAAC;AAEH,QAAQ,CAAC,qBAAqB,EAAE,GAAG,EAAE;IACpC,EAAE,CAAC,qBAAqB,EAAE,GAAG,EAAE;QAC9B,MAAM,IAAI,GAAG;;;;CAId,CAAC;QACA,MAAM,CAAC,mBAAmB,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,aAAa,CAAC,CAAC;IACvD,CAAC,CAAC,CAAC;AACJ,CAAC,CAAC,CAAC"}
@@ -0,0 +1,35 @@
1
+ import type { Anchor, WeftConfig } from "../types.js";
2
+ export type DocType = "markdown" | "openapi";
3
+ /** Built-in extension-to-doc-type mapping. Answers "how do I parse this if handed it" — see `INDEXED_EXTENSIONS` below for "which files do I go looking for," a narrower, separate question. */
4
+ export declare const EXTENSION_MAP: Record<string, DocType>;
5
+ /**
6
+ * Merge the built-in map with a project's `extensions` config. Takes the
7
+ * config's extensions as plain data rather than a `WeftConfig`, so this module
8
+ * stays free of any config-loading dependency; callers resolve `config.extensions`
9
+ * themselves.
10
+ */
11
+ export declare function resolveExtensionMap(extensions?: Record<string, DocType>): Record<string, DocType>;
12
+ export declare function getDocType(filePath: string, extensionMap?: Record<string, DocType>): DocType | undefined;
13
+ /**
14
+ * File extensions the indexer turns into nodes.
15
+ *
16
+ * Shared with validation and freshness so none of them can drift: a check
17
+ * that an edge resolves has to know which link targets were ever eligible to
18
+ * become nodes, and the freshness baseline has to glob the same set the
19
+ * indexer does. Note this is narrower than `getDocType`, which also maps
20
+ * `.json`. A project can extend this set at runtime via `extensions` (see
21
+ * `resolveIndexedExtensions`), but the defaults here are deliberate and are
22
+ * never derived from `EXTENSION_MAP` — indexing `.json` by default, say,
23
+ * would turn today's correctly-ignored links to a missing `./schema.json`
24
+ * into `edge-target-missing` errors.
25
+ */
26
+ export declare const INDEXED_EXTENSIONS: readonly ["md", "markdown", "yaml", "yml"];
27
+ /** The extensions (no leading dot) the indexer scans for: the defaults plus anything `config.extensions` added. */
28
+ export declare function resolveIndexedExtensions(config: WeftConfig): string[];
29
+ /** True when a node id names a file the indexer would have turned into a node. */
30
+ export declare function isIndexedPath(path: string, indexedExtensions?: readonly string[]): boolean;
31
+ export declare function extractAnchors(content: string, docType: DocType): Anchor[];
32
+ export declare function extractTitle(content: string, docType: DocType): string | undefined;
33
+ export { extractMarkdownAnchors, extractMarkdownTitle } from "./markdown.js";
34
+ export { extractOpenApiAnchors, extractOpenApiTitle } from "./openapi.js";
35
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/anchors/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAItD,MAAM,MAAM,OAAO,GAAG,UAAU,GAAG,SAAS,CAAC;AAE7C,gMAAgM;AAChM,eAAO,MAAM,aAAa,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAMjD,CAAC;AAEF;;;;;GAKG;AACH,wBAAgB,mBAAmB,CAAC,UAAU,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAEjG;AAED,wBAAgB,UAAU,CACzB,QAAQ,EAAE,MAAM,EAChB,YAAY,GAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAiB,GACnD,OAAO,GAAG,SAAS,CAGrB;AAED;;;;;;;;;;;;GAYG;AACH,eAAO,MAAM,kBAAkB,4CAA6C,CAAC;AAE7E,mHAAmH;AACnH,wBAAgB,wBAAwB,CAAC,MAAM,EAAE,UAAU,GAAG,MAAM,EAAE,CAKrE;AAED,kFAAkF;AAClF,wBAAgB,aAAa,CAC5B,IAAI,EAAE,MAAM,EACZ,iBAAiB,GAAE,SAAS,MAAM,EAAuB,GACvD,OAAO,CAGT;AAED,wBAAgB,cAAc,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO,GAAG,MAAM,EAAE,CAO1E;AAED,wBAAgB,YAAY,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO,GAAG,MAAM,GAAG,SAAS,CAOlF;AAED,OAAO,EAAE,sBAAsB,EAAE,oBAAoB,EAAE,MAAM,eAAe,CAAC;AAC7E,OAAO,EAAE,qBAAqB,EAAE,mBAAmB,EAAE,MAAM,cAAc,CAAC"}
@@ -0,0 +1,68 @@
1
+ import { extractMarkdownAnchors, extractMarkdownTitle } from "./markdown.js";
2
+ import { extractOpenApiAnchors, extractOpenApiTitle } from "./openapi.js";
3
+ /** Built-in extension-to-doc-type mapping. Answers "how do I parse this if handed it" — see `INDEXED_EXTENSIONS` below for "which files do I go looking for," a narrower, separate question. */
4
+ export const EXTENSION_MAP = {
5
+ ".md": "markdown",
6
+ ".markdown": "markdown",
7
+ ".yaml": "openapi",
8
+ ".yml": "openapi",
9
+ ".json": "openapi",
10
+ };
11
+ /**
12
+ * Merge the built-in map with a project's `extensions` config. Takes the
13
+ * config's extensions as plain data rather than a `WeftConfig`, so this module
14
+ * stays free of any config-loading dependency; callers resolve `config.extensions`
15
+ * themselves.
16
+ */
17
+ export function resolveExtensionMap(extensions) {
18
+ return { ...EXTENSION_MAP, ...extensions };
19
+ }
20
+ export function getDocType(filePath, extensionMap = EXTENSION_MAP) {
21
+ const ext = filePath.slice(filePath.lastIndexOf(".")).toLowerCase();
22
+ return extensionMap[ext];
23
+ }
24
+ /**
25
+ * File extensions the indexer turns into nodes.
26
+ *
27
+ * Shared with validation and freshness so none of them can drift: a check
28
+ * that an edge resolves has to know which link targets were ever eligible to
29
+ * become nodes, and the freshness baseline has to glob the same set the
30
+ * indexer does. Note this is narrower than `getDocType`, which also maps
31
+ * `.json`. A project can extend this set at runtime via `extensions` (see
32
+ * `resolveIndexedExtensions`), but the defaults here are deliberate and are
33
+ * never derived from `EXTENSION_MAP` — indexing `.json` by default, say,
34
+ * would turn today's correctly-ignored links to a missing `./schema.json`
35
+ * into `edge-target-missing` errors.
36
+ */
37
+ export const INDEXED_EXTENSIONS = ["md", "markdown", "yaml", "yml"];
38
+ /** The extensions (no leading dot) the indexer scans for: the defaults plus anything `config.extensions` added. */
39
+ export function resolveIndexedExtensions(config) {
40
+ const configured = config.extensions
41
+ ? Object.keys(config.extensions).map((ext) => ext.slice(1).toLowerCase())
42
+ : [];
43
+ return [...INDEXED_EXTENSIONS, ...configured];
44
+ }
45
+ /** True when a node id names a file the indexer would have turned into a node. */
46
+ export function isIndexedPath(path, indexedExtensions = INDEXED_EXTENSIONS) {
47
+ const ext = path.slice(path.lastIndexOf(".") + 1).toLowerCase();
48
+ return indexedExtensions.includes(ext);
49
+ }
50
+ export function extractAnchors(content, docType) {
51
+ switch (docType) {
52
+ case "markdown":
53
+ return extractMarkdownAnchors(content);
54
+ case "openapi":
55
+ return extractOpenApiAnchors(content);
56
+ }
57
+ }
58
+ export function extractTitle(content, docType) {
59
+ switch (docType) {
60
+ case "markdown":
61
+ return extractMarkdownTitle(content);
62
+ case "openapi":
63
+ return extractOpenApiTitle(content);
64
+ }
65
+ }
66
+ export { extractMarkdownAnchors, extractMarkdownTitle } from "./markdown.js";
67
+ export { extractOpenApiAnchors, extractOpenApiTitle } from "./openapi.js";
68
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/anchors/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,sBAAsB,EAAE,oBAAoB,EAAE,MAAM,eAAe,CAAC;AAC7E,OAAO,EAAE,qBAAqB,EAAE,mBAAmB,EAAE,MAAM,cAAc,CAAC;AAI1E,gMAAgM;AAChM,MAAM,CAAC,MAAM,aAAa,GAA4B;IACrD,KAAK,EAAE,UAAU;IACjB,WAAW,EAAE,UAAU;IACvB,OAAO,EAAE,SAAS;IAClB,MAAM,EAAE,SAAS;IACjB,OAAO,EAAE,SAAS;CAClB,CAAC;AAEF;;;;;GAKG;AACH,MAAM,UAAU,mBAAmB,CAAC,UAAoC;IACvE,OAAO,EAAE,GAAG,aAAa,EAAE,GAAG,UAAU,EAAE,CAAC;AAC5C,CAAC;AAED,MAAM,UAAU,UAAU,CACzB,QAAgB,EAChB,eAAwC,aAAa;IAErD,MAAM,GAAG,GAAG,QAAQ,CAAC,KAAK,CAAC,QAAQ,CAAC,WAAW,CAAC,GAAG,CAAC,CAAC,CAAC,WAAW,EAAE,CAAC;IACpE,OAAO,YAAY,CAAC,GAAG,CAAC,CAAC;AAC1B,CAAC;AAED;;;;;;;;;;;;GAYG;AACH,MAAM,CAAC,MAAM,kBAAkB,GAAG,CAAC,IAAI,EAAE,UAAU,EAAE,MAAM,EAAE,KAAK,CAAU,CAAC;AAE7E,mHAAmH;AACnH,MAAM,UAAU,wBAAwB,CAAC,MAAkB;IAC1D,MAAM,UAAU,GAAG,MAAM,CAAC,UAAU;QACnC,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,WAAW,EAAE,CAAC;QACzE,CAAC,CAAC,EAAE,CAAC;IACN,OAAO,CAAC,GAAG,kBAAkB,EAAE,GAAG,UAAU,CAAC,CAAC;AAC/C,CAAC;AAED,kFAAkF;AAClF,MAAM,UAAU,aAAa,CAC5B,IAAY,EACZ,oBAAuC,kBAAkB;IAEzD,MAAM,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,WAAW,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,CAAC,WAAW,EAAE,CAAC;IAChE,OAAO,iBAAiB,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC;AACxC,CAAC;AAED,MAAM,UAAU,cAAc,CAAC,OAAe,EAAE,OAAgB;IAC/D,QAAQ,OAAO,EAAE,CAAC;QACjB,KAAK,UAAU;YACd,OAAO,sBAAsB,CAAC,OAAO,CAAC,CAAC;QACxC,KAAK,SAAS;YACb,OAAO,qBAAqB,CAAC,OAAO,CAAC,CAAC;IACxC,CAAC;AACF,CAAC;AAED,MAAM,UAAU,YAAY,CAAC,OAAe,EAAE,OAAgB;IAC7D,QAAQ,OAAO,EAAE,CAAC;QACjB,KAAK,UAAU;YACd,OAAO,oBAAoB,CAAC,OAAO,CAAC,CAAC;QACtC,KAAK,SAAS;YACb,OAAO,mBAAmB,CAAC,OAAO,CAAC,CAAC;IACtC,CAAC;AACF,CAAC;AAED,OAAO,EAAE,sBAAsB,EAAE,oBAAoB,EAAE,MAAM,eAAe,CAAC;AAC7E,OAAO,EAAE,qBAAqB,EAAE,mBAAmB,EAAE,MAAM,cAAc,CAAC"}
@@ -0,0 +1,15 @@
1
+ import type { Anchor } from "../types.js";
2
+ /**
3
+ * Extract heading anchors from Markdown content.
4
+ *
5
+ * Slugs come from `github-slugger`, the implementation GitHub's own rendering
6
+ * uses, rather than an approximation of it: links are authored against how the
7
+ * document renders on GitHub (DD-2), so GitHub's slugs are the correct ones.
8
+ * The slugger also owns the `-1`, `-2` suffixes it appends to repeated slugs.
9
+ */
10
+ export declare function extractMarkdownAnchors(content: string): Anchor[];
11
+ /** Extract the first prose paragraph as a plain-text description. */
12
+ export declare function extractMarkdownDescription(content: string): string | undefined;
13
+ /** Extract the title (first H1) from Markdown content. */
14
+ export declare function extractMarkdownTitle(content: string): string | undefined;
15
+ //# sourceMappingURL=markdown.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"markdown.d.ts","sourceRoot":"","sources":["../../src/anchors/markdown.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AAa1C;;;;;;;GAOG;AACH,wBAAgB,sBAAsB,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,EAAE,CAwBhE;AAED,qEAAqE;AACrE,wBAAgB,0BAA0B,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAe9E;AAED,0DAA0D;AAC1D,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAMxE"}
@@ -0,0 +1,71 @@
1
+ import GithubSlugger from "github-slugger";
2
+ import { toString as mdastToString } from "mdast-util-to-string";
3
+ import remarkParse from "remark-parse";
4
+ import { unified } from "unified";
5
+ import { visit } from "unist-util-visit";
6
+ /**
7
+ * Split content into lines, tolerating CRLF.
8
+ *
9
+ * A trailing `\r` would otherwise survive into every line: `.` does not match
10
+ * `\r` and `$` (no `m` flag) only matches at end of input, so the line patterns
11
+ * below silently fail on any file checked out with Windows line endings.
12
+ */
13
+ function toLines(content) {
14
+ return content.split(/\r?\n/);
15
+ }
16
+ /**
17
+ * Extract heading anchors from Markdown content.
18
+ *
19
+ * Slugs come from `github-slugger`, the implementation GitHub's own rendering
20
+ * uses, rather than an approximation of it: links are authored against how the
21
+ * document renders on GitHub (DD-2), so GitHub's slugs are the correct ones.
22
+ * The slugger also owns the `-1`, `-2` suffixes it appends to repeated slugs.
23
+ */
24
+ export function extractMarkdownAnchors(content) {
25
+ const tree = unified().use(remarkParse).parse(content);
26
+ const slugger = new GithubSlugger();
27
+ const anchors = [];
28
+ visit(tree, "heading", (node) => {
29
+ // Slug the heading's *rendered* text, not its source line. GitHub slugs
30
+ // what it rendered, so `## See [docs](x.md)` is `#see-docs` — slugging the
31
+ // raw line would produce `#see-docsxmd` and the id in the page would never
32
+ // match the anchor in the graph.
33
+ //
34
+ // Image alt text and raw HTML are excluded to match `hast-util-to-string`,
35
+ // which is what the renderer's slug plugin sees.
36
+ const text = mdastToString(node, { includeImageAlt: false, includeHtml: false });
37
+ anchors.push({
38
+ slug: `#${slugger.slug(text)}`,
39
+ text,
40
+ line: node.position?.start.line ?? 0,
41
+ level: node.depth,
42
+ });
43
+ });
44
+ return anchors;
45
+ }
46
+ /** Extract the first prose paragraph as a plain-text description. */
47
+ export function extractMarkdownDescription(content) {
48
+ for (const line of toLines(content)) {
49
+ const trimmed = line.trim();
50
+ if (!trimmed ||
51
+ trimmed.startsWith("#") ||
52
+ trimmed.startsWith(">") ||
53
+ trimmed.startsWith("-") ||
54
+ trimmed.startsWith("|"))
55
+ continue;
56
+ const plain = trimmed.replace(/\[([^\]]+)\]\([^)]+\)/g, "$1").replace(/[*_`]/g, "");
57
+ if (plain.length > 20)
58
+ return plain.slice(0, 200);
59
+ }
60
+ return undefined;
61
+ }
62
+ /** Extract the title (first H1) from Markdown content. */
63
+ export function extractMarkdownTitle(content) {
64
+ for (const line of toLines(content)) {
65
+ const match = line.match(/^#\s+(.+)$/);
66
+ if (match)
67
+ return match[1].trim();
68
+ }
69
+ return undefined;
70
+ }
71
+ //# sourceMappingURL=markdown.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"markdown.js","sourceRoot":"","sources":["../../src/anchors/markdown.ts"],"names":[],"mappings":"AAAA,OAAO,aAAa,MAAM,gBAAgB,CAAC;AAE3C,OAAO,EAAE,QAAQ,IAAI,aAAa,EAAE,MAAM,sBAAsB,CAAC;AACjE,OAAO,WAAW,MAAM,cAAc,CAAC;AACvC,OAAO,EAAE,OAAO,EAAE,MAAM,SAAS,CAAC;AAClC,OAAO,EAAE,KAAK,EAAE,MAAM,kBAAkB,CAAC;AAGzC;;;;;;GAMG;AACH,SAAS,OAAO,CAAC,OAAe;IAC/B,OAAO,OAAO,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;AAC/B,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,UAAU,sBAAsB,CAAC,OAAe;IACrD,MAAM,IAAI,GAAG,OAAO,EAAE,CAAC,GAAG,CAAC,WAAW,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;IACvD,MAAM,OAAO,GAAG,IAAI,aAAa,EAAE,CAAC;IACpC,MAAM,OAAO,GAAa,EAAE,CAAC;IAE7B,KAAK,CAAC,IAAI,EAAE,SAAS,EAAE,CAAC,IAAa,EAAE,EAAE;QACxC,wEAAwE;QACxE,2EAA2E;QAC3E,2EAA2E;QAC3E,iCAAiC;QACjC,EAAE;QACF,2EAA2E;QAC3E,iDAAiD;QACjD,MAAM,IAAI,GAAG,aAAa,CAAC,IAAI,EAAE,EAAE,eAAe,EAAE,KAAK,EAAE,WAAW,EAAE,KAAK,EAAE,CAAC,CAAC;QAEjF,OAAO,CAAC,IAAI,CAAC;YACZ,IAAI,EAAE,IAAI,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE;YAC9B,IAAI;YACJ,IAAI,EAAE,IAAI,CAAC,QAAQ,EAAE,KAAK,CAAC,IAAI,IAAI,CAAC;YACpC,KAAK,EAAE,IAAI,CAAC,KAAK;SACjB,CAAC,CAAC;IACJ,CAAC,CAAC,CAAC;IAEH,OAAO,OAAO,CAAC;AAChB,CAAC;AAED,qEAAqE;AACrE,MAAM,UAAU,0BAA0B,CAAC,OAAe;IACzD,KAAK,MAAM,IAAI,IAAI,OAAO,CAAC,OAAO,CAAC,EAAE,CAAC;QACrC,MAAM,OAAO,GAAG,IAAI,CAAC,IAAI,EAAE,CAAC;QAC5B,IACC,CAAC,OAAO;YACR,OAAO,CAAC,UAAU,CAAC,GAAG,CAAC;YACvB,OAAO,CAAC,UAAU,CAAC,GAAG,CAAC;YACvB,OAAO,CAAC,UAAU,CAAC,GAAG,CAAC;YACvB,OAAO,CAAC,UAAU,CAAC,GAAG,CAAC;YAEvB,SAAS;QACV,MAAM,KAAK,GAAG,OAAO,CAAC,OAAO,CAAC,wBAAwB,EAAE,IAAI,CAAC,CAAC,OAAO,CAAC,QAAQ,EAAE,EAAE,CAAC,CAAC;QACpF,IAAI,KAAK,CAAC,MAAM,GAAG,EAAE;YAAE,OAAO,KAAK,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC;IACnD,CAAC;IACD,OAAO,SAAS,CAAC;AAClB,CAAC;AAED,0DAA0D;AAC1D,MAAM,UAAU,oBAAoB,CAAC,OAAe;IACnD,KAAK,MAAM,IAAI,IAAI,OAAO,CAAC,OAAO,CAAC,EAAE,CAAC;QACrC,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,CAAC;QACvC,IAAI,KAAK;YAAE,OAAO,KAAK,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;IACnC,CAAC;IACD,OAAO,SAAS,CAAC;AAClB,CAAC"}
@@ -0,0 +1,24 @@
1
+ import type { Anchor } from "../types.js";
2
+ /** Parse an OpenAPI YAML/JSON spec and return the raw object, or undefined if invalid. */
3
+ export declare function parseOpenApiSpec(content: string): Record<string, unknown> | undefined;
4
+ /**
5
+ * Anchor slug for one operation, without the leading `#`.
6
+ *
7
+ * Exported because the renderer stamps this onto the element it draws: if the
8
+ * two computed it separately they would only agree by coincidence, and an
9
+ * anchor in the graph would stop resolving the moment either changed.
10
+ */
11
+ export declare function openApiOperationAnchor(path: string, method: string, operationId?: string): string;
12
+ /** Anchor slug for one schema, without the leading `#`. */
13
+ export declare function openApiSchemaAnchor(name: string): string;
14
+ /**
15
+ * Extract anchors from an OpenAPI YAML/JSON spec: operation IDs and schema names.
16
+ *
17
+ * These carry no `line` or `level`: they are positions in a parsed object, not
18
+ * headings in a source file, and the parser discards source offsets. `text` is
19
+ * the operation id or schema name the slug was built from.
20
+ */
21
+ export declare function extractOpenApiAnchors(content: string): Anchor[];
22
+ /** Extract the title from an OpenAPI spec. */
23
+ export declare function extractOpenApiTitle(content: string): string | undefined;
24
+ //# sourceMappingURL=openapi.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"openapi.d.ts","sourceRoot":"","sources":["../../src/anchors/openapi.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AAE1C,0FAA0F;AAC1F,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,SAAS,CAQrF;AAED;;;;;;GAMG;AACH,wBAAgB,sBAAsB,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,WAAW,CAAC,EAAE,MAAM,GAAG,MAAM,CAEjG;AAED,2DAA2D;AAC3D,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAExD;AAED;;;;;;GAMG;AACH,wBAAgB,qBAAqB,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,EAAE,CA+B/D;AAED,8CAA8C;AAC9C,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAGvE"}
@@ -0,0 +1,75 @@
1
+ import { parse } from "yaml";
2
+ /** Parse an OpenAPI YAML/JSON spec and return the raw object, or undefined if invalid. */
3
+ export function parseOpenApiSpec(content) {
4
+ try {
5
+ const spec = parse(content);
6
+ if (!spec || typeof spec !== "object")
7
+ return undefined;
8
+ return spec;
9
+ }
10
+ catch {
11
+ return undefined;
12
+ }
13
+ }
14
+ /**
15
+ * Anchor slug for one operation, without the leading `#`.
16
+ *
17
+ * Exported because the renderer stamps this onto the element it draws: if the
18
+ * two computed it separately they would only agree by coincidence, and an
19
+ * anchor in the graph would stop resolving the moment either changed.
20
+ */
21
+ export function openApiOperationAnchor(path, method, operationId) {
22
+ return operationId ?? `/paths${path.replace(/\//g, "~1")}/${method}`;
23
+ }
24
+ /** Anchor slug for one schema, without the leading `#`. */
25
+ export function openApiSchemaAnchor(name) {
26
+ return `/components/schemas/${name}`;
27
+ }
28
+ /**
29
+ * Extract anchors from an OpenAPI YAML/JSON spec: operation IDs and schema names.
30
+ *
31
+ * These carry no `line` or `level`: they are positions in a parsed object, not
32
+ * headings in a source file, and the parser discards source offsets. `text` is
33
+ * the operation id or schema name the slug was built from.
34
+ */
35
+ export function extractOpenApiAnchors(content) {
36
+ const spec = parse(content);
37
+ if (!spec || typeof spec !== "object")
38
+ return [];
39
+ const anchors = [];
40
+ // Extract from paths — operation IDs or method+path combos
41
+ if (spec.paths && typeof spec.paths === "object") {
42
+ for (const [path, methods] of Object.entries(spec.paths)) {
43
+ if (!methods || typeof methods !== "object")
44
+ continue;
45
+ for (const [method, operation] of Object.entries(methods)) {
46
+ if (!operation || typeof operation !== "object" || !isHttpMethod(method))
47
+ continue;
48
+ const op = operation;
49
+ const operationId = typeof op.operationId === "string" ? op.operationId : undefined;
50
+ anchors.push({
51
+ slug: `#${openApiOperationAnchor(path, method, operationId)}`,
52
+ text: operationId ?? `${method.toUpperCase()} ${path}`,
53
+ });
54
+ }
55
+ }
56
+ }
57
+ // Extract from components/schemas
58
+ const schemas = spec.components?.schemas ?? spec.definitions; // OpenAPI 2.x
59
+ if (schemas && typeof schemas === "object") {
60
+ for (const name of Object.keys(schemas)) {
61
+ anchors.push({ slug: `#${openApiSchemaAnchor(name)}`, text: name });
62
+ }
63
+ }
64
+ return anchors;
65
+ }
66
+ /** Extract the title from an OpenAPI spec. */
67
+ export function extractOpenApiTitle(content) {
68
+ const spec = parse(content);
69
+ return spec?.info?.title;
70
+ }
71
+ const HTTP_METHODS = new Set(["get", "post", "put", "patch", "delete", "head", "options", "trace"]);
72
+ function isHttpMethod(method) {
73
+ return HTTP_METHODS.has(method.toLowerCase());
74
+ }
75
+ //# sourceMappingURL=openapi.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"openapi.js","sourceRoot":"","sources":["../../src/anchors/openapi.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,10 @@
1
+ /**
2
+ * Browser-safe exports from @lepid-labs/weft-core — no Node.js built-ins.
3
+ * Use this entry point in browser/embed builds.
4
+ */
5
+ export { openApiOperationAnchor, openApiSchemaAnchor, parseOpenApiSpec, } from "./anchors/openapi.js";
6
+ export { nodeIdToDocPath } from "./node-path.js";
7
+ export { INCLUDES, INCLUDE_DEFAULTS, extractSection } from "./includes.js";
8
+ export type { SectionRange } from "./includes.js";
9
+ export type { Anchor, Assertions, IncludeContributes, IncludeDefaults, IncludeHeadingShift, Manifest, ProjectManifest, ProjectsIndex, WeftNode, WeftEdge, WeftProject, WeftProjectRef, LinkRef, SearchResult, SiteConfig, StyleConfig, WeftConfig, } from "./types.js";
10
+ //# sourceMappingURL=browser.d.ts.map