npm package: @mowogames/mdf
Description: MDF (Mowo Data Format) — a typed, human-readable serialization format for game engines
Author: Mowo Games
Latest version: 1.0.2

# @mowogames/mdf

<p align="center">
  <strong>MDF (Mowo Data Format) parser and serializer for JavaScript/TypeScript.</strong>
</p>

<p align="center">
  <img src="https://img.shields.io/badge/TypeScript-5.4-blue?style=flat-square" alt="TypeScript">
  <img src="https://img.shields.io/badge/ESM%20%2B%20CJS-yes-brightgreen?style=flat-square" alt="Dual module">
  <img src="https://img.shields.io/badge/dependencies-zero-green?style=flat-square" alt="Zero deps">
  <img src="https://img.shields.io/badge/license-MIT-yellow?style=flat-square" alt="MIT License">
</p>

---

A typed, human-readable data format designed for game engines. First-class support for `vec3()`, `quat()`, `rgb()`, `hex()`, `uuid()`, asset references, and bare enums.

This is the official JavaScript/TypeScript implementation of [MDF](https://github.com/MowoGames/mdf).

## Install

```bash
npm install @mowogames/mdf
```

## Quick Start

### Parse

```typescript
import { parse } from "@mowogames/mdf";

const doc = parse(`
  Scene "Forest" {
    version: 2
    gravity: vec3(0, -9.81, 0)

    GameObject "Player" {
      active: true
      mesh: @"Assets/Models/Character.fbx"

      Transform {
        position: vec3(0, 1, 0)
        rotation: quat(0, 0.707, 0, 0.707)
      }

      RigidBody {
        type: Dynamic
        mass: 75.0
      }
    }
  }
`);

const root = doc.root();
console.log(root.type);  // "Scene"
console.log(root.name);  // "Forest"

const gravity = root.getVec3("gravity");
console.log(gravity);  // { x: 0, y: -9.81, z: 0 }

const player = root.findChild("GameObject", "Player");
console.log(player.getBool("active"));     // true
console.log(player.getString("mesh"));     // "Assets/Models/Character.fbx"

const pos = player.findChild("Transform").getVec3("position");
console.log(pos);  // { x: 0, y: 1, z: 0 }
```

### Build & Serialize

```typescript
import {
  MdfDocument, MdfInt, MdfBool, MdfString, MdfVec3Val,
  MdfEnum, MdfAssetRef, MdfUUID, stringify
} from "@mowogames/mdf";

const doc = new MdfDocument();
const scene = doc.createRoot("Scene", "MyLevel");
scene.set("version", MdfInt(1));
scene.set("gravity", MdfVec3Val(0, -9.81, 0));

const player = scene.addChild("GameObject", "Player");
player.set("active", MdfBool(true));
player.set("tag", MdfString("Player"));
player.set("id", MdfUUID("550e8400-e29b-41d4-a716-446655440000"));

const transform = player.addChild("Transform");
transform.set("position", MdfVec3Val(0, 1, 0));

const body = player.addChild("RigidBody");
body.set("type", MdfEnum("Dynamic"));

console.log(stringify(doc));
```

Output:

```
Scene "MyLevel" {
    version: 1
    gravity: vec3(0.0, -9.81, 0.0)

    GameObject "Player" {
        active: true
        tag: "Player"
        id: uuid("550e8400-e29b-41d4-a716-446655440000")

        Transform {
            position: vec3(0.0, 1.0, 0.0)
        }

        RigidBody {
            type: Dynamic
        }
    }
}
```

## Value Types

| Type | MDF Syntax | Constructor |
|------|-----------|-------------|
| Null | `null` | `MdfNull()` |
| Bool | `true` / `false` | `MdfBool(true)` |
| Int | `42` | `MdfInt(42)` |
| Float | `3.14` | `MdfFloat(3.14)` |
| String | `"hello"` | `MdfString("hello")` |
| Vec2 | `vec2(1, 2)` | `MdfVec2Val(1, 2)` |
| Vec3 | `vec3(1, 2, 3)` | `MdfVec3Val(1, 2, 3)` |
| Vec4 | `vec4(1, 2, 3, 4)` | `MdfVec4Val(1, 2, 3, 4)` |
| Quat | `quat(0, 0, 0, 1)` | `MdfQuatVal(0, 0, 0, 1)` |
| Color | `rgb(0.5, 0.2, 0.1)` | Parsed as Vec4 with alpha 1.0 |
| Hex | `hex(#FF6600)` | Parsed as normalized Vec4 |
| UUID | `uuid("...")` | `MdfUUID("...")` |
| Asset Ref | `@"path"` | `MdfAssetRef("path")` |
| Enum | `Dynamic` | `MdfEnum("Dynamic")` |
| Array | `[1, 2, 3]` | `MdfArray([...])` |

## API

### `parse(source: string): MdfDocument`

Parse MDF text. Throws on syntax errors.

### `stringify(doc: MdfDocument, indent?: number): string`

Serialize a document to MDF text format.

### `MdfDocument`

```typescript
doc.createRoot(type, name?)  // Create root node
doc.root()                   // Get first root (throws if empty)
doc.roots                    // All root nodes
```

### `MdfNode`

```typescript
node.type                    // Node type identifier
node.name                    // Optional name
node.set(key, value)         // Set property (chainable)
node.get(key)                // Get raw MdfValue
node.has(key)                // Check if property exists
node.properties              // Map<string, MdfValue>

// Typed getters (throw on type mismatch)
node.getBool(key)            // boolean
node.getInt(key)             // number (truncated)
node.getFloat(key)           // number
node.getString(key)          // string (works for String, UUID, AssetRef, Enum)
node.getVec2(key)            // { x, y }
node.getVec3(key)            // { x, y, z }
node.getVec4(key)            // { x, y, z, w }
node.getArray(key)           // MdfValue[]

// Children
node.addChild(type, name?)   // Add child node
node.findChild(type, name?)  // Find first child by type
node.findChildren(type)      // Find all children by type
node.children                // All children
```

## Format Features

- `//` line comments and `/* */` block comments
- Trailing commas in arrays
- Scientific notation (`1e-6`)
- Escape sequences in strings (`\n`, `\t`, `\\`, `\"`)
- Multiple root nodes per document
- Heterogeneous arrays

## Compatibility

Fully compatible with the [C++ MDF library](https://github.com/MowoGames/mdf). Files written by either implementation can be read by the other.

## License

MIT