Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/elfrask/cls/llms.txt

Use this file to discover all available pages before exploring further.

CLS ships two orthogonal module systems that work side-by-side. Source modules (.clsx files) use an export/import mechanism to share symbols across files at the source level, and are the primary tool for day-to-day development. Compiled libraries (.clslib archives) package pre-compiled WASM binaries for distribution, functioning like a .dll or .so for CLS programs. Both systems share the same ModuleResolver abstraction so the runtime stays agnostic about where code comes from.

System A — Source Modules

Exporting Symbols

Any top-level declaration can be made public by prefixing it with export. Declarations without export remain private to the file.
export function doble(x: int) -> int { return x * 2; };
export var VERSION = "1.0.0";
export enum Color { Rojo, Verde, Azul };
export class Contador {
    var valor: int = 0;
    function main(v: int) { me.valor = v; }
    function obtener() -> int { return me.valor; }
};
export structure Par { a: int, b: int };
Exportable declaration kinds: function, var, const, class, enum, structure.

Importing a Full Module

import "path" as alias loads the entire module and binds all its exported symbols under a namespace alias.
import "modules/lib" as lib;

print(lib.VERSION);          # → "1.0.0"
print(lib.doble(4));         # → 8
print(lib.Color.Rojo);       # → "Rojo"

var c = lib.Contador(5);
print(c.obtener());          # → 5

var p = lib.Par(1, 2);
print(p);
The alias is optional. import "lib" — without as — uses the bare path as the namespace name.

Named Imports (from … import)

Pull specific symbols out of a module into the local scope:
from "json" import parse, stringify;

var data = parse('{"key": "value"}');
print(stringify(data));
Rename an individual import with as:
from "math" import sqrt as raiz;
raiz(16);    # → 4.0

Merging a Module into Local Scope (include)

include "lib" imports every exported symbol directly into the current scope without a namespace prefix:
include "math";

print(abs(-10));    # available without prefix
print(sqrt(9));

Module Loading Sequence

When the interpreter encounters an import statement it delegates to the ModuleResolver configured by the host node. The resolution order is:
1

Cache check

If the module has already been loaded this session, return the cached result immediately.
2

Internal modules

Check whether the path matches a built-in module (math, json, async, or node-specific modules such as fs, http, Lib).
3

External hook

Delegate to the node’s file-system hook (reads the .clsx file from disk relative to the working directory, or modules/<pkg>/mod.clsx for installed packages).
4

Error

If none of the above succeed, the runtime raises a module-not-found error.
The resolved source is compiled and executed in an isolated scope (Interpreter::load_module_source). Only symbols marked export are returned to the caller — as a record value.

Multi-Module Type Checking

clx check understands imports. When you run it on a file, the type checker resolves every import and from statement, loads the referenced modules, and passes their exported declarations as a prelude to the type verifier. This means types defined in other files are fully available for annotation:
import "modules/colores" as colores;

var c: Color = colores.Color.Rojo;   # 'Color' resolved from the prelude
Module paths are resolved relative to the directory of the file being checked.

Inline Modules

The module (and namespace) keywords create a named, self-contained scope directly inside a file. The body executes in an isolated environment and its exported symbols are collected into a record bound to the module name.
module Utils {
    export function saludar() -> String { return "hola"; }
    export var version = "1.0";
};

namespace Config {
    var appName = "CLS";
    function build() -> String { return appName; }
};

print(Utils.saludar());    # → "hola"
print(Utils.version);      # → "1.0"
print(Config.appName);     # → "CLS"
print(Config.build());     # → "CLS"
Inline modules are great for grouping utilities inside a single file without splitting them into separate .clsx files. They produce a plain record value, so you can pass or return them like any other value.

System B — Compiled Libraries (.clslib)

.clslib files are zip archives that bundle pre-compiled .clbin binaries (WASM). They are the distribution format for third-party or performance-sensitive libraries — equivalent to a .dll or .so for CLS.
var myLib = Lib.load("./lib.clslib");
Resolution uses the ClsLibResolver (separate from the source ModuleResolver, independently configurable by the host node). The resolver searches:
  1. The current working directory
  2. The $CLS_LIB_PATH environment variable paths
  3. Any additional paths registered by the node
.clslib files ship alongside a .clsapp package — not embedded inside it.
WASM codegen (.clbin) and the full .clslib build pipeline are in active development. The Lib.load API is stable but the underlying WASM execution layer is not yet production-ready.

Built-in Modules

These modules are available in every CLS environment without installation.

math

Mathematical constants and functions: abs, sqrt, pow, min, max, floor, ceil, round, sin, cos, tan, log, random, range, PI, E.
import "math" as math;
print(math.sqrt(16));   # → 4.0
print(math.PI);         # → 3.14159…

json

JSON serialisation and deserialisation.
import "json" as json;
var obj = json.parse('{"a": 1}');
print(json.stringify(obj));   # → '{"a":1}'

async

Async/await primitives. Core module — available everywhere.
async function fetch(url: String) -> String {
    var result = await http.get(url);
    return result;
};

fs / http / Lib

Desktop-only modules (available in the clx/clxr nodes). fs provides filesystem access; http provides HTTP client; Lib provides .clslib loading.
import "fs" as fs;
var data = fs.readFile("app://config.json");

Dependency Management

Packages installed via clx install (or clx add <pkg>) land in the modules/ directory of your project:
my-app/
├── src/
│   └── main.clsx
├── modules/
│   └── colors/
│       └── mod.clsx      ← entry point for the "colors" package
└── cls.json
Import them the same way as any local module — the resolver handles the lookup:
import "modules/colors" as colors;

print(colors.Color.Rojo);
Package metadata lives in cls.json under the "dependencies" key:
{
  "name": "my-app",
  "version": "0.1.0",
  "entry": "src/main.clsx",
  "dependencies": {
    "colors": "^1.0.0"
  }
}
Run clx install to materialise the modules/ directory from the lockfile, or clx add <pkg> to add and install a new package in one step.

Quick Reference

# Full module under alias
import "lib" as lib;
print(lib.doble(4));

# Full module — path as alias (no 'as')
import "lib";
print(lib.doble(4));

# Named imports
from "json" import parse, stringify;

# Named import with rename
from "math" import sqrt as raiz;

# Merge everything into local scope
include "math";
abs(-5);

# Inline module
module Util {
    export function greet() -> String { return "hi"; }
};
print(Util.greet());

# Compiled library
var native = Lib.load("./native.clslib");

Build docs developers (and LLMs) love