Changelog — React Native
Every release of the @koolbase/react-native SDK, newest first. Dates are when each version was published to npm.
v12.9.0
Minor1 October 2026Changes
- —Storage: publicUrlFor({ bucket, path, transform }), the CDN URL for a file in a public bucket using the project identity the SDK learned at bootstrap -- no projectId argument, as Flutter's publicUrlFor. While identity is unavailable (the first bootstrap has not completed) it throws KoolbaseStorageProjectIdentityError (code project_identity_unavailable) and nudges a background bootstrap refresh, so a later call in the same session succeeds.
- —Bootstrap at initialize: initialize now fetches the bootstrap payload (flags, remote config, version policy, project identity) in the background, as the Flutter SDK does. It never blocks startup. Flags expose projectId() and refresh().
v12.8.0
Minor30 September 2026Changes
- —Conditional writes. update(id, data, { expectedRevision }) and delete(id, { expectedRevision }) apply only if the record still carries that revision; otherwise they throw KoolbaseRevisionMismatchError, as the Flutter SDK's update and delete do (expected_revision in the body and in the URL respectively). Without the option both behave exactly as before.
- —A write refused as stale refreshes the live lists. On a revision mismatch the collection's open queries re-run (every one when the collection is not known), so a list already shows the newer data; the error is thrown unchanged.
- —Post-write refreshes run in the background, as the Flutter SDK's do. 12.7.0 awaited them, so every write waited for every live list on its collection to reload before resolving.
v12.7.0
Minor30 September 2026Changes
- —Live lists refresh after a write. Every open collection query (useCollection, KoolbaseCollectionController) re-runs after a successful insert, update, delete, upsert, batch or delete-where on its collection -- as the Flutter SDK's post-write refresh does. Silent (no refreshing), each query with its own filters, ordering and limit; a failure in one never stops the others. A delete of a record never read on this device refreshes every live query rather than guessing its collection. Until now a list kept what it loaded until the app was reloaded.
v12.6.0
Minor26 September 2026New
- —useRecord(collection, id): one record by id, as React state, with status (loading, loaded, notFound or error), record and refresh(). 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 failed refresh keeps the record shown
- —KoolbaseRecordController: the same behaviour without React
v12.5.1
Patch26 September 2026Fixed
- —Installs in an Expo SDK 57 app: @react-native-async-storage/async-storage 2.2 is accepted as well as 3.x. Expo SDK 57 and Expo Go ship 2.2.0, and the SDK uses only getItem, setItem, removeItem and getAllKeys, which both provide
v12.5.0
Minor26 September 2026New
- —useCollection(collection, { where, orderBy, orderDesc, limit }): a collection as React state, with status (loading, loaded or error), records, refresh() and loadMore(), plus isFromCache, refreshing, loadingMore and an exact hasMore. It shows the cached result first and replaces it when the server answers; a failed refresh keeps what is shown. Filters are equality only for now: where: { field: value }. Needs React 18 or later
- —If rows are added or removed on the server while more than one page is loaded, one can be skipped until refresh(), which reloads from the first page. No row is ever shown twice
- —KoolbaseCollectionController: the same behaviour without React
- —Koolbase.db.query(collection, { onRefresh }): when a query is answered from the cache, onRefresh receives the server's result once the background refresh lands
Changed
- —Query cache keys no longer depend on the order of keys in the options, so { a, b } and { b, a } share one cache entry. Entries cached by an earlier version are missed once and fetched again
v12.4.0
Minor25 September 2026Changed
- —Analytics is off by default. Pass analyticsEnabled: true to initialize to send events. While it is off, Koolbase.analytics calls do nothing, and the first logs how to turn it on
- —KoolbaseNetworkError.userMessage: a short message that is safe to show to people, alongside the detailed message for developers
v12.3.0
Minor25 September 2026New
- —Koolbase.db.query(collection, { cache: 'network-only' }) skips the local cache and waits for the server; the result still refreshes the cache. Use it for data changed by a Function, a server or another user
- —Koolbase.db.invalidate(collection) forgets this device's cached query results for a collection, so the next query waits for the server
Changed
- —Network failures throw KoolbaseNetworkError instead of a bare network TypeError. It is still a TypeError, and names the host that could not be reached
v12.2.0
Minor23 September 2026New
- —Two-step sign-in (MFA): every sign-in method now throws MfaRequiredError carrying a challengeToken when the person's account has an authenticator on, including phone, Google and Apple, whose error parsers all map it. verifyMfa and verifyRecoveryCode finish sign-in and store the session, the latter returning { session, recoveryCodesRemaining }
- —enrollMfa, confirmMfaEnrollment, mfaStatus, stepUpMfa, disableMfa and regenerateRecoveryCodes manage it. Six new error classes in the mapping table and fidelity list
v12.1.0
Minor23 September 2026New
- —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
- —Fiscal: KoolbaseFiscal with submit() and status(). A timeout on submit means poll status() with the same clientRef — do not submit again, or the sale may be recorded twice
- —Reference errors are catchable: KoolbaseReferenceInvalidError, KoolbaseReferenceInUseError, KoolbaseDanglingReferencesError (carrying the offending records) and KoolbaseCollectionReferencedError
Changed
- —oauthLogin() now points to signInWithGoogle() and signInWithApple(). Its deprecation said OAuth was not yet shipped and to use email and password, which had long stopped being true
v12.0.0
Major20 September 2026Breaking
- —Fourteen error classes now report the code the server actually sends. error.code is a public field and on these classes it carried a value the API has never emitted — EmailAlreadyInUseError said email_taken for a server that says email_in_use, and the Apple and Google classes each reported a provider-split code where the server sends one unified code. Catching by type has always worked and is unaffected: no class, constructor or export changed
- —If you compare error.code against a literal, or assert on it in tests, update those comparisons. The full old-to-new table is in the package CHANGELOG. A comparison against an old value could never have matched a real response, but it can match a mock, which is why this is a major release
New
- —Account settings: auth.getCurrentUser(), auth.updateProfile(), auth.changePassword() and auth.deleteAccount(). These existed on the server and in the Flutter SDK and not here, so a web application could not offer a settings screen at all. changePassword requires the current password — a stolen session should not be enough to lock the real owner out
- —Session management: 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. Each session carries its device label, IP, user agent and timestamps, with isCurrent marking this device; token hashes are never included
- —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
- —db.aggregate(). Count and total over a whole collection, with the read rule applied inside the query — not a paged query you add up in the client. And never a bare number: collections are schemaless, so every result carries its accounting, saying how many records contributed to each measure and how many were skipped. A calendar bucket requires a timezone and the types enforce it
Changed
- —x-koolbase-platform-version falls back to unknown rather than being sent empty. The browser version is read from the user agent, which recognises Chrome, Firefox, Safari and Edge — so Brave, Opera, Samsung Internet and in-app webviews reported nothing. An empty header stored against a session is indistinguishable from an SDK that never reported itself
v11.4.0
Minor20 September 2026New
- —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 or who waited past the 24-hour link expiry had no way back except contacting the developer
- —It returns nothing and reveals nothing: the server answers identically whether the address has an account, has none, or is already verified, because anything else would let anyone discover who has signed up. Show the same check-your-email message either way. resendVerificationEmail() is unchanged, for a signed-in user who can safely be told more
v11.3.0
Minor20 September 2026Fixed
- —Thirty error codes the API emits were not mapped, so they arrived as generic errors and an app's instanceof check silently never ran
- —Two mappings were for codes the API does not emit — session_expired and token_revoked. The server sends invalid_refresh_token; branching on the other two could never have matched
- —restoreSession no longer clears the stored session on InvalidCredentialsError. During a restore that error points at the project key, not the user's session, so deleting the refresh token signed people out with no way back
v11.2.0
Minor20 September 2026Fixed
- —A function that timed out was indistinguishable from one that threw: a 504 arrived as a generic execution failure. A timeout means retry or raise the limit; an exception means fix the code
- —upload_expired and cap_below_usage were unmapped in storage, and four database codes with them — ambiguous_match among them
- —Errors report the code the server sent rather than their category, so a collection_not_found response no longer produces an error saying not_found
v11.1.0
Minor20 September 2026Fixed
- —Fourteen error codes the API emits were not mapped, arriving as generic errors
- —WeakPasswordError carries the server's message when there is one, so a project with a stricter rule than the SDK's can say what it requires
v11.0.0
Major20 September 2026Breaking
- —register() returns a discriminated result rather than a user. Under a verified-contact policy the server issues no session, and the SDK was fabricating one from a response with no tokens — so an app believed it had signed someone in who could not make a single authenticated request. The result is now either authenticated with a session, or verification_required with none, and the type makes the difference impossible to ignore
- —A pending signup persists nothing, fires no auth-state change, and does not destroy an existing session. Signing up a second account while signed in no longer signs the first one out on a project that requires verification
v10.4.0
Minor19 September 2026New
- —auth.verifyEmail(token) — complete email verification from a token, so an app can handle its own verification link rather than sending users to a hosted page
- —auth.resendVerificationEmail() — re-send to a signed-in user, with the cooldown and daily cap reported as typed errors rather than a generic refusal
v10.3.0
Minor19 September 2026Fixed
- —Analytics events carried no user unless the app called identify() — so every event from a signed-in user was attributed to nobody, and funnels that crossed the sign-in boundary broke. Events now carry the signed-in user automatically
v10.2.0
Minor19 September 2026Added
- —Cross-tab coordination in the browser SDK that shares this core. React Native is one process over its own storage, so nothing changes here — the platform seam simply gained a locking contract that React Native satisfies by running the work.
v10.1.0
Minor19 September 2026Changed
- —UploadOptions.file accepts a Blob in addition to { uri, name, type }. The React Native path is unchanged; the union exists because the same core now serves a browser, where a File is the only form the bytes come in.
v10.0.2
Patch19 September 2026Fixed
- —The analytics flush interval and a pending realtime reconnect counted as work on Node's event loop, so a CLI, a test runner or a build step that initialised the SDK never exited. Both timers are now unref'd where the runtime supports it.
v10.0.1
Patch19 September 2026Fixed
- —Runs under server rendering without crashing. Neither browser store exists during a server render, and throwing there took the whole page down rather than degrading
v10.0.0
Major19 September 2026Changed
- —The package is now a thin layer over @koolbase/core. Auth, database, the offline queue, conflicts, realtime, storage and functions moved into a shared core that this package and the new @koolbase/js both compose. Imports do not change: everything the core exports is re-exported here. What changes is that one test suite now proves both hosts, so a fix in one is a fix in the other.
- —Host access goes through a platform adapter. AsyncStorage, NetInfo, AppState and Platform are consumed by one file and nothing else in the SDK imports a native module. KoolbaseConfig.platform accepts a custom adapter.
- —The default session storage is decided by the platform — keychain when react-native-keychain is installed, as before, but chosen by the adapter rather than hard-coded in auth.
Removed
- —Code push. Koolbase.codePush, codePushChannel, onMandatoryUpdate, KoolbaseCodePush, BundleManifest and BundlePayload are gone. Code push is a Flutter feature, where it patches the Dart VM; the React Native version pushed config and flag overrides, which Remote Config and Feature Flags already do without a bundle. Delete any codePushChannel from your initialize config — flag and config reads keep working from the remote values.
- —Koolbase.executeFlow and the logic engine. Flows only ever arrived through code-push bundles, so with no bundle the method could only return an empty result. Removed rather than left as a call that does nothing.
- —KoolbaseAppleAuth — deprecated since 1.9.0 and throwing not_implemented since then. Koolbase.auth.signInWithApple(...) is the Apple flow and is unchanged.
- —jszip — code push was its only user. The core now has no runtime dependencies.
v9.2.0
Minor3 August 2026Fixed
- —delete() queued the write before attempting it and never removed it, so a delete that succeeded stayed in the queue and replayed later — against a record that may since have been recreated under the same id. It can now also fail: it previously returned Promise<void> with no throw path, so a refused delete reported success.
- —Offline insert chains broke at the server boundary. A record created offline got a local_ id, and any queued update or delete addressed that id — which ceased to exist the moment the insert replayed. Ids are now UUID v4 from the moment of creation, so a chain of offline edits replays exactly as it was made.
- —A 401 carrying a body code did not clear the session: responses were mapped by their body's code field before their status, so a server answering a rejected credential with code: 'validation_error' left the dead session in place. Status is now authoritative.
- —Every cold query() hit the network twice — the stale-while-revalidate refresh fired before the cache was consulted.
- —Signed-out state read as empty state. Per-user caches and queues fell back to a shared anonymous bucket, so a signed-out call reported zero pending writes rather than refusing.
- —A replayed write left the cache it invalidated behind, and a terminally rejected insert left its optimistic record standing — the app displayed a record that does not exist and never will.
- —Realtime reconnected every three seconds forever, with no backoff and no ceiling.
Added
- —db.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.
- —Offline update and delete are queued only when the SDK knows what the record looked like at the time; otherwise they throw KoolbaseOfflineBaselineUnavailableError rather than queueing something that cannot be replayed safely. A refused write becomes a durable conflict resolvable four ways, rather than a blind retry.
- —One exception hierarchy: KoolbaseError is the root, with the data, storage, auth and Function families beneath it.
v9.1.0
Minor25 July 2026Fixed
- —Device identity is now a single shared, persisted value across the SDK. Feature flags, code push and messaging were each initialised with a hardcoded 'rn-device' string instead of a real device id. That silently broke three things: every device collided on one messaging registration row (only the last-registered device per project received push), every device produced the same feature-flag rollout bucket (a 10% rollout was on for everyone or no one, never 10%), and code-push targeting collided the same way
v9.0.0
MajorJune 2026Changed
- —Package renamed from @techfinityedge/koolbase-react-native to @koolbase/react-native for brand consistency with the rest of the Koolbase SDKs. The API surface, behavior, and exports are identical to 8.0.0 — only the package name changed.
- —Migration: update the dependency name and every import path (@techfinityedge/koolbase-react-native to @koolbase/react-native). No code changes beyond the import specifier. The old package is deprecated on npm and points here.
v8.0.0
MajorJune 2026New
- —Three search modes from a single endpoint. KoolbaseDatabase.searchSemantic accepts a new mode option of type SearchMode 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)
- —KoolbaseDatabase.searchSemantic accepts a new minSimilarity option (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 SearchMode type exported from @techfinityedge/koolbase-react-native
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)
v7.0.0
MajorJune 2026Breaking
- —KoolbaseDatabase.searchSemantic now validates that exactly one of queryVector / queryText is supplied and throws Error otherwise. The queryVector parameter is now optional; existing callers that always passed it continue to work unchanged
New
- —KoolbaseDatabase.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
- —KoolbaseDatabase.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)
v6.0.0
MajorJune 2026New
- —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
- —KoolbaseDatabase.setVector(recordId, field, vector) — writes (or replaces) a vector for a record on the named field. The field must already be declared on the collection via the dashboard or CLI; the vector's length must match the field's declared dimension
- —KoolbaseDatabase.getVector(recordId, field) — reads a stored vector back as KoolbaseVector { recordId, fieldName, vector, createdAt, updatedAt }
- —KoolbaseDatabase.deleteVector(recordId, field) — removes a record's vector slot. Does NOT remove the field declaration itself — the field stays settable on other records
- —KoolbaseDatabase.searchSemantic({ collection, field, queryVector, limit, where }) — runs an HNSW similarity search ranking records by cosine distance. The collection's read rule is applied after the lookup; where is an optional equality filter map. Returns SemanticSearchResult { hits, total } where each hit carries record and distance
- —Object versioning — full read + write surface against versioned buckets. KoolbaseStorage.listVersions(bucket, path) returns the timeline newest-first; each KoolbaseObjectVersion carries versionId, size, etag, metadata, createdAt, plus isCurrent and isDeleteMarker flags
- —KoolbaseStorage.getVersion(bucket, path, versionId) — metadata for a single version, current or historical
- —KoolbaseStorage.getDownloadUrl(bucket, path, versionId?) — optional versionId; omit to download current, pass to download a historical version directly. Throws for delete markers
- —KoolbaseStorage.restoreVersion(bucket, path, versionId) — brings a history version back as the new current. The previously-current row is snapshotted into history first, so restore is itself a versioned event you can undo
- —KoolbaseStorage.purgeVersion(bucket, path, versionId) — hard-removes a single history row plus its .versions/ R2 bytes
- —New typed exports: KoolbaseVector, KoolbaseSemanticHit, SemanticSearchResult, KoolbaseVectorDimensionMismatchError, KoolbaseObjectVersion
Changed
- —Vector fields must be declared on a collection ahead of time via the dashboard or CLI — the React Native SDK does not declare schema (mirrors how collections and storage buckets are declared). 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 similarity search has no useful offline semantics
- —Semantic search respects the collection's read rule the same way query() does — owner / scoped / conditional records are filtered to the caller after the HNSW lookup, so strict rules may return fewer than limit results
Fixed
- —Database errors table in README.md previously listed storage error rows (copy-paste bug). Now lists the actual database error subclasses
Migration
- —Purely additive — no existing methods, types, or exports changed. Upgrading from 5.x requires only yarn upgrade @techfinityedge/koolbase-react-native (or the equivalent npm/pnpm/bun command) and rebuilding
v5.2.0
MinorJune 2026New
- —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
- —UploadOptions.metadata?: Record<string, string> — optional metadata map passed at upload time and recorded in the same confirm step. On an overwrite upload, the new metadata replaces (does not merge into) the previous object's metadata, matching object-store semantics where overwrite produces a new object rather than patching the old
- —KoolbaseObject.metadata: Record<string, string> on every returned object — always non-null (empty object when no keys are set), so callers never have to null-check before reading. Defensive decode in mapObjectFromServer handles older cached responses without metadata gracefully
- —KoolbaseStorageMetadataInvalidError — typed error 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, pairs with koolbase_flutter v6.2.0
v5.1.1
PatchJune 2026Fixed
- —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, v5.1.0 customers see generic KoolbaseStorageError instead of the typed subclass for storage limit errors. No semantic changes beyond the case match
v5.1.0
MinorJune 2026New
- —Three new typed errors covering the bucket-limit failure modes: KoolbaseStorageQuotaError (bucket-wide size cap), KoolbaseStorageFileTooLargeError (per-file size cap), and KoolbaseStorageMimeTypeError (content-type allowlist with type/* wildcards). All extend KoolbaseStorageError so existing instanceof KoolbaseStorageError catch-all blocks continue to work; check the specific type to branch on the kind of limit hit
- —Storage error mapper (koolbaseStorageError / koolbaseStorageErrorFromResponse) recognizes the new codes via code-first lookup and the new HTTP statuses (413, 415) via status fallback
- —Backwards-compatible — pure additive surface, pairs with koolbase_flutter v6.1.0
v5.0.0
MajorJune 2026Breaking
- —Storage URLs realigned to current server contract. The v3.0.0 security audit updated auth headers but left storage calling the old pre-refactor endpoints (/v1/sdk/storage/{bucket}/upload, /download, /delete), which the server no longer routes. Storage uploads have been non-functional since v3.0.0. v5.0.0 realigns to /v1/sdk/storage/upload-url, /confirm, /download-url, /object
- —3-step upload flow — upload() now does presign → R2 PUT (raw binary, not multipart) → confirm, matching koolbase_flutter v6.0.0. Confirmation records the object in storage_objects, populates etag/size, and prevents the orphan reaper from deleting your file. Previous 'uploads' bypassed confirm entirely and would have been swept on the next reaper pass
- —upload() return shape changed — returns UploadResult { object, downloadUrl } instead of { url }. object is the full KoolbaseObject metadata (id, size, content type, timestamps)
- —Safe-by-default uploads — UploadOptions accepts an overwrite?: boolean field, defaulting to false. Uploads to a path where an object already exists are rejected with KoolbaseStorageConflictError instead of silently overwriting. Pass overwrite: true to opt into the previous replacing behaviour
- —Storage operations now throw typed KoolbaseStorageError subtypes instead of generic Error
New
- —KoolbaseStorageError — base class for all storage failures, mirroring the KoolbaseDataError pattern from the database layer
- —KoolbaseStorageConflictError — thrown when an upload would replace an existing object and overwrite: false. Exposes the colliding path from the server response
- —KoolbaseStorageNotFoundError, KoolbaseStorageValidationError, KoolbaseStoragePermissionError — typed errors for the other storage error classes (404, 400, 403)
- —koolbaseStorageError(status, body) and koolbaseStorageErrorFromResponse(res) — code-first response-to-error mappers, matching the database-errors module pattern
- —KoolbaseObject and UploadResult types in types.ts — full object metadata is now part of the public surface
Migration
- —If your app uploads to deterministic paths and relied on silent overwrite, pass overwrite: true explicitly
- —For user-supplied filenames, catch KoolbaseStorageConflictError and prompt the user before retrying with overwrite: true
- —If you used const { url } = await upload(...), switch to const { object, downloadUrl } = await upload(...)
- —Consider replacing generic Error catches around storage operations with KoolbaseStorageError (or specific subclasses) for cleaner branching
- —Verification recommended — storage in v3.0.0 through v4.2.1 was non-functional; v5.0.0 is the first working upload path since the security audit. Test uploads end-to-end on real iOS and Android devices after upgrading (RN's fetch Blob-PUT behaviour can vary by platform)
v4.2.1
PatchMay 2026Fixed
- —Realtime now delivers deleted events — v4.1.0 dropped them because the event type required a record (deletes don't carry one). record is now optional and recordId is provided on deletes, matching the Flutter SDK
v4.2.0
MinorMay 2026Fixed
- —Realtime now works — the client previously hit the wrong endpoint, authenticated with the public key instead of the user session, never sent a subscribe message, and parsed the wrong event shape, so it delivered nothing. Rewritten to the real protocol: connects with the signed-in user's session, subscribes per collection, and streams created/updated events with automatic reconnect
Changed
- —Koolbase.realtime.subscribe(collection, cb) no longer needs a project — it's derived from the user's session
v4.0.0
MajorMay 2026Breaking
- —insert and update are now online-first with an offline fallback (matching upsert). When the server is reachable they await the response and throw typed errors on rejection — a unique-constraint conflict throws KoolbaseConflictError with the offending field. Previously these methods swallowed all server errors and insert always returned a local-id optimistic record
Changed
- —insert returns the authoritative server record (real id) on success instead of an optimistic local_-prefixed id; only a genuine network failure falls back to the optimistic, queued offline path
Migration
- —Wrap insert/update in try/catch to handle conflicts (catch KoolbaseConflictError for duplicates). If you relied on the returned id starting with "local_", treat the returned record's id as authoritative — when online it is now the server id
v3.1.0
MinorMay 2026New
- —Koolbase.db.batch(operations) — run multiple writes as a single atomic transaction; all operations commit together or none are applied. Supports insert / update / delete / upsert across multiple collections, returning one BatchResult per operation in order. Online-only by design (atomicity needs the server's authoritative view); throws on network failure rather than queuing
- —BatchOp factory (BatchOp.insert / update / delete / upsert) and BatchResult interface exported from types — brings React Native to parity with koolbase_flutter's batch()
v3.0.0
MajorMay 2026Security
- —The data plane no longer trusts a client-supplied x-user-id — request identity is derived solely from the verified Bearer access token. db, storage, functions, and the sync engine all send Authorization: Bearer on every call
New
- —KoolbaseAuth.validAccessToken() — a refresh-aware access-token provider used internally by the data-plane clients
v2.4.0
MinorMay 2026New
- —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 the init config fires the moment one stages with { version, bundleId } so you can prompt the user to restart
v2.3.0
MinorMay 2026New
- —Typed data-layer errors — KoolbaseNotFoundError, KoolbaseValidationError, KoolbasePermissionError, and KoolbaseRateLimitError — plus a shared KoolbaseDataError base. query, get, upsert, and deleteWhere now throw these (selected from the server's error code) instead of a generic Error
Changed
- —Auth errors are now selected from the server's stable error code, with status/message fallback for older servers — retiring brittle message string-matching
- —KoolbaseConflictError now exposes the collided field and extends KoolbaseDataError
v2.2.0
MinorMay 2026New
- —KoolbaseConflictError — thrown by upsert on a unique-constraint violation (HTTP 409). insert and update are optimistic/offline-first and surface conflicts at sync time, not as a thrown error
v2.1.0
MinorMay 2026New
- —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
v2.0.0
MajorMay 2026Breaking
- —Flat record shape — records are no longer wrapped in a data envelope on the wire; your fields come back top-level, with system metadata in a reserved $-prefixed namespace ($id, $createdAt, $updatedAt, $collection, $createdBy). The SDK maps this back into KoolbaseRecord, so you still read fields via record.data.<field>
- —Removed KoolbaseRecord.projectId and KoolbaseRecord.collectionId
- —Requires a Koolbase server on the flat record contract (shipped alongside this release). Older servers return the legacy envelope and are not compatible
New
- —KoolbaseRecord.collection — the record's collection name
Fixed
- —KoolbaseRecord.createdAt / updatedAt are now reliably populated — under the previous raw cast they were silently undefined (snake_case wire vs camelCase)
Changed
- —Realtime events and populated/related records now use the same flat shape
- —Offline cache is forward-compatible — existing entries stay readable and refresh to the new shape on the next online read; pending offline writes are preserved
v1.11.0
MinorMay 2026New
- —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, optional nonce check
- —SignInWithGoogleParams interface in types.ts
- —Three typed errors: GoogleSignInNotConfiguredError, InvalidGoogleTokenError, GoogleEmailRequiredError (reuses OAuthEmailConflictError and UserDisabledError)
- —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 OAuthEmailConflictError
- —Configure the provider per environment with client IDs from Google Cloud Console (iOS, Android, web)
v1.10.1
PatchMay 2026Improved
- —README rewritten to reflect the v1.10.0 surface; removed a fictional signInWithGoogle reference; replaced the deprecated KoolbaseAppleAuth.signIn(callback) example with signInWithApple; added an onAuthStateChange example; bumped the install snippet from ^1.8.0 to ^1.10.0
v1.10.0
MinorMay 2026New
- —Sign in with Apple — production-ready end-user OAuth via koolbase.auth.signInWithApple({identityToken, nonce?, fullName?}), routing to /v1/sdk/auth/oauth/apple
- —RS256-only JWKS verification, audience bound to your project's iOS Bundle ID, 15-minute replay defense, optional nonce check
- —AppleFullName and SignInWithAppleParams interfaces in types.ts
- —Four typed errors: AppleSignInNotConfiguredError, InvalidAppleTokenError, AppleEmailRequiredError, OAuthEmailConflictError
- —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 OAuthEmailConflictError
- —Configure the provider (Bundle ID) per environment before use
Deprecated
- —KoolbaseAppleAuth.signIn and oauthLogin remain deprecated and throw KoolbaseAuthError('not_implemented'); the v1.10.0 surface is koolbase.auth.signInWithApple(...) on the auth instance
v1.9.0
MinorMay 2026Fixed
- —Critical: v1.8.0 and earlier shipped silent breakages on the SDK auth surface — anyone on an earlier version should upgrade immediately
- —x-api-key header is now sent on every auth request — previously only Content-Type was sent, so the server resolved every project-scoped call as anonymous and every /v1/sdk/auth/* endpoint returned 401
- —Password reset endpoints were targeting wrong paths — forgotPassword and resetPassword silently 404'd; now corrected to /password-reset and /password-reset/confirm
- —Session responses are now mapped — the server returns snake_case (access_token / refresh_token / expires_at); the SDK cast directly to camelCase, so session.accessToken was undefined and the Authorization header sent Bearer undefined. Now mapped on every session-returning endpoint
- —register() was discarding the session the server returned (kept only the user); now persists the full session
New
- —Persistent sessions — new SecureAuthStorage default backed by react-native-keychain (iOS Keychain + Android Keystore). The peer dependency is optional — apps without it get a clear warning and run without persistence rather than crashing. Custom backends via the KoolbaseAuthStorage interface + KoolbaseConfig.authStorage
- —Offline-aware session restoration — restoreSession() returns a RestoreResult enum (NoSession / Restored / Expired / Offline); optimistic state is populated from disk before any network call, so authenticated UI renders immediately at launch
- —Auth state listener — KoolbaseAuth.onAuthStateChange(listener) (Firebase/Supabase style); fires immediately with current state, then on every change; returns an unsubscribe function
- —Single-flight token refresh — concurrent callers hitting a stale token share one underlying refresh
- —Typed error hierarchy — 10 new errors: InvalidCredentialsError, EmailAlreadyInUseError, UserDisabledError, WeakPasswordError, SessionExpiredError, TokenRevokedError, AccountLockedError (with lockedUntil), UnlockTokenInvalidError, RateLimitError, NetworkError (all extend KoolbaseAuthError)
- —Account unlock — KoolbaseAuth.unlock(token) consumes the one-shot token from a brute-force unlock email
- —Device metadata — seven identifying headers per auth request (stable per-install UUID device label, SDK version, platform info, app version)
- —Configurable timeout — KoolbaseConfig.authTimeout (default 10000ms) via AbortController
- —Injectable fetch — KoolbaseConfig.fetch accepts an alternate fetch implementation (testing, proxies, instrumented HTTP)
- —koolbaseSdkVersion constant exported for runtime version introspection
Changed
- —logout() returns Promise<boolean> — true if the server-side call succeeded, false otherwise; the local session is always cleared regardless
- —setSession() is now async (Promise<void>) so storage persistence completes before the call resolves; source-compatible
- —register() validates password length client-side (must be >= 8 characters) before hitting the network; throws WeakPasswordError
Deprecated
- —KoolbaseAuth.oauthLogin() and KoolbaseAppleAuth.signIn() now throw KoolbaseAuthError('not_implemented') — earlier implementations routed through /v1/auth/oauth (the dashboard developer OAuth endpoint) and never created end-user sessions, so Apple Sign-In never actually worked for SDK consumers. Proper OAuth ships in v1.10.0 at /v1/sdk/auth/oauth/{provider}
Migration
- —Most apps work without code changes. To opt into persistence, install react-native-keychain (>= 8.0.0, optional peer dependency) and call restoreSession() at app launch
- —Apple Sign-In users: temporarily switch to email/password until v1.10.0 — the deprecated method now throws explicitly rather than silently failing
v1.8.0
MinorMay 2026New
- —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 — an object with user_id (string or null) and is_authenticated (boolean)
- —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
v1.7.0
MinorMay 2026New
- —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 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, PhoneVerifyResult, SendOtpParams, VerifyOtpParams, LinkPhoneParams; KoolbaseUser now exposes phoneNumber and phoneVerified
- —New errors: InvalidPhoneNumberError, OtpExpiredError, OtpInvalidError, OtpMaxAttemptsError, OtpRateLimitError, PhoneAlreadyLinkedError, SmsConfigMissingError (all extend KoolbaseAuthError)
- —Numbers must be E.164; configure an SMS provider (Twilio, Africa's Talking, or Hubtel) in the dashboard first
v1.6.1
PatchApril 2026Improved
- —README — Logic Engine v2 operators
v1.6.0
MinorApril 2026New
- —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
v1.5.0
MinorApril 2026New
- —Sign in with Apple — KoolbaseAppleAuth.signIn() and KoolbaseAuth.oauthLogin()
- —Apple identity token verified server-side using Apple's JWKS endpoint; works with any Apple credential provider
Deprecated
- —Superseded — this Apple implementation never created project-scoped end-user sessions and was deprecated in v1.9.0. Use Koolbase.auth.signInWithApple(...) from v1.10.0
v1.4.0
MinorApril 2026New
- —Koolbase Cloud Messaging — KoolbaseMessaging, push notification delivery via FCM
- —Koolbase.messaging.registerToken({token, platform, userId?}) — register an FCM device token
- —Koolbase.messaging.send({to, title, body, data?}) — send a push notification to a specific device
- —KoolbaseConfig.messagingEnabled (default true); device ID automatically reused from the analytics stable device ID (AsyncStorage)
- —Setup: add your FCM server key as a project secret named FCM_SERVER_KEY in the dashboard
v1.3.1
PatchApril 2026Improved
- —README — added Code Push, Analytics, and Logic Engine sections, clearer getting-started guide
v1.3.0
MinorApril 2026New
- —Analytics — KoolbaseAnalytics with batched flush; Koolbase.analytics top-level accessor
- —track(eventName, properties), screenView(screenName, properties), identify(userId), setUserProperty(key, value), setUserProperties(map), reset(), flush(), dispose()
- —Auto events: app_open, screen_view, session_end. Batch flush every 30s, on background, on close, or at 20 events
- —Anonymous by default (stable device_id via AsyncStorage), attach user_id on identify(); KoolbaseConfig gains analyticsEnabled and appVersion
- —Logic Engine v1 — Koolbase.executeFlow(flowId, context); KoolbaseLogicEngine deterministic evaluator; node types if / sequence / event / set; operators eq, neq, gt, lt, and, or, exists; data sources context / config / flags
- —BundlePayload extended with flows and screens fields; the flow evaluator never throws — returns a safe FlowResult on any error
v1.1.0
MinorMarch 2026New
- —Offline-first database powered by AsyncStorage — cache-first reads (instant UI, background refresh), optimistic writes (local then sync)
- —Auto-sync on network reconnect via NetInfo; manual Koolbase.db.syncPendingWrites(); QueryResult.isFromCache flag
- —Write queue with max 3 retries before dropping; user-scoped cache — no cross-user data leakage; PendingWrite type exported
v1.0.0
MajorMarch 2026New
- —Initial release of the Koolbase React Native SDK
- —Auth — register, login, logout, current user
- —Database — insert, query, get, update, delete, populate
- —Storage — upload, download, delete; Realtime — WebSocket subscriptions
- —Functions — invoke deployed functions; feature flags and remote config; version enforcement