Skip to content

step serializer

Maarten Vroegindeweij edited this page Feb 18, 2026 · 1 revision

@ifc-factory/step-serializer

Layer 0 — Zero dependencies. Low-level STEP physical file (ISO 10303-21) tokenizer, reader, and writer. Completely schema-agnostic.

Installation

pnpm add @ifc-factory/step-serializer

Usage

Read a STEP File

import { 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)`);
}

Write a STEP File

import { writeStepFile } from '@ifc-factory/step-serializer';
import { writeFileSync } from 'node:fs';

const output = writeStepFile(stepFile);
writeFileSync('output.ifc', output);

Round-Trip

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.ifc

Inspect Individual Entities

const 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)';
}

API

readStepFile(source: string): StepFile

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

writeStepFile(stepFile: StepFile): string

Serializes a StepFile object back to a STEP format string.

Parameters:

  • stepFile — A StepFile object

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)

Types

StepFile

interface StepFile {
  header: StepHeader;
  entities: Map<number, StepEntityInstance>;
}

StepHeader

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[];
  };
}

StepEntityInstance

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
}

StepValue (union type)

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)

Value Type Details

// 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;
}

Tokenizer

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 Types

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

Unicode Handling

STEP files encode non-ASCII characters using escape sequences:

Encoding Types

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 é

Usage

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\\"

Error Handling

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}`);
  }
}

See Also

Clone this wiki locally