toolcraft 0.0.786 → 0.0.788
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/README.md +5 -0
- package/node_modules/@poe-code/safe-fs/dist/core.d.ts +1 -1
- package/node_modules/@poe-code/safe-fs/dist/core.js +1 -1
- package/node_modules/@poe-code/safe-fs/dist/storage.d.ts +21 -1
- package/node_modules/@poe-code/safe-fs/dist/storage.js +78 -23
- package/node_modules/@poe-code/xml-ast/README.md +4 -1
- package/node_modules/@poe-code/xml-ast/dist/index.d.ts +9 -1
- package/node_modules/@poe-code/xml-ast/dist/index.js +35 -11
- 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.788",
|
|
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.788",
|
|
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.788",
|
|
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.788",
|
|
152
152
|
"license": "MIT"
|
|
153
153
|
},
|
|
154
154
|
{
|
|
@@ -10,6 +10,11 @@ working data. Supply `{fs, cwd, env, signal}` and an optional page-cache count
|
|
|
10
10
|
`env.TMPDIR` or `cwd`, with conditional unlink and cleanup through `close()`.
|
|
11
11
|
Providers must support those capabilities; there is no host fallback. Use an
|
|
12
12
|
external backend for large data because memory filesystems retain spilled bytes.
|
|
13
|
+
To bound aggregate pages across multiple live stores, pass the same
|
|
14
|
+
`new PagedStorageCache(16)` as the third argument to each `PagedStorage` constructor.
|
|
15
|
+
This shares a 256 KiB resident-page budget; `cache.residentBytes` reports current
|
|
16
|
+
page retention. Eviction uses each store's filesystem, and each store still owns
|
|
17
|
+
its `close()` cleanup. The budget excludes caller buffers and backend storage.
|
|
13
18
|
`IntegerTable.entries()` replays records in unsigned key order with a fixed
|
|
14
19
|
16-level node stack and batches of at most 128 records. It accepts zero-filled
|
|
15
20
|
caller storage with `allocate`, `read` and `write`; mutation during traversal
|
|
@@ -18,4 +18,4 @@ export { createFileSystem, readConfigRecord, validateFileSystemConfig } from "./
|
|
|
18
18
|
export type { FileSystemConfig, FileSystemAdapterDescriptor, FileSystemAdapterRegistry } from "./config.js";
|
|
19
19
|
export { createPortableFileSystemAdapterRegistry } from "./config.portable.js";
|
|
20
20
|
export { readFileStream } from "./fs/read-file-stream.js";
|
|
21
|
-
export { PagedStorage, IntegerTable, type PagedStorageContext } from "./storage.js";
|
|
21
|
+
export { PagedStorage, PagedStorageCache, IntegerTable, type PagedStorageContext } from "./storage.js";
|
|
@@ -16,4 +16,4 @@ export * from "./fs/s3/http/index.js";
|
|
|
16
16
|
export { createFileSystem, readConfigRecord, validateFileSystemConfig } from "./config.js";
|
|
17
17
|
export { createPortableFileSystemAdapterRegistry } from "./config.portable.js";
|
|
18
18
|
export { readFileStream } from "./fs/read-file-stream.js";
|
|
19
|
-
export { PagedStorage, IntegerTable } from "./storage.js";
|
|
19
|
+
export { PagedStorage, PagedStorageCache, IntegerTable } from "./storage.js";
|
|
@@ -5,12 +5,31 @@ export interface PagedStorageContext {
|
|
|
5
5
|
readonly env: Readonly<Record<string, string | undefined>>;
|
|
6
6
|
readonly signal: AbortSignal;
|
|
7
7
|
}
|
|
8
|
+
type Page = {
|
|
9
|
+
bytes: Uint8Array;
|
|
10
|
+
dirty: boolean;
|
|
11
|
+
};
|
|
12
|
+
/** Shares one resident-page limit across stores, retaining each store's caller
|
|
13
|
+
* filesystem and cleanup ownership. Serializes cache users through eviction IO. */
|
|
14
|
+
export declare class PagedStorageCache {
|
|
15
|
+
private readonly maxPages;
|
|
16
|
+
private readonly pages;
|
|
17
|
+
private active;
|
|
18
|
+
constructor(maxPages: number);
|
|
19
|
+
get residentBytes(): number;
|
|
20
|
+
run<T>(action: () => Promise<T>): Promise<T>;
|
|
21
|
+
acquire(): Promise<Page>;
|
|
22
|
+
retain(page: Page, release: () => Promise<void>): void;
|
|
23
|
+
touch(page: Page): void;
|
|
24
|
+
forget(page: Page): void;
|
|
25
|
+
}
|
|
8
26
|
/** A bounded page cache backed only by the caller filesystem. The default is
|
|
9
27
|
* one MiB; callers own close() on every outcome. Memory filesystems still retain
|
|
10
28
|
* spilled data in RAM, so large workloads require an external backing provider. */
|
|
11
29
|
export declare class PagedStorage {
|
|
12
30
|
private readonly context;
|
|
13
31
|
private readonly maxPages;
|
|
32
|
+
private readonly cache?;
|
|
14
33
|
private readonly pages;
|
|
15
34
|
private readonly controller;
|
|
16
35
|
private readonly signal;
|
|
@@ -23,7 +42,7 @@ export declare class PagedStorage {
|
|
|
23
42
|
private end;
|
|
24
43
|
private active;
|
|
25
44
|
private closing;
|
|
26
|
-
constructor(context: PagedStorageContext, maxPages?: number);
|
|
45
|
+
constructor(context: PagedStorageContext, maxPages?: number, cache?: PagedStorageCache | undefined);
|
|
27
46
|
allocate(length: number): number;
|
|
28
47
|
private operation;
|
|
29
48
|
private spill;
|
|
@@ -55,3 +74,4 @@ export declare class IntegerTable {
|
|
|
55
74
|
private lookup;
|
|
56
75
|
private persist;
|
|
57
76
|
}
|
|
77
|
+
export {};
|
|
@@ -1,12 +1,50 @@
|
|
|
1
1
|
import { FsError, isFsError } from "./contracts/errors.js";
|
|
2
2
|
const pageBytes = 16 * 1024;
|
|
3
3
|
let serial = 0;
|
|
4
|
+
/** Shares one resident-page limit across stores, retaining each store's caller
|
|
5
|
+
* filesystem and cleanup ownership. Serializes cache users through eviction IO. */
|
|
6
|
+
export class PagedStorageCache {
|
|
7
|
+
maxPages;
|
|
8
|
+
pages = new Map();
|
|
9
|
+
active = Promise.resolve();
|
|
10
|
+
constructor(maxPages) {
|
|
11
|
+
this.maxPages = maxPages;
|
|
12
|
+
if (!Number.isSafeInteger(maxPages) || maxPages < 1)
|
|
13
|
+
throw new RangeError("Invalid storage page cache size");
|
|
14
|
+
}
|
|
15
|
+
get residentBytes() { return this.pages.size * pageBytes; }
|
|
16
|
+
run(action) {
|
|
17
|
+
const work = this.active.then(action);
|
|
18
|
+
this.active = work.then(() => undefined, () => undefined);
|
|
19
|
+
return work;
|
|
20
|
+
}
|
|
21
|
+
async acquire() {
|
|
22
|
+
if (this.pages.size < this.maxPages)
|
|
23
|
+
return { bytes: new Uint8Array(pageBytes), dirty: false };
|
|
24
|
+
const [page, release] = this.pages.entries().next().value;
|
|
25
|
+
await release();
|
|
26
|
+
this.pages.delete(page);
|
|
27
|
+
page.bytes.fill(0);
|
|
28
|
+
return page;
|
|
29
|
+
}
|
|
30
|
+
retain(page, release) {
|
|
31
|
+
this.pages.delete(page);
|
|
32
|
+
this.pages.set(page, release);
|
|
33
|
+
}
|
|
34
|
+
touch(page) {
|
|
35
|
+
const release = this.pages.get(page);
|
|
36
|
+
this.pages.delete(page);
|
|
37
|
+
this.pages.set(page, release);
|
|
38
|
+
}
|
|
39
|
+
forget(page) { this.pages.delete(page); }
|
|
40
|
+
}
|
|
4
41
|
/** A bounded page cache backed only by the caller filesystem. The default is
|
|
5
42
|
* one MiB; callers own close() on every outcome. Memory filesystems still retain
|
|
6
43
|
* spilled data in RAM, so large workloads require an external backing provider. */
|
|
7
44
|
export class PagedStorage {
|
|
8
45
|
context;
|
|
9
46
|
maxPages;
|
|
47
|
+
cache;
|
|
10
48
|
pages = new Map();
|
|
11
49
|
controller = new AbortController();
|
|
12
50
|
signal;
|
|
@@ -19,9 +57,10 @@ export class PagedStorage {
|
|
|
19
57
|
end = 8; // Zero is the absent-pointer sentinel.
|
|
20
58
|
active = Promise.resolve();
|
|
21
59
|
closing;
|
|
22
|
-
constructor(context, maxPages = 64) {
|
|
60
|
+
constructor(context, maxPages = 64, cache) {
|
|
23
61
|
this.context = context;
|
|
24
62
|
this.maxPages = maxPages;
|
|
63
|
+
this.cache = cache;
|
|
25
64
|
if (!Number.isSafeInteger(maxPages) || maxPages < 1)
|
|
26
65
|
throw new RangeError("Invalid storage page cache size");
|
|
27
66
|
this.signal = AbortSignal.any([context.signal, this.controller.signal]);
|
|
@@ -36,10 +75,11 @@ export class PagedStorage {
|
|
|
36
75
|
}
|
|
37
76
|
operation(action) {
|
|
38
77
|
this.signal.throwIfAborted();
|
|
39
|
-
const
|
|
78
|
+
const execute = () => {
|
|
40
79
|
this.signal.throwIfAborted();
|
|
41
80
|
return action();
|
|
42
|
-
}
|
|
81
|
+
};
|
|
82
|
+
const work = this.active.then(() => this.cache ? this.cache.run(execute) : execute());
|
|
43
83
|
this.active = work.then(() => undefined, () => undefined);
|
|
44
84
|
return work;
|
|
45
85
|
}
|
|
@@ -101,6 +141,7 @@ export class PagedStorage {
|
|
|
101
141
|
if (cached) {
|
|
102
142
|
this.pages.delete(number);
|
|
103
143
|
this.pages.set(number, cached);
|
|
144
|
+
this.cache?.touch(cached);
|
|
104
145
|
return cached;
|
|
105
146
|
}
|
|
106
147
|
let reusable;
|
|
@@ -110,11 +151,14 @@ export class PagedStorage {
|
|
|
110
151
|
const [oldNumber, oldPage] = this.pages.entries().next().value;
|
|
111
152
|
await this.flush(oldNumber, oldPage);
|
|
112
153
|
this.pages.delete(oldNumber);
|
|
154
|
+
this.cache?.forget(oldPage);
|
|
113
155
|
reusable = oldPage;
|
|
114
156
|
}
|
|
115
157
|
// Flush owns the bytes until its awaited write completes. Reuse only then;
|
|
116
158
|
// zero new/unwritten ranges so the previous page cannot leak into them.
|
|
117
|
-
const page = reusable ?? { bytes: new Uint8Array(pageBytes), dirty: false };
|
|
159
|
+
const page = reusable ?? (this.cache ? await this.cache.acquire() : { bytes: new Uint8Array(pageBytes), dirty: false });
|
|
160
|
+
// Shared eviction may await IO owned by a different, still-live signal.
|
|
161
|
+
this.signal.throwIfAborted();
|
|
118
162
|
if (reusable)
|
|
119
163
|
page.bytes.fill(0);
|
|
120
164
|
if (number * pageBytes < this.diskLength) {
|
|
@@ -128,6 +172,12 @@ export class PagedStorage {
|
|
|
128
172
|
}
|
|
129
173
|
}
|
|
130
174
|
this.pages.set(number, page);
|
|
175
|
+
this.cache?.retain(page, async () => {
|
|
176
|
+
if (!this.descriptor)
|
|
177
|
+
await this.spill();
|
|
178
|
+
await this.flush(number, page);
|
|
179
|
+
this.pages.delete(number);
|
|
180
|
+
});
|
|
131
181
|
return page;
|
|
132
182
|
}
|
|
133
183
|
write(position, bytes) {
|
|
@@ -168,28 +218,33 @@ export class PagedStorage {
|
|
|
168
218
|
this.controller.abort(new FsError("ECANCELED"));
|
|
169
219
|
return this.closing ??= (async () => {
|
|
170
220
|
await this.active.catch(() => { });
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
221
|
+
const retire = async () => {
|
|
222
|
+
for (const page of this.pages.values())
|
|
223
|
+
this.cache?.forget(page);
|
|
224
|
+
this.pages.clear();
|
|
225
|
+
const descriptor = this.descriptor;
|
|
226
|
+
if (!descriptor)
|
|
227
|
+
return;
|
|
228
|
+
const cancellation = new FsError("ECANCELED");
|
|
229
|
+
try {
|
|
230
|
+
if (!this.unlinked) {
|
|
231
|
+
const expected = this.initial ?? await descriptor.stat();
|
|
232
|
+
await this.context.fs.removeFileConditional(this.path, { parent: this.parent, expected }).catch(error => {
|
|
233
|
+
if (!isFsError(error) || error.code !== "ENOENT")
|
|
234
|
+
throw error;
|
|
235
|
+
});
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
finally {
|
|
239
|
+
// Conditional descriptors must discard their private staged writes, not
|
|
240
|
+
// publish the scratch data when the command retires its handle.
|
|
241
|
+
await descriptor.close({ signal: AbortSignal.abort(cancellation) }).catch(error => {
|
|
242
|
+
if (error !== cancellation)
|
|
181
243
|
throw error;
|
|
182
244
|
});
|
|
183
245
|
}
|
|
184
|
-
}
|
|
185
|
-
|
|
186
|
-
// Conditional descriptors must discard their private staged writes, not
|
|
187
|
-
// publish the scratch data when the command retires its handle.
|
|
188
|
-
await descriptor.close({ signal: AbortSignal.abort(cancellation) }).catch(error => {
|
|
189
|
-
if (error !== cancellation)
|
|
190
|
-
throw error;
|
|
191
|
-
});
|
|
192
|
-
}
|
|
246
|
+
};
|
|
247
|
+
await (this.cache ? this.cache.run(retire) : retire());
|
|
193
248
|
})();
|
|
194
249
|
}
|
|
195
250
|
}
|
|
@@ -26,7 +26,10 @@ Pass positive integer limits for depth, element and content counts, attributes,
|
|
|
26
26
|
`parseXmlSteps` yields work counts so a host can account for parsing and yield between checkpoints. Syntax errors and resource limits remain errors by default; parsing never fetches external entities.
|
|
27
27
|
Pass `recover: message => report(message)` to repair truncated elements,
|
|
28
28
|
mismatched closing tags, and undeclared entities. Each repair calls the callback.
|
|
29
|
-
Resource limits and the prohibition on DTDs remain in force.
|
|
29
|
+
Resource limits and the prohibition on DTDs remain in force. With `retainTree: false`,
|
|
30
|
+
`parseXmlSteps` discards completed nodes and can emit synchronous `events`, including
|
|
31
|
+
repaired closing events. Drain events between generator checkpoints to await
|
|
32
|
+
external storage; the source string and open parser frames remain resident.
|
|
30
33
|
|
|
31
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 remains available through the buffered parser.
|
|
32
35
|
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { XmlStreamEvent } from "./stream.js";
|
|
1
2
|
export { XmlLimitError } from "./errors.js";
|
|
2
3
|
export interface XmlName {
|
|
3
4
|
readonly name: string;
|
|
@@ -40,6 +41,13 @@ export interface XmlLimits {
|
|
|
40
41
|
readonly maxTextLength?: number;
|
|
41
42
|
readonly onElement?: (element: XmlName, parent: XmlName | undefined, depth: number) => void;
|
|
42
43
|
}
|
|
43
|
-
export
|
|
44
|
+
export interface XmlStepLimits extends XmlLimits {
|
|
45
|
+
/** Return root metadata without retaining descendants or text. */
|
|
46
|
+
readonly retainTree?: boolean;
|
|
47
|
+
/** Synchronous ordered events, including repaired closes. Requires retainTree:
|
|
48
|
+
* false. Drive the step iterator to await external consumers between windows. */
|
|
49
|
+
readonly events?: (event: XmlStreamEvent) => void;
|
|
50
|
+
}
|
|
51
|
+
export declare function parseXmlSteps(input: string, limits?: XmlStepLimits): Generator<number, XmlElement, void>;
|
|
44
52
|
export declare function parseXml(input: string, limits?: XmlLimits): XmlElement;
|
|
45
53
|
export { parseXmlStream, type XmlStreamLimits, type XmlStreamEvent } from "./stream.js";
|
|
@@ -187,6 +187,10 @@ export function* parseXmlSteps(input, limits = {}) {
|
|
|
187
187
|
const maxNodes = limits.maxNodes ?? Infinity;
|
|
188
188
|
const maxAttributes = limits.maxAttributes ?? Infinity;
|
|
189
189
|
const retainContent = limits.retainContent !== false;
|
|
190
|
+
const retainTree = limits.retainTree !== false;
|
|
191
|
+
const retain = retainTree && retainContent;
|
|
192
|
+
if (limits.events && retainTree)
|
|
193
|
+
throw new TypeError("XML events require retainTree: false");
|
|
190
194
|
const maxContentNodes = limits.maxContentNodes ?? Infinity;
|
|
191
195
|
const emptyContent = Object.freeze([]);
|
|
192
196
|
const emptyAttributes = Object.freeze([]);
|
|
@@ -323,11 +327,13 @@ export function* parseXmlSteps(input, limits = {}) {
|
|
|
323
327
|
admitText(resolved);
|
|
324
328
|
const parent = stack.at(-1);
|
|
325
329
|
if (parent) {
|
|
326
|
-
|
|
330
|
+
if (retainTree)
|
|
331
|
+
parent.element.text += resolved;
|
|
327
332
|
if (resolved.length) {
|
|
328
333
|
admitContent();
|
|
329
|
-
if (
|
|
334
|
+
if (retain)
|
|
330
335
|
parent.content.push({ kind: "text", text: resolved });
|
|
336
|
+
limits.events?.({ type: "content", content: { kind: "text", text: resolved }, parent: parent.element });
|
|
331
337
|
}
|
|
332
338
|
}
|
|
333
339
|
else {
|
|
@@ -347,8 +353,9 @@ export function* parseXmlSteps(input, limits = {}) {
|
|
|
347
353
|
}
|
|
348
354
|
if (resolved.length) {
|
|
349
355
|
admitContent();
|
|
350
|
-
if (
|
|
356
|
+
if (retain)
|
|
351
357
|
(root ? epilog : prolog).push({ kind: "text", text: resolved });
|
|
358
|
+
limits.events?.({ type: "content", content: { kind: "text", text: resolved }, parent: undefined });
|
|
352
359
|
}
|
|
353
360
|
}
|
|
354
361
|
offset = endPos;
|
|
@@ -362,9 +369,10 @@ export function* parseXmlSteps(input, limits = {}) {
|
|
|
362
369
|
const text = source.slice(offset + 4, end);
|
|
363
370
|
admitText(text);
|
|
364
371
|
admitContent();
|
|
365
|
-
if (
|
|
372
|
+
if (retain) {
|
|
366
373
|
(parent?.content ?? (root ? epilog : prolog)).push({ kind: "comment", text });
|
|
367
374
|
}
|
|
375
|
+
limits.events?.({ type: "content", content: { kind: "comment", text }, parent: parent?.element });
|
|
368
376
|
offset = end + 3;
|
|
369
377
|
}
|
|
370
378
|
else if (source.startsWith("<![CDATA[", offset)) {
|
|
@@ -376,10 +384,12 @@ export function* parseXmlSteps(input, limits = {}) {
|
|
|
376
384
|
const cdataText = source.slice(offset + 9, end);
|
|
377
385
|
admitText(cdataText);
|
|
378
386
|
const parent = stack.at(-1);
|
|
379
|
-
|
|
387
|
+
if (retainTree)
|
|
388
|
+
parent.element.text += cdataText;
|
|
380
389
|
admitContent();
|
|
381
|
-
if (
|
|
390
|
+
if (retain)
|
|
382
391
|
parent.content.push({ kind: "cdata", text: cdataText });
|
|
392
|
+
limits.events?.({ type: "content", content: { kind: "cdata", text: cdataText }, parent: parent.element });
|
|
383
393
|
offset = end + 3;
|
|
384
394
|
}
|
|
385
395
|
else if (source.startsWith("<?", offset)) {
|
|
@@ -417,9 +427,10 @@ export function* parseXmlSteps(input, limits = {}) {
|
|
|
417
427
|
const text = content.slice(wsStart);
|
|
418
428
|
admitText(text);
|
|
419
429
|
admitContent();
|
|
420
|
-
if (
|
|
430
|
+
if (retain) {
|
|
421
431
|
(parent?.content ?? (root ? epilog : prolog)).push({ kind: "processing-instruction", target, text });
|
|
422
432
|
}
|
|
433
|
+
limits.events?.({ type: "content", content: { kind: "processing-instruction", target, text }, parent: parent?.element });
|
|
423
434
|
}
|
|
424
435
|
offset = end + 2;
|
|
425
436
|
}
|
|
@@ -434,11 +445,14 @@ export function* parseXmlSteps(input, limits = {}) {
|
|
|
434
445
|
yield 512;
|
|
435
446
|
pendingWork -= 512;
|
|
436
447
|
}
|
|
437
|
-
|
|
448
|
+
const closed = source[offset++] === ">" ? stack.pop() : undefined;
|
|
449
|
+
if (closed?.name !== name) {
|
|
438
450
|
if (!limits.recover)
|
|
439
451
|
invalid("mismatched closing tag");
|
|
440
452
|
limits.recover("mismatched closing tag");
|
|
441
453
|
}
|
|
454
|
+
if (closed)
|
|
455
|
+
limits.events?.({ type: "close", element: closed.element, parent: stack.at(-1)?.element });
|
|
442
456
|
}
|
|
443
457
|
else {
|
|
444
458
|
offset++;
|
|
@@ -581,12 +595,14 @@ export function* parseXmlSteps(input, limits = {}) {
|
|
|
581
595
|
yield 512;
|
|
582
596
|
pendingWork -= 512;
|
|
583
597
|
}
|
|
584
|
-
const content =
|
|
598
|
+
const content = retain ? [] : undefined;
|
|
585
599
|
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 }) } : {}) };
|
|
586
600
|
const parent = stack.at(-1);
|
|
587
601
|
if (parent) {
|
|
588
|
-
|
|
589
|
-
|
|
602
|
+
if (retainTree) {
|
|
603
|
+
parent.element.children.push(element);
|
|
604
|
+
parent.content?.push(element);
|
|
605
|
+
}
|
|
590
606
|
}
|
|
591
607
|
else if (root)
|
|
592
608
|
invalid("multiple root elements");
|
|
@@ -604,8 +620,11 @@ export function* parseXmlSteps(input, limits = {}) {
|
|
|
604
620
|
// every admission counter still run for each distinct physical element.
|
|
605
621
|
if (empty && !attributes && name.length <= 512 && source.slice(offset - name.length - 3, offset) === `<${name}/>`)
|
|
606
622
|
previousEmpty = { suffix: name + "/>", name, prefix, localName };
|
|
623
|
+
limits.events?.({ type: "open", element, parent: parent?.element });
|
|
607
624
|
if (!empty)
|
|
608
625
|
stack.push({ element, content, name, namespaces });
|
|
626
|
+
else
|
|
627
|
+
limits.events?.({ type: "close", element, parent: parent?.element });
|
|
609
628
|
}
|
|
610
629
|
}
|
|
611
630
|
if (pendingWork > 0) {
|
|
@@ -618,6 +637,11 @@ export function* parseXmlSteps(input, limits = {}) {
|
|
|
618
637
|
if (!limits.recover)
|
|
619
638
|
invalid("incomplete document");
|
|
620
639
|
limits.recover("incomplete document");
|
|
640
|
+
while (stack.length) {
|
|
641
|
+
const closed = stack.pop();
|
|
642
|
+
limits.events?.({ type: "close", element: closed.element, parent: stack.at(-1)?.element });
|
|
643
|
+
yield 1;
|
|
644
|
+
}
|
|
621
645
|
}
|
|
622
646
|
return root;
|
|
623
647
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "toolcraft",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.788",
|
|
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.788",
|
|
181
181
|
"toolcraft-design": "*",
|
|
182
182
|
"@poe-code/frontmatter": "*",
|
|
183
183
|
"@poe-code/agent-mcp-config": "*",
|