Skip to content

Releases: asanre/verivent

v0.10.1

Choose a tag to compare

@asanre asanre released this 23 Oct 08:51

New

  • Exports say where a session was captured. The inspector's Export and the web viewer's download open with one "type": "source" entry: platform (android / ios), the app's version (appVersion, left out when the app declares none) and veriventVersion. Readers from 0.9.0 and 0.10.0 skip it, as they skip anything that isn't an analytics event.
  • Console lines carry the same source on every event, since a console has no header to keep it in. The analytics-events skill writes it as the opener of the session it rebuilds, and names the platform and versions in its report.

Changes

  • Android: the core adds a non-exported ContentProvider (VeriventInitProvider) to read the app's version. Remove it with tools:node="remove" to leave the version out.
  • Web viewer: the download is now the same file the inspector writes, by the device, through a new GET /api/export route (behind the same token, so a black-box test can fetch it too). It holds every captured event, even while the page is paused, and is indented by four spaces.

Full Changelog: v0.10.0...v0.10.1

v0.10.0

Choose a tag to compare

@asanre asanre released this 22 Oct 20:25

Breaking changes

  • Baseline: object Baseline with record()/diff() is gone. A baseline is now a flow, checked step by step:
    Baseline.parse(json).flow(waiter) { step("browse") { … } }, or Baseline.record(name) + toJson() to record it.
    • New: FlowScope.step/bound, Waiter, BaselineDiff/Finding, BaselineMismatch, and lastEvent(… on …) with shouldMatchPlan()/shouldHave().
  • Baseline files from 0.9 ("baseline": 1) are rejected with recorded with Verivent 0.9: record it again. Record them again.
  • iOS, Verivent.Request.path (the keepRequest filter) is now percent-encoded as sent (/my%20file), like Ktor's encodedPath. The captured path changes the same way.
  • Android: the live inspector notification no longer shows on the lock screen (VISIBILITY_SECRET).
  • Exports carry only analytics events. The inspector's Export and the web viewer's download leave network calls out; they stay in the live timeline. The format is the same, and the console lines already carried only events.

New

  • iOS UI tests: the VeriventUITesting product checks the app's analytics from an XCUITest against the same baseline files, with no test code in the app (VeriventApp.launch(app), flow { step {} }, bind, lastEvent).
    • Underneath are HTTP routes on the app's server (/api/baseline/*), behind the same token gate.
  • Path redaction: sanitizePath { } (Ktor) and redactPath: (iOS) rewrite a secret in a path (/reset/<token>). Off by default.

Fixes

  • An event with more than 10,000 lists and maps reports Too many lists and maps to check · over 10,000 per event instead of a false type error.
  • A body whose declared charset can't be used is shown as UTF-8 with a warning in the log, once per charset.
  • Collapsed timeline groups stay collapsed when their opening event is evicted.
  • A baseline sequence failure places an out-of-order event before the next event that did arrive.
  • A baseline recording writes each event's properties in alphabetical order, so iOS records the same file as Android, run after run.
  • iOS: each redirect hop is closed and handed back to the loading system, as Apple's URLProtocol contract asks, so it gets its own entry and the host's redirect delegate still runs.

Samples

Both samples are now the same small shop, with a Developer screen. They share one tracking plan and one set of baselines (sample/shared). The same baseline passes on Android and on iOS.

Full Changelog: v0.9.0...v0.10.0

v0.9.0

Choose a tag to compare

@asanre asanre released this 20 Oct 13:33

Full Changelog: v0.8.0...v0.9.0

v0.8.0

Choose a tag to compare

@asanre asanre released this 19 Oct 19:15

Breaking: Snoop is now Verivent

The library is renamed. Versions up to 0.7.0 stay on Maven Central as io.github.asanre:snoop*; from 0.8.0 on it ships as:

debugImplementation("io.github.asanre:verivent:0.8.0")
releaseImplementation("io.github.asanre:verivent-no-op:0.8.0")
debugImplementation("io.github.asanre:verivent-ui:0.8.0")
releaseImplementation("io.github.asanre:verivent-ui-no-op:0.8.0")
debugImplementation("io.github.asanre:verivent-ktor:0.8.0")
releaseImplementation("io.github.asanre:verivent-ktor-no-op:0.8.0")
  • Packages move from com.snoop.* to io.github.asanre.verivent.*.

  • Public types drop the brand, since the package carries it:

    0.7 0.8
    SnoopCore Inspector
    SnoopAnalytics / SnoopAnalyticsConfig AnalyticsSink / AnalyticsSinkConfig
    SnoopWebServer WebViewer
    SnoopEntry / SnoopStore TimelineEntry / TimelineStore
    ui Snoop InspectorLauncher
    Ktor plugin Snoop / SnoopConfig Verivent / VeriventConfig
  • Runtime strings follow: Logcat tags VeriventEvent and Verivent, notification channel verivent_inspector, header X-Verivent, theme Theme.Verivent.

  • iOS: the Swift package moves to https://github.com/asanre/verivent-swift.git (product Verivent, framework VeriventKit), and Snoop / SnoopURLProtocol become Verivent / VeriventURLProtocol. The old NativeEvents.configure overloads are gone.

Tracking plan

Load the analytics team's tracking plan and every event captured from then on is checked against it: a JSON Schema subset per event and channel (required, properties with type, enum and pattern, additionalProperties).

  • Each event the plan defines carries planVersion and issues, shown in the inspector, the web viewer, the JSON export and console lines. Messages state what the plan expects, never the value sent.
  • Load it from code (Inspector.loadTrackingPlan(json) / clearTrackingPlan(), or Verivent.loadTrackingPlan in Swift), by pasting it in the inspector's plan strip, from the web viewer's plan panel, or with adb into the storage directory.
  • Inspector.configure(storageDir = …) keeps the plan across launches; it is read and written off the calling thread.
  • A redacted property is only checked for required and type, so a verdict can't give its value away. A string over 256 characters isn't run through pattern and is reported as too long to check.

See the tracking plan guide.

Console lines

AnalyticsSink { logTo() } writes every captured event as one JSON line to Logcat (tag VeriventEvent) or the unified log on iOS, or pass logTo { … } to use your own logging. Long lines are split into [<id> i/n] pieces. On iOS: Verivent.configure(logToConsole: true).

Fixes

  • Ktor: a request timeout or cancellation while Verivent reads the response body is recorded as a failed call instead of logging a stack trace and leaving it pending.
  • Web viewer: a tracking plan upload is bounded in bytes, and every request that changes state needs the X-Verivent header.

Web viewer and inspector

  • The plan's verdict is shown on each event (✕ n / ✓), with a Breaks plan filter and a coverage view of the events the plan defines.
  • The timeline groups events whenever they carry a group label.

Full Changelog: v0.7.0...v0.8.0

v0.7.0

Choose a tag to compare

@asanre asanre released this 09 Oct 12:06

Breaking

  • SnoopAnalytics.log(..., userProperties = …) is now log(..., sections = …): a map from block title to parameters, shown below Parameters in the order given. Empty blocks are dropped. AnalyticsEntry.userProperties is now AnalyticsEntry.sections.

    // 0.6
    snoop.log(name, channel, properties, userProperties = currentUserProperties)
    // 0.7
    snoop.log(name, channel, properties, sections = mapOf("User properties" to currentUserProperties))

    properties is still the event itself: filter, annotate, groupBy, Raw and Copy payload only read it.

Fixes

  • snoop ships consumer R8 rules for the JVM-only classes Ktor server references, so minified Android builds no longer fail with missing java.lang.management classes.

Web viewer

  • The event detail has a close button; on narrow screens it covers the list instead of squeezing it.
  • More room between parameter blocks, in the web and Compose viewers.

Full changelog: v0.6.0...v0.7.0

v0.6.0

Choose a tag to compare

@asanre asanre released this 08 Oct 16:00

Breaking: artifacts merged

snoop-core and snoop-analytics are now a single snoop artifact (and snoop-core-no-op / snoop-analytics-no-op are snoop-no-op). Replace those coordinates with io.github.asanre:snoop / io.github.asanre:snoop-no-op. Packages and APIs are unchanged, so no source edits are needed. snoop-ui and snoop-ktor keep their coordinates and bring snoop along.

New: user properties per event

SnoopAnalytics.log(..., userProperties = ...) records the user properties in effect when an event was sent. The event detail shows them in their own User properties block (Compose and web viewer), redacted by sanitizeProperty like any parameter, and the export includes them.

Full Changelog: v0.5.0...v0.6.0

v0.5.0

Choose a tag to compare

@asanre asanre released this 07 Oct 17:43

Full Changelog: v0.4.0...v0.5.0

v0.4.0

Choose a tag to compare

@asanre asanre released this 28 Aug 22:04

Full Changelog: v0.3.0...v0.4.0

v0.3.0

Choose a tag to compare

@asanre asanre released this 27 Aug 15:12

Timeline grouping moves from viewer configuration to a mark stamped as the event is captured, the
inspector now insets itself against the system bars, and filters survive closing it.

Breaking changes

Grouping is configured on the analytics logger, not on SnoopCore. Only an analytics event can
ever open a group, so the setting used to be dead config in a build without snoop-analytics — and
the viewers still offered a Group chip that could do nothing. The logger now stamps
AnalyticsEntry.groupLabel at capture, and every viewer groups by that stamp.

// before
SnoopCore.configure(groupByEvent = "page_shown", groupLabelProperty = "page")

// after
val snoop = SnoopAnalytics { groupBy("page_shown", "page") }

Because the decision is made at capture, reconfiguring no longer regroups what is already in the
buffer — it applies to events logged from then on.

  • SnoopCore.groupByEvent and SnoopCore.groupLabelProperty are gone, along with their
    configure parameters. configure now takes capacity, maxTotalBytes and title.
  • SnoopScreen(groupByEvent = …, groupLabelProperty = …) loses both parameters; the per-instance
    override is gone. The Group chip still turns grouping off for a session, and it is now hidden
    entirely when nothing in the timeline can open a group.
  • The web viewer no longer requests /api/config; the route is removed. Grouping rides along on the
    entries themselves.

The Swift API is unchanged — Snoop.configure(groupByEvent:groupLabelProperty:) keeps its
signature and now routes to the analytics DSL.

Added

  • SnoopCore.configure(title = "…") renames the tool across every surface it draws: the inspector
    header, the Android and iOS notifications, and the web viewer's tab and header.
  • AnalyticsEntry.groupLabel, non-null on the event that opens a group. It is computed from the
    redacted properties, so a group never surfaces a value the host asked to hide.

Fixed

  • The inspector no longer slides under the status bar. With targetSdk 35+ edge-to-edge applies
    process-wide, and Snoop's hand-rolled top bar meant Material 3 never consumed the top inset.
    SnoopScreen now insets its own content against the status bar, navigation bar, display cutout
    and IME, while painting its background edge to edge behind them. This also fixes the notch and
    home indicator on iOS when a host embeds SnoopScreen.
  • Filters and grouping survive closing the inspector. Closing it finishes SnoopActivity and
    discards its saved state, so they used to reset — and the type/channel filters were lost even on
    rotation. The search query still resets on purpose: reopening behind a stale search that hides
    everything disorients more than it helps.

v0.2.0

Choose a tag to compare

@asanre asanre released this 13 Aug 08:17

Full Changelog: v0.1.0...v0.2.0