Data Lens is the maintained multi-module Web Application example. It opens CSV or JSON chosen by the user, parses and filters the rows locally, shows typed summaries, exports text, and persists only view preferences. Imported documents and parsed rows are not sent to a server or stored as durable browser state.
Complete First Application before this
guide. You should already be comfortable with modules, Result, tests,
topaz.lock, and --locked. Building the final browser product also 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 for quick source experiments,
not for deploying Data Lens.
The maintained source is divided by responsibility rather than placed in one entry file:
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.tpzdata-lens-core owns CSV and JSON parsing, filtering, sorting, summaries, and
export. The Web package owns 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, and a visible error separate:
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 Result with a stable location such as
JSON $[0].category: missing field. The update function stores the message in
error; the view renders it as text. Source content is never inserted with
innerHTML.
The lifecycle starts with an in-memory sample and requests only the saved view preferences:
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 an explicit user choice. Saving a session writes only the query, active-only filter, and sort mode. 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 the sibling core package while authoring the
application. Then vendor once. vendor verifies and copies the registry
dependency and writes the lock; do not run lock first for a registry
dependency that has not been vendored.
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 path for a command entry with main; Data Lens has a
Web lifecycle, so its observable development command is topaz dev.
In the browser, verify both CSV and JSON, Unicode labels and filenames,
filtering, both sort modes, text export, save/reload/forget preference state,
cancellation, one malformed document, and recovery after a failed edit.
topaz dev keeps serving the last successful product until the source is
valid again.
Product and run without the Topaz package source
The managed product contains:
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.wasmCopy the entire directory to a fresh static root and serve it over HTTP. The
product does not need 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.