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