* perf(daemon): offload PNG decode/encode and screenshot pixel diff to a worker thread
PNG decode (inflateSync) and per-pixel screenshot diffing previously ran
synchronously on the daemon event loop, stalling all concurrent sessions
while multi-MB screenshots were processed.
- add src/utils/png-worker.ts worker_threads entry (rslib internal/png-worker)
handling one decode, encode, or diff-pixels job per message
- add src/utils/png-worker-client.ts async wrappers (decodePngAsync,
encodePngAsync, computeScreenshotDiffPixelsAsync) that lazily spawn the
worker, resolve it next to the current module in dev (.ts) and dist (.js)
like the companion tunnel entry, and fall back to the in-process
synchronous path when the worker is unavailable
- extract the unchanged pixel-compare loop into
src/utils/screenshot-diff-pixels.ts so both paths share identical logic
- route daemon call sites (screenshot-overlay annotate, compareScreenshots)
through the async wrappers; results stay byte-identical
https://claude.ai/code/session_01LXZXzxi55sZ11DSyqWyBA2
* refactor(daemon): apply review findings to PNG worker offload
- guard postMessage failures: a job-specific send error (e.g. DataCloneError)
now cleans up its pending entry and falls back to sync for that call only
- resolve the worker entry via a shared src/utils/internal-entry.ts helper
that returns null on any resolution failure (non-file import.meta.url)
- match repo precedent: spawn the worker with --experimental-strip-types
when the resolved entry is a .ts module
- report permanent worker degradation once (scoped diagnostic + process
warning with the failure reason) instead of silently going sync-only
- daemon lifecycle: pre-warm the worker at startup and terminate it during
shutdown with a 1s best-effort timeout (daemon-only)
- collapse the three async wrappers onto one kind-typed job runner with a
single unavailability channel (rejection, no null path) and drop the dead
mismatched-result guards
- derive the diff-pixels contract types from screenshot-diff-pixels.ts and
share toBuffer via the contract module
- serialize worker errors with normalizeError and reconstruct AppError
(code/message/details) client-side; the worker reuses decodePng so decode
failures are identical on both paths
- transfer result buffers back to the client when a view fully owns its
ArrayBuffer; clone pooled buffers to protect Node's shared buffer pool
- decode baseline/current screenshots concurrently in compareScreenshots
- move resizePngFileToMaxSize to src/utils/png-resize.ts and route its
decode/encode through the worker (daemon screenshot --max-size path)
https://claude.ai/code/session_01LXZXzxi55sZ11DSyqWyBA2
* fix(fallow): declare png worker entry and simplify resultTransferList
The Fallow audit flagged src/utils/png-worker.ts as unreachable from any
entry point and resultTransferList as over the complexity threshold.
- Declare src/utils/png-worker.ts in .fallowrc.json's entry list: it is a
worker_threads entry point loaded at runtime (mirrors src/daemon.ts /
src/companion-tunnel.ts and the internal/png-worker rslib entry).
- Flatten resultTransferList into filter/map over an extracted
ownsEntireArrayBuffer predicate, preserving the exact transfer rule
(byteOffset === 0, byteLength === owner.byteLength, real ArrayBuffer).
- Add direct unit coverage asserting fully-owned buffers are transferred
while pooled/offset views are not.
https://claude.ai/code/session_01LXZXzxi55sZ11DSyqWyBA2
---------
Co-authored-by: Claude <noreply@anthropic.com>
* refactor: expose metro and remote config node apis
* refactor: tighten public node api surface
* refactor: tighten metro public type boundaries
* refactor: normalize remote config public profile
* refactor: make remote config parser library-owned
* refactor: share remote config parsing primitives
* refactor: avoid regex in base url normalization