falconer 0.3.2
falconer: ^0.3.2 copied to clipboard
A Chucker-style HTTP inspector for the Dio client: capture, inspect, search and export requests in a native UI. Supports Android and iOS.
0.3.2 #
Documentation only — no code, API or behaviour changes.
- Dropped the
Statussection from the README. It pinned a version number (v0.3.0) into prose that no release step updated, so it went stale the moment0.3.1shipped. Platform support is already stated in the intro, and pub.dev shows the current version itself.
0.3.1 #
Documentation only — no code, API or behaviour changes.
-
Install instructions now point at pub.dev.
0.3.0was published with a README written before the package existed on pub.dev: it said "Not yet on pub.dev" and told readers to add a git dependency pinned to a tag. The install path is nowflutter pub add falconer.The command deliberately does not include
dio. Every Falconer user already depends on Dio, andpub addon an existing dependency silently rewrites its constraint to the latest release rather than leaving it alone — sopub add dio falconerwould quietly bump a pinneddioconstraint.diostill belongs independencies, since your own code imports it.pub.dev renders the README from the published archive, per version, so correcting it required a release rather than a repo push.
-
Clarified that
falconerbelongs independencies, notdev_dependencies— app code referencesFalconerInterceptorin every build, while capture itself stays debug-only.
0.3.0 #
Makes debug-only capture a structural guarantee instead of a default that could be flipped, and puts a hard ceiling on how long captured payload can sit on the device.
First release published to pub.dev. 0.1.0 and 0.2.0 were development
releases consumed as git dependencies; neither was published to pub.dev, and
0.2.0 was never tagged. Install with flutter pub add falconer from this
version on.
Removed — BREAKING #
-
RetentionPeriod.foreveris gone;oneMonthis now the maximum window. Captured traffic is unredacted-by-default payload in an on-device SQLite file, and retention is the only thing that bounds it — there is no row or size cap. An unbounded window let a long-lived debug build accumulate request and response bodies indefinitely. Eventual deletion is now a property of the tool rather than a setting the host app has to remember to choose.Migration: replace
retention: RetentionPeriod.foreverwith the longest window you actually need (RetentionPeriod.oneMonthat most). Both native sides map the retiredforeverkey onto their one-day fallback, so an app pinned to an older plugin against a newer engine deletes more, never less.
Changed #
-
Default retention is now
oneWeek(wasoneDay) — a week of history is more useful for chasing intermittent failures, and capture is debug-only. Retention is the window in which captured payloads stay readable on the device, so pass a shorterretention:if your traffic is sensitive. Note sweeps are time-based only: there is no row or size cap, and response images are stored in-row, so a longer window grows the store without bound.The native pre-
configurefallbacks stay at one day; they apply only before the firstconfigurearrives and are replaced by it. -
The declared SDK constraints are now
sdk: ^3.9.0/flutter: '>=3.35.0'(were^3.9.2/>=3.3.0). The old Flutter bound was never reachable — the Dart bound already excluded every Flutter predating Dart 3.9, so>=3.3.0advertised support that could not resolve. The Dart bound is relaxed from^3.9.2to^3.9.0because Flutter 3.35.0–3.35.2 bundle Dart 3.9.0 and 3.35.3+ bundle 3.9.2; leaving it at^3.9.2would have kept 3.35.0–3.35.2 excluded regardless of the Flutter line. Widening only — no version that previously resolved is affected. -
The list screen and the ongoing notification now read a narrow column projection, not the full row. Those queries re-run on every write, and the wide row carries
requestBody,responseBodyand the in-row image BLOB — each capped atmaxContentLength(250 KB by default) — so a busy session was re-reading up to half a megabyte per transaction to draw a handful of short fields. Detail reads still take the full row. No API change. -
The transaction list shows a loading shimmer while the first read is in flight, instead of flashing the "nothing captured yet" empty state at a database that has not answered yet. Honours the platform's reduce-motion setting; a load that resolves within 120 ms never draws a placeholder at all.
The list projection and the shimmer ship inside the platform engines. iOS gets them from the pod; Android from
io.github.alifhasnain:falconer-*, now bumped to0.2.0on Maven Central (ext.falconer_version). Droppingforeverneeded no engine change on the wire: Dart simply stops sending the key, and an engine that still recognises it never hears it.
Removed — BREAKING (debug-only capture) #
-
FalconerConfig.enableInReleaseBuildsis gone.resolveEnablednow returnsfalsefor every release build, so no configuration can enable capture in release —enabled: trueincluded.enabledremains, honoured in debug builds only.The removed flag never delivered a working inspector in release anyway: the native engine is absent from release builds on both platforms (Method A), so setting it
trueonly re-enabled the Dart hot path and paid for payloads that the no-op engine discarded. Removing it deletes a footgun rather than a feature.Migration: delete
enableInReleaseBuilds:from yourFalconerConfig(...). If you need capture in a non-debug build,DOCUMENTATION.md §7documents the consumer-side build edits for a staging flavour. -
maybeWarnReleaseCapture/releaseCaptureWarned(internal) removed — the condition they warned about is now unreachable.
Added #
-
RetentionPeriod.oneMonth— a rolling 30 days, not a calendar month. The default staysoneWeek; this is opt-in viaretention:. Retention is the window in which captured payloads stay readable on the device, and sweeps are time-based only (no row or size cap, response images stored in-row), so a month on a busy app grows the store considerably. Prefer the shortest window that is still useful.Requires the matching engine release on Android. The Kotlin mirror ships in
falconer-impl; with an older engine,oneMonthfalls back silently to the one-day window. iOS ships in the plugin pod, so it needs nothing extra.Golden tests on all three sides now pin the retention wire-key set, so a one-sided addition fails the build instead of sweeping at the wrong interval.
Fixed #
-
Falconer.configureno longer throws into the host app. A failing platform channel is swallowed and reported viadebugPrint, matching the fire-and-forget policy the log calls already used — a diagnostic tool must not be able to break the app it inspects. Previously, callingconfigurebefore the binding existed threwBinding has not yet been initializedstraight out ofmain(), and the native side silently kept its default redaction set, so headers still appeared as**redacted**even whenredactHeaders: {}was passed.configurestill needs a live binding to reach the channel: callWidgetsFlutterBinding.ensureInitialized()before it if it runs at the top ofmain. The warning now says exactly that when the call fails.
0.2.0 #
Adds iOS support at feature parity with Android. No consumer API changes —
the shared Dart capture pipeline is unchanged, so the same FalconerInterceptor
and Falconer.configure(...) work on both platforms.
Added #
- iOS backend (Swift / SwiftUI): capture ingestion, system-
libsqlite3storage with configurable retention and a live count, and a native inspection UI (list → detail tabs → in-body search with highlight, JSON pretty-print, image preview). Minimum deployment target iOS 15. - Export on iOS. Share a transaction as cURL or text via the iOS share sheet
(
UIActivityViewController). - Entry points on iOS.
Falconer.launchUi()presents the inspector in its own window; shake-to-open is wired in debug builds (iOS has no notification-to-task model like Android). - iOS release stripping (Method A). The entire inspector is compiled behind
#if DEBUG, so a release IPA contains no inspector code — verified by symbol inspection (nm/strings):RealFalconerEngine, the SQLite wrapper and the SwiftUI views are absent from release; only the inertNoOpFalconerEngineand the thin plugin remain. No third-party pod, andlibsqlite3is linked in Debug only.
Notes #
- iOS ships inside the plugin pod — there is no separate native artifact
repository (unlike Android/Maven), so the Android engine coordinate
(
io.github.alifhasnain:*) stays at0.1.0. showNotificationis a no-op on iOS;requestNotificationPermission()resolvestrue(nothing to grant — the entry point is shake +launchUi).
0.1.0 #
First development release. Android-only; the API is pre-1.0 and may change.
Added #
- Dio interceptor. Add one
FalconerInterceptorto any Dio client to capture requests, responses and errors. Multiple clients share one list. - Capture pipeline. Two-phase logging (request / response / error) with body encoding for JSON, text, multipart and images; unsupported and truncated bodies are marked, never silently dropped.
- Storage. Room-backed persistence with configurable retention and a live
transaction count exposed over an
EventChannel. - Native inspection UI (Jetpack Compose): transaction list, detail tabs (overview / request / response) with swipeable navigation, in-body search with match highlighting, JSON pretty-printing and image preview.
- Export. Share a transaction as cURL or text, or save it to a
.txtfile. - Notification entry point. A foreground notification opens the inspector in its own task; runtime permission handling on Android 13+.
- Configuration.
Falconer.configure(FalconerConfig(...))for retention, max content length and the redact-header set.
Security & privacy #
- Sensitive headers (
Authorization,Cookie,Set-Cookie,Proxy-Authorization,X-Api-Key,X-Auth-Token) are redacted in Dart before crossing the platform channel; secrets never reach native logs or the database. - Capture is inert in release builds unless explicitly opted in with both
enabled: trueandenableInReleaseBuilds: true. - Do not capture cardholder data (PAN/CVV) or other regulated PII; exclude such endpoints and keep release capture disabled in payment/PCI-DSS contexts.
Notes #
- v1 is Android-only. All capture and redaction logic is pure Dart behind a platform interface, so iOS support can be added without API changes.
