toolcraft 0.0.799 → 0.0.801

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.799",
141
+ "version": "0.0.801",
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.799",
151
+ "version": "0.0.801",
152
152
  "license": "MIT"
153
153
  },
154
154
  {
@@ -138,7 +138,7 @@
138
138
  },
139
139
  {
140
140
  "name": "toolcraft",
141
- "version": "0.0.799",
141
+ "version": "0.0.801",
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.799",
151
+ "version": "0.0.801",
152
152
  "license": "MIT"
153
153
  },
154
154
  {
@@ -136,6 +136,9 @@ function getTaskListDirectory(taskList) {
136
136
  return isTaskListConfig(taskList) ? taskList.dir : "unknown";
137
137
  }
138
138
  function isDeepEqualStateMachine(left, right) {
139
+ if (left.initial !== right.initial) {
140
+ return false;
141
+ }
139
142
  if (!areEqualStrings(left.states, right.states)) {
140
143
  return false;
141
144
  }
@@ -44,7 +44,12 @@ function normalizePlan(value, seen) {
44
44
  }
45
45
  const normalized = {};
46
46
  for (const key of Object.keys(value).sort()) {
47
- normalized[key] = normalizePlan(value[key], seen);
47
+ Object.defineProperty(normalized, key, {
48
+ value: normalizePlan(value[key], seen),
49
+ enumerable: true,
50
+ writable: true,
51
+ configurable: true
52
+ });
48
53
  }
49
54
  return normalized;
50
55
  }
package/dist/runtime.d.ts CHANGED
@@ -227,10 +227,14 @@ export interface CommandRequirementOptions {
227
227
  }
228
228
  export declare function resolveCommandSecrets(command: Command<any, any, any, any>, env?: Record<string, string | undefined>): Record<string, string | undefined>;
229
229
  export declare function assertCommandRequirements(command: Command<any, any, any, any>, context: GroupCheckContext<any>, options?: CommandRequirementOptions): Promise<void>;
230
- export declare function defineCommand<TServices extends object = EmptyServices, TName extends string = string, TParamsSchema extends ObjectSchema<any> = AnyObjectSchema, TSecrets extends SecretDeclarations | undefined = undefined, TResult = unknown, TOwnScope extends ScopeInput = undefined, TOwnHumanInLoop extends HumanInLoopConfig<TParamsSchema> | null | undefined = undefined>(config: Omit<CommandConfig<TServices, TParamsSchema, TSecrets, TResult>, "name" | "scope" | "humanInLoop"> & {
230
+ export declare function defineCommand<TServices extends object = EmptyServices, TName extends string = string, TParamsSchema extends ObjectSchema<any> = AnyObjectSchema, TSecrets extends SecretDeclarations | undefined = undefined, TResult = unknown, TOwnScope extends ScopeInput = undefined, TOwnHumanInLoop extends {
231
+ mode: HumanInLoopMode;
232
+ } | null | undefined = undefined>(config: Omit<CommandConfig<TServices, TParamsSchema, TSecrets, TResult>, "name" | "scope" | "humanInLoop"> & {
231
233
  name: TName;
232
234
  scope?: TOwnScope;
233
- humanInLoop?: TOwnHumanInLoop;
235
+ humanInLoop?: (TOwnHumanInLoop | {
236
+ [K in keyof TOwnHumanInLoop]: TOwnHumanInLoop[K];
237
+ }) & (HumanInLoopConfig<TParamsSchema> | null);
234
238
  }): Command<TServices, TParamsSchema, TSecrets, TResult> & TypedCommandMetadata<TName, TParamsSchema, TResult, TOwnScope, ResolveOwnHumanInLoopMode<TOwnHumanInLoop>>;
235
239
  export declare function defineStreamCommand<TServices extends object = EmptyServices, TName extends string = string, TParamsSchema extends ObjectSchema<any> = AnyObjectSchema, TSecrets extends SecretDeclarations | undefined = undefined, TEventSchema extends AnySchema = AnySchema, TOwnScope extends ScopeInput = undefined>(config: Omit<StreamCommandConfig<TServices, TParamsSchema, TSecrets, TEventSchema>, "name" | "scope"> & {
236
240
  name: TName;
@@ -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,31 +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 `"set"`, bind the supplied prefix
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, and individual names, attribute values
73
- and namespace URIs still need a token storage strategy.
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. With this flag,
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. With `fragmentAttributes: true`, ordinary values are validated and
85
- stored as source spans on attribute records, then replayed in at most 512 UTF-16
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
86
96
  units. Hosts must preserve the optional `source` field in append/read requests.
87
97
  The third `onAttribute` argument is `true` for continuations of the same logical
88
98
  attribute; await each fragment before requesting the next step. Empty attributes
89
99
  still emit once. Entity diagnostics are emitted during validation, not replay.
90
- This mode requires `storeAttributes: true`. Namespace declaration values and
91
- individual names still retain their token cost.
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.
92
109
 
93
110
  Pass `undefined` as the source length to parse incrementally. Such read requests
94
111
  have `streaming: true` and request at most 512 UTF-16 units. Return any nonempty
@@ -107,4 +124,16 @@ decoding replay bounded source windows before publishing a logical node, retaini
107
124
  its diagnostic and limit ordering. Numeric entity references are decoded without
108
125
  materializing the token. Set `compactDeclaration: true` to retain normalized declaration fields without
109
126
  copying long whitespace spans; validation always reads bounded windows. The
110
- buffered API keeps the original declaration spelling. Names and namespace declaration values still require their own memory budget.
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. With `deferElementNames: true` and `retainTree: false`, long element local names
135
+ also use `nameSource`/`localNameSource` spans. Closing tags compare source windows;
136
+ element events retain the short `prefix` separately. Stored documents copy these
137
+ names into token references for formatting, XPath and xq grouping. Attribute names
138
+ and namespace prefixes longer than 512 UTF-16 units still retain their token cost;
139
+ this option does not impose a name-length limit.
@@ -1,9 +1,14 @@
1
+ import type { XmlSourceSpan } from "./source.js";
2
+ import type { XmlNamespaceScope } from "./frames.js";
1
3
  export interface XmlAttributeRecord {
2
4
  name: string;
3
5
  prefix: string;
4
6
  localName: string;
5
7
  value: string;
6
8
  next: number;
9
+ nameSource?: XmlSourceSpan;
10
+ localNameSource?: XmlSourceSpan;
11
+ prefixSource?: XmlSourceSpan;
7
12
  source?: {
8
13
  start: number;
9
14
  end: number;
@@ -19,7 +24,13 @@ export type XmlAttributeRequest = {
19
24
  attributeOperation: 'has' | 'expanded';
20
25
  state: XmlAttributeState;
21
26
  name: string;
27
+ nameSource?: XmlSourceSpan;
22
28
  found?: boolean;
29
+ namespace?: {
30
+ scope: XmlNamespaceScope;
31
+ prefix: string;
32
+ prefixSource?: XmlSourceSpan;
33
+ };
23
34
  } | {
24
35
  attributeOperation: 'append';
25
36
  state: XmlAttributeState;
@@ -44,11 +55,15 @@ export declare class XmlAttributes {
44
55
  get length(): number;
45
56
  get size(): number;
46
57
  get first(): number;
47
- has(name: string): Generator<XmlAttributeRequest, boolean, void>;
58
+ has(name: string, nameSource?: XmlSourceSpan): Generator<XmlAttributeRequest, boolean, void>;
48
59
  append(name: string, prefix: string, localName: string, value: string, source?: {
49
60
  start: number;
50
61
  end: number;
51
- }): Generator<XmlAttributeRequest, void, void>;
62
+ }, nameSource?: XmlSourceSpan, localNameSource?: XmlSourceSpan, prefixSource?: XmlSourceSpan): Generator<XmlAttributeRequest, void, void>;
52
63
  read(reference: number, values: boolean): Generator<XmlAttributeRequest, XmlAttributeRecord, void>;
53
- expanded(name: string): Generator<XmlAttributeRequest, boolean, void>;
64
+ expanded(name: string, namespace?: {
65
+ scope: XmlNamespaceScope;
66
+ prefix: string;
67
+ prefixSource?: XmlSourceSpan;
68
+ }, nameSource?: XmlSourceSpan): Generator<XmlAttributeRequest, boolean, void>;
54
69
  }
@@ -12,25 +12,25 @@ export class XmlAttributes {
12
12
  get length() { return this.state.length; }
13
13
  get size() { return this.state.size; }
14
14
  get first() { return this.state.first; }
15
- *has(name) {
15
+ *has(name, nameSource) {
16
16
  if (!this.length)
17
17
  return false;
18
18
  if (!this.external)
19
19
  return this.names.has(name);
20
- const request = { attributeOperation: 'has', state: this.state, name };
20
+ const request = { attributeOperation: 'has', state: this.state, name, ...(nameSource ? { nameSource } : {}) };
21
21
  yield request;
22
22
  if (request.found === undefined)
23
23
  throw new TypeError('Incomplete XML attribute lookup');
24
24
  return request.found;
25
25
  }
26
- *append(name, prefix, localName, value, source) {
26
+ *append(name, prefix, localName, value, source, nameSource, localNameSource, prefixSource) {
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, ...(source ? { source } : {}) } };
33
+ const request = { attributeOperation: 'append', state: this.state, attribute: { name, prefix, localName, value, ...(source ? { source } : {}), ...(nameSource ? { nameSource } : {}), ...(localNameSource ? { localNameSource } : {}), ...(prefixSource ? { prefixSource } : {}) } };
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, nameSource) {
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 } : {}), ...(nameSource ? { nameSource } : {}) };
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 { XmlContent, XmlElement } from './index.js';
1
+ import type { XmlSourceStep, XmlSourceSpan } 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,17 +34,34 @@ 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
+ readonly prefixSource?: XmlSourceSpan;
41
+ reference?: number;
42
+ complete?: true;
43
+ } | {
44
+ readonly namespaceOperation: 'has';
45
+ readonly scope: XmlNamespaceScope;
46
+ readonly prefix: string;
47
+ readonly prefixSource?: XmlSourceSpan;
48
+ found?: boolean;
49
+ } | {
36
50
  readonly namespaceOperation: 'get';
37
51
  readonly scope: XmlNamespaceScope;
38
52
  readonly prefix: string;
53
+ readonly prefixSource?: XmlSourceSpan;
39
54
  value?: string;
40
55
  complete?: true;
41
56
  } | {
42
57
  readonly namespaceOperation: 'set';
43
58
  readonly scope: XmlNamespaceScope;
44
59
  readonly prefix: string;
45
- readonly value: string;
60
+ readonly prefixSource?: XmlSourceSpan;
61
+ readonly value: string | Generator<XmlSourceStep | string, void, void>;
46
62
  result?: XmlNamespaceScope;
47
63
  };
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>;
64
+ export declare function namespaceValue(scope: Map<string, string> | XmlNamespaceScope, prefix: string, prefixSource?: XmlSourceSpan): Generator<XmlNamespaceRequest, string | undefined, void>;
65
+ export declare function namespaceMetadata(scope: Map<string, string> | XmlNamespaceScope, prefix: string, deferred?: boolean, prefixSource?: XmlSourceSpan): Generator<XmlNamespaceRequest, Pick<XmlName, 'namespace' | 'namespaceReference'>, void>;
66
+ export declare function hasNamespace(scope: Map<string, string> | XmlNamespaceScope, prefix: string, prefixSource?: XmlSourceSpan): Generator<XmlNamespaceRequest, boolean, void>;
67
+ export declare function bindNamespace(scope: Map<string, string> | XmlNamespaceScope, prefix: string, value: string | Generator<XmlSourceStep | string, void, void>, prefixSource?: XmlSourceSpan): Generator<XmlNamespaceRequest, Map<string, string> | XmlNamespaceScope, void>;
@@ -43,21 +43,43 @@ export class XmlFrames {
43
43
  return frame;
44
44
  }
45
45
  }
46
- export function* namespaceValue(scope, prefix) {
46
+ export function* namespaceValue(scope, prefix, prefixSource) {
47
47
  if (scope instanceof Map)
48
48
  return scope.get(prefix);
49
- const request = { namespaceOperation: 'get', scope, prefix };
49
+ const request = { namespaceOperation: 'get', scope, prefix, ...(prefixSource ? { prefixSource } : {}) };
50
50
  yield request;
51
51
  if (!request.complete)
52
52
  throw new TypeError('Incomplete XML namespace read');
53
53
  return request.value;
54
54
  }
55
- export function* bindNamespace(scope, prefix, value) {
55
+ export function* namespaceMetadata(scope, prefix, deferred = false, prefixSource) {
56
+ if (!deferred || scope instanceof Map)
57
+ return { namespace: (yield* namespaceValue(scope, prefix, prefixSource)) ?? '' };
58
+ if (prefix === 'xml')
59
+ return { namespace: 'http://www.w3.org/XML/1998/namespace' };
60
+ const request = { namespaceOperation: 'reference', scope, prefix, ...(prefixSource ? { prefixSource } : {}) };
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, prefixSource) {
67
+ if (scope instanceof Map)
68
+ return scope.has(prefix);
69
+ const request = { namespaceOperation: 'has', scope, prefix, ...(prefixSource ? { prefixSource } : {}) };
70
+ yield request;
71
+ if (request.found === undefined)
72
+ throw new TypeError('Incomplete XML namespace membership lookup');
73
+ return request.found;
74
+ }
75
+ export function* bindNamespace(scope, prefix, value, prefixSource) {
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
  }
60
- const request = { namespaceOperation: 'set', scope, prefix, value };
82
+ const request = { namespaceOperation: 'set', scope, prefix, value, ...(prefixSource ? { prefixSource } : {}) };
61
83
  yield request;
62
84
  if (!request.result)
63
85
  throw new TypeError('Incomplete XML namespace update');
@@ -3,13 +3,21 @@ 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
+ readonly nameSource?: XmlSourceSpan;
13
+ readonly nameReference?: number;
14
+ readonly localNameSource?: XmlSourceSpan;
15
+ readonly localNameReference?: number;
16
+ readonly prefix?: string;
17
+ readonly prefixSource?: XmlSourceSpan;
18
+ readonly prefixReference?: number;
12
19
  readonly namespace: string;
20
+ readonly namespaceReference?: number;
13
21
  readonly localName: string;
14
22
  }
15
23
  export interface XmlAttribute extends XmlName {
@@ -21,6 +29,8 @@ export type XmlContent = XmlElement | {
21
29
  } | {
22
30
  readonly kind: "processing-instruction";
23
31
  readonly target: string;
32
+ readonly targetSource?: XmlSourceSpan;
33
+ readonly targetReference?: number;
24
34
  readonly text: string;
25
35
  };
26
36
  export interface XmlElement extends XmlName {
@@ -65,6 +75,14 @@ export interface XmlSourceLimits extends XmlStepLimits {
65
75
  readonly storeFrames?: boolean;
66
76
  /** Resolve namespace scopes through host-serviced requests. Requires retainTree: false. */
67
77
  readonly storeNamespaces?: boolean;
78
+ /** Carry host namespace token references in event metadata. Requires stored namespaces. */
79
+ readonly deferNamespaces?: boolean;
80
+ /** Emit long processing-instruction targets as source spans. Requires fragmentContent. */
81
+ readonly deferContentNames?: boolean;
82
+ /** Defer long element names and, with external namespaces, their prefixes. */
83
+ readonly deferElementNames?: boolean;
84
+ /** Defer attribute names and namespace declaration prefixes to source spans. */
85
+ readonly deferAttributeNames?: boolean;
68
86
  /** Store attribute collections through host requests and emit resolved attributes individually. */
69
87
  readonly storeAttributes?: boolean;
70
88
  readonly fragmentAttributes?: boolean;
@@ -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,41 @@ 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 = -1, 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 >= 0 || first)
83
+ invalid("invalid qualified name");
84
+ colon = offset;
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
+ return colon;
105
+ }
71
106
  function* entity(source, start, end, recover) {
72
107
  // Inspect only a bounded prefix, even for arbitrarily long numeric references.
73
108
  const prefix = typeof source === "string" ? source.slice(start, Math.min(end, start + 4))
@@ -260,12 +295,20 @@ function validateLimits(limits) {
260
295
  }
261
296
  export function* parseXmlSourceSteps(length, limits = {}) {
262
297
  validateLimits(limits);
298
+ if (limits.deferAttributeNames && (!limits.storeAttributes || !limits.storeNamespaces))
299
+ throw new TypeError("Deferred attribute names require stored attributes and namespaces");
300
+ if (limits.deferElementNames && limits.retainTree !== false)
301
+ throw new TypeError("Deferred element names require retainTree: false");
302
+ if (limits.deferContentNames && !limits.fragmentContent)
303
+ throw new TypeError("Deferred XML content names require fragmentContent: true");
263
304
  if (limits.fragmentAttributes && !limits.storeAttributes)
264
305
  throw new TypeError("XML attribute fragments require storeAttributes: true");
265
306
  if (limits.storeFrames && limits.retainTree !== false)
266
307
  throw new TypeError("Stored XML frames require retainTree: false");
267
308
  if (limits.storeAttributes && limits.retainTree !== false)
268
309
  throw new TypeError("Stored XML attributes require retainTree: false");
310
+ if (limits.deferNamespaces && !limits.storeNamespaces)
311
+ throw new TypeError("Deferred XML namespaces require stored namespaces");
269
312
  if (limits.storeNamespaces && limits.retainTree !== false)
270
313
  throw new TypeError("Stored XML namespaces require retainTree: false");
271
314
  if (limits.fragmentContent && limits.retainTree !== false)
@@ -322,7 +365,24 @@ export function* parseXmlSourceSteps(length, limits = {}) {
322
365
  }
323
366
  return offset - start;
324
367
  };
325
- const scanName = function* () {
368
+ const sameName = function* (left, right, span) {
369
+ if (!left.nameSource && !span)
370
+ return left.name === right;
371
+ const leftLength = left.nameSource ? left.nameSource.end - left.nameSource.start : left.name.length;
372
+ const rightLength = span ? span.end - span.start : right.length;
373
+ if (leftLength !== rightLength)
374
+ return false;
375
+ for (let index = 0; index < leftLength; index += 512) {
376
+ const end = Math.min(leftLength, index + 512);
377
+ const a = left.nameSource ? yield* source.slice(left.nameSource.start + index, left.nameSource.start + end) : left.name.slice(index, end);
378
+ const b = span ? yield* source.slice(span.start + index, span.start + end) : right.slice(index, end);
379
+ yield end - index;
380
+ if (a !== b)
381
+ return false;
382
+ }
383
+ return true;
384
+ };
385
+ const scanName = function* (defer) {
326
386
  const start = offset;
327
387
  while (yield* source.has(offset)) {
328
388
  const c = (yield* source.charCodeAt(offset));
@@ -335,6 +395,21 @@ export function* parseXmlSourceSteps(length, limits = {}) {
335
395
  pendingWork -= 512;
336
396
  }
337
397
  }
398
+ if (defer && offset - start > 512) {
399
+ const colon = yield* validateNameSpan(source, start, offset);
400
+ if (defer === "content")
401
+ return ["", "", "", { start, end: offset }];
402
+ if (colon - start > 512 && !limits.storeNamespaces) {
403
+ const name = yield* source.slice(start, offset);
404
+ return [name, name.slice(0, colon - start), name.slice(colon - start + 1)];
405
+ }
406
+ const localStart = colon < 0 ? start : colon + 1;
407
+ const prefixSource = colon - start > 512 ? { start, end: colon } : undefined;
408
+ const prefix = colon < 0 || prefixSource ? "" : yield* source.slice(start, colon);
409
+ const localNameSource = offset - localStart > 512 ? { start: localStart, end: offset } : undefined;
410
+ const local = localNameSource ? "" : yield* source.slice(localStart, offset);
411
+ return ["", prefix, local, { start, end: offset }, localNameSource, prefixSource];
412
+ }
338
413
  const name = (yield* source.slice(start, offset));
339
414
  pendingWork += offset - start;
340
415
  while (pendingWork >= 512) {
@@ -344,7 +419,7 @@ export function* parseXmlSourceSteps(length, limits = {}) {
344
419
  const [prefix, localName] = qualifiedNameSync(name, qualifiedNames);
345
420
  return [name, prefix, localName];
346
421
  };
347
- const rawContent = function* (kind, start, end, parent, target = "") {
422
+ const rawContent = function* (kind, start, end, parent, target = "", targetSource) {
348
423
  if (end - start > maxTextLength - textLength)
349
424
  throw new XmlLimitError("maxTextLength", "XML text limit exceeded");
350
425
  textLength += end - start;
@@ -358,7 +433,7 @@ export function* parseXmlSourceSteps(length, limits = {}) {
358
433
  finish--;
359
434
  }
360
435
  const text = yield* source.slice(cursor, finish);
361
- const content = kind === "processing-instruction" ? { kind, target, text } : { kind, text };
436
+ const content = kind === "processing-instruction" ? { kind, target, text, ...(targetSource ? { targetSource } : {}) } : { kind, text };
362
437
  if (kind === "cdata" && retainTree)
363
438
  parent.element.text += text;
364
439
  if (retain)
@@ -494,7 +569,7 @@ export function* parseXmlSourceSteps(length, limits = {}) {
494
569
  else if ((yield* source.startsWith("<?", offset))) {
495
570
  const start = offset;
496
571
  offset += 2;
497
- const [target] = yield* scanName();
572
+ const [target, , , targetSource] = yield* scanName(limits.deferContentNames ? "content" : undefined);
498
573
  while (pendingWork >= 512) {
499
574
  yield 512;
500
575
  pendingWork -= 512;
@@ -527,7 +602,7 @@ export function* parseXmlSourceSteps(length, limits = {}) {
527
602
  yield 512;
528
603
  pendingWork -= 512;
529
604
  }
530
- yield* rawContent("processing-instruction", body, end, yield* stack.peek(), target);
605
+ yield* rawContent("processing-instruction", body, end, yield* stack.peek(), target, targetSource);
531
606
  }
532
607
  offset = end + 2;
533
608
  }
@@ -536,14 +611,14 @@ export function* parseXmlSourceSteps(length, limits = {}) {
536
611
  }
537
612
  else if ((yield* source.charCodeAt(offset + 1)) === 47) {
538
613
  offset += 2;
539
- const [name] = yield* scanName();
614
+ const [name, , , nameSource] = yield* scanName(limits.deferElementNames ? "element" : undefined);
540
615
  yield* skipWhitespace();
541
616
  while (pendingWork >= 512) {
542
617
  yield 512;
543
618
  pendingWork -= 512;
544
619
  }
545
620
  const closed = (yield* source.slice(offset++, offset)) === ">" ? (yield* stack.pop()) : undefined;
546
- if (closed?.name !== name) {
621
+ if (!closed || !(yield* sameName(closed.element, name, nameSource))) {
547
622
  if (!limits.recover)
548
623
  invalid("mismatched closing tag");
549
624
  limits.recover("mismatched closing tag");
@@ -554,9 +629,9 @@ export function* parseXmlSourceSteps(length, limits = {}) {
554
629
  else {
555
630
  offset++;
556
631
  const repeated = previousEmpty && (yield* source.startsWith(previousEmpty.suffix, offset)) ? previousEmpty : undefined;
557
- const [name, prefix, localName] = repeated
632
+ const [name, prefix, localName, nameSource, localNameSource, prefixSource] = repeated
558
633
  ? [repeated.name, repeated.prefix, repeated.localName]
559
- : (yield* scanName());
634
+ : (yield* scanName(limits.deferElementNames ? "element" : undefined));
560
635
  if (repeated)
561
636
  offset += name.length;
562
637
  // Preserve scan/validation work while reusing the admitted spelling.
@@ -581,12 +656,12 @@ export function* parseXmlSourceSteps(length, limits = {}) {
581
656
  break;
582
657
  if (ws === 0)
583
658
  invalid("attributes require whitespace");
584
- const [attribute, attrPrefix, attrLocal] = yield* scanName();
659
+ const [attribute, attrPrefix, attrLocal, nameSource, localNameSource, attrPrefixSource] = yield* scanName(limits.deferAttributeNames ? "attribute" : undefined);
585
660
  while (pendingWork >= 512) {
586
661
  yield 512;
587
662
  pendingWork -= 512;
588
663
  }
589
- if ((yield* attributes.has(attribute)))
664
+ if ((yield* attributes.has(attribute, nameSource)))
590
665
  invalid("duplicate attribute");
591
666
  if (++attributeCount > maxAttributes)
592
667
  throw new XmlLimitError("maxAttributes", "XML attribute limit exceeded");
@@ -608,18 +683,23 @@ export function* parseXmlSourceSteps(length, limits = {}) {
608
683
  }
609
684
  if (end < 0)
610
685
  invalid("unterminated attribute");
611
- const fragmented = limits.fragmentAttributes && attribute !== "xmlns" && attrPrefix !== "xmlns";
612
- let value = "";
686
+ const fragmented = limits.fragmentAttributes && (limits.storeNamespaces || attribute !== "xmlns" && attrPrefix !== "xmlns");
687
+ const valueStart = offset;
688
+ let value = "", size = 0, matchesXml = true, matchesXmlns = true;
613
689
  if (fragmented) {
614
690
  if ((yield* source.indexOf("<", offset, end)) >= 0)
615
691
  invalid("less-than in attribute");
616
- let size = 0;
617
692
  for (const part of textFragments(source, offset, end, true, limits.recover, true)) {
618
693
  if (typeof part !== "string")
619
694
  yield part;
620
- else
695
+ else {
696
+ matchesXml = matchesXml && part === xmlNamespace.slice(size, size + part.length);
697
+ matchesXmlns = matchesXmlns && part === xmlnsNamespace.slice(size, size + part.length);
621
698
  size += part.length;
699
+ }
622
700
  }
701
+ matchesXml = matchesXml && size === xmlNamespace.length;
702
+ matchesXmlns = matchesXmlns && size === xmlnsNamespace.length;
623
703
  if (size > maxTextLength - textLength)
624
704
  throw new XmlLimitError("maxTextLength", "XML text limit exceeded");
625
705
  textLength += size;
@@ -646,13 +726,14 @@ export function* parseXmlSourceSteps(length, limits = {}) {
646
726
  }
647
727
  admitText(value);
648
728
  }
649
- yield* attributes.append(attribute, attrPrefix, attrLocal, value, fragmented ? { start: offset, end } : undefined);
729
+ yield* attributes.append(attribute, attrPrefix, attrLocal, value, limits.fragmentAttributes ? { start: offset, end } : undefined, nameSource, localNameSource, attrPrefixSource);
650
730
  offset = end + 1;
651
- if (attribute === "xmlns" || attribute.startsWith("xmlns:")) {
652
- const nsPrefix = attribute === "xmlns" ? "" : attribute.slice(6);
653
- if (nsPrefix === "xmlns" || value === xmlnsNamespace
654
- || (nsPrefix === "xml") !== (value === xmlNamespace)
655
- || (nsPrefix !== "" && value === ""))
731
+ if (attribute === "xmlns" || attrPrefix === "xmlns") {
732
+ const nsPrefix = attribute === "xmlns" ? "" : attrLocal;
733
+ const nsPrefixSource = attrPrefix === "xmlns" ? localNameSource : undefined;
734
+ if (nsPrefix === "xmlns" || (fragmented ? matchesXmlns : value === xmlnsNamespace)
735
+ || (nsPrefix === "xml") !== (fragmented ? matchesXml : value === xmlNamespace)
736
+ || ((nsPrefix !== "" || nsPrefixSource) && (fragmented ? size === 0 : value === "")))
656
737
  invalid("invalid namespace binding");
657
738
  if (!ownsNamespaces && namespaces instanceof Map) {
658
739
  const copy = new Map();
@@ -663,7 +744,8 @@ export function* parseXmlSourceSteps(length, limits = {}) {
663
744
  namespaces = copy;
664
745
  ownsNamespaces = true;
665
746
  }
666
- namespaces = yield* bindNamespace(namespaces, nsPrefix, value);
747
+ namespaces = yield* bindNamespace(namespaces, nsPrefix, fragmented
748
+ ? textFragments(source, valueStart, end, true, limits.recover ? () => { } : undefined, true) : value, nsPrefixSource);
667
749
  if (namespaces.size > maxNamespaces)
668
750
  throw new XmlLimitError("maxNamespaces", "XML namespace scope limit exceeded");
669
751
  }
@@ -673,14 +755,15 @@ export function* parseXmlSourceSteps(length, limits = {}) {
673
755
  const record = yield* attributes.read(cursor, false);
674
756
  cursor = record.next;
675
757
  const { name: attribute, prefix: attrPrefix, localName: attrLocal } = record;
676
- if (attribute === "xmlns" || attribute.startsWith("xmlns:"))
758
+ if (attribute === "xmlns" || attrPrefix === "xmlns")
677
759
  continue;
678
760
  pendingWork += attribute.length;
679
- if (attrPrefix && (yield* namespaceValue(namespaces, attrPrefix)) === undefined)
761
+ if ((attrPrefix || record.prefixSource) && !(yield* hasNamespace(namespaces, attrPrefix, record.prefixSource)))
680
762
  invalid("unbound attribute prefix");
681
763
  if (attributes.length > 1) {
682
- const key = `${attrPrefix ? (yield* namespaceValue(namespaces, attrPrefix)) : ""}\0${attrLocal}`;
683
- if (yield* attributes.expanded(key))
764
+ const backed = limits.storeAttributes && !(namespaces instanceof Map) ? namespaces : undefined;
765
+ const key = backed ? attrLocal : `${attrPrefix ? (yield* namespaceValue(namespaces, attrPrefix)) : ""}\0${attrLocal}`;
766
+ if (yield* attributes.expanded(key, backed ? { scope: backed, prefix: attrPrefix, ...(record.prefixSource ? { prefixSource: record.prefixSource } : {}) } : undefined, record.localNameSource))
684
767
  invalid("duplicate expanded attribute");
685
768
  }
686
769
  }
@@ -690,14 +773,15 @@ export function* parseXmlSourceSteps(length, limits = {}) {
690
773
  yield 512;
691
774
  pendingWork -= 512;
692
775
  }
693
- if (prefix === "xmlns" || (prefix && (yield* namespaceValue(namespaces, prefix)) === undefined))
776
+ if (prefix === "xmlns" || ((prefix || prefixSource) && !(yield* hasNamespace(namespaces, prefix, prefixSource))))
694
777
  invalid("unbound element prefix");
695
778
  if (++nodes > maxNodes)
696
779
  throw new XmlLimitError("maxNodes", "XML resource limit exceeded");
697
780
  if (stack.length + 1 > maxDepth)
698
781
  throw new XmlLimitError("maxDepth", "XML resource limit exceeded");
699
- const namespace = (yield* namespaceValue(namespaces, prefix)) ?? "";
700
- limits.onElement?.({ name, namespace, localName }, (yield* stack.peek())?.element, stack.length + 1);
782
+ const namespace = yield* namespaceMetadata(namespaces, prefix, limits.deferNamespaces, prefixSource);
783
+ const elementName = { name, localName, ...(nameSource ? { nameSource, prefix } : {}), ...(localNameSource ? { localNameSource } : {}), ...(prefixSource ? { prefixSource } : {}) };
784
+ limits.onElement?.({ ...elementName, ...namespace }, (yield* stack.peek())?.element, stack.length + 1);
701
785
  admitContent();
702
786
  const retainedAttributes = [];
703
787
  if (retainContent) {
@@ -717,7 +801,7 @@ export function* parseXmlSourceSteps(length, limits = {}) {
717
801
  pendingWork -= 512;
718
802
  }
719
803
  const content = retain ? [] : undefined;
720
- 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 }) } : {}) };
804
+ const element = { kind: "element", ...elementName, ...namespace, children: [], text: "", content: content ?? emptyContent, attributes: retainContent ? retainedAttributes : emptyAttributes, namespaces: retainContent && namespaces instanceof Map ? namespaces : emptyNamespaces, ...(root === undefined && retainContent ? { prolog, epilog, ...(declaration === undefined ? {} : { declaration }) } : {}) };
721
805
  const parent = (yield* stack.peek());
722
806
  if (parent) {
723
807
  if (retainTree) {
@@ -739,7 +823,7 @@ export function* parseXmlSourceSteps(length, limits = {}) {
739
823
  }
740
824
  // Only cache compact, attribute-free spellings. Namespace resolution and
741
825
  // every admission counter still run for each distinct physical element.
742
- if (empty && !attributes.length && name.length <= 512 && (yield* source.slice(offset - name.length - 3, offset)) === `<${name}/>`)
826
+ if (empty && !nameSource && !attributes.length && name.length <= 512 && (yield* source.slice(offset - name.length - 3, offset)) === `<${name}/>`)
743
827
  previousEmpty = { suffix: name + "/>", name, prefix, localName };
744
828
  limits.events?.({ type: "open", element, parent: parent?.element });
745
829
  if (limits.storeAttributes && limits.onAttribute && retainContent) {
@@ -747,8 +831,9 @@ export function* parseXmlSourceSteps(length, limits = {}) {
747
831
  for (let cursor = attributes.first; cursor;) {
748
832
  const attribute = yield* attributes.read(cursor, true);
749
833
  cursor = attribute.next;
750
- const namespace = attribute.name === "xmlns" || attribute.prefix === "xmlns" ? xmlnsNamespace
751
- : attribute.prefix ? (yield* namespaceValue(namespaces, attribute.prefix)) : "";
834
+ const namespace = attribute.name === "xmlns" || attribute.prefix === "xmlns" ? { namespace: xmlnsNamespace }
835
+ : attribute.prefix || attribute.prefixSource ? yield* namespaceMetadata(namespaces, attribute.prefix, limits.deferNamespaces, attribute.prefixSource) : { namespace: "" };
836
+ const nameMetadata = { name: attribute.name, localName: attribute.localName, ...(attribute.nameSource ? { nameSource: attribute.nameSource, prefix: attribute.prefix } : {}), ...(attribute.localNameSource ? { localNameSource: attribute.localNameSource } : {}), ...(attribute.prefixSource ? { prefixSource: attribute.prefixSource } : {}) };
752
837
  let continuation = false;
753
838
  if (attribute.source) {
754
839
  for (const part of textFragments(source, attribute.source.start, attribute.source.end, true, limits.recover ? () => { } : undefined, true)) {
@@ -756,13 +841,13 @@ export function* parseXmlSourceSteps(length, limits = {}) {
756
841
  yield part;
757
842
  continue;
758
843
  }
759
- limits.onAttribute({ name: attribute.name, localName: attribute.localName, namespace, value: part }, element, continuation);
844
+ limits.onAttribute({ ...nameMetadata, ...namespace, value: part }, element, continuation);
760
845
  continuation = true;
761
846
  yield 1;
762
847
  }
763
848
  }
764
849
  if (!continuation) {
765
- limits.onAttribute({ name: attribute.name, localName: attribute.localName, namespace, value: attribute.value }, element);
850
+ limits.onAttribute({ ...nameMetadata, ...namespace, value: attribute.value }, element);
766
851
  yield 1;
767
852
  }
768
853
  }
@@ -1,3 +1,7 @@
1
+ export interface XmlSourceSpan {
2
+ readonly start: number;
3
+ readonly end: number;
4
+ }
1
5
  /** Fill value before advancing. Streaming reads may be short; mark complete at EOF. */
2
6
  export interface XmlSourceRead {
3
7
  readonly offset: number;
@@ -8,7 +8,7 @@
8
8
  },
9
9
  {
10
10
  "name": "toolcraft-schema",
11
- "version": "0.0.799",
11
+ "version": "0.0.801",
12
12
  "license": "MIT"
13
13
  }
14
14
  ]
@@ -23,6 +23,7 @@ export function spinner() {
23
23
  };
24
24
  return {
25
25
  start(message = "") {
26
+ clearTimer();
26
27
  currentMessage = stripAnsi(message);
27
28
  if (format === "json") {
28
29
  return;
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "toolcraft-schema",
3
- "version": "0.0.799",
3
+ "version": "0.0.801",
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.799",
3
+ "version": "0.0.801",
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.799",
180
+ "toolcraft-schema": "0.0.801",
181
181
  "toolcraft-design": "*",
182
182
  "@poe-code/frontmatter": "*",
183
183
  "@poe-code/agent-mcp-config": "*",