Remote / foldkit-remote

When it does not do what you expect

Each of these is a design of the package showing through, not a fault. The symptom names the section; the cure is one call.

Why is it still Initial?

Initial means required data is absent and nothing is fetching it, so a spinner here hides a wiring mistake. Ask the Model:

ts
Data.why(model, projection, { surfaces }) // the record you give Data.subscriptions
// { state: 'Initial', reason: 'NotFetching', surfaces: ['ProjectPage'],
//   message: 'ProjectPage reads it and is active, yet nothing is fetching it. …' }
  • NotObserved: no active Surface reads it. The Surface is not in the record you gave Data.subscriptions, or its params are undefined for this Model (the route is not there yet).
  • NotFetching: an active Surface reads it and nothing started a request, which almost always means Remote's Subscriptions are not installed in the runtime.
  • Without surfaces it can only say Unknown. For every other state it says what the state means in words.

Data.plan(model, projection) shows whether Remote believes anything is missing at all.

Why is it stale?

Under the default RemotePolicy.cacheFirst, a present field is never asked for again. That is the point of a cache, and the reason a list shows what the server said last time. To see change:

  • On demand: Data.refresh(model, projection) from update marks what the screen reads Refreshing and the active Subscription fetches it again, old value still on screen. A refresh button, a window regaining focus.
  • By age: RemotePolicy.staleWhileRevalidate({ maxAge }) refetches a value older than maxAge the next time it is planned.
  • Always: RemotePolicy.networkOnly requests every selected field, cached value visible meanwhile.
  • As it happens: Data.live instead of Data.get, with a server that publishes changes; see Live data.

Connections have no age: a list re-queries when invalidated, refreshed, or under networkOnly. See Reading, policies, and prefetch.

Why did my change disappear?

A mutation's optimistic patches are layers over the store while the request is in flight. When the server answers, the layers come off and the cache shows what the server said:

  • It failed. MutationFailed removes the layers; Data.mutation(model, requestId) reads Failed with the error. Nothing retries it: the mutation is not a durable queue, and if losing it is data loss the edit belongs to foldkit-sync.
  • It succeeded, but the server's result did not include the field. The layer came off and the store still holds the old value, until a read or the result writes the new one. A mutation Output that returns the changed entity settles this at once.
  • A preview shown with Data.overlay stays until Data.lift; a settled mutation never takes it along.

See Mutations and optimistic state.

Why is it gone after navigating away?

Remote is a cache, and it forgets facts no active feature needs. Retention roots come from the active Surfaces; after the grace period, RetentionChanged collects everything they do not reach. A screen you left, and nothing else reads, loses its rows, which is safe because coming back refetches them. To keep something a screen does not read, name it in a retain entry or a connections list; see Retention.

Why does a value the server has read Failed?

Failed with no previous and no request error means the stored value did not decode against the Selection: the server's shape and the entity's Schema disagree; the error is a DecodeError with the Schema's message. An Unavailable error means the server answered without a field the Selection names and will not answer with it; see When a field is withheld. A failed request reads the same way, with the request's error; see When a read fails. A RemoteProtocolError means the client and server disagree on REMOTE_PROTOCOL_VERSION, and nothing is silently accepted.

Why did a live update not show?

Live events carry cursors. One behind the Model's is a duplicate and is dropped; one ahead of the next is a gap, recorded in RemoteModel.gaps and not applied, so the screen keeps what the server last said rather than a history with a hole in it. The live entry then restarts the stream and refetches what it covers; see When a stream breaks.