UNPKG

xsd-lookup

Version:
778 lines (591 loc) β€’ 31 kB
# XSD schema validation for XML nodes, attributes and values A comprehensive TypeScript-based XSD schema validation system for XML files, which provides nodes, attributes, and values validation against XSD schemas, including the strict hierarchical dependencies. Developed to support scripting language validation in **X4: Foundations** game modding, but can be used for any XML validation needs, based on XSD schemas. ## 🎯 Features - **πŸ” XSD-Based Validation**: Pure XSD schema validation without hardcoded logic - **πŸ“Š Comprehensive Attribute Validation**: Validates attribute existence, types, and values - **πŸ”§ Infrastructure Attribute Filtering**: Automatically ignores XML namespace attributes (`xmlns`, `xmlns:*`, `xsi:*`) - **πŸ”— Type Inheritance Support**: Handles complex XSD type hierarchies and restrictions - **🧩 Hierarchical Context**: Getting list of possible child elements based on current element hierarchy - **πŸ”„ Previous Sibling Context**: Optional previous sibling element name to filter results based on sequence constraints - **⚑ Fast Child Validity Check**: Direct API to check if a specific child is valid under a parent (with optional previous sibling) - **πŸ”€ Union Type Processing**: Merges validation rules from multiple member types - **πŸ“‹ Enumeration Support**: Complete enumeration value extraction and validation with annotations - **🎯 SimpleType Enumeration Discovery**: Extract enumeration values from named SimpleTypes, including union types - **πŸ“ Range Validation**: Numeric and length constraint validation - **πŸ—‚οΈ Multi-line Normalization**: Handles multi-line XML attribute values correctly - **πŸ“ˆ Performance Optimized**: Caching and indexing for fast validation - **🧰 Singleton Convenience**: Use the exported `xsdReference` instance instead of creating your own ## πŸš€ Quick Start ### Installation ```powershell npm install xsd_lookup --save ``` ### Basic Usage ```typescript // ES6 imports (from TypeScript/modern JavaScript) import { XsdReference } from 'xsd_lookup'; // Initialize the validation system const xsdRef = new XsdReference(); xsdRef.init('./tests/data/xsd'); const schemaName = 'aiscripts'; const elementName = 'set_value'; const elementHierarchy = ['actions', 'attention', 'aiscript']; // bottom-up (parent -> root) const elementDefinition = xsdRef.getElementDefinition(schemaName, elementName, elementHierarchy); console.log(`Element ${elementName} is ${elementDefinition ? 'defined' : 'not defined'} in the schema.`); // Returns Record<string, EnhancedAttributeInfo> const elementAttributes = xsdRef.getElementAttributesWithTypes(schemaName, elementName, elementHierarchy); console.log(`Element ${elementName} has ${Object.keys(elementAttributes).length} attributes.`); // Static utilities operate on the attribute info map const attributeValues = XsdReference.getAttributePossibleValues(elementAttributes, 'operation'); console.log(`Possible values for 'operation': ${Array.from(attributeValues.keys()).join(', ')}`); const checkAttributeValue = XsdReference.validateAttributeValueAgainstRules(elementAttributes, 'operation', 'unknown'); console.log(`Attribute 'operation' value 'unknown' is ${checkAttributeValue.isValid ? 'valid' : 'invalid'}.`); // Get enumeration values from SimpleType definitions const classEnums = xsdRef.getSimpleTypeEnumerationValues('common', 'classlookup'); if (classEnums) { console.log(`Found ${classEnums.values.length} class types: ${classEnums.values.slice(0, 3).join(', ')}...`); } // Clean up resources when done (optional but recommended) xsdRef.dispose(); ``` Alternatively, use the singleton instance: ```typescript import { xsdReference } from 'xsd_lookup'; // Initialize once xsdReference.init('./tests/data/xsd'); const elementName = 'set_value'; const elementHierarchy = ['actions', 'attention', 'aiscript']; const elementDefinition = xsdReference.getElementDefinition('aiscripts', elementName, elementHierarchy); const elementAttributes = xsdReference.getElementAttributesWithTypes('aiscripts', elementName, elementHierarchy); // Record<string, EnhancedAttributeInfo> // Clean up when done xsdReference.dispose(); ``` ## πŸ“– API Reference ### 🏷️ Type Definitions #### Exported Interfaces ```typescript // Location of a schema element definition interface ElementLocation { uri: string; // file:// URI to the XSD file line: number; // 1-based column: number; // 1-based lengthOfStartTag: number; // number of characters in the element's start tag } // Enhanced attribute information with validation rules interface EnhancedAttributeInfo { name: string; type?: string; // Location packaged as a single object location?: ElementLocation; required?: boolean; patterns?: string[]; enumValues?: string[]; enumValuesAnnotations?: Map<string, string>; annotation?: string; minLength?: number; maxLength?: number; minInclusive?: number; maxInclusive?: number; minExclusive?: number; maxExclusive?: number; } // Attribute validation result interface AttributeValidationResult { isValid: boolean; errorMessage?: string; expectedType?: string; restrictions?: string[]; allowedValues?: string[]; } // Attribute name validation result interface AttributeNameValidationResult { wrongAttributes: string[]; missingRequiredAttributes: string[]; } ``` Notes: - Methods returning attribute info use an object map keyed by attribute name: Record<string, EnhancedAttributeInfo>. ### 🏷️ Input parameters - `schemaName`: Name of the schema to operate on (e.g., 'aiscripts'). Equal to the XSD file name without extension. - `elementName`: Name of the XML element to validate (e.g., 'do_if'). - `attributeName`: Name of the attribute to validate (e.g., 'value'). - `value`: The value to validate against the attribute's XSD rules (e.g., 'player.money gt 1000'). - `hierarchy`: Parameter to specify the hierarchy of elements in bottom-up order (from immediate parent to root element) in XML-file. This is crucial for correct validation context. #### πŸ—οΈ Hierarchy Parameter Usage **Important**: All methods that accept a `hierarchy` parameter expect it in **bottom-up order** (from immediate parent to root element). ##### Hierarchy Examples ```typescript // For XML structure: // <aiscript> // <attention> // <actions> // <do_if value="$condition"> // <debug_text text="message" /> // </do_if> // <set_value name="$value" exact="100" /> // </actions> // </attention> // </aiscript> // For 'do_if' element: const doIfHierarchy = ['actions', 'attention', 'aiscript']; // For 'debug_text' element: const debugTextHierarchy = ['do_if', 'actions', 'attention', 'aiscript']; // Usage: const attributes = xsdRef.getElementAttributesWithTypes('aiscripts', 'do_if', doIfHierarchy); const validation = xsdRef.validateAttributeValue('aiscripts', 'debug_text', 'text', 'Hello', debugTextHierarchy); ``` ### XsdReference Class The main entry point for any operations. #### Constructor ```typescript new XsdReference() ``` ##### `init(xsdDirectory: string): void` Initialize or reinitialize the instance with the directory that contains your .xsd files. This clears any previously loaded schemas. ```typescript const xsdRef = new XsdReference(); xsdRef.init('./tests/data/xsd'); ``` ##### Singleton instance You can also use a ready-to-use singleton and avoid manual instantiation. ```typescript import { xsdReference } from 'xsd_lookup'; xsdReference.init('./tests/data/xsd'); // Then call API methods on xsdReference const children = xsdReference.getPossibleChildElements('aiscripts', 'actions', ['attention', 'aiscript']); ``` #### Core Methods ##### `getSchema(schemaName: string): Schema | null` Load and return a schema by name. ```typescript const schema = xsdRef.getSchema('aiscripts'); ``` ##### `getAvailableSchemas(): string[]` Get all currently loaded schema types. ```typescript const loadedSchemas: string[] = xsdRef.getAvailableSchemas(); // Returns: ['aiscripts', 'md'] (schemas that are currently loaded in memory) ``` ##### `getDiscoverableSchemas(): string[]` Get all discoverable schema files in the XSD directory. ```typescript const availableSchemas: string[] = xsdRef.getDiscoverableSchemas(); // Returns: ['aiscripts', 'common', 'md'] (all .xsd files found in directory, without extension) ``` ##### `getElementDefinition(schemaName: string, elementName: string, hierarchy?: string[]): Element | undefined` Get the element definition for a specific element in a schema, considering hierarchy context. **Important**: The `hierarchy` parameter should be provided in **bottom-up order** (from immediate parent to root element). ```typescript // For element structure: <aiscript><attention><actions><set_value> // Hierarchy for 'set_value' element should be: ['actions', 'attention', 'aiscript'] const elementDef = xsdRef.getElementDefinition('aiscripts', 'set_value', ['actions', 'attention', 'aiscript']); // Returns: Element definition object or undefined if not found ``` ##### `getElementAttributes(schemaName: string, elementName: string, hierarchy?: string[]): Record<string, Element>` Get basic attribute elements for an element as a map of attribute name to the defining Element node. **Important**: The `hierarchy` parameter should be provided in **bottom-up order** (from immediate parent to root element). ```typescript // For element structure: <aiscript><attention><actions><set_value> // Hierarchy for 'set_value' element should be: ['actions', 'attention', 'aiscript'] const attributes: Record<string, Element> = xsdRef.getElementAttributes('aiscripts', 'set_value', ['actions', 'attention', 'aiscript']); // Returns Record<string, EnhancedAttributeInfo> keyed by attribute name: // { // name: Element, // DOM element reference to xs:attribute definition // value: Element, // ... // } ``` ##### `getElementAttributesWithTypes(schemaName: string, elementName: string, hierarchy?: string[], elementDef?: Element): Record<string, EnhancedAttributeInfo>` Get all attributes for an element with complete type information including: - Type name - Required status - Enumeration values (if applicable) - Pattern restrictions - Numeric/length constraints - Attribute definition location (file URI, line, column, lengthOfStartTag) - Attribute annotation text (if available) Performance tip: You can pass an optional `elementDef` (from `getElementDefinition`) to avoid re-resolving the element in the schema. **Important**: The `hierarchy` parameter should be provided in **bottom-up order** (from immediate parent to root element). ```typescript // For element structure: <aiscript><attention><actions><do_if> // Hierarchy for 'do_if' element should be: ['actions', 'attention', 'aiscript'] const attributes = xsdRef.getElementAttributesWithTypes('aiscripts', 'do_if', ['actions', 'attention', 'aiscript']); // Returns Record<string, EnhancedAttributeInfo> keyed by attribute name: // { // value: { // name: 'value', // type: 'expression', // required: true, // patterns: ['[pattern regex]'], // enumValues: undefined, // annotation: 'Description text...'(optional), // location: { // uri: 'file:///C:/path/to/aiscripts.xsd', // line: 123, // column: 5, // lengthOfStartTag: 42 // } // } // } ``` ##### `validateAttributeValue(schemaName: string, elementName: string, attributeName: string, value: string, hierarchy?: string[]): AttributeValidationResult` Validate an attribute value against XSD constraints. **Important**: The `hierarchy` parameter should be provided in **bottom-up order** (from immediate parent to root element). ```typescript // For element structure: <aiscript><attention><actions><do_if><debug_text> // Hierarchy for 'debug_text' element should be: ['do_if', 'actions', 'attention', 'aiscript'] const result: AttributeValidationResult = xsdRef.validateAttributeValue('aiscript', 'debug_text', 'chance', '50', ['do_if', 'actions', 'attention', 'aiscript']); // Returns: // { // isValid: true, // expectedType: 'expression', // restrictions: ['Pattern: ...'] // } ``` ##### `getPossibleChildElements(schemaName: string, elementName: string, hierarchy?: string[], previousSibling?: string): Map<string, string>` Get possible child elements for a given element, with their annotation text. **Important**: The `hierarchy` parameter should be provided in **bottom-up order** (from immediate parent to root element). **Important**: The `previousSibling` parameter can be used to filter results based on sequence constraints (e.g., if the element must come after a specific sibling). ```typescript // For element structure: <aiscript><attention><actions> // Get possible children of 'actions' element const children: Map<string, string> = xsdRef.getPossibleChildElements('aiscripts', 'actions', ['attention', 'aiscript']); // Returns: Map where key is child element name, value is annotation text // Example: Map { 'do_if' => 'Conditional execution', 'do_while' => 'Loop execution', ... } // Usage examples: if (children.size > 0) { console.log('Possible child elements:'); for (const [elementName, annotation] of children) { console.log(` ${elementName}: ${annotation || '(no description)'}`); } } ``` ##### `isValidChild(schemaName: string, elementName: string, parentName: string, parentHierarchy?: string[], previousSibling?: string): boolean` Check if a specific element is valid as a child of a given parent in the provided hierarchy, using the same engine and sequence/choice constraints as `getPossibleChildElements`, but without building the full child set. **Important**: The `parentHierarchy` parameter should be provided in **bottom-up order** (from immediate parent to root element). **Important**: The optional `previousSibling` parameter narrows the check to respect sequence constraints within the parent’s content model. ```typescript // Validate if 'do_else' can follow 'do_if' inside <actions> const ok = xsdRef.isValidChild( 'aiscripts', 'do_else', 'actions', ['attention', 'aiscript'], 'do_if' ); console.log('do_else after do_if is', ok ? 'allowed' : 'not allowed'); // Check if 'do_if' can start under <actions> (no previousSibling) const canStart = xsdRef.isValidChild('aiscripts', 'do_if', 'actions', ['attention', 'aiscript']); console.log('do_if at start of <actions> is', canStart ? 'allowed' : 'not allowed'); ``` ##### `getSimpleTypesWithBaseType(schemaName: string, baseType: string): string[]` Get all simple types that use a specific type as their base. ```typescript // Find all simple types based on 'lvalueexpression' const derivedTypes: string[] = xsdRef.getSimpleTypesWithBaseType('aiscripts', 'lvalueexpression'); // Returns: ['expression', 'objectref', 'paramname', ...] (types that extend lvalueexpression) // Find all types based on xs:string const stringTypes: string[] = xsdRef.getSimpleTypesWithBaseType('common', 'xs:string'); // Returns: ['name', 'comment', 'text', ...] (string-based types) ``` ##### `getSimpleTypeEnumerationValues(schemaName: string, simpleTypeName: string): { values: string[], annotations: Map<string, string> } | null` Get enumeration values for a named SimpleType, including support for union types. **Union Type Support**: This method properly handles union types by extracting enumeration values from all member types and combining them into a single result. ```typescript // Get enumeration values from a simple enumeration type const quadrantResult = xsdRef.getSimpleTypeEnumerationValues('common', 'quadrantlookup'); // Returns: // { // values: ['quadrant.none', 'quadrant.front', 'quadrant.back'], // annotations: Map { // 'quadrant.none' => 'No specific quadrant', // 'quadrant.front' => 'Front quadrant', // 'quadrant.back' => 'Back quadrant' // } // } // Get enumeration values from a union type (combines multiple enumerations) const classResult = xsdRef.getSimpleTypeEnumerationValues('common', 'classlookup'); // Returns: // { // values: ['class.component', 'class.buildprocessor', 'class.ship', 'class.station', ...], // annotations: Map { // 'class.component' => 'Component', // 'class.buildprocessor' => 'Build processor', // 'class.ship' => 'Ship', // // ... 80+ values from union of 'positionalclasslookup' and 'expression' types // } // } // Handle non-existent or non-enumeration types const nonExistent = xsdRef.getSimpleTypeEnumerationValues('common', 'nonexistent'); // Returns: null // Usage example: if (classResult) { console.log(`Found ${classResult.values.length} enumeration values`); for (const value of classResult.values) { const annotation = classResult.annotations.get(value); console.log(` ${value}: ${annotation || '(no description)'}`); } // Check if a specific value exists if (classResult.values.includes('class.ship')) { console.log('class.ship is a valid enumeration value'); } } ``` ##### `dispose(): void` Clear all internal caches to release resources. ```typescript // Get a schema instance const schema = xsdRef.getSchema('aiscripts'); // Use the schema for validation... const result = schema.validateAttributeValue('do_if', 'value', 'condition'); // Clean up schema resources when done schema.dispose(); ``` **Note**: Not recommended for frequent use in performance-sensitive scenarios. #### Static Methods ##### `XsdReference.getElementLocation(element: Element): ElementLocation` Get the source file URI and 1-based position for a schema element definition. ```typescript // Obtain an element definition first const def = xsdRef.getElementDefinition('aiscripts', 'do_if', ['actions', 'attention', 'aiscript']); if (def) { const loc = XsdReference.getElementLocation(def); // Example output: file:///C:/path/to/tests/data/xsd/aiscripts.xsd:123:5 console.log(`${loc.uri}:${loc.line}:${loc.column}`); // Start tag length is also available when you need precise range handling // console.log('start tag length:', loc.lengthOfStartTag); } ``` Notes: - uri is a file:// URI pointing to the XSD file that defined the element. - line and column are 1-based positions within that file. - lengthOfStartTag is the character length of the element’s start tag (from '<' to the matching '>'). ##### `XsdReference.validateAttributeNames(attributeInfos: Record<string, EnhancedAttributeInfo>, providedAttributes: string[]): AttributeNameValidationResult` Validate attribute names against schema definitions. This static method checks which attributes are valid and identifies missing required attributes. **πŸ”§ Infrastructure Attribute Handling**: XML infrastructure attributes (`xmlns`, `xmlns:*`, `xsi:*`) are automatically filtered out before validation. ```typescript // Get attribute info first const attributeInfos = xsdRef.getElementAttributesWithTypes('aiscripts', 'do_if', ['actions', 'attention', 'aiscript']); const providedAttrs = ['value', 'chance', 'xmlns:xsi', 'invalid_attr']; const nameValidation: AttributeNameValidationResult = XsdReference.validateAttributeNames(attributeInfos, providedAttrs); // Returns: // { // wrongAttributes: ['invalid_attr'], // Infrastructure attrs filtered out // missingRequiredAttributes: ['negate', ...] // Required attributes missing // } // Note: 'xmlns:xsi' is ignored and doesn't appear in wrongAttributes ``` ##### `XsdReference.validateAttributeValueAgainstRules(attributeInfos: Record<string, EnhancedAttributeInfo>, attributeName: string, attributeValue: string)` Validate an attribute value against all XSD rules (patterns, enumerations, ranges, etc.). This static method provides detailed validation with rule violation information. **πŸ”§ Infrastructure Attribute Handling**: XML infrastructure attributes (`xmlns`, `xmlns:*`, `xsi:*`) are automatically ignored and always return `{ isValid: true }`. ```typescript // Get attribute info first const attributeInfos = xsdRef.getElementAttributesWithTypes('aiscripts', 'do_if', ['actions', 'attention', 'aiscript']); const valueValidation = XsdReference.validateAttributeValueAgainstRules( attributeInfos, 'value', 'player.money gt 1000' ); // Returns: // { // isValid: true, // errorMessage: undefined, // Only present if invalid // violatedRules: undefined // Only present if invalid - array of specific rule violations // } // Example with invalid value: const invalidValidation = XsdReference.validateAttributeValueAgainstRules( attributeInfos, 'chance', '150' ); // Returns: // { // isValid: false, // errorMessage: "Value must be <= 100", // violatedRules: ["Value must be <= 100"] // } // Infrastructure attributes are ignored: const infraValidation = XsdReference.validateAttributeValueAgainstRules( attributeInfos, 'xmlns:xsi', 'http://www.w3.org/2001/XMLSchema-instance' ); // Returns: // { // isValid: true // } ``` ##### `XsdReference.getAttributePossibleValues(attributeInfos: Record<string, EnhancedAttributeInfo>, attributeName: string): Map<string, string>` Get all possible enumeration values for an attribute, if it has enumeration restrictions. **πŸ”§ Infrastructure Attribute Handling**: XML infrastructure attributes always return an empty Map. ```typescript // Get attribute info first const attributeInfos = xsdRef.getElementAttributesWithTypes('aiscripts', 'set_value', ['actions', 'attention', 'aiscript']); const possibleValues: Map<string, string> = XsdReference.getAttributePossibleValues(attributeInfos, 'operator'); // Returns: Map<string, string> where key is enum value, value is annotation // Example: Map { 'set' => 'Set value', 'add' => 'Add to value', 'subtract' => 'Subtract from value', ... } // Returns: Map() (empty map if no enumeration exists or if it's an infrastructure attribute) // Usage examples: if (possibleValues.size > 0) { console.log('Available values:'); for (const [value, annotation] of possibleValues) { console.log(` ${value}: ${annotation || '(no description)'}`); } // Get just the values as an array const valueArray = Array.from(possibleValues.keys()); console.log('Values only:', valueArray); // ['set', 'add', 'subtract', 'insert'] } ``` ##### `XsdReference.filterAttributesByType(attributeInfos: Record<string, EnhancedAttributeInfo>, attributeType: string): string[]` Filter attributes by their XSD type (e.g., 'xs:string', 'xs:int', 'expression'). **πŸ”§ Infrastructure Attribute Handling**: XML infrastructure attributes are automatically excluded from results. ```typescript // Get attribute info first const attributeInfos = xsdRef.getElementAttributesWithTypes('aiscripts', 'do_if', ['actions', 'attention', 'aiscript']); const stringAttributes: string[] = XsdReference.filterAttributesByType(attributeInfos, 'xs:string'); // Returns: ['comment', 'name'] (example - attributes with xs:string type) const expressionAttributes: string[] = XsdReference.filterAttributesByType(attributeInfos, 'expression'); // Returns: ['value', 'chance'] (example - attributes with expression type) ``` ##### `XsdReference.filterAttributesByRestriction(attributeInfos: Record<string, EnhancedAttributeInfo>, restrictionType: 'enumeration' | 'pattern' | 'length' | 'range'): string[]` Filter attributes by the type of XSD restriction they have. **πŸ”§ Infrastructure Attribute Handling**: XML infrastructure attributes are automatically excluded from results. ```typescript // Get attribute info first const attributeInfos = xsdRef.getElementAttributesWithTypes('aiscripts', 'do_if', ['actions', 'attention', 'aiscript']); const enumAttributes: string[] = XsdReference.filterAttributesByRestriction(attributeInfos, 'enumeration'); // Returns: ['operator', 'type'] (example - attributes with enumeration restrictions) const patternAttributes: string[] = XsdReference.filterAttributesByRestriction(attributeInfos, 'pattern'); // Returns: ['ref', 'name'] (example - attributes with pattern restrictions) const rangeAttributes: string[] = XsdReference.filterAttributesByRestriction(attributeInfos, 'range'); // Returns: ['min', 'max'] (example - attributes with numeric range restrictions) const lengthAttributes: string[] = XsdReference.filterAttributesByRestriction(attributeInfos, 'length'); // Returns: ['text'] (example - attributes with length restrictions) ``` ##### `extractAnnotationText(schemaName: string, element: Element): string | undefined` (instance) Extract annotation text from an XML element in the context of a specific schema. This is used by the test runner to track annotation coverage and is also available for consumers. ```typescript // This is typically used internally, but can be useful for custom schema processing const element: Element = /* DOM element from XSD */; const annotation: string | undefined = xsdRef.extractAnnotationText('aiscripts', element); // Returns: "Description text from xs:annotation/xs:documentation" or undefined // Example usage with element definitions: const elementDef = xsdRef.getElementDefinition('aiscripts', 'do_if', ['actions', 'attention', 'aiscript']); if (elementDef) { const description = xsdRef.extractAnnotationText('aiscripts', elementDef); console.log('Element description:', description || 'No description available'); } ``` #### Typical Validation Workflow ```typescript // 1. Get schema and attribute info once const xsdRef = new XsdReference(); xsdRef.init('./tests/data/xsd'); const attributeInfos = xsdRef.getElementAttributesWithTypes('aiscripts', 'do_if', ['actions', 'attention', 'aiscript']); // 2. Validate attribute names (fast, no schema lookup needed) // Infrastructure attributes (xmlns, xsi:) are automatically filtered out const nameValidation: AttributeNameValidationResult = XsdReference.validateAttributeNames(attributeInfos, ['value', 'xmlns:xsi', 'invalid_attr']); if (nameValidation.wrongAttributes.length > 0) { console.error('Invalid attributes:', nameValidation.wrongAttributes); // Only shows 'invalid_attr' } // 3. Validate attribute values (fast, no schema lookup needed) // Infrastructure attributes automatically return { isValid: true } for (const attrName of Object.keys(attributeInfos)) { const valueValidation = XsdReference.validateAttributeValueAgainstRules( attributeInfos, attrName, attributeValue ); if (!valueValidation.isValid) { console.error(`Invalid value for ${attrName}:`, valueValidation.violatedRules); } } // 4. Use helper methods to analyze attributes by type or restriction const enumAttributes: string[] = XsdReference.filterAttributesByRestriction(attributeInfos, 'enumeration'); const possibleValues: Map<string, string> = XsdReference.getAttributePossibleValues(attributeInfos, 'operator'); // Work with possible values Map if (possibleValues.size > 0) { console.log('Operator values:', Array.from(possibleValues.keys())); // Check if a specific value is valid if (possibleValues.has('eq')) { console.log('eq annotation:', possibleValues.get('eq') || '(no description)'); } } // Alternatively, using the singleton // import { xsdReference } from 'xsd_lookup'; // xsdReference.init('./tests/data/xsd'); // const attributeInfos = xsdReference.getElementAttributesWithTypes('aiscripts', 'do_if', ['actions', 'attention', 'aiscript']); ``` ### Schema Class There is a main engine for validating XML files against XSD schemas. It provides detailed validation capabilities for a specific XSD schema. **Note**: Not recommended to use directly, prefer using `XsdReference` for schema management. ### XsdDetector Class Automatically detects the appropriate XSD schema for XML files. #### `getSchemaName(xmlFilePath: string): string | null` Detect schema name from XML file path and content. ```typescript const schemaName = XsdDetector.getSchemaName('./scripts/my-script.xml'); // Returns: 'aiscripts' ``` ### οΏ½ Infrastructure Attribute Handling The validation system automatically handles XML infrastructure attributes to focus validation on content-relevant attributes: #### What are Infrastructure Attributes? Infrastructure attributes are XML namespace and schema-related attributes that are part of the XML specification itself, not your content: - `xmlns` - Default namespace declaration - `xmlns:*` - Namespace prefix declarations (e.g., `xmlns:xsi`) - `xsi:*` - XML Schema Instance attributes (e.g., `xsi:schemaLocation`) #### Automatic Filtering All static validation methods automatically filter out infrastructure attributes: ```xml <!-- Example XML with infrastructure attributes --> <do_if value="player.money gt 1000" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="..." chance="50"> <!-- content --> </do_if> ``` ```typescript // Only 'value' and 'chance' are validated; xmlns/xsi attributes are ignored const attributeInfos = xsdRef.getElementAttributesWithTypes('aiscripts', 'do_if', hierarchy); const nameValidation = XsdReference.validateAttributeNames( attributeInfos, ['value', 'chance', 'xmlns:xsi', 'xsi:schemaLocation'] ); // Result: { wrongAttributes: [], missingRequiredAttributes: [] } // Infrastructure attributes don't appear in wrongAttributes const valueValidation = XsdReference.validateAttributeValueAgainstRules( attributeInfos, 'xmlns:xsi', 'http://www.w3.org/2001/XMLSchema-instance' ); // Result: { isValid: true } - Infrastructure attributes always pass ``` ## πŸ”§ Development ### Project Structure ```text β”œβ”€β”€ src/ # TypeScript source files β”‚ β”œβ”€β”€ Schema.ts # Core validation engine β”‚ β”œβ”€β”€ XsdReference.ts # Main API interface β”‚ └── XsdDetector.ts # Schema auto-detection β”œβ”€β”€ dist/ # Compiled JavaScript output β”œβ”€β”€ tests/ # Comprehensive test suite β”‚ β”œβ”€β”€ data/ # Test data and schemas β”‚ β”‚ β”œβ”€β”€ xsd/ # XSD schema definitions β”‚ β”‚ β”œβ”€β”€ aiscripts/ # AI script test files β”‚ β”‚ └── md/ # Mission director test files β”‚ └── test_all_files_comprehensive.js β”œβ”€β”€ package.json # Project configuration └── tsconfig.json # TypeScript configuration ``` ## πŸ“„ License This project is licensed under the MIT License - see the LICENSE file for details. ## πŸ™ Acknowledgments - **X4 Foundations** by Egosoft for the complex and rich modding system - **XML Schema (XSD)** specification for comprehensive validation standards - **TypeScript** and **Node.js** ecosystem for excellent development tools ### Performance Tips - Reuse `XsdReference` instances when validating multiple files with the same schemas - Call `dispose()` only when completely finished with validation tasks - Use static methods (`XsdReference.validateAttributeNames`, etc.) for batch operations with pre-fetched attribute info