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-core contains pure discovery, catalogs, tile plans, and processing recipes.
  • crates/dezoomify-job contains the pure effect/state machine through output finalization and cleanup.
  • crates/dezoomify-protocol is the Rust protocol source for the schema and packages/protocol-ts.
  • crates/dezoomify-native contains native effects used by CLI and Tauri.
  • crates/dezoomify-wasm adapts core and job behavior for browser hosts.
  • packages/shared-ui is the shared UI; packages/browser-runtime owns browser workers, decoding, canvases, and bounded caching.
  • crates/fixture-server serves controlled origins, testdata/scenarios contains shared scenarios, and crates/xtask owns 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:

TargetOutput
wasmreal WASM artifact under target/wasm32-unknown-unknown/
webfull 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)
clireal dezoomify-cli binary under target/debug/
desktopthe 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)
extensionreal 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:

  1. scripts/build-site.mjs builds the Vite+React app, help pages, and wasm glue, then assembles dist/: the legacy site (vendored under legacy/, from master before the merge, kept verbatim) serves /, the new app serves /beta, and _routes.json limits Function invocation to /api/proxy (new app) and /proxy (legacy, re-exported from legacy/functions/proxy.js).
  2. A push to master is uploaded as the production deployment. A pull request targeting master from this repository is uploaded as a preview on the stable branch alias pr-<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.
  3. GitHub records each deployment in the production or preview environment and exposes its environment URL as the PR's View deployment link. The preview URL remains stable as the PR receives new commits.
  4. 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:

TargetEnvironment
uifull site build served at http://127.0.0.1:8081/ (the shared UI runs inside the beta app at /beta)
webfull site build served at http://127.0.0.1:8080/, exactly as deployed
desktopthe real Tauri development application; fails closed with the webview system package list when they are missing
extensionan 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:

  1. Add core parser/plan coverage and scenario-local payloads.
  2. Run cargo xtask fixtures verify and cargo xtask test core --parity.
  3. Run cargo xtask test scenario --scenario <scenario-id>.

Change the shared UI

  1. Run cargo xtask dev ui while changing host-neutral components.
  2. Run cargo xtask test ui, then the affected test web, test desktop, or test extension integration target.
  3. Run cargo xtask build web to catch integration and bundle-policy failures.

Change the protocol

  1. Edit only the Rust source and protocol fixtures.
  2. Run cargo xtask protocol generate and cargo xtask test protocol.
  3. Run cargo xtask protocol check and 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.