liquid_analytics 0.2.2 copy "liquid_analytics: ^0.2.2" to clipboard
liquid_analytics: ^0.2.2 copied to clipboard

Vendor-neutral analytics for Flutter. Emit one typed event and fan it out to any set of providers through a middleware pipeline and a built-in consent gate.

Changelog #

0.2.2 #

Fixes from a review of how the library handles personal data. No type signatures change; two behaviour changes are called out below.

  • Fixed: an unreadable delivery queue could permanently break startup. QueuedEntry.fromJson asserted every field and resolved both enums by name, and nothing between it and Liquid.init() caught the throw. A write truncated by a process kill or a full disk — or a queue persisted by a build with a ConsentCategory this one no longer has — therefore failed initialization on every launch, permanently, since nothing ever rewrites the bad blob. JsonDeliveryStore now skips entries it cannot decode and treats an undecodable blob as an empty queue, and the delivery engine starts empty rather than propagating a failure from a host-supplied store.

  • Fixed: revoking consent stopped delivery but not retention. Queued entries have been re-checked at drain time since 0.1.1, so nothing was ever sent on a stale grant — but a queue that never drained again kept them, on disk wherever a DeliveryStore is configured. Consent changes now evict matching entries immediately and write through the persistence debounce, because the point of a withdrawal is to stop retaining the data.

  • Fixed: an entry inside its retry backoff survived a revocation. The drain returned not-yet-due entries to the queue before consulting consent, so a revoked event could wait out its backoff — up to maxBackoff, five minutes by default — before being dropped. Consent is now judged ahead of the retry schedule.

  • Behaviour change: PiiRedactor also scrubs context. No bundled sink forwards context, so nothing was reaching a provider, but context is serialized into the delivery queue — an unredacted value was written to whatever store the app configured, and a custom sink that forwarded context would have leaked silently.

    Migration: none required; expect context values under a redacted key to be replaced.

  • Behaviour change: DebugSink is silent in release builds. Messages carry whatever properties and traits the caller attached, and platform logs are readable outside the app, so a debug sink left in a shipped build leaked data the consent gate never saw. The new enabled flag defaults to !kReleaseMode, following the precedent RecordingSink set with postDevToolsEvents.

    Migration: pass DebugSink(enabled: true) if you were relying on it logging in release.

  • JsonDeliveryStore now documents what it actually stores — the full message, in plain text, until the queue drains — with an atomic write-and-rename example and a note to keep the file out of automatic backups.

0.2.1 #

Delivery-engine hardening. No public API changes; one new optional option.

  • Fixed: events enqueued during a flush could be silently dropped. The drain worked from a snapshot and then rebuilt the queue from it, so anything enqueued while it awaited a slow sink was discarded. Later arrivals are now re-attached. This could lose events on any sink slow enough to overlap an enqueue, and is the most significant fix in this release.
  • Fixed: flush() no longer no-ops when called concurrently. It previously returned an already-completed future, so await liquid.flush() could return before anything was flushed — including on the app-backgrounding path. A call made mid-flush now schedules exactly one follow-up pass and every such caller shares it.
  • Removed the 250 ms polling timer. The engine polled every flush policy four times a second for the life of the process. FlushPolicy.shouldFlush is now a notifying setter, so policies wake the engine the moment a flush is due and an idle client costs nothing. Custom policies that assign shouldFlush directly keep working unchanged.
  • Queue writes are debounced. DeliveryStore.save replaces the whole queue, so persisting per enqueue was O(n) per event and O(n²) across a burst — exactly the outage the queue exists to survive. Tune with the new DeliveryOptions.persistDebounce (default 200 ms; Duration.zero restores write-through). The queue is always written on flush and on dispose.
  • Fixed: events emitted before init() are no longer discarded. The assert guarding this is stripped in release builds, and start() then replaced the queue with the stored one. Loaded entries are now merged ahead of anything already queued.

0.2.0 #

Two breaking behaviour changes. Both make the library fail safe rather than fail open; neither changes a type signature.

  • BREAKING — ConsentPolicy.map is now an explicit allowlist. Previously a category with no entry in the map was delivered to every capable sink. Now a non-empty map means a category with no entry reaches no sink, so forgetting to map a category withholds data instead of broadcasting it. An entirely empty map still means "routing is not configured" and every capable sink stays eligible, so ConsentPolicy.allowAll and the default policy behave as before. Debug builds log a one-time warning per unmapped category.

    Migration: if you pass a non-empty map, add an entry for every category you emit.

  • BREAKING — PiiRedactor now recurses. Redacted keys were matched only at the top level of properties and traits, so {'user': {'email': ...}} reached every destination that forwards nested structures (PostHog, Mixpanel, Amplitude, Segment). Keys are now matched at any depth, including inside maps held in lists.

    Migration: none required, but expect more values to be redacted. Iterables are normalized to lists, which is what the provider mappers already did.

0.1.1 #

Two correctness fixes in the consent gate and the event pipeline. Both are behaviour changes; neither changes the public API.

  • Consent is now re-checked when the delivery queue drains. Previously consent was evaluated only when an event was emitted and when it was enqueued. An event that was queued (batch mode, or awaiting retry after a sink failure) while consent was granted would still be delivered even if the user revoked consent before the flush — and because the queue is persisted, that could happen on a later launch. Queued entries whose category has since been denied, or which no longer route to the sink under ConsentPolicy.map, are now dropped; entries whose category has returned to undecided are held rather than sent.
  • Event order is now preserved when middleware is asynchronous. LiquidMiddleware.apply returns a FutureOr, but the client did not wait for one message to finish the pipeline before starting the next, so any middleware that awaited (device info, disk, network) let later events overtake earlier ones and reach sinks out of order. The pipeline is now serialized, which also fixes ordering of the consent buffer replay. A slow sink now delays the events behind it — use DeliveryOptions.batchMode for HTTP-heavy destinations.
  • Liquid.flush() now waits for in-flight pipeline work before draining, so events emitted immediately before the call are included.
  • A middleware that throws no longer stops subsequent events from being processed.
  • Add an example/ covering typed events, consent gating and fan-out.

0.1.0 #

  • Initial release of liquid_analytics.
  • Typed events, middleware pipeline, consent gate with buffer/replay.
  • Offline delivery queue with exponential backoff and optional persistence.
  • Batch mode with count/timer flush policies; lifecycle flush.
1
likes
160
points
287
downloads

Documentation

API reference

Publisher

verified publisherketok.id

Weekly Downloads

Vendor-neutral analytics for Flutter. Emit one typed event and fan it out to any set of providers through a middleware pipeline and a built-in consent gate.

Repository (GitHub)
View/report issues
Contributing

Topics

#analytics #flutter #consent #gdpr

License

MIT (license)

Dependencies

flutter, meta

More

Packages that depend on liquid_analytics