kitty_sdk 0.11.0
kitty_sdk: ^0.11.0 copied to clipboard
Kitty Flutter SDK — unified package for device fingerprint attribution (Flare) and authentication, user management, licenses & subscriptions (Account) against the Kitty Launch platform APIs.
0.11.0 #
- New: Google Play client purchase —
KittyAccount.loginWithPlayStore()andKittyAccount.claimPlayStoreSubscriptions(). The Play analog of the App Store pair, for apps whose Android purchases must land on the Kitty account layer. Android hands the client an opaque purchase token rather than a signed transaction, so both take{packageName, purchaseToken}and authenticity comes from server-side verification against the Google Play Developer API —loginWithPlayStorereturns null when no account matches the purchase, and throws when the server could not verify it (409 not-entitling, 503 verification unavailable) so a purchase can never read as "logged in" on an unconfirmed token.claimPlayStoreSubscriptionsbinds a verified purchase to the signed-in account and transfers the licence off the virtualtxn_*@googleplay.internaluser. New public modelsPlayStoreLoginResult,ClaimPlayStoreResult,ClaimPlayStoreDetail. Additive: no host changes required, and no behaviour changes for apps that do not call them. - New:
KittyAccount.getFirebaseToken()— mints a Firebase custom token via the new authenticatedGET /api/account/vault/firebase-login(factory/account/vault/firebase-loginwire parity:{accessToken}), so the Firestore-backed legacy media gallery keeps working when an app runs on the Kitty account backend. Token-only by design: the SDK carries nofirebase_authdependency — the app performsFirebaseAuth.signInWithCustomTokenitself. The bearer is attached and refreshed byAuthorizedHttpClient;x-project-idselects the per-project Firebase credentials server-side. - Added
example/— a rehearsal host that exercises the legacy auth bridge end to end against a local stack. - Legacy auth bridge: users are no longer logged out at backend cutover. An app updated onto the Kitty backend is holding an access/refresh pair that Kitty never issued and cannot validate, so the first authenticated call after cutover returns 401 and the user lands on a login screen. The SDK can now exchange that pair for a real Kitty session. New
KittyAccount.adoptLegacySession({accessToken, refreshToken}): call it once at startup with the pair read from the app's own pre-Kitty storage, and the SDK stores it asTokenOrigin.legacy, exchanges it atPOST /account/vault/login-legacyon the first 401, retries the original request, and returns the restored account. It returns null when the exchange is refused — show the ordinary login screen then. Calling it while a Kitty session already exists is a no-op, so it is safe to call unconditionally at startup. - A failed exchange is always recoverable. The legacy pair is never cleared on failure — not on a 401, not on a 5xx, not on a malformed response — so a refusal is retried on the next launch rather than stranding the user permanently. This also required a fix in
AccountRepository.refreshAccountInfo: itsReauthRequiredExceptionhandler cleared all stored tokens, which would have destroyed the legacy pair on the very first failed exchange, including on theinitialize()launch path. It now keeps a legacy pair while still dropping the cached account, so the app shows login and the next launch can retry. An explicitlogout()still clears everything. - The exchange runs at most once per launch, never in a loop. The attempt is recorded before the first
await, so concurrent 401s coalesce into a single request and every later caller sees that attempt's outcome — including its failure — rather than triggering another. - Ships dark. No behaviour changes for any app that does not call
adoptLegacySession: without it nothing is ever taggedTokenOrigin.legacy, the new branch is unreachable, and no bridge request is made. The exchange is additionally inert unless the account config supplies a project id, andx-project-idis sent on the exchange only — deliberately not added todefaultHeaders, which would put it on every authenticated request. - Fix — a failed token refresh was reported to concurrent callers as a success.
AuthorizedHttpClientcompleted its refresh waiters with a hardcodedtruein afinallyblock, while the refresh itself returnedfalseon all three failure paths (no refresh token, non-2xx, exception). Waiters were told the refresh had succeeded and retried with the still-stale token, hitting 401 again and re-entering refresh — roughly tripling the calls reaching the endpoint on a day when refreshes fail. Waiters now receive the real outcome. (The same defect exists in the pre-Kitty client this SDK forked from.) - New public API:
TokenOrigin(kitty/legacy) onAuthTokens, plusTokenStorage.originandTokenStorage.hasLegacyPair.TokenOrigin.legacyis reachable only throughadoptLegacySession—AuthTokens.fromJsonalways yieldskitty, so no server response can put the SDK into legacy mode. Origin is local bookkeeping and is not serialized bytoJson. An install that predates this change has no stored origin and reads askitty, so no migration is needed. - Additive and backward-compatible: no host changes are required, and the refresh-coalescing fix applies to every app on a version bump alone. 44 new tests under
test/account/(75 total). - New pre-login checks + password-reset facade surface (for the mobile-nidrana account-layer swap).
VaultApiClientgainsuserExist(GET /account/vault/users/exist— the pre-login existence check the mobile login/signup UX branches on) andcanLoginWithPassword(GET /account/vault/can-login-with-password; note it returns false for both "no such user" and "user exists but passwordless", so pair it withuserExistwhere that distinction matters). Both are exposed onAccountRepository(isUserExist/canLoginWithPassword, deliberately silent in analytics — they run on every login-screen step) and onKittyAccount, which additionally now surfaces the existing repository methodssendResetPasswordCode,validateResetPasswordCodeandresetPassword(resetPasswordalso logs the user in — the server mints a session, matching the legacy flow). 5 new client tests.
0.10.19 #
- Account: v2
/users/currentclient + per-license token support. NewUsersApiClient.getCurrentUserV2()callsGET /api/account/v2/users/current— same contract as v1, plus every license carriestokensPerCycle(renewable allowance per billing cycle) andsupportsTokens(plan-level flag,tokensPerCycle > 0).Licensegains both fields with safe defaults (0/false), so v1 responses and previously cached accounts keep parsing unchanged;toJsonroundtrips them through the SDK's account cache. NotesupportsTokens: falsedoes not mean a zero balance — one-time top-up tokens live inGET /api/account/tokens/balance. Requires a backend with the/api/account/v2/users/*routes deployed; additive and backward-compatible.
0.10.18 #
- Auth: valid login codes are no longer rejected as "Please enter a valid code" when PostHog isn't ready. Root cause:
_resolveSid()threw aStateErrorwhen analytics wasn't initialized, or produced an emptysidwhen PostHog wasn't actually ready (systematic on web, whereinitPostHogWebsilently no-ops if theposthogJS global hasn't loaded yet while the SDK still reports analytics as initialized). The emptysidwent on the wire and failed the backend'sverify-codevalidation with a 400, and the UI treated every error as a wrong code — so a correct code produced a code-blame message, sometimes without any network call at all. Fixes: (1)sidis resolved defensively — never throws, never empty; when unavailable it is omitted fromverify-code/signup-password(the verify-code backend treatssidas optional attribution metadata, so login without it is fully supported); (2) after a successful code login that went out without a sid, the real distinct id is attached best-effort viaattach-sid(the same pattern App Store login already used, now shared); (3)verify-codemaps 400 →AccountErrorType.badRequestand 429 →tooManyAttempts(401 staysloginCodeNotValid); (4) the code screen now shows a generic "Something went wrong… contact us" message for non-code failures instead of blaming the code. - Behaviour change —
KittyAuthCodeState.isWrongCodeis narrower (exported public API). It waserrorType != null && errorType != noInternet, i.e. true for any error except offline. It is now true only for a real code rejection (loginCodeNotValid/invalidCode). Two new getters accompany it:isOfflineandhasGenericError(any error that is neither a code rejection nor offline). Hosts that branched onisWrongCodeto mean "something failed" must switch toerrorType != null. - Offline stays delegated to the host.
hasGenericErrorrenders inline under the code input;noInternetdeliberately renders nothing inline, matching the existing login form — the auth flows already invoke the host'sonNoInternetcallback, and duplicating the message inline would double up with the host's own snackbar. - API restored —
sidis back onKittyAccount.verifyLoginCode. It was removed in 0.2.2, when sid resolution moved entirely insideAccountRepositoryon the assumption that analytics is always available. That assumption is what broke login here, so the caller-supplied override returns: an explicitsidwins over auto-resolution, and a blank/whitespace value is normalized to "absent" rather than sent as an empty string. AdditionallyKittyAccount.initialize/KittySdk.initializeaccept aSidResolver(sidResolver/accountSidResolver) — an asyncString? Function()— so hosts that already know their analytics id can supply it directly instead of depending onKittyAnalytics. Exported asSidResolver. - Failure-mode change for
signupWithPasswordwithout analytics. It previously threw a client-sideStateErrorbefore any request. It now omits the sid and reaches the server, which still requiressidforsignup-password, so the call fails with a 400ApiExceptioncarrying a clear validation message. Hosts catchingStateErroraround signup must widen toApiException. Relaxing the server-side signup schema is tracked separately; onlyverify-codeis fixed end-to-end here. - Attribution can never break or stall login. Sid resolution and the post-auth attach are both bounded:
getDistinctId()is capped at 500 ms (a half-booted PostHog on web can leave that Future pending forever, which would hang login before any network call — the one original failure mode with no fallback), and the lateattach-sidis capped at 2 s (it runs through the authorized client, whose 401 retry ladder adds 1 s + 2 s of backoff while the already-authenticated user waits). Both failures are swallowed by design — sid is attribution-only — and a regression test pins the invariant from both sides. - New diagnostics via the existing
onAnalyticsEventhost callback (deliberately host-side, since these fire exactly when PostHog is unavailable):LoginSidAttachedAfterAuthwhen the late attach succeeded, andLoginSidUnavailablewith areasonproperty distinguishingresolver_threw/analytics_uninitialized/analytics_timeout/analytics_blank— so a host whosesidResolverthrows is not indistinguishable from PostHog merely not being ready. - Additive and backward-compatible apart from the
isWrongCodenarrowing and the signup failure-mode change noted above: no host changes are required to get the login fix, only a version bump. First tests for the account client, repository, auth state and code form (27 new tests undertest/account/).
0.10.17 #
- Settings: hide an untrusted billing period instead of showing a misleading "1 year".
KittySubscriptionPagenow derives the card's billing period via the newreliableBillingPeriod(License)helper. A web/Lago license whosedurationis exactly the Account API's un-populated default (365 days) is ambiguous — a genuine yearly plan vs the old default that was never populated — so the period is treated as unknown and omitted (the card falls back to the plan name). Healed/real Lago periods (weekly / monthly / …) and all App Store / Google Play durations — including a genuine yearly 365 — are shown as before. This guarantees no user sees a wrong billing cycle while legacy web licenses back-fill their realdurationon renewal. Trade-off: a genuine Lago yearly license hides its period until its duration is populated. New helpershasReliableBillingPeriod(License)/reliableBillingPeriod(License)inlicense_helpers.dart(exported viakitty_sdk.dart); first unit tests added undertest/account/ui/settings/helpers/. - Additive and backward-compatible: no API/schema change; apps get the safer display with a
kitty_sdkversion bump.
0.10.16 #
- Settings: Web2Wave "manage subscription" card.
KittySubscriptionPagenow renders a bordered card instead of a flat list: a body header, a current-phase block (status badge + trial/plan title + price + expiry), and — for a trial that will auto-convert — a separate Upcoming block showing the post-trial charge (amount + next-charge date). Non-trial or non-renewing licenses collapse to a single block. The cancel/renew action and its analytics events (CancelSubscriptionButtonClicked/RenewSubscriptionButtonClicked) are unchanged. A legal-links footer (Privacy Policy / Terms of Use / Subscription Policy) now appears on the page whenever those URLs are configured — required for web2app funnel compliance ("manage without leaving"). - Settings: top-level "Manage subscription" entry.
KittySettingsPagenow shows a Manage-subscription item directly on the Settings screen (new optionalonSubscriptionTap/onSubscriptionsTap; visible when logged-in with ≥1 license — a single license opens the detail card, multiple opens the list). Previously it was reachable only via Account → Manage subscriptions. Backward-compatible: the entry is hidden when the callbacks aren't wired. - New reusable widget
KittyLegalLinks. The three legal links are extracted fromKittyLegalInfoPageinto a shared widget (same labels, analytics events, andlaunchUrlbehaviour) so they also render as the subscription-card footer. - New helpers (exported via
kitty_sdk.dart):shouldShowUpcomingBlock(License)andtrialLengthLabel(License, {suffix}). The latter derives the trial length fromexpiresAt - createdAt, falling back to a bare label when the dates are missing or the span is non-positive. - Status-fidelity badge. The current-phase badge reflects the true license status — Active / Cancelling / Suspended / Expired — rather than a binary, so a dunning (
suspended) orcancellinglicense no longer reads as green "Active". The header ("You have 1 active subscription") is shown only for active licenses; a trial always displays its expiry (the charge, when known, lives in the Upcoming block). - Theme:
KittySettingsCardTheme+KittySettingsBadgeThemeadded toKittySettingsTheme(ascard/badge). All fields are nullable with sensible fallbacks, so the card renders correctly with no host theming; overridecard/badgeto brand the container, divider, and the Active / Upcoming / Cancelling / Suspended / Expired badge colors. - New
KittySettingsConfigcopy fields:manageSubscriptionHeader,subscriptionActiveBadgeLabel,subscriptionUpcomingBadgeLabel,subscriptionExpiredBadgeLabel,subscriptionCancellingBadgeLabel,subscriptionSuspendedBadgeLabel,trialLabel,subscriptionSuffix(all with English defaults, threaded throughresolveWithSdkConfig), plus ahasLegalLinksgetter. The old flat-row copy fieldscurrentPlanPrefix,statusPrefix,pricePrefix,billingPeriodPrefix, andnextChargeAmountPrefixare no longer rendered by the card and are now@Deprecated(kept for source compatibility). - Additive and backward-compatible: apps get sensible defaults with no changes beyond a
kitty_sdkversion bump; theming the card and overriding copy are optional.
0.10.15 #
- Settings:
onAccountDeletedcleanup hook onKittySettingsConfig. New optionalFuture<void> Function()? onAccountDeleted.KittySettingsCubit.deleteAccount()invokes it immediately beforeKittyAccount.deleteAccount()— while the session/token is still valid — so host apps can wipe product-specific user data (e.g. images stored in their own backend) as part of account deletion. The call is wrapped in a guard: any error it throws is caught (tracked asAccountDataCleanupFailed) and never blocks account deletion. Additive and backward-compatible — apps that don't set the callback are unaffected.
0.10.14 #
- Fix: the "contact us" link in login-form error messages did nothing unless
KittyAuthUiConfig.onSupportEmailTapwas set. The code form already fell back to opening amailto:link withKittySdkConfig.supportEmailwhen the callback was not provided, but the login form wired the tap recognizer straight to the (possibly null) callback, so with onlysupportEmailconfigured the link was dead. Both forms now share one helper:onSupportEmailTapif provided, otherwisemailto:to the SDK-configured support email.
0.10.13 #
- Fix: Apple Pay button missing on the web buy-now checkout (Flutter web). On Flutter web
KittyWebBuyNowPagerenders the checkout inside an iframe (flutter_inappwebview web implementation). Safari exposesApplePaySessionto cross-origin iframes only when the iframe carriesallow="payment", so the embedded checkout silently hid the Apple Pay button while Google Pay and card stayed visible (the checkout'ssupportsApplePay()gate sawtypeof ApplePaySession === 'undefined').InAppWebViewSettingsnow setsiframeAllow: 'payment'. Native iOS builds are unaffected: WKWebView never exposes Apple Pay JS — the StoreKit IAP path covers native. Note: consumer apps that still ship their own pre-migration copy of this page (e.g. mobile-renovio) need the same one-line change locally.
0.10.12 #
- Settings: Subscription Policy link on the legal info page. New
subscriptionPolicyUrlonKittySdkConfig/KittySettingsConfig(+subscriptionPolicyLabel, default "Subscription Policy"). When set,KittyLegalInfoPagerenders it next to Privacy Policy and Terms of Use (tracked asSettingsSubscriptionPolicyClicked). Required for web2app funnel compliance (refund policy visibility). - Settings: subscription page now shows renewal info.
KittySubscriptionPagerenders Price (license.price+ currency), Billing period (derived fromlicense.duration) and — for active auto-renewing subscriptions only — Next charge date (subscriptionNextChargeDate, falling back toexpiresAt) and Next charge amount (subscriptionNextChargeAmount, when > 0). Cancelled/cancelling plans keep showing only the expiry date. New label overrides:pricePrefix,billingPeriodPrefix,nextChargeDatePrefix,nextChargeAmountPrefix. - Fix: "Current plan" value was clipped on the subscription page. The default
currentPlanPrefixputs the value on a second line ('Current plan:\n'), butKittySettingsMenuItemhard-capped text at one line, so the plan name never showed. The menu item now takes amaxLinesparameter (default 1) and the current-plan row uses 2. - Added
formatDate,formatLicensePriceandformatBillingPeriodhelpers tolicense_helpers.dart(exported viakitty_sdk.dart).
0.10.11 #
- Fix: on mobile web browsers the on-screen keyboard did not appear when tapping the login code input. The code screen appears after the async send-code call, so
KittyCodeInput'sautofocusgrabbed focus outside a user gesture — mobile browsers focus the hidden input but keep the keyboard hidden, and a tap on the already-focused field was a focus no-op (the user had to tap elsewhere and tap the input again). Tapping the code input while it already holds focus now forces a blur → refocus cycle within the tap's own event task (web only; native platforms unchanged), so the keyboard pulls up on the first tap.
0.10.10 #
- Fix:
AccountDeleteRequested(andLogOutSuccess) never reaching PostHog. The PostHog native SDKs batch events (flush at 20 events / every 30s) and account deletion is a terminal flow — the queued event was waiting for a batch flush that never happened because the app was killed or uninstalled right after.AccountRepository.deleteAccount()andlogout()now force an immediate flush of queued events before tearing down. Additionally,LogOutSuccessis now tracked beforeKittyAnalytics.reset()so it is attributed to the logging-out user instead of a freshly rotated anonymous distinct ID. - Added
KittyAnalytics.flush()— flushes queued PostHog events immediately (no-op on web, where the JS SDK manages its own send cycle). Use it before any flow where the app may be killed before the next scheduled batch flush.
0.10.9 #
- Fix (follow-up to 0.10.8): buy-now loader still spun forever on StoreKit 2 cancel with
in_app_purchase_storekit0.4.4–0.4.6. 0.10.8 only handled the case whereSK2Product.purchasereturnsuserCancelled/pending(0.4.7+). On 0.4.4 the native layer instead throws aPlatformException(storekit2_purchase_cancelled/storekit2_purchase_pending), whichBuyNowHelper.buySubscriptionsilently swallowed (itson PlatformExceptionhandler only coveredstorekit_duplicate_product_object), so the bloc never leftPurchaseInProgressState. NowIOSSK2ExtendedProductDetails.buy()handles cancel/pending whether reported as a thrownPlatformExceptionor a returned result, andbuySubscriptionrethrows any otherPlatformExceptioninstead of swallowing it. Fixes the loader for the whole^0.4.0range.
0.10.8 #
- Fix: in-app buy-now loader spinning forever when the user cancels the native payment sheet (StoreKit 2).
KittyInAppBuyNowPagekept its loading overlay up indefinitely if the user dismissed the StoreKit payment dialog. Under StoreKit 2 (the default inin_app_purchase_storekit>= 0.4) the idiomaticbuyNonConsumablediscards the purchase result and StoreKit emits nothing onpurchaseStreamfor a user cancellation, so the bloc never leftPurchaseInProgressState. The SK2 purchase path now reads theSK2ProductPurchaseResultdirectly and surfacesuserCancelled/pendingas aBuyNowPurchaseNotCompletedException, which the buy-now bloc handles by clearing the loader (and trackingBuySubscriptionCanceled/BuySubscriptionPending). StoreKit 2 stays enabled; StoreKit 1 and Android behaviour are unchanged (cancellation already arrives via the purchase stream there).
0.10.7 #
TokenBalancenow exposes a per-license breakdown.GET /account/tokens/balance?projectId=...now returns an additionallicensesarray (one entry per license belonging to the authenticated user in the project, withlicenseId,balance,subscriptionBalanceandtotal). The top-level aggregate fields keep the same shape as before, so existing code continues to work.- For single-license responses (
?licenseId=...) the samelicensesarray is returned with a single entry mirroring the top-level balance. - Added
LicenseTokenBalancemodel andTokenBalance.licenses/TokenBalance.licenseBalance(licenseId)helpers (exported fromkitty_sdk.dart).
0.10.6 #
- Aggregated balance is now a single backend request.
GET /account/tokens/balanceacceptsprojectId(in addition tolicenseId) and the server returns the sum ofbalance/subscriptionBalance/totalacross the user's licenses in that project.KittyTokens.getBalance()(withoutlicenseId) makes one HTTP call instead of one per license — no more fan-out from the client. - Added
TokensRepository.getAggregateBalanceForProject({required String projectId})and the matchingTokensApiClientmethod for SDK consumers that need the aggregate from a custom repository. - Aggregated
TokenBalance.licenseIdis nownull(returned as such by the backend).
0.10.5 #
KittyTokens.getBalance()now aggregates across all of the user's licenses when called withoutlicenseId. PasslicenseId:explicitly to get the balance for a single license.TokenBalanceextended: now exposeslicenseId,subscriptionBalanceandtotal(=balance + subscriptionBalance) returned by the backend.licenseIdisnullfor aggregated balances.TokensState.tokenCountnow returnsbalance.totalinstead ofbalance.balance, so the built-in account-page UI shows the full spendable amount (top-up + subscription cycle tokens). If you relied ontokenCountto show only the top-up part, switch tostate.balance?.balance.- Returns a zero
TokenBalance(instead of throwing) when the authenticated user has no licenses.
0.10.4 #
- Tokens module aligned with new backend contract (breaking):
/account/tokens/balanceand/account/tokens/operationsnow require alicenseId(UUID) query param instead ofprojectId.KittyTokens.getBalance()/getOperations()andTokensCubit.loadBalance()/loadOperations()now accept an optionallicenseId:argument; when omitted the SDK picks the first usable license of the currently authenticated user. - Removed
KittySdk.tokens.addTokens/removeTokens: those endpoints are no longer exposed to clients — they live underinternal-tokensand require an internal API key or a per-projectkap_bearer token. Call them from your backend instead. KittyTokens.initializeno longer takes aprojectId(licenses already carry their own project association).
0.10.2 #
- Logout cleanup: Account logout now resets PostHog and clears local auth state;
KittySettingsConfig.onLogoutlets host apps run custom settings-page logout handling. - Auto-identify control: Added
enableAutoIdentifytoKittySdk.initialize()so apps can disable startup fingerprint identify and prevent automatic PostHog aliasing.
0.10.0 #
- Buy Now module migrated from
mobile-renovio: full in-app and web buy-now flow with bloc state management, web purchase bridge and restore helpers. - Buy Now configuration: new
KittyBuyNowConfigwithproductIds,webBuyNowUrl,pwaBuyNowUrl(Stage),domain,eulaUrl,privacyPolicyUrl,customerSupportEmail,isStaging,isAutoRestorePurchaseEnabled. Wire it throughKittySdkConfig.buyNowand access globally viaKittySdk.buyNow/KittyBuyNow.I. - Buy Now UI theming: added
KittyBuyNowThemeforKittyInAppBuyNowPageandKittyWebBuyNowPage, split into logical blocks:page,navBar,mainBlock,subscription,buttons,legal, andwebView. Replaces the previousInAppBuyNowTheme. KittyBuyNowThemeandKittyBuyNowThemeProviderare exported fromkitty_sdk.dart.
0.9.0 #
- Account UI theming:
KittyThemenow accepts a directThemeDataoverride viaKittyTheme(themeData: ...). The SDK merges it over the default SDK theme when provided. - Auth UI theming: Added
KittyAuthThemeforKittyAuthFlowandKittyBindEmailFlow. It supports per-flow styling for background, AppBar, header widgets, titles, inputs, buttons, code input boxes, resend/support sections, spacings, and errors. - Settings UI theming: Added
KittySettingsThemeforKittySettingsFlow, split into logical blocks:page,menuItem,buttons,destructive,subscription,tokens,dialog, andbindEmail. KittyAuthUiConfig.iconis deprecated. UseKittyAuthTheme.loginHeaderWidgetorKittyAuthTheme.codeHeaderWidgetinstead.KittyAuthThemeandKittySettingsThemeare exported fromkitty_sdk.dart.
0.8.0 #
- Tokens module: New module for managing user token balances and operations.
KittyTokensConfig— configureisDebugto show/hide debug UI in settings.KittySdk.tokens— static accessor for the Tokens client withgetBalance(),addTokens(),removeTokens(), andgetOperations(). (Note:addTokens/removeTokenswere removed from the client in 0.10.3 — see above.)- Integrated into
KittyAccountPageto automatically display the user's token balance (and debug controls if enabled) when the module is initialized. - Conditional initialization — Tokens module only starts when
KittySdkConfig.tokensis provided and the user is authenticated.
0.6.0 #
- KittyOtel module: New observability module for sending logs, traces, and metrics via OTEL to the Kitty Launch platform.
KittyOtelConfig— configure endpoint, token, batching, compression, and queue size.KittySdk.otel— static accessor for the OTEL client withaddLog(),addTrace(),addMetric(), and manualflush().- Automatic batching with configurable interval and batch size, gzip compression, and retry logic.
- Conditional initialization — OTEL module only starts when
KittySdkConfig.otelis provided.
0.5.5 #
- Fingerprint auto-login: On SDK init, if Flare identifies a strong fingerprint match (
IP_v4/GEO_UA) with aloginCodeinuserData, the SDK automatically exchanges it for auth tokens — seamless web-to-mobile login. onFingerprintAutoLogincallback: New optional parameter inKittySdk.initialize()— notifies the host app when fingerprint auto-login succeeds.- Safe
userDatacasts:distinctIdandloginCodefrom fingerprintuserDatanow use?.toString()instead ofas String?to prevent runtime type errors.
0.5.4 #
- KittySdkConfig: Added
productName,privacyPolicyUrl,termsOfUseUrl,shareAppText,shareAppLinkfields so all app-level config lives in one place. - KittySettingsFlow: Automatically resolves empty settings config fields from
KittySdkConfig— no need to duplicate values between SDK init and settings config.
0.5.3 #
- Fix: Bumped
posthog_flutterminimum to^5.15.0— fixesPostHogFeatureFlagResult not foundcompile error when resolving to older 5.x versions.
0.5.2 #
- Auto-identify on init:
KittySdk.initialize()now automatically callsflare.identify()if no previous result is cached. On a match, aliases the web PostHogdistinctIdfrom the fingerprint with the current mobile session — linking web and mobile analytics.
0.5.1 #
- PostHog per-project routing: SDK now sends
x-ph-api-keyandx-ph-hostheaders in every API request (both authenticated and unauthenticated). The backend uses these to routeidentify/aliascalls to the correct PostHog project — no shared PostHog instance needed. KittyAccountConfig.baseHeaderscentralizes common headers (content-type,x-project-id, PostHog keys) to avoid duplication across API clients.
0.5.0 #
- AccountInfo.primaryEmail: Backend now sends
primaryEmailin/users/currentresponse; SDK uses it for theemailgetter instead ofemails[0]. Fixes email not updating after bind-email flow. - Bind-email UI: Title moved to AppBar for consistent design;
embeddedmode prevents duplicate Scaffold/AppBar. - Delete account: Now performs a client-side logout instead of calling the non-existent
mark-for-deletionAPI. - Contact Support button: Upgraded to
FilledButton.tonalfor better visibility on the code page; fixedmailto:URI construction that was dropping the email domain. - Code page support section: Hidden on initial code entry, shown only after the user taps "Resend Code".
- PostHog: Session replay and lifecycle events disabled by default for lightweight event-only tracking.
0.4.0 #
- KittyTheme: New theming system with auto light/dark detection from platform brightness. All SDK forms (auth + settings) now render with a consistent minimalist design.
KittySdkConfig.themeaccepts aKittyTheme(primaryColor: ...)to customize button, link, and accent colors across all SDK UI.- Redesigned code input boxes, settings menu items, and form layouts for a cleaner look.
- Breaking: widgets now use SDK theme instead of inheriting host app theme. Override via
KittySdkConfig.theme.
0.3.3 #
- Updated auth form texts to match design: "Access Your Purchased Account" title, "Enter email..." hint, "Log In" button, "Check Your Email" code page, "RESEND CODE (XX sec)" timer.
- Restructured code form support section with contact prompt, tappable support link, and outside hours message.
- Added
supportEmailtoKittySdkConfigfor global configuration — no longer needed per-form. - Resend timer default changed from 5 minutes to 30 seconds.
0.3.2 #
- Fix:
claimAppStoreSubscriptionsnow sendsx-project-idheader — licenses are correctly associated with the project instead of being created withproject_id=null.
0.3.1 #
- Removed sign-up mode from auth flow — login + 2FA code is a single unified flow with no login/signup distinction.
- Removed
isSignUp,toggleSignUp(),signUpTitle,dontHaveAccountText,signUpLinkText,alreadyHaveAccountText,logInLinkText,emailAlreadyInUseError,tryLoggingInText,emailTakenError,showSignUpTogglefrom public API.
0.3.0 #
- Account UI — Auth flow: Configurable login and 2FA code forms (
KittyAuthFlow,KittyLoginForm,KittyCodeForm) withKittyAuthUiConfigfor customizing icon, support email, all text content, timer duration, and code length. - Account UI — Settings flow: Full settings module (
KittySettingsFlow) with account management, subscription details, cancel/renew, delete/restore account, legal info, logout — all driven byKittySettingsConfig. No dependency onauto_routeor external analytics/environment packages. - New dependencies:
flutter_bloc,url_launcher,share_plus.
0.2.3 #
KittyAnalytics.initialize()now callsposthog.init()via JS interop on web, so PostHog API key is only configured in Dart — no need to hardcode it inindex.html.
0.2.2 #
AccountRepositorynow auto-resolvessidfromKittyAnalytics(PostHog distinctId) forverifyLoginCodeandsignupWithPassword.- Removed
sidparameter fromKittyAccount.verifyLoginCode()public API. - Removed
canLoginWithPasswordfromAccountRepositoryandVaultApiClient.
0.2.1 #
- Fix: add web platform support — guard
Platformcalls withkIsWebto preventdart:iocrashes on web. DeviceInfoCollectornow returns browser metadata on web instead of throwingUnsupportedError.FlarePurchaseObserverandAppStorePurchaseObservergracefully no-op on web.
0.2.0 #
- Analytics module: PostHog event tracking, user identification, feature flags, session replay.
KittySdkConfig.analyticsparameter for configuring PostHog via unified initialization.
0.1.0 #
- Initial release.
- Flare module: device fingerprint identification and purchase attribution.
- Account module: authentication, user management, licenses, subscriptions.
- Unified
KittySdk.initialize()entry point.