-
Notifications
You must be signed in to change notification settings - Fork 1
step serializer
Maarten Vroegindeweij edited this page Feb 18, 2026
·
1 revision
Layer 0 — Zero dependencies. Low-level STEP physical file (ISO 10303-21) tokenizer, reader, and writer. Completely schema-agnostic.
pnpm add @ifc-factory/step-serializerimport { readStepFile } from '@ifc-factory/step-serializer';
import { readFileSync } from 'node:fs';
const source = readFileSync('model.ifc', 'utf-8');
const stepFile = readStepFile(source);
// Header information
console.log('Description:', stepFile.header.fileDescription.description);
console.log('File name:', stepFile.header.fileName.name);
console.log('Timestamp:', stepFile.header.fileName.timestamp);
console.log('Author:', stepFile.header.fileName.author);
console.log('Schema:', stepFile.header.fileSchema.schemas);
// Entity count
console.log(`Entities: ${stepFile.entities.size}`);
// Iterate entities
for (const [id, entity] of stepFile.entities) {
console.log(`#${id} = ${entity.typeName}(${entity.attributes.length} attrs)`);
}import { writeStepFile } from '@ifc-factory/step-serializer';
import { writeFileSync } from 'node:fs';
const output = writeStepFile(stepFile);
writeFileSync('output.ifc', output);const source = readFileSync('input.ifc', 'utf-8');
const stepFile = readStepFile(source);
const output = writeStepFile(stepFile);
writeFileSync('output.ifc', output);
// output.ifc is semantically equivalent to input.ifcconst entity = stepFile.entities.get(42);
if (entity) {
console.log(`Type: ${entity.typeName}`);
console.log(`ID: #${entity.id}`);
console.log('Attributes:');
for (let i = 0; i < entity.attributes.length; i++) {
const attr = entity.attributes[i];
console.log(` [${i}] ${describeValue(attr)}`);
}
}
function describeValue(val: StepValue): string {
if (val === null) return '$ (omitted)';
if (typeof val === 'number') return `${val} (number)`;
if (typeof val === 'string') return `'${val}' (string)`;
if (typeof val === 'boolean') return `.${val ? 'T' : 'F'}. (boolean)`;
if ('ref' in val) return `#${val.ref} (entity ref)`;
if ('enum' in val) return `.${val.enum}. (enum)`;
if ('typeName' in val) return `${val.typeName}(${describeValue(val.value)}) (typed)`;
if (Array.isArray(val)) return `(${val.map(describeValue).join(', ')}) (list)`;
return '* (derived)';
}Parses a STEP physical file string into a structured StepFile object.
Parameters:
-
source— The full STEP file content as a string
Returns: StepFile with parsed header and entity map
Throws: StepParseError on malformed input
Serializes a StepFile object back to a STEP format string.
Parameters:
-
stepFile— AStepFileobject
Returns: A valid ISO 10303-21 string
Behavior:
- Entities are sorted by ID for deterministic output
- Header entities are written in the standard order
- Values are properly escaped (strings, unicode)
interface StepFile {
header: StepHeader;
entities: Map<number, StepEntityInstance>;
}interface StepHeader {
fileDescription: {
description: string[];
implementationLevel: string;
};
fileName: {
name: string;
timestamp: string;
author: string[];
organization: string[];
preprocessorVersion: string;
originatingSystem: string;
authorization: string;
};
fileSchema: {
schemas: string[];
};
}interface StepEntityInstance {
id: number; // The express ID (e.g., 42 for #42)
typeName: string; // The entity type (e.g., "IFCWALL")
attributes: StepValue[]; // Positional attribute values
}The fundamental value type in STEP files:
type StepValue =
| number // integers (42) and reals (3.14)
| string // 'single-quoted strings'
| boolean // .T. / .F.
| null // $ (omitted value)
| StepDerived // * (derived value)
| StepEntityRef // #123 (entity reference)
| StepEnum // .ELEMENT. (enumeration)
| StepTypedValue // IFCLABEL('text') (typed value)
| StepValue[]; // (1, 2, 3) (list/tuple)// Entity reference
interface StepEntityRef {
ref: number; // The referenced entity ID
}
// Enumeration value
interface StepEnum {
enum: string; // The enum value without dots (e.g., "SOLIDWALL")
}
// Typed value (wrapper)
interface StepTypedValue {
typeName: string; // e.g., "IFCLABEL"
value: StepValue; // The wrapped value
}
// Derived marker
interface StepDerived {
derived: true;
}The tokenizer is generator-based for memory efficiency:
import { tokenize } from '@ifc-factory/step-serializer';
const tokens = tokenize(source);
// Yields tokens one at a time — suitable for large files| Token | Example | Description |
|---|---|---|
ISO_TAG |
ISO-10303-21; |
File start marker |
END_ISO_TAG |
END-ISO-10303-21; |
File end marker |
KEYWORD |
HEADER, DATA, ENDSEC
|
Section keywords |
IDENT |
IFCWALL, FILE_NAME
|
Identifiers |
INTEGER |
42, -5
|
Integer literals |
REAL |
3.14, 1.0E-6
|
Real number literals |
STRING |
'Hello' |
String literals (decoded) |
ENUM |
.SOLIDWALL. |
Enumeration values |
ENTITY_REF |
#123 |
Entity references |
BOOLEAN |
.T., .F.
|
Boolean values |
UNKNOWN |
.U. |
Unknown/logical value |
OMITTED |
$ |
Omitted attribute |
DERIVED |
* |
Derived attribute |
LPAREN |
( |
List/tuple start |
RPAREN |
) |
List/tuple end |
COMMA |
, |
Separator |
SEMICOLON |
; |
Statement end |
EQUALS |
= |
Assignment |
HASH |
# |
Entity ref prefix |
STEP files encode non-ASCII characters using escape sequences:
| Escape | Encoding | Example | Result |
|---|---|---|---|
\X\HH |
ISO 8859-1 (1 byte) | \X\E9 |
é |
\X2\HHHH\X0\ |
UCS-2 / BMP (2 bytes) | \X2\00E9\X0\ |
é |
\X4\HHHHHHHH\X0\ |
UCS-4 / full Unicode (4 bytes) | \X4\0001F600\X0\ |
😀 |
\S\c |
ISO 8859 high-bit | \S\e |
é |
import { decodeStepString, encodeStepString } from '@ifc-factory/step-serializer';
// Decode STEP-encoded string to JavaScript string
const decoded = decodeStepString("Caf\\X\\E9"); // "Café"
// Encode JavaScript string to STEP encoding
const encoded = encodeStepString("Café"); // "Caf\\X2\\00E9\\X0\\"import { readStepFile, StepParseError } from '@ifc-factory/step-serializer';
try {
const stepFile = readStepFile(source);
} catch (error) {
if (error instanceof StepParseError) {
console.log(`Parse error: ${error.message}`);
}
}- STEP File Format — Detailed explanation of the STEP format
- step-parser — Schema-aware layer on top of step-serializer
- Architecture — Two-pass parsing architecture
Ifc-Factory
Getting Started
Concepts
Packages
API & Reference
Development