@oh-my-pi/pi-utils 17.2.9 → 17.2.10
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.
- package/CHANGELOG.md +21 -0
- package/dist/types/acp/connection.d.ts +118 -0
- package/dist/types/acp/protocol.d.ts +526 -0
- package/dist/types/acp/schema.d.ts +41 -0
- package/dist/types/acp/stream.d.ts +8 -0
- package/dist/types/acp/transport.d.ts +81 -0
- package/dist/types/acp.d.ts +6 -0
- package/dist/types/browsers.d.ts +68 -0
- package/dist/types/chalk.d.ts +125 -0
- package/dist/types/dates.d.ts +7 -0
- package/dist/types/docx/converter.d.ts +46 -0
- package/dist/types/docx/xml.d.ts +26 -0
- package/dist/types/docx/zip.d.ts +6 -0
- package/dist/types/docx.d.ts +11 -0
- package/dist/types/dom/core.d.ts +431 -0
- package/dist/types/dom/parser.d.ts +7 -0
- package/dist/types/dom/selector.d.ts +5 -0
- package/dist/types/dom.d.ts +5 -0
- package/dist/types/headers.d.ts +34 -0
- package/dist/types/logger/rotating-file.d.ts +18 -0
- package/dist/types/lru.d.ts +46 -0
- package/dist/types/marked/core.d.ts +445 -0
- package/dist/types/marked.d.ts +2 -0
- package/dist/types/postmortem.d.ts +14 -0
- package/dist/types/procmgr.d.ts +16 -0
- package/dist/types/prompt.d.ts +2 -2
- package/dist/types/readability/readability.d.ts +9 -0
- package/dist/types/readability/readerable.d.ts +10 -0
- package/dist/types/readability/types.d.ts +70 -0
- package/dist/types/readability.d.ts +4 -0
- package/dist/types/template.d.ts +62 -0
- package/dist/types/turndown/gfm.d.ts +11 -0
- package/dist/types/turndown/html.d.ts +5 -0
- package/dist/types/turndown/service.d.ts +21 -0
- package/dist/types/turndown/types.d.ts +70 -0
- package/dist/types/turndown.d.ts +4 -0
- package/dist/types/vterm/buffer.d.ts +99 -0
- package/dist/types/vterm/terminal.d.ts +44 -0
- package/dist/types/vterm.d.ts +8 -0
- package/dist/types/xml.d.ts +31 -0
- package/package.json +2 -5
- package/src/acp/connection.ts +344 -0
- package/src/acp/protocol.ts +466 -0
- package/src/acp/schema.ts +160 -0
- package/src/acp/stream.ts +82 -0
- package/src/acp/transport.ts +213 -0
- package/src/acp.ts +6 -0
- package/src/browsers.ts +501 -0
- package/src/chalk.ts +312 -0
- package/src/dates.ts +194 -0
- package/src/docx/converter.ts +681 -0
- package/src/docx/xml.ts +166 -0
- package/src/docx/zip.ts +87 -0
- package/src/docx.ts +20 -0
- package/src/dom/core.ts +1254 -0
- package/src/dom/parser.ts +370 -0
- package/src/dom/selector.ts +290 -0
- package/src/dom.ts +33 -0
- package/src/headers.ts +167 -0
- package/src/logger/rotating-file.ts +149 -0
- package/src/logger.ts +12 -28
- package/src/lru.ts +185 -0
- package/src/marked/core.ts +1576 -0
- package/src/marked.ts +2 -0
- package/src/postmortem.ts +44 -1
- package/src/procmgr.ts +21 -4
- package/src/prompt.ts +5 -22
- package/src/readability/readability.ts +533 -0
- package/src/readability/readerable.ts +51 -0
- package/src/readability/types.ts +72 -0
- package/src/readability.ts +11 -0
- package/src/template.ts +586 -0
- package/src/turndown/gfm.ts +106 -0
- package/src/turndown/html.ts +257 -0
- package/src/turndown/service.ts +334 -0
- package/src/turndown/types.ts +81 -0
- package/src/turndown.ts +5 -0
- package/src/vterm/buffer.ts +218 -0
- package/src/vterm/terminal.ts +773 -0
- package/src/vterm.ts +8 -0
- package/src/xml.ts +313 -0
- package/src/winston-daily-rotate-file.d.ts +0 -6
package/src/vterm.ts
ADDED
package/src/xml.ts
ADDED
|
@@ -0,0 +1,313 @@
|
|
|
1
|
+
/** Behavior-compatible reimplementation of fast-xml-parser's used surface. */
|
|
2
|
+
|
|
3
|
+
/** Context supplied to an array-selection callback. */
|
|
4
|
+
export type XMLArraySelector = (
|
|
5
|
+
tagName: string,
|
|
6
|
+
jPath: string,
|
|
7
|
+
isLeafNode: boolean,
|
|
8
|
+
isAttribute: boolean | null,
|
|
9
|
+
) => boolean;
|
|
10
|
+
|
|
11
|
+
/** Options supported by {@link XMLParser}. */
|
|
12
|
+
export interface XMLParserOptions {
|
|
13
|
+
/** Omits attributes when true (default). */
|
|
14
|
+
ignoreAttributes?: boolean;
|
|
15
|
+
/** Prefix applied to attribute property names. */
|
|
16
|
+
attributeNamePrefix?: string;
|
|
17
|
+
/** Property used for text alongside children or attributes. */
|
|
18
|
+
textNodeName?: string;
|
|
19
|
+
/** Trims ordinary text and attribute values. */
|
|
20
|
+
trimValues?: boolean;
|
|
21
|
+
/** Coerces numeric and boolean element text. */
|
|
22
|
+
parseTagValue?: boolean;
|
|
23
|
+
/** Coerces numeric and boolean attribute values. */
|
|
24
|
+
parseAttributeValue?: boolean;
|
|
25
|
+
/** Controls entity processing and its expansion ceiling. */
|
|
26
|
+
processEntities?: boolean | { maxTotalExpansions?: number };
|
|
27
|
+
/** Selects element or attribute properties that must always be arrays. */
|
|
28
|
+
isArray?: XMLArraySelector;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
type XmlObject = Record<string, unknown>;
|
|
32
|
+
|
|
33
|
+
const STANDARD_ENTITIES: Record<string, string> = { amp: "&", apos: "'", gt: ">", lt: "<", quot: '"' };
|
|
34
|
+
|
|
35
|
+
interface ParsedElement {
|
|
36
|
+
name: string;
|
|
37
|
+
value: unknown;
|
|
38
|
+
leaf: boolean;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
interface ParserSettings {
|
|
42
|
+
ignoreAttributes: boolean;
|
|
43
|
+
attributeNamePrefix: string;
|
|
44
|
+
textNodeName: string;
|
|
45
|
+
trimValues: boolean;
|
|
46
|
+
parseTagValue: boolean;
|
|
47
|
+
parseAttributeValue: boolean;
|
|
48
|
+
processEntities: boolean;
|
|
49
|
+
maxTotalExpansions: number;
|
|
50
|
+
isArray?: XMLArraySelector;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/** Parses XML into the object shape used by the document converters. */
|
|
54
|
+
export class XMLParser {
|
|
55
|
+
readonly #settings: ParserSettings;
|
|
56
|
+
|
|
57
|
+
constructor(options: XMLParserOptions = {}) {
|
|
58
|
+
this.#settings = {
|
|
59
|
+
ignoreAttributes: options.ignoreAttributes ?? true,
|
|
60
|
+
attributeNamePrefix: options.attributeNamePrefix ?? "@_",
|
|
61
|
+
textNodeName: options.textNodeName ?? "#text",
|
|
62
|
+
trimValues: options.trimValues ?? true,
|
|
63
|
+
parseTagValue: options.parseTagValue ?? true,
|
|
64
|
+
parseAttributeValue: options.parseAttributeValue ?? false,
|
|
65
|
+
processEntities: options.processEntities !== false,
|
|
66
|
+
maxTotalExpansions:
|
|
67
|
+
typeof options.processEntities === "object"
|
|
68
|
+
? (options.processEntities.maxTotalExpansions ?? 100_000)
|
|
69
|
+
: 100_000,
|
|
70
|
+
isArray: options.isArray,
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/** Parses one XML document. */
|
|
75
|
+
parse(xml: string): unknown {
|
|
76
|
+
return new XmlReader(xml, this.#settings).parseDocument();
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
class XmlReader {
|
|
81
|
+
readonly #xml: string;
|
|
82
|
+
readonly #settings: ParserSettings;
|
|
83
|
+
readonly #entities = new Map<string, string>();
|
|
84
|
+
#position = 0;
|
|
85
|
+
#expansions = 0;
|
|
86
|
+
|
|
87
|
+
constructor(xml: string, settings: ParserSettings) {
|
|
88
|
+
this.#xml = xml.charCodeAt(0) === 0xfeff ? xml.slice(1) : xml;
|
|
89
|
+
this.#settings = settings;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
parseDocument(): XmlObject {
|
|
93
|
+
const document: XmlObject = {};
|
|
94
|
+
while (this.#position < this.#xml.length) {
|
|
95
|
+
if (this.#xml.startsWith("<!--", this.#position)) {
|
|
96
|
+
this.#skipThrough("-->");
|
|
97
|
+
continue;
|
|
98
|
+
}
|
|
99
|
+
if (this.#xml.startsWith("<!DOCTYPE", this.#position)) {
|
|
100
|
+
this.#readDoctype();
|
|
101
|
+
continue;
|
|
102
|
+
}
|
|
103
|
+
if (this.#xml.startsWith("<?", this.#position)) {
|
|
104
|
+
this.#readProcessingInstruction(document);
|
|
105
|
+
continue;
|
|
106
|
+
}
|
|
107
|
+
if (this.#xml[this.#position] === "<") {
|
|
108
|
+
const element = this.#readElement("");
|
|
109
|
+
this.#addValue(document, element.name, element.name, element.value, element.leaf, null);
|
|
110
|
+
continue;
|
|
111
|
+
}
|
|
112
|
+
this.#position++;
|
|
113
|
+
}
|
|
114
|
+
return document;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
#readElement(parentPath: string): ParsedElement {
|
|
118
|
+
this.#position++;
|
|
119
|
+
const name = this.#readName();
|
|
120
|
+
const path = parentPath ? `${parentPath}.${name}` : name;
|
|
121
|
+
const attributes: [string, string][] = [];
|
|
122
|
+
let selfClosing = false;
|
|
123
|
+
while (this.#position < this.#xml.length) {
|
|
124
|
+
this.#skipWhitespace();
|
|
125
|
+
if (this.#xml.startsWith("/>", this.#position)) {
|
|
126
|
+
this.#position += 2;
|
|
127
|
+
selfClosing = true;
|
|
128
|
+
break;
|
|
129
|
+
}
|
|
130
|
+
if (this.#xml[this.#position] === ">") {
|
|
131
|
+
this.#position++;
|
|
132
|
+
break;
|
|
133
|
+
}
|
|
134
|
+
const attributeName = this.#readName();
|
|
135
|
+
this.#skipWhitespace();
|
|
136
|
+
let value = "true";
|
|
137
|
+
if (this.#xml[this.#position] === "=") {
|
|
138
|
+
this.#position++;
|
|
139
|
+
this.#skipWhitespace();
|
|
140
|
+
value = this.#readAttributeValue();
|
|
141
|
+
}
|
|
142
|
+
attributes.push([attributeName, value]);
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
const children: XmlObject = {};
|
|
146
|
+
let text = "";
|
|
147
|
+
let hasChildren = false;
|
|
148
|
+
if (!selfClosing) {
|
|
149
|
+
while (this.#position < this.#xml.length) {
|
|
150
|
+
if (this.#xml.startsWith(`</${name}`, this.#position)) {
|
|
151
|
+
this.#position += name.length + 2;
|
|
152
|
+
const close = this.#xml.indexOf(">", this.#position);
|
|
153
|
+
this.#position = close < 0 ? this.#xml.length : close + 1;
|
|
154
|
+
break;
|
|
155
|
+
}
|
|
156
|
+
if (this.#xml.startsWith("<![CDATA[", this.#position)) {
|
|
157
|
+
const end = this.#xml.indexOf("]]>", this.#position + 9);
|
|
158
|
+
const raw = this.#xml.slice(this.#position + 9, end < 0 ? this.#xml.length : end);
|
|
159
|
+
const normalized = raw;
|
|
160
|
+
text += this.#settings.parseTagValue ? String(coerceValue(normalized)) : normalized;
|
|
161
|
+
this.#position = end < 0 ? this.#xml.length : end + 3;
|
|
162
|
+
continue;
|
|
163
|
+
}
|
|
164
|
+
if (this.#xml.startsWith("<!--", this.#position)) {
|
|
165
|
+
this.#skipThrough("-->");
|
|
166
|
+
continue;
|
|
167
|
+
}
|
|
168
|
+
if (this.#xml.startsWith("<?", this.#position)) {
|
|
169
|
+
this.#skipThrough("?>");
|
|
170
|
+
continue;
|
|
171
|
+
}
|
|
172
|
+
if (this.#xml[this.#position] === "<") {
|
|
173
|
+
const child = this.#readElement(path);
|
|
174
|
+
hasChildren = true;
|
|
175
|
+
this.#addValue(children, child.name, `${path}.${child.name}`, child.value, child.leaf, null);
|
|
176
|
+
continue;
|
|
177
|
+
}
|
|
178
|
+
const next = this.#xml.indexOf("<", this.#position);
|
|
179
|
+
const end = next < 0 ? this.#xml.length : next;
|
|
180
|
+
const normalized = this.#normalizeText(this.#xml.slice(this.#position, end), true);
|
|
181
|
+
text += this.#settings.parseTagValue ? String(coerceValue(normalized)) : normalized;
|
|
182
|
+
this.#position = end;
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
const hasAttributes = !this.#settings.ignoreAttributes && attributes.length > 0;
|
|
187
|
+
const parsedText = this.#settings.parseTagValue ? coerceValue(text) : text;
|
|
188
|
+
if (!hasChildren && !hasAttributes) return { name, value: parsedText, leaf: true };
|
|
189
|
+
if (text !== "") children[this.#settings.textNodeName] = parsedText;
|
|
190
|
+
if (hasAttributes) {
|
|
191
|
+
for (const [attributeName, raw] of attributes) {
|
|
192
|
+
const key = `${this.#settings.attributeNamePrefix}${attributeName}`;
|
|
193
|
+
const value = this.#settings.parseAttributeValue ? coerceValue(raw) : raw;
|
|
194
|
+
this.#addValue(children, key, `${path}.${attributeName}`, value, true, true);
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
return { name, value: children, leaf: !hasChildren };
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
#readAttributeValue(): string {
|
|
201
|
+
const quote = this.#xml[this.#position];
|
|
202
|
+
if (quote === '"' || quote === "'") {
|
|
203
|
+
const start = ++this.#position;
|
|
204
|
+
const end = this.#xml.indexOf(quote, start);
|
|
205
|
+
this.#position = end < 0 ? this.#xml.length : end + 1;
|
|
206
|
+
return this.#normalizeText(this.#xml.slice(start, end < 0 ? this.#xml.length : end), true);
|
|
207
|
+
}
|
|
208
|
+
const start = this.#position;
|
|
209
|
+
while (this.#position < this.#xml.length && !/[\s>]/.test(this.#xml[this.#position])) this.#position++;
|
|
210
|
+
return this.#normalizeText(this.#xml.slice(start, this.#position), true);
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
#readProcessingInstruction(target: XmlObject): void {
|
|
214
|
+
this.#position += 2;
|
|
215
|
+
const name = this.#readName();
|
|
216
|
+
const bodyStart = this.#position;
|
|
217
|
+
const end = this.#xml.indexOf("?>", bodyStart);
|
|
218
|
+
const body = this.#xml.slice(bodyStart, end < 0 ? this.#xml.length : end);
|
|
219
|
+
this.#position = end < 0 ? this.#xml.length : end + 2;
|
|
220
|
+
const value: XmlObject = {};
|
|
221
|
+
let hasAttribute = false;
|
|
222
|
+
const attributePattern = /([^\s=]+)\s*=\s*(["'])(.*?)\2/g;
|
|
223
|
+
for (const match of body.matchAll(attributePattern)) {
|
|
224
|
+
if (!this.#settings.ignoreAttributes) {
|
|
225
|
+
value[`${this.#settings.attributeNamePrefix}${match[1]}`] = this.#normalizeText(match[3]!, true);
|
|
226
|
+
hasAttribute = true;
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
target[`?${name}`] = hasAttribute ? value : "";
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
#readDoctype(): void {
|
|
233
|
+
const start = this.#position;
|
|
234
|
+
let bracketDepth = 0;
|
|
235
|
+
let quote = "";
|
|
236
|
+
while (this.#position < this.#xml.length) {
|
|
237
|
+
const char = this.#xml[this.#position++];
|
|
238
|
+
if (quote) {
|
|
239
|
+
if (char === quote) quote = "";
|
|
240
|
+
} else if (char === '"' || char === "'") quote = char;
|
|
241
|
+
else if (char === "[") bracketDepth++;
|
|
242
|
+
else if (char === "]") bracketDepth--;
|
|
243
|
+
else if (char === ">" && bracketDepth === 0) break;
|
|
244
|
+
}
|
|
245
|
+
const declaration = this.#xml.slice(start, this.#position);
|
|
246
|
+
const entityPattern = /<!ENTITY\s+([^\s]+)\s+(["'])(.*?)\2\s*>/gs;
|
|
247
|
+
for (const match of declaration.matchAll(entityPattern)) this.#entities.set(match[1]!, match[3]!);
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
#normalizeText(value: string, entities: boolean): string {
|
|
251
|
+
let normalized = this.#settings.trimValues ? value.trim() : value;
|
|
252
|
+
if (entities && this.#settings.processEntities && normalized.includes("&"))
|
|
253
|
+
normalized = this.#expandEntities(normalized);
|
|
254
|
+
return normalized;
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
#expandEntities(value: string, stack?: ReadonlySet<string>): string {
|
|
258
|
+
return value.replace(/&([A-Za-z_:][\w.:-]*);/g, (whole, name: string) => {
|
|
259
|
+
const standard = STANDARD_ENTITIES[name];
|
|
260
|
+
if (standard !== undefined) return standard;
|
|
261
|
+
const replacement = this.#entities.get(name);
|
|
262
|
+
if (replacement === undefined || stack?.has(name)) return whole;
|
|
263
|
+
this.#expansions++;
|
|
264
|
+
if (this.#expansions > this.#settings.maxTotalExpansions) throw new Error("Entity expansion limit exceeded");
|
|
265
|
+
const nextStack = new Set(stack);
|
|
266
|
+
nextStack.add(name);
|
|
267
|
+
return this.#expandEntities(replacement, nextStack);
|
|
268
|
+
});
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
#addValue(
|
|
272
|
+
target: XmlObject,
|
|
273
|
+
name: string,
|
|
274
|
+
path: string,
|
|
275
|
+
value: unknown,
|
|
276
|
+
leaf: boolean,
|
|
277
|
+
attribute: boolean | null,
|
|
278
|
+
): void {
|
|
279
|
+
const present = Object.hasOwn(target, name);
|
|
280
|
+
if (present) {
|
|
281
|
+
const current = target[name];
|
|
282
|
+
if (Array.isArray(current)) current.push(value);
|
|
283
|
+
else target[name] = [current, value];
|
|
284
|
+
return;
|
|
285
|
+
}
|
|
286
|
+
const forceArray = this.#settings.isArray?.(name, path, leaf, attribute) ?? false;
|
|
287
|
+
target[name] = forceArray ? [value] : value;
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
#readName(): string {
|
|
291
|
+
const start = this.#position;
|
|
292
|
+
while (this.#position < this.#xml.length && !/[\s/>=]/.test(this.#xml[this.#position])) this.#position++;
|
|
293
|
+
return this.#xml.slice(start, this.#position);
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
#skipWhitespace(): void {
|
|
297
|
+
while (this.#position < this.#xml.length && /\s/.test(this.#xml[this.#position])) this.#position++;
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
#skipThrough(marker: string): void {
|
|
301
|
+
const end = this.#xml.indexOf(marker, this.#position + marker.length);
|
|
302
|
+
this.#position = end < 0 ? this.#xml.length : end + marker.length;
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
function coerceValue(value: string): unknown {
|
|
307
|
+
if (value === "true") return true;
|
|
308
|
+
if (value === "false") return false;
|
|
309
|
+
if (value !== "" && /^[+-]?(?:0[xX][\da-fA-F]+|(?:\d+(?:\.\d*)?|\.\d+)(?:[eE][+-]?\d+)?)$/.test(value)) {
|
|
310
|
+
return Number(value);
|
|
311
|
+
}
|
|
312
|
+
return value;
|
|
313
|
+
}
|