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.