@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,370 @@
1
+ import { Document, type DocumentFragment, Element, Text } from "./core";
2
+
3
+ const RAW_TEXT_ELEMENTS: Record<string, true> = { script: true, style: true };
4
+ const VOID_ELEMENTS: Record<string, true> = {
5
+ area: true,
6
+ base: true,
7
+ br: true,
8
+ col: true,
9
+ embed: true,
10
+ hr: true,
11
+ img: true,
12
+ input: true,
13
+ link: true,
14
+ meta: true,
15
+ param: true,
16
+ source: true,
17
+ track: true,
18
+ wbr: true,
19
+ };
20
+ const CLOSE_ON_OPEN: Record<string, readonly string[]> = {
21
+ li: ["li"],
22
+ dt: ["dt", "dd"],
23
+ dd: ["dt", "dd"],
24
+ tr: ["tr"],
25
+ th: ["th", "td"],
26
+ td: ["th", "td"],
27
+ option: ["option"],
28
+ thead: ["thead", "tbody", "tfoot"],
29
+ tbody: ["thead", "tbody", "tfoot"],
30
+ tfoot: ["thead", "tbody", "tfoot"],
31
+ };
32
+ const P_CLOSERS: Record<string, true> = {
33
+ address: true,
34
+ article: true,
35
+ aside: true,
36
+ blockquote: true,
37
+ div: true,
38
+ dl: true,
39
+ fieldset: true,
40
+ footer: true,
41
+ form: true,
42
+ h1: true,
43
+ h2: true,
44
+ h3: true,
45
+ h4: true,
46
+ h5: true,
47
+ h6: true,
48
+ header: true,
49
+ hr: true,
50
+ menu: true,
51
+ nav: true,
52
+ ol: true,
53
+ p: true,
54
+ pre: true,
55
+ section: true,
56
+ table: true,
57
+ ul: true,
58
+ };
59
+
60
+ const NAMED_ENTITIES: Record<string, string> = {
61
+ AElig: "Æ",
62
+ Aacute: "Á",
63
+ Acirc: "Â",
64
+ Agrave: "À",
65
+ Aring: "Å",
66
+ Atilde: "Ã",
67
+ Auml: "Ä",
68
+ Ccedil: "Ç",
69
+ ETH: "Ð",
70
+ Eacute: "É",
71
+ Ecirc: "Ê",
72
+ Egrave: "È",
73
+ Euml: "Ë",
74
+ Iacute: "Í",
75
+ Icirc: "Î",
76
+ Igrave: "Ì",
77
+ Iuml: "Ï",
78
+ Ntilde: "Ñ",
79
+ Oacute: "Ó",
80
+ Ocirc: "Ô",
81
+ Ograve: "Ò",
82
+ Oslash: "Ø",
83
+ Otilde: "Õ",
84
+ Ouml: "Ö",
85
+ THORN: "Þ",
86
+ Uacute: "Ú",
87
+ Ucirc: "Û",
88
+ Ugrave: "Ù",
89
+ Uuml: "Ü",
90
+ Yacute: "Ý",
91
+ aacute: "á",
92
+ acirc: "â",
93
+ aelig: "æ",
94
+ agrave: "à",
95
+ aring: "å",
96
+ atilde: "ã",
97
+ auml: "ä",
98
+ amp: "&",
99
+ apos: "'",
100
+ bull: "•",
101
+ brvbar: "¦",
102
+ ccedil: "ç",
103
+ cedil: "¸",
104
+ cent: "¢",
105
+ copy: "©",
106
+ deg: "°",
107
+ curren: "¤",
108
+ divide: "÷",
109
+ emsp: " ",
110
+ ensp: " ",
111
+ euro: "€",
112
+ eacute: "é",
113
+ ecirc: "ê",
114
+ egrave: "è",
115
+ eth: "ð",
116
+ euml: "ë",
117
+ frac12: "½",
118
+ frac14: "¼",
119
+ frac34: "¾",
120
+ gt: ">",
121
+ iacute: "í",
122
+ icirc: "î",
123
+ iexcl: "¡",
124
+ igrave: "ì",
125
+ iquest: "¿",
126
+ iuml: "ï",
127
+ hellip: "…",
128
+ laquo: "«",
129
+ ldquo: "“",
130
+ lsquo: "‘",
131
+ lt: "<",
132
+ mdash: "—",
133
+ macr: "¯",
134
+ micro: "µ",
135
+ middot: "·",
136
+ nbsp: " ",
137
+ ndash: "–",
138
+ ntilde: "ñ",
139
+ oacute: "ó",
140
+ ocirc: "ô",
141
+ ograve: "ò",
142
+ ordf: "ª",
143
+ ordm: "º",
144
+ oslash: "ø",
145
+ otilde: "õ",
146
+ ouml: "ö",
147
+ para: "¶",
148
+ pound: "£",
149
+ plusmn: "±",
150
+ quot: '"',
151
+ raquo: "»",
152
+ rdquo: "”",
153
+ reg: "®",
154
+ rsquo: "’",
155
+ sect: "§",
156
+ shy: "­",
157
+ szlig: "ß",
158
+ thorn: "þ",
159
+ sup1: "¹",
160
+ sup2: "²",
161
+ sup3: "³",
162
+ thinsp: " ",
163
+ times: "×",
164
+ trade: "™",
165
+ uacute: "ú",
166
+ ucirc: "û",
167
+ ugrave: "ù",
168
+ uml: "¨",
169
+ uuml: "ü",
170
+ yacute: "ý",
171
+ yuml: "ÿ",
172
+ yen: "¥",
173
+ };
174
+
175
+ /** Decode common named entities and all valid numeric character references. */
176
+ export function decodeEntities(value: string): string {
177
+ return value.replace(/&(#(?:x[0-9a-f]+|\d+)|[a-z][a-z0-9]+);/gi, (whole, entity: string) => {
178
+ if (entity[0] !== "#") return NAMED_ENTITIES[entity] ?? NAMED_ENTITIES[entity.toLowerCase()] ?? whole;
179
+ const hexadecimal = entity[1]?.toLowerCase() === "x";
180
+ const codePoint = Number.parseInt(entity.slice(hexadecimal ? 2 : 1), hexadecimal ? 16 : 10);
181
+ if (
182
+ !Number.isFinite(codePoint) ||
183
+ codePoint <= 0 ||
184
+ codePoint > 0x10ffff ||
185
+ (codePoint >= 0xd800 && codePoint <= 0xdfff)
186
+ ) {
187
+ return "�";
188
+ }
189
+ return String.fromCodePoint(codePoint);
190
+ });
191
+ }
192
+
193
+ function findTagEnd(html: string, start: number): number {
194
+ let quote = "";
195
+ for (let index = start; index < html.length; index++) {
196
+ const character = html[index];
197
+ if (quote) {
198
+ if (character === quote) quote = "";
199
+ } else if (character === '"' || character === "'") {
200
+ quote = character;
201
+ } else if (character === ">") {
202
+ return index;
203
+ }
204
+ }
205
+ return html.length - 1;
206
+ }
207
+
208
+ function parseStartTag(
209
+ source: string,
210
+ ): { name: string; attributes: Array<[string, string]>; selfClosing: boolean } | null {
211
+ let index = 0;
212
+ while (/\s/.test(source[index] ?? "")) index++;
213
+ const nameMatch = /^[^\s/>]+/.exec(source.slice(index));
214
+ if (!nameMatch) return null;
215
+ const name = nameMatch[0].toLowerCase();
216
+ index += nameMatch[0].length;
217
+ const attributes: Array<[string, string]> = [];
218
+ let selfClosing = false;
219
+ while (index < source.length) {
220
+ while (/\s/.test(source[index] ?? "")) index++;
221
+ if (source[index] === "/") {
222
+ selfClosing = true;
223
+ break;
224
+ }
225
+ const attributeMatch = /^[^\s=/>]+/.exec(source.slice(index));
226
+ if (!attributeMatch) break;
227
+ const attributeName = attributeMatch[0];
228
+ index += attributeMatch[0].length;
229
+ while (/\s/.test(source[index] ?? "")) index++;
230
+ let value = "";
231
+ if (source[index] === "=") {
232
+ index++;
233
+ while (/\s/.test(source[index] ?? "")) index++;
234
+ const quote = source[index];
235
+ if (quote === '"' || quote === "'") {
236
+ index++;
237
+ const end = source.indexOf(quote, index);
238
+ if (end < 0) {
239
+ value = source.slice(index);
240
+ index = source.length;
241
+ } else {
242
+ value = source.slice(index, end);
243
+ index = end + 1;
244
+ }
245
+ } else {
246
+ const valueMatch = /^[^\s>]+/.exec(source.slice(index));
247
+ value = valueMatch?.[0] ?? "";
248
+ index += value.length;
249
+ }
250
+ }
251
+ if (!attributes.some(([existing]) => existing.toLowerCase() === attributeName.toLowerCase())) {
252
+ attributes.push([attributeName, decodeEntities(value)]);
253
+ }
254
+ }
255
+ return { name, attributes, selfClosing };
256
+ }
257
+
258
+ function closeOptionalElements(stack: Element[], incoming: string): void {
259
+ const current = stack[stack.length - 1];
260
+ if (!current) return;
261
+ if (current.localName === "p" && P_CLOSERS[incoming]) {
262
+ stack.pop();
263
+ return;
264
+ }
265
+ const closeable = CLOSE_ON_OPEN[incoming];
266
+ if (closeable?.includes(current.localName)) stack.pop();
267
+ }
268
+
269
+ function parseInto(html: string, document: Document, root: Document | DocumentFragment, contextTag?: string): void {
270
+ const stack: Element[] = [];
271
+ const xmlLike = root instanceof Document && /^\s*(?:<\?xml[\s\S]*?\?>\s*)?<(?:feed|rss)\b/i.test(html);
272
+ let parent: Document | DocumentFragment | Element = root;
273
+ let index = 0;
274
+ while (index < html.length) {
275
+ if (html.startsWith("<!--", index)) {
276
+ const end = html.indexOf("-->", index + 4);
277
+ const contentEnd = end < 0 ? html.length : end;
278
+ parent.appendChild(document.createComment(html.slice(index + 4, contentEnd)));
279
+ index = end < 0 ? html.length : end + 3;
280
+ continue;
281
+ }
282
+ if (html[index] !== "<") {
283
+ const next = html.indexOf("<", index);
284
+ const end = next < 0 ? html.length : next;
285
+ parent.appendChild(new Text(decodeEntities(html.slice(index, end)), document, xmlLike));
286
+ index = end;
287
+ continue;
288
+ }
289
+ if (/^<!doctype\b/i.test(html.slice(index))) {
290
+ const end = html.indexOf(">", index + 2);
291
+ index = end < 0 ? html.length : end + 1;
292
+ continue;
293
+ }
294
+ if (html.startsWith("<![CDATA[", index)) {
295
+ const end = html.indexOf("]]>", index + 9);
296
+ const contentEnd = end < 0 ? html.length : end;
297
+ parent.appendChild(new Text(html.slice(index + 9, contentEnd), document, xmlLike));
298
+ index = end < 0 ? html.length : end + 3;
299
+ continue;
300
+ }
301
+ if (html[index + 1] === "/") {
302
+ const end = findTagEnd(html, index + 2);
303
+ const closingName = html
304
+ .slice(index + 2, end)
305
+ .trim()
306
+ .split(/\s/, 1)[0]
307
+ .toLowerCase();
308
+ const matchIndex = stack.findLastIndex(element => element.localName === closingName);
309
+ if (matchIndex >= 0) stack.length = matchIndex;
310
+ parent = stack[stack.length - 1] ?? root;
311
+ index = end + 1;
312
+ continue;
313
+ }
314
+ if (html[index + 1] === "!" || html[index + 1] === "?") {
315
+ const end = html.indexOf(">", index + 2);
316
+ index = end < 0 ? html.length : end + 1;
317
+ continue;
318
+ }
319
+ const end = findTagEnd(html, index + 1);
320
+ const parsed = parseStartTag(html.slice(index + 1, end));
321
+ if (!parsed) {
322
+ parent.appendChild(document.createTextNode("<"));
323
+ index++;
324
+ continue;
325
+ }
326
+ closeOptionalElements(stack, parsed.name);
327
+ parent = stack[stack.length - 1] ?? root;
328
+ const namespace =
329
+ parent instanceof Element && parent.namespaceURI === "http://www.w3.org/2000/svg"
330
+ ? "http://www.w3.org/2000/svg"
331
+ : parsed.name === "svg"
332
+ ? "http://www.w3.org/2000/svg"
333
+ : "http://www.w3.org/1999/xhtml";
334
+ const element = document.createElementNS(namespace, parsed.name);
335
+ for (let attributeIndex = parsed.attributes.length - 1; attributeIndex >= 0; attributeIndex--) {
336
+ const [name, value] = parsed.attributes[attributeIndex];
337
+ element.setAttribute(name, value);
338
+ }
339
+ parent.appendChild(element);
340
+ index = end + 1;
341
+ if (RAW_TEXT_ELEMENTS[parsed.name] && !parsed.selfClosing) {
342
+ const closePattern = new RegExp(`</${parsed.name}\\s*>`, "ig");
343
+ closePattern.lastIndex = index;
344
+ const match = closePattern.exec(html);
345
+ const rawEnd = match?.index ?? html.length;
346
+ element.appendChild(new Text(html.slice(index, rawEnd), document));
347
+ index = match ? closePattern.lastIndex : html.length;
348
+ continue;
349
+ }
350
+ if (!VOID_ELEMENTS[parsed.name] && !(namespace === "http://www.w3.org/2000/svg" && parsed.selfClosing)) {
351
+ stack.push(element);
352
+ parent = element;
353
+ }
354
+ }
355
+ void contextTag;
356
+ }
357
+
358
+ /** Parse markup into a document fragment. */
359
+ export function parseFragment(html: string, document: Document, contextTag?: string): DocumentFragment {
360
+ const fragment = document.createDocumentFragment();
361
+ parseInto(html, document, fragment, contextTag);
362
+ return fragment;
363
+ }
364
+
365
+ /** Parse markup directly into a document. */
366
+ export function parseDocument(html: string): Document {
367
+ const document = new Document();
368
+ parseInto(html, document, document);
369
+ return document;
370
+ }
@@ -0,0 +1,290 @@
1
+ import { type Document, type DocumentFragment, Element, Node } from "./core";
2
+
3
+ interface ComplexSelector {
4
+ simples: string[];
5
+ combinators: string[];
6
+ }
7
+
8
+ function splitTopLevel(value: string, delimiter: string): string[] {
9
+ const parts: string[] = [];
10
+ let start = 0;
11
+ let brackets = 0;
12
+ let parentheses = 0;
13
+ let quote = "";
14
+ for (let index = 0; index < value.length; index++) {
15
+ const character = value[index];
16
+ if (quote) {
17
+ if (character === quote && value[index - 1] !== "\\") quote = "";
18
+ continue;
19
+ }
20
+ if (character === '"' || character === "'") quote = character;
21
+ else if (character === "[") brackets++;
22
+ else if (character === "]") brackets--;
23
+ else if (character === "(") parentheses++;
24
+ else if (character === ")") parentheses--;
25
+ else if (character === delimiter && brackets === 0 && parentheses === 0) {
26
+ parts.push(value.slice(start, index).trim());
27
+ start = index + 1;
28
+ }
29
+ }
30
+ parts.push(value.slice(start).trim());
31
+ return parts.filter(Boolean);
32
+ }
33
+
34
+ function parseComplex(selector: string): ComplexSelector {
35
+ const simples: string[] = [];
36
+ const combinators: string[] = [];
37
+ let current = "";
38
+ let brackets = 0;
39
+ let parentheses = 0;
40
+ let quote = "";
41
+ for (let index = 0; index < selector.length; index++) {
42
+ const character = selector[index];
43
+ if (quote) {
44
+ current += character;
45
+ if (character === quote && selector[index - 1] !== "\\") quote = "";
46
+ continue;
47
+ }
48
+ if (character === '"' || character === "'") {
49
+ quote = character;
50
+ current += character;
51
+ continue;
52
+ }
53
+ if (character === "[") brackets++;
54
+ else if (character === "]") brackets--;
55
+ else if (character === "(") parentheses++;
56
+ else if (character === ")") parentheses--;
57
+ if (brackets || parentheses) {
58
+ current += character;
59
+ continue;
60
+ }
61
+ if (character === ">" || character === "+" || character === "~") {
62
+ if (current.trim()) simples.push(current.trim());
63
+ current = "";
64
+ combinators.push(character);
65
+ while (/\s/.test(selector[index + 1] ?? "")) index++;
66
+ continue;
67
+ }
68
+ if (/\s/.test(character)) {
69
+ while (/\s/.test(selector[index + 1] ?? "")) index++;
70
+ const next = selector[index + 1];
71
+ if (current.trim()) {
72
+ simples.push(current.trim());
73
+ current = "";
74
+ if (next !== ">" && next !== "+" && next !== "~" && next !== undefined) combinators.push(" ");
75
+ }
76
+ continue;
77
+ }
78
+ current += character;
79
+ }
80
+ if (current.trim()) simples.push(current.trim());
81
+ while (combinators.length >= simples.length) combinators.pop();
82
+ return { simples, combinators };
83
+ }
84
+
85
+ function readIdentifier(source: string, start: number): { value: string; end: number } {
86
+ let value = "";
87
+ let index = start;
88
+ while (index < source.length) {
89
+ const character = source[index];
90
+ if (character === "\\" && index + 1 < source.length) {
91
+ value += source[index + 1];
92
+ index += 2;
93
+ continue;
94
+ }
95
+ if (!/[a-zA-Z0-9_-]/.test(character)) break;
96
+ value += character;
97
+ index++;
98
+ }
99
+ return { value, end: index };
100
+ }
101
+
102
+ function findClosing(source: string, start: number, opener: string, closer: string): number {
103
+ let depth = 1;
104
+ let quote = "";
105
+ for (let index = start + 1; index < source.length; index++) {
106
+ const character = source[index];
107
+ if (quote) {
108
+ if (character === quote && source[index - 1] !== "\\") quote = "";
109
+ } else if (character === '"' || character === "'") quote = character;
110
+ else if (character === opener) depth++;
111
+ else if (character === closer && --depth === 0) return index;
112
+ }
113
+ return source.length - 1;
114
+ }
115
+
116
+ function matchAttribute(element: Element, expression: string): boolean {
117
+ const match = /^\s*([^\s~|^$*!=]+)\s*(?:(\^=|\$=|\*=|~=|\|=|=)\s*(?:(["'])(.*?)\3|([^\s]+))\s*([isIS])?)?\s*$/.exec(
118
+ expression,
119
+ );
120
+ if (!match) return false;
121
+ const [, name, operator, , quotedValue, bareValue, flag] = match;
122
+ const actual = element.getAttribute(name);
123
+ if (!operator) return actual !== null;
124
+ if (actual === null) return false;
125
+ let left = actual;
126
+ let right = (quotedValue ?? bareValue ?? "").replace(/\\(.)/g, "$1");
127
+ if (flag?.toLowerCase() === "i") {
128
+ left = left.toLowerCase();
129
+ right = right.toLowerCase();
130
+ }
131
+ switch (operator) {
132
+ case "=":
133
+ return left === right;
134
+ case "^=":
135
+ return left.startsWith(right);
136
+ case "$=":
137
+ return left.endsWith(right);
138
+ case "*=":
139
+ return left.includes(right);
140
+ case "~=":
141
+ return left.split(/\s+/).includes(right);
142
+ case "|=":
143
+ return left === right || left.startsWith(`${right}-`);
144
+ default:
145
+ return false;
146
+ }
147
+ }
148
+
149
+ function matchNth(element: Element, expression: string): boolean {
150
+ const siblings = element.parentElement?.children ?? [];
151
+ const index = siblings.indexOf(element) + 1;
152
+ const normalized = expression.trim().toLowerCase().replace(/\s+/g, "");
153
+ if (normalized === "odd") return index % 2 === 1;
154
+ if (normalized === "even") return index % 2 === 0;
155
+ if (/^[+-]?\d+$/.test(normalized)) return index === Number(normalized);
156
+ const match = /^([+-]?\d*)n([+-]\d+)?$/.exec(normalized);
157
+ if (!match) return false;
158
+ const coefficient = match[1] === "" || match[1] === "+" ? 1 : match[1] === "-" ? -1 : Number(match[1]);
159
+ const offset = Number(match[2] ?? 0);
160
+ return coefficient === 0
161
+ ? index === offset
162
+ : (index - offset) / coefficient >= 0 && Number.isInteger((index - offset) / coefficient);
163
+ }
164
+
165
+ function matchPseudo(element: Element, name: string, argument: string | undefined): boolean {
166
+ switch (name) {
167
+ case "not":
168
+ return argument !== undefined && !matchesSelector(element, argument);
169
+ case "is":
170
+ case "where":
171
+ return argument !== undefined && matchesSelector(element, argument);
172
+ case "first-child":
173
+ return element.parentElement?.firstElementChild === element;
174
+ case "last-child":
175
+ return element.parentElement?.lastElementChild === element;
176
+ case "only-child":
177
+ return element.parentElement?.children.length === 1;
178
+ case "empty":
179
+ return element.childNodes.every(child => child.nodeType === Node.COMMENT_NODE || child.textContent === "");
180
+ case "root":
181
+ return element.ownerDocument?.documentElement === element;
182
+ case "nth-child":
183
+ return argument !== undefined && matchNth(element, argument);
184
+ case "first-of-type": {
185
+ for (let sibling = element.previousElementSibling; sibling; sibling = sibling.previousElementSibling) {
186
+ if (sibling.tagName === element.tagName) return false;
187
+ }
188
+ return true;
189
+ }
190
+ case "last-of-type": {
191
+ for (let sibling = element.nextElementSibling; sibling; sibling = sibling.nextElementSibling) {
192
+ if (sibling.tagName === element.tagName) return false;
193
+ }
194
+ return true;
195
+ }
196
+ default:
197
+ return false;
198
+ }
199
+ }
200
+
201
+ function matchSimple(element: Element, selector: string): boolean {
202
+ let index = 0;
203
+ if (selector[index] === "*") index++;
204
+ else if (/[a-zA-Z_]/.test(selector[index] ?? "")) {
205
+ const tag = readIdentifier(selector, index);
206
+ if (element.localName !== tag.value.toLowerCase()) return false;
207
+ index = tag.end;
208
+ }
209
+ while (index < selector.length) {
210
+ const marker = selector[index];
211
+ if (marker === "#" || marker === ".") {
212
+ const identifier = readIdentifier(selector, index + 1);
213
+ if (!identifier.value) return false;
214
+ if (marker === "#" ? element.id !== identifier.value : !element.classList.contains(identifier.value))
215
+ return false;
216
+ index = identifier.end;
217
+ continue;
218
+ }
219
+ if (marker === "[") {
220
+ const end = findClosing(selector, index, "[", "]");
221
+ if (!matchAttribute(element, selector.slice(index + 1, end))) return false;
222
+ index = end + 1;
223
+ continue;
224
+ }
225
+ if (marker === ":") {
226
+ const identifier = readIdentifier(selector, index + 1);
227
+ let argument: string | undefined;
228
+ index = identifier.end;
229
+ if (selector[index] === "(") {
230
+ const end = findClosing(selector, index, "(", ")");
231
+ argument = selector.slice(index + 1, end);
232
+ index = end + 1;
233
+ }
234
+ if (!matchPseudo(element, identifier.value.toLowerCase(), argument)) return false;
235
+ continue;
236
+ }
237
+ return false;
238
+ }
239
+ return true;
240
+ }
241
+
242
+ function matchComplexAt(element: Element, complex: ComplexSelector, index: number): boolean {
243
+ if (!matchSimple(element, complex.simples[index])) return false;
244
+ if (index === 0) return true;
245
+ const combinator = complex.combinators[index - 1] ?? " ";
246
+ if (combinator === ">")
247
+ return element.parentElement !== null && matchComplexAt(element.parentElement, complex, index - 1);
248
+ if (combinator === "+")
249
+ return (
250
+ element.previousElementSibling !== null && matchComplexAt(element.previousElementSibling, complex, index - 1)
251
+ );
252
+ if (combinator === "~") {
253
+ for (let sibling = element.previousElementSibling; sibling; sibling = sibling.previousElementSibling) {
254
+ if (matchComplexAt(sibling, complex, index - 1)) return true;
255
+ }
256
+ return false;
257
+ }
258
+ for (let ancestor = element.parentElement; ancestor; ancestor = ancestor.parentElement) {
259
+ if (matchComplexAt(ancestor, complex, index - 1)) return true;
260
+ }
261
+ return false;
262
+ }
263
+
264
+ /** Whether an element matches a CSS selector list. */
265
+ export function matchesSelector(element: Element, selector: string): boolean {
266
+ for (const part of splitTopLevel(selector, ",")) {
267
+ const complex = parseComplex(part);
268
+ if (complex.simples.length && matchComplexAt(element, complex, complex.simples.length - 1)) return true;
269
+ }
270
+ return false;
271
+ }
272
+
273
+ /** Query descendants of a node in document order. */
274
+ export function querySelectorAllFrom(
275
+ root: Document | DocumentFragment | Element,
276
+ selector: string,
277
+ includeRoot: boolean,
278
+ ): Element[] {
279
+ const result: Element[] = [];
280
+ const visit = (node: Node): void => {
281
+ if (node instanceof Element && matchesSelector(node, selector)) result.push(node);
282
+ for (const child of node.childNodes) visit(child);
283
+ };
284
+ if (includeRoot) {
285
+ for (const child of root.childNodes) visit(child);
286
+ } else {
287
+ for (const child of root.childNodes) visit(child);
288
+ }
289
+ return result;
290
+ }
package/src/dom.ts ADDED
@@ -0,0 +1,33 @@
1
+ /** Behavior-compatible reimplementation of linkedom's used surface. */
2
+
3
+ import { DOMWindow } from "./dom/core";
4
+ import { parseDocument } from "./dom/parser";
5
+
6
+ export {
7
+ Attr,
8
+ Comment,
9
+ CSSStyleDeclaration,
10
+ CustomEvent,
11
+ DOMTokenList,
12
+ DOMWindow,
13
+ Document,
14
+ DocumentFragment,
15
+ Element,
16
+ Event,
17
+ EventTarget,
18
+ HTMLElement,
19
+ HTMLIFrameElement,
20
+ HTMLMetaElement,
21
+ HTMLTemplateElement,
22
+ NamedNodeMap,
23
+ Node,
24
+ NodeType,
25
+ SVGElement,
26
+ serializeNode,
27
+ Text,
28
+ } from "./dom/core";
29
+
30
+ /** Parse HTML or XML-like markup into a lightweight window and document. */
31
+ export function parseHTML(html: string): DOMWindow {
32
+ return new DOMWindow(parseDocument(html));
33
+ }