@linxiraos/pi-utils 1.0.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 (244) hide show
  1. package/CHANGELOG.md +449 -0
  2. package/README.md +36 -0
  3. package/dist/types/abortable.d.ts +32 -0
  4. package/dist/types/acp/connection.d.ts +118 -0
  5. package/dist/types/acp/protocol.d.ts +526 -0
  6. package/dist/types/acp/schema.d.ts +41 -0
  7. package/dist/types/acp/stream.d.ts +8 -0
  8. package/dist/types/acp/transport.d.ts +81 -0
  9. package/dist/types/acp.d.ts +6 -0
  10. package/dist/types/async.d.ts +20 -0
  11. package/dist/types/binary.d.ts +17 -0
  12. package/dist/types/browsers.d.ts +68 -0
  13. package/dist/types/chalk.d.ts +125 -0
  14. package/dist/types/cli.d.ts +134 -0
  15. package/dist/types/color.d.ts +102 -0
  16. package/dist/types/dates.d.ts +7 -0
  17. package/dist/types/dirs.d.ts +282 -0
  18. package/dist/types/docx/converter.d.ts +46 -0
  19. package/dist/types/docx/xml.d.ts +26 -0
  20. package/dist/types/docx/zip.d.ts +6 -0
  21. package/dist/types/docx.d.ts +11 -0
  22. package/dist/types/dom/core.d.ts +431 -0
  23. package/dist/types/dom/parser.d.ts +7 -0
  24. package/dist/types/dom/selector.d.ts +5 -0
  25. package/dist/types/dom.d.ts +5 -0
  26. package/dist/types/env.d.ts +124 -0
  27. package/dist/types/fetch-retry.d.ts +96 -0
  28. package/dist/types/file-lock.d.ts +23 -0
  29. package/dist/types/format.d.ts +37 -0
  30. package/dist/types/frontmatter.d.ts +46 -0
  31. package/dist/types/fs-error.d.ts +31 -0
  32. package/dist/types/glob.d.ts +28 -0
  33. package/dist/types/headers.d.ts +34 -0
  34. package/dist/types/index.d.ts +39 -0
  35. package/dist/types/json-parse.d.ts +74 -0
  36. package/dist/types/json.d.ts +14 -0
  37. package/dist/types/logger/rotating-file.d.ts +18 -0
  38. package/dist/types/logger.d.ts +96 -0
  39. package/dist/types/loop-phase.d.ts +10 -0
  40. package/dist/types/lru.d.ts +46 -0
  41. package/dist/types/marked/core.d.ts +445 -0
  42. package/dist/types/marked.d.ts +2 -0
  43. package/dist/types/mermaid-ascii.d.ts +11 -0
  44. package/dist/types/mime.d.ts +29 -0
  45. package/dist/types/module-timer.d.ts +1 -0
  46. package/dist/types/path-tree.d.ts +76 -0
  47. package/dist/types/path.d.ts +2 -0
  48. package/dist/types/peek-file.d.ts +29 -0
  49. package/dist/types/postmortem.d.ts +104 -0
  50. package/dist/types/process-name.d.ts +7 -0
  51. package/dist/types/procmgr.d.ts +74 -0
  52. package/dist/types/prompt.d.ts +18 -0
  53. package/dist/types/ptree.d.ts +110 -0
  54. package/dist/types/readability/readability.d.ts +9 -0
  55. package/dist/types/readability/readerable.d.ts +10 -0
  56. package/dist/types/readability/types.d.ts +70 -0
  57. package/dist/types/readability.d.ts +4 -0
  58. package/dist/types/ring.d.ts +93 -0
  59. package/dist/types/runtime-install.d.ts +78 -0
  60. package/dist/types/sanitize-text.d.ts +29 -0
  61. package/dist/types/snowflake.d.ts +25 -0
  62. package/dist/types/stderr-guard.d.ts +22 -0
  63. package/dist/types/stream.d.ts +67 -0
  64. package/dist/types/tab-spacing.d.ts +24 -0
  65. package/dist/types/temp.d.ts +17 -0
  66. package/dist/types/template.d.ts +62 -0
  67. package/dist/types/timing-buffer.d.ts +22 -0
  68. package/dist/types/tls-fetch.d.ts +37 -0
  69. package/dist/types/turndown/gfm.d.ts +11 -0
  70. package/dist/types/turndown/html.d.ts +5 -0
  71. package/dist/types/turndown/service.d.ts +21 -0
  72. package/dist/types/turndown/types.d.ts +70 -0
  73. package/dist/types/turndown.d.ts +4 -0
  74. package/dist/types/type-guards.d.ts +5 -0
  75. package/dist/types/vendor/mermaid-ascii/ascii/ansi.d.ts +41 -0
  76. package/dist/types/vendor/mermaid-ascii/ascii/canvas.d.ts +89 -0
  77. package/dist/types/vendor/mermaid-ascii/ascii/class-diagram.d.ts +7 -0
  78. package/dist/types/vendor/mermaid-ascii/ascii/converter.d.ts +12 -0
  79. package/dist/types/vendor/mermaid-ascii/ascii/draw.d.ts +66 -0
  80. package/dist/types/vendor/mermaid-ascii/ascii/edge-bundling.d.ts +48 -0
  81. package/dist/types/vendor/mermaid-ascii/ascii/edge-routing.d.ts +43 -0
  82. package/dist/types/vendor/mermaid-ascii/ascii/er-diagram.d.ts +7 -0
  83. package/dist/types/vendor/mermaid-ascii/ascii/grid.d.ts +56 -0
  84. package/dist/types/vendor/mermaid-ascii/ascii/index.d.ts +65 -0
  85. package/dist/types/vendor/mermaid-ascii/ascii/multiline-utils.d.ts +27 -0
  86. package/dist/types/vendor/mermaid-ascii/ascii/pathfinder.d.ts +17 -0
  87. package/dist/types/vendor/mermaid-ascii/ascii/sequence.d.ts +7 -0
  88. package/dist/types/vendor/mermaid-ascii/ascii/shapes/circle.d.ts +11 -0
  89. package/dist/types/vendor/mermaid-ascii/ascii/shapes/corners.d.ts +34 -0
  90. package/dist/types/vendor/mermaid-ascii/ascii/shapes/diamond.d.ts +11 -0
  91. package/dist/types/vendor/mermaid-ascii/ascii/shapes/hexagon.d.ts +11 -0
  92. package/dist/types/vendor/mermaid-ascii/ascii/shapes/index.d.ts +26 -0
  93. package/dist/types/vendor/mermaid-ascii/ascii/shapes/rectangle.d.ts +31 -0
  94. package/dist/types/vendor/mermaid-ascii/ascii/shapes/rounded.d.ts +11 -0
  95. package/dist/types/vendor/mermaid-ascii/ascii/shapes/special.d.ts +59 -0
  96. package/dist/types/vendor/mermaid-ascii/ascii/shapes/stadium.d.ts +17 -0
  97. package/dist/types/vendor/mermaid-ascii/ascii/shapes/state.d.ts +30 -0
  98. package/dist/types/vendor/mermaid-ascii/ascii/shapes/types.d.ts +55 -0
  99. package/dist/types/vendor/mermaid-ascii/ascii/types.d.ts +206 -0
  100. package/dist/types/vendor/mermaid-ascii/ascii/validate.d.ts +51 -0
  101. package/dist/types/vendor/mermaid-ascii/ascii/xychart.d.ts +2 -0
  102. package/dist/types/vendor/mermaid-ascii/class/parser.d.ts +6 -0
  103. package/dist/types/vendor/mermaid-ascii/class/types.d.ts +102 -0
  104. package/dist/types/vendor/mermaid-ascii/er/parser.d.ts +6 -0
  105. package/dist/types/vendor/mermaid-ascii/er/types.d.ts +76 -0
  106. package/dist/types/vendor/mermaid-ascii/index.d.ts +1 -0
  107. package/dist/types/vendor/mermaid-ascii/multiline-utils.d.ts +9 -0
  108. package/dist/types/vendor/mermaid-ascii/parser.d.ts +7 -0
  109. package/dist/types/vendor/mermaid-ascii/sequence/parser.d.ts +6 -0
  110. package/dist/types/vendor/mermaid-ascii/sequence/types.d.ts +130 -0
  111. package/dist/types/vendor/mermaid-ascii/text-metrics.d.ts +21 -0
  112. package/dist/types/vendor/mermaid-ascii/types.d.ts +114 -0
  113. package/dist/types/vendor/mermaid-ascii/xychart/colors.d.ts +25 -0
  114. package/dist/types/vendor/mermaid-ascii/xychart/parser.d.ts +6 -0
  115. package/dist/types/vendor/mermaid-ascii/xychart/types.d.ts +145 -0
  116. package/dist/types/version.d.ts +18 -0
  117. package/dist/types/vterm/buffer.d.ts +99 -0
  118. package/dist/types/vterm/terminal.d.ts +44 -0
  119. package/dist/types/vterm.d.ts +8 -0
  120. package/dist/types/which.d.ts +37 -0
  121. package/dist/types/worker-host.d.ts +51 -0
  122. package/dist/types/xml.d.ts +31 -0
  123. package/package.json +59 -0
  124. package/src/abortable.ts +109 -0
  125. package/src/acp/connection.ts +344 -0
  126. package/src/acp/protocol.ts +466 -0
  127. package/src/acp/schema.ts +160 -0
  128. package/src/acp/stream.ts +82 -0
  129. package/src/acp/transport.ts +213 -0
  130. package/src/acp.ts +6 -0
  131. package/src/async.ts +91 -0
  132. package/src/binary.ts +50 -0
  133. package/src/browsers.ts +501 -0
  134. package/src/chalk.ts +312 -0
  135. package/src/cli.ts +529 -0
  136. package/src/color.ts +302 -0
  137. package/src/dates.ts +194 -0
  138. package/src/dirs.ts +1026 -0
  139. package/src/docx/converter.ts +681 -0
  140. package/src/docx/xml.ts +166 -0
  141. package/src/docx/zip.ts +87 -0
  142. package/src/docx.ts +20 -0
  143. package/src/dom/core.ts +1254 -0
  144. package/src/dom/parser.ts +370 -0
  145. package/src/dom/selector.ts +290 -0
  146. package/src/dom.ts +33 -0
  147. package/src/env.ts +400 -0
  148. package/src/fetch-retry.ts +379 -0
  149. package/src/file-lock.ts +69 -0
  150. package/src/format.ts +113 -0
  151. package/src/frontmatter.ts +205 -0
  152. package/src/fs-error.ts +56 -0
  153. package/src/glob.ts +189 -0
  154. package/src/headers.ts +167 -0
  155. package/src/index.ts +59 -0
  156. package/src/json-parse.ts +821 -0
  157. package/src/json.ts +23 -0
  158. package/src/logger/rotating-file.ts +149 -0
  159. package/src/logger.ts +798 -0
  160. package/src/loop-phase.ts +49 -0
  161. package/src/lru.ts +185 -0
  162. package/src/marked/core.ts +1576 -0
  163. package/src/marked.ts +2 -0
  164. package/src/mermaid-ascii.ts +31 -0
  165. package/src/mime.ts +159 -0
  166. package/src/module-timer.ts +148 -0
  167. package/src/path-tree.ts +147 -0
  168. package/src/path.ts +28 -0
  169. package/src/peek-file.ts +188 -0
  170. package/src/postmortem.ts +433 -0
  171. package/src/process-name.ts +57 -0
  172. package/src/procmgr.ts +247 -0
  173. package/src/prompt.ts +545 -0
  174. package/src/ptree.ts +412 -0
  175. package/src/readability/readability.ts +533 -0
  176. package/src/readability/readerable.ts +51 -0
  177. package/src/readability/types.ts +72 -0
  178. package/src/readability.ts +11 -0
  179. package/src/ring.ts +169 -0
  180. package/src/runtime-install.ts +393 -0
  181. package/src/sanitize-text.ts +96 -0
  182. package/src/snowflake.ts +121 -0
  183. package/src/stderr-guard.ts +155 -0
  184. package/src/stream.ts +454 -0
  185. package/src/tab-spacing.ts +336 -0
  186. package/src/temp.ts +134 -0
  187. package/src/template.ts +586 -0
  188. package/src/timing-buffer.ts +47 -0
  189. package/src/tls-fetch.ts +178 -0
  190. package/src/turndown/gfm.ts +106 -0
  191. package/src/turndown/html.ts +257 -0
  192. package/src/turndown/service.ts +334 -0
  193. package/src/turndown/types.ts +81 -0
  194. package/src/turndown.ts +5 -0
  195. package/src/type-guards.ts +17 -0
  196. package/src/vendor/mermaid-ascii/NOTICE +33 -0
  197. package/src/vendor/mermaid-ascii/ascii/ansi.ts +409 -0
  198. package/src/vendor/mermaid-ascii/ascii/canvas.ts +476 -0
  199. package/src/vendor/mermaid-ascii/ascii/class-diagram.ts +699 -0
  200. package/src/vendor/mermaid-ascii/ascii/converter.ts +271 -0
  201. package/src/vendor/mermaid-ascii/ascii/draw.ts +1382 -0
  202. package/src/vendor/mermaid-ascii/ascii/edge-bundling.ts +328 -0
  203. package/src/vendor/mermaid-ascii/ascii/edge-routing.ts +297 -0
  204. package/src/vendor/mermaid-ascii/ascii/er-diagram.ts +441 -0
  205. package/src/vendor/mermaid-ascii/ascii/grid.ts +578 -0
  206. package/src/vendor/mermaid-ascii/ascii/index.ts +187 -0
  207. package/src/vendor/mermaid-ascii/ascii/multiline-utils.ts +78 -0
  208. package/src/vendor/mermaid-ascii/ascii/pathfinder.ts +277 -0
  209. package/src/vendor/mermaid-ascii/ascii/sequence.ts +460 -0
  210. package/src/vendor/mermaid-ascii/ascii/shapes/circle.ts +27 -0
  211. package/src/vendor/mermaid-ascii/ascii/shapes/corners.ts +127 -0
  212. package/src/vendor/mermaid-ascii/ascii/shapes/diamond.ts +27 -0
  213. package/src/vendor/mermaid-ascii/ascii/shapes/hexagon.ts +27 -0
  214. package/src/vendor/mermaid-ascii/ascii/shapes/index.ts +101 -0
  215. package/src/vendor/mermaid-ascii/ascii/shapes/rectangle.ts +175 -0
  216. package/src/vendor/mermaid-ascii/ascii/shapes/rounded.ts +27 -0
  217. package/src/vendor/mermaid-ascii/ascii/shapes/special.ts +296 -0
  218. package/src/vendor/mermaid-ascii/ascii/shapes/stadium.ts +114 -0
  219. package/src/vendor/mermaid-ascii/ascii/shapes/state.ts +192 -0
  220. package/src/vendor/mermaid-ascii/ascii/shapes/types.ts +73 -0
  221. package/src/vendor/mermaid-ascii/ascii/types.ts +273 -0
  222. package/src/vendor/mermaid-ascii/ascii/validate.ts +120 -0
  223. package/src/vendor/mermaid-ascii/ascii/xychart.ts +875 -0
  224. package/src/vendor/mermaid-ascii/class/parser.ts +290 -0
  225. package/src/vendor/mermaid-ascii/class/types.ts +121 -0
  226. package/src/vendor/mermaid-ascii/er/parser.ts +181 -0
  227. package/src/vendor/mermaid-ascii/er/types.ts +91 -0
  228. package/src/vendor/mermaid-ascii/index.ts +14 -0
  229. package/src/vendor/mermaid-ascii/multiline-utils.ts +30 -0
  230. package/src/vendor/mermaid-ascii/parser.ts +645 -0
  231. package/src/vendor/mermaid-ascii/sequence/parser.ts +207 -0
  232. package/src/vendor/mermaid-ascii/sequence/types.ts +146 -0
  233. package/src/vendor/mermaid-ascii/text-metrics.ts +71 -0
  234. package/src/vendor/mermaid-ascii/types.ts +164 -0
  235. package/src/vendor/mermaid-ascii/xychart/colors.ts +140 -0
  236. package/src/vendor/mermaid-ascii/xychart/parser.ts +115 -0
  237. package/src/vendor/mermaid-ascii/xychart/types.ts +150 -0
  238. package/src/version.ts +99 -0
  239. package/src/vterm/buffer.ts +218 -0
  240. package/src/vterm/terminal.ts +773 -0
  241. package/src/vterm.ts +8 -0
  242. package/src/which.ts +234 -0
  243. package/src/worker-host.ts +100 -0
  244. package/src/xml.ts +313 -0
@@ -0,0 +1,334 @@
1
+ import { parseHtmlFragment, serializeNode } from "./html";
2
+ import type {
3
+ ResolvedTurndownOptions,
4
+ RuleFilter,
5
+ TurndownNode,
6
+ TurndownOptions,
7
+ TurndownPlugin,
8
+ TurndownRule,
9
+ } from "./types";
10
+
11
+ const BLOCK_ELEMENTS: Readonly<Record<string, true>> = {
12
+ ADDRESS: true,
13
+ ARTICLE: true,
14
+ ASIDE: true,
15
+ BLOCKQUOTE: true,
16
+ BODY: true,
17
+ CANVAS: true,
18
+ CENTER: true,
19
+ DD: true,
20
+ DETAILS: true,
21
+ DIR: true,
22
+ DIV: true,
23
+ DL: true,
24
+ DT: true,
25
+ FIELDSET: true,
26
+ FIGCAPTION: true,
27
+ FIGURE: true,
28
+ FOOTER: true,
29
+ FORM: true,
30
+ FRAMESET: true,
31
+ H1: true,
32
+ H2: true,
33
+ H3: true,
34
+ H4: true,
35
+ H5: true,
36
+ H6: true,
37
+ HEADER: true,
38
+ HGROUP: true,
39
+ HR: true,
40
+ HTML: true,
41
+ ISINDEX: true,
42
+ LI: true,
43
+ MAIN: true,
44
+ MENU: true,
45
+ NAV: true,
46
+ NOFRAMES: true,
47
+ NOSCRIPT: true,
48
+ OL: true,
49
+ OUTPUT: true,
50
+ P: true,
51
+ PRE: true,
52
+ SECTION: true,
53
+ SUMMARY: true,
54
+ TABLE: true,
55
+ TBODY: true,
56
+ TD: true,
57
+ TFOOT: true,
58
+ TH: true,
59
+ THEAD: true,
60
+ TR: true,
61
+ UL: true,
62
+ };
63
+ const NONBLANK_EMPTY_ELEMENTS: Readonly<Record<string, true>> = {
64
+ A: true,
65
+ AUDIO: true,
66
+ BR: true,
67
+ HR: true,
68
+ IFRAME: true,
69
+ IMG: true,
70
+ INPUT: true,
71
+ SCRIPT: true,
72
+ SOURCE: true,
73
+ TD: true,
74
+ TH: true,
75
+ VIDEO: true,
76
+ };
77
+
78
+ type RuleEntry = { key: string; rule: TurndownRule };
79
+ type Reference = { destination: string };
80
+
81
+ const DEFAULT_OPTIONS: ResolvedTurndownOptions = {
82
+ headingStyle: "setext",
83
+ hr: "* * *",
84
+ bulletListMarker: "*",
85
+ codeBlockStyle: "indented",
86
+ fence: "```",
87
+ emDelimiter: "_",
88
+ strongDelimiter: "**",
89
+ linkStyle: "inlined",
90
+ linkReferenceStyle: "full",
91
+ preformattedCode: false,
92
+ };
93
+
94
+ function matchesFilter(filter: RuleFilter, node: TurndownNode, options: ResolvedTurndownOptions): boolean {
95
+ if (typeof filter === "function") return filter(node, options);
96
+ const name = node.nodeName.toLowerCase();
97
+ if (typeof filter === "string") return name === filter.toLowerCase();
98
+ return filter.some(tag => name === tag.toLowerCase());
99
+ }
100
+
101
+ function contentWithFlankingWhitespace(content: string, delimiter: string): string {
102
+ const leading = content.match(/^\s+/)?.[0] ?? "";
103
+ const trailing = content.match(/\s+$/)?.[0] ?? "";
104
+ const body = content.slice(leading.length, trailing ? -trailing.length : undefined);
105
+ return body ? `${leading}${delimiter}${body}${delimiter}${trailing}` : "";
106
+ }
107
+
108
+ function languageFromCode(node: TurndownNode): string {
109
+ const code = Array.from(node.children).find(child => child.nodeName === "CODE");
110
+ const className = code?.getAttribute("class") ?? "";
111
+ return /(?:^|\s)language-([^\s]+)/.exec(className)?.[1] ?? "";
112
+ }
113
+
114
+ function listItemPrefix(node: TurndownNode, options: ResolvedTurndownOptions): string {
115
+ const parent = node.parentNode;
116
+ if (parent?.nodeName !== "OL") return `${options.bulletListMarker} `;
117
+ const start = Number(parent.getAttribute("start") ?? "1");
118
+ const siblings = Array.from(parent.children);
119
+ return `${(Number.isFinite(start) ? start : 1) + siblings.indexOf(node)}. `;
120
+ }
121
+ function hasNonblankDescendant(node: TurndownNode): boolean {
122
+ for (const child of Array.from(node.children)) {
123
+ if (NONBLANK_EMPTY_ELEMENTS[child.nodeName] || hasNonblankDescendant(child)) return true;
124
+ }
125
+ return false;
126
+ }
127
+
128
+ /** Convert HTML fragments to Markdown with extensible Turndown-compatible rules. */
129
+ export default class TurndownService {
130
+ readonly options: ResolvedTurndownOptions;
131
+ readonly #rules: RuleEntry[] = [];
132
+ readonly #keepFilters: RuleFilter[] = [];
133
+ readonly #removeFilters: RuleFilter[] = [];
134
+ #references: Reference[] = [];
135
+ #previousSourceWhitespace = false;
136
+
137
+ constructor(options: TurndownOptions = {}) {
138
+ this.options = { ...DEFAULT_OPTIONS, ...options };
139
+ }
140
+
141
+ /** Install a highest-priority named conversion rule. */
142
+ addRule(key: string, rule: TurndownRule): this {
143
+ const previous = this.#rules.findIndex(entry => entry.key === key);
144
+ if (previous >= 0) this.#rules.splice(previous, 1);
145
+ this.#rules.unshift({ key, rule });
146
+ return this;
147
+ }
148
+
149
+ /** Preserve matching elements as HTML when no custom rule handles them. */
150
+ keep(filter: RuleFilter): this {
151
+ this.#keepFilters.unshift(filter);
152
+ return this;
153
+ }
154
+
155
+ /** Drop matching elements and all of their converted content. */
156
+ remove(filter: RuleFilter): this {
157
+ this.#removeFilters.unshift(filter);
158
+ return this;
159
+ }
160
+
161
+ /** Install one plugin or an ordered list of plugins. */
162
+ use(plugin: TurndownPlugin | readonly TurndownPlugin[]): this {
163
+ if (typeof plugin === "function") {
164
+ plugin(this);
165
+ } else {
166
+ for (const install of plugin) install(this);
167
+ }
168
+ return this;
169
+ }
170
+
171
+ /** Escape Markdown punctuation using Turndown's public escaping rules. */
172
+ escape(text: string): string {
173
+ return text
174
+ .replace(/\\/g, "\\\\")
175
+ .replace(/([*_[\]])/g, "\\$1")
176
+ .replace(/^(\s*)(#{1,6}|[+>])(?=\s)/gm, "$1\\$2")
177
+ .replace(/^(\s*)-(?=\s)/gm, "$1\\-")
178
+ .replace(/^(\s*\d+)\.(?=\s)/gm, "$1\\.");
179
+ }
180
+
181
+ /** Convert an HTML string or standards-shaped DOM node to Markdown. */
182
+ turndown(input: string | TurndownNode): string {
183
+ const root = typeof input === "string" ? parseHtmlFragment(input) : input;
184
+ this.#references = [];
185
+ this.#previousSourceWhitespace = false;
186
+ let markdown =
187
+ root.nodeType === 9 || root.nodeType === 11 ? this.#convertChildren(root) : this.#convertNode(root);
188
+ markdown = markdown
189
+ .replace(/^[\t\r\n]+/, "")
190
+ .replace(/[\t\r\n ]+$/, "")
191
+ .replace(/\n{3,}/g, "\n\n");
192
+ if (this.#references.length > 0) {
193
+ markdown += `\n\n${this.#references.map(reference => reference.destination).join("\n")}`;
194
+ }
195
+ return markdown;
196
+ }
197
+
198
+ /** Convert only a node's children within the active conversion. */
199
+ convertChildren(node: TurndownNode): string {
200
+ return this.#convertChildren(node);
201
+ }
202
+
203
+ #convertChildren(node: TurndownNode): string {
204
+ let content = "";
205
+ for (const child of Array.from(node.childNodes)) content += this.#convertNode(child);
206
+ return content;
207
+ }
208
+
209
+ #convertNode(node: TurndownNode): string {
210
+ if (node.nodeType === 3) {
211
+ const text = node.textContent ?? "";
212
+ if (node.parentNode?.nodeName === "PRE") return text;
213
+ let collapsed = text.replace(/[\t\r\n\f ]+/g, " ");
214
+ if (this.#previousSourceWhitespace && collapsed.startsWith(" ")) collapsed = collapsed.slice(1);
215
+ this.#previousSourceWhitespace = collapsed.endsWith(" ");
216
+ return node.parentNode?.nodeName === "CODE" ? collapsed : this.escape(collapsed);
217
+ }
218
+ if (node.nodeType !== 1) return this.#convertChildren(node);
219
+ if (!(node.textContent ?? "").trim() && !NONBLANK_EMPTY_ELEMENTS[node.nodeName] && !hasNonblankDescendant(node)) {
220
+ return (
221
+ this.options.blankReplacement?.("", node, this.options) ?? (BLOCK_ELEMENTS[node.nodeName] ? "\n\n" : "")
222
+ );
223
+ }
224
+
225
+ const block = Boolean(BLOCK_ELEMENTS[node.nodeName]);
226
+ if (block) this.#previousSourceWhitespace = false;
227
+ const content = this.#convertChildren(node);
228
+ if (block) this.#previousSourceWhitespace = false;
229
+ if (node.nodeName === "CODE") this.#previousSourceWhitespace = false;
230
+ const custom = this.#rules.find(entry => matchesFilter(entry.rule.filter, node, this.options));
231
+ if (custom) return custom.rule.replacement(content, node, this.options);
232
+ if (this.#keepFilters.some(filter => matchesFilter(filter, node, this.options))) {
233
+ return this.options.keepReplacement?.(content, node, this.options) ?? serializeNode(node);
234
+ }
235
+ if (this.#removeFilters.some(filter => matchesFilter(filter, node, this.options))) return "";
236
+ return this.#defaultReplacement(content, node);
237
+ }
238
+
239
+ #defaultReplacement(content: string, node: TurndownNode): string {
240
+ const name = node.nodeName;
241
+ if (name === "P") return content.trim() ? `\n\n${content.trim()}\n\n` : "";
242
+ if (/^H[1-6]$/.test(name)) {
243
+ const level = Number(name.charAt(1));
244
+ const body = content.trim();
245
+ if (this.options.headingStyle === "setext" && level < 3) {
246
+ return `\n\n${body}\n${(level === 1 ? "=" : "-").repeat(body.length)}\n\n`;
247
+ }
248
+ return `\n\n${"#".repeat(level)} ${body}\n\n`;
249
+ }
250
+ if (name === "BR") return " \n";
251
+ if (name === "HR") return `\n\n${this.options.hr}\n\n`;
252
+ if (name === "BLOCKQUOTE") {
253
+ const body = content
254
+ .trim()
255
+ .replace(/\n{3,}/g, "\n\n")
256
+ .replace(/^/gm, "> ");
257
+ return body ? `\n\n${body}\n\n` : "";
258
+ }
259
+ if (name === "UL" || name === "OL") {
260
+ const body = content.replace(/^\n+|\n+$/g, "");
261
+ return node.parentNode?.nodeName === "LI" ? `\n${body}` : `\n\n${body}\n\n`;
262
+ }
263
+ if (name === "LI") {
264
+ const body = content.replace(/^\n+/, "").replace(/\n+$/, "\n").replace(/\n/gm, "\n ");
265
+ return `${listItemPrefix(node, this.options)}${body}${node.nextSibling ? "\n" : ""}`;
266
+ }
267
+ if (name === "PRE" && Array.from(node.children).some(child => child.nodeName === "CODE")) {
268
+ return this.#replacePre(node);
269
+ }
270
+ if (name === "EM" || name === "I") return contentWithFlankingWhitespace(content, this.options.emDelimiter);
271
+ if (name === "STRONG" || name === "B") {
272
+ return contentWithFlankingWhitespace(content, this.options.strongDelimiter);
273
+ }
274
+ if (name === "CODE") return this.#replaceInlineCode(content);
275
+ if (name === "A") return this.#replaceLink(content, node);
276
+ if (name === "IMG") return this.#replaceImage(node);
277
+ if (this.options.defaultReplacement) return this.options.defaultReplacement(content, node, this.options);
278
+ if (BLOCK_ELEMENTS[name]) return content.trim() ? `\n\n${content.trim()}\n\n` : "";
279
+ return content;
280
+ }
281
+
282
+ #replacePre(node: TurndownNode): string {
283
+ const text = node.textContent ?? "";
284
+ if (this.options.codeBlockStyle === "indented") {
285
+ return `\n\n${text.replace(/^/gm, " ")}\n\n`;
286
+ }
287
+ const fenceCharacter = this.options.fence.charAt(0);
288
+ let longestFence = this.options.fence.length;
289
+ let run = 0;
290
+ for (const character of text) {
291
+ run = character === fenceCharacter ? run + 1 : 0;
292
+ longestFence = Math.max(longestFence, run + 1);
293
+ }
294
+ const fence = fenceCharacter.repeat(longestFence);
295
+ const body = text.replace(/\n$/, "");
296
+ return `\n\n${fence}${languageFromCode(node)}\n${body}\n${fence}\n\n`;
297
+ }
298
+
299
+ #replaceInlineCode(text: string): string {
300
+ const body = text.trim();
301
+ if (!body) return "";
302
+ let longestRun = 0;
303
+ let run = 0;
304
+ for (const character of body) {
305
+ run = character === "`" ? run + 1 : 0;
306
+ longestRun = Math.max(longestRun, run);
307
+ }
308
+ const delimiter = "`".repeat(longestRun + 1);
309
+ const padding = /^`|`$/.test(body) ? " " : "";
310
+ return `${delimiter}${padding}${body}${padding}${delimiter}`;
311
+ }
312
+
313
+ #replaceLink(content: string, node: TurndownNode): string {
314
+ const href = (node.getAttribute("href") ?? "").replace(/([()<>])/g, "\\$1");
315
+ const title = node.getAttribute("title");
316
+ const destination = `${href}${title ? ` "${title.replace(/"/g, '\\"')}"` : ""}`;
317
+ if (this.options.linkStyle !== "referenced") return `[${content}](${destination})`;
318
+ const referenceNumber = this.#references.length + 1;
319
+ let label = String(referenceNumber);
320
+ if (this.options.linkReferenceStyle === "collapsed") label = "";
321
+ if (this.options.linkReferenceStyle === "shortcut") label = content;
322
+ const marker = this.options.linkReferenceStyle === "shortcut" ? `[${content}]` : `[${content}][${label}]`;
323
+ this.#references.push({ destination: `[${label || content}]: ${destination}` });
324
+ return marker;
325
+ }
326
+
327
+ #replaceImage(node: TurndownNode): string {
328
+ const source = (node.getAttribute("src") ?? "").replace(/([()<>])/g, "\\$1");
329
+ if (!source) return "";
330
+ const alternative = this.escape(node.getAttribute("alt") ?? "");
331
+ const title = node.getAttribute("title");
332
+ return `![${alternative}](${source}${title ? ` "${title.replace(/"/g, '\\"')}"` : ""})`;
333
+ }
334
+ }
@@ -0,0 +1,81 @@
1
+ /** Behavior-compatible reimplementation of turndown's used surface. */
2
+
3
+ /** A DOM-shaped node accepted by conversion rules. */
4
+ export interface TurndownNode {
5
+ readonly nodeType: number;
6
+ readonly nodeName: string;
7
+ readonly parentNode: TurndownNode | null;
8
+ readonly childNodes: ArrayLike<TurndownNode>;
9
+ readonly children: ArrayLike<TurndownNode>;
10
+ readonly firstChild: TurndownNode | null;
11
+ readonly lastChild: TurndownNode | null;
12
+ readonly nextSibling: TurndownNode | null;
13
+ readonly previousSibling: TurndownNode | null;
14
+ readonly textContent: string | null;
15
+ readonly outerHTML?: string;
16
+ getAttribute(name: string): string | null;
17
+ hasAttribute(name: string): boolean;
18
+ }
19
+
20
+ /** Options supported by the HTML-to-Markdown converter. */
21
+ export interface TurndownOptions {
22
+ headingStyle?: "setext" | "atx";
23
+ hr?: string;
24
+ bulletListMarker?: "*" | "-" | "+";
25
+ codeBlockStyle?: "indented" | "fenced";
26
+ fence?: string;
27
+ emDelimiter?: "_" | "*";
28
+ strongDelimiter?: "**" | "__";
29
+ linkStyle?: "inlined" | "referenced";
30
+ linkReferenceStyle?: "full" | "collapsed" | "shortcut";
31
+ preformattedCode?: boolean;
32
+ blankReplacement?: ReplacementFunction;
33
+ keepReplacement?: ReplacementFunction;
34
+ defaultReplacement?: ReplacementFunction;
35
+ }
36
+
37
+ /** Fully resolved options passed to replacement callbacks. */
38
+ export interface ResolvedTurndownOptions {
39
+ headingStyle: "setext" | "atx";
40
+ hr: string;
41
+ bulletListMarker: "*" | "-" | "+";
42
+ codeBlockStyle: "indented" | "fenced";
43
+ fence: string;
44
+ emDelimiter: "_" | "*";
45
+ strongDelimiter: "**" | "__";
46
+ linkStyle: "inlined" | "referenced";
47
+ linkReferenceStyle: "full" | "collapsed" | "shortcut";
48
+ preformattedCode: boolean;
49
+ blankReplacement?: ReplacementFunction;
50
+ keepReplacement?: ReplacementFunction;
51
+ defaultReplacement?: ReplacementFunction;
52
+ }
53
+
54
+ /** A tag name, list of tag names, or predicate selecting nodes for a rule. */
55
+ export type RuleFilter =
56
+ | string
57
+ | readonly string[]
58
+ | ((node: TurndownNode, options: ResolvedTurndownOptions) => boolean);
59
+
60
+ /** Produces Markdown for a matched node and its already-converted children. */
61
+ export type ReplacementFunction = (content: string, node: TurndownNode, options: ResolvedTurndownOptions) => string;
62
+
63
+ /** A named conversion rule accepted by `addRule`. */
64
+ export interface TurndownRule {
65
+ filter: RuleFilter;
66
+ replacement: ReplacementFunction;
67
+ }
68
+
69
+ /** A plugin that installs one or more conversion rules. */
70
+ export type TurndownPlugin = (service: TurndownServiceLike) => void;
71
+
72
+ /** Structural service surface available to plugins. */
73
+ export interface TurndownServiceLike {
74
+ readonly options: ResolvedTurndownOptions;
75
+ addRule(key: string, rule: TurndownRule): this;
76
+ convertChildren(node: TurndownNode): string;
77
+ keep(filter: RuleFilter): this;
78
+ remove(filter: RuleFilter): this;
79
+ turndown(input: string | TurndownNode): string;
80
+ escape(text: string): string;
81
+ }
@@ -0,0 +1,5 @@
1
+ /** Behavior-compatible reimplementation of turndown's used surface. */
2
+
3
+ export * from "./turndown/gfm";
4
+ export { default, default as TurndownService } from "./turndown/service";
5
+ export * from "./turndown/types";
@@ -0,0 +1,17 @@
1
+ export function isRecord(value: unknown): value is Record<string, unknown> {
2
+ return !!value && typeof value === "object" && !Array.isArray(value);
3
+ }
4
+
5
+ /** Reads an own string-valued property without invoking accessors. */
6
+ export function stringProperty(value: object, key: string): string | undefined {
7
+ const field = Object.getOwnPropertyDescriptor(value, key)?.value;
8
+ return typeof field === "string" ? field : undefined;
9
+ }
10
+
11
+ export function asRecord(value: unknown): Record<string, unknown> | null {
12
+ return isRecord(value) ? value : null;
13
+ }
14
+
15
+ export function toError(value: unknown): Error {
16
+ return value instanceof Error ? value : new Error(String(value));
17
+ }
@@ -0,0 +1,33 @@
1
+ This directory contains an in-house Mermaid-diagram-to-ASCII renderer adapted
2
+ from beautiful-mermaid (https://github.com/lukilabs/beautiful-mermaid), used
3
+ under the MIT License.
4
+
5
+ Copyright (c) 2026 Craft Docs
6
+
7
+ Only the ASCII rendering pipeline is ported (flowchart/state, sequence, class,
8
+ ER, and xychart diagrams); the SVG renderer and its `elkjs` graph-layout
9
+ dependency, the browser entry point, and the SVG theme/style modules were
10
+ dropped. Terminal display width is reimplemented on `Bun.stringWidth`, and
11
+ inline label formatting (HTML tags, markdown emphasis) is reduced to plain text
12
+ for ASCII output. Layout and edge-routing logic is preserved faithfully so
13
+ ASCII output matches the upstream package.
14
+
15
+ MIT License
16
+
17
+ Permission is hereby granted, free of charge, to any person obtaining a copy
18
+ of this software and associated documentation files (the "Software"), to deal
19
+ in the Software without restriction, including without limitation the rights
20
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
21
+ copies of the Software, and to permit persons to whom the Software is
22
+ furnished to do so, subject to the following conditions:
23
+
24
+ The above copyright notice and this permission notice shall be included in all
25
+ copies or substantial portions of the Software.
26
+
27
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
28
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
29
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
30
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
31
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
32
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
33
+ SOFTWARE.