Changelog
Every release of the koolbase_flutter SDK, newest first. Dates are when each version was published to pub.dev.
v12.14.0
Minor30 September 2026
Changes
- —Live lists refresh after doc(id).update and doc(id).delete. Every open query on the record's collection re-runs in the background, as it already did after insert, upsert, batch and deleteWhere; before, an update or delete through a document reference left every list showing that collection stale until it was reloaded. A delete of a record never read on this device refreshes every open query rather than guessing its collection (refreshAllCollectionStreams).
- —A refused conditional write refreshes too. When update or delete with expectedRevision is refused because the record changed (KoolbaseRevisionMismatchException), the collection's open queries re-run, so the list already shows the newer data.
- —Release notes. Version headings in this changelog are now ##, which the release workflow reads; with # every GitHub release since the workflow began had empty notes.
v12.13.0
Minor28 September 2026
Fixed
- —KoolbaseCollectionGrid works inside a scrolling page: its empty state no longer throws (BoxConstraints forces an infinite height), and scrollsWithPage: true lays the cells out at their natural height with Load more below them, as the list's page mode does
- —KoolbaseCollectionGrid renders from KoolbaseTestData in widget tests, as KoolbaseCollectionList does
v12.12.0
Minor27 September 2026
New
- —KoolbaseTestData (import package:koolbase_flutter/testing.dart): records supplied to KoolbaseCollectionList and KoolbaseRecordView in widget tests, so a screen can be pumped populated. It replaces only the data source; controllers, paging, states and rendering are the production code. Lists page like the server (limit, offset, exact total); an unknown record id is not found, as the API answers. Testing and certification infrastructure, not a local-data API
v12.11.0
Minor27 September 2026
New
- —KoolbaseCollectionList(scrollsWithPage: true), for a list inside a scrolling page: the rows lay out at their natural height and the page does the scrolling. Load more stays a row; pull-to-refresh belongs to the page, so the list adds none. Without it, a list with records inside a scrolling column cannot size itself (Vertical viewport was given unbounded height). The default is unchanged
v12.10.0
Minor26 September 2026
New
- —KoolbaseRecordView: one record by id, as a scope. It hands the record to builder, which returns your own widget, with loading, notFound and error slots and defaults. notFound covers a missing id, a record that does not exist, one this user may not read (the API does not tell them apart) and a record from a different collection. A changed id starts a new load
- —KoolbaseRecordController: the same without the widget. A failed refresh keeps the record shown
v12.9.0
Minor25 September 2026
Changed
- —Analytics is off by default. Pass analyticsEnabled: true in KoolbaseConfig to send events. While it is off, Koolbase.analytics calls do nothing (the first logs how to turn it on), so apps that track events keep working
- —KoolbaseError.userMessage: a short message that is safe to show to people for network errors. On the web, the network error also names the page's address and Trusted Origins
- —Koolbase.db.invalidate(collection) forgets cached query results for a collection
v12.8.0
Minor23 September 2026
New
- —Two-step sign-in (MFA): every sign-in method now throws MfaRequiredException carrying a challengeToken when the person's account has an authenticator on, including phone, Google and Apple. verifyMfa and verifyRecoveryCode finish sign-in and store the session. enrollMfa, confirmMfaEnrollment, mfaStatus, stepUpMfa, disableMfa and regenerateRecoveryCodes manage it. Six new exceptions registered in the error-code fidelity suite
v12.7.0
Minor23 September 2026
New
- —Sign in with an emailed code: requestEmailCode sends a six-digit code, and signing in with it stores the session exactly as password login does. A new address becomes an account only while sign-ups are open, checked at sign-in. Each code works once and allows three attempts, and the request answers the same whether or not the address has an account. Projects can switch it off, which surfaces as its own error class
- —Reference errors are catchable: KoolbaseReferenceInvalidException, KoolbaseReferenceInUseException, KoolbaseDanglingReferencesException (carrying the offending records) and KoolbaseCollectionReferencedException
v12.6.0
Minor21 September 2026
New
- —UploadResult exposes path and publicUrl, not only a signed downloadUrl. downloadUrl is signed and expires, so persisting it produces a broken image later. Persist path for a private bucket and publicUrl for a public one — a stable CDN URL, null for a private bucket. UploadResult also carries the bucket the object went to, which publicUrl needs
v12.5.0
Minor20 September 2026
New
- —auth.listSessions(), auth.revokeSession(id) and auth.revokeAllOtherSessions(). Three endpoints had been live on the server the whole time and no SDK exposed any of them, so sign out my other devices existed and was unreachable. Each session carries its device label, IP, user agent and timestamps, and isCurrent marks this device. Token hashes are never included
- —revokeAllOtherSessions keeps this session and returns how many ended, which is what the feature means after a lost phone. Revoking the current session through revokeSession ends this one too; the next request's 401 clears it locally, so a UI that knows a row is current should call logout() for that one
- —auth.auditLog(). The account's own security history for a recent-activity screen: sign-ins, failures, lockouts, password changes, verification. The server sanitizes each event against a per-type field allowlist, so an entry carries what its type may say and nothing more
- —auth.resendVerificationEmailToAddress(email). Ask for a new verification email with no session. A project requiring verified contact issues none until the account verifies and refuses login until then, so a user whose email went to spam could not sign in to ask for another. Returns nothing and reveals nothing: the server answers identically whether the address has an account, has none, or is already verified
Fixed
- —The six x-koolbase-* identity headers this SDK sends are pinned by a test against the set the API allows, so they cannot drift without failing here
v12.4.0
Minor19 September 2026
Fixed
- —Fourteen exceptions reported codes the API has never emitted. EmailAlreadyInUseException said email_taken for a server that says email_in_use; the Apple and Google exceptions each reported a provider-split code where the server sends one unified code. code is a public field, so anyone reading it instead of catching by type was comparing against a string that would never arrive. Catching by type has always worked and is unaffected
- —invalid_refresh_token was unmapped in the database path — the code the server actually sends when a session is over. It fell through to the generic fallback, so koolbaseDataErrorNotifying never fired for it and the session was not cleared. It now produces KoolbaseSessionExpiredException, which four doc comments told applications to catch and nothing had ever thrown
- —A function that timed out was indistinguishable from one that threw: a 504 fell into the generic 5xx branch and arrived as FunctionExecutionException. Those need different answers, so a timeout is now FunctionTimeoutException and a 429 is FunctionRateLimitException
- —Forty-four error codes the API emits were not mapped, arriving as bare exceptions so an app's on clause silently never ran. Thirteen in auth including token_expired, token_used, account_exists and oauth_only_account; the rest across database, storage and functions
- —KoolbaseNotFoundException and KoolbaseValidationException report the code they were built from rather than their category, so a collection_not_found response no longer produces an exception saying not_found
v12.3.0
Minor17 September 2026
New
- —auth.changePassword(currentPassword:, newPassword:). An app could not offer a change-password screen until now; the only route to a new password was the reset email. The current password is verified first. On success every other session for the user is signed out and this device stays in
- —InvalidPasswordException when the current password is wrong, or the account signed up through a provider and has no password to change. The server returns the same code for both so a caller cannot probe an account's sign-in methods
Fixed
- —WeakPasswordException is now actually thrown. It was defined and mapped but nothing raised it: a too-short password came back uncoded on register and as invalid_token on reset. All three password paths now return weak_password, so one catch covers signup, reset and change
v12.2.0
Minor17 September 2026
New
- —Pagination. KoolbaseCollectionList and KoolbaseCollectionGrid show a Load more past the last loaded record while the collection has more, exact from the query's total rather than guessed from a short page. KoolbaseCollectionController gains loadMore(), hasMore and loadingMore
- —loadMore does not resubscribe: the realtime subscription stays on the first page, so a live insert updates that page in place and later pages stay put. refresh() resets to one page
v12.1.0
Minor17 September 2026
New
- —Koolbase.db.aggregate(...): count, sum, avg, min, max over the whole authorized set, grouped by a field or a calendar bucket. Every result carries its accounting, how many records contributed and how many were skipped as missing or non-numeric, so a total is never a bare number. A calendar bucket requires a timezone; the KoolbaseGroupBy.month(...) constructors make it impossible to omit. Online-only; read rules apply inside the query
v12.0.0
Major8 September 2026
New
- —Flutter Web. Koolbase.initialize no longer refuses the browser: auth, database, storage, realtime and functions work as they do on a phone. The refusal predated the work — everything under it already ran; what was missing was an honest account of the two things a browser cannot do
- —Koolbase.isCodePushAvailable — false on the web, where there is no binary to patch. Check it on a platform-agnostic path rather than reaching for kIsWeb
- —A project's Trusted Origins now govern the API, not only storage buckets. A browser app calling api.koolbase.com from its own domain was refused before, with no remedy a customer could apply; the list is theirs to edit under Configuration → Trusted Origins
Breaking
- —Koolbase.codePush throws UnsupportedError on the web instead of initialize throwing. Code that worked around the old refusal — skipping Koolbase entirely under kIsWeb — should now guard the code push call itself, or check isCodePushAvailable
Note
- —Offline sync is skipped on the web: every read is live and every write goes straight out. drift on the web needs sqlite3.wasm and a worker shipped beside the app, which most apps will not carry
v11.6.1
Patch8 September 2026
Fixed
- —README documents KoolbaseCollectionList.visible and the install snippet points at the current version
v11.6.0
Minor8 September 2026
New
- —KoolbaseCollectionList.visible — transform the loaded records before BOTH the empty decision and the rows, so a filter that leaves nothing shows the empty slot rather than an empty list. It runs on every build over what is already loaded; it does not query
- —Search-as-you-type in the Designer is built on it: a case-insensitive substring match across the string fields you name, over the page the list has loaded
v11.5.0
Minor4 September 2026
New
- —Koolbase.initializeForTesting() — widget tests can build Koolbase screens. db and auth exist, every HTTP request answers with an empty success, auth storage is in memory, and no platform channel is touched. resetForTesting() pairs with it; InMemoryAuthStorage is public for your own tests
Fixed
- —The httpClient passed to the database client now reaches every query and document reference. It was accepted and never used, so a test that injected a client still hit the network
- —An empty collection list inside a scrolling column no longer throws: the empty slot only pins its height when a finite height is available. Found by a generated smoke test, not by a user, which is the point of shipping them
v11.4.2
Patch18 September 2026
Fixed
- —Device outcomes are attributable again. Every patch event the SDK sent omitted patch_id, and the server only increments a patch's counters when that field is present — so every download and activation ever reported landed in the events table and moved no number. The staged patch id is now persisted beside the staged patch number and promoted on activation, so it survives the process boundary between downloading a patch and applying it
- —Android reports patch_activated. The reconcile branch that promotes a staged patch on Android logged and returned without sending anything, so the platform carrying almost all real traffic had never recorded a single activation
- —Failures are no longer silent. A non-200 download, a checksum mismatch, an engine that declines a staged patch, and any exception in between now report rather than disappearing
v11.4.1
Patch18 September 2026
Changed
- —Flutter Web now refuses at Koolbase.initialize rather than starting a client whose most important features silently do nothing. Code push, OTA updates, offline-first sync and version enforcement have no browser equivalent, so this SDK targets Android and iOS and the message points at the JS SDK. The package began compiling for web in 11.4.0, which is precisely why an explicit refusal became necessary — a working-looking client is worse than a clear no
- —platforms: is declared in the pubspec rather than inferred, so pub.dev states the boundary instead of guessing at it
v11.4.0
Minor18 September 2026
New
- —Koolbase.storage.publicUrlFor(bucket:, path:, transform:) — the runtime form of publicUrl that needs no projectId argument. The SDK learns its project identity from the bootstrap payload it already fetches and caches, so apps never carry or compose project identity themselves
- —KoolbaseStorageProjectIdentityException, thrown while identity has not yet arrived — a fresh install that has never completed a bootstrap. Deliberately distinct from a null public URL, which means the project is known and the object simply has no public URL. The throw also nudges a background bootstrap refresh, so the session heals once connectivity returns without a restart
Fixed
- —The bootstrap payload carries project_id and the update gate compares it explicitly. Identity sits outside the payload hash because it can never change, so version comparison alone would have discarded the first identity-bearing payload on apps whose flags and config were unchanged
v11.3.0
Minor27 August 2026
New
- —KoolbaseError — one canonical, platform-wide error type. Catch anything, call KoolbaseError.from(e), branch on code. The 44 typed exceptions stay exactly as they are; this sits above them, so an app that wants to branch on what a failure MEANS no longer has to know the SDK's internal taxonomy
- —KoolbaseErrorCode has eleven members, and the bar for adding one is that an application could reasonably do something DIFFERENT because of it. rateLimited exists because waiting is not retrying; contactNotVerified exists because it routes to 'resend verification', not 'check your password'
- —retryable is derived from code rather than stored, so the two can never disagree. details carries structured context through normalization — the field that collided, and for a revision mismatch the server's current record, returned WITH the refusal so deciding what to do next needs no second fetch
Fixed
- —Transport failures are typed for the first time. Found by measuring rather than guessing: across 14,732 lines, four places in the whole SDK caught a transport failure — so a database read that lost connection threw a raw SocketException straight through to the caller. On a phone with patchy signal that is the most common failure there is, and it was the one failure with no typed home
- —KoolbaseError.from maps SocketException, TimeoutException and ClientException to KoolbaseErrorCode.network. Normalization is total: an unmapped server code, a bare string, an integer — anything becomes a KoolbaseError rather than escaping, because this runs in error paths where a second failure has nowhere to go
Note
- —message is developer diagnostic data and must never be shown to an end user. It is English-only and not stable API. Map code to your own copy instead
v11.2.1
Patch19 August 2026
Fixed
- —Analytics events now carry the signed-in user automatically. identify() existed, but nothing errored when an app never called it — so every event landed anonymous, and retention, funnels and any per-user analysis were quietly worthless. Found in a real project: 53 events, 8 registered users, zero events carrying a user id
- —identify() remains as an override for apps with their own identity system; the existing reset() releases it
v11.2.0
Minor19 August 2026
New
- —Koolbase.fiscal — authority-grade sales recording. submit() records a transaction and drives fiscalization; status() returns the certification the tax authority granted (in Ghana: the GRA signature, receipt number and verification QR a compliant receipt must carry). Live for Ghana's GRA E-VAT; the reference adapter runs the same machine — gapless numbering, sealed immutable documents, full evidence — anywhere an authority integration doesn't exist yet
- —The clientRef you pass is idempotent: resubmitting the same reference always returns the same intent, never a second fiscal number. A timed-out submit may still have fiscalized, so retrying or polling both converge on the truth
- —FiscalIntentResult exposes status, numbers, blockedReason, certification, isFiscalized and isPending. A blocked result means nothing was consumed — no fiscal number was spent
Changed
- —Fiscal deliberately does NOT use the offline write queue. A register needs a real answer or an explicit 'not yet'; silently queueing a fiscalization would let a cashier believe a receipt is legal when the authority has never seen it. Offline-capable point of sale should record the commercial sale locally and submit fiscally on reconnect
v11.1.1
Patch18 August 2026
Fixed
- —Offline writes now refuse with KoolbaseUnauthenticatedException when no user is signed in, instead of enqueueing under a null owner. A null-owner queue row is invisible to every per-user read and replay — the write neither fails nor succeeds; it vanishes. Found on device as a parked ticket that never reached the server
v11.1.0
Minor18 August 2026
New
- —KoolbaseQuery.get() gains fresh: true — skips the cache and returns the network's answer directly. Required for read-after-write, reconciliation, and local projections that must only ingest server-provenance data. A fresh read still updates the cache, so stale-while-revalidate callers benefit from it
v11.0.0
Major17 August 2026
Breaking
- —signUp returns SignUpResult, not KoolbaseUser — verification state is in the type rather than something you infer. Code that used the returned user directly needs updating
New
- —ContactNotVerifiedException, thrown when a project requires a verified contact and the user has not completed it
v10.5.0
Minor16 August 2026
New
- —KoolbaseCollectionGrid — the grid counterpart to the stale-while-revalidate list widget
v10.4.0
Minor12 August 2026
Fixed
- —Streams now behave the way they read. .stream fetches on first listen — it was a bare relay off a broadcast controller, so a stream-only listener waited forever on a collection nobody had called get() on. Both gaps surfaced as 'the UI just doesn't update': nothing errored, nothing logged, the data was simply absent
v10.3.0
Minor5 August 2026
Added
- —KoolbaseAuthGate: branches the tree on auth state correctly. Calls restoreSession() once at mount behind a restoring slot so returning users never see a login flash; seeds synchronously from currentUser (the auth stream is a broadcast with no replay — a listen-only gate would hang forever); RestoreResult.offline counts as signed IN per its optimistic semantics, with restoredOffline exposed on the scope; a restore that throws lands on signed-out, never a crash
- —KoolbaseAuthScope: descendants read user/status/restoredOffline without statics, rebuilding on identity change
- —KoolbaseCollectionList: an opinionated pull-to-refresh list with the stale-while-revalidate contract handled — the cached first arrival renders (isFromCache exposed), the network arrival replaces it, and a failed refresh keeps the records. Stale beats blank: only a first load with nothing to show is an error state, and it carries a retry. empty/error/loading are slots; per-item appearance is the caller's itemBuilder
- —KoolbaseCollectionController: the list's data half, widget-free — the fetch, stream, and refresh lifecycle for grids, slivers, and custom scroll layouts without re-implementing the discipline
Fixed
- —Query refresh streams were keyed by collection name alone, so two different queries on one collection shared a controller — each background refresh emitted its records to both listeners, silently wrong data on any two-list screen. Streams are now keyed by query identity (collection + filters + user), matching their documented contract; KoolbaseQuery.streamKey exposes the identity
- —Behavior note: a stream on a never-fetched query no longer receives refreshes triggered by other queries on the same collection — that was the contamination, not a feature. The common pattern (build a query, get(), listen to that query's stream) is unchanged
v10.2.0
Minor4 August 2026
Added
- —ConflictOperation.insert: a queued insert refused as a duplicate (unique constraint) is held like any other terminal refusal — previously its operation was coerced to update, and resolving it issued a PATCH against a record id that exists nowhere
- —Resolving a rejected insert IS the insert, retried. resolveWithMerge carries amended data (the 'fix the colliding title' path); the conflict's id rides as the idempotency key, so a resolution whose response is lost returns the original on retry rather than duplicating
- —resolveWithServer on an insert-conflict means the colliding row stands: the conflict clears without a request
v10.1.2
Patch3 August 2026
Fixed
- —A refused resolution now teaches the stored conflict: when the record moved again while someone was deciding, the 409's current revision is absorbed, so the next attempt is conditional against reality instead of permanently replaying a stale condition. Previously a conflict whose resolution failed once was unresolvable except by abandon
- —The conflict-resolution HTTP path is injectable (httpClient constructor parameter) — an unmockable resolution path is why this bug had no test to catch it
v10.1.1
Patch2 August 2026
Changed
- —Signed out, pendingWrites() and watchPendingWrites() throw KoolbaseUnauthenticatedException rather than returning empty — a silent empty is indistinguishable from 'all synced', and a null identity could even match legacy no-owner writes. Found on device: a signed-out display showed 0 pending while queued writes sat unsent
- —The stream emits the refusal as an error event per emission, so a sign-out under a live badge errors the moment the session dies
v10.1.0
Minor2 August 2026
Added
- —pendingWrites() — changes made offline waiting to be sent, oldest first. For sync indicators and for warning a user about to log out with unsynced edits: queues are per-user and survive logout, so those edits sync whenever that user next signs in on this device — possibly never
- —watchPendingWrites() — the live version, for a sync badge, mirroring watchConflicts()
- —The returned PendingWrite excludes replay internals (baselines, revisions, raw payloads); a delete carries no data
v10.0.0
Major1 August 2026
Breaking
- —One exception hierarchy: KoolbaseException is the root, with the data, storage, and auth families beneath it
- —A rejected credential is no longer a KoolbaseDataException. It is KoolbaseUnauthenticatedException, a sibling — code catching KoolbaseDataException around a query will stop catching 401s
- —KoolbaseSessionExpiredException remains as a deprecated subclass, removed in 11.0.0. Renamed because a 401 covers an expired session, a revoked key, and missing credentials, and the server does not distinguish them
Fixed
- —Storage, Functions, and background sync now clear a rejected session. Only database calls did, so an app whose failing call happened to be an upload or an invoke kept believing it was signed in
- —Function failures are told apart: not found, permission, validation, quota exceeded, execution. Every failed invocation used to be one type carrying a status number
v9.9.0
Minor1 August 2026
Added
- —update and delete take an optional expectedRevision, applying the write only if the record still carries it — otherwise KoolbaseRevisionMismatchException, with the server's current copy attached
v9.8.0
Minor1 August 2026
Added
- —Offline update and delete. Previously only inserts queued; edits threw when the network was unreachable
- —A write is queued only when the SDK knows what the record looked like at the time — otherwise KoolbaseOfflineBaselineUnavailableException. Replaying a change without that would overwrite whatever happened meanwhile
- —Replay is conditional on that revision. A write the server refuses becomes a durable conflict, readable from conflicts() and resolvable four ways, surviving restarts
- —Records carry a revision, cached individually from every path that returns one
v9.7.0
Minor29 July 2026
Changed
- —A rejected session now signs the user out. A 401 used to produce a generic error while the session stayed on disk, so an app looped: call, reject, retry, forever, with no path back to login
- —By the time you catch it, the user is already signed out — route to login rather than retrying. A 403 is unchanged and does not clear the session
Added
- —auth.clearStoredSession() — discards a session already known to be unusable, without a server call
Fixed
- —Queued offline writes are no longer replayed under the wrong user. The queue outlives sessions, so a write made offline could be sent after someone else signed in on the same device
v9.6.0
Minor26 July 2026
Added
- —auth.resendVerificationEmail() — throttled self-service resend of a verification email
v9.5.0
Minor25 July 2026
Breaking
- —Client-side messaging send() removed — sending a push from a device was never safe to expose
Fixed
- —Function deploy() used the wrong token path
v9.4.1
Patch22 July 2026
Fixed
- —Stubbed VM patch bindings so the package compiles on stock Flutter
v9.4.0
MinorJuly 2026
New
- —iOS VM Code Push — over-the-air Dart updates on iOS, at parity with Android. The SDK checks in, downloads, verifies (Ed25519 signature + build_id pinned to the exact binary), and applies the patch on the next cold launch
- —Flash-free boot apply on iOS. Koolbase.initialize completes the boot-time patch apply (local disk only, ~5ms measured on device) before returning, so the app’s first frame already runs the patched code — no v1→v2 flash, zero configuration. The network check/download stays fully asynchronous and never blocks startup
- —Device outcome events — the client reports patch_downloaded (on stage), patch_activated (on the boot that promotes a new patch, with patch_number), and patch_failed (on rejection, with the code) to /v1/code-push/patch-events. Dashboards and rollout decisions can count real activations instead of inferring from patch-check serves. Fire-and-forget: reporting never delays boot
Fixed
- —iOS patch bookkeeping after a rejected download. A rejected patch that fell back to the existing one could be recorded as current, so the device reported a patch it wasn’t running and stopped receiving offers. The boot apply now records what actually happened and reconciliation consumes it; a clean base boot resets the reported patch to 0
- —Rejected iOS patches are quarantined, not retried. A persisted patch that fails verification (e.g. stale after an app update) is moved aside on first rejection instead of being re-read and re-rejected every launch. Quarantined artifacts are removed once a healthy boot completes
v9.3.0
MinorJuly 2026
New
- —Code Push (VM-level) reports flutter_version on patch-check, so the resolver can refuse a patch built on a different Flutter engine version. Reads the CLI-stamped assets/koolbase_flutter_version asset and sends it alongside build_id / release_version
- —Closes two cross-engine mis-serve cases: colliding build_id across engine versions, and release_version matching on app version alone
Changed
- —Fully backward-compatible — an app built without the asset sends no flutter_version and the server falls back to legacy matching. No change for apps already in the field
v9.2.1
PatchJuly 2026
Changed
- —Widen package_info_plus to >=8.0.0 <10.0.0 and flutter_secure_storage to >=9.0.0 <11.0.0. The SDK only uses the stable surface of both (PackageInfo version/buildNumber; SecureStorage read/write/delete with default AndroidOptions and standard KeychainAccessibility), so the previous latest-major pins needlessly blocked — and for secure_storage risked force-migrating — host apps on the prior major
v9.2.0
MinorJuly 2026
Fixed
- —Code Push (bundle): recall/rollback now actually reverts a recalled bundle on device. The runtime resolver persists a pending-revert marker when the server issues a rollback and consumes it at the start of the next cold launch, before re-applying any stored bundle. Previously the rollback was logged but never persisted, so a recalled bundle kept re-applying on every launch
- —Pairs with the server-side resolver fix returning rollback/revert_to:0 when a device runs a bundle that has been recalled with no published replacement. Affects iOS and Android bundle recall
v9.1.0
MinorJune 2026
New
- —Code Push (VM-level): KoolbaseVmPatchClient — over-the-air Dart code updates for Android. Self-contained: no MainActivity or platform-channel wiring required
- —Checks in with the Koolbase resolver, downloads and stages patches; the Koolbase engine applies them on next launch with signature + build_id verification and automatic crash-revert
- —Per-ABI build_id resolution for multi-ABI app bundles (arm64-v8a / armeabi-v7a). Reports the running build’s build_id and current patch number on check-in
v9.0.0
MajorJune 2026
New
- —Three search modes from a single endpoint. KoolbaseQuery.searchSemantic accepts a new mode parameter of type KoolbaseSearchMode with three values: semantic (default, pure vector via HNSW + cosine), lexical (pure BM25 over the field's source text via Postgres ts_rank_cd), and hybrid (vector + lexical fused with reciprocal rank fusion, k=60)
- —KoolbaseQuery.searchSemantic accepts a new minSimilarity parameter (0..100, optional). Server-side filter that drops results below the given similarity percentage before they cross the wire — saves bandwidth on weak matches. Only valid for semantic and hybrid modes; rejected by the server on lexical mode with a 400 validation_error
- —Auto-lexical-indexing on record write — when a vector field has a source_field configured, every insert and update also populates a lexical row in db_record_lexical alongside the embedding job. No extra configuration; no extra worker. Powers lexical and hybrid search out of the box for new writes
- —Lexical backfill admin endpoint (POST /v1/projects/{id}/collections/{name}/vector-fields/{field}/lexical-backfill) for populating lexical rows on records that pre-date the write-path hook. Available from the dashboard's Vector Fields modal via a book icon next to the embed-backfill wand. Idempotent, synchronous, no provider charges (Postgres handles the indexing)
- —New KoolbaseSearchMode enum exported from package:koolbase_flutter/koolbase_flutter.dart
Changed
- —No breaking changes. mode and minSimilarity are both optional; existing searchSemantic callers continue to work unchanged. Major bump reflects the conceptual expansion of the search contract (three retrieval modes instead of one), not API-breaking removals
- —Lexical and hybrid modes require the vector field to have a source_field configured. They also require queryText — BM25 has no notion of vector queries, so passing queryVector with mode=lexical or mode=hybrid is rejected
- —Requires Koolbase API release with hybrid search shipped (June 8 2026 or later)
v8.0.0
MajorJune 2026
Breaking
- —KoolbaseQuery.searchSemantic now validates that exactly one of queryVector / queryText is supplied and throws ArgumentError otherwise. The queryVector parameter is now optional; existing callers that always passed it continue to work unchanged
New
- —KoolbaseQuery.searchSemantic accepts a new queryText parameter. When supplied, the server embeds it inline using the vector field's configured provider (Gemini or OpenAI) before running HNSW lookup — no client-side embedding model required for typical search use cases
- —KoolbaseQuery.embedText queues an embedding job for a specific record's vector field. Used for backfilling vectors on records that pre-date the auto-embed hook, or for embedding text other than the record's configured source field
- —Auto-embed on record write is automatic once a vector field has its embedding_provider, embedding_model, and source_field configured — declare these from the dashboard's Vector Fields panel or via the CLI
- —Requires Koolbase API release 771728d or later (Phase 2 Stage A3a)
v7.0.0
MajorJune 2026
New
- —Semantic search via vector similarity — query records by meaning, not just by field equality. Mirrors the server-side vector primitive shipped in Koolbase Phase 1 AI
- —Koolbase.db.doc(id).setVector(field, vector) — store a vector for a record
- —Koolbase.db.doc(id).getVector(field) — read a stored vector back as KoolbaseVector
- —Koolbase.db.doc(id).deleteVector(field) — remove a record's vector
- —Koolbase.db.collection(name).searchSemantic(field:, queryVector:, limit:, where:) — HNSW similarity search with optional filter scoping, returning ranked hits with cosine distance
- —New types: KoolbaseVector, KoolbaseSemanticHit, KoolbaseSemanticSearchResult
- —New typed exception: KoolbaseVectorDimensionMismatchException for length-mismatch errors
Changed
- —Vector fields must be declared on a collection ahead of time via the dashboard or CLI — the mobile SDK does not declare schema. Supported dimensions in this release: 384, 768, 1024, 1536 (3072 lands when pgvector upgrades; until then use the model's dimensions=1536 parameter for Matryoshka truncation)
- —Vector operations are online-only — they are not cached locally or queued offline because HNSW search has no useful offline semantics
- —Semantic search respects the collection's read rule the same way .get() does: owner / scoped / conditional rules apply after the HNSW lookup, so strict rules may return fewer than limit results
Migration
- —Purely additive — no existing methods or types changed. Upgrading from 6.x requires only flutter pub upgrade koolbase_flutter and a re-run of flutter pub get
v6.5.0
MinorJune 2026
New
- —Object versioning — full read + write surface against versioned buckets. No behavior change on non-versioned buckets
- —KoolbaseObjectVersion model — one entry in a path's version timeline. Carries versionId, size, metadata, isDeleteMarker, isCurrent, createdAt and the rest of the version-row shape
- —KoolbaseStorageClient.listVersions({bucket, path}) — full timeline newest-first, current + history mixed
- —KoolbaseStorageClient.getVersion({bucket, path, versionId}) — metadata for one specific version
- —KoolbaseStorageClient.restoreVersion({bucket, path, versionId}) — bring a history version back to current; the previously-current row is snapshotted to history first, so restore is itself a versioned event
- —KoolbaseStorageClient.purgeVersion({bucket, path, versionId}) — hard removes a single history version (row + R2 bytes)
Changed
- —getDownloadUrl(...) accepts an optional versionId — when present, the returned URL points to that specific version's bytes from .versions/
- —delete(...) accepts an optional forcePurge — true wipes the entire timeline for the path (all history rows, all .versions/ R2 keys, canonical, and the current row)
v6.4.0
MinorJune 2026
New
- —Edge image transforms — KoolbaseImageTransform value class (width, height, format, quality, fit, dpr, gravity) plus KoolbaseImageFormat, KoolbaseImageFit, KoolbaseImageGravity enums for type-safe option construction. Out-of-range numeric values clamp silently to Cloudflare's valid ranges
- —KoolbaseStorageClient.publicUrl({transform}) and KoolbaseObject.publicUrl(bucket, {transform}) accept an optional transform — the resulting URL hits Cloudflare's image pipeline at cdn.koolbase.com/cdn-cgi/image/<opts>/... and serves a resized, re-encoded copy
- —KoolbaseStorageClient.publicUrlWithPreset({projectId, presetName, bucket, path}) and KoolbaseObject.publicUrlWithPreset(bucket, presetName) — resolve a named preset stored server-side, managed via the dashboard or REST API
- —Cloudflare bills unique transformations per calendar month; every Koolbase account includes 5,000 free. Transformed responses are edge-cached for 4 hours
- —No breaking changes — additive. Existing publicUrl calls without transform produce the same URL as 6.3.0
v6.3.0
MinorJune 2026
New
- —Public bucket CDN URLs — KoolbaseObject gains an r2Bucket: String field identifying which physical R2 bucket holds the object's bytes (always populated)
- —'koolbase-storage-public' means the object has a stable CDN URL; anything else (typically 'koolbase-storage') means it's in private storage and reads go through a presigned URL via getDownloadUrl
v6.2.0
MinorJune 2026
New
- —koolbase.storage.updateMetadata(bucket:, path:, metadata:) — partial metadata update with merge semantics: string values set or update keys, null values delete keys, omitted keys are left untouched. One call covers add/update/delete in any combination; the server applies the merge atomically with a single JSONB operation. Pairs with @koolbase/react-native v5.2.0
- —Optional metadata: Map<String, String> parameter on upload() — attach custom metadata at upload time, recorded in the same confirm step. On an overwrite upload, the new metadata fully replaces the previous object's metadata (matches object-store semantics where overwrite produces a new object, not a patch of the old)
- —KoolbaseObject.metadata: Map<String, String> on every returned object — always non-null (empty map when no keys are set), so callers never have to null-check before reading. Defensive decode in the object parser handles older cached responses without metadata gracefully
- —KoolbaseStorageMetadataInvalidException — typed exception for server-side metadata validation failures (HTTP 400, wire code metadata_invalid). Exposes a detail field naming the failing key and the rule it broke (e.g. 'key "bad key": must match [a-z0-9_]+'), so apps can surface actionable errors to users without parsing strings
- —Storage error mapper (koolbaseStorageError / koolbaseStorageErrorFromResponse) recognizes the new metadata_invalid wire code via code-first lookup
- —Server validation budget: <= 50 keys per object, <= 8KB total size, keys 1-64 chars matching [a-z0-9_]+, values <= 1024 chars, leading underscore reserved. Validation runs against the merged state — a payload that would result in a 51st key is rejected even if it only contains 2 keys
- —Backwards-compatible — pure additive surface
v6.1.1
PatchJune 2026
Fixed
- —Storage error mapper now switches on lowercase wire codes (path_conflict, quota_exceeded, file_too_large, mime_not_allowed) after the server normalized storage codes to lowercase snake_case. Without this patch, v6.1.0 customers see generic KoolbaseStorageException instead of the typed subclass for storage limit errors. No semantic changes beyond the case match
v6.1.0
MinorJune 2026
New
- —Three new typed exceptions for bucket-limit failures: KoolbaseStorageQuotaExceededException (bucket-wide size cap), KoolbaseStorageFileTooLargeException (per-file size cap), and KoolbaseStorageMimeTypeException (content-type allowlist with type/* wildcards). All extend KoolbaseStorageException so existing catch-all blocks continue to work; catch the specifics to surface clearer messages or prompt the user to delete files, pick a smaller file, or pick a different file type
- —Storage error mapper (koolbaseStorageError / koolbaseStorageErrorFromResponse) recognizes the new bucket-limit codes and the new HTTP statuses (413, 415)
- —Backwards-compatible — existing on KoolbaseStorageException catches continue to work; the new types are additive
v6.0.0
MajorJune 2026
Breaking
- —Realtime no longer takes a projectId — the project is derived from your session token, matching the React Native SDK. Migrate Koolbase.realtime.on(projectId: ..., collection: 'x') to Koolbase.realtime.on(collection: 'x'). Applies to on, onRecordCreated, onRecordUpdated, and onRecordDeleted
- —Storage uploads are now safe-by-default. KoolbaseStorageClient.upload() to a bucket+path where an object already exists throws KoolbaseStorageConflictException instead of silently replacing the existing object. Pass overwrite: true to opt into the previous replacing behaviour
- —Storage operations now throw typed KoolbaseStorageException subtypes instead of generic Exception. Catching Exception still works, but catching the specific subtypes (or the KoolbaseStorageException base) gives cleaner branching
- —Requires a Koolbase server build with PATH_CONFLICT 409 support (shipped alongside this release)
New
- —KoolbaseStorageException — base class for all storage failures, mirroring the KoolbaseDataException pattern from the database layer
- —KoolbaseStorageConflictException — thrown when an upload would replace an existing object and overwrite: false. Exposes the colliding path from the server response
- —KoolbaseStorageNotFoundException, KoolbaseStorageValidationException, KoolbaseStoragePermissionException — typed exceptions for the other storage error classes (404, 400, 403). Storage operations now throw these instead of a generic Exception
- —koolbaseStorageError(statusCode, body) and koolbaseStorageErrorFromResponse(res) — code-first response-to-exception mappers, matching the database layer's pattern
Migration
- —If your app uploads to deterministic paths (e.g. avatars/{user_id}.png) and relied on silent overwrite, pass overwrite: true explicitly on the upload call
- —For user-supplied filenames, catch KoolbaseStorageConflictException and prompt the user before retrying with overwrite: true
- —Consider replacing generic Exception catches around storage operations with KoolbaseStorageException (or specific subtypes) for cleaner branching
v5.1.0
MinorMay 2026
Fixed
- —Realtime now connects. The client was protocol-correct but was never handed an access token (the push-based setToken was never wired), so it never connected. Switched to the same token-provider model as the other clients; it now authenticates with the user session and streams created/updated/deleted events
Changed
- —Removed KoolbaseRealtimeClient.setToken — dead push-model plumbing that was never wired. The token now flows from the SDK automatically
v5.0.0
MajorMay 2026
Security
- —Data-plane requests (database, storage, functions, offline sync) now authenticate with the signed-in user's access token (Authorization: Bearer) instead of the x-user-id header. The header is no longer sent or trusted. Requires the matching Koolbase server build
Breaking
- —KoolbaseStorageClient.upload() no longer accepts a userId parameter — identity comes from the active session automatically
- —End-user identity flows automatically from Koolbase.auth; manual Koolbase.db.setUserId(...) is no longer needed for auth (it remains only for tagging offline-cached records)
New
- —KoolbaseAuthClient.validAccessToken() — returns a currently-valid token, refreshing (single-flight) near expiry; data-plane clients pull from it per request so identity follows the live session
v4.1.0
MinorMay 2026
New
- —Code Push mandatory bundles — the SDK now honors a bundle's mandatory flag. Koolbase.codePush.hasMandatoryUpdate returns true when a mandatory bundle is staged for the next launch (poll it on resume to gate your UI), and the onMandatoryUpdate callback on KoolbaseConfig fires the moment one stages with MandatoryUpdateInfo(version, bundleId) so you can prompt the user to restart
v4.0.0
MajorMay 2026
Breaking
- —Removed the Koolbase.ota client (KoolbaseOtaClient) and its models (OtaCheckResult, OtaProgress, OtaDownloadState). Use Koolbase.codePush for config, flag, and directive overrides, or Koolbase Storage to ship and read raw files
- —Removed the non-functional oauthLogin() stubs from AuthApi and KoolbaseAuthClient (they only ever threw UnimplementedError). Use Koolbase.auth.signInWithApple or signInWithGoogle
- —Removed the deprecated KoolbaseAppleAuth class. Use Koolbase.auth.signInWithApple
- —Removed the deprecated AuthStorage alias. Use SecureAuthStorage — the API is identical at the call site
- —Dropped the sign_in_with_apple dependency; it was only used by the removed KoolbaseAppleAuth. If your app uses Apple Sign-In, declare sign_in_with_apple in your own pubspec.yaml
Changed
- —Code push is now the single bundle client across the SDK — matching the React Native SDK and the server. One surface for over-the-air config, flag, directive, and SDUI updates
v3.3.0
MinorMay 2026
New
- —Typed data-layer exceptions — KoolbaseNotFoundException, KoolbaseValidationException, KoolbasePermissionException, and KoolbaseRateLimitException — plus a shared KoolbaseDataException base. Database operations now throw these (code-first) instead of a generic Exception
Changed
- —Auth exceptions are now selected from the server's stable error code, with status/message fallback for older servers — retiring brittle message string-matching
- —KoolbaseConflictException now exposes the collided field when the server reports it
Fixed
- —insert no longer queues a server-rejected write (e.g. a unique conflict) as an offline write — 4xx rejections surface immediately; only genuine network failures are queued
v3.2.0
MinorMay 2026
New
- —KoolbaseConflictException — thrown by insert, update, and upsert when a write violates a collection's unique constraint (HTTP 409). Catch it to handle duplicates
v3.1.1
PatchMay 2026
Changed
- —README now documents upsert and deleteWhere (no code changes)
v3.1.0
MinorMay 2026
New
- —Koolbase.db.upsert(collection:, match:, data:) — insert-or-update by a match filter; returns KoolbaseUpsertResult { record, created }. Online-only
- —Koolbase.db.deleteWhere(collection:, filters:) — bulk delete by filter; returns the number of records deleted. Online-only
v3.0.0
MajorMay 2026
Breaking
- —Flat record shape — KoolbaseRecord no longer wraps fields under a data envelope. Your fields are now top-level, with system metadata in a reserved $-prefixed namespace: $id, $createdAt, $updatedAt, $collection, and $createdBy
- —Removed KoolbaseRecord.projectId and KoolbaseRecord.collectionId — internal identifiers are no longer exposed on records
- —Requires a Koolbase server on the flat record contract (shipped alongside this release). Older servers return the legacy envelope and will not parse
New
- —record['field'] — direct field access, shorthand for record.data['field']
- —record.collection — the record's collection name
Changed
- —Populated records (via populate()) and realtime payloads now use the same flat $-shape as direct reads
- —Offline cache (Drift) bumped to schema v2 — stale read caches are cleared on upgrade so they refetch in the new shape; pending offline writes are preserved
Migration
- —Remove any record.projectId / record.collectionId usage — those fields are gone
- —record.data['field'] still works; record['field'] is the new shorthand
v2.11.0
MinorMay 2026
New
- —Sign in with Google — production-ready end-user OAuth via Koolbase.auth.signInWithGoogle(idToken, nonce), routing to /v1/sdk/auth/oauth/google
- —RS256-only JWKS verification against Google's certs, multi-audience support (iOS / Android / web client IDs per environment), 15-minute replay defense, and optional nonce check
- —Three typed exceptions: GoogleSignInNotConfiguredException, InvalidGoogleTokenException, GoogleEmailRequiredException
- —Auto-link policy matches Apple — a Google identity attaches to an existing user only when both emails are verified and match (case-insensitive); otherwise creates a new user or surfaces OAuthEmailConflictException
- —Configure google_client_ids per environment in project_oauth_configs before use (dashboard OAuth UI lands in a later release)
v2.10.0
MinorMay 2026
New
- —Sign in with Apple — production-ready end-user OAuth via Koolbase.auth.signInWithApple(identityToken, nonce, fullName), with a dedicated server endpoint at /v1/sdk/auth/oauth/apple
- —Server-side verification against the project's configured Bundle ID via Apple's JWKS — RS256-only, audience-bound, replay defense (iat max-age 15min), optional nonce check
- —Auto-link policy — an Apple identity attaches to an existing user only when both emails are verified and match; otherwise creates a new user or surfaces OAuthEmailConflictException
- —Four typed exceptions: AppleSignInNotConfiguredException, InvalidAppleTokenException, AppleEmailRequiredException, OAuthEmailConflictException
- —Configure the provider (Bundle ID) in project_oauth_configs before use
Deprecated
- —KoolbaseAppleAuth.signIn and oauthLogin remain deprecated and continue to throw UnimplementedError
- —The v1.5.0 callback-based Apple API was a wrong-shape design and won't be revived — use Koolbase.auth.signInWithApple(...) on the auth client
v2.9.1
PatchMay 2026
Fixed
- —Device metadata headers from v2.9.0 are now actually attached to auth requests — v2.9.0 had the DeviceMetadata code but no construction site, so no x-koolbase-* headers were sent on the wire. Upgrade from v2.9.0 to get the metadata features
New
- —KoolbaseConfig.authTimeout (Duration, default 10s) — per-request auth timeout
- —KoolbaseConfig.httpClient (http.Client?) — inject your own HTTP client for logging, retries, proxy, or shared pools; the SDK won't close a caller-supplied client. Currently scoped to auth requests
- —AuthApi.dispose() — closes the underlying HTTP client only if the SDK owns it
Changed
- —KoolbaseAuthClient.logout() now returns Future<bool> (whether the server-side call succeeded). The local session is always cleared regardless. Source-compatible
- —dispose() now cascades to AuthApi.dispose() so the HTTP client is closed on shutdown
v2.9.0
MinorMay 2026
New
- —KoolbaseAuthStorage abstract interface — plug in custom auth storage backends (encryption layers, web targets, test mocks)
- —SecureAuthStorage default (flutter_secure_storage, iOS Keychain first_unlock_this_device, Android EncryptedSharedPreferences) — now persists the full session: access token, refresh token, expiry, and user
- —PersistedSession value class for fully-typed session persistence
- —RestoreResult enum from restoreSession() (noSession / restored / expired / offline) — offline-aware restoration with no network round-trip at launch
- —KoolbaseAuthClient.unlock(token) — consume an unlock token from a brute-force unlock email
- —DeviceMetadata built automatically at initialize(); stable per-install device label. koolbaseSdkVersion constant exported
- —Typed exceptions: AccountLockedException (429 + lockout, nullable lockedUntil), RateLimitException (429), UnlockTokenInvalidException, TokenRevokedException
Changed
- —restoreSession() now returns Future<RestoreResult> (was Future<void>) — source-compatible; branch on the enum for offline-aware UI
- —AuthApi constructor is no longer const — it accepts optional DeviceMetadata
- —Single-flight refresh — concurrent callers hitting an expired token share one underlying refresh
- —Every authenticated request now carries User-Agent and x-koolbase-* headers (SDK, version, platform, app version, device label)
Fixed
- —Refresh-token race — parallel calls no longer trigger competing refreshes; server-side rotation no longer invalidates peer in-flight tokens
- —Offline launch — restoreSession() no longer logs users out on a network failure; it distinguishes auth rejection from network errors
- —401-on-refresh now correctly throws SessionExpiredException (previously surfaced as InvalidCredentialsException)
- —Profile updates (updateProfile, getCurrentUser, linkPhone) are now re-persisted and survive app restart
- —linkPhone now emits on authStateChanges; forgotPassword now surfaces errors as typed exceptions
Deprecated
- —oauthLogin(), AuthApi.oauthLogin(), and KoolbaseAppleAuth.signIn() — these targeted the dashboard OAuth handler and never created end-user sessions; all three now throw UnimplementedError. Proper end-user OAuth ships in v2.10.x
- —AuthStorage class replaced by SecureAuthStorage — kept as a deprecated subclass for source compatibility, removed in v3.0.0
v2.8.0
MinorMay 2026
New
- —Functions: authenticated invocations now forward the signed-in user's session automatically — Koolbase.functions.invoke() includes the access token when a user is signed in via Koolbase.auth
- —Functions receive caller identity via ctx.auth — a map with user_id (string or null) and is_authenticated (bool)
- —Unauthenticated invokes continue to work; Functions decide whether they require auth and respond with AUTH_REQUIRED if needed
- —Token refresh is transparent — the next invoke after a refresh uses the fresh token with no client-side wiring
- —Backwards compatible — no breaking changes
v2.7.0
MinorMay 2026
New
- —Phone + OTP authentication — sign users in with their phone number, for markets and apps where email isn't the primary identifier
- —sendOtp({phoneNumber}) — sends a 6-digit OTP to an E.164 number and returns the expiry timestamp
- —verifyOtp({phoneNumber, code}) — verifies the code and signs in (creates the account if new); returns PhoneVerifyResult with an isNewUser flag
- —linkPhone({phoneNumber, code}) — links a phone number to an already-authenticated user
- —New types OtpSendResult and PhoneVerifyResult; KoolbaseUser now exposes phoneNumber and phoneVerified
- —New exceptions: InvalidPhoneNumberException, OtpExpiredException, OtpInvalidException, OtpMaxAttemptsException, OtpRateLimitException, PhoneAlreadyLinkedException, SmsConfigMissingException
- —Numbers must be E.164; configure an SMS provider (Twilio, Africa's Talking, or Hubtel) in the dashboard first
v2.6.4
PatchMay 2026
Improved
- —README — full feature documentation
v2.6.3
PatchApril 2026
Changed
- —Updated drift to ^2.31.0 and drift_flutter to ^0.2.8
v2.6.2
PatchApril 2026
Changed
- —Updated dependencies to latest versions
- —Fixed static analysis warnings
- —Removed deprecated encryptedSharedPreferences parameter
v2.6.1
PatchApril 2026
Improved
- —README — Logic Engine v2 operators
v2.6.0
MinorApril 2026
New
- —Logic Engine v2 — richer conditions with new operators: gte, lte, contains, starts_with, ends_with, in_list, not_in_list, between, is_true, is_false, not_exists
- —All operators work with AND/OR condition groups
v2.5.1
PatchApril 2026
Improved
- —README — added Sign in with Apple section
v2.5.0
MinorApril 2026
New
- —Sign in with Apple — KoolbaseAppleAuth.signIn(), KoolbaseAuthClient.oauthLogin(), and AuthApi.oauthLogin()
- —Apple identity token verified server-side using Apple's JWKS endpoint
- —Supports email relay addresses from Apple's private email relay
v2.4.0
MinorApril 2026
New
- —Koolbase Cloud Messaging — KoolbaseMessaging, push notification delivery via FCM
- —Koolbase.messaging.registerToken(token, platform) — register an FCM device token with Koolbase
- —Koolbase.messaging.send(to, title, body, data) — send a push notification to a specific device
- —KoolbaseConfig.messagingEnabled (default true); device ID automatically attached to token registration
- —Setup: add your FCM server key as a project secret named FCM_SERVER_KEY in the dashboard
v2.3.1
PatchApril 2026
Improved
- —README — added Code Push, Analytics, and Logic Engine sections, comparison table, clearer getting-started guide
v2.3.0
MinorApril 2026
New
- —Koolbase Analytics — KoolbaseAnalyticsClient with batched flush; Koolbase.analytics top-level accessor
- —track(eventName, properties), screenView(screenName), setUserProperty(key, value), identify(userId), reset()
- —KoolbaseNavigatorObserver — auto screen tracking via the Flutter navigator
- —Auto events: app_open, screen_view, session_end. Batch flush every 30s, on background, on close, or at 20 queued events
- —Events retry on network failure (re-queued up to batch size); KoolbaseConfig.analyticsEnabled (default true)
v2.2.0
MinorApril 2026
New
- —Logic Engine v1 — event-driven flows. FlowExecutor (deterministic flow runtime), FlowContext (context / config / flags with dot-notation), FlowResult
- —Node types: if, sequence, event (terminal), set. Operators: eq, neq, gt, lt, and, or, exists. Data sources: context, config, flags
- —BundlePayload extended with a flows field; KoolbaseDynamicScreen auto-executes flows on rfw events
- —Koolbase.executeFlow() top-level accessor; KoolbaseCodePushClient.executeFlow() for direct access
v2.1.0
MinorApril 2026
New
- —Server-Driven UI via rfw — KoolbaseDynamicScreen renders server-defined UI from the active bundle
- —KoolbaseCodePushScope (wires the code push client into the tree) and KoolbaseRfwWidget (custom widget registration)
- —Default widget library: Column, Row, Stack, Container, Text, ElevatedButton, Card, and Koolbase widgets
- —ScreenResolver extracts and caches rfw binaries from the bundle; bundle payload supports a screens field (screenId to .rfw filename)
- —KoolbaseDynamicScreen guarantees: never crash, never block, never surprise — all failures fall back to the local widget
Fixed
- —KoolbaseCodePushScope.of(context) moved to didChangeDependencies to avoid initState context restrictions
v2.0.0
MajorMarch 2026
New
- —Code Push — runtime bundle delivery. KoolbaseCodePushClient handles the full bundle lifecycle (check, download, verify, cache, activate)
- —BundleCache four-slot system (pending, ready, active, archive); BundleVerifier with sha256 verification on every download
- —KoolbaseUpdater (background check + download on cold launch); BundleLoader (promote ready to active, rollback)
- —RuntimeOverrideEngine — merge precedence: app defaults to Remote Config to runtime bundle. config*() accessors and isEnabled() return bundle values when a bundle is active
- —KoolbaseConfig.codePushChannel (default 'stable'); Koolbase.codePush accessor
Migration
- —Add codePushChannel to your KoolbaseConfig if you want a specific channel. No other breaking changes
v1.9.0
MinorMarch 2026
New
- —Functions: Dart runtime support. FunctionRuntime enum (deno and dart); deploy() to ship functions directly from Flutter
Fixed
- —invoke() request body format
v1.8.0
MinorMarch 2026
New
- —Offline-first database powered by Drift — cache-first reads (instant UI, background refresh), optimistic writes (local then sync)
- —Auto-sync on network reconnect via connectivity_plus; manual Koolbase.db.syncPendingWrites(); QueryResult.isFromCache flag
- —Write queue with max 3 retries before dropping; user-scoped cache — no cross-user data leakage
v1.7.0
MinorMarch 2026
New
- —Database: .populate() on the query builder for relational data — fetch related records from other collections in a single query
- —Usage: .populate(['author_id:users', 'category_id:categories']). Populated records are injected with the _id suffix removed (author_id becomes author)
v1.6.0
MinorMarch 2026
New
- —Full BaaS feature set — auth, database, storage, realtime, functions, feature flags, remote config, version enforcement, and OTA updates
v1.5.0
MinorMarch 2026
New
- —OTA Updates — Koolbase.ota, over-the-air bundle updates for Flutter apps
v1.4.0
MinorFebruary 2026
New
- —Realtime — Koolbase.realtime WebSocket SDK
- —on(projectId, collection) for all events; onRecordCreated / onRecordUpdated / onRecordDeleted streams
- —connectionState stream; setToken(token) for authenticated subscriptions
- —Auto-reconnect with 3-second backoff; reference-counted subscriptions, safe for multiple listeners
v1.3.0
MinorJanuary 2026
New
- —Database — Koolbase.db with a fluent query builder
- —collection('name').where('field', isEqualTo: value).limit(20).get(); insert(); doc(id).get / update / delete
- —KoolbaseRecord, KoolbaseCollection, and QueryResult models
- —Collection-level permission enforcement: public, authenticated, owner
v1.2.0
MinorDecember 2025
New
- —Storage — Koolbase.storage file storage SDK
- —upload() to Cloudflare R2 via presigned URLs; getDownloadUrl() for signed download URLs; delete()
- —KoolbaseObject, KoolbaseBucket, and UploadResult models; three-step upload flow (get URL, upload, confirm)
v1.1.0
MinorNovember 2025
New
- —Auth — Koolbase.auth full authentication SDK
- —signUp, login, logout, forgotPassword, resetPassword, verifyEmail; currentUser, isAuthenticated, authStateChanges stream
- —Automatic session restoration on app start; secure token storage via flutter_secure_storage; JWT access tokens with automatic refresh
- —KoolbaseUser and AuthSession models; KoolbaseAuthException and typed exceptions
v1.0.0
MajorOctober 2025
New
- —Initial release
- —Feature flags with rollout percentages and kill switches
- —Remote config — string, int, double, bool, map
- —Version enforcement with force / soft update policies
- —Offline support with local cache; background polling