Parser and hierarchical model for FIEBDC-3 BC3 files.
npm install bc3BC3 is an open-source TypeScript library for parsing and modeling FIEBDC-3 / BC3 construction database files.
It provides a structured parsing pipeline and a hierarchical object model representing a BC3 document, allowing you to navigate concepts, decompositions, measurements, and relationships in a type-safe way.
✅ Active development — Core parsing and domain model are functional. API is stable for basic usage.
Current version: v0.6.0
``bash`
npm install bc3
`typescript
import { BC3 } from 'bc3';
// Parse a BC3 file
const bc3Text = ~V|RIB Spain|FIEBDC-3/2020\\02102025|Presto 25.00||...
~C|001010|h|CAPATAZ|29.11|020922|1|
...;
const result = BC3.parse(bc3Text, { mode: 'lenient' });
`
- Full BC3 parsing: Supports all standard BC3 record types (~V, ~C, ~D, ~M, ~T, ~X, ~L, ~A, ~E, ~K)
- Hierarchical model: Navigate concepts as a tree structure with parent-child relationships
- Multiple occurrences: Handle concepts that appear multiple times in different branches
- Decompositions: Access factor, performance (rendimiento), and percentage codes
- Measurements: Parse measurement data with positions, totals, and details
- Metadata: Extract document version, generator, dates, and properties
- Diagnostics: Collect warnings and errors during parsing
Parses a BC3 text input and returns a structured result.
Parameters:
- input (string): Raw BC3 text contentoptions
- (optional):mode
- : 'strict' | 'lenient' (default: 'lenient')
Returns: ParseResult
`typescript`
interface ParseResult {
document?: BC3Document; // The parsed document
diagnostics: Diagnostic[]; // Warnings and errors
}
The main domain model representing a parsed BC3 file.
Key methods:
- getConcept(code: string): Get a concept node by normalized codecountConceptOccurrences(code: string)
- : Count how many times a concept appears in the treegetAllPathsToConcept(code: string)
- : Get all paths from root to a conceptgetPathToConcept(code: string)
- : Get the first path to a conceptgetParentNodes(code: string)
- : Get all parent nodes of a conceptgetChildNodes(code: string)
- : Get direct children of a conceptgetDecompositionInfo(parentCode, childCode)
- : Get decomposition data (performance, factor)getHierarchySummary()
- : Get statistics about the tree structure
Properties:
- roots: ConceptNode[]: Root nodes of the hierarchyconceptsByCode: Map
- : Lookup map for all conceptsmetadata?: DocumentMetadata
- : Document metadata from ~V recordentities: Map
- : Parsed entitiesspecificationsDictionary?: Specification
- : Specifications dictionaryitCodesDictionary?: ITCodes
- : IT codes dictionary
Represents a node in the hierarchical BC3 structure.
Properties:
- concept: Concept: The concept data (code, unit, summary, prices, dates, etc.)children: ConceptNode[]
- : Direct child nodesdecompositions: Decomposition[]
- : Decomposition relationshipsmeasurements: Measurement[]
- : Associated measurementsattachments: Attachment[]
- : Linked attachments
`typescript
const result = BC3.parse(bc3Text);
const document = result.document;
if (document) {
// Walk the tree
document.walkTree((node, depth) => {
const indent = ' '.repeat(depth);
console.log(${indent}${node.concept.codeNorm} - ${node.concept.summary});
});
// Get hierarchy summary
const summary = document.getHierarchySummary();
console.log(Total nodes: ${summary.totalNodes});Max depth: ${summary.maxDepth}
console.log();`
}
`typescriptConcept ${code} appears ${occurrences} times
const code = '001010';
const occurrences = document.countConceptOccurrences(code);
console.log();
// Get all paths to the concept
const paths = document.getAllPathsToConcept(code);
paths.forEach((path, idx) => {
console.log(
Path ${idx + 1}: ${path.map((n) => n.concept.codeNorm).join(' → ')},`
);
});
`typescript
const parentCode = '300100';
const childCode = '001010';
const decompInfo = document.getDecompositionInfo(parentCode, childCode);
if (decompInfo) {
console.log(Performance: ${decompInfo.performance});Factor: ${decompInfo.factor}
console.log();
// Calculate amount
const child = document.getConcept(childCode);
if (child && child.concept.prices.length > 0) {
const price = child.concept.prices[child.concept.prices.length - 1];
const amount = price * (decompInfo.performance || 0);
console.log(Amount: ${formatPrice(amount)});`
}
}
- Continues parsing even when encountering errors
- Collects diagnostics (warnings/errors) but doesn't throw
- Best for production use when you want to handle partial data
- Stops parsing on errors
- Useful for validation and debugging
- Throws structured exceptions with diagnostics
The core architecture and design decisions are documented in:
- Grammar rules
BC3 lexical rules: record delimiters, fields, subfields, and whitespace handling.
- Domain model
Core entities: BC3Document, Concept, Decomposition, Measurement, attachments.
- Parsing modes
Strict vs lenient parsing, warnings, errors, and diagnostics strategy.
- Design patterns
Builder, Strategy, and Composite responsibilities and interactions.
- Module boundaries
Folder structure, importers, parsing pipeline, and dependency rules.
- Public API
Public API contract, sync/async decisions, inputs, outputs, and extensibility.
- Development happens on developmain`
- Each change is tracked via GitHub Issues and GitHub Projects
- Versioning and releases are managed with Changesets
- Publishing to npm is automated on merges to
- High-level roadmap and task breakdown are maintained in GitHub Projects
- Each architectural decision is documented before implementation
MIT © Igor HC