Libraries
Libraries are the functions a document can call that aren't part of the document itself: printing, string and list helpers, time, encryption, and so on. They're grouped by name, and you call them with that name: Time::now(), Num::abs(-2).
Calling Library Functions
There are three ways to call a library function, and they're all the same call:
#[main]
fn main() {
const name = 'stof';
pln(Str::upper(name)); // the library, then the function
pln(name.upper()); // as a method: the value's type picks the library (Str)
pln(Str.upper(name)); // a dot works too, unless a field or variable is named Str
}These docs use Lib::func() for calling a library directly, and the method form on values. :: always means the library, so it can't be confused with a field or variable of the same name, and it reads as a library call at a glance. The method form works for every type library: a string's methods come from Str, a list's from List, an object's from Obj, and so on. Std functions don't need a name at all, so pln(..) is Std::pln(..).
Lib?::func() returns null instead of throwing when the library or function doesn't exist, which is handy for optional host libraries:
#[main]
fn main() {
const sent = App?::notify('hello'); // null: this host has no App library
pln(sent ?? 'no App library here');
}What's Built In
| Library | What it's for | Available in |
|---|---|---|
| Std | Printing, assertions, parsing and exporting, collections, introspection | Everywhere |
| Str, Num, List, Map, Set, Tup | Methods on strings, numbers, and collections | Everywhere |
| Obj, Fn, Data | Working with objects, functions, and data in the document graph | Everywhere |
| Blob, Ver, Prompt | Bytes, semantic versions, and prompt trees for AI contexts | Everywhere |
| Time | Timestamps, durations, and dates | Everywhere |
| Md | Markdown to HTML or JSON | Everywhere |
| Age | Encrypting parts of a document | Everywhere |
| Http | Network requests | When the host turns it on: JavaScript, Python, Rust (http feature), CLI |
| fs | Reading and writing files | When the host turns it on: Python, Rust, CLI |
| Image | Loading, editing, and exporting images | Python, Rust (image feature), CLI |
| Text and images from PDFs | Python, Rust (pdf feature), CLI |
"Everywhere" means the JavaScript package (Node.js and browsers), Python, Rust, and the stof CLI. The runnable examples on this site use the JavaScript package.
What a Document Can Reach
A document can always read and change itself and call the libraries above that are marked "Everywhere". Nothing on the outside, though: the file system, environment variables, importing files from disk, and the network are all off until the host turns them on:
| Access | JavaScript | Python | Rust |
|---|---|---|---|
Network (Http) | doc.allowHttp(['api.example.com']) | doc.allow_http_hosts([...]) | graph.allow_http_hosts([...]) |
| Network, any host | doc.allowHttp() | doc.allow_http() | graph.allow_http() |
| Files, environment, file imports | not available | doc.allow_system() | graph.allow_system() |
The stof CLI turns everything on, since it runs your own files. Each call also has a time limit, 2 minutes by default, which the host can change: doc.setMaxExecutionTime(ms) (JavaScript), doc.set_max_execution_time(seconds) (Python), graph.max_execution_time (Rust).
Adding Your Own
The host can add its own library functions, and that's the usual way to give a document access to something specific: your database, your notification service, your app's state. A document can call those and nothing else it wasn't given.
import { StofDoc } from '@formata/stof';
const doc = await StofDoc.new(`
#[main]
fn main() {
App::notify('order shipped');
}
`);
doc.lib('App', 'notify', (message: string) => console.log('notify:', message));
await doc.run();
from pystof import Doc
doc = Doc()
doc.lib('App', 'notify', lambda message: print('notify:', message))
In Rust, a library function is a LibFunc inserted with graph.insert_libfunc(..).