Reference: Core API¶
Complete API reference for Verity's core data layer
The Verity core is the backend of your frontend—a composable data layer that sits between your server's truth-state and your UI's view-state. It handles fetching, caching, staleness, coalescing, and directive-driven invalidation.
Installation
<!-- Load core library -->
<script src="https://cdn.jsdelivr.net/npm/verity-dl@latest/verity/shared/static/lib/core.min.js"></script>
<!-- Load adapter (Alpine, React, Vue, or Svelte) -->
<script src="https://cdn.jsdelivr.net/npm/verity-dl@latest/verity/shared/static/adapters/alpine.min.js"></script>
<script>
// Initialize with options
DL.init({
sse: { url: '/api/events' }
})
</script>
Philosophy Recap¶
Before diving into the API, remember Verity's core separation:
- Truth-state (server-owned): Domain data, business logic, authoritative state
- View-state (client-owned): UI concerns like open menus, focus, local filters
- Verity (the mediator): Fetches truth-state, tracks staleness, applies directives
Verity never lets you lie to the user. No optimistic updates, no temporary states. The UI reflects what the server confirms.
See Philosophy for the full picture.
init(options)¶
Initializes Verity's core data layer with configuration options. This is called once at application startup.
Signature¶
Options¶
SSE (Server-Sent Events)¶
{
sse?: {
enabled?: boolean // Enable SSE connection (default: true)
url?: string // SSE endpoint (default: "/api/events")
audience?: string // Audience subscription (default: "global")
clientIdParam?: string // Query param for client ID (default: "client_id")
audienceParam?: string // Query param for audience (default: "audience")
withCredentials?: boolean // Include cookies (default: false)
connectOnInit?: boolean // Auto-connect on init (default: true)
initialRetryMs?: number // Initial reconnect delay (default: 2000)
maxRetryMs?: number // Max reconnect delay (default: 30000)
backoffMultiplier?: number // Exponential backoff multiplier (default: 2)
resyncOnGap?: boolean // Resync on sequence gap (default: true)
resyncJitterMinMs?: number // Min resync jitter (default: 1500)
resyncJitterMaxMs?: number // Max resync jitter (default: 3500)
}
}
Audience-based routing:
// Global events (all clients)
DL.init({
sse: { audience: 'global' }
})
// User-specific events
DL.init({
sse: { audience: `user-${currentUserId}` }
})
// Team-specific events
DL.init({
sse: { audience: `team-${teamId}` }
})
Reconnection behavior: - SSE disconnects trigger exponential backoff reconnection - On reconnect, Verity checks for missed sequence numbers - If gap detected, triggers resync (force-refresh active collections)
Disable SSE:
Memory Cache¶
{
memory?: {
enabled?: boolean // Enable in-memory caching (default: true)
maxItemsPerType?: number // Per-type cache size limit (default: 512)
itemEntryTtlMs?: number // Item cache TTL (default: 15 minutes)
maxCollectionRefsPerCollection?: number // Per-collection cache size (default: 12)
collectionEntryTtlMs?: number // Collection cache TTL (default: 10 minutes)
pruneIntervalMs?: number // Cache pruning interval (default: 60000)
}
}
Example:
Bulk/Coalescing¶
Why this matters:
When multiple directives arrive rapidly (e.g., SSE burst), Verity coalesces them into a single batch refetch. delayMs controls the window.
Example:
Complete Example¶
DL.init({
sse: {
enabled: true,
url: '/api/events',
audience: 'global',
withCredentials: true,
initialRetryMs: 2000,
maxRetryMs: 30000,
backoffMultiplier: 2
},
memory: {
enabled: true,
maxItemsPerType: 1000,
itemEntryTtlMs: 900000
},
bulk: {
delayMs: 100
}
})
createType(name, config)¶
Registers an item type with fetch logic, optional levels, and staleness configuration.
Signature¶
function createType(name: string, config: TypeConfig): void
interface TypeConfig {
fetch: (id: string | number) => Promise<any> // Fetch single item by ID
bulkFetch?: (ids: (string | number)[], level?: string) => Promise<any[]> // Optional bulk fetch
stalenessMs?: number // Per-type staleness (default: 15000)
levelConversionMap?: { [level: string]: string[] } // Level conversion rules
levels?: {
[levelName: string]: {
fetch: (id: string | number) => Promise<any>
checkIfExists?: (data: any) => boolean
stalenessMs?: number
bulkFetch?: (ids: (string | number)[], level?: string) => Promise<any[]>
levelConversionMap?: { [level: string]: string[] }
}
}
}
Parameters¶
fetch (required)¶
Async function that fetches a single item by ID. Must return the data or throw an error.
DL.createType('user', {
fetch: async (id) => {
const res = await fetch(`/api/users/${id}`)
if (!res.ok) throw new Error('Failed to fetch user')
return res.json()
}
})
bulkFetch (optional)¶
Async function that fetches multiple items at once. Verity uses this for efficient batching.
DL.createType('invoice', {
fetch: async (id) => {
const res = await fetch(`/api/invoices/${id}`)
return res.json()
},
bulkFetch: async (ids, level = 'default') => {
const res = await fetch('/api/invoices/bulk', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ ids, level })
})
return res.json() // Returns array of items
}
})
stalenessMs (optional)¶
Per-type staleness in milliseconds. Default is 15000 (15 seconds).
// Dashboard should refetch every 5 seconds
DL.createType('dashboard', {
fetch: async () => { /* ... */ },
stalenessMs: 5000
})
// User profile can be cached for 5 minutes
DL.createType('current_user', {
fetch: async () => { /* ... */ },
stalenessMs: 300000
})
levels (optional)¶
Define multiple data levels for progressive loading (e.g., "simplified", "expanded", "full").
DL.createType('invoice', {
fetch: async (id) => {
// Default level fetch
const res = await fetch(`/api/invoices/${id}`)
return res.json()
},
stalenessMs: 60000,
levels: {
simplified: {
fetch: async (id) => {
const res = await fetch(`/api/invoices/${id}?level=simplified`)
return res.json()
},
checkIfExists: (data) => data && data.title != null,
levelConversionMap: {
expanded: ['simplified'], // Expanded contains simplified data
default: ['simplified']
}
},
expanded: {
fetch: async (id) => {
const res = await fetch(`/api/invoices/${id}?level=expanded`)
return res.json()
},
checkIfExists: (data) => data && data.description != null
}
}
})
levelConversionMap (optional)¶
Defines which levels can be derived from other levels. Prevents redundant fetches.
DL.createType('article', {
fetch: async (id) => { /* ... */ },
levelConversionMap: {
full: ['summary', 'default'] // Full level includes summary and default data
},
levels: {
summary: { /* ... */ },
full: { /* ... */ }
}
})
Example¶
DL.createType('invoice', {
fetch: async (id) => {
const res = await fetch(`/api/invoices/${id}`)
return res.json()
},
bulkFetch: async (ids) => {
const res = await fetch('/api/invoices/bulk', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ ids })
})
return res.json()
},
stalenessMs: 60000,
levels: {
simplified: {
fetch: async (id) => {
const res = await fetch(`/api/invoices/${id}?level=simplified`)
return res.json()
}
}
}
})
createCollection(name, config)¶
Registers a parameterized collection (e.g., "invoices", "users", "orders").
Signature¶
function createCollection(name: string, config: CollectionConfig): void
interface CollectionConfig {
fetch: (params?: any) => Promise<{ ids: any[], count?: number }> // Fetch function
stalenessMs?: number // Per-collection staleness (default: 15000)
}
Parameters¶
fetch (required)¶
Async function that fetches the collection. Must return an object with ids (array of IDs) and optionally count.
DL.createCollection('invoices', {
fetch: async (params = {}) => {
const url = new URL('/api/invoices', window.location.origin)
if (params.status && params.status !== 'all') {
url.searchParams.set('status', params.status)
}
if (params.q) {
url.searchParams.set('q', params.q)
}
const res = await fetch(url)
const data = await res.json()
return {
ids: data.ids, // Array of invoice IDs
count: data.count // Total count
}
}
})
Important: Collections return IDs only, not full items. Use fetchItem() or framework adapters to fetch the actual items.
stalenessMs (optional)¶
Per-collection staleness in milliseconds. Default is 15000 (15 seconds).
// Real-time feed: refetch every 3 seconds
DL.createCollection('activity_feed', {
fetch: async (params) => { /* ... */ },
stalenessMs: 3000
})
// Archived data: cache for 10 minutes
DL.createCollection('archived_orders', {
fetch: async (params) => { /* ... */ },
stalenessMs: 600000
})
Example¶
DL.createCollection('invoices', {
fetch: async (params = {}) => {
const url = new URL('/api/invoices', window.location.origin)
if (params.status) url.searchParams.set('status', params.status)
if (params.sort) url.searchParams.set('sort', params.sort)
if (params.direction) url.searchParams.set('direction', params.direction)
const res = await fetch(url)
if (!res.ok) throw new Error('Failed to fetch invoices')
return res.json() // { ids: [1, 2, 3], count: 42 }
},
stalenessMs: 60000 // 1 minute
})
fetchItem(typeName, id, level, options)¶
Fetches a single item and returns a reactive reference with data and metadata.
Signature¶
function fetchItem(
typeName: string,
id: string | number,
level?: string | null,
options?: FetchItemOptions
): ItemReference
interface FetchItemOptions {
force?: boolean // Force refetch even if cached (default: false)
silent?: boolean // Silent fetch (no loading state) (default: false)
}
interface ItemReference {
data: any | null
meta: {
isLoading: boolean
lastFetched: string | null // ISO timestamp
error: Error | null
activeQueryId: string | null
lastUsedAt: string | null
}
}
Parameters¶
typeName: Type name (registered viacreateType)id: Item IDlevel(optional): Data level (e.g., "simplified", "expanded")options(optional):force: Force refetch even if freshsilent: Fetch without showing loading state
Returns: ItemReference¶
interface ItemReference {
data: any | null // The fetched item data
meta: {
isLoading: boolean // true while fetching
lastFetched: string | null // ISO timestamp of last fetch
error: Error | null // Last error, if any
activeQueryId: string // Unique query ID
lastUsedAt: string // ISO timestamp of last access
}
}
Example¶
// Register type first
DL.createType('user', {
fetch: async (id) => {
const res = await fetch(`/api/users/${id}`)
return res.json()
}
})
// Fetch item
const userRef = DL.fetchItem('user', 123)
// Access data
if (userRef.meta.isLoading) {
console.log('Loading...')
} else if (userRef.meta.error) {
console.error('Error:', userRef.meta.error)
} else {
console.log('User:', userRef.data)
}
// Force refetch
const freshUserRef = DL.fetchItem('user', 123, null, { force: true })
// Silent background fetch
const bgUserRef = DL.fetchItem('user', 123, null, { silent: true })
Reactivity¶
The returned reference is reactive. In framework adapters, it automatically triggers re-renders:
// React
const userRef = useItem('user', 123)
return <div>{userRef.data?.name}</div>
// Alpine
Alpine.store('lib').it('user', 123)
// Vue
const userRef = dl.it('user', 123)
fetchCollection(name, options)¶
Fetches a collection and returns a reactive reference with IDs and metadata.
Signature¶
function fetchCollection(
name: string,
options?: FetchCollectionOptions
): CollectionReference
interface FetchCollectionOptions {
params?: any // Query parameters
force?: boolean // Force refetch even if cached (default: false)
}
interface CollectionReference {
data: {
ids: any[] // Array of item IDs
count: number // Total count
}
meta: {
isLoading: boolean
lastFetched: string | null // ISO timestamp
error: Error | null
activeQueryId: string | null
paramsSnapshot: any // Params used for this fetch
paramsKey: string // Cache key for params
lastUsedAt: string | null
}
}
Parameters¶
name: Collection name (registered viacreateCollection)options(optional):params: Query parameters (filters, sorting, etc.)force: Force refetch even if fresh
Returns: CollectionReference¶
interface CollectionReference {
data: {
ids: any[] // Array of item IDs (not full items!)
count: number // Total count
}
meta: {
isLoading: boolean // true while fetching
lastFetched: string | null // ISO timestamp of last fetch
error: Error | null // Last error, if any
activeQueryId: string // Unique query ID
paramsSnapshot: any // Params used for this fetch
paramsKey: string // Cache key for params
lastUsedAt: string // ISO timestamp of last access
}
}
Example¶
// Register collection first
DL.createCollection('invoices', {
fetch: async (params = {}) => {
const url = new URL('/api/invoices', window.location.origin)
if (params.status) url.searchParams.set('status', params.status)
const res = await fetch(url)
return res.json() // { ids: [1, 2, 3], count: 42 }
}
})
// Fetch collection
const colRef = DL.fetchCollection('invoices', {
params: { status: 'active' }
})
// Access data
console.log(colRef.data.ids) // [1, 2, 3, ...]
console.log(colRef.data.count) // 42
// Force refetch
const freshColRef = DL.fetchCollection('invoices', {
params: { status: 'active' },
force: true
})
// Different params = different cache entry
const completedRef = DL.fetchCollection('invoices', {
params: { status: 'completed' }
})
Parameter Caching¶
Each unique set of parameters creates a separate cache entry:
const activeRef = DL.fetchCollection('invoices', { params: { status: 'active' } })
const pausedRef = DL.fetchCollection('invoices', { params: { status: 'paused' } })
// These are two separate cache entries
// activeRef.data.ids !== pausedRef.data.ids
applyDirectives(directives)¶
Applies server-sent directives to invalidate and refetch cached data.
Signature¶
Parameters¶
interface Directive {
op: "refresh" | "invalidate"
kind?: "collection" | "item"
name?: string // Collection or type name
id?: string | number // Item ID (for items)
level?: string // Item level (for items)
key?: string // Directive deduplication key
params?: any // Collection params
idempotency_key?: string
source?: string // Client ID that triggered this (to prevent echo)
seq?: number // Sequence number
audience?: string // Target audience
}
Directive Types¶
Refresh Collection¶
{
op: "refresh",
kind: "collection",
name: "invoices",
params: { status: "active" } // Optional: specific params
}
Refresh Item¶
{
op: "refresh",
kind: "item",
name: "invoice",
id: 42,
level: "expanded" // Optional: specific level
}
Invalidate (Legacy)¶
Behavior¶
- Validates directives
- Identifies affected cache entries
- Marks them as stale
- Triggers refetch (coalesced with other pending invalidations)
- Deduplicates via
idempotency_keyorkey - Ignores directives with
sourcematching current client ID
Example¶
// After a mutation, server returns directives
const response = await fetch('/api/invoices/42', {
method: 'PUT',
headers: {
'Content-Type': 'application/json',
'X-Verity-Directive-Request': 'send me directives'
},
body: JSON.stringify({ status: 'paid' })
})
// Server responds with directives in header or body
const directives = JSON.parse(response.headers.get('X-Verity-Directives'))
// Apply directives
DL.applyDirectives(directives)
Common pattern with helper:
const directiveHeaders = (extra = {}) => ({
'X-Verity-Directive-Request': 'send me directives',
...extra
})
async function updateInvoice(id, data) {
const res = await fetch(`/api/invoices/${id}`, {
method: 'PUT',
headers: directiveHeaders({ 'Content-Type': 'application/json' }),
body: JSON.stringify(data)
})
const payload = await res.json()
// Directives are returned in payload or headers
if (payload.directives) {
DL.applyDirectives(payload.directives)
}
return payload
}
onChange(callback)¶
Subscribe to Verity state changes. Returns unsubscribe function.
Signature¶
Example¶
// Subscribe to all state changes
const unsubscribe = DL.onChange(() => {
console.log('State changed!')
console.log('Current state:', DL.state())
})
// Later: unsubscribe
unsubscribe()
Note: This is a low-level API. Framework adapters handle reactivity automatically.
onLifecycle(eventName, callback)¶
Subscribe to lifecycle events for debugging and monitoring.
Signature¶
function onLifecycle(
eventName: string | "*",
callback: (event: { event: string, detail: any }) => void
): () => void
Events¶
| Event | Detail | When |
|---|---|---|
collection:fetch:intent |
{ name, params, force, qid } |
Before collection fetch |
collection:fetch:success |
{ name, params, qid } |
After successful collection fetch |
collection:fetch:complete |
{ name, params, qid } |
Collection fetch finished |
collection:fetch:skip |
{ name, params, reason } |
Collection fetch skipped (cache hit) |
collection:cache-hit |
{ name, params, reason } |
Collection served from cache |
item:fetch:intent |
{ type, id, level, qid } |
Before item fetch |
item:fetch:success |
{ type, id, level, qid, strategy } |
After successful item fetch |
item:fetch:complete |
{ type, id, level, qid } |
Item fetch finished |
item:fetch:skip |
{ type, id, level, reason } |
Item fetch skipped (cache hit) |
item:cache-hit |
{ type, id, level, reason } |
Item served from cache |
directive:received |
{ directives, audience, source } |
Directives received |
directive:processed |
{ directive } |
Single directive processed |
sse:open |
{ audience, url } |
SSE connection opened |
sse:error |
{ retryInMs } |
SSE connection error |
sse:resync-dispatch |
{ context } |
Resync triggered |
sse:gap |
{ audience, lastSeq, incomingSeq } |
Sequence gap detected |
Example¶
// Listen to all events
DL.onLifecycle('*', ({ event, detail }) => {
console.log(`Event: ${event}`, detail)
})
// Listen to specific event
const unsubscribe = DL.onLifecycle('item:fetch:success', ({ event, detail }) => {
console.log('Item fetched:', detail.type, detail.id)
})
// Later: unsubscribe
unsubscribe()
clientId()¶
Returns the unique identifier for this client instance.
Signature¶
Usage¶
const id = DL.clientId()
console.log(id) // "client-abc123xyz" or UUID
// Include in mutation requests to prevent directive echo
fetch('/api/invoices', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-Verity-Client-ID': DL.clientId() // ← Server echoes this in directives
},
body: JSON.stringify({ title: 'New invoice' })
})
Why?
- Server includes client ID in directive source field
- Client ignores directives with matching source
- Prevents double-application of local mutations
state()¶
Returns a snapshot of the entire Verity state (for debugging).
Signature¶
function state(): VerityState
interface VerityState {
types: { [name: string]: TypeInfo }
collections: { [name: string]: CollectionInfo }
sse: SSEInfo
bulk: BulkInfo
memory: MemoryInfo
}
Example¶
const snapshot = DL.state()
console.log('Registered types:', Object.keys(snapshot.types))
console.log('Registered collections:', Object.keys(snapshot.collections))
console.log('SSE connected:', snapshot.sse.connected)
SSE Control Methods¶
connectSse(options)¶
Manually connect to SSE endpoint.
disconnectSse()¶
Disconnect from SSE endpoint.
configureSse(config)¶
Update SSE configuration.
Complete Example¶
// Initialize Verity
DL.init({
sse: {
enabled: true,
url: '/api/events',
audience: 'global',
withCredentials: true
},
memory: {
enabled: true,
maxItemsPerType: 1000
},
bulk: {
delayMs: 100
}
})
// Register an item type
DL.createType('invoice', {
fetch: async (id) => {
const res = await fetch(`/api/invoices/${id}`)
return res.json()
},
bulkFetch: async (ids, level = 'default') => {
const res = await fetch('/api/invoices/bulk', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ ids, level })
})
return res.json()
},
stalenessMs: 60000, // 1 minute
levels: {
simplified: {
fetch: async (id) => {
const res = await fetch(`/api/invoices/${id}?level=simplified`)
return res.json()
},
checkIfExists: (data) => data && data.title != null
}
}
})
// Register a collection
DL.createCollection('invoices', {
fetch: async (params = {}) => {
const url = new URL('/api/invoices', window.location.origin)
if (params.status) url.searchParams.set('status', params.status)
if (params.q) url.searchParams.set('q', params.q)
const res = await fetch(url)
return res.json() // { ids: [1, 2, 3], count: 42 }
},
stalenessMs: 30000 // 30 seconds
})
// Fetch data
const colRef = DL.fetchCollection('invoices', {
params: { status: 'active' }
})
console.log('Invoice IDs:', colRef.data.ids)
const itemRef = DL.fetchItem('invoice', 42, 'simplified')
console.log('Invoice data:', itemRef.data)
// Subscribe to lifecycle events
DL.onLifecycle('*', ({ event, detail }) => {
console.log('Event:', event, detail)
})
// Mutation helper
const directiveHeaders = (extra = {}) => ({
'X-Verity-Directive-Request': 'send me directives',
...extra
})
async function updateInvoice(id, data) {
const res = await fetch(`/api/invoices/${id}`, {
method: 'PUT',
headers: directiveHeaders({ 'Content-Type': 'application/json' }),
body: JSON.stringify(data)
})
const payload = await res.json()
// Apply server directives
if (payload.directives) {
DL.applyDirectives(payload.directives)
}
return payload
}
// Use it
await updateInvoice(42, { status: 'paid' })
isItemLoading(typeName, id, level)¶
Checks if a specific item is currently being fetched (has an in-flight request). This is the source of truth for loading state, checking the actual in-flight request map rather than relying on the isLoading flag.
Signature¶
Parameters¶
typeName: The type name (registered viacreateType)id: The item identifierlevel(optional): The level name (null for default level)
Returns: boolean¶
Returns true if there is an active fetch in progress for this exact item/level combination, false otherwise.
Example¶
// Check if user is loading
const loading = DL.isItemLoading('user', 123)
console.log('User 123 is loading:', loading)
// Check specific level
const detailsLoading = DL.isItemLoading('user', 123, 'detailed')
console.log('User 123 details loading:', detailsLoading)
// Use in reactive getters for accurate loading state
get isLoadingAuth() {
return DL.isItemLoading('me', 'current')
}
Why Use This Instead of ref.meta.isLoading?¶
The isLoading flag can sometimes be out of sync with reality due to timing issues with reactive frameworks. isItemLoading() checks the actual in-flight request map, which is the single source of truth.
// ❌ May be inaccurate with reactive frameworks
get isLoadingAuth() {
const meRef = Alpine.store('lib').it('me', 'current')
return meRef.meta.isLoading
}
// ✅ Always accurate - checks actual in-flight requests
get isLoadingAuth() {
return DL.isItemLoading('me', 'current')
}
hasAnyInFlightRequests()¶
Checks if there are ANY in-flight requests (items or collections) across the entire application. Useful for global loading indicators.
Signature¶
Returns: boolean¶
Returns true if there are any active fetches in progress, false if all requests have completed.
Example¶
// Global loading indicator
const showGlobalSpinner = DL.hasAnyInFlightRequests()
// Alpine.js example
Alpine.store('app', {
get isLoading() {
return DL.hasAnyInFlightRequests()
}
})
// React example
function GlobalLoadingIndicator() {
const [isLoading, setIsLoading] = React.useState(false)
React.useEffect(() => {
const interval = setInterval(() => {
setIsLoading(DL.hasAnyInFlightRequests())
}, 100)
return () => clearInterval(interval)
}, [])
return isLoading ? <Spinner /> : null
}
Use in DevTools¶
Both methods are also exposed in the devtools snapshot:
const snapshot = DL.devtools()
console.log('In-flight items:', snapshot.inFlight.items)
console.log('In-flight collections:', snapshot.inFlight.collections)
console.log('Total in-flight:', snapshot.inFlight.totalCount)
console.log('Has any in-flight:', snapshot.inFlight.hasAnyInFlight)
Summary¶
Core Methods:
| Method | Purpose |
|---|---|
init() |
Initialize Verity with configuration |
createType() |
Register item type with fetch logic |
createCollection() |
Register collection type |
fetchItem() |
Fetch single item and get reactive reference |
fetchCollection() |
Fetch collection IDs and get reactive reference |
applyDirectives() |
Apply server directives to invalidate cache |
isItemLoading() |
Check if specific item has in-flight request (source of truth) |
hasAnyInFlightRequests() |
Check if any requests are in-flight globally |
onChange() |
Subscribe to state changes |
onLifecycle() |
Subscribe to lifecycle events |
clientId() |
Get unique client identifier |
state() |
Get state snapshot (debugging) |
devtools() |
Get detailed devtools snapshot |
Configuration:
| Option | Default | Purpose |
|---|---|---|
bulk.delayMs |
50 |
Directive coalescing window (ms) |
memory.maxItemsPerType |
512 |
Max cached items per type |
memory.itemEntryTtlMs |
900000 |
Item cache TTL (15 min) |
sse.url |
/api/events |
SSE endpoint |
sse.audience |
global |
Audience subscription |
sse.initialRetryMs |
2000 |
Initial retry delay |
sse.maxRetryMs |
30000 |
Max retry delay |
Key Concepts:
- Staleness: Items are fresh for
stalenessMs, then stale but usable - Coalescing: Directives batched within
bulk.delayMswindow - Deduplication: Directives deduplicated by
idempotency_keyorkey - Sequence tracking: Detects missed SSE messages, triggers resync
- Levels: Progressive data loading (e.g., simplified → expanded)
Next Steps¶
- Directives: Directive API reference
- Adapters: Framework adapters
- Concepts: Truth vs View State
- Architecture: Backend of Frontend