v0.3.0
Umbrella <AbloProvider> for React apps. One provider component now owns the full lifecycle — singleton rotation on auth change, Strict-Mode-safe bootstrap, beforeunload cleanup, session-expiry IndexedDB wipe, post-bootstrap hooks, mesh client construction. Replaces the ad-hoc provider glue every consumer had to write themselves.
Declarative props absorb every class of lifecycle glue; the status hook returns a tagged union so impossible states are unrepresentable. The reference integration shrank from 515 LOC of hand-rolled singleton/AbortController/beforeunload/reaction-bridge wiring to a 60-LOC thin wrapper that just passes props through.
Added
<AbloProvider>— umbrella provider at@abloatai/ablo/react. Props include data config (schema,url,userId,organizationId), auth (capabilityToken/apiKey/ session cookie fallback), declarative behavior (preventUnsavedChanges,lostConnectionTimeout,postBootstrap), callbacks (onSessionExpired,onError,resolveUsers), and DI escape hatches.<SyncGroupProvider id="matter:...">+useSyncGroup()— per-entity scope context.<ClientSideSuspense fallback={...}>— gate renders until the engine reportsconnected. Phase-1 non-Suspense; phase-2 upgrades to real Suspense.useSyncStatus()rewritten as a tagged union:{ name: 'initial' | 'connecting' | 'connected' | 'reconnecting' | 'disconnected' | 'needs-auth', ... }. Impossible states are unrepresentable.useCurrentUserId()— returns theuserIdprop. Replaces downstream consumers’ defineProperty hacks on the store.useErrorListener(cb)— imperative error callback (Sentry/Datadog).useSync<R>()anduseSyncStore<T>()accept generic parameters so consumers can widen to their concrete schema types withoutas unknowncasts at call sites.BaseSyncedStore.purge()/SyncEngine.purge()— disconnect + wipe everyablo_*/ablo-*IndexedDB. Called automatically on session expiry.SyncEngine.onSessionError(listener)— subscribe to session-error events. Multiple subscribers supported.- Commit payload projection built into
TransactionQueue. Mutations are automatically projected onto the model’s schema-declared fields (dropping framework internals__class/__typename/clientId/syncStatusand anything not declared), withfield.json()values auto-stringified for TEXT columns andundefineddropped on updates. No config port, no consumer hook — the SDK derives correct wire payloads from the schema alone. Apps that previously maintained hand-rolled extractor tables can delete them entirely.
Breaking (continued)
- Removed
SyncEngineConfig.extractCreateInputandSyncEngineConfig.buildUpdateInput. The SDK’s built-in projection replaces them. Consumers who passed these inconfigOverridesshould delete the override; the default now covers 100% of identity-column mutations. TheconfigOverridesprop still exists but its remaining fields are all deprecated (see below) and scheduled for removal in v0.4.
Deprecated (vestigial — removal in v0.4)
SyncEngineConfig.modelCreatePriority,defaultCreatePriority,defaultNonCreatePriority— never read at runtime.SyncEngineConfig.batchableModels— never read at runtime.SyncEngineConfig.dedicatedDeleteModels— never read at runtime.SyncEngineConfig.preserveCaseModels— never read at runtime.SyncEngineConfig.essentialFields— used only in debug logging, no behavioral effect.SyncEngineConfig.classNameFallbackMap— dead path;ModelRegistry.registerModelsFromSchemaregisters by constructor identity, bypassing the class-name fallback entirely.
Breaking
- Removed
<SyncProvider>— folded into<AbloProvider>. Migrate by swapping the provider and passinguserId/organizationId/urlinstead of a pre-constructed store. - Removed
createAbloContext()factory and its returnedAbloProvider/useAblo/useParticipanttriple. Mesh is now always-on inside<AbloProvider>;useAblo()anduseParticipant(opts)are always available. Schema-typed mesh hooks are on the roadmap. - Removed
withSync(no-op alias ofobserver). Importobserverfrommobx-react-litedirectly if needed. - Removed
useSyncContextfrom the public surface (never used outside the SDK’s test helpers). useSyncStatus()return shape changed from six booleans to a tagged union. Migration:const { isReady } = useSyncStatus()→const status = useSyncStatus(); const isReady = status.name === 'connected'.SyncStoreContractgained six sync-status getters and asyncStatusfield. Third-party classes implementing the contract must add these (additive for callers).
Migration
// Before (0.2.x)
const { AbloProvider, useAblo, useParticipant } = createAbloContext<typeof schema>();
function Root() {
const sync = createSyncEngine({ url, schema, user });
const ablo = new Ablo({ schema });
return (
<SyncProvider store={sync._store} organizationId={orgId}>
<AbloProvider ablo={ablo}>
<App />
</AbloProvider>
</SyncProvider>
);
}
// After (0.3.0)
function Root() {
return (
<AbloProvider
schema={schema}
url={url}
userId={userId}
organizationId={orgId}
preventUnsavedChanges
onSessionExpired={() => router.replace('/signin')}
>
<ClientSideSuspense fallback={<Skeleton />}>
<App />
</ClientSideSuspense>
</AbloProvider>
);
}
No breaking change to useQuery / useOne / useMutate / useReader / useMutators / useUndoScope / usePresence / useIntent — call sites remain source-compatible.