M3: SwiftUI app — TrafficList, Inspector, SQLite store#74
Conversation
There was a problem hiding this comment.
1 issue found across 12 files
Reply with feedback, questions, or to request a fix.
Re-trigger cubic
This is likely a transient issue. You can re-trigger a run from the dashboard. |
Fixes for PR #74 review comments (cubic): - FlowStore: protect against the persist/clear race. Each clear() bumps a generation counter; in-flight persistence tasks check the counter inside the database.write closure and skip the save if the user has cleared since the task was scheduled. Without this, a "Clear" click could be silently undone by a still-pending insert that landed afterwards. - New thread-safe GenerationCounter helper (NSLock-backed). Tests: - TrafficFilterTests: 10 cases covering search, host/method/status bucket filters, errors-only, status bucket boundaries - JSONFormatterTests: empty / valid JSON / non-JSON content types / shape detection / leading whitespace / invalid JSON - Package.swift: ReverseAPITests testTarget
|
@greptile review Generated by Claude Code |
e78f8e9 to
fbf9bd7
Compare
Fixes for PR #74 review comments (cubic): - FlowStore: protect against the persist/clear race. Each clear() bumps a generation counter; in-flight persistence tasks check the counter inside the database.write closure and skip the save if the user has cleared since the task was scheduled. Without this, a "Clear" click could be silently undone by a still-pending insert that landed afterwards. - New thread-safe GenerationCounter helper (NSLock-backed). Tests: - TrafficFilterTests: 10 cases covering search, host/method/status bucket filters, errors-only, status bucket boundaries - JSONFormatterTests: empty / valid JSON / non-JSON content types / shape detection / leading whitespace / invalid JSON - Package.swift: ReverseAPITests testTarget
33bfb0d to
3c7f333
Compare
|
@greptile review Generated by Claude Code |
Fixes for PR #74 review comments (cubic): - FlowStore: protect against the persist/clear race. Each clear() bumps a generation counter; in-flight persistence tasks check the counter inside the database.write closure and skip the save if the user has cleared since the task was scheduled. Without this, a "Clear" click could be silently undone by a still-pending insert that landed afterwards. - New thread-safe GenerationCounter helper (NSLock-backed). Tests: - TrafficFilterTests: 10 cases covering search, host/method/status bucket filters, errors-only, status bucket boundaries - JSONFormatterTests: empty / valid JSON / non-JSON content types / shape detection / leading whitespace / invalid JSON - Package.swift: ReverseAPITests testTarget
3c7f333 to
d9d7ff5
Compare
6a29aaf to
1d3b348
Compare
- ReverseAPI app target wired through Package.swift - AppState (@mainactor @observable) owns engine, flow store, installer, system proxy, capture toggles, CA install/uninstall - FlowStore: GRDB-backed SQLite persistence with reactive @observable array of CapturedFlow, subscribes to FlowBus, loads last 500 on boot - TrafficListView: SwiftUI Table with method/host/path/status/size/duration columns, search box, host/method/status/errors filters - InspectorView: Overview / Request / Response tabs with header table and JSON-aware body pretty printer - CaptureToolbar: capture, CA trust, system proxy, clear; status line - ProxyEngine: split stop() (channel close) from terminate() (also shuts down the event-loop group) so capture can be toggled - NIOFoundationCompat added to ReverseAPIProxy deps
Fixes for PR #74 review comments (cubic): - FlowStore: protect against the persist/clear race. Each clear() bumps a generation counter; in-flight persistence tasks check the counter inside the database.write closure and skip the save if the user has cleared since the task was scheduled. Without this, a "Clear" click could be silently undone by a still-pending insert that landed afterwards. - New thread-safe GenerationCounter helper (NSLock-backed). Tests: - TrafficFilterTests: 10 cases covering search, host/method/status bucket filters, errors-only, status bucket boundaries - JSONFormatterTests: empty / valid JSON / non-JSON content types / shape detection / leading whitespace / invalid JSON - Package.swift: ReverseAPITests testTarget
d9d7ff5 to
c27a3ee
Compare
There was a problem hiding this comment.
1 issue found across 2 files (changes from recent commits).
Tip: Review your code locally with the cubic CLI to iterate faster.
Re-trigger cubic
There was a problem hiding this comment.
1 issue found across 3 files (changes from recent commits).
Tip: Review your code locally with the cubic CLI to iterate faster.
Re-trigger cubic
There was a problem hiding this comment.
2 issues found across 3 files (changes from recent commits).
Tip: Review your code locally with the cubic CLI to iterate faster.
Re-trigger cubic
There was a problem hiding this comment.
2 issues found across 6 files (changes from recent commits).
Tip: Review your code locally with the cubic CLI to iterate faster.
Re-trigger cubic
There was a problem hiding this comment.
1 issue found across 4 files (changes from recent commits).
Tip: Review your code locally with the cubic CLI to iterate faster.
Re-trigger cubic
|
You're iterating quickly on this pull request. To help protect your rate limits, cubic has paused automatic reviews on new pushes for now—when you're ready for another review, comment |
Stacked on top of #73.
First UI slice — a working SwiftUI macOS app on top of the proxy engine.
What's in
ReverseAPI(alongsiderae-proxy) with@mainSwiftUI App@MainActor+@Observable) — owns the engine, flow store, CA installer, system proxy controller. Handles toggle capture / install CA / enable system proxy / clear, all backgrounded off the main thread where they may block.~/Library/Application Support/ReverseAPI/flows.sqlite. Subscribes toFlowBus, keepsflows: [CapturedFlow]reactive, persists finished flows asynchronously, restores the last 500 on launch.Tablewith Time / Method / Host / Path / Status / Size / Duration columns + search + filter menu (errors-only, by host / method / status bucket).ProxyEngine fix
Split
stop()(just closes the listening channel) fromterminate()(also shuts down the event-loop group). Without this, capture could only be toggled once.Run
First launch: click Install CA, then System proxy, then Start capture. Every app that respects the macOS proxy will now show up in the list.
Out of scope
Generated by Claude Code
Summary by cubic
Adds a SwiftUI macOS app named
raewith a live traffic list, inspector, and SQLite persistence, plus safer system-proxy lifecycle and clean shutdown. Responses stream to the client while capturing to cut latency, and the UI does less work during live capture.New Features
ReverseAPIapp target andraeexecutable;AppStatemanages the proxy engine, CA trust, system proxy, reactive store, and a boot-failure view.FlowStore:GRDB.swift-backed SQLite (last 500 flows), subscribes toFlowBus, writes finished flows off the main thread, restores recent flows on launch; exposes incremental host/method options to avoid recompute churn.TrafficListView: table with search and filters (errors, host, method, status buckets, resource types).InspectorView: Overview/Request/Response tabs with JSON pretty print, text/binary fallback, and timings.raeon launch; targeted disable andisEnabled(host:port)check; lifecycle restores the proxy on window close and quit.Bug Fixes
Accept-Encoding: identityfor clearer bodies.ProxyEngine.stop()(close channel) fromterminate()(also shuts down the event-loop group) for repeatable toggling and clean app exit; hook app lifecycle for graceful shutdown.FlowStore: guard persist/clear races via a generation counter so in-flight writes are skipped after Clear; reduce UI work by caching host/method options and size formatting.Written for commit 773e59f. Summary will update on new commits. Review in cubic
Greptile Summary
Adds the first SwiftUI macOS UI slice on top of the existing proxy engine: a live traffic list (
TrafficListView), a request/response inspector (InspectorView), a GRDB-backed SQLite flow store (FlowStore), and anAppStateobject that ties capture toggling, CA trust installation, and system-proxy control together. TheProxyEngineis also fixed sostop()andterminate()are separated, enabling repeated capture toggling.@MainActor+@Observabledesign is clean;persist()uses a generation counter to guard against clear-vs-write races; synchronousloadInitial()/clear()DB calls on the main actor and silent swallowing of persist errors are flagged in existing review comments.Tablewith seven columns, a filter bar for host/method/status/errors, and search;ByteCountFormatteris re-allocated per-cell-render andhostOptions/methodOptionsare O(n) scanned on every flow update (noted below).terminate()is correctly split out but is not yet wired into the SwiftUI app lifecycle (existing comment).Confidence Score: 5/5
The changes are well-structured and the new UI layer correctly delegates blocking work off the main thread; no regressions are introduced.
The new code introduces no data-loss or correctness regressions; the generation counter correctly handles clear-vs-persist races, the subscription and weak-self patterns are correct, and the filter logic is well-tested. The only new findings are minor render-efficiency nits in TrafficListView.
FlowStore.swift carries the most open risk from prior review comments (synchronous DB I/O on main actor, silent persist failures); TrafficListView.swift has the two new render-efficiency notes.
Important Files Changed
Comments Outside Diff (3)
macos/Sources/ReverseAPIProxy/ProxyEngine.swift, line 1149-1152 (link)terminate()is never called at app exitterminate()was added to cleanly shut down the NIOEventLoopGroup, but nothing in the SwiftUI app orAppStateever invokes it.stopCapture()only callsengine.stop(), which closes the listener but leaves the thread pool running. When the user quits the app, NIO's threads are abandoned to OS process cleanup rather than being gracefully drained. Sinceterminate()is the only path togroup.shutdownGracefully(), anAppState.terminate()method (hooked into the SwiftUI scene lifecycle viaonReceive(NotificationCenter.default.publisher(for: NSApplication.willTerminateNotification))orNSApplicationDelegate.applicationWillTerminate) would close this out cleanly.Prompt To Fix With AI
macos/Sources/ReverseAPI/Storage/FlowStore.swift, line 358-370 (link)Both error paths in
persist()discard the error: thePersistedFlow(from:)conversion bails out with a barereturn, and the GRDB write usestry?which drops any write failure. If the database is full, the path is wrong, or the file is corrupt, finished flows are lost with no signal to the user or any log output. Even aprint/Loggercall on the catch branch would make this diagnosable.Prompt To Fix With AI
macos/Sources/ReverseAPI/Storage/FlowStore.swift, line 327-332 (link)loadInitial()(called frominit) andclear()both use the synchronousdatabase.read/database.writeAPIs, which block the calling thread. BecauseFlowStoreis@MainActor, these block the main thread. With 500 rows on cold start or a large DB on clear, this can visibly stall the UI.persist()correctly offloads to a detached task; the same pattern (ordatabase.asyncRead/asyncWrite) should be applied here.Prompt To Fix With AI
Prompt To Fix All With AI
Reviews (3): Last reviewed commit: "M3 review fixes + tests" | Re-trigger Greptile