Build Applications

Data Lens

Build, test, package, and run the maintained local-first CSV and JSON web application.

Data Lens is a maintained multi-module web application example. It opens user-selected CSV or JSON files, parses and filters rows locally, displays typed summaries, exports text, and persists only view preferences. Imported documents and parsed rows are neither sent to a server nor stored in durable browser state.

Complete First Application before reading this guide. You should already be comfortable with modules, Result, tests, topaz.lock, and the --locked flag. Building the final browser product requires Rust with the wasm32-unknown-unknown target.

Why this is a Web Application

Choose web-app when Topaz owns the model, messages, update function, and view. Choose raw web or web-worker only when an existing JavaScript host owns the UI or worker protocol. The Playground is designed for quick source experiments, not for deploying Data Lens.

The maintained source is organized by responsibility rather than contained in a single entry file:

Output
data-lens/
  topaz.toml
  topaz.lock
  src/main.tpz
  src/model.tpz
  src/session.tpz
  src/view.tpz
  tests/application.tpz
  tests/exporting.tpz
  tests/parsing.tpz
  tests/transform.tpz
  styles/app.css
  registry/lens_labels/1.0.0/...
  vendor/lens_labels/1.0.0/...

data-lens-core/
  topaz.toml
  src/lib.tpz

data-lens-core handles CSV and JSON parsing, filtering, sorting, summaries, and text export. The Web package manages browser messages, local-state encoding, and the view. lens_labels is a locked registry dependency copied into vendor.

Model the data before the UI

The application model keeps source text, controls, typed rows, summary state, and visible errors separate:

TOPAZ
import data_lens_core { DataFormat, DataRow, SortMode, Summary }

export record Model {
  source: string,
  query: string,
  activeOnly: bool,
  format: DataFormat,
  sortMode: SortMode,
  rows: Array<DataRow>,
  summary: Summary,
  status: string,
  error: Option<string>,
}

Parsing returns a Result containing a stable error location such as JSON $[0].category: missing field. The update function stores the message in error, which the view renders as plain text. Source content is never inserted with innerHTML.

The lifecycle initializes with an in-memory sample and requests only saved view preferences:

TOPAZ
export function init() -> WebAppStep<Model, Msg> {
  let base = Model {
    source: CSV_SAMPLE,
    query: "",
    activeOnly: false,
    format: DataFormat.Csv,
    sortMode: SortMode.ByName,
    rows: [],
    summary: emptySummary(),
    status: "Ready",
    error: None,
  }
  WebAppStep {
    model: applyModel(base),
    commands: [loadState("data-lens-session-load", "session", Msg.LocalCompleted)],
  }
}

Opening a document returns bounded UTF-8 text after explicit user selection. Saving a session writes only the query, active-only filter, and sort mode. User cancellation, malformed UTF-8, unavailable storage, quota exhaustion, and corrupt saved state remain typed application outcomes.

Shortest maintained task path

Create the registry input and sibling core package while authoring the application, then run the vendor workflow once. vendor verifies and copies the registry dependency while writing the lock file. Do not run lock first for a registry dependency that has not been vendored.

BASH
topaz vendor --root data-lens --from data-lens/registry
topaz fmt --check --root data-lens
topaz check --root data-lens --locked
topaz test data-lens/tests/parsing.tpz --root data-lens --locked
topaz test data-lens/tests/transform.tpz --root data-lens --locked
topaz test data-lens/tests/application.tpz --root data-lens --locked
topaz test data-lens/tests/exporting.tpz --root data-lens --locked
topaz dev --root data-lens --port 8000
topaz build --root data-lens --locked --release --out-dir data-lens-product

topaz run is the direct execution path for command entries containing main. Because Data Lens follows a Web lifecycle, its observable development command is topaz dev.

In the browser, verify CSV and JSON format handling, Unicode labels and filenames, filtering, both sort modes, text export, preference state management (saving, reloading, and resetting), cancellation, malformed document handling, and recovery after a failed edit. topaz dev continues serving the last successful product until the source is valid again.

Product and run without the Topaz package source

The managed product directory contains:

Output
data-lens-product/
├── GENERATED-OUTPUT-NOTICE.txt
├── LICENSE
├── NOTICE
├── index.html
├── styles
│   └── app.css
├── topaz-app.js
├── topaz-artifact.json
├── topaz-web-capabilities.json
├── topaz-web.d.ts
├── topaz-web.js
└── topaz-web.wasm

Copy the entire directory to a fresh static root and serve it over HTTP. The product does not require the package source, Topaz CLI, registry, npm runtime, CDN, or a data service. If ES modules or WASM fail under file://, use a local static HTTP server. If the build rejects the output directory because a managed file changed or another target owns it, keep the directory intact and choose a new empty --out-dir.