toolcraft 0.0.788 → 0.0.790
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 +2 -2
- package/node_modules/@poe-code/safe-fs/dist/core.js +1 -1
- package/node_modules/@poe-code/xml-ast/README.md +25 -1
- package/node_modules/@poe-code/xml-ast/dist/characters.d.ts +3 -0
- package/node_modules/@poe-code/xml-ast/dist/characters.js +53 -0
- package/node_modules/@poe-code/xml-ast/dist/frames.d.ts +28 -0
- package/node_modules/@poe-code/xml-ast/dist/frames.js +45 -0
- package/node_modules/@poe-code/xml-ast/dist/index.d.ts +15 -0
- package/node_modules/@poe-code/xml-ast/dist/index.js +120 -94
- package/node_modules/@poe-code/xml-ast/dist/source.d.ts +24 -0
- package/node_modules/@poe-code/xml-ast/dist/source.js +70 -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.790",
|
|
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.790",
|
|
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.790",
|
|
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.790",
|
|
152
152
|
"license": "MIT"
|
|
153
153
|
},
|
|
154
154
|
{
|
|
@@ -8,8 +8,8 @@ export * from "./fs/object-publication/index.js";
|
|
|
8
8
|
export * from "./fs/webdav/index.js";
|
|
9
9
|
export * from "./bridge/index.js";
|
|
10
10
|
export * from "./python/index.js";
|
|
11
|
-
export { parseXml, parseXmlSteps, parseXmlStream, XmlLimitError } from "./xml.js";
|
|
12
|
-
export type { XmlName, XmlAttribute, XmlContent, XmlElement, XmlLimits } from "./xml.js";
|
|
11
|
+
export { parseXml, parseXmlSteps, parseXmlSourceSteps, normalizeXmlChunks, parseXmlStream, XmlLimitError } from "./xml.js";
|
|
12
|
+
export type { XmlName, XmlAttribute, XmlContent, XmlElement, XmlLimits, XmlParserFrame, XmlFrameRequest } 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";
|
|
@@ -8,7 +8,7 @@ export * from "./fs/object-publication/index.js";
|
|
|
8
8
|
export * from "./fs/webdav/index.js";
|
|
9
9
|
export * from "./bridge/index.js";
|
|
10
10
|
export * from "./python/index.js";
|
|
11
|
-
export { parseXml, parseXmlSteps, parseXmlStream, XmlLimitError } from "./xml.js";
|
|
11
|
+
export { parseXml, parseXmlSteps, parseXmlSourceSteps, normalizeXmlChunks, parseXmlStream, XmlLimitError } from "./xml.js";
|
|
12
12
|
export * from "./contracts/object.js";
|
|
13
13
|
export { ObjectAuthority } from "./fs/object-authority.js";
|
|
14
14
|
export * from "./fs/s3/index.js";
|
|
@@ -31,7 +31,7 @@ Resource limits and the prohibition on DTDs remain in force. With `retainTree: f
|
|
|
31
31
|
repaired closing events. Drain events between generator checkpoints to await
|
|
32
32
|
external storage; the source string and open parser frames remain resident.
|
|
33
33
|
|
|
34
|
-
`parseXmlStream` consumes each chunk before requesting the next and calls the optional asynchronous checkpoint with at most 512 UTF-16 units. It preserves the buffered parser’s tree shape, namespaces and retained content. The source is closed on parsing or checkpoint failure. Decode byte sources incrementally before passing their strings. This avoids a full source string; the resulting tree and individual XML tokens still reside in memory. Recovery
|
|
34
|
+
`parseXmlStream` consumes each chunk before requesting the next and calls the optional asynchronous checkpoint with at most 512 UTF-16 units. It preserves the buffered parser’s tree shape, namespaces and retained content. The source is closed on parsing or checkpoint failure. Decode byte sources incrementally before passing their strings. This avoids a full source string; the resulting tree and individual XML tokens still reside in memory. Recovery is available through `parseXmlSteps` or the externally backed source API.
|
|
35
35
|
|
|
36
36
|
For validation or root-name detection, pass `retainTree: false` to `parseXmlStream`. It validates the complete document and enforces the same limits, but returns a root element with empty data fields. Only open element frames, namespace scopes and current parser tokens remain resident; completed descendants, text, attributes and document content are not retained. `onElement` still observes each element.
|
|
37
37
|
|
|
@@ -45,3 +45,27 @@ window awaits the consumer before advancing. Event elements contain names,
|
|
|
45
45
|
attributes and namespace metadata; individual XML tokens and open ancestry still
|
|
46
46
|
remain resident. This mode lets a caller own document storage independently of
|
|
47
47
|
the codec. Consumer failures propagate unchanged and close the input iterator.
|
|
48
|
+
|
|
49
|
+
`normalizeXmlChunks(chunks)` validates XML characters and normalizes BOM/line endings
|
|
50
|
+
in windows of at most 513 UTF-16 units, preserving surrogate pairs across chunks.
|
|
51
|
+
Store those windows in caller-owned backing and drive `parseXmlSourceSteps(length,
|
|
52
|
+
limits)`: numbers are work checkpoints; read requests specify UTF-16 `offset` and
|
|
53
|
+
`length` (at most 4096 units). Fill each request's `value` before advancing the
|
|
54
|
+
iterator. The source must already be validated and normalized. This supports the
|
|
55
|
+
same repairs and events without a complete source string; tokens and open ancestry
|
|
56
|
+
still remain resident. Close the iterator and backing on errors or cancellation.
|
|
57
|
+
|
|
58
|
+
For tree-free external parsing, set `storeFrames: true` alongside `retainTree: false`.
|
|
59
|
+
The iterator also yields `frameOperation` requests: `push` saves the supplied frame,
|
|
60
|
+
`pop` removes the latest frame, and `peek` needs its `frame` filled with the saved
|
|
61
|
+
metadata. Keep the host stack in bounded backing storage. The parser caches one
|
|
62
|
+
current frame; individual frame metadata and namespace scopes still have their own
|
|
63
|
+
size cost. The default string and stream APIs retain their existing ancestry behavior.
|
|
64
|
+
|
|
65
|
+
Pass `undefined` as the source length to parse incrementally. Such read requests
|
|
66
|
+
have `streaming: true` and request at most 512 UTF-16 units. Return any nonempty
|
|
67
|
+
prefix available without pulling more input; set `complete: true` only when that
|
|
68
|
+
read reaches EOF (an empty EOF read is valid). Earlier offsets may be requested
|
|
69
|
+
again, so retain source spans in caller-backed storage. With `normalizeXmlChunks`,
|
|
70
|
+
pass `true` as the second argument to flush at input-chunk boundaries and preserve
|
|
71
|
+
early parser failures. The string parser and known-length protocol are unchanged.
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
export declare function validCharacter(point: number): boolean;
|
|
2
|
+
/** Validate scalar values and normalize XML line endings in fixed windows. */
|
|
3
|
+
export declare function normalizeXmlChunks(source: AsyncIterable<string> | Iterable<string>, flushChunks?: boolean): AsyncGenerator<string>;
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
export function validCharacter(point) {
|
|
2
|
+
return point === 9 || point === 10 || point === 13
|
|
3
|
+
|| (point >= 0x20 && point <= 0xd7ff)
|
|
4
|
+
|| (point >= 0xe000 && point <= 0xfffd)
|
|
5
|
+
|| (point >= 0x10000 && point <= 0x10ffff);
|
|
6
|
+
}
|
|
7
|
+
/** Validate scalar values and normalize XML line endings in fixed windows. */
|
|
8
|
+
export async function* normalizeXmlChunks(source, flushChunks = false) {
|
|
9
|
+
let first = true, afterCR = false, high = 0, output = '';
|
|
10
|
+
for await (const chunk of source) {
|
|
11
|
+
for (let index = 0; index < chunk.length; index++) {
|
|
12
|
+
const code = chunk.charCodeAt(index);
|
|
13
|
+
if (first) {
|
|
14
|
+
first = false;
|
|
15
|
+
if (code === 0xfeff)
|
|
16
|
+
continue;
|
|
17
|
+
}
|
|
18
|
+
if (high) {
|
|
19
|
+
if (code < 0xdc00 || code > 0xdfff)
|
|
20
|
+
throw new SyntaxError('Invalid XML: invalid character');
|
|
21
|
+
output += String.fromCharCode(high, code);
|
|
22
|
+
high = 0;
|
|
23
|
+
}
|
|
24
|
+
else {
|
|
25
|
+
if (afterCR) {
|
|
26
|
+
afterCR = false;
|
|
27
|
+
if (code === 10)
|
|
28
|
+
continue;
|
|
29
|
+
}
|
|
30
|
+
if (code >= 0xd800 && code <= 0xdbff) {
|
|
31
|
+
high = code;
|
|
32
|
+
continue;
|
|
33
|
+
}
|
|
34
|
+
if (!validCharacter(code))
|
|
35
|
+
throw new SyntaxError('Invalid XML: invalid character');
|
|
36
|
+
afterCR = code === 13;
|
|
37
|
+
output += code === 13 ? '\n' : chunk[index];
|
|
38
|
+
}
|
|
39
|
+
if (output.length >= 512) {
|
|
40
|
+
yield output;
|
|
41
|
+
output = '';
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
if (flushChunks && output) {
|
|
45
|
+
yield output;
|
|
46
|
+
output = '';
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
if (high)
|
|
50
|
+
throw new SyntaxError('Invalid XML: invalid character');
|
|
51
|
+
if (output)
|
|
52
|
+
yield output;
|
|
53
|
+
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import type { XmlContent, XmlElement } from './index.js';
|
|
2
|
+
export interface XmlParserFrame {
|
|
3
|
+
element: XmlElement;
|
|
4
|
+
content: XmlContent[] | undefined;
|
|
5
|
+
name: string;
|
|
6
|
+
namespaces: Map<string, string>;
|
|
7
|
+
}
|
|
8
|
+
export type XmlFrameRequest = {
|
|
9
|
+
readonly frameOperation: 'push';
|
|
10
|
+
readonly frame: XmlParserFrame;
|
|
11
|
+
} | {
|
|
12
|
+
readonly frameOperation: 'pop';
|
|
13
|
+
} | {
|
|
14
|
+
readonly frameOperation: 'peek';
|
|
15
|
+
frame?: XmlParserFrame;
|
|
16
|
+
};
|
|
17
|
+
/** External mode caches only the current frame. The host owns the linked stack. */
|
|
18
|
+
export declare class XmlFrames {
|
|
19
|
+
private readonly external;
|
|
20
|
+
private readonly frames;
|
|
21
|
+
private depth;
|
|
22
|
+
private current;
|
|
23
|
+
constructor(external: boolean);
|
|
24
|
+
get length(): number;
|
|
25
|
+
peek(): Generator<XmlFrameRequest, XmlParserFrame | undefined, void>;
|
|
26
|
+
push(frame: XmlParserFrame): Generator<XmlFrameRequest, void, void>;
|
|
27
|
+
pop(): Generator<XmlFrameRequest, XmlParserFrame | undefined, void>;
|
|
28
|
+
}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
/** External mode caches only the current frame. The host owns the linked stack. */
|
|
2
|
+
export class XmlFrames {
|
|
3
|
+
external;
|
|
4
|
+
frames = [];
|
|
5
|
+
depth = 0;
|
|
6
|
+
current;
|
|
7
|
+
constructor(external) {
|
|
8
|
+
this.external = external;
|
|
9
|
+
}
|
|
10
|
+
get length() { return this.external ? this.depth : this.frames.length; }
|
|
11
|
+
*peek() {
|
|
12
|
+
if (!this.external)
|
|
13
|
+
return this.frames.at(-1);
|
|
14
|
+
if (!this.depth)
|
|
15
|
+
return undefined;
|
|
16
|
+
if (!this.current) {
|
|
17
|
+
const request = { frameOperation: 'peek' };
|
|
18
|
+
yield request;
|
|
19
|
+
if (!request.frame)
|
|
20
|
+
throw new TypeError('Incomplete XML frame read');
|
|
21
|
+
this.current = request.frame;
|
|
22
|
+
}
|
|
23
|
+
return this.current;
|
|
24
|
+
}
|
|
25
|
+
*push(frame) {
|
|
26
|
+
if (!this.external) {
|
|
27
|
+
this.frames.push(frame);
|
|
28
|
+
return;
|
|
29
|
+
}
|
|
30
|
+
yield { frameOperation: 'push', frame };
|
|
31
|
+
this.current = frame;
|
|
32
|
+
this.depth++;
|
|
33
|
+
}
|
|
34
|
+
*pop() {
|
|
35
|
+
if (!this.external)
|
|
36
|
+
return this.frames.pop();
|
|
37
|
+
if (!this.depth)
|
|
38
|
+
return undefined;
|
|
39
|
+
const frame = yield* this.peek();
|
|
40
|
+
yield { frameOperation: 'pop' };
|
|
41
|
+
this.depth--;
|
|
42
|
+
this.current = undefined;
|
|
43
|
+
return frame;
|
|
44
|
+
}
|
|
45
|
+
}
|
|
@@ -1,3 +1,8 @@
|
|
|
1
|
+
import { type XmlFrameRequest } from "./frames.js";
|
|
2
|
+
export type { XmlParserFrame, XmlFrameRequest } from "./frames.js";
|
|
3
|
+
export { normalizeXmlChunks } from "./characters.js";
|
|
4
|
+
import { type XmlSourceStep } from "./source.js";
|
|
5
|
+
export type { XmlSourceRead, XmlSourceStep } from "./source.js";
|
|
1
6
|
import type { XmlStreamEvent } from "./stream.js";
|
|
2
7
|
export { XmlLimitError } from "./errors.js";
|
|
3
8
|
export interface XmlName {
|
|
@@ -48,6 +53,16 @@ export interface XmlStepLimits extends XmlLimits {
|
|
|
48
53
|
* false. Drive the step iterator to await external consumers between windows. */
|
|
49
54
|
readonly events?: (event: XmlStreamEvent) => void;
|
|
50
55
|
}
|
|
56
|
+
export interface XmlSourceLimits extends XmlStepLimits {
|
|
57
|
+
/** Store active parser frames through host-serviced requests. Requires retainTree: false. */
|
|
58
|
+
readonly storeFrames?: boolean;
|
|
59
|
+
}
|
|
60
|
+
export type XmlParseStep = XmlSourceStep | XmlFrameRequest;
|
|
61
|
+
/** Parse a validated, BOM-free, line-normalized UTF-16 source via bounded read requests. */
|
|
62
|
+
export declare function parseXmlSourceSteps(length: number | undefined, limits?: XmlStepLimits & {
|
|
63
|
+
readonly storeFrames?: false;
|
|
64
|
+
}): Generator<XmlSourceStep, XmlElement, void>;
|
|
65
|
+
export declare function parseXmlSourceSteps(length: number | undefined, limits: XmlSourceLimits): Generator<XmlParseStep, XmlElement, void>;
|
|
51
66
|
export declare function parseXmlSteps(input: string, limits?: XmlStepLimits): Generator<number, XmlElement, void>;
|
|
52
67
|
export declare function parseXml(input: string, limits?: XmlLimits): XmlElement;
|
|
53
68
|
export { parseXmlStream, type XmlStreamLimits, type XmlStreamEvent } from "./stream.js";
|
|
@@ -1,7 +1,11 @@
|
|
|
1
|
+
import { XmlFrames } from "./frames.js";
|
|
2
|
+
import { validCharacter } from "./characters.js";
|
|
3
|
+
export { normalizeXmlChunks } from "./characters.js";
|
|
4
|
+
import { XmlSource } from "./source.js";
|
|
1
5
|
import { XmlLimitError } from "./errors.js";
|
|
2
6
|
export { XmlLimitError } from "./errors.js";
|
|
3
7
|
function* find(source, needle, start) {
|
|
4
|
-
const found = source.indexOf(needle, start);
|
|
8
|
+
const found = typeof source === "string" ? source.indexOf(needle, start) : yield* source.indexOf(needle, start);
|
|
5
9
|
const end = found < 0 ? source.length : found;
|
|
6
10
|
let remaining = end - start;
|
|
7
11
|
while (remaining >= 512) {
|
|
@@ -17,12 +21,6 @@ const xmlnsNamespace = "http://www.w3.org/2000/xmlns/";
|
|
|
17
21
|
function invalid(message) {
|
|
18
22
|
throw new SyntaxError(`Invalid XML: ${message}`);
|
|
19
23
|
}
|
|
20
|
-
function validCharacter(point) {
|
|
21
|
-
return point === 9 || point === 10 || point === 13
|
|
22
|
-
|| (point >= 0x20 && point <= 0xd7ff)
|
|
23
|
-
|| (point >= 0xe000 && point <= 0xfffd)
|
|
24
|
-
|| (point >= 0x10000 && point <= 0x10ffff);
|
|
25
|
-
}
|
|
26
24
|
function nameStart(point) {
|
|
27
25
|
return point === 95 || (point >= 65 && point <= 90) || (point >= 97 && point <= 122)
|
|
28
26
|
|| (point >= 0xc0 && point <= 0xd6) || (point >= 0xd8 && point <= 0xf6)
|
|
@@ -182,72 +180,41 @@ function* validDeclaration(content, expectedEncoding) {
|
|
|
182
180
|
}
|
|
183
181
|
return offset === content.length;
|
|
184
182
|
}
|
|
185
|
-
|
|
183
|
+
function validateLimits(limits) {
|
|
184
|
+
for (const limit of [limits.maxDepth, limits.maxNodes, limits.maxAttributes, limits.maxContentNodes, limits.maxAttributesPerElement, limits.maxNamespaces]) {
|
|
185
|
+
if (limit !== undefined && ((limit !== Infinity && !Number.isSafeInteger(limit)) || limit < 1))
|
|
186
|
+
throw new RangeError("XML limits must be positive integers");
|
|
187
|
+
}
|
|
188
|
+
if (limits.maxTextLength !== undefined && ((limits.maxTextLength !== Infinity && !Number.isSafeInteger(limits.maxTextLength)) || limits.maxTextLength < 1))
|
|
189
|
+
throw new RangeError("XML limits must be positive integers");
|
|
190
|
+
if (limits.events && limits.retainTree !== false)
|
|
191
|
+
throw new TypeError("XML events require retainTree: false");
|
|
192
|
+
}
|
|
193
|
+
export function* parseXmlSourceSteps(length, limits = {}) {
|
|
194
|
+
validateLimits(limits);
|
|
195
|
+
if (limits.storeFrames && limits.retainTree !== false)
|
|
196
|
+
throw new TypeError("Stored XML frames require retainTree: false");
|
|
197
|
+
const source = new XmlSource(length);
|
|
186
198
|
const maxDepth = limits.maxDepth ?? Infinity;
|
|
187
199
|
const maxNodes = limits.maxNodes ?? Infinity;
|
|
188
200
|
const maxAttributes = limits.maxAttributes ?? Infinity;
|
|
189
201
|
const retainContent = limits.retainContent !== false;
|
|
190
202
|
const retainTree = limits.retainTree !== false;
|
|
191
203
|
const retain = retainTree && retainContent;
|
|
192
|
-
if (limits.events && retainTree)
|
|
193
|
-
throw new TypeError("XML events require retainTree: false");
|
|
194
204
|
const maxContentNodes = limits.maxContentNodes ?? Infinity;
|
|
195
205
|
const emptyContent = Object.freeze([]);
|
|
196
206
|
const emptyAttributes = Object.freeze([]);
|
|
197
207
|
const emptyNamespaces = new Map();
|
|
198
208
|
const maxAttributesPerElement = limits.maxAttributesPerElement ?? Infinity;
|
|
199
209
|
const maxNamespaces = limits.maxNamespaces ?? Infinity;
|
|
200
|
-
for (const limit of [limits.maxDepth, limits.maxNodes, limits.maxAttributes, limits.maxContentNodes, limits.maxAttributesPerElement, limits.maxNamespaces]) {
|
|
201
|
-
if (limit !== undefined && ((limit !== Infinity && !Number.isSafeInteger(limit)) || limit < 1))
|
|
202
|
-
throw new RangeError("XML limits must be positive integers");
|
|
203
|
-
}
|
|
204
210
|
const maxTextLength = limits.maxTextLength ?? Infinity;
|
|
205
|
-
if (limits.maxTextLength !== undefined && ((maxTextLength !== Infinity && !Number.isSafeInteger(maxTextLength)) || maxTextLength < 1))
|
|
206
|
-
throw new RangeError("XML limits must be positive integers");
|
|
207
211
|
let textLength = 0;
|
|
208
212
|
const admitText = (text) => {
|
|
209
213
|
if (text.length > maxTextLength - textLength)
|
|
210
214
|
throw new XmlLimitError("maxTextLength", "XML text limit exceeded");
|
|
211
215
|
textLength += text.length;
|
|
212
216
|
};
|
|
213
|
-
const
|
|
214
|
-
const start = input.charCodeAt(0) === 0xfeff ? 1 : 0;
|
|
215
|
-
let chunkStart = start;
|
|
216
|
-
let chunkLength = 0;
|
|
217
|
-
let normalizedChunk = "";
|
|
218
|
-
// Keep validated spans intact; only changed line endings need new strings.
|
|
219
|
-
for (let index = start; index < input.length; index++) {
|
|
220
|
-
const point = input.codePointAt(index);
|
|
221
|
-
if (!validCharacter(point))
|
|
222
|
-
invalid("invalid character");
|
|
223
|
-
if (point === 13) {
|
|
224
|
-
normalizedChunk += input.slice(chunkStart, index) + "\n";
|
|
225
|
-
if (input.charCodeAt(index + 1) === 10)
|
|
226
|
-
index++;
|
|
227
|
-
chunkStart = index + 1;
|
|
228
|
-
chunkLength++;
|
|
229
|
-
}
|
|
230
|
-
else {
|
|
231
|
-
chunkLength += point > 0xffff ? 2 : 1;
|
|
232
|
-
if (point > 0xffff)
|
|
233
|
-
index++;
|
|
234
|
-
}
|
|
235
|
-
if (chunkLength >= 512) {
|
|
236
|
-
if (normalizedChunk) {
|
|
237
|
-
chunks.push(normalizedChunk + input.slice(chunkStart, index + 1));
|
|
238
|
-
normalizedChunk = "";
|
|
239
|
-
chunkStart = index + 1;
|
|
240
|
-
}
|
|
241
|
-
chunkLength = 0;
|
|
242
|
-
yield 512;
|
|
243
|
-
}
|
|
244
|
-
}
|
|
245
|
-
if (chunkLength)
|
|
246
|
-
yield chunkLength;
|
|
247
|
-
if (chunks.length || normalizedChunk)
|
|
248
|
-
chunks.push(normalizedChunk + input.slice(chunkStart));
|
|
249
|
-
const source = chunks.length ? chunks.join("") : input.slice(start);
|
|
250
|
-
const stack = [];
|
|
217
|
+
const stack = new XmlFrames(limits.storeFrames === true);
|
|
251
218
|
let root;
|
|
252
219
|
const prolog = [];
|
|
253
220
|
const epilog = [];
|
|
@@ -265,8 +232,8 @@ export function* parseXmlSteps(input, limits = {}) {
|
|
|
265
232
|
let pendingWork = 0;
|
|
266
233
|
const skipWhitespace = function* () {
|
|
267
234
|
const start = offset;
|
|
268
|
-
while (
|
|
269
|
-
const c = source.charCodeAt(offset);
|
|
235
|
+
while (yield* source.has(offset)) {
|
|
236
|
+
const c = (yield* source.charCodeAt(offset));
|
|
270
237
|
if (c !== 32 && c !== 9 && c !== 10 && c !== 13)
|
|
271
238
|
break;
|
|
272
239
|
offset++;
|
|
@@ -280,8 +247,8 @@ export function* parseXmlSteps(input, limits = {}) {
|
|
|
280
247
|
};
|
|
281
248
|
const scanName = function* () {
|
|
282
249
|
const start = offset;
|
|
283
|
-
while (
|
|
284
|
-
const c = source.charCodeAt(offset);
|
|
250
|
+
while (yield* source.has(offset)) {
|
|
251
|
+
const c = (yield* source.charCodeAt(offset));
|
|
285
252
|
if (c === 32 || c === 9 || c === 13 || c === 10 || c === 47 || c === 61 || c === 62 || c === 63)
|
|
286
253
|
break;
|
|
287
254
|
offset++;
|
|
@@ -291,7 +258,7 @@ export function* parseXmlSteps(input, limits = {}) {
|
|
|
291
258
|
pendingWork -= 512;
|
|
292
259
|
}
|
|
293
260
|
}
|
|
294
|
-
const name = source.slice(start, offset);
|
|
261
|
+
const name = (yield* source.slice(start, offset));
|
|
295
262
|
pendingWork += offset - start;
|
|
296
263
|
while (pendingWork >= 512) {
|
|
297
264
|
yield 512;
|
|
@@ -300,7 +267,7 @@ export function* parseXmlSteps(input, limits = {}) {
|
|
|
300
267
|
const [prefix, localName] = qualifiedNameSync(name, qualifiedNames);
|
|
301
268
|
return [name, prefix, localName];
|
|
302
269
|
};
|
|
303
|
-
while (
|
|
270
|
+
while (yield* source.has(offset)) {
|
|
304
271
|
pendingWork += 1;
|
|
305
272
|
if (pendingWork >= 512) {
|
|
306
273
|
while (pendingWork >= 512) {
|
|
@@ -308,10 +275,10 @@ export function* parseXmlSteps(input, limits = {}) {
|
|
|
308
275
|
pendingWork -= 512;
|
|
309
276
|
}
|
|
310
277
|
}
|
|
311
|
-
if (source.charCodeAt(offset) !== 60) {
|
|
312
|
-
const next = source.indexOf("<", offset);
|
|
278
|
+
if ((yield* source.charCodeAt(offset)) !== 60) {
|
|
279
|
+
const next = (yield* source.indexOf("<", offset));
|
|
313
280
|
const endPos = next < 0 ? source.length : next;
|
|
314
|
-
const text = source.slice(offset, endPos);
|
|
281
|
+
const text = (yield* source.slice(offset, endPos));
|
|
315
282
|
pendingWork += (endPos - offset) + text.length;
|
|
316
283
|
while (pendingWork >= 512) {
|
|
317
284
|
yield 512;
|
|
@@ -325,7 +292,7 @@ export function* parseXmlSteps(input, limits = {}) {
|
|
|
325
292
|
pendingWork -= 512;
|
|
326
293
|
}
|
|
327
294
|
admitText(resolved);
|
|
328
|
-
const parent = stack.
|
|
295
|
+
const parent = (yield* stack.peek());
|
|
329
296
|
if (parent) {
|
|
330
297
|
if (retainTree)
|
|
331
298
|
parent.element.text += resolved;
|
|
@@ -360,13 +327,13 @@ export function* parseXmlSteps(input, limits = {}) {
|
|
|
360
327
|
}
|
|
361
328
|
offset = endPos;
|
|
362
329
|
}
|
|
363
|
-
else if (source.startsWith("<!--", offset)) {
|
|
330
|
+
else if ((yield* source.startsWith("<!--", offset))) {
|
|
364
331
|
const end = yield* find(source, "-->", offset + 4);
|
|
365
|
-
|
|
332
|
+
const text = end < 0 ? "" : yield* source.slice(offset + 4, end);
|
|
333
|
+
if (end < 0 || (yield* find(text, "--", 0)) >= 0 || text.endsWith("-")) {
|
|
366
334
|
invalid("malformed comment");
|
|
367
335
|
}
|
|
368
|
-
const parent = stack.
|
|
369
|
-
const text = source.slice(offset + 4, end);
|
|
336
|
+
const parent = (yield* stack.peek());
|
|
370
337
|
admitText(text);
|
|
371
338
|
admitContent();
|
|
372
339
|
if (retain) {
|
|
@@ -375,15 +342,15 @@ export function* parseXmlSteps(input, limits = {}) {
|
|
|
375
342
|
limits.events?.({ type: "content", content: { kind: "comment", text }, parent: parent?.element });
|
|
376
343
|
offset = end + 3;
|
|
377
344
|
}
|
|
378
|
-
else if (source.startsWith("<![CDATA[", offset)) {
|
|
345
|
+
else if ((yield* source.startsWith("<![CDATA[", offset))) {
|
|
379
346
|
if (!stack.length)
|
|
380
347
|
invalid("CDATA outside root");
|
|
381
348
|
const end = yield* find(source, "]]>", offset + 9);
|
|
382
349
|
if (end < 0)
|
|
383
350
|
invalid("unterminated CDATA");
|
|
384
|
-
const cdataText = source.slice(offset + 9, end);
|
|
351
|
+
const cdataText = (yield* source.slice(offset + 9, end));
|
|
385
352
|
admitText(cdataText);
|
|
386
|
-
const parent = stack.
|
|
353
|
+
const parent = (yield* stack.peek());
|
|
387
354
|
if (retainTree)
|
|
388
355
|
parent.element.text += cdataText;
|
|
389
356
|
admitContent();
|
|
@@ -392,7 +359,7 @@ export function* parseXmlSteps(input, limits = {}) {
|
|
|
392
359
|
limits.events?.({ type: "content", content: { kind: "cdata", text: cdataText }, parent: parent.element });
|
|
393
360
|
offset = end + 3;
|
|
394
361
|
}
|
|
395
|
-
else if (source.startsWith("<?", offset)) {
|
|
362
|
+
else if ((yield* source.startsWith("<?", offset))) {
|
|
396
363
|
const start = offset;
|
|
397
364
|
offset += 2;
|
|
398
365
|
const [target] = yield* scanName();
|
|
@@ -403,19 +370,19 @@ export function* parseXmlSteps(input, limits = {}) {
|
|
|
403
370
|
const end = yield* find(source, "?>", offset);
|
|
404
371
|
if (end < 0)
|
|
405
372
|
invalid("unterminated processing instruction");
|
|
406
|
-
const content = source.slice(offset, end);
|
|
373
|
+
const content = (yield* source.slice(offset, end));
|
|
407
374
|
if (target.length === 3 && target.toLowerCase() === "xml") {
|
|
408
375
|
if (start !== 0 || target !== "xml"
|
|
409
376
|
|| !(yield* validDeclaration(content, limits.expectedEncoding))) {
|
|
410
377
|
invalid("unsupported XML declaration");
|
|
411
378
|
}
|
|
412
379
|
if (retainContent)
|
|
413
|
-
declaration = source.slice(start, end + 2);
|
|
380
|
+
declaration = (yield* source.slice(start, end + 2));
|
|
414
381
|
}
|
|
415
382
|
else if (content && !" \t\n\r".includes(content[0]))
|
|
416
383
|
invalid("invalid processing instruction");
|
|
417
384
|
if (!(target.length === 3 && target.toLowerCase() === "xml")) {
|
|
418
|
-
const parent = stack.
|
|
385
|
+
const parent = (yield* stack.peek());
|
|
419
386
|
let wsStart = 0;
|
|
420
387
|
while (wsStart < content.length && " \t\n\r".includes(content[wsStart]))
|
|
421
388
|
wsStart++;
|
|
@@ -434,10 +401,10 @@ export function* parseXmlSteps(input, limits = {}) {
|
|
|
434
401
|
}
|
|
435
402
|
offset = end + 2;
|
|
436
403
|
}
|
|
437
|
-
else if (source.startsWith("<!", offset)) {
|
|
404
|
+
else if ((yield* source.startsWith("<!", offset))) {
|
|
438
405
|
invalid("DTD and entity declarations are forbidden");
|
|
439
406
|
}
|
|
440
|
-
else if (source.charCodeAt(offset + 1) === 47) {
|
|
407
|
+
else if ((yield* source.charCodeAt(offset + 1)) === 47) {
|
|
441
408
|
offset += 2;
|
|
442
409
|
const [name] = yield* scanName();
|
|
443
410
|
yield* skipWhitespace();
|
|
@@ -445,18 +412,18 @@ export function* parseXmlSteps(input, limits = {}) {
|
|
|
445
412
|
yield 512;
|
|
446
413
|
pendingWork -= 512;
|
|
447
414
|
}
|
|
448
|
-
const closed = source
|
|
415
|
+
const closed = (yield* source.slice(offset++, offset)) === ">" ? (yield* stack.pop()) : undefined;
|
|
449
416
|
if (closed?.name !== name) {
|
|
450
417
|
if (!limits.recover)
|
|
451
418
|
invalid("mismatched closing tag");
|
|
452
419
|
limits.recover("mismatched closing tag");
|
|
453
420
|
}
|
|
454
421
|
if (closed)
|
|
455
|
-
limits.events?.({ type: "close", element: closed.element, parent: stack.
|
|
422
|
+
limits.events?.({ type: "close", element: closed.element, parent: (yield* stack.peek())?.element });
|
|
456
423
|
}
|
|
457
424
|
else {
|
|
458
425
|
offset++;
|
|
459
|
-
const repeated = previousEmpty && source.startsWith(previousEmpty.suffix, offset) ? previousEmpty : undefined;
|
|
426
|
+
const repeated = previousEmpty && (yield* source.startsWith(previousEmpty.suffix, offset)) ? previousEmpty : undefined;
|
|
460
427
|
const [name, prefix, localName] = repeated
|
|
461
428
|
? [repeated.name, repeated.prefix, repeated.localName]
|
|
462
429
|
: (yield* scanName());
|
|
@@ -471,7 +438,7 @@ export function* parseXmlSteps(input, limits = {}) {
|
|
|
471
438
|
}
|
|
472
439
|
let attributes;
|
|
473
440
|
const attrMeta = [];
|
|
474
|
-
let namespaces = stack.
|
|
441
|
+
let namespaces = (yield* stack.peek())?.namespaces ?? new Map([["xml", xmlNamespace]]);
|
|
475
442
|
let ownsNamespaces = stack.length === 0;
|
|
476
443
|
while (!repeated) {
|
|
477
444
|
const ws = (yield* skipWhitespace());
|
|
@@ -479,7 +446,7 @@ export function* parseXmlSteps(input, limits = {}) {
|
|
|
479
446
|
yield 512;
|
|
480
447
|
pendingWork -= 512;
|
|
481
448
|
}
|
|
482
|
-
const ch = source.charCodeAt(offset);
|
|
449
|
+
const ch = (yield* source.charCodeAt(offset));
|
|
483
450
|
if (ch === 47 || ch === 62 || limits.recover && offset === source.length)
|
|
484
451
|
break;
|
|
485
452
|
if (ws === 0)
|
|
@@ -496,13 +463,13 @@ export function* parseXmlSteps(input, limits = {}) {
|
|
|
496
463
|
if ((attributes?.size ?? 0) >= maxAttributesPerElement)
|
|
497
464
|
throw new XmlLimitError("maxAttributesPerElement", "XML attribute limit exceeded");
|
|
498
465
|
yield* skipWhitespace();
|
|
499
|
-
if (source
|
|
466
|
+
if ((yield* source.slice(offset++, offset)) !== "=")
|
|
500
467
|
invalid("missing attribute equals");
|
|
501
468
|
yield* skipWhitespace();
|
|
502
|
-
const quote = source
|
|
469
|
+
const quote = (yield* source.slice(offset++, offset));
|
|
503
470
|
if (quote !== '"' && quote !== "'")
|
|
504
471
|
invalid("unquoted attribute");
|
|
505
|
-
const end = source.indexOf(quote, offset);
|
|
472
|
+
const end = (yield* source.indexOf(quote, offset));
|
|
506
473
|
const scanLen = (end < 0 ? source.length : end) - offset;
|
|
507
474
|
pendingWork += scanLen;
|
|
508
475
|
while (pendingWork >= 512) {
|
|
@@ -511,7 +478,7 @@ export function* parseXmlSteps(input, limits = {}) {
|
|
|
511
478
|
}
|
|
512
479
|
if (end < 0)
|
|
513
480
|
invalid("unterminated attribute");
|
|
514
|
-
const raw = source.slice(offset, end);
|
|
481
|
+
const raw = (yield* source.slice(offset, end));
|
|
515
482
|
pendingWork += raw.length * 2;
|
|
516
483
|
while (pendingWork >= 512) {
|
|
517
484
|
yield 512;
|
|
@@ -578,7 +545,7 @@ export function* parseXmlSteps(input, limits = {}) {
|
|
|
578
545
|
if (stack.length + 1 > maxDepth)
|
|
579
546
|
throw new XmlLimitError("maxDepth", "XML resource limit exceeded");
|
|
580
547
|
const namespace = namespaces.get(prefix) ?? "";
|
|
581
|
-
limits.onElement?.({ name, namespace, localName }, stack.
|
|
548
|
+
limits.onElement?.({ name, namespace, localName }, (yield* stack.peek())?.element, stack.length + 1);
|
|
582
549
|
admitContent();
|
|
583
550
|
const retainedAttributes = [];
|
|
584
551
|
if (retainContent && attributes) {
|
|
@@ -597,7 +564,7 @@ export function* parseXmlSteps(input, limits = {}) {
|
|
|
597
564
|
}
|
|
598
565
|
const content = retain ? [] : undefined;
|
|
599
566
|
const element = { kind: "element", name, namespace, localName, children: [], text: "", content: content ?? emptyContent, attributes: retainContent ? retainedAttributes : emptyAttributes, namespaces: retainContent ? namespaces : emptyNamespaces, ...(root === undefined && retainContent ? { prolog, epilog, ...(declaration === undefined ? {} : { declaration }) } : {}) };
|
|
600
|
-
const parent = stack.
|
|
567
|
+
const parent = (yield* stack.peek());
|
|
601
568
|
if (parent) {
|
|
602
569
|
if (retainTree) {
|
|
603
570
|
parent.element.children.push(element);
|
|
@@ -608,21 +575,21 @@ export function* parseXmlSteps(input, limits = {}) {
|
|
|
608
575
|
invalid("multiple root elements");
|
|
609
576
|
else
|
|
610
577
|
root = element;
|
|
611
|
-
const empty = source
|
|
578
|
+
const empty = (yield* source.slice(offset, offset + 1)) === "/";
|
|
612
579
|
if (empty)
|
|
613
580
|
offset++;
|
|
614
|
-
if (source
|
|
581
|
+
if ((yield* source.slice(offset++, offset)) !== ">") {
|
|
615
582
|
if (!limits.recover)
|
|
616
583
|
invalid("unterminated start tag");
|
|
617
584
|
limits.recover("unterminated start tag");
|
|
618
585
|
}
|
|
619
586
|
// Only cache compact, attribute-free spellings. Namespace resolution and
|
|
620
587
|
// every admission counter still run for each distinct physical element.
|
|
621
|
-
if (empty && !attributes && name.length <= 512 && source.slice(offset - name.length - 3, offset) === `<${name}/>`)
|
|
588
|
+
if (empty && !attributes && name.length <= 512 && (yield* source.slice(offset - name.length - 3, offset)) === `<${name}/>`)
|
|
622
589
|
previousEmpty = { suffix: name + "/>", name, prefix, localName };
|
|
623
590
|
limits.events?.({ type: "open", element, parent: parent?.element });
|
|
624
591
|
if (!empty)
|
|
625
|
-
stack.push({ element, content, name, namespaces });
|
|
592
|
+
yield* stack.push({ element, content, name, namespaces });
|
|
626
593
|
else
|
|
627
594
|
limits.events?.({ type: "close", element, parent: parent?.element });
|
|
628
595
|
}
|
|
@@ -638,13 +605,72 @@ export function* parseXmlSteps(input, limits = {}) {
|
|
|
638
605
|
invalid("incomplete document");
|
|
639
606
|
limits.recover("incomplete document");
|
|
640
607
|
while (stack.length) {
|
|
641
|
-
const closed = stack.pop();
|
|
642
|
-
limits.events?.({ type: "close", element: closed.element, parent: stack.
|
|
608
|
+
const closed = (yield* stack.pop());
|
|
609
|
+
limits.events?.({ type: "close", element: closed.element, parent: (yield* stack.peek())?.element });
|
|
643
610
|
yield 1;
|
|
644
611
|
}
|
|
645
612
|
}
|
|
646
613
|
return root;
|
|
647
614
|
}
|
|
615
|
+
function* normalizeXmlSteps(input) {
|
|
616
|
+
const chunks = [];
|
|
617
|
+
const start = input.charCodeAt(0) === 0xfeff ? 1 : 0;
|
|
618
|
+
let chunkStart = start;
|
|
619
|
+
let chunkLength = 0;
|
|
620
|
+
let normalizedChunk = "";
|
|
621
|
+
// Keep validated spans intact; only changed line endings need new strings.
|
|
622
|
+
for (let index = start; index < input.length; index++) {
|
|
623
|
+
const point = input.codePointAt(index);
|
|
624
|
+
if (!validCharacter(point))
|
|
625
|
+
invalid("invalid character");
|
|
626
|
+
if (point === 13) {
|
|
627
|
+
normalizedChunk += input.slice(chunkStart, index) + "\n";
|
|
628
|
+
if (input.charCodeAt(index + 1) === 10)
|
|
629
|
+
index++;
|
|
630
|
+
chunkStart = index + 1;
|
|
631
|
+
chunkLength++;
|
|
632
|
+
}
|
|
633
|
+
else {
|
|
634
|
+
chunkLength += point > 0xffff ? 2 : 1;
|
|
635
|
+
if (point > 0xffff)
|
|
636
|
+
index++;
|
|
637
|
+
}
|
|
638
|
+
if (chunkLength >= 512) {
|
|
639
|
+
if (normalizedChunk) {
|
|
640
|
+
chunks.push(normalizedChunk + input.slice(chunkStart, index + 1));
|
|
641
|
+
normalizedChunk = "";
|
|
642
|
+
chunkStart = index + 1;
|
|
643
|
+
}
|
|
644
|
+
chunkLength = 0;
|
|
645
|
+
yield 512;
|
|
646
|
+
}
|
|
647
|
+
}
|
|
648
|
+
if (chunkLength)
|
|
649
|
+
yield chunkLength;
|
|
650
|
+
if (chunks.length || normalizedChunk)
|
|
651
|
+
chunks.push(normalizedChunk + input.slice(chunkStart));
|
|
652
|
+
return chunks.length ? chunks.join("") : input.slice(start);
|
|
653
|
+
}
|
|
654
|
+
export function* parseXmlSteps(input, limits = {}) {
|
|
655
|
+
validateLimits(limits);
|
|
656
|
+
const source = yield* normalizeXmlSteps(input);
|
|
657
|
+
const parser = parseXmlSourceSteps(source.length, limits);
|
|
658
|
+
let step = parser.next();
|
|
659
|
+
try {
|
|
660
|
+
while (!step.done) {
|
|
661
|
+
if (typeof step.value === "number")
|
|
662
|
+
yield step.value;
|
|
663
|
+
else
|
|
664
|
+
step.value.value = source.slice(step.value.offset, step.value.offset + step.value.length);
|
|
665
|
+
step = parser.next();
|
|
666
|
+
}
|
|
667
|
+
return step.value;
|
|
668
|
+
}
|
|
669
|
+
finally {
|
|
670
|
+
if (!step.done)
|
|
671
|
+
parser.return(undefined);
|
|
672
|
+
}
|
|
673
|
+
}
|
|
648
674
|
export function parseXml(input, limits = {}) {
|
|
649
675
|
const parser = parseXmlSteps(input, limits);
|
|
650
676
|
let result = parser.next();
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/** Fill value before advancing. Streaming reads may be short; mark complete at EOF. */
|
|
2
|
+
export interface XmlSourceRead {
|
|
3
|
+
readonly offset: number;
|
|
4
|
+
readonly length: number;
|
|
5
|
+
readonly streaming?: boolean;
|
|
6
|
+
value?: string;
|
|
7
|
+
complete?: boolean;
|
|
8
|
+
}
|
|
9
|
+
export type XmlSourceStep = number | XmlSourceRead;
|
|
10
|
+
/** One fixed window; individual returned tokens still have their own size cost. */
|
|
11
|
+
export declare class XmlSource {
|
|
12
|
+
private window;
|
|
13
|
+
private start;
|
|
14
|
+
private end;
|
|
15
|
+
private readonly streaming;
|
|
16
|
+
constructor(length: number | undefined);
|
|
17
|
+
get length(): number;
|
|
18
|
+
private load;
|
|
19
|
+
has(offset: number): Generator<XmlSourceStep, boolean, void>;
|
|
20
|
+
slice(start: number, end?: number): Generator<XmlSourceStep, string, void>;
|
|
21
|
+
charCodeAt(offset: number): Generator<XmlSourceStep, number, void>;
|
|
22
|
+
startsWith(value: string, offset: number): Generator<XmlSourceStep, boolean, void>;
|
|
23
|
+
indexOf(value: string, offset: number): Generator<XmlSourceStep, number, void>;
|
|
24
|
+
}
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
/** One fixed window; individual returned tokens still have their own size cost. */
|
|
2
|
+
export class XmlSource {
|
|
3
|
+
window = '';
|
|
4
|
+
start = -1;
|
|
5
|
+
end;
|
|
6
|
+
streaming;
|
|
7
|
+
constructor(length) {
|
|
8
|
+
if (length !== undefined && (!Number.isSafeInteger(length) || length < 0))
|
|
9
|
+
throw new RangeError('Invalid XML source length');
|
|
10
|
+
this.streaming = length === undefined;
|
|
11
|
+
this.end = length ?? Infinity;
|
|
12
|
+
}
|
|
13
|
+
get length() { return this.end; }
|
|
14
|
+
*load(offset) {
|
|
15
|
+
if (offset >= this.end || offset >= this.start && offset < this.start + this.window.length)
|
|
16
|
+
return;
|
|
17
|
+
const start = this.streaming ? offset : Math.floor(offset / 4096) * 4096;
|
|
18
|
+
const request = { offset: start, length: Math.min(this.streaming ? 512 : 4096, this.end - start),
|
|
19
|
+
...(this.streaming ? { streaming: true } : {}) };
|
|
20
|
+
yield request;
|
|
21
|
+
if (typeof request.value !== 'string' || request.value.length > request.length ||
|
|
22
|
+
(!this.streaming && request.value.length !== request.length) ||
|
|
23
|
+
(!request.value.length && !request.complete && this.streaming))
|
|
24
|
+
throw new TypeError('Incomplete XML source read');
|
|
25
|
+
if (this.streaming && request.complete)
|
|
26
|
+
this.end = start + request.value.length;
|
|
27
|
+
this.window = request.value;
|
|
28
|
+
this.start = start;
|
|
29
|
+
}
|
|
30
|
+
*has(offset) {
|
|
31
|
+
yield* this.load(offset);
|
|
32
|
+
return offset < this.end;
|
|
33
|
+
}
|
|
34
|
+
*slice(start, end = this.length) {
|
|
35
|
+
start = start < 0 ? Math.max(0, this.length + start) : Math.min(start, this.length);
|
|
36
|
+
end = end < 0 ? Math.max(0, this.length + end) : Math.min(end, this.length);
|
|
37
|
+
let result = '';
|
|
38
|
+
while (start < Math.min(end, this.length)) {
|
|
39
|
+
yield* this.load(start);
|
|
40
|
+
if (start >= this.end)
|
|
41
|
+
break;
|
|
42
|
+
const stop = Math.min(end, this.start + this.window.length);
|
|
43
|
+
result += this.window.slice(start - this.start, stop - this.start);
|
|
44
|
+
start = stop;
|
|
45
|
+
}
|
|
46
|
+
return result;
|
|
47
|
+
}
|
|
48
|
+
*charCodeAt(offset) {
|
|
49
|
+
return (yield* this.slice(offset, offset + 1)).charCodeAt(0);
|
|
50
|
+
}
|
|
51
|
+
*startsWith(value, offset) {
|
|
52
|
+
for (let index = 0; index < value.length; index++) {
|
|
53
|
+
if ((yield* this.charCodeAt(offset + index)) !== value.charCodeAt(index))
|
|
54
|
+
return false;
|
|
55
|
+
}
|
|
56
|
+
return true;
|
|
57
|
+
}
|
|
58
|
+
*indexOf(value, offset) {
|
|
59
|
+
let tail = '';
|
|
60
|
+
while (yield* this.has(offset)) {
|
|
61
|
+
const part = tail + this.window.slice(offset - this.start);
|
|
62
|
+
const found = part.indexOf(value);
|
|
63
|
+
if (found >= 0)
|
|
64
|
+
return offset - tail.length + found;
|
|
65
|
+
tail = value.length > 1 ? part.slice(-(value.length - 1)) : '';
|
|
66
|
+
offset = this.start + this.window.length;
|
|
67
|
+
}
|
|
68
|
+
return -1;
|
|
69
|
+
}
|
|
70
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "toolcraft",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.790",
|
|
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.790",
|
|
181
181
|
"toolcraft-design": "*",
|
|
182
182
|
"@poe-code/frontmatter": "*",
|
|
183
183
|
"@poe-code/agent-mcp-config": "*",
|