Skip to content
AbloAblo Docs
Esc
navigateopen⌘Jpreview
On this page

Upgrade Guide

A safe workflow for upgrading the pre-1.0 SDK.

Ablo is pre-1.0, so a minor release may contain a breaking API change. Patch releases normally remain compatible within the same minor line. The 0.64.1 release includes the explicit React and collaboration migrations below.

React and package boundaries after 0.64

Run npx ablo upgrade for migration hints before editing. It previews changes; --write applies only mechanical rewrites. React changes requiring application judgment are reported with their replacements.

Previous API Current API
useSync() or zero-argument useAblo() useAbloClient() for actions; await client.ready() when initialization is required
useSyncStatus() useAblo(client => client.status)
usePeers() useAblo(client => client.presence.others)
Scoped peers usePresence(client => client.records, id, { excludeSelf: true })
useMutationFailureListener(listener) useMutationFailure(listener)
useErrorListener() AbloProvider’s onError prop for startup errors
useCurrentUserId() Your application’s authentication context
useSDKSyncStore() / useSyncStore() Public client operations; getAbloStore(client) from /client for custom local-store adapters
ClientSideSuspense / DefaultFallback Application UI through the provider’s fallback prop
GroupScope for presence Model and record arguments

useAblo(selector) returns detached snapshots for rendering. They have no model methods or relation accessors. useAbloClient() returns the writable client and does not subscribe to row changes. Do not perform writes inside a selector.

Presence, activities and claims

Activity is replaced by Ablo.PresenceActivity, including for agents. A session is Ablo.PresenceSession: identify the execution with presenceSessionId and its actor with participant.id and participant.kind. A participant can have several sessions, so exclude self by session rather than user ID.

presence.active contains this session’s activities; presence.others contains other sessions, each with an activities array. forModel(model, id) includes self by default; pass { excludeSelf: true } as its third argument to omit self. The same option on usePresence still announces the component’s reading activity and cleans it up on unmount.

An activity has operation, target, source, and ISO timestamp fields. Read the model and row from activity.target.model and .id; field-specific activity uses .field or .fields (never both, and always with a row ID). Activities describe visible work; they do not grant write authority.

Claims are acquired through await using claim = await client.records.claim({ id }). The protected row is claim.data; pass claim to the write. Claim lookup uses client.records.claim.state({ id }) and .queue({ id }). Claim metadata is on claim.target.meta, and a multi-field claim uses fields in the claim target. Do not reconstruct a claim handle from a presence activity. See Claims.

Imports and schema ownership

The root factory creates a headless client; /client creates the reactive client. Import the Ablo type namespace beside the factory you use. Both expose the shared schema, presence-activity and claim types. Reactive-only types such as Ablo.Status, Ablo.Reads, Ablo.Store, and Ablo.Mutator belong to /client (or /react for its re-exported reactive factory).

Prefer explicit schema inference:

import { defineMutators } from '@abloatai/ablo/client';
import { useMutators, useUndoScope } from '@abloatai/ablo/react';
import { schema } from './schema';

const definitions = defineMutators(schema, {
  records: {
    rename: async ({ tx, args }: {
      tx: import('@abloatai/ablo/client').Ablo.Mutator.Transaction<typeof schema>;
      args: { id: string; title: string };
    }) => tx.mutations.records.update({ id: args.id, title: args.title }),
  },
});

function useRecordActions() {
  const { scope } = useUndoScope(schema, 'record-editor');
  return useMutators(schema, definitions, { undoScope: scope });
}

Ambient registration remains available for a single application. Its module must be included in that TypeScript program and import @abloatai/ablo before augmenting Register. The public registration now reaches the downstream Transaction and Humans resolvers through the emitted declarations. A package compiled independently cannot inherit a consuming app’s ambient declaration.

Structural collaboration adapters

Generic subscribe(event, handler) preserves exactly the argument tuples in your event map. Receive optional server attribution through the session’s collaboration.subscribe(event, (payload, context) => ...); low-level transport adapters use subscribeCollaboration. Model-scoped client.records.events.on continues to supply authenticated context as its second callback argument. Legacy servers may omit context on application events; handle undefined there.

Upgrade safely

  1. Pin the version you run instead of depending on a floating range.
  2. Read the changelog entries between your installed and target versions.
  3. Use the documentation bundled with the target package while changing code.
  4. Run type-checks and tests before updating a production branch.
  5. Run the three-state deployment plan, then push the exact reviewed plan.
npm install --save-exact @abloatai/ablo@<version> @abloatai/cli@<version>
npx ablo docs
npx ablo docs api
npx ablo plan
npx ablo plan --json

npx ablo docs is version-matched to the installed package. Prefer it during an upgrade: the hosted website documents the newest release, which may expose a method your pinned package does not yet contain.

What to review

Pay particular attention when a release changes:

  • model method signatures or return values;
  • claim acquisition, contention, or release behavior;
  • credential scope or session minting;
  • schema serialization and push validation;
  • database connection or Data Source setup;
  • error codes your application handles explicitly.

The release changelog names required edits next to the feature that changed. Avoid branching on credential prefixes or undocumented internals; use exported types and server-confirmed identity instead.

Schema and database safety

An SDK upgrade and a database migration are separate operations coordinated by one deployment plan.

  • ablo plan compares source, the active Ablo artifact, and PostgreSQL without changing any of them. Its fingerprint pins all three observations.
  • ablo push consumes that reviewed fingerprint and refuses if any state moved.
  • ablo check is the database-compatibility view of the same plan.
  • Your ORM or migration tool remains responsible for tables, columns, constraints, and application data migrations.

When both need to change, deploy the database migration in a backwards-compatible form first, push the compatible Ablo schema, then remove old application paths. For a live rename or required-field change, keep expand, dual-write, resumable backfill, verification, switch, and contract as explicit gates. Contract is a later, separately approved deployment—not the tail of expand.

If an upgrade fails

Use the typed error code and request ID rather than matching message text. The Errors reference gives the recovery step for each public error. If a failure only occurs on the new minor version, keep the previous pinned version in production while reproducing it against an isolated branch.

See the release changelog for version-specific changes and Deployment for the production rollout sequence.

Was this page helpful?