# @dowser/http-testing

## 0.11.0

## 0.10.0

## 0.9.0

### Minor Changes

- feat(recorder): prod-shippable recorder with feature-flag activation

  `DowserRecordRoot` now accepts an `enabled?: boolean` prop that gates every
  side effect (event tap, http tap, overlay render, documents-dir writes).
  When `enabled` is `false` the recorder is a pure pass-through, so you can
  ship it in production App Store / Play Store builds and gate it behind
  your existing feature-flag system (LaunchDarkly, GrowthBook, Statsig,
  ConfigCat, homegrown — dowser doesn't care, it just reads a boolean).

  When a session finalizes, the overlay shows Share + Dismiss buttons. Share
  opens the iOS / Android share sheet via `expo-sharing` (install it
  yourself: `npx expo install expo-sharing`). First-time activation on a
  given device shows a one-tap consent banner, persisted via AsyncStorage
  through a new `warningStorage` prop.

  For developers running debug builds on real Android devices,
  `dowser pull --platform android --target real` pulls session zips over
  USB via `adb run-as` + `adb exec-out cat`. iOS real-device pull is
  intentionally not supported — staff users use the in-app share sheet.

  See the new "Ship the recorder in production builds" section in the
  README for the full integration example.

## 0.8.0

### Minor Changes

- **`dowser watch` — collaborative review with comments and pin annotations**

  A new `dowser` subcommand hosts a recording for live or async team review.
  Reviewers join a shareable URL (cloudflared tunnel by default, `--local` for
  localhost), type a name, and add comments + numbered pin annotations on
  individual frames. Comments persist to `<artifact>.dowser.sqlite` next to
  the zip and travel with the recording.

  ```bash
  dowser watch                          # latest in ./dowser-recordings/
  dowser watch ./flow.zip --local       # localhost only
  dowser watch --dir ~/recordings       # custom directory
  ```

  Flags: `--dir`, `--local`, `--port`, `--no-open`, `--name`. Requires Node
  22+ for built-in `node:sqlite`; `cloudflared` is optional (skip with
  `--local`).

  The recorder viewer is now a single Preact + signals tree shared across
  recorder zips, `dowser share` HTML, and `dowser watch` — the host overlay
  mounts only when `DOWSER_HOST` is injected at serve time, so static
  artifacts stay self-contained and inert. Per-author pin colors keep
  multi-reviewer threads legible.

## 0.7.6

### Patch Changes

- fix(recorder): capture XHR, scope selectors below host, full-screen captures

  Three gaps surfaced by a real recorded session, where axios calls were missing,
  taps inside a modal were mis-attributed to the recorder host, and modal
  screenshots showed only the page behind:

  - HTTP tap now patches `XMLHttpRequest` in addition to `fetch`. Axios's RN
    adapter uses XHR, so a fetch-only tap missed every axios call. Observe-only
    — `originalSend` still runs, the request still hits the network. The
    `loadend` handler is wrapped in try/catch so a throwing recorder handler
    can't propagate into the consumer's XHR event dispatch. Status-0 records
    use a `null` body instead of a synthetic error string.
  - Selector resolver takes a required `recorderHost` sentinel and stops
    walking there. Taps below the host with no stable selector previously
    walked up and resolved to the host's own `testID="dowser-recorder-host"`.
    They now fall through to a structural `noStableTarget` instead.
  - `resolveTapTarget` returns null when the target tag IS the host fiber.
    Direct background taps on the host are dowser infrastructure, not part
    of the consumer's UI.
  - Screenshot pipeline switched from `captureRef(hostRef)` to `captureScreen()`.
    RN modals render in a separate `UIWindow` outside the React tree;
    `captureRef` returned the page behind. The recorder overlay now exposes
    an imperative `withHidden(fn)` handle that hides the overlay for the
    duration of the capture and restores it whether `fn` resolves, rejects,
    or throws synchronously — restore is a property of the type, not caller
    discipline.
  - `saveScreenshot` exceptions are caught inside the capture pipeline so a
    disk-full or permission error drops only the screenshot, not the whole
    action frame.

  Existing recordings won't backfill — re-record after consumers update.

## 0.7.5

### Patch Changes

- [#18](https://github.com/getdowser/dowser/pull/18) [`8ad24b9`](https://github.com/getdowser/dowser/commit/8ad24b90f75da10a0a7bd5764621845502c7cf07) Thanks [@devtoil](https://github.com/devtoil)! - fix(recorder): http tap no longer breaks consumer fetches

  The recorder's HTTP tap relied on `Response.clone()` to read the body for
  recording while preserving the original for the caller. RN's whatwg-fetch
  polyfill is unreliable here — clones can share body state, and `clone()`
  itself can throw in some Hermes builds. Symptom in the wild: with
  `EXPO_PUBLIC_DOWSER=record`, every `await fetch().then(r => r.json())` in
  the consumer either threw "Already read" or hung. Now the tap reads the
  body once and hands the caller a freshly-constructed Response with the
  same status, headers, and body bytes — independent of `clone()`.
