@ossy/sdk-react
v1.40.3
Published
Software Development Kit React
Readme
React bindings
Thin React layer over @ossy/sdk. One hook — useSdk() — for reads, writes, cache invalidation, and optimistic updates.
Getting started
npm install @ossy/sdk-react @ossy/sdk @ossy/foldimport { WorkspaceProvider, useSdk, AsyncStatus } from '@ossy/sdk-react'
import { ReactSdk } from '@ossy/sdk-react'
import { ListResources, CreateResource } from '@ossy/resources'
const sdk = ReactSdk.of({
workspaceId: 'your-workspace-id',
apiUrl: 'https://api.ossy.se/api/v0',
})
export const App = () => (
<WorkspaceProvider sdk={sdk}>
<MyComponent />
</WorkspaceProvider>
)
function MyComponent() {
const sdk = useSdk()
const location = '/@ossy/domains/'
const { status, data: resources, error, refetch } = sdk.read(ListResources, { location })
const handleCreate = async () => {
await sdk.invoke(CreateResource, { type: 'document', location, name: 'Hello', content: {} })
sdk.invalidate(sdk.cacheKey(ListResources, { location }))
}
if (status === AsyncStatus.Loading) return <>Loading…</>
return (
<>
<button onClick={handleCreate}>Create</button>
{resources?.map((r) => <div key={r.id}>{r.name}</div>)}
</>
)
}WorkspaceProvider subscribes to SSE push invalidation automatically (ADR 0008 §9) and drops stale read-cache keys when the server emits invalidate.
API
| Export | Purpose |
|---|---|
| WorkspaceProvider | Provides SDK + shared read cache + SSE invalidation |
| useSdk() | Returns { invoke, invokeOptimistic, read, invalidate, cacheKey, sdk } |
| useRead() | Same as sdk.read — hook for reactive reads |
| usePushInvalidation() | Wire SSE push to a read-cache store (used by WorkspaceProvider) |
| cacheKey(action, payload) | Stable cache key for an action + payload |
| projectionKey(projectionId, scopeId) | Cache key for projection reads |
| applyOptimisticResource() | Fold a pending event into cached resource state |
| rollbackOptimisticResource() | Restore cache entry after failed optimistic invoke |
| useReadCacheStore() | Access the shared read-cache store inside <Cache> |
| ReactSdk | Browser SDK with invoke and subscribePush |
| ActionRef | Platform action POJO type { id, access? } |
| resolveActionId() | Normalize dot ids to slash (@ossy.booking.actions.create → @ossy/booking/actions/create) |
| AsyncStatus | Loading state constants for UI |
| normalizeLocation() | Normalize resource location paths |
| stableSerialize() | Deterministic JSON for cache keys |
useSdk() shape
const sdk = useSdk()
// Reactive read — re-renders when cache updates
const { status, data, error, refetch } = sdk.read(ListResources, { location })
// Command
await sdk.invoke(CreateResource, payload)
// Optimistic command — folds pending event into cached resource, rolls back on error
await sdk.invokeOptimistic(UpdateResource, payload, {
type: '@ossy/booking/schema/booking',
resourceId: 'booking-1',
event: 'Updated',
payload: { status: 'confirmed' },
})
// Invalidate cache (triggers re-fetch on next read)
sdk.invalidate('location:/@ossy/domains/')
sdk.invalidate(sdk.cacheKey(ListResources, { location }))read is a React hook — call it unconditionally at the top of your component (same rules as useState).
Push invalidation
WorkspaceProvider calls usePushInvalidation internally. When the server sends SSE frames with invalidate: string[], matching read-cache keys are cleared and active useRead hooks refetch.
For custom setups, subscribe manually:
import { usePushInvalidation, useReadCacheStore } from '@ossy/sdk-react'
function MyBridge({ sdk }) {
const store = useReadCacheStore()
usePushInvalidation(sdk, store)
return null
}Requires workspaceId on the SDK config (or same-origin workspace cookie). See @ossy/sdk README.
Cache key conventions
| Action + payload | Cache key |
|---|---|
| @ossy/resources/actions/list + { location } | location:${normalizeLocation(location)} |
| @ossy/resources/actions/search + query | search:${stableSerialize(query)} |
| @ossy/resources/actions/get + { resourceId \| id } | resource:${resourceId} |
| @ossy/booking/actions/list | action:@ossy/booking/actions/list |
| @ossy/platform/actions/list-task-runs + { workspaceId } | projection:@ossy/platform/data/task-run-list:${workspaceId} |
| default | action:${actionId} or action:${actionId}:${stableSerialize(payload)} |
Import action POJOs from feature packages (@ossy/resources, @ossy/workspaces, …). Action ids use canonical @ossy/{package}/actions/... notation.
Dependencies
Peer dependencies: @ossy/sdk, @ossy/fold, react, react-dom.
No Ramda.
