Data Lens is the full Web Application Loop in one maintained application. It parses JSON and CSV in the browser, keeps rows and summaries in nominal types, normalizes multilingual labels through a locked package dependency, and renders filtering, sorting, selection, errors, and keyboard interaction without sending the dataset to a server. Current Topaz provides this workflow, including explicit user-selected file input and text export.
Choose the application product
Use web-app when Topaz owns application state, events, updates, and the view.
The compiler checks one init/update/view lifecycle and emits a complete
static product with a bounded safe DOM host. Use raw web or web-worker
instead when an existing JavaScript host owns the UI or worker protocol and
calls selected typed Topaz exports. The Playground is a compiler sandbox; it
is not either deployment model.
Data Lens uses web-app because its model and interactions belong in Topaz.
Package shape
Start with the installed scaffold, then split parsing, transformation, model, view, and lifecycle code into modules:
topaz init --target web-app --root data-lens
mkdir -p data-lens/src data-lens/tests registry/lens_labels/1.0.0/src
The application manifest declares the effective target, managed stylesheet, and one local dependency:
[package]
name = "data_lens"
version = "0.1.0"
language = "5.8"
entry = "src/main.tpz"
[build]
target = "web-app"
deterministic = true
[web]
title = "Topaz Data Lens"
styles = ["styles/app.css"]
assets = []
lifecycle = "v2"
[capabilities.web]
open_text = true
download_text = true
local_state = true
[dependencies]
std = "5.8"
lens_labels = "1.0.0"
Keep the application in seven source modules:
src/main.tpz lifecycle and messages
src/model.tpz nominal rows, summary, model, and message enum
src/session.tpz bounded JSON preferences for durable local state
src/parsing.tpz JSON/CSV decoding and deterministic data-path errors
src/transform.tpz filtering, sorting, and aggregates
src/exporting.tpz deterministic UTF-8 CSV/JSON output
src/view.tpz typed Html<Msg> constructionThe model makes state transitions explicit:
export record DataRow {
name: string,
category: string,
value: int,
active: bool,
}
export record Summary {
totalRows: int,
activeRows: int,
totalValue: int,
}
export enum SortMode { ByName, ByValue }
export record Model {
source: string,
query: string,
activeOnly: bool,
sortMode: SortMode,
rows: Array<DataRow>,
summary: Summary,
status: string,
error: Option<string>,
}The entry exports lifecycle v2. Browser details and local-data completions
arrive through WebAppEvent; application messages remain nominal values:
export function init() -> WebAppStep<Model, Msg> {
WebAppStep { model: initialModel(), commands: [loadState("data-lens-session-load", "session", Msg.LocalCompleted)] }
}
export function update(model: Model, message: Msg, event: WebAppEvent) -> WebAppStep<Model, Msg> {
match message {
case OpenDocument => step(model, [openText("data-lens-open", ".csv,.json,text/csv,application/json", Msg.LocalCompleted)])
case SaveSession => saveSession(model)
case ForgetSession => step(model, [deleteState("data-lens-session-delete", "session", Msg.LocalCompleted)])
case LocalCompleted => {
match event {
case LocalData(local) => completeLocalData(model, local)
case LocalState(local) => completeLocalState(model, local)
case Browser(_) => step(model, [])
}
}
case DownloadResults => downloadResults(model)
case Apply | Submit => step(applyModel(model), [])
case Clear => step(clearModel(), [dom(focus("#source"))])
case _ => updateFromBrowser(model, message, event)
}
}
export function view(model: Model) -> Html<Msg> {
render(model)
}Parsing returns Result with stable paths such as
JSON $[0].category: missing field. The update stores that error in the model;
the view renders it as text. Do not catch it in JavaScript or inject source
through innerHTML.
OpenText supplies only a bounded UTF-8 document name, media type, byte count,
and text after the user chooses it. It never supplies a path or reusable file
handle. DownloadText reports DownloadStarted after browser dispatch; the
application does not claim that the user completed a physical save.
Session persistence is deliberately narrower than the model. Save session writes only the query, active-only flag, and sort mode through saveState; reload restores those preferences through loadState. Source text, parsed rows, summaries, filenames, and errors are never persisted. Forget session uses deleteState, while corrupt, denied, quota, and unavailable storage results are rendered as typed application status instead of being hidden by the host.
Lock, check, and test
Populate the local lens_labels registry package, then vendor it once. All
later commands use the lockfile and vendored bytes:
topaz vendor --root data-lens --from registry
rm -rf registry
topaz check --root data-lens --locked
topaz fmt --root data-lens --check
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 doc --root data-lens --locked --out-dir data-lens-docs
Selected test files still run in package context, so imports, dependency identity, language mode, and the module root match the application.
Develop and package
Use the loopback development server while authoring, then build the managed product:
topaz dev --root data-lens --port 8000
topaz build --root data-lens --locked --release --out-dir data-lens-product
Exercise real CSV and JSON file selection, Unicode labels, text filtering, the active-only checkbox, both sort modes, session save/reload/forget, submit and Enter, cancellation, invalid UTF-8, clearing, one missing-field error, and downloaded CSV/JSON content. A failed edit keeps the last good development product. The maintained installed path includes a 256-row multilingual CSV and 128-row JSON document, Unicode filenames, eight sequential file and state operations, semantic summaries, focus recovery, and exact exported data.
The final directory contains HTML, the safe application host, the checked raw
Web facade, WASM, declared styles, licenses, notices, and
topaz-web-capabilities.json, and topaz-artifact.json. Copy only
data-lens-product/ to a fresh static root,
remove package source and build storage, disable network access, and serve that
directory. All scenarios above must continue to work: the product needs no
Topaz installation, registry, source file, npm runtime, CDN, or remote data
service.
Related documentation
- Application Loop covers the native, Python, and Web workflows from installation.
- Packages & Distribution defines locking,
vendoring, and
[web]inputs. - Artifacts & Deployment defines managed files and source-free deployment.