XSD Code Generation Strategy for FEL
🎯 Overview​
The FELgt repository uses XSD-to-C# code generation to automatically create strongly-typed classes from SAT's XML schemas. This is a brilliant approach that we should adopt for TypeScript.
💡 Why Code Generation is Powerful​
Benefits​
-
âś… 100% Schema Compliance
- Generated code matches XSD exactly
- No manual interpretation errors
- Guaranteed structure correctness
-
âś… Type Safety
- TypeScript interfaces generated from XSD
- Compile-time validation
- IDE autocomplete support
-
âś… Auto-Updates
- When SAT updates XSD, regenerate code
- No manual updates needed
- Always in sync with official schema
-
âś… Reduced Errors
- No typos in XML element names
- Correct namespace declarations
- Proper data types
-
âś… Documentation
- Generated types serve as documentation
- See all required/optional fields
- Understand relationships
🔧 Available Tools for TypeScript​
Option 1: xsd2ts (Recommended)​
Package: xsd2ts or xsd-typescript-generator
Installation:
npm install --save-dev xsd-typescript-generator
# or
npm install --save-dev xsd2ts
Usage:
xsd2ts -i docs/fel/xsd/*.xsd -o apps/backend/src/fel/generated/
Output:
- TypeScript interfaces
- Type definitions
- Namespace mappings
Option 2: quicktype​
Package: quicktype
Installation:
npm install -g quicktype
Usage:
quicktype --src docs/fel/xsd/fact.xsd --lang typescript --out apps/backend/src/fel/generated/fact.types.ts
Features:
- Supports XSD
- Generates TypeScript types
- Can generate JSON Schema first, then TypeScript
Option 3: Custom Script (Recommended for Full Control)​
Create: packages/backend/scripts/src/fel/generate-fel-types.ts
Approach:
- Parse XSD files
- Generate TypeScript interfaces
- Generate XML builder helpers
- Generate validation functions
📋 Implementation Strategy​
Phase 1: Download XSD Files​
# Create directory
mkdir -p docs/fel/xsd
# Download SAT XSD files
cd docs/fel/xsd
wget -r --no-parent --accept=xsd https://cat.desa.sat.gob.gt/xsd/alfa/
# Download XML Signature schema
wget https://www.w3.org/TR/2002/REC-xmldsig-core-20020212/xmldsig-core-schema.xsd
Phase 2: Generate TypeScript Types​
Script: packages/backend/scripts/src/fel/generate-fel-types.ts
import { parseXSD } from 'xsd-parser';
import { generateTypeScript } from 'xsd-to-typescript';
import fs from 'fs';
import path from 'path';
async function generateFelTypes() {
const xsdDir = path.join(__dirname, '../../../docs/fel/xsd');
const outputDir = path.join(__dirname, '../../../apps/backend/src/fel/generated');
// Parse XSD files
const factXsd = await parseXSD(path.join(xsdDir, 'fact.xsd'));
const ncreXsd = await parseXSD(path.join(xsdDir, 'ncre.xsd'));
const ndebXsd = await parseXSD(path.join(xsdDir, 'ndeb.xsd'));
const anulacionXsd = await parseXSD(path.join(xsdDir, 'anulacion.xsd'));
// Generate TypeScript interfaces
const factTypes = generateTypeScript(factXsd, 'FactDocument');
const ncreTypes = generateTypeScript(ncreXsd, 'CreditNoteDocument');
const ndebTypes = generateTypeScript(ndebXsd, 'DebitNoteDocument');
const anulacionTypes = generateTypeScript(anulacionXsd, 'CancellationDocument');
// Write to files
fs.writeFileSync(path.join(outputDir, 'fact.types.ts'), factTypes);
fs.writeFileSync(path.join(outputDir, 'ncre.types.ts'), ncreTypes);
fs.writeFileSync(path.join(outputDir, 'ndeb.types.ts'), ndebTypes);
fs.writeFileSync(path.join(outputDir, 'anulacion.types.ts'), anulacionTypes);
console.log('âś… FEL types generated successfully!');
}
Phase 3: Add to Package Scripts​
File: packages/backend/scripts/package.json
{
"scripts": {
"generate:fel-types": "tsx src/fel/generate-fel-types.ts"
}
}
Phase 4: Use Generated Types​
Update: apps/backend/src/fel/domain/fel.interface.ts
// Import generated types
import type { FactDocument } from '../generated/fact.types';
import type { CreditNoteDocument } from '../generated/ncre.types';
import type { DebitNoteDocument } from '../generated/ndeb.types';
import type { CancellationDocument } from '../generated/anulacion.types';
// Union type for all FEL documents
export type FelDocument =
| FactDocument
| CreditNoteDocument
| DebitNoteDocument
| CancellationDocument;
🎨 Hybrid Approach (Recommended)​
Why Hybrid?​
Full code generation can be complex and may not match your current patterns. A hybrid approach gives you:
- âś… Generated types for validation
- âś… Manual XML builders (your current approach)
- âś… Best of both worlds
Strategy​
-
Generate Types from XSD
- Use for validation
- Use for type checking
- Reference for XML structure
-
Keep Manual XML Builders
- Your current
XmlConversionServiceapproach - More control over structure
- Easier to customize
- Your current
-
Use Types for Validation
- Validate JSON before XML conversion
- Type-check XML builders
- Ensure compliance
Example​
// Generated from XSD
interface FactDocument {
DatosGenerales: {
Tipo: 'FACT';
FechaHoraEmision: string;
CodigoMoneda: string;
};
Emisor: { /* ... */ };
Receptor: { /* ... */ };
Items: { /* ... */ };
// ... all fields from XSD
}
// Your current interface (simplified)
interface Invoice {
generalData: { type: string; dateTimeIssue: string; currencyCode: string };
issuerData: { /* ... */ };
receiverData: { /* ... */ };
items: InvoiceItem[];
// ... your simplified structure
}
// Mapper function
function mapInvoiceToFactDocument(invoice: Invoice): FactDocument {
return {
DatosGenerales: {
Tipo: 'FACT',
FechaHoraEmision: invoice.generalData.dateTimeIssue,
CodigoMoneda: invoice.generalData.currencyCode,
},
// ... map other fields
};
}
// Validation
function validateFactDocument(doc: FactDocument): boolean {
// Use XSD-generated types for validation
// Check required fields, data types, etc.
return true;
}
🔄 Workflow Integration​
Development Workflow​
# 1. Download latest XSD files (when SAT updates)
pnpm fel:download-xsd
# 2. Regenerate TypeScript types
pnpm generate:fel-types
# 3. TypeScript compiler will catch any breaking changes
pnpm build
# 4. Update XML builders if needed
# (Your XmlConversionService)
CI/CD Integration​
# .github/workflows/fel-types.yml
name: Update FEL Types
on:
schedule:
- cron: '0 0 * * 1' # Weekly check
workflow_dispatch:
jobs:
update-types:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Download XSD files
run: pnpm fel:download-xsd
- name: Generate types
run: pnpm generate:fel-types
- name: Check for changes
run: |
git diff --exit-code || {
echo "FEL types have changed!"
git add apps/backend/src/fel/generated/
git commit -m "chore: update FEL types from XSD"
git push
}
📊 Comparison: Manual vs Generated​
Manual Approach (Current)​
Pros:
- âś… Full control
- âś… Simplified interfaces
- âś… Easy to customize
- âś… Matches your patterns
Cons:
- ❌ Manual updates when XSD changes
- ❌ Risk of missing fields
- ❌ No automatic validation
- ❌ Potential for errors
Generated Approach​
Pros:
- âś… Always in sync with XSD
- âś… Type safety
- âś… Auto-updates
- âś… No manual errors
Cons:
- ❌ More complex types
- ❌ May not match your patterns
- ❌ Requires XSD parsing
- ❌ Generated code can be verbose
Hybrid Approach (Best)​
Pros:
- âś… Best of both worlds
- âś… Types for validation
- âś… Manual builders for control
- âś… Reference for structure
Cons:
- ⚠️ Slight complexity
- ⚠️ Need to maintain mapping
🚀 Recommended Implementation​
Step 1: Download XSD Files​
mkdir -p docs/fel/xsd
cd docs/fel/xsd
wget -r --no-parent --accept=xsd https://cat.desa.sat.gob.gt/xsd/alfa/
Step 2: Create Generation Script​
File: packages/backend/scripts/src/fel/generate-fel-types.ts
Use a library like:
xsd-typescript-generatorquicktype- Or custom parser
Step 3: Generate Types​
pnpm generate:fel-types
Step 4: Use Types for Reference​
- Keep your current
Invoiceinterface - Use generated types as reference
- Validate against generated types
- Update XML builders based on generated types
Step 5: Optional Validation​
import type { FactDocument } from '../generated/fact.types';
function validateInvoice(invoice: Invoice): void {
// Convert to FactDocument structure
const factDoc = mapInvoiceToFactDocument(invoice);
// Validate against XSD-generated types
// (would need runtime validation library)
}
💡 My Recommendation​
Use a Hybrid Approach:
- Download XSD files - Store in
docs/fel/xsd/ - Generate TypeScript types - For reference and validation
- Keep your current XML builders - They work well
- Use generated types as reference - When extending for NCRE/NDEB/ANULACION
- Optional: Add runtime validation - Using generated types
Why?
- Your current approach is clean and works
- Generated types ensure you don't miss fields
- Hybrid gives you flexibility
- Less risk than full code generation
📝 Next Steps​
-
Research TypeScript XSD generators:
- Test
xsd-typescript-generator - Test
quicktype - Evaluate output quality
- Test
-
Download XSD files:
mkdir -p docs/fel/xsd
cd docs/fel/xsd
wget -r --no-parent --accept=xsd https://cat.desa.sat.gob.gt/xsd/alfa/ -
Create generation script:
- Start simple
- Generate basic types
- Iterate based on results
-
Integrate into workflow:
- Add to package.json scripts
- Document in README
- Consider CI/CD integration
🔗 References​
- FELgt Repository - C# XSD code generation example
- SAT XSD Repository - Official schemas
- Official FEL GitHub - SAT's official repo
Last Updated: 2025-12-18
Status: Strategy Document - Ready for Implementation Decision