Skip to content

@lanexio/parser-grammar-xml

This page documents @lanexio/parser-grammar-xml, the XML grammar package implementing XML 1.0 5th Edition with optional DTD validation, external entity resolution, and comprehensive attribute-level DTD information.

  • Version: Stable
  • Module name: parser-grammar-xml
  • Package: @lanexio/parser-grammar-xml
  • Import path: @lanexio/parser-grammar-xml
  • Layer: 2 (Grammar)
  • Runtime: Universal
  • Module format: ESM
  • Stability: Stable
  • Primary use case: Parse XML documents into a flat AST with optional DTD validation.
  • You need to parse XML 1.0 documents into a traversable AST.
  • You need optional DTD validation (content models, required attributes, ID/IDREF consistency).
  • You need to resolve external DTD subsets via a callback.
  • You need access to parsed DTD declarations (element, attribute-list, entity).
BoundaryDescription
InputsUint8Array (source bytes) + optional ParseXmlOptions
OutputsLexTree (flat AST)
Side effectsNone (the parser does no I/O; external DTD resolved via callback)
DeterminismYes
External dependencies@lanexio/parser-core
Never-throw guaranteeYes
Security surfaceNone (parse only, no output generation; no entity expansion)
  1. Install the package.

    Terminal window
    pnpm add @lanexio/parser-grammar-xml
  2. Import the named export.

    import { parseXml } from '@lanexio/parser-grammar-xml';
RequirementRequiredLayerNotes
@lanexio/parser-coreYes1^1.0.0
import { parseXml } from '@lanexio/parser-grammar-xml';
const encoder = new TextEncoder();
const bytes = encoder.encode('<root><item id="1">value</item></root>');
const tree = parseXml(bytes);
console.log(tree.nodeCount);
import { parseXml } from '@lanexio/parser-grammar-xml';
const encoder = new TextEncoder();
const tree = parseXml(
encoder.encode('<!DOCTYPE root [ <!ELEMENT root (#PCDATA)> ]><root>text</root>'),
{ validate: true }
);
import { parseXml } from '@lanexio/parser-grammar-xml';
const encoder = new TextEncoder();
const tree = parseXml(encoder.encode('<a xmlns:p="urn:x"><p:b/></a>'), {
mode: 'strict',
});

mode: "strict" (or the strict: true alias, ADR 0044) is the strict conformance profile whose contract is “WF + reject external-DTD / undefined-entity”. On top of the always-on well-formedness checks it runs:

  • namespace-prefix well-formedness unconditionally: an unbound element or attribute prefix flags the node even in documents with no namespace declarations;
  • internal-subset DTD validation when the document carries an internal DTD subset (VC: Root Element Type, element content models, attribute constraints);
  • the external-DTD surface: a DOCTYPE that references an external subset (SYSTEM or PUBLIC) which no resolver read flags the DocType node as an error node and the Document root. A resolver that read the subset keeps the reference clean.

The parse profiles compose validate and strict:

ProfileWhat runs
default (lenient)WF-only recovery; an unresolved external-DTD document parses clean
validate: truefull DTD validating pass
mode: "strict" / strict: truenamespace-prefix WF + internal-subset DTD validation + external-DTD surface
validate: true + strictfull DTD validating pass on top of the strict surface

Both validate and strict forward through the registered xmlGrammar.parse options bag and the pack parse entry point, so parse(bytes, { grammarOptions: { validate: true, mode: "strict" } }) behaves identically to the direct parseXml(bytes, { validate: true, mode: "strict" }) call.

Strict mode never throws; every input, including the large and non-UTF-8 fixtures in test_files/xml, produces a tree with metadata.language === "xml" (verified by the strict-mode corpus never-throw sweep).

The default mode: "lenient" stays well-formedness-strict and byte-lossless but does not run the strict profile; files that error only in strict mode are strict-profile rejections (unbound prefixes, internal-subset DTD violations, unresolved external-DTD references), not well-formedness defects. See the XML conformance page for the artifact reclassification table (xml-1.1, not-wf, errors/fail, non-UTF-8 encodings, c14n, errata, and the remaining documented buckets) that keeps the REAL-DEFECT bucket empty.

ExportTypeDescription
parseXml(bytes: Uint8Array, options?: ParseXmlOptions) => LexTreeParse XML. Never throws.
XmlKindconst objectNumeric kind IDs for all XML node types.
XmlFieldconst objectNumeric field IDs for XML element slots.
XML_FIELD_NAMES_BY_IDreadonly string[]Field name lookup by numeric field ID.
XML_KIND_NAMES_BY_IDreadonly Record<number, string>Kind-name lookup by numeric ID.
xmlGrammarLanexioParserPureGrammarGrammar descriptor for use with parser-pure.
xmlRegistrationGrammarRegistrationRegistration for the unified grammar registry.
xmlReuseOracleReuseOracleIncremental reuse oracle.
FieldTypeRequiredDefaultDescription
mode"lenient" | "strict"No"lenient""strict" runs the strict conformance profile (ADR 0044): namespace-prefix well-formedness unconditionally, internal-subset DTD validation, and the external-DTD surface (an unresolved external subset flags the DocType). Never throws.
validatebooleanNofalseWhen true, runs the full DTD validating pass after parsing (VC Root Element Type, element content models, attribute constraints). Forwarded through xmlGrammar.parse and the pack parse entry point.
resolveExternalExternalResolverNoundefinedCallback for resolving external DTD subset references. The parser does no I/O.
baseUristringNo""Base URI for resolving relative SYSTEM identifiers.
namespaceCheckbooleanNotrueWhen false, skip namespace prefix resolution and xmlns declaration tracking.
languagestringNo"xml"Language stamp written to tree.metadata.language on every parse result. Future sub-dialects (such as the registered xhtml/svg aliases) may pass their own.
PropertyTypeDescription
rootLexNodeRoot node of the XML document.
nodeCountnumberTotal nodes in the tree.
sourceUint8ArrayOriginal parsed bytes.
metadata.languagestringLanguage stamp on every parse result — "xml" by default, or the language option value. An empty document parses to a Document node containing a single XmlKind.Error leaf (root.hasError is true).
TypePurposeNotes
ParseXmlOptionsOptions for parseXmlSee options table above.
ExternalResolverCallback for resolving external DTD subsets(publicId, systemId, baseUri) => Uint8Array | null
DtdInfoParsed DTD declarationsElements, attlists, entities
DtdElementDeclDTD element declarationName and content model
DtdAttlistDeclDTD attribute-list declarationElement name and attr defs
DtdAttrDefSingle attribute definition in ATTLISTName, type, default
DtdEntityDeclDTD entity declarationName and value
XmlKindTypeUnion of all XmlKind valuesType-safe kind reference
XmlFieldTypeUnion of all XmlField valuesType-safe field reference

The resolveExternal callback allows callers to provide external DTD content without the parser performing network I/O. The parser’s no-expand security model prevents billion-laughs attacks.

  • No direct accessibility surface. The XML parser produces flat AST data structures.
ConcernStatus
Generated output semanticsNot applicable (data format)
ARIA attributes in serialized outputNot applicable
Semantic element round-tripNot applicable
  • parseXml never throws on any byte sequence.
  • The parser uses a no-expand security model for entities (billion-laughs guarantee).
ThreatMitigationStatus
Malformed input byte sequencePanic-free guarantee: all inputs accepted, errors produce LexError AST nodesImplemented
Billion-laughs (entity expansion)No entity expansion: entities are tracked but not substituted in parsed outputImplemented
PackageRelationshipLayerNotes
@lanexio/parser-coreRequires1Provides LexTree, LexNode, LexCursor
@lanexio/parserConsumes6Unified entry point
VersionDateStatusNotable changes
1.0.02026-08-28CurrentNever-throw on large spans in both profiles: chunked decodeRange replaces the unbounded spread-decode sites. Strict-mode corpus sweep (6112 files) never throws.
1.0.02026-05-29PriorInitial stable release. Apache-2.0.
  • None.