feat(service): answer a live read's rows inline, for polling while Realtime fails

Draft
CH
11h ago
+277-70

This is the first of two stacked PRs for "a live read polls while Realtime fails". Today, if Realtime fails, a live read stops updating. This PR is the service side: a way to ask for a live read's rows inline, so the host can poll while Realtime is down. The host change that switches to polling and back stacks on top of it.

What changes

  • Contract (contract/live.ts): both live reads accept ?poll=true.

  • Procedures (live-procedures.ts, sealedParts): a live read is answered with each statement's rows inline at LIVE_POLL_URL when any of these is true:

    • the reader asked to poll;
    • Realtime is off;
    • Realtime fails to seal a part.

    The rows are read by pollSealer (realtime.ts), which runs the statement on the service's SqlClient. Rows only travel through the existing /api/live/* procedures, behind the same signed-in caller. There is no new route and no new token.

  • Wire (live-wire.ts): sealedLiveQuerySchema gains an optional rows. It travels as JSON text, because the wire's JSON Schema has no form for unknown values. The JSON Schema goldens are regenerated, and the only change in them is rows.

  • Client: HostedTaskClient.boardLive(taskId, poll) can ask for the inline rows.

Behavior change

Before this PR, a deployment without Realtime, or one where Realtime refused to seal, answered unavailable. It now answers the rows polled. The two tests that asserted unavailable now assert polling.

Tests

  • A board asked to poll, or one where Realtime is off or refusing, is answered with its rows at the poll address. The rows decode to the board's live row.
  • The account inbox with Realtime off answers every part's rows.
  • The client sends poll=true, and decodes the rows it gets back.

Made with Luke