Build Applications

Application Loop

Choose and complete the native, Python, Web, or bounded 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 one product target instead of building every target in sequence.

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

Raw Web and Worker packages are 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 has the following identity. Save it as topaz.toml:

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

[build]
target = "native"
deterministic = true

[dependencies]
std = "5.11"

The helper module owns 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 turns 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)
}

Create topaz.lock once, then use the same order for normal 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 changing files. A failed check or test stops before a product is written; fix the first Topaz diagnostic and repeat the same command. Use topaz explain TPZ#### when a diagnostic code needs a longer explanation.

Path A: native or Python command application

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

Build only the product you intend to deliver:

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 trees 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] means program on Unix and program.exe on Windows. Copy the complete chosen directory to a fresh location and run from its root:

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

Use the first command in the native product and the second in the Python product; they are alternatives. If the application declares [capabilities.fs], place its data/ and writable output directories beside the product and keep 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 changed managed file, keep it for inspection and build into a new empty directory.

Path B: managed Web Application

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

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 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 is a loopback development server and keeps the last good 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 common license and artifact files. Copy the complete directory to a static host and serve it over HTTP; opening ES modules through file:// is not the deployment path.

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

Path C: bounded 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 concrete 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 the common 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 added.

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