Development
September 15, 2026 ยท View on GitHub
The repository is one monorepo. Rust crates, generated protocol artifacts,
The shared UI, hosts, extension packaging, and release tooling change together.
Run repository tasks from the root through cargo xtask; use direct Cargo or
pnpm commands only when debugging the task runner or a component-specific test.
Working areas
crates/dezoomify-corecontains pure discovery, catalogs, tile plans, and processing recipes.crates/dezoomify-jobcontains the pure effect/state machine through output finalization and cleanup.crates/dezoomify-protocolis the Rust protocol source for the schema andpackages/protocol-ts.crates/dezoomify-nativecontains native effects used by CLI and Tauri.crates/dezoomify-wasmadapts core and job behavior for browser hosts.packages/shared-uiis the shared UI;packages/browser-runtimeowns browser workers, decoding, canvases, and bounded caching.crates/fixture-serverserves controlled origins,testdata/scenarioscontains shared scenarios, andcrates/xtaskowns repository tasks.
The exact dependency direction is in Architecture.
Task grammar
The canonical form is cargo xtask <task> [target] [options].
cargo xtask setup
cargo xtask check
cargo xtask test
cargo xtask test core
cargo xtask build web
cargo xtask dev web
setup verifies the configured Rust, Node, WASM, and wasm-bindgen tools,
bootstraps the pinned pnpm when needed, installs the frozen workspace
dependencies, and reports browser status.
It never installs browser binaries or Rust toolchains. check runs formatting,
lint, type checking, dependency boundaries, generated-file checks, and manifest
validation without rewriting source files.
Bare test is the fast deterministic unit and contract loop. test all runs
the full deterministic suite. Focused targets are documented in
Testing. No test other than test live contacts public source
sites.
cargo xtask setup configures this checkout to use the versioned
.githooks/pre-commit hook. The hook runs cargo xtask ci check and
cargo xtask test ui, which catch formatting and generated shared-UI artifact
failures before a commit.
Builds
cargo xtask build <target> output:
| Target | Output |
|---|---|
wasm | real WASM artifact under target/wasm32-unknown-unknown/ |
web | full site build via scripts/build-site.mjs: wasm adapter plus browser glue under wasm/, the Vite production bundle, help pages, and the deployable dist/ tree (requires wasm-bindgen-cli matching the version in Cargo.lock; see below) |
cli | real dezoomify-cli binary under target/debug/ |
desktop | the lean shell always compiles; the Tauri window shell (feature tauri) additionally compiles when the platform webview system packages are present; without --unsigned-test and with the bundler prerequisites installed, a real bundle for the matching host is produced (Linux deb, Windows msi/nsis, macOS dmg; see Native apps) |
extension | real store-shaped ZIPs for chromium and firefox under target/extension/, packaged by the same script the store-submission workflow uses |
Examples:
cargo xtask build desktop --unsigned-test
cargo xtask build extension
cargo xtask build cli
The browser-runtime build is cargo xtask test browser --build-only. Shared UI
artifacts are built by build web, build desktop, and build extension; there
are no separate build browser, build ui, build native, or build all
aliases.
The website ships a Vite production bundle. The TypeScript/TSX sources
(src/*.ts, the shared-UI and browser-runtime sources they import) are the
single source of truth: type-checked, unit-tested, and bundled by Vite with
vite.config.ts (base: "/beta/"). The wasm glue under wasm/, the Vite
build output under dist/, and the pages under help/ are generated
artifacts and are never committed: the website-deploy workflow builds them
on every push to master (see the deployment contract below), and
cargo xtask build web builds them locally.
Website deployment contract
One Cloudflare Pages project (the original dezoomify) receives builds from
GitHub Actions through .github/workflows/website-deploy.yml:
scripts/build-site.mjsbuilds the Vite+React app, help pages, and wasm glue, then assemblesdist/: the legacy site (vendored underlegacy/, frommasterbefore the merge, kept verbatim) serves/, the new app serves/beta, and_routes.jsonlimits Function invocation to/api/proxy(new app) and/proxy(legacy, re-exported fromlegacy/functions/proxy.js).- A push to
masteris uploaded as the production deployment. A pull request targetingmasterfrom this repository is uploaded as a preview on the stable branch aliaspr-<number>.dezoomify.pages.dev. The preview job uses the pull request merge ref, so it verifies the exact result that reviewers would merge. The project's automatic git deployments are disabled, so this workflow is the only publisher; a push can never clobber production with a repository tree. - GitHub records each deployment in the
productionorpreviewenvironment and exposes its environment URL as the PR's View deployment link. The preview URL remains stable as the PR receives new commits. - The workflow verifies the live deployment (production or preview): both apps, both proxy routes, wasm content types, the generated help section, and that no repository files are served.
master is the single production branch. Fork pull requests intentionally do
not receive previews: the workflow uses the normal pull_request event and
only same-repository PRs can run the credentialed deployment job. This avoids
checking out untrusted code in a privileged pull_request_target workflow.
Cloudflare preview deployments are public by default and carry noindex; the
preview uses the same restricted metadata proxy and repository-file exposure
gates as production. The deployment never serves repository files, so
internal docs and plans stay private.
Development servers
cargo xtask dev <target> runs the named app's development environment and
prints its URLs and cleanup instructions:
| Target | Environment |
|---|---|
ui | full site build served at http://127.0.0.1:8081/ (the shared UI runs inside the beta app at /beta) |
web | full site build served at http://127.0.0.1:8080/, exactly as deployed |
desktop | the real Tauri development application; fails closed with the webview system package list when they are missing |
extension | an unpacked load staged from the sources and a Chromium launch with an isolated throwaway profile; chromium engine only, other engines fail closed |
dev web and dev ui serve the assembled dist/ tree through
scripts/dev-server.mjs: a loopback static server plus the same POST/OPTIONS /api/proxy metadata relay that Cloudflare runs from functions/api/proxy.ts
(the relay core lives once in src/server/proxy.ts). Nothing extra is
installed; the app is fully functional from cargo xtask dev web alone.
dev desktop starts the Vite server on http://localhost:1420/, waits for it
to accept connections, and then launches the Tauri window shell. The frontend
server is non-interactive and its complete process tree is stopped when the
shell exits, including a second launch forwarded to an existing desktop app.
For example, use cargo xtask dev extension --browser chromium or
cargo xtask dev web. Start standalone deterministic origins with
cargo xtask fixtures serve --port 0. Development commands bind local services
to loopback and never fall back to public resources.
Maintenance
Generated protocol files are derived from Rust and are never edited by hand.
Fixture and protocol commands are deterministic unless their name explicitly
says live.
cargo xtask protocol generate
cargo xtask protocol generate --check
cargo xtask protocol check
cargo xtask fixtures verify
cargo xtask fixtures serve --port 0 --write-address target/fixture-server.addr
protocol generate refreshes checked-in TypeScript, schema, and capability
artifacts. Its --check form compares against a temporary generation, while
protocol check runs cross-language goldens, fingerprints, portability, and
generated-marker checks. Golden candidates change only through the explicit
maintenance option reported by protocol generate --help. fixtures verify
validates manifests, provenance, licenses, routes, and hashes.
Releases
Release tasks consume an immutable plan. Building does not sign or publish, and verification uses public keys only.
export DEZOOMIFY_VERSION="$(cargo xtask release version)"
cargo xtask release plan [--numbered]
cargo xtask release build --plan target/release-dist/<version>/plan.json --target <target>
cargo xtask release verify --plan target/release-dist/<version>/plan.json --artifacts target/release-dist/<version>
Signing, notarization, deployment, store submission, and publication run as separate protected CI operations against the verified artifact digests.
Common workflows
Add or change a format
Follow Contributing a format. In short:
- Add core parser/plan coverage and scenario-local payloads.
- Run
cargo xtask fixtures verifyandcargo xtask test core --parity. - Run
cargo xtask test scenario --scenario <scenario-id>.
Change the shared UI
- Run
cargo xtask dev uiwhile changing host-neutral components. - Run
cargo xtask test ui, then the affectedtest web,test desktop, ortest extensionintegration target. - Run
cargo xtask build webto catch integration and bundle-policy failures.
Change the protocol
- Edit only the Rust source and protocol fixtures.
- Run
cargo xtask protocol generateandcargo xtask test protocol. - Run
cargo xtask protocol checkand the affected host test targets.
Before a pull request
Run cargo xtask check and the fast cargo xtask test during development, then
cargo xtask test all and cargo xtask ci local. Run cargo xtask test live
only when the change needs an explicit advisory compatibility sample.
Change rules
- Domain decisions belong in core or job code, not UI and transport code.
- Effectful code implements protocol effects; it does not reproduce job policy.
- Runtime differences use capabilities and shared error codes.
- Add a shared scenario whenever more than one runtime exercises behavior.
- Keep generic lifecycle, retry, and transport-effect policy in the job engine; keep the website's direct-first, classified automatic metadata proxy eligibility policy in the web app at the repository root. App integrations execute supplied transport effects and report results; they never invent a hidden fallback or per-attempt proxy consent flow.
- Redact credentials and sensitive URLs at every diagnostic boundary.