Skip to content

Latest commit

 

History

History
58 lines (40 loc) · 3.04 KB

File metadata and controls

58 lines (40 loc) · 3.04 KB

CLAUDE.md

This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.

Project Overview

@agicash/qr-scanner is a high-performance web QR code scanner powered by ZXing-C++ WebAssembly. It's a drop-in replacement for nimiq/qr-scanner that feeds full-resolution frames (720-1080px+) to a WASM decoder running in a Web Worker, keeping the UI at 60fps.

Commands

Use bun as the package manager. Never use npm, npx, yarn, or pnpm.

bun install              # Install dependencies
bun run build            # Build library (ESM + CJS + .d.ts via tsup)
bun run test             # Run all tests (vitest)
bun run test:watch       # Run tests in watch mode
bun vitest run tests/scan-region.test.ts  # Run a single test file
bun run typecheck        # Type check (tsc --noEmit)
bun run dev              # Start demo dev server (Vite + HTTPS)

Architecture

QrScanner (index.ts)      ← Public API facade, static methods
  └─ Scanner (scanner.ts) ← Orchestrator, composes all components below
       ├─ CameraManager (camera.ts)       ← getUserMedia, stream lifecycle, flash
       ├─ FrameExtractor (frame-extractor.ts) ← rAF loop, canvas crop, backpressure
       ├─ Worker (worker.ts)              ← Web Worker running zxing-wasm decoder
       └─ ScanOverlay (overlay.ts)        ← Scan region mask + QR outline SVG

Key data flow: Video stream → FrameExtractor crops to scan region via canvas → ImageData transferred to Worker → zxing-wasm decodes → result posted back → onDecode callback + overlay update.

Backpressure: FrameExtractor skips frames while the Worker is busy. Combined with maxScansPerSecond (default 15), the loop self-tunes without manual FPS management.

Build outputs: tsup produces two separate bundles — the main library (ESM + CJS) and the worker (ESM only, with zxing-wasm bundled in via noExternal).

Key Design Decisions

  • No downscaling — unlike nimiq/qr-scanner's 400px cap, scan regions are sent at full resolution to the WASM decoder
  • Composition over inheritance — QrScanner wraps Scanner, which composes CameraManager, FrameExtractor, and ScanOverlay
  • OffscreenCanvas with fallback — uses OffscreenCanvas when available, falls back to hidden HTMLCanvasElement for older Safari
  • Transferable ImageData — frames sent to Worker without copying
  • scanImage() uses zxing-wasm directly (no Worker) for static image decoding

Testing

Tests use Vitest with jsdom environment. tests/setup.ts provides an ImageData polyfill.

  • Unit tests (mocked dependencies): scan-region, camera, frame-extractor, worker
  • Integration tests (real WASM decoder): scan-image, scanner
  • Test fixtures in tests/fixtures/ — QR PNGs at various densities generated by tests/generate-fixtures.ts

Types

All public types are defined in src/types.ts including ScanResult, ScannerOptions, WorkerRequest/WorkerResponse message types. The worker message types enforce the contract between main thread and Worker.