Skip to content

fix(gui): react-doctor shared foundations#466

Merged
Wibias merged 8 commits into
lidge-jun:devfrom
Wibias:fix/react-doctor-foundations
Jul 26, 2026
Merged

fix(gui): react-doctor shared foundations#466
Wibias merged 8 commits into
lidge-jun:devfrom
Wibias:fix/react-doctor-foundations

Conversation

@Wibias

@Wibias Wibias commented Jul 25, 2026

Copy link
Copy Markdown
Collaborator

Summary

  • Add fetch-json, client-resource, and intl-formatters helpers used by follow-up react-doctor cleanup PRs.
  • Store the GUI API auth token in memory only (no sessionStorage) to avoid XSS-readable persistence.

Stack

Test plan

  • bun x tsc -b in gui/
  • Smoke: open GUI, enter API token when prompted, confirm authenticated /api/* calls still work across reload (token must be re-entered — intentional)

Summary by CodeRabbit

  • New Features
    • Added reusable client-side resource caching with refresh, optional polling, and per-key loading/error state.
    • Introduced helpers for safe JSON response handling and new i18n formatters for credit dates and estimated USD values.
  • Bug Fixes
    • Improved API authentication to prefer in-memory tokens, defensively clear legacy session entries, and retry once after auth failures without leaking legacy tokens.
  • Tests
    • Added/updated Bun and React polling tests to validate token prompt behavior and unmount/in-flight request correctness.

Add fetch/json helpers, client-resource hooks, and intl formatters, and keep the API auth token in memory instead of sessionStorage.
@github-actions github-actions Bot added the bug Something isn't working label Jul 25, 2026
@coderabbitai

coderabbitai Bot commented Jul 25, 2026

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

@Wibias, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 25 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: e49b08cd-907f-4080-adee-d02676808b6c

📥 Commits

Reviewing files that changed from the base of the PR and between afc99ec and 3616d2a.

📒 Files selected for processing (2)
  • gui/src/client-resource.ts
  • gui/tests/client-resource-poll.test.tsx
📝 Walkthrough

Walkthrough

The GUI now stores API tokens in memory, adds keyed client-resource state with polling and cancellation, provides status-aware JSON helpers, and introduces cached date and number formatting utilities.

Changes

GUI client utilities

Layer / File(s) Summary
In-memory API authentication
gui/src/api.ts, gui/tests/api-auth-memory.test.ts
API tokens move from sessionStorage to module memory; legacy storage is cleared, same-origin retry behavior remains, and tests cover storage cleanup and cross-origin protection.
Client resource state and fetching
gui/src/client-resource.ts, gui/tests/client-resource-poll.test.tsx
Adds keyed external-store state with subscriptions, polling, abortable fetches, stale-result protection, refresh support, and imperative cache updates, with tests for subscriber ownership and keyed refreshes.
Response JSON parsing helpers
gui/src/fetch-json.ts
Adds success-gated JSON readers that handle empty and 204 responses and extract structured error messages.
Cached internationalized formatting
gui/src/intl-formatters.ts
Adds cached number formatting, shared ISO credit-date formatting, and validated estimated USD formatting with four fractional digits.

Estimated code review effort: 3 (Moderate) | ~30 minutes

Sequence Diagram(s)

sequenceDiagram
  participant ReactHook
  participant ClientResourceStore
  participant ResourceFetcher
  ReactHook->>ClientResourceStore: subscribe to keyed resource
  ClientResourceStore->>ResourceFetcher: start or poll fetch with AbortSignal
  ResourceFetcher-->>ClientResourceStore: resolve or reject result
  ClientResourceStore-->>ReactHook: emit snapshot update
Loading

Possibly related PRs

  • lidge-jun/opencodex#470: Touches the same shared GUI utility modules, including in-memory API authentication and the resource, JSON, and formatting helpers.

Suggested labels: chore

Suggested reviewers: ingwannu, lidge-jun

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately reflects the PR’s main purpose: shared GUI foundations for react-doctor follow-up work.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Comment @coderabbitai help to get the list of available commands.

chatgpt-codex-connector[bot]

This comment was marked as resolved.

coderabbitai[bot]

This comment was marked as resolved.

@lidge-jun

Copy link
Copy Markdown
Owner

🔒 Under maintainer review — detailed feedback incoming

@lidge-jun (maintainer) has this PR in an active review pass. Please do not merge, rebase, or
close it
until the detailed review lands; a full comment with specific file:line findings,
failure modes, and suggested fixes is being prepared.

This is a claim marker so two maintainers do not review or land the same PR at once. If you are a
maintainer and need this PR urgently, please reply here first instead of merging.

Baseline for this pass: dev is at bef0dfbe (yesterday's integration batch ebc62d1f plus
#458, #456, #391, #392, #463, #465). If this PR touches the same files, the upcoming review will
name the exact overlapping paths so you only have to rebase once.

No action needed from you until then. Thanks for the contribution and for your patience.

Review tracker: devlog/_plan/260726_pr_review_pass · marker posted by the maintainer review pass

@lidge-jun

Copy link
Copy Markdown
Owner

Thank you for extracting the shared react-doctor foundations — the client-resource store is the right shape for this codebase, and the exact head verifies cleanly (bun run typecheck and bun run lint:gui both pass locally, matrix CI green).

One substantive finding before merge.

gui/src/client-resource.ts:143 — declared dependency invalidation is silently discarded. useKeyedClientResource advertises explicit dependency-based invalidation, but _deps is dropped at line 153. If a loader input changes while key stays stable, the hook neither resubscribes nor refetches, so consumers keep rendering stale data until polling ticks or the user forces a refresh. Please either fold the dependencies into resource identity/invalidation, or remove the parameter entirely and require callers to encode every dependency in key. A parameter that looks load-bearing but is inert is the more dangerous of the two options.

Overlap with dev: none. No touched path has changed on dev since this PR's c03ed0a3 base, and three-way merge inspection is clean. No rebase required.

Test gap: the new resource store has no tests at all. Please add hook/store regressions for dependency changes, key changes, disabled-to-enabled transitions, polling cleanup, abort/latest-result ordering, and falsy resource values. The _deps case should fail before the fix and pass after it.


Reviewed as part of a maintainer review pass against dev at bef0dfbe. Verification: exact head, fresh local bun run typecheck + bun run lint:gui, plus hosted matrix CI.

Honor keyed deps invalidation, skip overlapping poll ticks, preserve falsy cache hits, abort in-flight fetches on imperative writes, recompute poll intervals across subscribers, evict idle stores, and tolerate empty/204 JSON bodies.
@Wibias

Wibias commented Jul 25, 2026

Copy link
Copy Markdown
Collaborator Author

Addressed Codex / CodeRabbit / maintainer review on client-resource + fetch-json:

  • useKeyedClientResource deps — element-wise invalidation now triggers refresh() when deps change (array identity ignored)
  • Slow polls — interval ticks skip while a request is in flight (no abort storm)
  • Falsy cache — loading gate uses data === undefined so false/0/null/""` stay cached
  • setClientResourceData — aborts in-flight + bumps generation so late responses cannot stomp
  • Multi-subscriber polling — tracks per-listener pollMs, uses Math.min, recomputes on subscribe/unsubscribe
  • Store eviction — deletes cache entry when the last subscriber leaves
  • fetch-json — 204 / empty-body success no longer throws SyntaxError

Type-safety of the string-keyed store map left as a documented API contract (trivial / no runtime check).

Wibias added 2 commits July 26, 2026 02:10
Delete leftover sessionStorage opencodex-api-token on auth install without reading it, and poll with a remaining subscriber's loader after others unmount.
Track the subscriber that started each request and invalidate it on unmount so a late resolve cannot overwrite shared snapshot data for remaining subscribers.
coderabbitai[bot]

This comment was marked as resolved.

Wibias added 3 commits July 26, 2026 02:25
…main

When the in-flight owner unsubscribes, start a replacement fetch from a remaining subscriber so the shared snapshot cannot stay stuck loading.
Keep poll timers stable under subscriber churn, force loading on keyed deps changes, expose undefined in fetch-json types, and tighten auth/resource tests.
Avoid attaching the memory API token or prompting on cross-origin absolute URLs that happen to use those path prefixes.
coderabbitai[bot]

This comment was marked as resolved.

Keep cached data when React tears down and reattaches a single subscriber for pollMs or enabled changes; only delete the store on the next tick if it stays empty.
@Wibias
Wibias merged commit d9e5102 into lidge-jun:dev Jul 26, 2026
9 checks passed
lidge-jun added a commit that referenced this pull request Jul 26, 2026
통합(dev push 완료): #437 803807a, #460 74ddd96, #431 82a47db,
#405 be16c1d, #468 후속 테스트 bcaf029.

close: 통합 4건 + 통합 없이 1건(#459, 설계 충돌). #457은 OPEN 유지.
이슈 close 0건 — dev에서 고쳐졌는데 안 닫힌 이슈가 없었다.

무효화: WP4(#466)와 WP6(#467)은 동료가 직접 머지했고, 우리 분석이
지적한 결함을 동료 커밋이 같은 방향으로 해소했다.

보안 보류 5건에 신규 발견 게시: #429 재분류, #355 OAuth 토큰 목적지,
#424 유료 브리지 기본 ON, #408 설치 락 해제, #403 4분할 요청.

A-gate는 사이클마다 3~5라운드를 돌았다. 반복된 원인은 계획서에
테스트나 코드를 적으면서 실물 소스를 확인하지 않은 것이었다.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants