Files
callstack__agent-device/.fallowrc.json
Michał Pierzchała b8172e3da3 perf(daemon): offload PNG decode/encode and screenshot diff to a worker thread (#734)
* 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>
2026-06-10 12:27:44 +02:00

87 lines
2.4 KiB
JSON

{
"$schema": "https://raw.githubusercontent.com/fallow-rs/fallow/main/schema.json",
"entry": [
"rslib.config.ts",
"src/index.ts",
"src/io.ts",
"src/artifacts.ts",
"src/metro.ts",
"src/remote-config.ts",
"src/install-source.ts",
"src/android-adb.ts",
"src/android-snapshot-helper.ts",
"src/contracts.ts",
"src/selectors.ts",
"src/finders.ts",
"src/bin.ts",
"src/companion-tunnel.ts",
"src/daemon.ts",
"src/utils/png-worker.ts",
"scripts/patch-xcuitest-runner-icon.ts",
"src/utils/update-check-entry.ts",
"test/scripts/metro-prepare-packaged-smoke.mjs",
"test/integration/*.test.ts",
"website/docs/404.mdx",
"website/rspress.config.ts"
],
"ignorePatterns": [
"examples/test-app/**",
"scripts/perf/**",
"ios-runner/AgentDeviceRunner/AgentDeviceRunnerUITests.xctestplan",
"scripts/write-xcuitest-cache-metadata.mjs"
],
"ignoreDependencies": ["@microsoft/api-extractor", "@theme"],
"ignoreExports": [
{
"file": "src/__tests__/test-utils/index.ts",
"exports": ["*"]
},
{
"file": "src/__tests__/test-utils/device-fixtures.ts",
"exports": ["LINUX_DEVICE", "ANDROID_TV_DEVICE", "TVOS_SIMULATOR"]
},
{
"file": "src/__tests__/test-utils/mocked-binaries.ts",
"exports": ["withMockedXcrun"]
},
{
"file": "src/daemon/app-log.ts",
"exports": ["readRecentAndroidLogcatForPackage"]
},
{
"file": "src/platforms/android/app-lifecycle.ts",
"exports": [
"parseAndroidForegroundApp",
"parseAndroidLaunchablePackages",
"parseAndroidUserInstalledPackages",
"installAndroidInstallablePath"
]
},
{
"file": "src/platforms/android/index.ts",
"exports": ["installAndroidInstallablePath"]
},
{
"file": "src/platforms/ios/apps.ts",
"exports": ["listSimulatorApps", "uninstallIosApp"]
},
{
"file": "src/platforms/ios/index.ts",
"exports": ["listSimulatorApps", "uninstallIosApp"]
}
],
"usedClassMembers": ["name", "listActiveLeases", "delete", "values", "elapsedMs", "isExpired"],
"rules": {
"unused-types": "off",
"duplicate-exports": "off"
},
"production": {
"dupes": true
},
"publicPackages": ["agent-device"],
"audit": {
"deadCodeBaseline": "fallow-baselines/dead-code.json",
"healthBaseline": "fallow-baselines/health.json"
}
}