# API Reference
> Complete export reference for `xml-xsd-engine` v1.7.0.  
> For usage recipes see [GUIDE.md](./GUIDE.md). For error codes see [ERRORS.md](./ERRORS.md).

---

## Table of Contents

1. [Import Paths](#1-import-paths)
2. [XML Parsing](#2-xml-parsing)
3. [XML Serialization](#3-xml-serialization)
4. [DOM Node Types](#4-dom-node-types)
5. [SAX / Event Parser](#5-sax--event-parser)
6. [XPath Engine](#6-xpath-engine)
7. [XSD Parsing & Schema Model](#7-xsd-parsing--schema-model)
8. [Validation](#8-validation)
9. [Type Validator](#9-type-validator)
10. [Output Formatters](#10-output-formatters)
11. [Async File I/O](#11-async-file-io)
12. [Stream Parser](#12-stream-parser)
13. [XML ↔ JSON Transforms](#13-xml--json-transforms)
14. [XSLT Transformer](#14-xslt-transformer)
15. [Schema Cache](#15-schema-cache)
16. [Batch Validator](#16-batch-validator)
17. [Plugin Registry](#17-plugin-registry)
18. [Parse Budget](#18-parse-budget)
19. [Error Types](#19-error-types)
20. [Utility Functions](#20-utility-functions)
21. [Code Generation *(v1.3)*](#21-code-generation-v13)
22. [Async Schema Loader *(v1.3)*](#22-async-schema-loader-v13)
23. [CLI Utilities *(v1.3)*](#23-cli-utilities-v13)
24. [SAX Instrumentation *(v1.5)*](#24-sax-instrumentation-v15)
25. [Schema Preflight *(v1.5)*](#25-schema-preflight-v15)
26. [Identity Constraint Engine *(v1.5)*](#26-identity-constraint-engine-v15)
27. [`xs:assert` Assertion Evaluator *(v1.5 — G24)*](#27-xsassert-assertion-evaluator-v15--g24)
28. [SHA-256 Utilities *(v1.5 — G40)*](#28-sha-256-utilities-v15--g40)
29. [`xml:id` / `xml:base` *(v1.5 — G10)*](#29-xmlid--xmlbase-v15--g10)
30. [DTD Entity Expansion *(v1.5 — G11)*](#30-dtd-entity-expansion-v15--g11)
31. [Platform Entry Points *(v1.5 — G9/G25)*](#31-platform-entry-points-v15--g9g25)
32. [`xs:list` / `xs:union` Hardening *(v1.5 — G12)*](#32-xslist--xsunion-hardening-v15--g12)
33. [XPath 2.0 Functions *(v1.5 — G6)*](#33-xpath-20-functions-v15--g6)
34. [XML Diff *(v1.6 — G16)*](#34-xml-diff-v16--g16)
35. [Schema Inference *(v1.6 — G17)*](#35-schema-inference-v16--g17)
36. [Fragment & Subtree Validation *(v1.6 — G45)*](#36-fragment--subtree-validation-v16--g45)
37. [Encoding Declaration *(v1.6)*](#37-encoding-declaration-v16)
38. [Canonical XML *(v1.7)*](#38-canonical-xml-v17)
39. [PSVI *(v1.7)*](#39-psvi-v17)
40. [SchemaMerger *(v1.7)*](#40-schemamerger-v17)
41. [XPath Cache Control *(v1.7)*](#41-xpath-cache-control-v17)
42. [Streaming Validation *(v1.7)*](#42-streaming-validation-v18-foundation)
43. [DFA Engine *(v1.7)*](#43-dfa-engine-v18-foundation)
44. [Incremental Subtree Revalidation *(v1.7)*](#44-incremental-subtree-revalidation-v18-foundation)
45. [Streaming xs:keyref Tracker *(v1.7)*](#45-streaming-xskeyref-tracker-v18-foundation)
46. [SchemaCache Improvements *(v1.7 / v1.7)*](#46-schemacache-improvements-v17--v18)
47. [IValidator Interface *(v1.7)*](#47-ivalidator-interface-v17)
48. [flattenGroupParticles *(v1.7)*](#48-flattengroupparticles-v17)

---

## 1. Import Paths

```ts
// Main entry — all core exports
import { ... } from 'xml-xsd-engine';

// CommonJS
const { ... } = require('xml-xsd-engine');

// Sub-path exports
import { ... } from 'xml-xsd-engine/async';       // file I/O helpers
import { ... } from 'xml-xsd-engine/stream';      // streaming parser
import { ... } from 'xml-xsd-engine/formatters';  // output formatters
import { ... } from 'xml-xsd-engine/transform';   // XML↔JSON + XSLT
import { ... } from 'xml-xsd-engine/cache';       // schema cache
import { ... } from 'xml-xsd-engine/codegen';     // code generation

// v1.5 — platform entry points (G9/G25)
import { ... } from 'xml-xsd-engine/browser';     // zero Node.js deps (browser/WASM)
import { ... } from 'xml-xsd-engine/deno';        // Deno-native helpers
import { ... } from 'xml-xsd-engine/bun';         // Bun-native helpers
```

---

## 2. XML Parsing

### `parseXml(source, options?): XmlDocument`

Parse an XML string into a DOM tree.

```ts
import { parseXml } from 'xml-xsd-engine';

const doc = parseXml('<root><item id="1">hello</item></root>');
```

| Parameter | Type | Default | Description |
|---|---|---|---|
| `source` | `string` | — | XML source string |
| `options.maxDepth` | `number` | `500` | Max element nesting depth |
| `options.maxAttributes` | `number` | `256` | Max attributes per element |
| `options.maxTextLength` | `number` | `10_000_000` | Max text node char count |
| `options.maxNodeCount` | `number` | `1_000_000` | Max total DOM nodes |
| `options.entityMap` | `Record<string, string>` | `{}` | Custom entity expansions |
| `options.expandDtdEntities` | `boolean` | `true` | Auto-parse `<!ENTITY>` from internal DTD subset *(v1.5 G11)* |
| `options.preserveWhitespace` | `boolean` | `false` | Keep whitespace-only text nodes |
| `options.resolveNamespaces` | `boolean` | `true` | Resolve namespace URIs |
| `options.encoding` | `string` | `''` | Override encoding stored on `XmlDocument.encoding` *(v1.6 G19)* |

**v1.5 — `xml:id` / `xml:base` (G10):** `XmlElement.xmlId` and `XmlElement.xmlBase` are populated automatically. Duplicate `xml:id` values throw `PARSE_DUPLICATE_ATTRIBUTE`. `XmlDocument.xmlIds` is a `Map<string, XmlElement>` for O(1) lookup.

**v1.6 — `encoding` option (G19):** When set, this value is stored as `XmlDocument.encoding`, overriding the `encoding` attribute in the XML declaration. Useful when the caller has already decoded the input via `TextDecoder` but wants the original encoding preserved on the document object.

**Throws:** `XmlError` on malformed XML or security limit exceeded.

### `class XmlParser`

Lower-level class. Use `parseXml()` for most cases.

```ts
const parser = new XmlParser(source, options);
const doc = parser.parse();
```

---

## 3. XML Serialization

### `serializeXml(node, options?): string`

Serialize a DOM node back to an XML string.

```ts
import { serializeXml } from 'xml-xsd-engine';

const xml = serializeXml(doc, { indent: 2, xmlDeclaration: true });
```

| Option | Type | Default | Description |
|---|---|---|---|
| `indent` | `number` | `2` | Spaces per indent level |
| `xmlDeclaration` | `boolean` | `false` | Prepend `<?xml version="1.0"?>` |
| `selfClose` | `boolean` | `true` | Use `<tag/>` for empty elements |
| `encoding` | `string` | `'UTF-8'` | Encoding attribute in declaration |
| `sortAttributes` | `boolean` | `false` | Sort attributes alphabetically *(v1.4 G20)* |
| `preferredPrefixes` | `Record<string, string>` | `{}` | URI → preferred prefix map *(v1.4 G20)* |

### `class XmlSerializer`

```ts
const s = new XmlSerializer({ indent: 4 });
const xml = s.serialize(doc);
```

---

## 4. DOM Node Types

All node classes are exported from `xml-xsd-engine`.

### `XmlDocument`

| Property | Type | Description |
|---|---|---|
| `root` | `XmlElement \| null` | The single root element |
| `children` | `XmlNode[]` | All top-level nodes (incl. PI, DOCTYPE, comments) |
| `version` | `string` | XML version (`"1.0"`) |
| `encoding` | `string` | Declared encoding (or value set via `ParseOptions.encoding`) |
| `standalone` | `boolean \| null` | Standalone declaration |
| `xmlIds` | `Map<string, XmlElement>` | O(1) lookup by `xml:id` value *(v1.5 G10)* |

### `XmlElement`

| Property / Method | Type | Description |
|---|---|---|
| `tagName` | `string` | Full qualified name (`"ns:local"` or `"local"`) |
| `localName` | `string` | Local part only |
| `prefix` | `string` | Namespace prefix (`""` if none) |
| `namespaceURI` | `string` | Resolved namespace URI (`""` if none) |
| `attributes` | `XmlAttribute[]` | All attributes in document order |
| `children` | `XmlNode[]` | All child nodes |
| `childElements` | `XmlElement[]` | Child elements only (no text/comments) |
| `textContent` | `string` | All text/CDATA children concatenated |
| `parent` | `XmlElement \| XmlDocument \| null` | Parent node |
| `sourceLine` | `number \| undefined` | 1-based line of opening tag *(v1.6 G2)* |
| `sourceCol` | `number \| undefined` | 1-based column of opening tag *(v1.6 G2)* |
| `xmlId` | `string \| undefined` | Value of `xml:id` attribute, if present *(v1.5 G10)* |
| `xmlBase` | `string \| undefined` | Resolved effective base URI from nearest `xml:base` ancestor *(v1.5 G10)* |
| `getAttribute(name)` | `string \| null` | Get attribute value by name |
| `setAttribute(name, value)` | `void` | Set/add attribute |
| `hasAttribute(name)` | `boolean` | Check attribute existence |
| `removeAttribute(name)` | `void` | Remove attribute |
| `getChild(localName)` | `XmlElement \| null` | First child with matching localName |
| `getChildren(localName)` | `XmlElement[]` | All children with matching localName |
| `find(path)` | `XmlElement \| null` | Navigate by `/`-separated path |
| `findAll(path)` | `XmlElement[]` | All matches for path |
| `walk(visitor)` | `void` | Depth-first visitor |

### Other node types

| Class | `nodeType` | Key property |
|---|---|---|
| `XmlText` | `'text'` | `value: string` |
| `XmlCData` | `'cdata'` | `value: string` |
| `XmlComment` | `'comment'` | `value: string` |
| `XmlProcessingInstruction` | `'processingInstruction'` | `target`, `data` |
| `XmlDoctype` | `'doctype'` | `content: string` |
| `XmlAttribute` | `'attribute'` | `name`, `localName`, `prefix`, `namespaceURI`, `value` |

### `XmlNodeType` enum

```ts
type XmlNodeType =
  | 'document' | 'element' | 'text' | 'cdata'
  | 'comment'  | 'processingInstruction' | 'doctype' | 'attribute';
```

---

## 5. SAX / Event Parser

### `parseSax(source, handler, options?): void`

Parse XML and fire callbacks without building a DOM.

```ts
import { parseSax } from 'xml-xsd-engine';

parseSax(xmlString, {
  startElement: (e) => console.log('open', e.event.localName),
  endElement:   (e) => console.log('close', e.event.localName),
  text:         (e) => console.log('text', e.value),
  error:        (e) => console.error(e.error),
});
```

### `class SaxParser`

```ts
const parser = new SaxParser(xmlString, options);

// Push API
parser
  .on('startElement', e => { /* e.event: StartElementEvent */ })
  .on('endElement',   e => { /* e.event: EndElementEvent */ })
  .on('text',         e => { /* e.value */ })
  .parse();

// Pull API
for (const event of parser.events()) {
  if (event.type === 'startElement') { /* event.event: StartElementEvent */ }
}
```

### SAX event types

| Event type | Payload fields |
|---|---|
| `startDocument` | — |
| `endDocument` | — |
| `startElement` | `event: StartElementEvent` (`localName`, `prefix`, `qName`, `namespaceURI`, `attributes`, `nsDeclarations`, `line`, `col`) |
| `endElement` | `event: EndElementEvent` (`localName`, `prefix`, `qName`, `namespaceURI`, `line`, `col`) |
| `text` | `value`, `line`, `col` |
| `cdata` | `value`, `line`, `col` |
| `comment` | `value`, `line`, `col` |
| `processingInstruction` | `target`, `data`, `line`, `col` |
| `xmlDeclaration` | `event: XmlDeclarationEvent` (`version`, `encoding`, `standalone`) |
| `doctype` | `content`, `line`, `col` |
| `error` | `error: XmlError` |

---

## 6. XPath Engine

### `xpath(context, expression): XmlElement[]`

Evaluate an XPath expression and return matched elements.

```ts
import { xpath } from 'xml-xsd-engine';

const books  = xpath(doc, '//book');
const byLang = xpath(doc, 'catalog/book[@lang="en"]');
const first  = xpath.first(doc, '//title');
const count  = xpath.count(doc, '//item');
const text   = xpath.string(doc, '//title');
```

| Helper | Returns | Description |
|---|---|---|
| `xpath(ctx, expr)` | `XmlElement[]` | All matches |
| `xpath.first(ctx, expr)` | `XmlElement \| null` | First match or null |
| `xpath.count(ctx, expr)` | `number` | Count of matches |
| `xpath.string(ctx, expr)` | `string` | `textContent` of first match |

### `compileXPath(expression): CompiledXPath` *(v1.4 G22)*

Pre-parse an XPath expression for reuse across multiple documents.

```ts
import { compileXPath } from 'xml-xsd-engine';

const expr = compileXPath('//book[@lang="en"]');
const found = expr.evaluate(doc.root!);
const first = expr.first(doc.root!);
const n     = expr.count(doc.root!);
const str   = expr.string(doc.root!);
```

Internal LRU cache (512 entries). `xpath()` uses it automatically.

### Supported syntax

| Syntax | Description |
|---|---|
| `child` | Child element by name |
| `*` | Any child element |
| `//step` | Descendant-or-self |
| `.` | Self |
| `..` | Parent |
| `@attr` | Attribute axis |
| `[n]` | Positional predicate (1-based) |
| `[last()]` | Last element |
| `[@attr]` | Has attribute |
| `[@attr="val"]` | Attribute value equals |
| `[contains(@attr,"v")]` | Attribute contains string |
| `[starts-with(@attr,"v")]` | Attribute starts-with |
| `[not(...)]` | Negation |
| `[count(...)=n]` | Child count |
| `[normalize-space(.)]` | Normalized text predicate *(v1.4)* |
| `[string-length(.)]` | String length predicate *(v1.4)* |

---

## 7. XSD Parsing & Schema Model

### `parseXsd(source, loader?): SchemaModel`

Parse and compile an XSD schema string.

```ts
import { parseXsd } from 'xml-xsd-engine';

const schema = parseXsd(xsdString);

// With xs:import / xs:include loader
const schema = parseXsd(xsdString, (location) => {
  return fs.readFileSync(location, 'utf8');
});
```

**Throws:** `XmlError` with `SCHEMA_*` code on invalid schema.

### `parseXsdAsync(source, loader?): Promise<SchemaModel>` *(v1.3)*

Async variant accepting `AsyncSchemaLoader`. See [§22](#22-async-schema-loader-v13).

### `class SchemaModel`

The compiled schema representation returned by `parseXsd()`.

| Method | Returns | Description |
|---|---|---|
| `resolveElement(name)` | `ElementDeclaration \| null` | Look up a global element |
| `resolveType(name)` | `ComplexTypeDefinition \| SimpleTypeDefinition \| null` | Look up a named type |
| `resolveComplexType(name)` | `ComplexTypeDefinition \| null` | Complex types only |
| `resolveSimpleType(name)` | `SimpleTypeDefinition \| null` | Simple types only |
| `resolveSubstitutionGroup(headName)` | `ElementDeclaration[]` | All substitutable elements |
| `detectCircularTypes()` | `Array<{typeName, chain}>` | Circular inheritance chains |
| `buildSubstitutionGroups()` | `void` | Build substitution index (called by XsdParser) |
| `merge(other)` | `void` | Merge another schema (for xs:import/xs:include) |
| `toJSON()` | `Record<string, unknown>` | Plain JSON-serialisable representation *(v1.3)* |

---

## 8. Validation

### `validate(doc, schema, options?): ValidationResult`

Validate a parsed DOM against a compiled schema.

```ts
import { validate } from 'xml-xsd-engine';

const result = validate(doc, schema);
console.log(result.valid);   // boolean
console.log(result.errors);  // ValidationIssue[]
```

### `validateFragment(xmlFragment, schema, options?): ValidationResult` *(v1.6 — G45)*

Parse a raw XML fragment string and validate it. Parse errors are reported as issues rather than thrown.

```ts
import { validateFragment } from 'xml-xsd-engine';

const result = validateFragment('<person><name>Alice</name></person>', schema);
```

### `validateSubtree(elem, schema, options?): ValidationResult` *(v1.6)*

Validate an `XmlElement` subtree directly — useful when extracted from a larger document.

### `revalidateSubtree(elem, schema, options?): ValidationResult` *(v1.7 — G38)*

Incremental re-validation of a single subtree. Designed for editor "lint on edit" — only the changed
element is re-checked. Identity constraints within the subtree are evaluated; cross-subtree keyref
references require a full `validate()` call.

```ts
import { revalidateSubtree } from 'xml-xsd-engine';
const result = revalidateSubtree(changedElem, schema, { recover: true });
```

```ts
import { validateSubtree } from 'xml-xsd-engine';

const result = validateSubtree(doc.root!.childElements[0], schema);
```

### `ValidationOptions`

| Option | Type | Default | Description |
|---|---|---|---|
| `maxErrors` | `number` | `100` | Stop collecting errors after this many |
| `allowUnknownElements` | `boolean` | `false` | *(pre-v1.4)* Ignore undeclared elements |
| `mode` | `'strict' \| 'lax'` | `'strict'` | *(v1.4 G41)* Unknown elements/attrs: error vs. warning |
| `recover` | `boolean` | `false` | *(v1.4 G43)* Continue past structural errors |
| `profile` | `boolean` | `false` | *(v1.4 G44)* Enable per-element timing |
| `onProfile` | `(e: ProfileEvent) => void` | — | *(v1.4 G44)* Profile callback |
| `rootType` | `string` | — | *(v1.6 G45)* Validate as this named type instead of inferring from root element name |
| `psvi` | `boolean` | `false` | *(v1.7)* Collect Post-Schema-Validation Infoset; populates `result.psvi` |

### `class ValidationEngine`

```ts
const engine = new ValidationEngine(schema, options);
const result = engine.validate(doc);
const result = engine.validateSubtree(elem, '/custom/path');  // v1.6 G45
```

| Property | Type | Description |
|---|---|---|
| `lastProfileEvents` | `ProfileEvent[]` | Per-element timing — populated after `validate()` with `profile: true` |

### `class ValidationResult`

| Property / Method | Type | Description |
|---|---|---|
| `valid` | `boolean` | `true` if no errors |
| `issues` | `ValidationIssue[]` | All errors + warnings + infos |
| `errors` | `ValidationIssue[]` | Error-severity issues only |
| `warnings` | `ValidationIssue[]` | Warning-severity issues only |
| `infos` | `ValidationIssue[]` | Info-severity issues only *(v1.4)* |
| `byCategory(cat)` | `ValidationIssue[]` | Filter by `ValidationErrorCategory` *(v1.4 G37)* |
| `summary` | `ValidationSummary` | `{ errorCount, warningCount, infoCount, byCategory }` *(v1.4)* |
| `addError(msg, path, line?, col?, opts?)` | `void` | Add an error issue |
| `addWarning(msg, path, opts?)` | `void` | Add a warning issue |
| `addInfo(msg, path)` | `void` | Add an info issue *(v1.4)* |
| `merge(other)` | `void` | Merge another result |

### `interface ValidationIssue`

```ts
interface ValidationIssue {
  severity: 'error' | 'warning' | 'info';
  message:  string;
  path:     string;       // XPath-like: '/root/catalog/book[2]/price'
  line?:    number;       // 1-based; populated for all error kinds in v1.6 (G2)
  col?:     number;       // 1-based
  offset?:  number;       // G2: 0-based char offset
  endLine?: number;       // G2: range end
  endCol?:  number;       // G2: range end column
  category?: ValidationErrorCategory;  // G37 stable category
  code?:     XmlErrorCode;             // G37 stable error code
  stage?:    string;                   // pipeline stage
}
```

> **v1.6 (G2):** `line` and `col` are now consistently populated for **all** error kinds in `ValidationEngine` — abstract element, xsi:nil, wrong type, missing/bad/unknown attributes, unexpected/unknown elements, `xs:assert` failures.

---

## 9. Type Validator

### `class TypeValidator`

Validates string values against XSD simple types.

```ts
import { TypeValidator } from 'xml-xsd-engine';

const tv = new TypeValidator(schema);
const result = tv.validate('hello', 'xs:string');
// result.valid: boolean, result.errors: string[]
```

Supports all **56** XSD built-in types (including `xs:normalizedString`, `xs:token`, `xs:Name`, `xs:ID`/`xs:IDREF`/`xs:ENTITY`, `xs:NMTOKENS`/`xs:IDREFS`/`xs:ENTITIES`, `xs:anySimpleType`) and custom `simpleType` definitions with all facets.

`TypeValidator.validateDef(value, simpleDef)` *(v1.7)* — validates against an inline `SimpleTypeDefinition` directly, without schema map lookup.

---

## 10. Output Formatters

### Pre-built formatters

```ts
import {
  textFormatter, compactFormatter, jsonFormatter,
  githubActionsFormatter, junitFormatter,
} from 'xml-xsd-engine';
// or: from 'xml-xsd-engine/formatters'

const output = textFormatter(result, { color: true, showSummary: true });
```

| Formatter | Output | Use case |
|---|---|---|
| `textFormatter` | Human-readable ANSI text | Terminal / developer output |
| `compactFormatter` | `file:line:col: ERROR msg` | One line per issue |
| `jsonFormatter` | JSON object | Machine consumers, APIs |
| `githubActionsFormatter` | `::error file=...` | GitHub Actions annotations |
| `junitFormatter` | JUnit XML `<testsuite>` | CI systems (Jenkins, GitLab) |

### `createFormatter(template): Formatter`

Build a custom formatter using callbacks:

```ts
import { createFormatter } from 'xml-xsd-engine';

const myFormatter = createFormatter({
  header: (file, result) => `=== ${file} ===\n`,
  issue:  (issue)        => `  [${issue.severity}] ${issue.path}: ${issue.message}\n`,
  footer: (file, result) => result.valid ? '  OK\n' : `  ${result.errors.length} error(s)\n`,
});
```

---

## 11. Async File I/O

```ts
import { readXmlFile, readXsdFile, validateFiles, validateFilesWithLoader }
  from 'xml-xsd-engine';
// or: from 'xml-xsd-engine/async'
```

| Function | Signature | Description |
|---|---|---|
| `readXmlFile` | `(path, options?) => Promise<XmlDocument>` | Read + parse XML file |
| `readXsdFile` | `(path, loader?) => Promise<SchemaModel>` | Read + compile XSD file |
| `validateFiles` | `(xmlPath, xsdPath, options?) => Promise<ValidationResult>` | Read both + validate |
| `validateFilesWithLoader` | `(xmlPath, xsdPath, loader, options?) => Promise<ValidationResult>` | With custom schema loader |

---

## 12. Stream Parser

```ts
import { XmlStreamParser, parseXmlStream, parseXmlFileStream }
  from 'xml-xsd-engine';
// or: from 'xml-xsd-engine/stream'
```

### `parseXmlFileStream(filePath, options?): Promise<XmlDocument>`

Parse a large XML file via streaming without loading it all into memory first.

```ts
const doc = await parseXmlFileStream('./large.xml');
```

### `parseXmlStream(readable, options?): Promise<XmlDocument>`

Parse from any `Readable` stream.

```ts
import { createReadStream } from 'fs';
const doc = await parseXmlStream(createReadStream('./large.xml'));
```

### `class XmlStreamParser`

Node.js `Transform` stream — writable XML in, `XmlDocument` event out.

```ts
import { createReadStream } from 'fs';
const parser = new XmlStreamParser();
parser.on('document', (doc: XmlDocument) => { ... });
parser.on('error',    (err: XmlError)    => { ... });
createReadStream('./large.xml').pipe(parser);
```

---

## 13. XML ↔ JSON Transforms

```ts
import { xmlToJson, jsonToXml, jsonToXmlString }
  from 'xml-xsd-engine';
// or: from 'xml-xsd-engine/transform'
```

### `xmlToJson(node, options?): unknown`

| Option | Type | Default | Description |
|---|---|---|---|
| `attributePrefix` | `string` | `'@'` | Prefix for attribute keys |
| `textKey` | `string` | `'_text'` | Key for text content |
| `collapseText` | `boolean` | `true` | Single text child → plain string |
| `coerceNumbers` | `boolean` | `false` | `"42"` → `42` |
| `coerceBooleans` | `boolean` | `false` | `"true"` → `true` |

### `jsonToXml(obj, options?): XmlDocument`

| Option | Type | Default | Description |
|---|---|---|---|
| `attributePrefix` | `string` | `'@'` | Which key prefix becomes attribute |
| `textKey` | `string` | `'_text'` | Key whose value becomes text node |
| `rootTag` | `string` | first key | Override root element tag name |

### `jsonToXmlString(obj, options?): string`

Shortcut: `serializeXml(jsonToXml(obj, options))`.

---

## 14. XSLT Transformer

```ts
import { transformXml, transformDocument, XsltTransformer }
  from 'xml-xsd-engine';
// or: from 'xml-xsd-engine/transform'
```

### `transformXml(sourceXml, stylesheetXml, options?): string`

One-shot transform: parse source + stylesheet, apply, return serialized string.

### `transformDocument(doc, stylesheetXml, options?): XmlDocument`

Transform a pre-parsed `XmlDocument`, return result as `XmlDocument`.

### `class XsltTransformer`

```ts
const t = new XsltTransformer(stylesheetXml, options);
const result: string      = t.transform(sourceXml);
const resultDoc: XmlDocument = t.transformDocument(doc);
```

| Option | Type | Default | Description |
|---|---|---|---|
| `indent` | `number` | `2` | Output indentation |
| `xmlDeclaration` | `boolean` | `false` | Include XML declaration |

---

## 15. Schema Cache

```ts
import { SchemaCache, globalSchemaCache }
  from 'xml-xsd-engine';
// or: from 'xml-xsd-engine/cache'
```

### `new SchemaCache(options?)`

| Option | Type | Default | Description |
|---|---|---|---|
| `maxSize` | `number` | `100` | LRU eviction after this many entries |
| `ttlMs` | `number` | `Infinity` | TTL in milliseconds |
| `trackContentHash` | `boolean` | `true` | Key entries as `key@sha256short(source)` *(v1.5 G40)* |

### Methods

| Method | Returns | Description |
|---|---|---|
| `getOrParse(key, xsdSource)` | `SchemaModel` | Get cached or parse from string |
| `getOrLoad(filePath)` | `Promise<SchemaModel>` | Get cached or read + parse file |
| `getOrLoadSync(filePath)` | `SchemaModel` | Synchronous file load with caching |
| `set(key, schema)` | `void` | Store a pre-compiled schema |
| `get(key)` | `SchemaModel \| null` | Retrieve without parsing |
| `has(key)` | `boolean` | Check existence |
| `invalidate(key)` | `boolean` | Remove one entry |
| `invalidateByContent(path, newSource)` | `void` | Remove stale entries whose hash differs *(v1.5)* |
| `getContentHash(key)` | `string \| null` | Return stored SHA-256 content hash *(v1.5)* |
| `getDependencies(key)` | `Map<string, string>` | `importPath → hash` dependency map *(v1.5)* |
| `clear()` | `void` | Remove all entries |
| `validateFile(xmlPath, xsdPath)` | `Promise<ValidationResult>` | Validate with caching |
| `validateFiles(xmlPaths, xsdPath)` | `Promise<ValidationResult[]>` | Batch validate with caching |
| `size` | `number` | Current entry count |
| `keys()` | `string[]` | All cache keys (hash suffix stripped) |

### `globalSchemaCache`

Pre-created `SchemaCache` instance with default settings, shared across the process.

---

## 16. Batch Validator

```ts
import { BatchValidator, batchValidateFiles, batchValidateStrings }
  from 'xml-xsd-engine';
```

### `batchValidateFiles(xmlPaths, xsdPath, options?): Promise<BatchValidationReport>`

### `batchValidateStrings(entries, xsdSource, options?): Promise<BatchValidationReport>`

```ts
const report = await batchValidateFiles(['a.xml', 'b.xml'], 'schema.xsd', {
  concurrency: 8,
  failFast:    false,
  onProgress:  (key, result) => console.log(key, result.valid),
});
```

### `class BatchValidator`

Accepts schema as: file path `string` | raw XSD `string` | pre-compiled `SchemaModel`.

| Method | Returns | Description |
|---|---|---|
| `validateFiles(paths[], opts?)` | `Promise<BatchValidationReport>` | Async file batch |
| `validateStrings(entries[], opts?)` | `Promise<BatchValidationReport>` | Async string batch |
| `validateString(xml)` | `ValidationResult` | Single synchronous validation |

### `interface BatchValidationReport`

```ts
interface BatchValidationReport {
  total:     number;
  passed:    number;
  failed:    number;
  allValid:  boolean;
  durationMs: number;
  results:   Map<string, ValidationResult>;
  summary:   string;    // "2 passed, 1 failed in 45ms"
}
```

---

## 17. Plugin Registry

```ts
import { PluginRegistry, defaultRegistry } from 'xml-xsd-engine';
```

### Extension points

```ts
const registry = new PluginRegistry();

// Custom simple-type validator
registry.registerTypeValidator('myType', (value, facets) => {
  return { valid: value.startsWith('X'), errors: [] };
});

// Custom entity resolver
registry.registerEntityResolver((name) => {
  if (name === 'company') return 'ACME Corp';
  return null;
});

// Post-validation hook
registry.registerSchemaValidatorHook((result, doc, schema) => {
  // add custom issues to result
});

// Pre-serialization transform
registry.registerSerializerTransform((node) => {
  // mutate or replace nodes before serialization
  return node;
});

// Merge multiple registries
const merged = PluginRegistry.merge(registry1, registry2);
```

### `defaultRegistry`

Pre-created global `PluginRegistry` instance.

---

## 18. Parse Budget

```ts
import { ParseBudget } from 'xml-xsd-engine';
```

Apply resource limits before parsing untrusted input.

```ts
const budget = new ParseBudget({
  wallTimeMs: 5_000,       // max 5 seconds
  bytes:      1_048_576,   // max 1 MB input
  nodes:      50_000,      // max DOM nodes
  depth:      100,         // max nesting depth
  maxAttributes: 32,       // max attributes per element
});

// Throws XmlError if any limit exceeded
const doc = budget.parse(xmlString);

// Non-throwing — returns cost report
const report = budget.report(xmlString);
// report.withinBudget: boolean
// report.wallTimeMs: number
// report.bytes: number
// report.nodes: number
```

---

## 19. Error Types

```ts
import { XmlError, XmlErrorCode, ERROR_CATEGORY, xmlError, parseError, secError }
  from 'xml-xsd-engine';
```

### `class XmlError extends Error`

| Property | Type | Description |
|---|---|---|
| `code` | `XmlErrorCode` | Machine-readable error code |
| `location` | `SourceLocation \| null` | Source position (`line`, `col`, `path?`, `source?`, `offset?`) |
| `category` | `ValidationErrorCategory` | Stable high-level category *(v1.4 G37)* |
| `stage` | `string \| undefined` | Pipeline stage that produced this error *(v1.4)* |
| `isSecurity` | `boolean` | `true` for `SEC_*` codes |
| `isWellFormedness` | `boolean` | `true` for `LEX_*`, `PARSE_*`, `NS_*` |
| `isValidation` | `boolean` | `true` for `VALID_*`, `SCHEMA_*` |
| `isIdentityConstraint` | `boolean` | `true` for identity constraint violations *(v1.4)* |

### `ERROR_CATEGORY`

Maps every `XmlErrorCode` to its `ValidationErrorCategory`. Stable across minor/patch versions.

### Factory helpers

```ts
xmlError(code, message, location?, stage?): XmlError
parseError(code, message, location?): XmlError  // LEX_* / PARSE_*
secError(code, message, location?): XmlError    // SEC_*
nsError(code, message, location?): XmlError     // NS_*
validError(code, message, location?): XmlError  // VALID_*
schemaError(code, message, location?): XmlError // SCHEMA_*
```

See [ERRORS.md](./ERRORS.md) for the full list of error codes.

---

## 20. Utility Functions

```ts
import {
  decodeEntities, encodeEntities, encodeAttrValue,
  normalizeAttrValue, isValidNCName, isValidQName, splitQName,
  resolveXmlBase, StringReader,
} from 'xml-xsd-engine';
```

| Function | Signature | Description |
|---|---|---|
| `decodeEntities` | `(s: string) => string` | Decode `&amp;` `&lt;` etc. |
| `encodeEntities` | `(s: string) => string` | Encode `<>&` for text content |
| `encodeAttrValue` | `(s: string, quote: '"'\|"'") => string` | Encode for attribute value |
| `normalizeAttrValue` | `(s: string) => string` | CDATA normalization (§3.3.3) |
| `isValidNCName` | `(s: string) => boolean` | Check XML NCName validity |
| `isValidQName` | `(s: string) => boolean` | Check XML QName validity |
| `splitQName` | `(s: string) => {prefix, local}` | Split `"ns:local"` → `{prefix:"ns", local:"local"}` |
| `resolveXmlBase` | `(base: string, ref: string) => string` | RFC 3986 §5.3 relative URI resolution *(v1.5 G10)* |
| `sha256Hex` | `(input: string) => string` | Full 64-char hex SHA-256 *(v1.5 G40)* |
| `sha256Short` | `(input: string, len?: number) => string` | Short hex SHA-256 *(v1.5 G40)* |

---

## 21. Code Generation *(v1.3)*

**Import path:** `xml-xsd-engine/codegen`  
**Also re-exported from:** `xml-xsd-engine`

### `generateTypeScript(schema, options?)`

```ts
import { generateTypeScript } from 'xml-xsd-engine/codegen';
function generateTypeScript(schema: SchemaModel, options?: XsdToTypeScriptOptions): string;
```

Generates TypeScript `interface` / `type` declarations from a compiled `SchemaModel`.

#### `XsdToTypeScriptOptions`

| Option | Type | Default | Description |
|---|---|---|---|
| `header` | `string` | auto notice | Comment placed at top of file |
| `exportAll` | `boolean` | `true` | Add `export` modifier to all declarations |
| `typePrefix` | `string` | `''` | Prefix added to every generated type name |
| `typeSuffix` | `string` | `''` | Suffix added to every generated type name |
| `useTypeAlias` | `boolean` | `false` | `false` = `interface`, `true` = `type =` |
| `jsdoc` | `boolean` | `true` | Emit JSDoc comments above each declaration |
| `indent` | `number` | `2` | Spaces per indent level |
| `skipBuiltins` | `boolean` | `true` | Skip built-in XSD simple types |
| `typeMap` | `Record<string, string>` | `{}` | Custom XSD-type → TypeScript-type overrides |

#### XSD → TypeScript type mapping

| XSD type | TypeScript |
|---|---|
| `xs:string` / `xs:token` / `xs:anyURI` … | `string` |
| `xs:boolean` | `boolean` |
| `xs:decimal` / `xs:integer` / `xs:int` … | `number` |
| `xs:anyType` | `unknown` |
| enumeration facets | `'val1' \| 'val2'` |
| `xs:list` | `T[]` |
| `xs:union` | `T1 \| T2` |
| `maxOccurs="unbounded"` | `T[]` |
| `minOccurs="0"` | `field?: T` |

---

### `generateJsonSchema(schema, options?)`

```ts
import { generateJsonSchema } from 'xml-xsd-engine/codegen';
function generateJsonSchema(schema: SchemaModel, options?: XsdToJsonSchemaOptions): JsonSchemaDocument;
```

Converts a `SchemaModel` to a JSON Schema **Draft 7** document.

#### `XsdToJsonSchemaOptions`

| Option | Type | Default | Description |
|---|---|---|---|
| `draft` | `string` | Draft 7 URI | JSON Schema `$schema` value |
| `id` | `string` | — | `$id` added to root schema |
| `title` | `string` | — | `title` added to root schema |
| `useDefinitions` | `boolean` | `true` | Emit `definitions` block; named types use `$ref` |
| `allowAdditional` | `boolean` | `true` | `additionalProperties: true` for `xs:any` |
| `typeMap` | `Record<string, JsonSchemaObject>` | `{}` | Custom overrides |

---

### `SchemaModel.toJSON()` *(v1.3)*

```ts
schema.toJSON(): Record<string, unknown>
```

Serializes the compiled schema to a plain JSON-serialisable object. Safe to pass to `JSON.stringify()`.

---

## 22. Async Schema Loader *(v1.3)*

### `parseXsdAsync(source, loader?)`

```ts
async function parseXsdAsync(
  xsdSource: string,
  loader?: AsyncSchemaLoader | SchemaLoader,
): Promise<SchemaModel>
```

### `AsyncSchemaLoader` type

```ts
type AsyncSchemaLoader = (location: string, namespace: string) => Promise<string | null>;
```

### Example

```ts
import { parseXsdAsync, AsyncSchemaLoader } from 'xml-xsd-engine';

const loader: AsyncSchemaLoader = async (location) => {
  const res = await fetch(`https://schemas.example.com/${location}`);
  return res.ok ? res.text() : null;
};

const schema = await parseXsdAsync(mainXsdSource, loader);
```

---

## 23. CLI Utilities *(v1.3)*

### `buildCodeFrame(source, line, col, contextLines?)`

```ts
function buildCodeFrame(source: string, line: number, col: number, contextLines?: number): string
```

Builds a source code frame string for display in terminals or log output:

```
  > 3 |     <price>abc</price>
            ^
```

Returns empty string if `source` is empty or `line < 1`.

---

### `appendCodeFrames(output, result, xmlSource, useColor)`

```ts
function appendCodeFrames(output: string, result: ValidationResult, xmlSource: string, useColor: boolean): string
```

Appends code frames for all `ValidationIssue` entries that carry `line` information to an already-formatted output string. Returns the original `output` unchanged if no errors have line info.

---

### CLI `--watch` flag

Re-validates XML and XSD files automatically whenever a watched file is saved.

```bash
xml-validate document.xml schema.xsd --watch
# xml-validate: Watching 2 file(s). Press Ctrl-C to stop.
# [10:01:34] ✔ Valid
# [10:01:58] CHANGED document.xml — re-validating...
# [10:01:58] ✖ INVALID
```

- Debounced 150 ms to avoid rapid re-triggers
- Reloads XSD when the schema file itself changes
- Not supported with `--stdin`

---

### CLI `--code-frame` flag

Appends a source excerpt with a `^` pointer beneath each validation error that carries line/col information.

```bash
xml-validate document.xml schema.xsd --code-frame
```

Only active with `--format text`. Respects `--color` / `--no-color`.

---

## 24. SAX Instrumentation *(v1.5 — G1-prep)*

**Source:** `src/parser/SaxInstrumentation.ts`  
**Exports:** `SaxEventConsumer`, `SaxValidationHint`, `NamespaceFrame`, `instrumentSax`, `createInstrumentedSax`, `collectSaxHints`

### `interface SaxValidationHint`

| Field | Type | Description |
|---|---|---|
| `kind` | `'open' \| 'close'` | `'open'` = startElement, `'close'` = endElement |
| `qualifiedName` | `string` | Qualified tag name as it appeared in source |
| `localName` | `string` | Local part (after prefix colon) |
| `namespaceURI` | `string` | Resolved namespace URI (`''` if none) |
| `depth` | `number` | Nesting depth (root element = 1) |
| `namespaceStack` | `NamespaceFrame[]` | All namespace bindings in scope |
| `attributes` | `Record<string, string> \| undefined` | Attribute map (only on `'open'` hints) |
| `line` | `number` | 1-based line number |
| `col` | `number` | 1-based column number |
| `path` | `string` | XPath-like path e.g. `/root/book[2]/title` |

### `interface SaxEventConsumer`

```ts
interface SaxEventConsumer {
  onHint(hint: SaxValidationHint): void;
  onEnd?(hadError: boolean): void;
  onError?(err: Error): void;
}
```

### Functions

| Function | Description |
|---|---|
| `collectSaxHints(xml)` | Collect all hints from a document into an array |
| `createInstrumentedSax(xml, consumer)` | Create a new `SaxParser` wired to `consumer` |
| `instrumentSax(parser, consumer)` | Attach instrumentation to an existing `SaxParser` (returns same instance) |

---

## 25. Schema Preflight *(v1.5 — G42)*

**Source:** `src/validator/SchemaPreflight.ts`  
**Exports:** `validateSchema`, `checkSchema`, `SchemaIssue`, `SchemaSeverity`

### `validateSchema(xsdSource): SchemaIssue[]`

Parse and run all preflight checks on an XSD source string.

```ts
import { validateSchema } from 'xml-xsd-engine';

const issues = validateSchema(xsdSource);
const errors = issues.filter(i => i.severity === 'error');
if (errors.length > 0) {
  console.error('Schema has errors:', errors.map(e => e.message));
}
```

**Throws:** `XmlError` only if the XSD source is not valid XML.

### `checkSchema(model): SchemaIssue[]`

Run preflight checks on an already-compiled `SchemaModel`. Useful when the schema was loaded via `parseXsdAsync` or from cache.

```ts
import { checkSchema } from 'xml-xsd-engine';

const issues = checkSchema(model);
```

### `interface SchemaIssue`

```ts
interface SchemaIssue {
  severity:   'error' | 'warning' | 'info';
  code:       XmlErrorCode;
  message:    string;
  component?: string;   // element name, type name, constraint name…
}
```

### Checks performed

| # | Check | Error code | Severity |
|---|---|---|---|
| 1 | Element `typeName` not in schema | `SCHEMA_UNDEFINED_REF` | error |
| 2 | Complex type base undefined | `SCHEMA_UNDEFINED_REF` | error |
| 3 | Simple type base undefined | `SCHEMA_UNDEFINED_REF` | error |
| 4 | Union member type undefined | `SCHEMA_UNDEFINED_REF` | warning |
| 5 | List item type undefined | `SCHEMA_UNDEFINED_REF` | warning |
| 6 | Circular `xs:extension` chain | `SCHEMA_CIRCULAR_TYPE` | error |
| 7 | Derivation from a `final` type | `SCHEMA_CIRCULAR_TYPE` | error |
| 8 | Same element declared with different types | `SCHEMA_CONFLICTING_DECL` | error |
| 9 | `xs:keyref refer=` target not found | `SCHEMA_KEYREF_UNRESOLVED` | error |
| 10 | Element particle `ref=` not found | `SCHEMA_UNDEFINED_REF` | warning |

---

## 26. Identity Constraint Engine *(v1.5 — G28b)*

**Source:** `src/validator/IdentityConstraintEngine.ts`  
**Exports:** `IdentityConstraintEngine`

```ts
import { IdentityConstraintEngine, ValidationResult } from 'xml-xsd-engine';

const engine = new IdentityConstraintEngine(schema);
const result = new ValidationResult();
engine.evaluate(doc, result);
```

**Violation code:** `VALID_IDENTITY_CONSTRAINT` · **Category:** `'identity'`

> `xs:keyref` evaluation is deferred to v2.1.

---

## 27. `xs:assert` Assertion Evaluator *(v1.5 — G24)*

```ts
import { AssertionEvaluator } from 'xml-xsd-engine';

const ev = new AssertionEvaluator(element);
ev.evaluate('@min &lt;= @max');           // true / false
ev.evaluate("starts-with(@code, 'A')");  // true / false
```

**Violation code:** `VALID_ASSERTION_FAILED` · **Category:** `'type'`

---

## 28. SHA-256 Utilities *(v1.5 — G40)*

```ts
import { sha256Hex, sha256Short } from 'xml-xsd-engine';
sha256Hex('hello');       // 64-char hex
sha256Short('hello');     // first 16 hex chars (default)
sha256Short('hello', 8);  // first 8 hex chars
```

---

## 29. `xml:id` / `xml:base` *(v1.5 — G10)*

```ts
const doc = parseXml('<root xml:base="http://ex.com/"><a xml:id="id1"/></root>');
doc.xmlIds.get('id1');              // → the <a> element
doc.root!.childElements[0].xmlId;  // 'id1'
doc.root!.xmlBase;                 // 'http://ex.com/'

resolveXmlBase('http://ex.com/docs/', '../img/logo.png');
// → 'http://ex.com/img/logo.png'
```

---

## 30. DTD Entity Expansion *(v1.5 — G11)*

```ts
const xml = `<?xml version="1.0"?>
<!DOCTYPE root [<!ENTITY greeting "Hello!">]>
<root>&greeting;</root>`;

parseXml(xml).root!.textContent;                // 'Hello!'
parseXml(xml, { expandDtdEntities: false });    // disables auto-expansion
parseXml(xml, { entityMap: { greeting: 'Hi' } }); // caller map takes precedence
```

External `SYSTEM`/`PUBLIC` entities are silently blocked (XXE prevention).

---

## 31. Platform Entry Points *(v1.5 — G9/G25)*

| Entry point | Use case |
|---|---|
| `xml-xsd-engine/browser` | Browser / WASM — zero Node.js deps |
| `xml-xsd-engine/deno` | Deno — adds `denoReadXmlFile`, `denoReadXsdFile`, `denoSchemaLoader` |
| `xml-xsd-engine/bun` | Bun — adds `bunReadXmlFile`, `bunReadXsdFile`, `bunSchemaLoader` |

`"sideEffects": false` in `package.json` enables full tree-shaking.

---

## 32. `xs:list` / `xs:union` Hardening *(v1.5 — G12)*

- Empty string is a valid zero-item `xs:list`
- `length`/`minLength`/`maxLength` facets on lists apply to item count (not string length)
- `SimpleTypeDefinition.itemTypeDef` — inline anonymous item type for `xs:list`
- `SimpleTypeDefinition.memberTypeDefs` — inline anonymous member types for `xs:union`

---

## 33. XPath 2.0 Functions *(v1.5 — G6)*

**Source:** `src/parser/XPath2Functions.ts` — all 22 functions exported from `xml-xsd-engine`.

| String functions | Sequence functions |
|---|---|
| `upperCase`, `lowerCase` | `distinctValues`, `isEmpty`, `exists` |
| `matchesXPath`, `replaceXPath`, `tokenize` | `stringJoin`, `sum`, `avg`, `minValue`, `maxValue` |
| `normalizeUnicode`, `encodeForUri`, `iriToUri` | `insertBefore`, `remove`, `subsequence`, `reverseSeq`, `indexOf` |
| `substringBefore`, `substringAfter` | |

---

## 34. XML Diff *(v1.6 — G16)*

**Source:** `src/utils/XmlDiff.ts`  
**Exports:** `diffXml`, `XmlChange`, `XmlChangeType`, `XmlDiffOptions`

### `diffXml(doc1, doc2, options?): XmlChange[]`

Compute the structural diff between two `XmlDocument`s. Returns changes in document order.

```ts
import { diffXml, parseXml } from 'xml-xsd-engine';

const doc1 = parseXml('<catalog><book id="1"><title>Dune</title></book></catalog>');
const doc2 = parseXml('<catalog><book id="1"><title>Dune 2</title></book></catalog>');

const changes = diffXml(doc1, doc2);
// [{ type: 'modified', path: '/catalog/book/title', from: 'Dune', to: 'Dune 2' }]
```

### `interface XmlChange`

```ts
interface XmlChange {
  type:  XmlChangeType;    // 'added' | 'removed' | 'modified' | 'type-change'
  path:  string;           // XPath-like: '/root/book[2]/title'
  from?: string;           // previous value (modified / removed)
  to?:   string;           // new value (modified / added)
}
```

### `XmlChangeType` values

| Value | Meaning |
|---|---|
| `'added'` | Node present in `doc2` but not `doc1` |
| `'removed'` | Node present in `doc1` but not `doc2` |
| `'modified'` | Text content or attribute value changed |
| `'type-change'` | Element tag name changed (entire subtree replaced) |

### `XmlDiffOptions`

| Option | Type | Default | Description |
|---|---|---|---|
| `ignoreWhitespace` | `boolean` | `true` | Ignore whitespace-only text differences |
| `ignoreComments` | `boolean` | `true` | Ignore comment nodes |
| `ignorePI` | `boolean` | `true` | Ignore processing instruction nodes |

---

## 35. Schema Inference *(v1.6 — G17)*

**Source:** `src/utils/SchemaInference.ts`  
**Exports:** `inferSchema`, `InferredSchema`, `InferSchemaOptions`

### `inferSchema(docs, options?): InferredSchema`

Observe one or more `XmlDocument`s and produce a draft `SchemaModel` + XSD string.

```ts
import { inferSchema, parseXml } from 'xml-xsd-engine';

const doc1 = parseXml('<catalog><product id="1"><name>Widget</name></product></catalog>');
const doc2 = parseXml('<catalog><product id="2"><name>Gadget</name></product></catalog>');

const inferred = inferSchema([doc1, doc2], { targetNamespace: 'http://example.com' });
console.log(inferred.toXsdString()); // valid xs:schema document
```

### `InferSchemaOptions`

| Option | Type | Default | Description |
|---|---|---|---|
| `targetNamespace` | `string` | `''` | Target namespace for generated schema |
| `targetNamespacePrefix` | `string` | `'tns'` | Prefix for target namespace |
| `inferSimpleTypes` | `boolean` | `true` | Infer `xs:boolean`/`xs:integer`/`xs:decimal`/`xs:date` etc. |
| `inferRequired` | `boolean` | `true` | Attributes present in every sample → `use="required"` |

### `interface InferredSchema`

```ts
interface InferredSchema {
  model:         SchemaModel;
  toXsdString(): string;   // emit a valid xs:schema document
}
```

### Type inference cascade (most-specific first)

`xs:boolean` → `xs:integer` → `xs:decimal` → `xs:dateTime` → `xs:date` → `xs:string`

### Multi-document merging

- Elements absent in some samples → `minOccurs="0"`
- Attributes absent in some samples → `use="optional"`
- `maxOccurs` derived from maximum observed repetitions across all samples

---

## 36. Fragment & Subtree Validation *(v1.6 — G45)*

**Source:** `src/validator/ValidationEngine.ts`  
**Exports:** `validateFragment`, `validateSubtree`

### `validateFragment(xmlFragment, schema, options?): ValidationResult`

Parse a raw XML fragment string and validate it. Parse errors are returned as `well-formedness` issues rather than thrown.

```ts
import { validateFragment } from 'xml-xsd-engine';

// Basic fragment validation
const result = validateFragment('<person><name>Alice</name><age>30</age></person>', schema);
console.log(result.valid);

// Validate as a specific named type (no top-level element declaration needed)
const result2 = validateFragment(fragment, schema, { rootType: 'PersonType' });
```

### `validateSubtree(elem, schema, options?): ValidationResult`

Validate an `XmlElement` subtree extracted from a larger document.

```ts
import { validateSubtree } from 'xml-xsd-engine';

const person = doc.root!.getChild('person')!;
const result = validateSubtree(person, schema);
```

### `ValidationOptions.rootType` *(v1.6)*

When set, the fragment/subtree is validated as if it were declared with the named complex or simple type from the schema, bypassing top-level element lookup.

```ts
const result = validateFragment('<data>42</data>', schema, { rootType: 'xs:integer' });
```

---

## 37. Encoding Declaration *(v1.6 — G19)*

**Source:** `src/parser/XmlParser.ts`

`ParseOptions.encoding` — new optional field in v1.6. When set, its value is stored as `XmlDocument.encoding`, overriding (or substituting for) the `encoding` attribute in the XML declaration.

```ts
const doc = parseXml(decodedString, { encoding: 'ISO-8859-1' });
console.log(doc.encoding);  // 'ISO-8859-1'
```

Useful when the caller has already decoded the input via `TextDecoder` but wants the original encoding name preserved on the document object. Backward compatible — defaults to `''` (no override).

---

## Canonical XML (v1.7.0 — G30)

### `canonicalize(input, options?)`

```ts
import { canonicalize, CanonicalizeOptions, CanonicalizeMode } from 'xml-xsd-engine';

function canonicalize(
  input: XmlDocument | XmlElement,
  options?: CanonicalizeOptions
): string;

interface CanonicalizeOptions {
  mode?: 'inclusive' | 'exclusive';   // default 'inclusive'
  inclusiveNamespaces?: string[];      // prefixes for exclusive C14N
  withComments?: boolean;              // default false
}
```

Returns the canonical XML string per W3C Canonical XML 1.0.

### `clearCanonicalCache()`

```ts
import { clearCanonicalCache } from 'xml-xsd-engine';
clearCanonicalCache(): void;
```

Invalidates the internal subtree memoization cache.

---

## PSVI (v1.7.0 — G29)

### `ValidationOptions.psvi`

```ts
interface ValidationOptions {
  // ... existing options ...
  psvi?: boolean;   // default false — enable PSVI annotation collection
}
```

### `ValidationResult.psvi`

```ts
interface ValidationResult {
  // ... existing fields ...
  psvi?: PsviMap;
}

type PsviMap = WeakMap<XmlElement, PsviAnnotation>;

interface PsviAnnotation {
  xsdType: string;
  normalizedValue: string | null;
  validity: 'valid' | 'invalid' | 'notKnown';
  memberType?: string;
}
```

### `makePsviAnnotation(xsdType, normalizedValue?, validity?, memberType?)`

```ts
import { makePsviAnnotation } from 'xml-xsd-engine';

const ann = makePsviAnnotation('xs:integer', '42', 'valid');
```

Factory helper — pools annotations to reduce allocations.

---

## extractPsvi (v1.7.0)

### `extractPsvi(psvi, root, options?)`

```ts
import { extractPsvi, ExtractPsviOptions } from 'xml-xsd-engine';

const result = validate(doc, schema, { psvi: true });
// Convert WeakMap → serialisable path→annotation map
const annotated = extractPsvi(result.psvi!, doc);
for (const [path, ann] of annotated) {
  console.log(path, ann.xsdType, ann.validity);
}
```

```ts
function extractPsvi(
  psvi:    PsviMap,
  root:    XmlDocument | XmlElement,
  options?: ExtractPsviOptions,
): Map<string, PsviAnnotation>;

interface ExtractPsviOptions {
  /** Only extract annotations for elements whose XPath-like path starts with one of these prefixes */
  paths?: string[];
}
```

Converts the `WeakMap`-backed PSVI into a serialisable `Map<path, PsviAnnotation>` by walking the document tree. `paths` filter is useful for IDE hover or subtree diagnostics — avoids a full tree walk.

---

## SchemaMerger (v1.7.0)

### `SchemaMerger`

```ts
import { SchemaMerger } from 'xml-xsd-engine';

const merger = new SchemaMerger(loader, sharedCache);
merger.handleImport(schemaLocation, namespace, targetSchema, subParser);
merger.handleRedefine(schemaLocation, namespace, targetSchema, subParser, applyOverrides);
merger.markImported(key);
merger.hasImported(key);
const cache = merger.cache; // shared parse cache
```

Standalone class that encapsulates `xs:import` / `xs:include` / `xs:redefine` merge logic. Uses SHA-256 content-hash caching to prevent re-parsing identical XSD source across a parse tree.

**Constructor:**
- `loader?: SchemaLoader` — receives `(schemaLocation, namespace) => string | null`
- `sharedCache?: Map<string, SchemaModel>` — pass in a shared cache to coordinate across sub-parsers

---

## XPath Cache Control (v1.7.0)

### `configureXPathCache(opts)`

```ts
import { configureXPathCache, xpathCacheStats, xpathCacheSize } from 'xml-xsd-engine';

// Tune cache sizes (default: coldMax=512, hotMax=128)
configureXPathCache({ coldMax: 256, hotMax: 64 });

// Inspect current state
const { coldSize, hotSize, hits, misses } = xpathCacheStats();
const total = xpathCacheSize();
```

```ts
function configureXPathCache(opts: { coldMax?: number; hotMax?: number }): void;
function xpathCacheStats(): XPathCacheStats;
function xpathCacheSize(): number;

interface XPathCacheStats {
  coldSize: number;
  hotSize:  number;
  hits:     number;
  misses:   number;
}
```

The XPath engine uses a **two-tier LRU cache**:
- **Cold tier** (512 slots by default): newly compiled expressions
- **Hot tier** (128 slots): frequently evaluated expressions (promoted after threshold hits)

---

## Streaming Code Generation (v1.7.0)

### `generateJsonSchemaStream(schema, options?)`

```ts
import { generateJsonSchemaStream } from 'xml-xsd-engine';

for await (const chunk of generateJsonSchemaStream(schema)) {
  process.stdout.write(chunk);
}
```

```ts
function generateJsonSchemaStream(
  schema:   SchemaModel,
  options?: XsdToJsonSchemaOptions,
): AsyncGenerator<string>;
```

Yields the JSON Schema output in ≈4 KB chunks. Avoids peak memory on large schemas.

### `generateTypeScriptStream(schema, options?)`

```ts
import { generateTypeScriptStream } from 'xml-xsd-engine';

for await (const chunk of generateTypeScriptStream(schema)) {
  process.stdout.write(chunk);
}
```

```ts
function generateTypeScriptStream(
  schema:   SchemaModel,
  options?: XsdToTypeScriptOptions,
): AsyncGenerator<string>;
```

Yields top-level TypeScript declarations as individual chunks.

---

## ValidationResult improvements (v1.7.0)

### `ValidationResult.issues`

All issues now carry accurate `line` and `col` source-map positions (G2 — v1.4). These are threaded from lexer tokens through the DOM and into validation errors.

### Error counting fix

Prior to v1.7.0, `_errorCount` was not incremented in `addError()`, causing `result.valid` to return `true` even when errors were present. This is fixed — `result.valid` and `result.errorCount` are now always consistent.

---

## SchemaCache improvements (v1.7.0)

### New options

```ts
interface SchemaCacheOptions {
  maxSize?:          number;  // max entries (LRU eviction)
  ttlMs?:            number;  // per-entry TTL in ms
  trackContentHash?: boolean; // SHA-256 content-hash keying (default true)
  maxVersionsPerKey?: number; // max hash-versioned entries per logical key (default 2)
}
```

`maxVersionsPerKey` prevents unbounded growth in long-running servers or watch-mode when the same file is parsed with different content over time.

### `invalidateByContent(filePath, newSource)`

```ts
cache.invalidateByContent('/path/to/schema.xsd', newXsdSource);
// Returns true if an entry was invalidated (content hash changed)
```

Automatically invalidates cache entries whose source content has changed. Used by CLI watch-mode integration.


---

## 38. Canonical XML *(v1.7)*

**Source:** `src/utils/XmlCanonical.ts` | **Exports:** `canonicalize`, `clearCanonicalCache`, `CanonicalizeOptions`, `CanonicalizeMode`

### `canonicalize(input, options?): string`

Produces W3C Canonical XML 1.0 byte-for-byte reproducible output. Empty elements expanded to start+end tags, attributes sorted, XML declaration stripped, CDATA sections expanded, namespaces rendered per C14N spec.

```ts
import { canonicalize, parseXml } from 'xml-xsd-engine';
const doc = parseXml('<e b="2" a="1"><![CDATA[x & y]]></e>');
canonicalize(doc);
// '<e a="1" b="2">x &amp; y</e>'
```

| Option | Type | Default | Description |
|--------|------|---------|-------------|
| `mode` | `'inclusive' / 'exclusive'` | `'inclusive'` | C14N mode |
| `withComments` | `boolean` | `false` | Preserve comment nodes in output |
| `inclusiveNamespaces` | `string[]` | `[]` | Prefixes to force-include in exclusive mode |

### `clearCanonicalCache()`

Clears the WeakMap subtree memoization cache. Call after DOM mutations to invalidate stale entries.

---

## 39. PSVI *(v1.7)*

**Source:** `src/validator/Psvi.ts` | **Exports:** `PsviAnnotation`, `PsviMap`, `makePsviAnnotation`, `extractPsvi`, `ExtractPsviOptions`

Enable with `validate(doc, schema, { psvi: true })`.

```ts
interface PsviAnnotation {
  xsdType:         string;          // 'xs:integer', '{http://ns}MyType'
  normalizedValue: string | null;   // whitespace-normalised; null for complex types
  validity:        'valid' | 'invalid' | 'notKnown';
  memberType?:     string;          // xs:union resolved member
}
```

`PsviMap` is `Map<XmlElement, PsviAnnotation>` — Map-based (not WeakMap) to expose `.size`.

```ts
const result = validate(doc, schema, { psvi: true });
const map = extractPsvi(result.psvi!, doc);
for (const [path, ann] of map) {
  console.log(path, ann.xsdType, ann.validity);
}
```

---

## 40. SchemaMerger *(v1.7)*

**Source:** `src/xsd/SchemaMerger.ts` | **Exports:** `SchemaMerger`

Centralizes `xs:import`, `xs:include`, and `xs:redefine`. SHA-256 content-hash caching prevents re-parsing identical sub-schemas.

```ts
import { SchemaMerger, parseXsd } from 'xml-xsd-engine';
const cache = new Map();
const merger = new SchemaMerger(myLoader, cache);
merger.handleImport(location, namespace, targetSchema, parseXsd);
```

---

## 41. XPath Cache Control *(v1.7)*

**Source:** `src/parser/XPathEngine.ts` | **Exports:** `configureXPathCache`, `xpathCacheStats`, `xpathCacheSize`

```ts
import { configureXPathCache, xpathCacheStats } from 'xml-xsd-engine';
configureXPathCache({ coldMax: 1024, hotMax: 256 });
const stats = xpathCacheStats();
// { coldSize: 142, hotSize: 31, hits: 8204, misses: 203, promotions: 31 }
```

---

## 42. Streaming Validation *(v1.7)*

**Source:** `src/validator/StreamingValidator.ts`
**Exports:** `validateStreaming`, `validateStream`, `validateStreamingGenerator`, `StreamingValidator`, `StreamingValidationOptions`, `StreamingValidationResult`, `StreamingIssue`

SAX-driven validation. No DOM required. Uses pre-compiled DFAs. Validates structure, types, attributes, and xs:keyref in a single pass.

```ts
import { validateStreaming, compileSchema, parseXsd } from 'xml-xsd-engine';

const compiled = compileSchema(parseXsd(xsdSource));
const result = validateStreaming(xmlSource, compiled, { mode: 'strict' });
// { valid, issues[], errorCount, warningCount, durationMs }
```

### `validateStream(stream, compiled, opts?)` — `Promise<StreamingValidationResult>`

```ts
import { createReadStream } from 'fs';
const result = await validateStream(createReadStream('large.xml'), compiled);
```

### `validateStreamingGenerator(xml, compiled, opts?)` — `AsyncGenerator<StreamingIssue>`

```ts
for await (const issue of validateStreamingGenerator(xmlSource, compiled)) {
  console.error(`[${issue.severity}] ${issue.path}: ${issue.message}`);
}
```

### `StreamingValidationOptions`

| Option | Type | Default | Description |
|--------|------|---------|-------------|
| `mode` | `'strict' / 'lax'` | `'strict'` | Unknown elements: error vs. warning |
| `recover` | `boolean` | `false` | Continue past errors |
| `maxErrors` | `number` | `100` | Stop after N errors |

### `StreamingIssue`

```ts
interface StreamingIssue {
  severity: 'error' | 'warning';
  message:  string;
  path:     string;
  line?:    number;
  col?:     number;
  code?:    string;
}
```

---

## 43. DFA Engine *(v1.7)*

**Source:** `src/validator/DfaEngine.ts`
**Exports:** `buildDfa`, `runDfa`, `DfaModel`, `DfaRunResult`, `NormalisedParticle`, `CompositorKind`

Deterministic Finite Automaton for content-model validation. One DFA per compositor kind (sequence / choice / all). Pre-compiled into `CompiledSchema.compiledDfas` at `compileSchema()` time.

```ts
import { buildDfa, runDfa } from 'xml-xsd-engine';
const dfa = buildDfa(particles, 'sequence');
const { valid, unexpected, missing, tooMany } = runDfa(dfa, ['a', 'b', 'c']);
```

`DfaModel.particleIndex: Map<string, NormalisedParticle>` — O(1) name-to-particle lookup, built once in `buildDfa()`.

---

## 44. Incremental Subtree Revalidation *(v1.7)*

**Source:** `src/validator/ValidationEngine.ts` | **Exports:** `revalidateSubtree`

Re-validates a single `XmlElement` subtree without processing the full document. For editor/IDE "lint on edit" integrations.

```ts
import { revalidateSubtree } from 'xml-xsd-engine';
const result = revalidateSubtree(changedElement, schema, { recover: true });
```

---

## 45. Streaming xs:keyref Tracker *(v1.7)*

**Source:** `src/validator/StreamingValidator.ts` | **Exports:** `StreamingKeyrefTracker`

Tracks xs:key, xs:unique, and xs:keyref tuples during a SAX pass. Validates referential integrity after parse completes. Used internally by `validateStreaming()`.

```ts
import { StreamingKeyrefTracker } from 'xml-xsd-engine';
const tracker = new StreamingKeyrefTracker(schema);
tracker.onElement(localName, path, attrs, textContent);
const issues = tracker.validate(); // StreamingIssue[]
```

---

## 46. SchemaCache Improvements *(v1.7)*

**Source:** `src/cache/SchemaCache.ts` | **Exports:** `SchemaCache`, `SchemaCacheOptions`, `globalSchemaCache`

### Complete `SchemaCacheOptions`

```ts
interface SchemaCacheOptions {
  maxSize?:           number;   // LRU cap (default 100)
  ttlMs?:             number;   // TTL in ms (default Infinity)
  trackContentHash?:  boolean;  // SHA-256 content-hash keying (default true)
  maxVersionsPerKey?: number;   // max versioned entries per logical key (default 2)
  statFast?:          boolean;  // fs.stat fast path for getOrLoad (default false) — v1.7
}
```

### New methods *(v1.7)*

```ts
cache.get(key: string): SchemaModel | undefined   // direct retrieval
cache.has(key: string): boolean                   // presence check
```

### `statFast` option *(v1.7)*

When `true`, `getOrLoad` uses `fs.stat` (mtime + size) as a fast freshness check before reading the full file and computing SHA-256. On a cache hit with matching stat fingerprint, the file read is skipped entirely.

```ts
const cache = new SchemaCache({ statFast: true });
const schema = await cache.getOrLoad('./invoice.xsd');
// Second call: stat check only (no file read if mtime+size unchanged)
```

---

## 47. IValidator Interface *(v1.7)*

**Source:** `src/validator/StreamingValidator.ts` | **Exports:** `IValidator`

Shared interface implemented by both `ValidationEngine` (DOM-based) and `StreamingValidator` (SAX-based). Enables polymorphic validator references.

```ts
interface IValidator {
  validate(xml: string | XmlDocument, schema: SchemaModel, opts?: ValidationOptions): ValidationResult;
}
```

`StreamingValidator implements IValidator` — both DOM and streaming validators share the same interface for use in dependency injection, testing, and custom pipelines.

---

## 48. flattenGroupParticles *(v1.7)*

**Source:** `src/pipeline/SchemaCompilerLite.ts` | **Exports:** `flattenGroupParticles`

Utility function that inlines `xs:group ref` particles into their parent sequence to eliminate synthetic `__group_sequence` particle names that would otherwise cause false DFA errors.

```ts
import { flattenGroupParticles } from 'xml-xsd-engine';

// Inline group particles before passing to buildDfa
const flattened = flattenGroupParticles(particles);
const dfa = buildDfa(flattened, 'sequence');
```

**Inlining rules:**
- Group with `minOccurs=1, maxOccurs=1` → particles inlined directly (transparent pass-through)
- Group with `minOccurs=0, maxOccurs=1` → inner particles made optional (`minOccurs=0`)
- Group with other occurrences → kept as wrapper (non-trivial repetition)

Applied automatically inside `compileSchema()` and `StreamingValidator._validateChildOrder()`. Exported for plugin authors building custom DFA-based validators.

