toolcraft 0.0.795 → 0.0.797

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 CHANGED
@@ -138,7 +138,7 @@
138
138
  },
139
139
  {
140
140
  "name": "toolcraft",
141
- "version": "0.0.795",
141
+ "version": "0.0.797",
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.795",
151
+ "version": "0.0.797",
152
152
  "license": "MIT"
153
153
  },
154
154
  {
@@ -138,7 +138,7 @@
138
138
  },
139
139
  {
140
140
  "name": "toolcraft",
141
- "version": "0.0.795",
141
+ "version": "0.0.797",
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.795",
151
+ "version": "0.0.797",
152
152
  "license": "MIT"
153
153
  },
154
154
  {
@@ -15,6 +15,9 @@ To bound aggregate pages across multiple live stores, pass the same
15
15
  This shares a 256 KiB resident-page budget; `cache.residentBytes` reports current
16
16
  page retention. Eviction uses each store's filesystem, and each store still owns
17
17
  its `close()` cleanup. The budget excludes caller buffers and backend storage.
18
+ Call `await storage.prepare()` before opening a directory iterator when its
19
+ snapshot includes the scratch directory. It acquires and unlinks the backing
20
+ handle up front, so later spills do not mutate that directory's namespace.
18
21
  `IntegerTable.entries()` replays records in unsigned key order with a fixed
19
22
  16-level node stack and batches of at most 128 records. It accepts zero-filled
20
23
  caller storage with `allocate`, `read` and `write`; mutation during traversal
@@ -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 } from "./xml.js";
12
+ export type { XmlName, XmlAttribute, XmlContent, XmlElement, XmlLimits, XmlParserFrame, XmlFrameRequest, XmlNamespaceScope, XmlNamespaceRequest } 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";
@@ -44,6 +44,8 @@ export declare class PagedStorage {
44
44
  private closing;
45
45
  constructor(context: PagedStorageContext, maxPages?: number, cache?: PagedStorageCache | undefined);
46
46
  allocate(length: number): number;
47
+ /** Acquire and unlink backing storage before observing a directory snapshot. */
48
+ prepare(): Promise<void>;
47
49
  private operation;
48
50
  private spill;
49
51
  private flush;
@@ -73,6 +73,11 @@ export class PagedStorage {
73
73
  this.end += length;
74
74
  return offset;
75
75
  }
76
+ /** Acquire and unlink backing storage before observing a directory snapshot. */
77
+ async prepare() {
78
+ await this.operation(async () => { if (!this.descriptor)
79
+ await this.spill(); });
80
+ }
76
81
  operation(action) {
77
82
  this.signal.throwIfAborted();
78
83
  const execute = () => {
@@ -62,6 +62,16 @@ metadata. Keep the host stack in bounded backing storage. The parser caches one
62
62
  current frame; individual frame metadata and namespace scopes still have their own
63
63
  size cost. The default string and stream APIs retain their existing ancestry behavior.
64
64
 
65
+ Also set `storeNamespaces: true` to resolve namespace scopes through host-backed
66
+ immutable storage. `namespaceOperation: "get"` supplies a scope and prefix; fill
67
+ `value` when present and set `complete: true`. For `"set"`, bind the supplied prefix
68
+ and value in a new scope, then fill `result` with its storage `reference` and the
69
+ number of distinct bindings (`size`). Frames carry this scope handle so shadowing
70
+ never changes ancestor scopes. In this mode event elements have an empty
71
+ `namespaces` map; their names and attributes still carry resolved namespace URIs.
72
+ The codec remains filesystem-independent, and individual names, attribute values
73
+ and namespace URIs still need a token storage strategy.
74
+
65
75
  Pass `undefined` as the source length to parse incrementally. Such read requests
66
76
  have `streaming: true` and request at most 512 UTF-16 units. Return any nonempty
67
77
  prefix available without pulling more input; set `complete: true` only when that
@@ -4,6 +4,7 @@ export interface XmlParserFrame {
4
4
  content: XmlContent[] | undefined;
5
5
  name: string;
6
6
  namespaces: Map<string, string>;
7
+ namespaceScope?: XmlNamespaceScope;
7
8
  }
8
9
  export type XmlFrameRequest = {
9
10
  readonly frameOperation: 'push';
@@ -26,3 +27,23 @@ export declare class XmlFrames {
26
27
  push(frame: XmlParserFrame): Generator<XmlFrameRequest, void, void>;
27
28
  pop(): Generator<XmlFrameRequest, XmlParserFrame | undefined, void>;
28
29
  }
30
+ /** A host-owned immutable namespace scope; count includes the built-in xml prefix. */
31
+ export interface XmlNamespaceScope {
32
+ readonly reference: number;
33
+ readonly size: number;
34
+ }
35
+ export type XmlNamespaceRequest = {
36
+ readonly namespaceOperation: 'get';
37
+ readonly scope: XmlNamespaceScope;
38
+ readonly prefix: string;
39
+ value?: string;
40
+ complete?: true;
41
+ } | {
42
+ readonly namespaceOperation: 'set';
43
+ readonly scope: XmlNamespaceScope;
44
+ readonly prefix: string;
45
+ readonly value: string;
46
+ result?: XmlNamespaceScope;
47
+ };
48
+ export declare function namespaceValue(scope: Map<string, string> | XmlNamespaceScope, prefix: string): Generator<XmlNamespaceRequest, string | undefined, void>;
49
+ export declare function bindNamespace(scope: Map<string, string> | XmlNamespaceScope, prefix: string, value: string): Generator<XmlNamespaceRequest, Map<string, string> | XmlNamespaceScope, void>;
@@ -43,3 +43,23 @@ export class XmlFrames {
43
43
  return frame;
44
44
  }
45
45
  }
46
+ export function* namespaceValue(scope, prefix) {
47
+ if (scope instanceof Map)
48
+ return scope.get(prefix);
49
+ const request = { namespaceOperation: 'get', scope, prefix };
50
+ yield request;
51
+ if (!request.complete)
52
+ throw new TypeError('Incomplete XML namespace read');
53
+ return request.value;
54
+ }
55
+ export function* bindNamespace(scope, prefix, value) {
56
+ if (scope instanceof Map) {
57
+ scope.set(prefix, value);
58
+ return scope;
59
+ }
60
+ const request = { namespaceOperation: 'set', scope, prefix, value };
61
+ yield request;
62
+ if (!request.result)
63
+ throw new TypeError('Incomplete XML namespace update');
64
+ return request.result;
65
+ }
@@ -1,5 +1,5 @@
1
- import { type XmlFrameRequest } from "./frames.js";
2
- export type { XmlParserFrame, XmlFrameRequest } from "./frames.js";
1
+ import { type XmlNamespaceRequest, type XmlFrameRequest } from "./frames.js";
2
+ export type { XmlParserFrame, XmlFrameRequest, XmlNamespaceScope, XmlNamespaceRequest } from "./frames.js";
3
3
  export { normalizeXmlChunks } from "./characters.js";
4
4
  import { type XmlSourceStep } from "./source.js";
5
5
  export type { XmlSourceRead, XmlSourceStep } from "./source.js";
@@ -61,11 +61,14 @@ export interface XmlSourceLimits extends XmlStepLimits {
61
61
  readonly fragmentContent?: boolean;
62
62
  /** Store active parser frames through host-serviced requests. Requires retainTree: false. */
63
63
  readonly storeFrames?: boolean;
64
+ /** Resolve namespace scopes through host-serviced requests. Requires retainTree: false. */
65
+ readonly storeNamespaces?: boolean;
64
66
  }
65
- export type XmlParseStep = XmlSourceStep | XmlFrameRequest;
67
+ export type XmlParseStep = XmlSourceStep | XmlFrameRequest | XmlNamespaceRequest;
66
68
  /** Parse a validated, BOM-free, line-normalized UTF-16 source via bounded read requests. */
67
69
  export declare function parseXmlSourceSteps(length: number | undefined, limits?: XmlStepLimits & {
68
70
  readonly storeFrames?: false;
71
+ readonly storeNamespaces?: false;
69
72
  }): Generator<XmlSourceStep, XmlElement, void>;
70
73
  export declare function parseXmlSourceSteps(length: number | undefined, limits: XmlSourceLimits): Generator<XmlParseStep, XmlElement, void>;
71
74
  export declare function parseXmlSteps(input: string, limits?: XmlStepLimits): Generator<number, XmlElement, void>;
@@ -1,4 +1,4 @@
1
- import { XmlFrames } from "./frames.js";
1
+ import { XmlFrames, bindNamespace, namespaceValue } from "./frames.js";
2
2
  import { validCharacter } from "./characters.js";
3
3
  export { normalizeXmlChunks } from "./characters.js";
4
4
  import { XmlSource } from "./source.js";
@@ -255,6 +255,8 @@ export function* parseXmlSourceSteps(length, limits = {}) {
255
255
  validateLimits(limits);
256
256
  if (limits.storeFrames && limits.retainTree !== false)
257
257
  throw new TypeError("Stored XML frames require retainTree: false");
258
+ if (limits.storeNamespaces && limits.retainTree !== false)
259
+ throw new TypeError("Stored XML namespaces require retainTree: false");
258
260
  if (limits.fragmentContent && limits.retainTree !== false)
259
261
  throw new TypeError("XML content fragments require retainTree: false");
260
262
  const source = new XmlSource(length);
@@ -277,6 +279,7 @@ export function* parseXmlSourceSteps(length, limits = {}) {
277
279
  throw new XmlLimitError("maxTextLength", "XML text limit exceeded");
278
280
  textLength += text.length;
279
281
  };
282
+ const initialNamespaces = limits.storeNamespaces ? yield* bindNamespace({ reference: 0, size: 0 }, "xml", xmlNamespace) : new Map([["xml", xmlNamespace]]);
280
283
  const stack = new XmlFrames(limits.storeFrames === true);
281
284
  let root;
282
285
  const prolog = [];
@@ -554,7 +557,8 @@ export function* parseXmlSourceSteps(length, limits = {}) {
554
557
  }
555
558
  let attributes;
556
559
  const attrMeta = [];
557
- let namespaces = (yield* stack.peek())?.namespaces ?? new Map([["xml", xmlNamespace]]);
560
+ const namespaceParent = yield* stack.peek();
561
+ let namespaces = namespaceParent?.namespaceScope ?? namespaceParent?.namespaces ?? (initialNamespaces instanceof Map ? new Map(initialNamespaces) : initialNamespaces);
558
562
  let ownsNamespaces = stack.length === 0;
559
563
  while (!repeated) {
560
564
  const ws = (yield* skipWhitespace());
@@ -618,7 +622,7 @@ export function* parseXmlSourceSteps(length, limits = {}) {
618
622
  || (nsPrefix === "xml") !== (value === xmlNamespace)
619
623
  || (nsPrefix !== "" && value === ""))
620
624
  invalid("invalid namespace binding");
621
- if (!ownsNamespaces) {
625
+ if (!ownsNamespaces && namespaces instanceof Map) {
622
626
  const copy = new Map();
623
627
  for (const [key, uri] of namespaces) {
624
628
  copy.set(key, uri);
@@ -627,7 +631,7 @@ export function* parseXmlSourceSteps(length, limits = {}) {
627
631
  namespaces = copy;
628
632
  ownsNamespaces = true;
629
633
  }
630
- namespaces.set(nsPrefix, value);
634
+ namespaces = yield* bindNamespace(namespaces, nsPrefix, value);
631
635
  if (namespaces.size > maxNamespaces)
632
636
  throw new XmlLimitError("maxNamespaces", "XML namespace scope limit exceeded");
633
637
  }
@@ -639,10 +643,10 @@ export function* parseXmlSourceSteps(length, limits = {}) {
639
643
  if (attribute === "xmlns" || attribute.startsWith("xmlns:"))
640
644
  continue;
641
645
  pendingWork += attribute.length;
642
- if (attrPrefix && !namespaces.has(attrPrefix))
646
+ if (attrPrefix && (yield* namespaceValue(namespaces, attrPrefix)) === undefined)
643
647
  invalid("unbound attribute prefix");
644
648
  if (expanded) {
645
- const key = `${attrPrefix ? namespaces.get(attrPrefix) : ""}\0${attrLocal}`;
649
+ const key = `${attrPrefix ? (yield* namespaceValue(namespaces, attrPrefix)) : ""}\0${attrLocal}`;
646
650
  if (expanded.has(key))
647
651
  invalid("duplicate expanded attribute");
648
652
  expanded.add(key);
@@ -654,13 +658,13 @@ export function* parseXmlSourceSteps(length, limits = {}) {
654
658
  yield 512;
655
659
  pendingWork -= 512;
656
660
  }
657
- if (prefix === "xmlns" || (prefix && !namespaces.has(prefix)))
661
+ if (prefix === "xmlns" || (prefix && (yield* namespaceValue(namespaces, prefix)) === undefined))
658
662
  invalid("unbound element prefix");
659
663
  if (++nodes > maxNodes)
660
664
  throw new XmlLimitError("maxNodes", "XML resource limit exceeded");
661
665
  if (stack.length + 1 > maxDepth)
662
666
  throw new XmlLimitError("maxDepth", "XML resource limit exceeded");
663
- const namespace = namespaces.get(prefix) ?? "";
667
+ const namespace = (yield* namespaceValue(namespaces, prefix)) ?? "";
664
668
  limits.onElement?.({ name, namespace, localName }, (yield* stack.peek())?.element, stack.length + 1);
665
669
  admitContent();
666
670
  const retainedAttributes = [];
@@ -670,7 +674,7 @@ export function* parseXmlSourceSteps(length, limits = {}) {
670
674
  const value = attributes.get(attribute);
671
675
  admitContent();
672
676
  retainedAttributes.push({ name: attribute, localName: attrLocal,
673
- namespace: attribute === "xmlns" || attrPrefix === "xmlns" ? xmlnsNamespace : attrPrefix ? namespaces.get(attrPrefix) : "", value });
677
+ namespace: attribute === "xmlns" || attrPrefix === "xmlns" ? xmlnsNamespace : attrPrefix ? (yield* namespaceValue(namespaces, attrPrefix)) : "", value });
674
678
  pendingWork += attribute.length + 1;
675
679
  }
676
680
  }
@@ -679,7 +683,7 @@ export function* parseXmlSourceSteps(length, limits = {}) {
679
683
  pendingWork -= 512;
680
684
  }
681
685
  const content = retain ? [] : undefined;
682
- 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 }) } : {}) };
686
+ 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 }) } : {}) };
683
687
  const parent = (yield* stack.peek());
684
688
  if (parent) {
685
689
  if (retainTree) {
@@ -705,7 +709,7 @@ export function* parseXmlSourceSteps(length, limits = {}) {
705
709
  previousEmpty = { suffix: name + "/>", name, prefix, localName };
706
710
  limits.events?.({ type: "open", element, parent: parent?.element });
707
711
  if (!empty)
708
- yield* stack.push({ element, content, name, namespaces });
712
+ yield* stack.push({ element, content, name, namespaces: namespaces instanceof Map ? namespaces : new Map(), ...(namespaces instanceof Map ? {} : { namespaceScope: namespaces }) });
709
713
  else
710
714
  limits.events?.({ type: "close", element, parent: parent?.element });
711
715
  }
@@ -8,7 +8,7 @@
8
8
  },
9
9
  {
10
10
  "name": "toolcraft-schema",
11
- "version": "0.0.795",
11
+ "version": "0.0.797",
12
12
  "license": "MIT"
13
13
  }
14
14
  ]
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "toolcraft-schema",
3
- "version": "0.0.795",
3
+ "version": "0.0.797",
4
4
  "license": "MIT",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "toolcraft",
3
- "version": "0.0.795",
3
+ "version": "0.0.797",
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.795",
180
+ "toolcraft-schema": "0.0.797",
181
181
  "toolcraft-design": "*",
182
182
  "@poe-code/frontmatter": "*",
183
183
  "@poe-code/agent-mcp-config": "*",