Specification · v1

API and CLI

ulang-lite is the reference implementation of v1: packages/ulang-lite in the µLang Git repository. It is MIT licensed, ES modules, TypeScript using only erasable syntax, with zero runtime dependencies. It runs unchanged in Cloudflare Workers, Node.js 22.18 and later (native type stripping) and browsers.

1. Getting it

ulang-lite is not on the npm registry. Get it from the repository:

git clone https://github.com/ThinkTechHoldings/ulang-git
cd ulang-git/packages/ulang-lite
node bin/ulang-lite.ts check ../../.forge/rules/ForgeClaim.ul

To use it from your own project, vendor packages/ulang-lite (for example at vendor/ulang-lite) and import src/index.ts on Node 22.18 or later. Node does not strip types from files under node_modules, so if you install it as a package, run npm run build first: that emits plain JavaScript to dist/, which is what the package's main export points at. Bundlers for Workers and browsers can take either.

2. Library

import { compileSource, checkFacts, contractDigest } from './vendor/ulang-lite/src/index.ts';

const result = compileSource(source);       // bounds scan → parse → subset → validate → compile
if (!result.ok) {
  const d = result.diagnostic;              // { kind, code, message, offset, line, column }
  throw new Error(`${d.line}:${d.column}: ${d.kind}[${d.code}] ${d.message}`);
}

const contract = result.contracts[0];       // CompiledContract
const valid = result.valid[0];              // ValidContract: AST plus resolved scope

const facts = JSON.parse(body);             // { "ClaimRequest": { … } }
const problem = checkFacts(valid, facts);   // null, or a message
if (problem) throw new Error(problem);

const decision = contract.decide('grant_claim', facts[contract.entity]);
// { verdict: 'DENY', clause_id: 'ForgeClaim.claim_overlap' }

const digest = contractDigest(valid.contract);   // SHA-256 of formatContract(valid.contract)
ExportPurpose
compileSource(source, options?)everything in one call. Returns { ok, contracts, valid } or { ok: false, diagnostic }
parseSource(source, options?) / parsebounds scan and parse only, without or with throwing
validateFile(file, options?) / validateContractsubset, structure and validation
compileContract(valid)build the decide function
checkFacts(valid, facts)check { "<Entity>": {…} } against the entity
formatContract / formatFilecanonical text
canonicalJson(contract)canonical JSON
contractDigest(contract)SHA-256 of the canonical text
sha256Hex, Sha256synchronous FIPS 180-4 SHA-256
scanBounds, resolveLimits, DEFAULT_LIMITS, HARD_LIMITSlimits
formatDiagnostic(d, file?), UlangErrordiagnostics
approxEqual(a, b)the numeric equality used by ==

3. Validation options

OptionDefaultEffect
limitsdefaultslower any limit for this call (but see errata E2 for condition depth)
requireEnforcedtruerefuse modes other than enforced
entitythe contract's entitythe entity name the contract must use
fieldsthe declared fieldsthe exact field set, as name → primitive, the entity must declare
actionsthe triggered actionsthe exact action set TRIGGER must list
maxConditionDepth16condition depth for this call. Keep it between 1 and 256 (errata E2).

entity, fields and actions pin a contract to an application's schema, which is how the µLang Git forge makes sure a contract governs exactly the actions and facts its API produces.

4. Command line

ulang-lite check FILE...                           parse and validate; one line per contract, with its SHA-256
ulang-lite eval FILE... --action A --facts F.json  decide action A; facts are {"<Entity>": {...}}
ulang-lite format FILE                             print the canonical text

Exit status: 0 success, 1 a file or the facts were rejected, 2 usage error.

These are real runs of the released command line, in the example project, made when this site was built. The command-line reference has more.

example projectreal output
$ ./ulang-lite check policy/deploy.ul
policy/deploy.ul: ok ProductionDeploy (6 clauses; actions deploy, rollback; sha256 686358ddc154b77b5b7b3a24870b8dd6f79b23d376ed039d6824a923284e4059)
example projectreal output
$ ./ulang-lite eval policy/deploy.ul --action deploy --facts facts/freeze.json
{"verdict":"DENY","clause_id":"ProductionDeploy.change_freeze","contract_sha256":"686358ddc154b77b5b7b3a24870b8dd6f79b23d376ed039d6824a923284e4059"}
example projectreal output
$ ./ulang-lite check scratch/typo.ul
scratch/typo.ul:29:24: unsupported[unknown-field]: unknown field DeployRequest.error_rate
[exit status 1]

5. Conformance

Another implementation conforms to v1 when, for every source and every action and facts, it gives the same:

The test vectors are a starting set, produced by ulang-lite.