stof 0.9.16

Data that carries its own logic.
Documentation

Stof: Data + Logic, Anywhere

Standard Transformation and Organization Format

Overview

Data and logic have always been separate. That makes things hard. Stof puts them together.

A portable document format where validation, functions, and behavior live alongside the data they belong to — in one document, across any service, language, or runtime.

  • Superset of JSON — valid JSON is always valid Stof. Works with YAML, TOML, and more out of the box.
  • Sandboxed execution — logic runs in a secure, isolated runtime. Safe to execute untrusted code from external sources.
  • Built in Rust, runs everywhere — native crate, WebAssembly for JS/TS (Node, Deno, Bun, browser), and Python bindings via PyPI.

Used in production: Limitr's pricing policy engine — plans, credits, limits, validation logic — runs entirely on Stof.

Data That Does Things

Stof starts where JSON ends. Add functions right next to the data they operate on.

import { stofAsync } from '@formata/stof';

const doc = await stofAsync`
    name: "Alice"
    age: 30
    
    fn greet() -> str {
        'Hello, ' + self.name + '!'
    }
    
    fn can_rent_car() -> bool {
        self.age >= 25
    }
`;

console.log(await doc.call('greet'));        // Hello, Alice!
console.log(await doc.call('can_rent_car')); // true

No separate schema file. No external validator. The data knows its own rules.

Units & Types

Rich type system with automatic unit conversions — time, distance, memory, temperature, and more.

import { stofAsync } from '@formata/stof';

const doc = await stofAsync`
#[type]
Point: {
    meters x: 0
    meters y: 0
    
    fn dist(other: Point) -> m {
        Num.sqrt((other.x - self.x).pow(2) + (other.y - self.y).pow(2))
    }
}

Point reference: {
    x: 1ft
    y: 2ft
}

fn distance(imported_json: str) -> inches {
    const imported = new {};
    parse(imported_json, imported, 'json');
    (self.reference.dist(imported) as inches).round(2)
}
`;

const dist = await doc.call('distance', '{ "x": 3, "y": 4 }');
console.log(dist); // 170.52

Format Interop

Combine JSON, YAML, TOML, and Stof in a single document. Parse one format, transform it, export as another.

import { stofAsync } from '@formata/stof';

const doc = await stofAsync`{
    json: '{"plans":{"pro":{"label":"Pro","price":{"amount":20},"entitlements":{"ai_chat":{"description":"AI Chat Feature","limit":{"credit":"chat-token","value":100000,"resets":true,"reset_inc":1.0}}}}}}'
    yaml: ''
    
    fn transform() {
        const policy = new {};
        parse(self.json, policy, 'json');
        
        policy.plans.pro.price.amount = 50;
        const entitlements = policy.plans.pro.entitlements;
        entitlements.ai_chat.limit.value *= 2;
        
        self.yaml = stringify('yaml', policy);
        Std.pln(self.yaml);
    }
}`;
doc.lib('Std', 'pln', (...args: unknown[])=>console.log(...args));

await doc.call('transform');
plans:
  pro:
    label: Pro
    price:
      amount: 50
    entitlements:
      ai_chat:
        description: AI Chat Feature
        limit:
          credit: chat-token
          value: 200000
          resets: true
          reset_inc: 1.0

Self-Expanding Contexts

This is the capability that changes everything.

Stof documents can parse new Stof into themselves at runtime — receiving code over the network and immediately executing it. The program grows while it runs, always sandboxed.

import { stofAsync } from '@formata/stof';

const doc = await stofAsync`
api: {}

fn load_api(stof: str) {
    parse(stof, self.api);
}`;

// Imagine this arriving over HTTP, from another service, or from an agent
const api = `
name: 'Stof'
fn message() -> str { 'Hello, ' + self.name ?? 'World' + '!!' }

#[main]
fn main() {
    pln(self.message());
}`;

doc.lib('Std', 'pln', (...args: unknown[])=>console.log(...args));
await doc.call('load_api', api);
await doc.run(); // calls #[main] funcs

// Hello, Stof

CLI

See installation docs for CLI instructions and more information.

#[main]
fn say_hi() {
    pln("Hello, world!");
}
> stof run example.stof
Hello, world!

Embedded

Stof is written in Rust, but use it where you work. Join the project Discord to contribute.

Rust

[dependencies]

stof = "0.9.*"

use stof::model::Graph;

fn main() {
    let mut graph = Graph::default();
    
    graph.parse_stof_src(r#"
        #[main]
        fn main() {
            pln("Hello, world!");
        }
    "#, None).unwrap();

    match graph.run(None, true) {
        Ok(res) => println!("{res}"),
        Err(err) => panic!("{err}"),
    }
}

Python

pip install stof

from pystof import Doc

STOF = """
#[main]
fn main() {
    const name = Example.name('Stof,', 'with Python');
    pln(`Hello, ${name}!!`)
}
"""

def name(first, last):
    return first + ' ' + last

def main():
    doc = Doc()
    doc.lib('Example', 'name', name)
    doc.parse(STOF)
    doc.run()

if __name__ == "__main__":
    main()

# Output:
# Hello, Stof, with Python!!

JavaScript/TypeScript

npm i @formata/stof

Initialization

Stof uses WebAssembly, so make sure to initialize it once.

// Node.js, Deno, & Bun - Auto-detects and loads WASM
import { initStof } from '@formata/stof';
await initStof();

// Vite
import { initStof } from '@formata/stof';
import stofWasm from '@formata/stof/wasm?url';
await initStof(stofWasm);

// Browser with bundler - Pass WASM explicitly (e.g. @rollup/plugin-wasm)
import { initStof } from '@formata/stof';
import stofWasm from '@formata/stof/wasm';
await initStof(await stofWasm());

Usage

import { initStof, StofDoc } from '@formata/stof';

await initStof();

const doc = new StofDoc();
doc.parse(`
    name: "Alice"
    age: 30
    fn greet() -> str {
        'Hello, ' + self.name
    }
`);

const greeting = await doc.call('greet');
console.log(greeting); // "Hello, Alice"
console.log(doc.get('age')); // 30

JavaScript Interop

await initStof();
const doc = new StofDoc();

doc.lib('console', 'log', (...args: unknown[]) => console.log(...args));
doc.lib('fetch', 'get', async (url: string) => {
    const res = await fetch(url);
    return await res.json();
}, true); // true = async function

doc.parse(`
    fn main() {
        const data = await fetch.get("https://api.example.com/data");
        console.log(data);
    }
`);

await doc.call('main');

Parse & Export

doc.parse({ name: "Bob", age: 25 });

const json = doc.stringify('json');
const obj = doc.record();

Supports: Node.js, Browser, Deno, Bun, Edge runtimes

License

Apache 2.0. See LICENSE for details.

Feedback & Community

  • Open issues or discussions on GitHub
  • Chat with us on Discord
  • Star the project to support future development!

Reach out to info@stof.dev to contact us directly