Build Applications

Application Loop

Select and complete the native, Python, Web, or HTTP path that matches your delivery target.

Use this page after First Application. The authoring loop is shared, but the delivery loop is not. Choose a single product target instead of building every target sequentially.

  • Choose native for a single platform-specific executable with no language runtime at deployment.
  • Choose Python when Python 3.11 or newer is already present in the deployment environment.
  • Choose Web Application when Topaz manages the browser model, messages, update function, and view.
  • Choose HTTP service when a checked request handler runs behind a process supervisor and, for public traffic, a reverse proxy.

Raw Web and Worker packages serve as integration products for an existing JavaScript host. They are covered in WASM & Playground.

Shared authoring loop

Start from a checked two-module package. A current native package manifest uses the following layout. Save it as topaz.toml:

TOML
[package]
name = "task_summary"
version = "0.1.0"
language = "5.19"
entry = "src/main.tpz"

[build]
target = "native"
deterministic = true

[dependencies]
std = "5.19"

The helper module handles the application calculation:

TOPAZ
export function remainingMinutes(values: Array<int>) -> int {
  let mut total = 0
  for value in values {
    total += value
  }
  total
}

The entry module converts the result into an observable command:

TOPAZ
import src.summary { remainingMinutes }

export function main(args: Array<string>, stdin: string) -> Result<int, string> {
  let minutes = remainingMinutes([10, 25])
  print("remaining={minutes}")
  Ok(0)
}

Generate topaz.lock once, then use the same sequence for routine work:

BASH
topaz lock --root task-summary
topaz fmt --check --root task-summary
topaz check --root task-summary --locked
topaz test --root task-summary --locked
topaz run --root task-summary --locked

fmt --check reports formatting drift without modifying files. A failed check or test halts before a product artifact is written. Fix the first Topaz diagnostic and repeat the command. Use topaz explain TPZ#### when a diagnostic code requires a detailed explanation.

Path A: native or Python command application

Native builds require a Rust toolchain for the target operating system and architecture. Python builds do not require Rust, but the resulting product requires Python 3.11 or newer.

Build only the product intended for delivery:

BASH
topaz build --release --root task-summary --locked --out-dir native-product
topaz build --target python --root task-summary --locked --out-dir python-product

The release tree structures are:

Output
native-product/
├── GENERATED-OUTPUT-NOTICE.txt
├── LICENSE
├── NOTICE
├── target
│   └── release
│       └── program[.exe]
└── topaz-artifact.json

python-product/
├── GENERATED-OUTPUT-NOTICE.txt
├── LICENSE
├── NOTICE
├── program.py
├── topaz-artifact.json
└── topaz_py_rt.py

program[.exe] specifies program on Unix and program.exe on Windows. Copy the selected directory completely to a new location and run from its root:

BASH
./target/release/program
python3 program.py

Use the first command for native products and the second for Python products. They are alternative paths. If the application declares [capabilities.fs], place its data/ and writable output directories alongside the product, using that directory as the working directory. Application data is not added to native or Python products automatically.

If a build reports a missing Rust toolchain, either install the matching Rust toolchain or choose Python. If an output directory contains another target or a modified managed file, preserve it for inspection and build into a new empty directory.

Path B: managed Web Application

A Web Application build requires Rust configured with the wasm32-unknown-unknown target. Create the checked scaffold instead of converting the command entry point:

BASH
topaz init --target web-app --root hello-web
topaz lock --root hello-web
topaz fmt --check --root hello-web
topaz check --root hello-web --locked
topaz test hello-web/tests/app.tpz --root hello-web --locked
topaz dev --root hello-web --port 8000
topaz build --root hello-web --locked --release --out-dir web-product

The generated entry point uses the checked lifecycle:

TOPAZ
import std.dom { Html, WebAppEvent, WebAppStep, text }

export record Model {
  message: string,
}

export enum Msg {
  Ready,
}

export function init() -> WebAppStep<Model, Msg> {
  WebAppStep { model: Model { message: "Hello from Topaz" }, commands: [] }
}

export function update(model: Model, message: Msg, event: WebAppEvent) -> WebAppStep<Model, Msg> {
  WebAppStep { model: model, commands: [] }
}

export function view(model: Model) -> Html<Msg> {
  text(model.message)
}

topaz dev runs a loopback development server and retains the last valid product after a failed edit. It is not a production server. The final directory contains HTML, the checked application host, the raw Web facade, WASM, capability metadata, declared styles and assets, and the standard license and artifact files. Copy the complete directory to a static host and serve it over HTTP. Opening ES modules through file:// is not a valid deployment path.

Data Lens is the maintained multi-module example for this path.

Path C: HTTP service

Create a service scaffold when the product is a request handler rather than a command or browser UI:

BASH
topaz init --target http-service --root hello-service
topaz lock --root hello-service
topaz fmt --check --root hello-service
topaz check --root hello-service --locked
topaz test --root hello-service --locked
topaz dev --root hello-service --port 8080
topaz build --root hello-service --locked --release --out-dir service-product

The handler boundary is explicit and synchronous:

TOPAZ
import std.http { HttpRequest, HttpResponse, text }

export function handle(req: HttpRequest) -> HttpResponse {
  if req.url.path() == "/health" {
    return text(200, "ok")
  }
  text(404, "not found")
}

The service product contains one native executable, embedded bounded defaults, third-party notices, and standard artifact files. It binds to loopback by default. A non-loopback bind requires an explicit process argument. TLS, HTTP/2, outbound networking, and a general Web framework are not included.

HTTP Service completes this path, including budgets, observable failures, source-free execution, and deployment recovery.