Skip to content

fix(cli): validate profile array elements and name --storage-domain - #173

Merged
chrischall merged 1 commit into
mainfrom
fix/cli-profile-validation-and-domain-hint
Jul 27, 2026
Merged

fix(cli): validate profile array elements and name --storage-domain#173
chrischall merged 1 commit into
mainfrom
fix/cli-profile-validation-and-domain-hint

Conversation

@chrischall

Copy link
Copy Markdown
Owner

Both nits from the auto-review follow-up issue.

profiles.ts — per-element validation

validateProfile only Array.isArray-checked captureHeaders / indexedDb / localStoragePointers / sessionStoragePointers / domSelectors. A typo inside an entry passed CLI validation and failed much later at the extension's hello validator, mid-pair, with a ProtocolError that names neither the profile nor which entry was wrong. These arrays are the hand-edited ones — only --capture-header and --dom-selector have flags — so they're exactly where a typo is likely.

Each array now gets a per-element shape check, and the error names the index (profile "x" has an invalid "indexedDb[1]").

Deliberately structural: required fields present and string-typed. The protocol's format rules (origin scheme, key/selector character sets, JSON-pointer syntax) stay in @fetchproxy/protocol's validator — duplicating them here would give two definitions of "valid" and they would drift.

multi-domain reads — name the flag, not the library option

Storage reads target exactly one declared domain. With several declared and none picked, the message that reached the user came from the server:

FetchproxyServer: this MCP declared multiple domains ["a.com", "b.com"] — pass { domain: '<one of them>' } on every per-call request

There is no object for a CLI user to pass. Two changes:

  • fpx cookies|local-storage|session-storage|indexeddb and fpx dom refuse up front, next to the existing scope-narrowing checks, mirroring fpx pair's --domain check. Both verbs read unconditionally, so the gap is provable without connecting — and a usage error must not cost a bridge round-trip or trigger pairing. A test asserts listen() was never called.
  • fpx session can't pre-check: a captureHeaders-only profile is legitimately multi-domain and never does a domain-scoped read (bootstrap only threads storageDomain into the cookie/localStorage/sessionStorage/IDB reads). So mapBridgeError rewrites the server's message there — keeping the declared-domain list, swapping the hint for --storage-domain.

One behavioural note: that rewritten path returns EXIT.USAGE (1) instead of EXIT.BRIDGE (2). It is a usage error — the bridge is fine, the invocation isn't — and a script distinguishing "retry" from "fix your command" was previously told the wrong one.

Verification

npm run build && npm test (CI's exact commands): 982 passed, npm run typecheck clean. Six new tests; reverting packages/cli/src fails five of them (the sixth is the over-rejection guard, which correctly passes either way).

Closes #152

🤖 Generated with Claude Code

https://claude.ai/code/session_011DKSBmZAgtkp3wfCB4TgMm

Both auto-review nits from #152.

profiles.json's object arrays (indexedDb, the pointer arrays, captureHeaders,
domSelectors) were only Array.isArray-checked, so a typo inside an entry passed
CLI validation and surfaced much later as a ProtocolError from the extension's
hello validator, mid-pair, with no hint about which profile or entry was at
fault. Each array now gets a per-element shape check that names the offending
index. Deliberately structural (required fields present and string-typed): the
protocol's format rules — origin scheme, key and selector character sets, JSON
pointer syntax — stay in @fetchproxy/protocol, since two definitions of valid
would drift.

Storage reads target one declared domain. On a multi-domain profile with none
picked, the message that reached the user came from the server and named the
library option: "pass { domain: '<one of them>' }" — something no CLI user can
pass. `fpx cookies|local-storage|session-storage|indexeddb` and `fpx dom` now
refuse before connecting (like the existing --domain check in `fpx pair`), so
the usage error costs no bridge round-trip and triggers no pairing. `fpx
session` can't pre-check — a captureHeaders-only profile is legitimately
multi-domain and never does a domain-scoped read — so mapBridgeError rewrites
the server's message there instead, keeping the declared-domain list and
swapping the hint for --storage-domain. That path now exits USAGE rather than
BRIDGE, which is what it is.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_011DKSBmZAgtkp3wfCB4TgMm
@github-actions github-actions Bot added the auto-review Trigger Claude + Copilot review on this PR label Jul 27, 2026
@claude

claude Bot commented Jul 27, 2026

Copy link
Copy Markdown

Claude finished @chrischall's task in 2m 19s —— View job


Review complete

  • Read CLAUDE.md conventions
  • Fetch PR diff
  • Review profiles.ts per-element validation
  • Review storage-scope.ts + verb wiring (dom.ts, read.ts)
  • Review bridge-errors.ts rewrite for session
  • Review new/updated tests
  • Post findings + verdict

No issues found. This is a tight, well-scoped fix:

  • profiles.ts: ELEMENT_SHAPE per-array checks map 1:1 onto the actual @fetchproxy/protocol decl shapes (CaptureHeaderDecl, IndexedDbScopeDecl, PointerDecl, DomSelectorDecl in packages/protocol/src/frames.ts), and correctly stay structural (presence + string-typed) rather than duplicating the protocol validator's format rules — matching the PR's stated intent. The ${k}[${i}] error naming is verified by the new profiles.test.ts cases, including the non-object-element and empty-keys-array cases.
  • storage-scope.ts / dom.ts / read.ts: requireStorageDomain is called before server.listen() in both verbs, matching the "usage errors must not cost a bridge round-trip" comment, and the new tests assert server.listen was never called.
  • bridge-errors.ts: the regex / — pass \{ domain.*$/ exactly matches the real server string built in packages/server/src/ws-server.ts's resolveBaseDomain (`FetchproxyServer: this MCP declared multiple domains [${declared}] — pass { domain: '<one of them>' } on every per-call request`), so the rewritten message correctly preserves the domain list and swaps in --storage-domain. Confirmed the EXIT.USAGE (vs EXIT.BRIDGE) behavioral change is scoped correctly: fetch/download/health never hit this branch in practice (assertUrlOnProfile in fetch.ts always resolves and passes an explicit domain; download/health don't thread a domain param at all), so the generic match in mapBridgeError only actually fires for session, as described.
  • Wording is consistent with the existing fpx pair multi-domain check in health.ts (runPair).
  • CI (build + full vitest suite) is green on this PR.

Verdict: pass

@github-actions

Copy link
Copy Markdown
Contributor

✅ Auto-review verdict: pass — Well-scoped fix that matches its description exactly: per-element profile validation aligns with the actual protocol decl shapes, and the storage-domain hint rewrite correctly matches the real server error string. No issues found.

@chrischall chrischall added the ready-to-merge Owner has reviewed; arm auto-merge to land when CI is green label Jul 27, 2026
@chrischall
chrischall enabled auto-merge (squash) July 27, 2026 19:15
@chrischall
chrischall merged commit 871d87c into main Jul 27, 2026
11 checks passed
@chrischall
chrischall deleted the fix/cli-profile-validation-and-domain-hint branch July 27, 2026 19:16
chrischall added a commit that referenced this pull request Jul 27, 2026
🤖 I have created a release *beep* *boop*
---


##
[1.6.2](v1.6.1...v1.6.2)
(2026-07-27)


### Bug Fixes

* **cli:** validate profile array elements and name --storage-domain
([#173](#173))
([871d87c](871d87c))

---
This PR was generated with [Release
Please](https://github.com/googleapis/release-please). See
[documentation](https://github.com/googleapis/release-please#release-please).

---------

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

auto-review Trigger Claude + Copilot review on this PR ready-to-merge Owner has reviewed; arm auto-merge to land when CI is green

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Auto-review follow-ups for PR #151: feat(cli): @fetchproxy/cli one-shot bridge CLI (fpx)

1 participant