toolcraft 0.0.798 → 0.0.800
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 +38 -7
- package/node_modules/@poe-code/xml-ast/dist/attributes.d.ts +17 -2
- package/node_modules/@poe-code/xml-ast/dist/attributes.js +4 -4
- package/node_modules/@poe-code/xml-ast/dist/frames.d.ts +17 -3
- package/node_modules/@poe-code/xml-ast/dist/frames.js +22 -0
- package/node_modules/@poe-code/xml-ast/dist/index.d.ts +11 -3
- package/node_modules/@poe-code/xml-ast/dist/index.js +133 -39
- package/node_modules/@poe-code/xml-ast/dist/source.d.ts +4 -0
- 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.800",
|
|
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.800",
|
|
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.800",
|
|
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.800",
|
|
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, XmlAttributeRequest, XmlAttributeRecord } from "./xml.js";
|
|
12
|
+
export type { XmlName, XmlAttribute, XmlContent, XmlElement, XmlLimits, XmlParserFrame, XmlFrameRequest, XmlNamespaceScope, XmlNamespaceRequest, XmlSourceRead, XmlSourceSpan, 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";
|
|
@@ -64,24 +64,48 @@ size cost. The default string and stream APIs retain their existing ancestry beh
|
|
|
64
64
|
|
|
65
65
|
Also set `storeNamespaces: true` to resolve namespace scopes through host-backed
|
|
66
66
|
immutable storage. `namespaceOperation: "get"` supplies a scope and prefix; fill
|
|
67
|
-
`value` when present and set `complete: true`. For `"
|
|
67
|
+
`value` when present and set `complete: true`. For `"has"`, fill `found` using
|
|
68
|
+
membership without reading the URI value. For `"set"`, bind the supplied prefix
|
|
68
69
|
and value in a new scope, then fill `result` with its storage `reference` and the
|
|
69
70
|
number of distinct bindings (`size`). Frames carry this scope handle so shadowing
|
|
70
71
|
never changes ancestor scopes. In this mode event elements have an empty
|
|
71
72
|
`namespaces` map; their names and attributes still carry resolved namespace URIs.
|
|
72
|
-
The codec remains filesystem-independent
|
|
73
|
-
|
|
73
|
+
The codec remains filesystem-independent. With `deferNamespaces: true`, resolved
|
|
74
|
+
URI metadata uses optional `namespaceReference` handles instead of complete strings.
|
|
75
|
+
Service `namespaceOperation: "reference"` by supplying the token `reference` when
|
|
76
|
+
present and setting `complete: true`. These handles belong to the host scope store;
|
|
77
|
+
retain or stream them before retiring that store. Built-in xml/xmlns names keep
|
|
78
|
+
their short namespace strings. Individual names still retain their token cost;
|
|
79
|
+
attribute values can use the fragment protocol below.
|
|
74
80
|
|
|
75
81
|
Set `storeAttributes: true` to move current-tag attribute values, source order,
|
|
76
82
|
and duplicate-name state behind `attributeOperation` requests (`has`, `append`,
|
|
77
83
|
`read`, and `expanded`). Service the request's `found` or `result` field as described
|
|
78
|
-
by `XmlAttributeRequest`, keeping records in caller-owned backing.
|
|
84
|
+
by `XmlAttributeRequest`, keeping records in caller-owned backing. When namespace
|
|
85
|
+
storage is also enabled, `expanded` supplies the local name and a `namespace` scope
|
|
86
|
+
and prefix. Check the pair of resolved URI and local name without joining them in
|
|
87
|
+
memory; an empty prefix means no attribute namespace, regardless of the default
|
|
88
|
+
element namespace. With this flag,
|
|
79
89
|
element `attributes` arrays are empty. Supply `onAttribute(attribute, element)` to
|
|
80
90
|
receive resolved attributes in source order, after the open event and before any
|
|
81
91
|
content or close event. The parser yields between attributes so a host can await
|
|
82
92
|
storage or a consumer. Namespace declarations may follow prefixed attributes;
|
|
83
93
|
resolution and duplicate-expanded-name validation still happen after the complete
|
|
84
|
-
start tag.
|
|
94
|
+
start tag. With `fragmentAttributes: true`, ordinary values are validated through source spans; all attribute
|
|
95
|
+
values are stored as spans on attribute records and replayed in at most 512 UTF-16
|
|
96
|
+
units. Hosts must preserve the optional `source` field in append/read requests.
|
|
97
|
+
The third `onAttribute` argument is `true` for continuations of the same logical
|
|
98
|
+
attribute; await each fragment before requesting the next step. Empty attributes
|
|
99
|
+
still emit once. Entity diagnostics are emitted during validation, not replay.
|
|
100
|
+
This mode requires `storeAttributes: true`. Combining it with `storeNamespaces`
|
|
101
|
+
also validates namespace declarations in bounded fragments. A namespace `set`
|
|
102
|
+
request's `value` may then be a generator: strings are decoded value fragments,
|
|
103
|
+
numbers are work checkpoints, and objects are source-read requests. Service each
|
|
104
|
+
step before advancing, consume the complete value before filling `result`, and
|
|
105
|
+
close the producer if storage fails. Reserved URI checks preserve their diagnostic
|
|
106
|
+
order without joining fragments. Expanded-name events return complete URI strings
|
|
107
|
+
unless deferred namespace metadata is enabled; individual names still retain
|
|
108
|
+
their token cost.
|
|
85
109
|
|
|
86
110
|
Pass `undefined` as the source length to parse incrementally. Such read requests
|
|
87
111
|
have `streaming: true` and request at most 512 UTF-16 units. Return any nonempty
|
|
@@ -100,5 +124,12 @@ decoding replay bounded source windows before publishing a logical node, retaini
|
|
|
100
124
|
its diagnostic and limit ordering. Numeric entity references are decoded without
|
|
101
125
|
materializing the token. Set `compactDeclaration: true` to retain normalized declaration fields without
|
|
102
126
|
copying long whitespace spans; validation always reads bounded windows. The
|
|
103
|
-
buffered API keeps the original declaration spelling.
|
|
104
|
-
|
|
127
|
+
buffered API keeps the original declaration spelling.
|
|
128
|
+
|
|
129
|
+
Combine `fragmentContent` with `deferContentNames: true` to validate long
|
|
130
|
+
processing-instruction targets through source windows. Their events have an empty
|
|
131
|
+
`target` and a `targetSource` span; replay that span before retiring the source.
|
|
132
|
+
All body continuations carry the same target span. Short targets remain strings.
|
|
133
|
+
The stored document engine transfers these spans to its own token store and
|
|
134
|
+
streams targets during output. Element and attribute names still require their
|
|
135
|
+
own memory budget.
|
|
@@ -1,9 +1,14 @@
|
|
|
1
|
+
import type { XmlNamespaceScope } from "./frames.js";
|
|
1
2
|
export interface XmlAttributeRecord {
|
|
2
3
|
name: string;
|
|
3
4
|
prefix: string;
|
|
4
5
|
localName: string;
|
|
5
6
|
value: string;
|
|
6
7
|
next: number;
|
|
8
|
+
source?: {
|
|
9
|
+
start: number;
|
|
10
|
+
end: number;
|
|
11
|
+
};
|
|
7
12
|
}
|
|
8
13
|
export interface XmlAttributeState {
|
|
9
14
|
reference: number;
|
|
@@ -16,6 +21,10 @@ export type XmlAttributeRequest = {
|
|
|
16
21
|
state: XmlAttributeState;
|
|
17
22
|
name: string;
|
|
18
23
|
found?: boolean;
|
|
24
|
+
namespace?: {
|
|
25
|
+
scope: XmlNamespaceScope;
|
|
26
|
+
prefix: string;
|
|
27
|
+
};
|
|
19
28
|
} | {
|
|
20
29
|
attributeOperation: 'append';
|
|
21
30
|
state: XmlAttributeState;
|
|
@@ -41,7 +50,13 @@ export declare class XmlAttributes {
|
|
|
41
50
|
get size(): number;
|
|
42
51
|
get first(): number;
|
|
43
52
|
has(name: string): Generator<XmlAttributeRequest, boolean, void>;
|
|
44
|
-
append(name: string, prefix: string, localName: string, value: string
|
|
53
|
+
append(name: string, prefix: string, localName: string, value: string, source?: {
|
|
54
|
+
start: number;
|
|
55
|
+
end: number;
|
|
56
|
+
}): Generator<XmlAttributeRequest, void, void>;
|
|
45
57
|
read(reference: number, values: boolean): Generator<XmlAttributeRequest, XmlAttributeRecord, void>;
|
|
46
|
-
expanded(name: string
|
|
58
|
+
expanded(name: string, namespace?: {
|
|
59
|
+
scope: XmlNamespaceScope;
|
|
60
|
+
prefix: string;
|
|
61
|
+
}): Generator<XmlAttributeRequest, boolean, void>;
|
|
47
62
|
}
|
|
@@ -23,14 +23,14 @@ export class XmlAttributes {
|
|
|
23
23
|
throw new TypeError('Incomplete XML attribute lookup');
|
|
24
24
|
return request.found;
|
|
25
25
|
}
|
|
26
|
-
*append(name, prefix, localName, value) {
|
|
26
|
+
*append(name, prefix, localName, value, source) {
|
|
27
27
|
if (!this.external) {
|
|
28
28
|
this.names.set(name, value);
|
|
29
29
|
this.records.push({ name, prefix, localName });
|
|
30
30
|
this.state = { reference: 0, first: 1, length: this.records.length, size: this.names.size };
|
|
31
31
|
return;
|
|
32
32
|
}
|
|
33
|
-
const request = { attributeOperation: 'append', state: this.state, attribute: { name, prefix, localName, value } };
|
|
33
|
+
const request = { attributeOperation: 'append', state: this.state, attribute: { name, prefix, localName, value, ...(source ? { source } : {}) } };
|
|
34
34
|
yield request;
|
|
35
35
|
if (!request.result)
|
|
36
36
|
throw new TypeError('Incomplete XML attribute append');
|
|
@@ -47,13 +47,13 @@ export class XmlAttributes {
|
|
|
47
47
|
throw new TypeError('Incomplete XML attribute read');
|
|
48
48
|
return request.result;
|
|
49
49
|
}
|
|
50
|
-
*expanded(name) {
|
|
50
|
+
*expanded(name, namespace) {
|
|
51
51
|
if (!this.external) {
|
|
52
52
|
const found = this.expandedNames.has(name);
|
|
53
53
|
this.expandedNames.add(name);
|
|
54
54
|
return found;
|
|
55
55
|
}
|
|
56
|
-
const request = { attributeOperation: 'expanded', state: this.state, name };
|
|
56
|
+
const request = { attributeOperation: 'expanded', state: this.state, name, ...(namespace ? { namespace } : {}) };
|
|
57
57
|
yield request;
|
|
58
58
|
if (request.found === undefined)
|
|
59
59
|
throw new TypeError('Incomplete XML expanded attribute lookup');
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { XmlSourceStep } from './source.js';
|
|
2
|
+
import type { XmlContent, XmlElement, XmlName } from './index.js';
|
|
2
3
|
export interface XmlParserFrame {
|
|
3
4
|
element: XmlElement;
|
|
4
5
|
content: XmlContent[] | undefined;
|
|
@@ -33,6 +34,17 @@ export interface XmlNamespaceScope {
|
|
|
33
34
|
readonly size: number;
|
|
34
35
|
}
|
|
35
36
|
export type XmlNamespaceRequest = {
|
|
37
|
+
readonly namespaceOperation: 'reference';
|
|
38
|
+
readonly scope: XmlNamespaceScope;
|
|
39
|
+
readonly prefix: string;
|
|
40
|
+
reference?: number;
|
|
41
|
+
complete?: true;
|
|
42
|
+
} | {
|
|
43
|
+
readonly namespaceOperation: 'has';
|
|
44
|
+
readonly scope: XmlNamespaceScope;
|
|
45
|
+
readonly prefix: string;
|
|
46
|
+
found?: boolean;
|
|
47
|
+
} | {
|
|
36
48
|
readonly namespaceOperation: 'get';
|
|
37
49
|
readonly scope: XmlNamespaceScope;
|
|
38
50
|
readonly prefix: string;
|
|
@@ -42,8 +54,10 @@ export type XmlNamespaceRequest = {
|
|
|
42
54
|
readonly namespaceOperation: 'set';
|
|
43
55
|
readonly scope: XmlNamespaceScope;
|
|
44
56
|
readonly prefix: string;
|
|
45
|
-
readonly value: string
|
|
57
|
+
readonly value: string | Generator<XmlSourceStep | string, void, void>;
|
|
46
58
|
result?: XmlNamespaceScope;
|
|
47
59
|
};
|
|
48
60
|
export declare function namespaceValue(scope: Map<string, string> | XmlNamespaceScope, prefix: string): Generator<XmlNamespaceRequest, string | undefined, void>;
|
|
49
|
-
export declare function
|
|
61
|
+
export declare function namespaceMetadata(scope: Map<string, string> | XmlNamespaceScope, prefix: string, deferred?: boolean): Generator<XmlNamespaceRequest, Pick<XmlName, 'namespace' | 'namespaceReference'>, void>;
|
|
62
|
+
export declare function hasNamespace(scope: Map<string, string> | XmlNamespaceScope, prefix: string): Generator<XmlNamespaceRequest, boolean, void>;
|
|
63
|
+
export declare function bindNamespace(scope: Map<string, string> | XmlNamespaceScope, prefix: string, value: string | Generator<XmlSourceStep | string, void, void>): Generator<XmlNamespaceRequest, Map<string, string> | XmlNamespaceScope, void>;
|
|
@@ -52,8 +52,30 @@ export function* namespaceValue(scope, prefix) {
|
|
|
52
52
|
throw new TypeError('Incomplete XML namespace read');
|
|
53
53
|
return request.value;
|
|
54
54
|
}
|
|
55
|
+
export function* namespaceMetadata(scope, prefix, deferred = false) {
|
|
56
|
+
if (!deferred || scope instanceof Map)
|
|
57
|
+
return { namespace: (yield* namespaceValue(scope, prefix)) ?? '' };
|
|
58
|
+
if (prefix === 'xml')
|
|
59
|
+
return { namespace: 'http://www.w3.org/XML/1998/namespace' };
|
|
60
|
+
const request = { namespaceOperation: 'reference', scope, prefix };
|
|
61
|
+
yield request;
|
|
62
|
+
if (!request.complete)
|
|
63
|
+
throw new TypeError('Incomplete XML namespace reference lookup');
|
|
64
|
+
return { namespace: '', ...(request.reference === undefined ? {} : { namespaceReference: request.reference }) };
|
|
65
|
+
}
|
|
66
|
+
export function* hasNamespace(scope, prefix) {
|
|
67
|
+
if (scope instanceof Map)
|
|
68
|
+
return scope.has(prefix);
|
|
69
|
+
const request = { namespaceOperation: 'has', scope, prefix };
|
|
70
|
+
yield request;
|
|
71
|
+
if (request.found === undefined)
|
|
72
|
+
throw new TypeError('Incomplete XML namespace membership lookup');
|
|
73
|
+
return request.found;
|
|
74
|
+
}
|
|
55
75
|
export function* bindNamespace(scope, prefix, value) {
|
|
56
76
|
if (scope instanceof Map) {
|
|
77
|
+
if (typeof value !== 'string')
|
|
78
|
+
throw new TypeError('Streamed XML namespace values require external scopes');
|
|
57
79
|
scope.set(prefix, value);
|
|
58
80
|
return scope;
|
|
59
81
|
}
|
|
@@ -3,13 +3,14 @@ export type { XmlAttributeRequest, XmlAttributeRecord, XmlAttributeState } from
|
|
|
3
3
|
import { type XmlNamespaceRequest, type XmlFrameRequest } from "./frames.js";
|
|
4
4
|
export type { XmlParserFrame, XmlFrameRequest, XmlNamespaceScope, XmlNamespaceRequest } from "./frames.js";
|
|
5
5
|
export { normalizeXmlChunks } from "./characters.js";
|
|
6
|
-
import { type XmlSourceStep } from "./source.js";
|
|
7
|
-
export type { XmlSourceRead, XmlSourceStep } from "./source.js";
|
|
6
|
+
import { type XmlSourceSpan, type XmlSourceStep } from "./source.js";
|
|
7
|
+
export type { XmlSourceRead, XmlSourceSpan, XmlSourceStep } from "./source.js";
|
|
8
8
|
import type { XmlStreamEvent } from "./stream.js";
|
|
9
9
|
export { XmlLimitError } from "./errors.js";
|
|
10
10
|
export interface XmlName {
|
|
11
11
|
readonly name: string;
|
|
12
12
|
readonly namespace: string;
|
|
13
|
+
readonly namespaceReference?: number;
|
|
13
14
|
readonly localName: string;
|
|
14
15
|
}
|
|
15
16
|
export interface XmlAttribute extends XmlName {
|
|
@@ -21,6 +22,8 @@ export type XmlContent = XmlElement | {
|
|
|
21
22
|
} | {
|
|
22
23
|
readonly kind: "processing-instruction";
|
|
23
24
|
readonly target: string;
|
|
25
|
+
readonly targetSource?: XmlSourceSpan;
|
|
26
|
+
readonly targetReference?: number;
|
|
24
27
|
readonly text: string;
|
|
25
28
|
};
|
|
26
29
|
export interface XmlElement extends XmlName {
|
|
@@ -65,9 +68,14 @@ export interface XmlSourceLimits extends XmlStepLimits {
|
|
|
65
68
|
readonly storeFrames?: boolean;
|
|
66
69
|
/** Resolve namespace scopes through host-serviced requests. Requires retainTree: false. */
|
|
67
70
|
readonly storeNamespaces?: boolean;
|
|
71
|
+
/** Carry host namespace token references in event metadata. Requires stored namespaces. */
|
|
72
|
+
readonly deferNamespaces?: boolean;
|
|
73
|
+
/** Emit long processing-instruction targets as source spans. Requires fragmentContent. */
|
|
74
|
+
readonly deferContentNames?: boolean;
|
|
68
75
|
/** Store attribute collections through host requests and emit resolved attributes individually. */
|
|
69
76
|
readonly storeAttributes?: boolean;
|
|
70
|
-
readonly
|
|
77
|
+
readonly fragmentAttributes?: boolean;
|
|
78
|
+
readonly onAttribute?: (attribute: XmlAttribute, element: XmlElement, continuation?: boolean) => void;
|
|
71
79
|
}
|
|
72
80
|
export type XmlParseStep = XmlSourceStep | XmlFrameRequest | XmlNamespaceRequest | XmlAttributeRequest;
|
|
73
81
|
/** Parse a validated, BOM-free, line-normalized UTF-16 source via bounded read requests. */
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { XmlAttributes } from "./attributes.js";
|
|
2
|
-
import { XmlFrames, bindNamespace, namespaceValue } from "./frames.js";
|
|
2
|
+
import { XmlFrames, bindNamespace, namespaceValue, namespaceMetadata, hasNamespace } from "./frames.js";
|
|
3
3
|
import { validCharacter } from "./characters.js";
|
|
4
4
|
export { normalizeXmlChunks } from "./characters.js";
|
|
5
5
|
import { XmlSource } from "./source.js";
|
|
@@ -68,6 +68,40 @@ function qualifiedNameSync(name, cache) {
|
|
|
68
68
|
}
|
|
69
69
|
return parts;
|
|
70
70
|
}
|
|
71
|
+
/** Validate a name span without retaining its spelling. */
|
|
72
|
+
function* validateNameSpan(source, start, end) {
|
|
73
|
+
let first = true, colon = false, work = 0;
|
|
74
|
+
for (let offset = start; offset < end;) {
|
|
75
|
+
let point = yield* source.charCodeAt(offset);
|
|
76
|
+
if (point >= 0xd800 && point <= 0xdbff && offset + 1 < end) {
|
|
77
|
+
const low = yield* source.charCodeAt(offset + 1);
|
|
78
|
+
if (low >= 0xdc00 && low <= 0xdfff)
|
|
79
|
+
point = 0x10000 + (point - 0xd800) * 1024 + low - 0xdc00;
|
|
80
|
+
}
|
|
81
|
+
if (point === 58) {
|
|
82
|
+
if (colon || first)
|
|
83
|
+
invalid("invalid qualified name");
|
|
84
|
+
colon = true;
|
|
85
|
+
first = true;
|
|
86
|
+
}
|
|
87
|
+
else {
|
|
88
|
+
if (!(first ? nameStart(point) : namePart(point)))
|
|
89
|
+
invalid("invalid qualified name");
|
|
90
|
+
first = false;
|
|
91
|
+
}
|
|
92
|
+
const size = point > 0xffff ? 2 : 1;
|
|
93
|
+
offset += size;
|
|
94
|
+
work += size;
|
|
95
|
+
if (work >= 512) {
|
|
96
|
+
yield 512;
|
|
97
|
+
work -= 512;
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
if (first)
|
|
101
|
+
invalid("invalid qualified name");
|
|
102
|
+
if (work)
|
|
103
|
+
yield work;
|
|
104
|
+
}
|
|
71
105
|
function* entity(source, start, end, recover) {
|
|
72
106
|
// Inspect only a bounded prefix, even for arbitrarily long numeric references.
|
|
73
107
|
const prefix = typeof source === "string" ? source.slice(start, Math.min(end, start + 4))
|
|
@@ -102,7 +136,7 @@ function* entity(source, start, end, recover) {
|
|
|
102
136
|
return String.fromCodePoint(point);
|
|
103
137
|
}
|
|
104
138
|
/** Decoded text fragments; source read/work requests remain host-serviced. */
|
|
105
|
-
function* textParts(source, start, end, decode, recover) {
|
|
139
|
+
function* textParts(source, start, end, decode, recover, attribute = false) {
|
|
106
140
|
let cursor = start;
|
|
107
141
|
while (cursor < end) {
|
|
108
142
|
let finish = Math.min(end, cursor + 512);
|
|
@@ -111,7 +145,13 @@ function* textParts(source, start, end, decode, recover) {
|
|
|
111
145
|
if (last >= 0xd800 && last <= 0xdbff)
|
|
112
146
|
finish--;
|
|
113
147
|
}
|
|
114
|
-
|
|
148
|
+
let raw = yield* source.slice(cursor, finish);
|
|
149
|
+
if (attribute) {
|
|
150
|
+
let normalized = "";
|
|
151
|
+
for (const character of raw)
|
|
152
|
+
normalized += "\t\n\r".includes(character) ? " " : character;
|
|
153
|
+
raw = normalized;
|
|
154
|
+
}
|
|
115
155
|
const found = decode ? raw.indexOf("&") : -1;
|
|
116
156
|
if (found < 0) {
|
|
117
157
|
yield raw;
|
|
@@ -140,9 +180,9 @@ function* textParts(source, start, end, decode, recover) {
|
|
|
140
180
|
cursor = semicolon + 1;
|
|
141
181
|
}
|
|
142
182
|
}
|
|
143
|
-
function* textFragments(source, start, end, decode, recover) {
|
|
183
|
+
function* textFragments(source, start, end, decode, recover, attribute = false) {
|
|
144
184
|
let pending = "";
|
|
145
|
-
for (const part of textParts(source, start, end, decode, recover)) {
|
|
185
|
+
for (const part of textParts(source, start, end, decode, recover, attribute)) {
|
|
146
186
|
if (typeof part !== "string") {
|
|
147
187
|
yield part;
|
|
148
188
|
continue;
|
|
@@ -254,10 +294,16 @@ function validateLimits(limits) {
|
|
|
254
294
|
}
|
|
255
295
|
export function* parseXmlSourceSteps(length, limits = {}) {
|
|
256
296
|
validateLimits(limits);
|
|
297
|
+
if (limits.deferContentNames && !limits.fragmentContent)
|
|
298
|
+
throw new TypeError("Deferred XML content names require fragmentContent: true");
|
|
299
|
+
if (limits.fragmentAttributes && !limits.storeAttributes)
|
|
300
|
+
throw new TypeError("XML attribute fragments require storeAttributes: true");
|
|
257
301
|
if (limits.storeFrames && limits.retainTree !== false)
|
|
258
302
|
throw new TypeError("Stored XML frames require retainTree: false");
|
|
259
303
|
if (limits.storeAttributes && limits.retainTree !== false)
|
|
260
304
|
throw new TypeError("Stored XML attributes require retainTree: false");
|
|
305
|
+
if (limits.deferNamespaces && !limits.storeNamespaces)
|
|
306
|
+
throw new TypeError("Deferred XML namespaces require stored namespaces");
|
|
261
307
|
if (limits.storeNamespaces && limits.retainTree !== false)
|
|
262
308
|
throw new TypeError("Stored XML namespaces require retainTree: false");
|
|
263
309
|
if (limits.fragmentContent && limits.retainTree !== false)
|
|
@@ -314,7 +360,7 @@ export function* parseXmlSourceSteps(length, limits = {}) {
|
|
|
314
360
|
}
|
|
315
361
|
return offset - start;
|
|
316
362
|
};
|
|
317
|
-
const scanName = function* () {
|
|
363
|
+
const scanName = function* (defer = false) {
|
|
318
364
|
const start = offset;
|
|
319
365
|
while (yield* source.has(offset)) {
|
|
320
366
|
const c = (yield* source.charCodeAt(offset));
|
|
@@ -327,6 +373,10 @@ export function* parseXmlSourceSteps(length, limits = {}) {
|
|
|
327
373
|
pendingWork -= 512;
|
|
328
374
|
}
|
|
329
375
|
}
|
|
376
|
+
if (defer && offset - start > 512) {
|
|
377
|
+
yield* validateNameSpan(source, start, offset);
|
|
378
|
+
return ["", "", "", { start, end: offset }];
|
|
379
|
+
}
|
|
330
380
|
const name = (yield* source.slice(start, offset));
|
|
331
381
|
pendingWork += offset - start;
|
|
332
382
|
while (pendingWork >= 512) {
|
|
@@ -336,7 +386,7 @@ export function* parseXmlSourceSteps(length, limits = {}) {
|
|
|
336
386
|
const [prefix, localName] = qualifiedNameSync(name, qualifiedNames);
|
|
337
387
|
return [name, prefix, localName];
|
|
338
388
|
};
|
|
339
|
-
const rawContent = function* (kind, start, end, parent, target = "") {
|
|
389
|
+
const rawContent = function* (kind, start, end, parent, target = "", targetSource) {
|
|
340
390
|
if (end - start > maxTextLength - textLength)
|
|
341
391
|
throw new XmlLimitError("maxTextLength", "XML text limit exceeded");
|
|
342
392
|
textLength += end - start;
|
|
@@ -350,7 +400,7 @@ export function* parseXmlSourceSteps(length, limits = {}) {
|
|
|
350
400
|
finish--;
|
|
351
401
|
}
|
|
352
402
|
const text = yield* source.slice(cursor, finish);
|
|
353
|
-
const content = kind === "processing-instruction" ? { kind, target, text } : { kind, text };
|
|
403
|
+
const content = kind === "processing-instruction" ? { kind, target, text, ...(targetSource ? { targetSource } : {}) } : { kind, text };
|
|
354
404
|
if (kind === "cdata" && retainTree)
|
|
355
405
|
parent.element.text += text;
|
|
356
406
|
if (retain)
|
|
@@ -486,7 +536,7 @@ export function* parseXmlSourceSteps(length, limits = {}) {
|
|
|
486
536
|
else if ((yield* source.startsWith("<?", offset))) {
|
|
487
537
|
const start = offset;
|
|
488
538
|
offset += 2;
|
|
489
|
-
const [target] = yield* scanName();
|
|
539
|
+
const [target, , , targetSource] = yield* scanName(limits.deferContentNames);
|
|
490
540
|
while (pendingWork >= 512) {
|
|
491
541
|
yield 512;
|
|
492
542
|
pendingWork -= 512;
|
|
@@ -519,7 +569,7 @@ export function* parseXmlSourceSteps(length, limits = {}) {
|
|
|
519
569
|
yield 512;
|
|
520
570
|
pendingWork -= 512;
|
|
521
571
|
}
|
|
522
|
-
yield* rawContent("processing-instruction", body, end, yield* stack.peek(), target);
|
|
572
|
+
yield* rawContent("processing-instruction", body, end, yield* stack.peek(), target, targetSource);
|
|
523
573
|
}
|
|
524
574
|
offset = end + 2;
|
|
525
575
|
}
|
|
@@ -600,28 +650,56 @@ export function* parseXmlSourceSteps(length, limits = {}) {
|
|
|
600
650
|
}
|
|
601
651
|
if (end < 0)
|
|
602
652
|
invalid("unterminated attribute");
|
|
603
|
-
const
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
653
|
+
const fragmented = limits.fragmentAttributes && (limits.storeNamespaces || attribute !== "xmlns" && attrPrefix !== "xmlns");
|
|
654
|
+
const valueStart = offset;
|
|
655
|
+
let value = "", size = 0, matchesXml = true, matchesXmlns = true;
|
|
656
|
+
if (fragmented) {
|
|
657
|
+
if ((yield* source.indexOf("<", offset, end)) >= 0)
|
|
658
|
+
invalid("less-than in attribute");
|
|
659
|
+
for (const part of textFragments(source, offset, end, true, limits.recover, true)) {
|
|
660
|
+
if (typeof part !== "string")
|
|
661
|
+
yield part;
|
|
662
|
+
else {
|
|
663
|
+
matchesXml = matchesXml && part === xmlNamespace.slice(size, size + part.length);
|
|
664
|
+
matchesXmlns = matchesXmlns && part === xmlnsNamespace.slice(size, size + part.length);
|
|
665
|
+
size += part.length;
|
|
666
|
+
}
|
|
667
|
+
}
|
|
668
|
+
matchesXml = matchesXml && size === xmlNamespace.length;
|
|
669
|
+
matchesXmlns = matchesXmlns && size === xmlnsNamespace.length;
|
|
670
|
+
if (size > maxTextLength - textLength)
|
|
671
|
+
throw new XmlLimitError("maxTextLength", "XML text limit exceeded");
|
|
672
|
+
textLength += size;
|
|
608
673
|
}
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
674
|
+
else {
|
|
675
|
+
const raw = (yield* source.slice(offset, end));
|
|
676
|
+
pendingWork += raw.length * 2;
|
|
677
|
+
while (pendingWork >= 512) {
|
|
678
|
+
yield 512;
|
|
679
|
+
pendingWork -= 512;
|
|
680
|
+
}
|
|
681
|
+
if (raw.indexOf("<") >= 0)
|
|
682
|
+
invalid("less-than in attribute");
|
|
683
|
+
let normalized = raw;
|
|
684
|
+
if (raw.includes("\t") || raw.includes("\n") || raw.includes("\r")) {
|
|
685
|
+
normalized = "";
|
|
686
|
+
for (const character of raw)
|
|
687
|
+
normalized += "\t\n\r".includes(character) ? " " : character;
|
|
688
|
+
}
|
|
689
|
+
value = normalized.indexOf("&") < 0 ? (pendingWork += normalized.length, normalized) : yield* entities(normalized, limits.recover);
|
|
690
|
+
while (pendingWork >= 512) {
|
|
691
|
+
yield 512;
|
|
692
|
+
pendingWork -= 512;
|
|
693
|
+
}
|
|
694
|
+
admitText(value);
|
|
616
695
|
}
|
|
617
|
-
|
|
618
|
-
yield* attributes.append(attribute, attrPrefix, attrLocal, value);
|
|
696
|
+
yield* attributes.append(attribute, attrPrefix, attrLocal, value, limits.fragmentAttributes ? { start: offset, end } : undefined);
|
|
619
697
|
offset = end + 1;
|
|
620
698
|
if (attribute === "xmlns" || attribute.startsWith("xmlns:")) {
|
|
621
699
|
const nsPrefix = attribute === "xmlns" ? "" : attribute.slice(6);
|
|
622
|
-
if (nsPrefix === "xmlns" || value === xmlnsNamespace
|
|
623
|
-
|| (nsPrefix === "xml") !== (value === xmlNamespace)
|
|
624
|
-
|| (nsPrefix !== "" && value === ""))
|
|
700
|
+
if (nsPrefix === "xmlns" || (fragmented ? matchesXmlns : value === xmlnsNamespace)
|
|
701
|
+
|| (nsPrefix === "xml") !== (fragmented ? matchesXml : value === xmlNamespace)
|
|
702
|
+
|| (nsPrefix !== "" && (fragmented ? size === 0 : value === "")))
|
|
625
703
|
invalid("invalid namespace binding");
|
|
626
704
|
if (!ownsNamespaces && namespaces instanceof Map) {
|
|
627
705
|
const copy = new Map();
|
|
@@ -632,7 +710,8 @@ export function* parseXmlSourceSteps(length, limits = {}) {
|
|
|
632
710
|
namespaces = copy;
|
|
633
711
|
ownsNamespaces = true;
|
|
634
712
|
}
|
|
635
|
-
namespaces = yield* bindNamespace(namespaces, nsPrefix,
|
|
713
|
+
namespaces = yield* bindNamespace(namespaces, nsPrefix, fragmented
|
|
714
|
+
? textFragments(source, valueStart, end, true, limits.recover ? () => { } : undefined, true) : value);
|
|
636
715
|
if (namespaces.size > maxNamespaces)
|
|
637
716
|
throw new XmlLimitError("maxNamespaces", "XML namespace scope limit exceeded");
|
|
638
717
|
}
|
|
@@ -645,11 +724,12 @@ export function* parseXmlSourceSteps(length, limits = {}) {
|
|
|
645
724
|
if (attribute === "xmlns" || attribute.startsWith("xmlns:"))
|
|
646
725
|
continue;
|
|
647
726
|
pendingWork += attribute.length;
|
|
648
|
-
if (attrPrefix && (yield*
|
|
727
|
+
if (attrPrefix && !(yield* hasNamespace(namespaces, attrPrefix)))
|
|
649
728
|
invalid("unbound attribute prefix");
|
|
650
729
|
if (attributes.length > 1) {
|
|
651
|
-
const
|
|
652
|
-
|
|
730
|
+
const backed = limits.storeAttributes && !(namespaces instanceof Map) ? namespaces : undefined;
|
|
731
|
+
const key = backed ? attrLocal : `${attrPrefix ? (yield* namespaceValue(namespaces, attrPrefix)) : ""}\0${attrLocal}`;
|
|
732
|
+
if (yield* attributes.expanded(key, backed ? { scope: backed, prefix: attrPrefix } : undefined))
|
|
653
733
|
invalid("duplicate expanded attribute");
|
|
654
734
|
}
|
|
655
735
|
}
|
|
@@ -659,14 +739,14 @@ export function* parseXmlSourceSteps(length, limits = {}) {
|
|
|
659
739
|
yield 512;
|
|
660
740
|
pendingWork -= 512;
|
|
661
741
|
}
|
|
662
|
-
if (prefix === "xmlns" || (prefix && (yield*
|
|
742
|
+
if (prefix === "xmlns" || (prefix && !(yield* hasNamespace(namespaces, prefix))))
|
|
663
743
|
invalid("unbound element prefix");
|
|
664
744
|
if (++nodes > maxNodes)
|
|
665
745
|
throw new XmlLimitError("maxNodes", "XML resource limit exceeded");
|
|
666
746
|
if (stack.length + 1 > maxDepth)
|
|
667
747
|
throw new XmlLimitError("maxDepth", "XML resource limit exceeded");
|
|
668
|
-
const namespace =
|
|
669
|
-
limits.onElement?.({ name, namespace, localName }, (yield* stack.peek())?.element, stack.length + 1);
|
|
748
|
+
const namespace = yield* namespaceMetadata(namespaces, prefix, limits.deferNamespaces);
|
|
749
|
+
limits.onElement?.({ name, ...namespace, localName }, (yield* stack.peek())?.element, stack.length + 1);
|
|
670
750
|
admitContent();
|
|
671
751
|
const retainedAttributes = [];
|
|
672
752
|
if (retainContent) {
|
|
@@ -686,7 +766,7 @@ export function* parseXmlSourceSteps(length, limits = {}) {
|
|
|
686
766
|
pendingWork -= 512;
|
|
687
767
|
}
|
|
688
768
|
const content = retain ? [] : undefined;
|
|
689
|
-
const element = { kind: "element", name, namespace, localName, children: [], text: "", content: content ?? emptyContent, attributes: retainContent ? retainedAttributes : emptyAttributes, namespaces: retainContent && namespaces instanceof Map ? namespaces : emptyNamespaces, ...(root === undefined && retainContent ? { prolog, epilog, ...(declaration === undefined ? {} : { declaration }) } : {}) };
|
|
769
|
+
const element = { kind: "element", name, ...namespace, localName, children: [], text: "", content: content ?? emptyContent, attributes: retainContent ? retainedAttributes : emptyAttributes, namespaces: retainContent && namespaces instanceof Map ? namespaces : emptyNamespaces, ...(root === undefined && retainContent ? { prolog, epilog, ...(declaration === undefined ? {} : { declaration }) } : {}) };
|
|
690
770
|
const parent = (yield* stack.peek());
|
|
691
771
|
if (parent) {
|
|
692
772
|
if (retainTree) {
|
|
@@ -716,10 +796,24 @@ export function* parseXmlSourceSteps(length, limits = {}) {
|
|
|
716
796
|
for (let cursor = attributes.first; cursor;) {
|
|
717
797
|
const attribute = yield* attributes.read(cursor, true);
|
|
718
798
|
cursor = attribute.next;
|
|
719
|
-
const namespace = attribute.name === "xmlns" || attribute.prefix === "xmlns" ? xmlnsNamespace
|
|
720
|
-
: attribute.prefix ?
|
|
721
|
-
|
|
722
|
-
|
|
799
|
+
const namespace = attribute.name === "xmlns" || attribute.prefix === "xmlns" ? { namespace: xmlnsNamespace }
|
|
800
|
+
: attribute.prefix ? yield* namespaceMetadata(namespaces, attribute.prefix, limits.deferNamespaces) : { namespace: "" };
|
|
801
|
+
let continuation = false;
|
|
802
|
+
if (attribute.source) {
|
|
803
|
+
for (const part of textFragments(source, attribute.source.start, attribute.source.end, true, limits.recover ? () => { } : undefined, true)) {
|
|
804
|
+
if (typeof part !== "string") {
|
|
805
|
+
yield part;
|
|
806
|
+
continue;
|
|
807
|
+
}
|
|
808
|
+
limits.onAttribute({ name: attribute.name, localName: attribute.localName, ...namespace, value: part }, element, continuation);
|
|
809
|
+
continuation = true;
|
|
810
|
+
yield 1;
|
|
811
|
+
}
|
|
812
|
+
}
|
|
813
|
+
if (!continuation) {
|
|
814
|
+
limits.onAttribute({ name: attribute.name, localName: attribute.localName, ...namespace, value: attribute.value }, element);
|
|
815
|
+
yield 1;
|
|
816
|
+
}
|
|
723
817
|
}
|
|
724
818
|
}
|
|
725
819
|
if (!empty)
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "toolcraft",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.800",
|
|
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.800",
|
|
181
181
|
"toolcraft-design": "*",
|
|
182
182
|
"@poe-code/frontmatter": "*",
|
|
183
183
|
"@poe-code/agent-mcp-config": "*",
|