Architecture

September 15, 2026 ยท View on GitHub

dezoomify is one monorepo containing Rust crates, generated protocol bindings, the shared UI, browser-extension packaging, and native applications. Dependencies point inward toward pure domain libraries; hosts own all effects.

Components

crates/dezoomify-core

A pure Rust library that converts supplied resource bytes and URLs into discovery results, image catalogs, tile plans, and processing recipes. It describes required resources but never fetches them. Its library code performs no network, filesystem, async runtime, image decoding/encoding, UI, DOM, clock, random source, process, or environment access. Pure parsing, URL manipulation, deterministic crypto, serialization, and data-structure libraries plus the log facade are permitted. Tests may invoke tooling without making it a normal dependency. Each format registers a stable id with a user-visible display name in one ordered registry; registry order defines automatic precedence.

crates/dezoomify-job

A pure, host-neutral Rust effect/state machine. It owns the job through output-destination selection, tile acquisition and processing outcomes, encoding, finalization, partial publication, and cleanup. Hosts feed results back into the machine and execute its I/O effects. The browser runtime drives it through the WASM adapter; the native runtime drives it through its job driver. See Job engine.

crates/dezoomify-protocol

The Rust source of truth for commands, events, capabilities, errors, and browser-to-native handoff. It generates the schema and packages/protocol-ts; all forms carry protocol-version information. See Protocol.

crates/dezoomify-native

The native effect implementation: HTTP transport, local-file access, image decoding, processing execution, and the PNG output encoder. The tile-cache helpers stay unwired (storage none); canvas assembly is bounded by the memory currently available to the process. Both the CLI and Tauri desktop application use it. See Native apps.

crates/dezoomify-wasm

The WASM adapter for core, job, and pure processing code. It does not own fetching, workers, decoding, browser canvases, storage, or output saves. See Browser runtime.

packages/shared-ui

One host-neutral React view renders discovery, selection, job progress, recovery, and output through typed .tsx components. Hosts mount it with renderView(container, state, callbacks, ctx); their effect layers (web worker, Vite bundling, Tauri commands, extension messaging) stay in the product packages. The TypeScript/TSX sources are the single source of truth and are bundled directly by Vite/WXT; there are no hand-maintained .js mirrors. The shared UI depends on generated TypeScript protocol types, not host-specific implementation details.

packages/browser-runtime

The browser host owns workers, readable-byte fetching, request activity, active-transport reporting, image decode, tile painting, canvas and save surfaces, and an optional bounded browser cache. The extension job tab uses its engine-effect executor. The website keeps a discovery-session orchestrator for processed-tile support while using the same runtime fetch, decode, painting, activity, preview, and save implementations. The executor owns canvas execution and host limits, never job policy (retries, cancellation, partial output, and ordering stay in the engine). Hosts supply transport eligibility and fallback policy: the web integration tries a direct browser fetch first, with browser credentials omitted, and may automatically use the metadata CORS proxy only after a classified CORS or network failure, or a direct fetch that does not complete within the 1500 ms metadata window, for an eligible public, non-credential metadata request (never tiles). The active transport is retained in the job's technical details and copied diagnostics. No cookies, Authorization, browser credentials, or user-supplied credential headers are sent to or by the proxy. The extension never uses the metadata CORS proxy; extension-to-native cookie handoff is separately consent-gated. The extension transport is tab-origin direct fetch followed by <img> tainted display-only, with the active transport always visible. It connects packages/shared-ui to crates/dezoomify-wasm on the website and in the extension. The website compatibility module src/webIntegration.ts is a re-export-only shim; browser integration policy lives in packages/browser-runtime/src/web-integration.ts. Transport display labels live only in packages/browser-runtime/src/transport-labels.ts.

Metadata CORS proxy

The metadata relay is one pure module, src/server/proxy.ts (handleProxyRequest), with three thin host adapters around it: functions/api/proxy.ts (Cloudflare Pages Function), src/server/proxy-node.ts (Node HTTP, used by the local dev server), and the node:test seam in test/proxy-*.test.mjs. Each adapter translates its host transport to the same relay call, so the SSRF, credential, redirect, size, content-type, and CORS policy is identical in tests, local development, and production. The dev server (scripts/dev-server.mjs) serves the assembled dist/ tree and routes /api/proxy to the Node adapter.

Support workspaces

packages/protocol-ts contains generated TypeScript protocol bindings. crates/fixture-server serves controlled origins, testdata/scenarios contains shared declarative cases, and crates/xtask owns repository generation and validation tasks.

Data flow

Shared UI or CLI
    | typed command
    v
crates/dezoomify-job <--> crates/dezoomify-core
    | effects             ^ supplied bytes/results
    v                     |
packages/browser-runtime or crates/dezoomify-native
    | typed events
    v
Shared UI or CLI

Discovery first emits resource requests. The active host acquires each resource and returns bytes to the core. A selected catalog entry becomes a tile plan and processing recipe. The job engine schedules effects within host limits and turns their results into events.

Boundary rules

  • Core and job logic remain deterministic and testable without I/O.
  • URLs, headers, credentials, bytes, and output destinations cross boundaries only through typed values.
  • Runtime differences appear as negotiated capabilities, and automatic fallback is exposed through active-transport state rather than hidden.
  • Errors cross host boundaries as stable protocol errors with typed recovery actions.
  • Shared scenarios cover the native runtime and CLI (native/cli-dzi, native/cli-tile-failure); see Testing.