toolcraft 0.0.797 → 0.0.799
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/composition.json +2 -2
- package/dist/composition.json +2 -2
- package/node_modules/@poe-code/safe-fs/dist/core.d.ts +1 -1
- package/node_modules/@poe-code/xml-ast/README.md +19 -2
- package/node_modules/@poe-code/xml-ast/dist/attributes.d.ts +54 -0
- package/node_modules/@poe-code/xml-ast/dist/attributes.js +62 -0
- package/node_modules/@poe-code/xml-ast/dist/index.d.ts +8 -1
- package/node_modules/@poe-code/xml-ast/dist/index.js +96 -37
- package/node_modules/tiny-stdio-mcp-server/dist/composition.json +1 -1
- package/node_modules/toolcraft-schema/package.json +1 -1
- package/package.json +2 -2
package/composition.json
CHANGED
|
@@ -138,7 +138,7 @@
|
|
|
138
138
|
},
|
|
139
139
|
{
|
|
140
140
|
"name": "toolcraft",
|
|
141
|
-
"version": "0.0.
|
|
141
|
+
"version": "0.0.799",
|
|
142
142
|
"license": "MIT"
|
|
143
143
|
},
|
|
144
144
|
{
|
|
@@ -148,7 +148,7 @@
|
|
|
148
148
|
},
|
|
149
149
|
{
|
|
150
150
|
"name": "toolcraft-schema",
|
|
151
|
-
"version": "0.0.
|
|
151
|
+
"version": "0.0.799",
|
|
152
152
|
"license": "MIT"
|
|
153
153
|
},
|
|
154
154
|
{
|
package/dist/composition.json
CHANGED
|
@@ -138,7 +138,7 @@
|
|
|
138
138
|
},
|
|
139
139
|
{
|
|
140
140
|
"name": "toolcraft",
|
|
141
|
-
"version": "0.0.
|
|
141
|
+
"version": "0.0.799",
|
|
142
142
|
"license": "MIT"
|
|
143
143
|
},
|
|
144
144
|
{
|
|
@@ -148,7 +148,7 @@
|
|
|
148
148
|
},
|
|
149
149
|
{
|
|
150
150
|
"name": "toolcraft-schema",
|
|
151
|
-
"version": "0.0.
|
|
151
|
+
"version": "0.0.799",
|
|
152
152
|
"license": "MIT"
|
|
153
153
|
},
|
|
154
154
|
{
|
|
@@ -9,7 +9,7 @@ export * from "./fs/webdav/index.js";
|
|
|
9
9
|
export * from "./bridge/index.js";
|
|
10
10
|
export * from "./python/index.js";
|
|
11
11
|
export { parseXml, parseXmlSteps, parseXmlSourceSteps, normalizeXmlChunks, parseXmlStream, XmlLimitError } from "./xml.js";
|
|
12
|
-
export type { XmlName, XmlAttribute, XmlContent, XmlElement, XmlLimits, XmlParserFrame, XmlFrameRequest, XmlNamespaceScope, XmlNamespaceRequest } from "./xml.js";
|
|
12
|
+
export type { XmlName, XmlAttribute, XmlContent, XmlElement, XmlLimits, XmlParserFrame, XmlFrameRequest, XmlNamespaceScope, XmlNamespaceRequest, XmlAttributeRequest, XmlAttributeRecord } from "./xml.js";
|
|
13
13
|
export * from "./contracts/object.js";
|
|
14
14
|
export { ObjectAuthority } from "./fs/object-authority.js";
|
|
15
15
|
export * from "./fs/s3/index.js";
|
|
@@ -72,6 +72,24 @@ never changes ancestor scopes. In this mode event elements have an empty
|
|
|
72
72
|
The codec remains filesystem-independent, and individual names, attribute values
|
|
73
73
|
and namespace URIs still need a token storage strategy.
|
|
74
74
|
|
|
75
|
+
Set `storeAttributes: true` to move current-tag attribute values, source order,
|
|
76
|
+
and duplicate-name state behind `attributeOperation` requests (`has`, `append`,
|
|
77
|
+
`read`, and `expanded`). Service the request's `found` or `result` field as described
|
|
78
|
+
by `XmlAttributeRequest`, keeping records in caller-owned backing. With this flag,
|
|
79
|
+
element `attributes` arrays are empty. Supply `onAttribute(attribute, element)` to
|
|
80
|
+
receive resolved attributes in source order, after the open event and before any
|
|
81
|
+
content or close event. The parser yields between attributes so a host can await
|
|
82
|
+
storage or a consumer. Namespace declarations may follow prefixed attributes;
|
|
83
|
+
resolution and duplicate-expanded-name validation still happen after the complete
|
|
84
|
+
start tag. With `fragmentAttributes: true`, ordinary values are validated and
|
|
85
|
+
stored as source spans on attribute records, then replayed in at most 512 UTF-16
|
|
86
|
+
units. Hosts must preserve the optional `source` field in append/read requests.
|
|
87
|
+
The third `onAttribute` argument is `true` for continuations of the same logical
|
|
88
|
+
attribute; await each fragment before requesting the next step. Empty attributes
|
|
89
|
+
still emit once. Entity diagnostics are emitted during validation, not replay.
|
|
90
|
+
This mode requires `storeAttributes: true`. Namespace declaration values and
|
|
91
|
+
individual names still retain their token cost.
|
|
92
|
+
|
|
75
93
|
Pass `undefined` as the source length to parse incrementally. Such read requests
|
|
76
94
|
have `streaming: true` and request at most 512 UTF-16 units. Return any nonempty
|
|
77
95
|
prefix available without pulling more input; set `complete: true` only when that
|
|
@@ -89,5 +107,4 @@ decoding replay bounded source windows before publishing a logical node, retaini
|
|
|
89
107
|
its diagnostic and limit ordering. Numeric entity references are decoded without
|
|
90
108
|
materializing the token. Set `compactDeclaration: true` to retain normalized declaration fields without
|
|
91
109
|
copying long whitespace spans; validation always reads bounded windows. The
|
|
92
|
-
buffered API keeps the original declaration spelling. Names
|
|
93
|
-
and namespace scopes still require their own memory budget.
|
|
110
|
+
buffered API keeps the original declaration spelling. Names and namespace declaration values still require their own memory budget.
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
export interface XmlAttributeRecord {
|
|
2
|
+
name: string;
|
|
3
|
+
prefix: string;
|
|
4
|
+
localName: string;
|
|
5
|
+
value: string;
|
|
6
|
+
next: number;
|
|
7
|
+
source?: {
|
|
8
|
+
start: number;
|
|
9
|
+
end: number;
|
|
10
|
+
};
|
|
11
|
+
}
|
|
12
|
+
export interface XmlAttributeState {
|
|
13
|
+
reference: number;
|
|
14
|
+
first: number;
|
|
15
|
+
length: number;
|
|
16
|
+
size: number;
|
|
17
|
+
}
|
|
18
|
+
export type XmlAttributeRequest = {
|
|
19
|
+
attributeOperation: 'has' | 'expanded';
|
|
20
|
+
state: XmlAttributeState;
|
|
21
|
+
name: string;
|
|
22
|
+
found?: boolean;
|
|
23
|
+
} | {
|
|
24
|
+
attributeOperation: 'append';
|
|
25
|
+
state: XmlAttributeState;
|
|
26
|
+
attribute: Omit<XmlAttributeRecord, 'next'>;
|
|
27
|
+
result?: XmlAttributeState;
|
|
28
|
+
} | {
|
|
29
|
+
attributeOperation: 'read';
|
|
30
|
+
state: XmlAttributeState;
|
|
31
|
+
reference: number;
|
|
32
|
+
values: boolean;
|
|
33
|
+
result?: XmlAttributeRecord;
|
|
34
|
+
};
|
|
35
|
+
/** Buffered APIs use local collections; external parsing retains only a store
|
|
36
|
+
* handle and one current attribute. Hosts preserve order and last-value recovery. */
|
|
37
|
+
export declare class XmlAttributes {
|
|
38
|
+
private readonly external;
|
|
39
|
+
private state;
|
|
40
|
+
private readonly names;
|
|
41
|
+
private readonly records;
|
|
42
|
+
private readonly expandedNames;
|
|
43
|
+
constructor(external: boolean);
|
|
44
|
+
get length(): number;
|
|
45
|
+
get size(): number;
|
|
46
|
+
get first(): number;
|
|
47
|
+
has(name: string): Generator<XmlAttributeRequest, boolean, void>;
|
|
48
|
+
append(name: string, prefix: string, localName: string, value: string, source?: {
|
|
49
|
+
start: number;
|
|
50
|
+
end: number;
|
|
51
|
+
}): Generator<XmlAttributeRequest, void, void>;
|
|
52
|
+
read(reference: number, values: boolean): Generator<XmlAttributeRequest, XmlAttributeRecord, void>;
|
|
53
|
+
expanded(name: string): Generator<XmlAttributeRequest, boolean, void>;
|
|
54
|
+
}
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
/** Buffered APIs use local collections; external parsing retains only a store
|
|
2
|
+
* handle and one current attribute. Hosts preserve order and last-value recovery. */
|
|
3
|
+
export class XmlAttributes {
|
|
4
|
+
external;
|
|
5
|
+
state = { reference: 0, first: 0, length: 0, size: 0 };
|
|
6
|
+
names = new Map();
|
|
7
|
+
records = [];
|
|
8
|
+
expandedNames = new Set();
|
|
9
|
+
constructor(external) {
|
|
10
|
+
this.external = external;
|
|
11
|
+
}
|
|
12
|
+
get length() { return this.state.length; }
|
|
13
|
+
get size() { return this.state.size; }
|
|
14
|
+
get first() { return this.state.first; }
|
|
15
|
+
*has(name) {
|
|
16
|
+
if (!this.length)
|
|
17
|
+
return false;
|
|
18
|
+
if (!this.external)
|
|
19
|
+
return this.names.has(name);
|
|
20
|
+
const request = { attributeOperation: 'has', state: this.state, name };
|
|
21
|
+
yield request;
|
|
22
|
+
if (request.found === undefined)
|
|
23
|
+
throw new TypeError('Incomplete XML attribute lookup');
|
|
24
|
+
return request.found;
|
|
25
|
+
}
|
|
26
|
+
*append(name, prefix, localName, value, source) {
|
|
27
|
+
if (!this.external) {
|
|
28
|
+
this.names.set(name, value);
|
|
29
|
+
this.records.push({ name, prefix, localName });
|
|
30
|
+
this.state = { reference: 0, first: 1, length: this.records.length, size: this.names.size };
|
|
31
|
+
return;
|
|
32
|
+
}
|
|
33
|
+
const request = { attributeOperation: 'append', state: this.state, attribute: { name, prefix, localName, value, ...(source ? { source } : {}) } };
|
|
34
|
+
yield request;
|
|
35
|
+
if (!request.result)
|
|
36
|
+
throw new TypeError('Incomplete XML attribute append');
|
|
37
|
+
this.state = request.result;
|
|
38
|
+
}
|
|
39
|
+
*read(reference, values) {
|
|
40
|
+
if (!this.external) {
|
|
41
|
+
const attribute = this.records[reference - 1];
|
|
42
|
+
return { ...attribute, value: values ? this.names.get(attribute.name) : '', next: reference < this.length ? reference + 1 : 0 };
|
|
43
|
+
}
|
|
44
|
+
const request = { attributeOperation: 'read', state: this.state, reference, values };
|
|
45
|
+
yield request;
|
|
46
|
+
if (!request.result)
|
|
47
|
+
throw new TypeError('Incomplete XML attribute read');
|
|
48
|
+
return request.result;
|
|
49
|
+
}
|
|
50
|
+
*expanded(name) {
|
|
51
|
+
if (!this.external) {
|
|
52
|
+
const found = this.expandedNames.has(name);
|
|
53
|
+
this.expandedNames.add(name);
|
|
54
|
+
return found;
|
|
55
|
+
}
|
|
56
|
+
const request = { attributeOperation: 'expanded', state: this.state, name };
|
|
57
|
+
yield request;
|
|
58
|
+
if (request.found === undefined)
|
|
59
|
+
throw new TypeError('Incomplete XML expanded attribute lookup');
|
|
60
|
+
return request.found;
|
|
61
|
+
}
|
|
62
|
+
}
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { type XmlAttributeRequest } from "./attributes.js";
|
|
2
|
+
export type { XmlAttributeRequest, XmlAttributeRecord, XmlAttributeState } from "./attributes.js";
|
|
1
3
|
import { type XmlNamespaceRequest, type XmlFrameRequest } from "./frames.js";
|
|
2
4
|
export type { XmlParserFrame, XmlFrameRequest, XmlNamespaceScope, XmlNamespaceRequest } from "./frames.js";
|
|
3
5
|
export { normalizeXmlChunks } from "./characters.js";
|
|
@@ -63,12 +65,17 @@ export interface XmlSourceLimits extends XmlStepLimits {
|
|
|
63
65
|
readonly storeFrames?: boolean;
|
|
64
66
|
/** Resolve namespace scopes through host-serviced requests. Requires retainTree: false. */
|
|
65
67
|
readonly storeNamespaces?: boolean;
|
|
68
|
+
/** Store attribute collections through host requests and emit resolved attributes individually. */
|
|
69
|
+
readonly storeAttributes?: boolean;
|
|
70
|
+
readonly fragmentAttributes?: boolean;
|
|
71
|
+
readonly onAttribute?: (attribute: XmlAttribute, element: XmlElement, continuation?: boolean) => void;
|
|
66
72
|
}
|
|
67
|
-
export type XmlParseStep = XmlSourceStep | XmlFrameRequest | XmlNamespaceRequest;
|
|
73
|
+
export type XmlParseStep = XmlSourceStep | XmlFrameRequest | XmlNamespaceRequest | XmlAttributeRequest;
|
|
68
74
|
/** Parse a validated, BOM-free, line-normalized UTF-16 source via bounded read requests. */
|
|
69
75
|
export declare function parseXmlSourceSteps(length: number | undefined, limits?: XmlStepLimits & {
|
|
70
76
|
readonly storeFrames?: false;
|
|
71
77
|
readonly storeNamespaces?: false;
|
|
78
|
+
readonly storeAttributes?: false;
|
|
72
79
|
}): Generator<XmlSourceStep, XmlElement, void>;
|
|
73
80
|
export declare function parseXmlSourceSteps(length: number | undefined, limits: XmlSourceLimits): Generator<XmlParseStep, XmlElement, void>;
|
|
74
81
|
export declare function parseXmlSteps(input: string, limits?: XmlStepLimits): Generator<number, XmlElement, void>;
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { XmlAttributes } from "./attributes.js";
|
|
1
2
|
import { XmlFrames, bindNamespace, namespaceValue } from "./frames.js";
|
|
2
3
|
import { validCharacter } from "./characters.js";
|
|
3
4
|
export { normalizeXmlChunks } from "./characters.js";
|
|
@@ -101,7 +102,7 @@ function* entity(source, start, end, recover) {
|
|
|
101
102
|
return String.fromCodePoint(point);
|
|
102
103
|
}
|
|
103
104
|
/** Decoded text fragments; source read/work requests remain host-serviced. */
|
|
104
|
-
function* textParts(source, start, end, decode, recover) {
|
|
105
|
+
function* textParts(source, start, end, decode, recover, attribute = false) {
|
|
105
106
|
let cursor = start;
|
|
106
107
|
while (cursor < end) {
|
|
107
108
|
let finish = Math.min(end, cursor + 512);
|
|
@@ -110,7 +111,13 @@ function* textParts(source, start, end, decode, recover) {
|
|
|
110
111
|
if (last >= 0xd800 && last <= 0xdbff)
|
|
111
112
|
finish--;
|
|
112
113
|
}
|
|
113
|
-
|
|
114
|
+
let raw = yield* source.slice(cursor, finish);
|
|
115
|
+
if (attribute) {
|
|
116
|
+
let normalized = "";
|
|
117
|
+
for (const character of raw)
|
|
118
|
+
normalized += "\t\n\r".includes(character) ? " " : character;
|
|
119
|
+
raw = normalized;
|
|
120
|
+
}
|
|
114
121
|
const found = decode ? raw.indexOf("&") : -1;
|
|
115
122
|
if (found < 0) {
|
|
116
123
|
yield raw;
|
|
@@ -139,9 +146,9 @@ function* textParts(source, start, end, decode, recover) {
|
|
|
139
146
|
cursor = semicolon + 1;
|
|
140
147
|
}
|
|
141
148
|
}
|
|
142
|
-
function* textFragments(source, start, end, decode, recover) {
|
|
149
|
+
function* textFragments(source, start, end, decode, recover, attribute = false) {
|
|
143
150
|
let pending = "";
|
|
144
|
-
for (const part of textParts(source, start, end, decode, recover)) {
|
|
151
|
+
for (const part of textParts(source, start, end, decode, recover, attribute)) {
|
|
145
152
|
if (typeof part !== "string") {
|
|
146
153
|
yield part;
|
|
147
154
|
continue;
|
|
@@ -253,8 +260,12 @@ function validateLimits(limits) {
|
|
|
253
260
|
}
|
|
254
261
|
export function* parseXmlSourceSteps(length, limits = {}) {
|
|
255
262
|
validateLimits(limits);
|
|
263
|
+
if (limits.fragmentAttributes && !limits.storeAttributes)
|
|
264
|
+
throw new TypeError("XML attribute fragments require storeAttributes: true");
|
|
256
265
|
if (limits.storeFrames && limits.retainTree !== false)
|
|
257
266
|
throw new TypeError("Stored XML frames require retainTree: false");
|
|
267
|
+
if (limits.storeAttributes && limits.retainTree !== false)
|
|
268
|
+
throw new TypeError("Stored XML attributes require retainTree: false");
|
|
258
269
|
if (limits.storeNamespaces && limits.retainTree !== false)
|
|
259
270
|
throw new TypeError("Stored XML namespaces require retainTree: false");
|
|
260
271
|
if (limits.fragmentContent && limits.retainTree !== false)
|
|
@@ -555,8 +566,7 @@ export function* parseXmlSourceSteps(length, limits = {}) {
|
|
|
555
566
|
yield 512;
|
|
556
567
|
pendingWork -= 512;
|
|
557
568
|
}
|
|
558
|
-
|
|
559
|
-
const attrMeta = [];
|
|
569
|
+
const attributes = new XmlAttributes(limits.storeAttributes === true);
|
|
560
570
|
const namespaceParent = yield* stack.peek();
|
|
561
571
|
let namespaces = namespaceParent?.namespaceScope ?? namespaceParent?.namespaces ?? (initialNamespaces instanceof Map ? new Map(initialNamespaces) : initialNamespaces);
|
|
562
572
|
let ownsNamespaces = stack.length === 0;
|
|
@@ -576,11 +586,11 @@ export function* parseXmlSourceSteps(length, limits = {}) {
|
|
|
576
586
|
yield 512;
|
|
577
587
|
pendingWork -= 512;
|
|
578
588
|
}
|
|
579
|
-
if (attributes
|
|
589
|
+
if ((yield* attributes.has(attribute)))
|
|
580
590
|
invalid("duplicate attribute");
|
|
581
591
|
if (++attributeCount > maxAttributes)
|
|
582
592
|
throw new XmlLimitError("maxAttributes", "XML attribute limit exceeded");
|
|
583
|
-
if (
|
|
593
|
+
if (attributes.size >= maxAttributesPerElement)
|
|
584
594
|
throw new XmlLimitError("maxAttributesPerElement", "XML attribute limit exceeded");
|
|
585
595
|
yield* skipWhitespace();
|
|
586
596
|
if ((yield* source.slice(offset++, offset)) !== "=")
|
|
@@ -598,23 +608,45 @@ export function* parseXmlSourceSteps(length, limits = {}) {
|
|
|
598
608
|
}
|
|
599
609
|
if (end < 0)
|
|
600
610
|
invalid("unterminated attribute");
|
|
601
|
-
const
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
yield
|
|
605
|
-
|
|
611
|
+
const fragmented = limits.fragmentAttributes && attribute !== "xmlns" && attrPrefix !== "xmlns";
|
|
612
|
+
let value = "";
|
|
613
|
+
if (fragmented) {
|
|
614
|
+
if ((yield* source.indexOf("<", offset, end)) >= 0)
|
|
615
|
+
invalid("less-than in attribute");
|
|
616
|
+
let size = 0;
|
|
617
|
+
for (const part of textFragments(source, offset, end, true, limits.recover, true)) {
|
|
618
|
+
if (typeof part !== "string")
|
|
619
|
+
yield part;
|
|
620
|
+
else
|
|
621
|
+
size += part.length;
|
|
622
|
+
}
|
|
623
|
+
if (size > maxTextLength - textLength)
|
|
624
|
+
throw new XmlLimitError("maxTextLength", "XML text limit exceeded");
|
|
625
|
+
textLength += size;
|
|
606
626
|
}
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
627
|
+
else {
|
|
628
|
+
const raw = (yield* source.slice(offset, end));
|
|
629
|
+
pendingWork += raw.length * 2;
|
|
630
|
+
while (pendingWork >= 512) {
|
|
631
|
+
yield 512;
|
|
632
|
+
pendingWork -= 512;
|
|
633
|
+
}
|
|
634
|
+
if (raw.indexOf("<") >= 0)
|
|
635
|
+
invalid("less-than in attribute");
|
|
636
|
+
let normalized = raw;
|
|
637
|
+
if (raw.includes("\t") || raw.includes("\n") || raw.includes("\r")) {
|
|
638
|
+
normalized = "";
|
|
639
|
+
for (const character of raw)
|
|
640
|
+
normalized += "\t\n\r".includes(character) ? " " : character;
|
|
641
|
+
}
|
|
642
|
+
value = normalized.indexOf("&") < 0 ? (pendingWork += normalized.length, normalized) : yield* entities(normalized, limits.recover);
|
|
643
|
+
while (pendingWork >= 512) {
|
|
644
|
+
yield 512;
|
|
645
|
+
pendingWork -= 512;
|
|
646
|
+
}
|
|
647
|
+
admitText(value);
|
|
614
648
|
}
|
|
615
|
-
|
|
616
|
-
(attributes ??= new Map()).set(attribute, value);
|
|
617
|
-
attrMeta.push([attribute, attrPrefix, attrLocal]);
|
|
649
|
+
yield* attributes.append(attribute, attrPrefix, attrLocal, value, fragmented ? { start: offset, end } : undefined);
|
|
618
650
|
offset = end + 1;
|
|
619
651
|
if (attribute === "xmlns" || attribute.startsWith("xmlns:")) {
|
|
620
652
|
const nsPrefix = attribute === "xmlns" ? "" : attribute.slice(6);
|
|
@@ -636,20 +668,20 @@ export function* parseXmlSourceSteps(length, limits = {}) {
|
|
|
636
668
|
throw new XmlLimitError("maxNamespaces", "XML namespace scope limit exceeded");
|
|
637
669
|
}
|
|
638
670
|
}
|
|
639
|
-
if (
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
671
|
+
if (attributes.length > 0) {
|
|
672
|
+
for (let cursor = attributes.first; cursor;) {
|
|
673
|
+
const record = yield* attributes.read(cursor, false);
|
|
674
|
+
cursor = record.next;
|
|
675
|
+
const { name: attribute, prefix: attrPrefix, localName: attrLocal } = record;
|
|
643
676
|
if (attribute === "xmlns" || attribute.startsWith("xmlns:"))
|
|
644
677
|
continue;
|
|
645
678
|
pendingWork += attribute.length;
|
|
646
679
|
if (attrPrefix && (yield* namespaceValue(namespaces, attrPrefix)) === undefined)
|
|
647
680
|
invalid("unbound attribute prefix");
|
|
648
|
-
if (
|
|
681
|
+
if (attributes.length > 1) {
|
|
649
682
|
const key = `${attrPrefix ? (yield* namespaceValue(namespaces, attrPrefix)) : ""}\0${attrLocal}`;
|
|
650
|
-
if (expanded
|
|
683
|
+
if (yield* attributes.expanded(key))
|
|
651
684
|
invalid("duplicate expanded attribute");
|
|
652
|
-
expanded.add(key);
|
|
653
685
|
}
|
|
654
686
|
}
|
|
655
687
|
}
|
|
@@ -668,13 +700,15 @@ export function* parseXmlSourceSteps(length, limits = {}) {
|
|
|
668
700
|
limits.onElement?.({ name, namespace, localName }, (yield* stack.peek())?.element, stack.length + 1);
|
|
669
701
|
admitContent();
|
|
670
702
|
const retainedAttributes = [];
|
|
671
|
-
if (retainContent
|
|
672
|
-
for (let
|
|
673
|
-
const
|
|
674
|
-
|
|
703
|
+
if (retainContent) {
|
|
704
|
+
for (let cursor = attributes.first; cursor;) {
|
|
705
|
+
const record = yield* attributes.read(cursor, !limits.storeAttributes);
|
|
706
|
+
cursor = record.next;
|
|
707
|
+
const { name: attribute, prefix: attrPrefix, localName: attrLocal, value } = record;
|
|
675
708
|
admitContent();
|
|
676
|
-
|
|
677
|
-
|
|
709
|
+
if (!limits.storeAttributes)
|
|
710
|
+
retainedAttributes.push({ name: attribute, localName: attrLocal,
|
|
711
|
+
namespace: attribute === "xmlns" || attrPrefix === "xmlns" ? xmlnsNamespace : attrPrefix ? (yield* namespaceValue(namespaces, attrPrefix)) : "", value });
|
|
678
712
|
pendingWork += attribute.length + 1;
|
|
679
713
|
}
|
|
680
714
|
}
|
|
@@ -705,9 +739,34 @@ export function* parseXmlSourceSteps(length, limits = {}) {
|
|
|
705
739
|
}
|
|
706
740
|
// Only cache compact, attribute-free spellings. Namespace resolution and
|
|
707
741
|
// every admission counter still run for each distinct physical element.
|
|
708
|
-
if (empty && !attributes && name.length <= 512 && (yield* source.slice(offset - name.length - 3, offset)) === `<${name}/>`)
|
|
742
|
+
if (empty && !attributes.length && name.length <= 512 && (yield* source.slice(offset - name.length - 3, offset)) === `<${name}/>`)
|
|
709
743
|
previousEmpty = { suffix: name + "/>", name, prefix, localName };
|
|
710
744
|
limits.events?.({ type: "open", element, parent: parent?.element });
|
|
745
|
+
if (limits.storeAttributes && limits.onAttribute && retainContent) {
|
|
746
|
+
yield 1;
|
|
747
|
+
for (let cursor = attributes.first; cursor;) {
|
|
748
|
+
const attribute = yield* attributes.read(cursor, true);
|
|
749
|
+
cursor = attribute.next;
|
|
750
|
+
const namespace = attribute.name === "xmlns" || attribute.prefix === "xmlns" ? xmlnsNamespace
|
|
751
|
+
: attribute.prefix ? (yield* namespaceValue(namespaces, attribute.prefix)) : "";
|
|
752
|
+
let continuation = false;
|
|
753
|
+
if (attribute.source) {
|
|
754
|
+
for (const part of textFragments(source, attribute.source.start, attribute.source.end, true, limits.recover ? () => { } : undefined, true)) {
|
|
755
|
+
if (typeof part !== "string") {
|
|
756
|
+
yield part;
|
|
757
|
+
continue;
|
|
758
|
+
}
|
|
759
|
+
limits.onAttribute({ name: attribute.name, localName: attribute.localName, namespace, value: part }, element, continuation);
|
|
760
|
+
continuation = true;
|
|
761
|
+
yield 1;
|
|
762
|
+
}
|
|
763
|
+
}
|
|
764
|
+
if (!continuation) {
|
|
765
|
+
limits.onAttribute({ name: attribute.name, localName: attribute.localName, namespace, value: attribute.value }, element);
|
|
766
|
+
yield 1;
|
|
767
|
+
}
|
|
768
|
+
}
|
|
769
|
+
}
|
|
711
770
|
if (!empty)
|
|
712
771
|
yield* stack.push({ element, content, name, namespaces: namespaces instanceof Map ? namespaces : new Map(), ...(namespaces instanceof Map ? {} : { namespaceScope: namespaces }) });
|
|
713
772
|
else
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "toolcraft",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.799",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -177,7 +177,7 @@
|
|
|
177
177
|
"saxes"
|
|
178
178
|
],
|
|
179
179
|
"optionalDependencies": {
|
|
180
|
-
"toolcraft-schema": "0.0.
|
|
180
|
+
"toolcraft-schema": "0.0.799",
|
|
181
181
|
"toolcraft-design": "*",
|
|
182
182
|
"@poe-code/frontmatter": "*",
|
|
183
183
|
"@poe-code/agent-mcp-config": "*",
|