@relyio/sdk 0.7.0 → 0.9.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,66 @@
1
1
  # @relyio/sdk
2
2
 
3
+ ## 0.9.0
4
+
5
+ ### Minor Changes
6
+
7
+ - 187624c: **`<RelyIOProvider live>` no longer misses a publish that lands while the page is loading.** The provider used to send its reads first and open the feed afterwards, from the feed's position at that moment — so anything published in between was in neither, and the page showed the old content until that entry changed again. With `live` on, the reads beneath the provider now wait until the feed has opened and sent its position — the first thing it sends, so there is no extra request and the edge counts your visitor once — and then go; anything published after that position is announced and refetched like any other change. The same holds after a connection that failed before it had a position: everything is refetched once the new one has sent its position. If the feed fails before it has one — it is down, or refuses the key — the reads are sent without it, so content never waits on a feed that is down. A server render is unchanged: its reads never wait. Nothing to change in your code, but `@relyio/react` now needs `@relyio/sdk` 0.9.0 or later, the release that carries this.
8
+
9
+ **A socket that neither opens nor fails is given up after fifteen seconds.** `liveChanges()` used to wait on such a socket — a network that holds the upgrade without refusing it — for as long as the page was open. It now closes a socket that has not opened and been told its position within `LIVE_SOCKET_OPEN_TIMEOUT_MS` (the poll interval, fifteen seconds; newly exported) and polls the edge from the same cursor instead, as it does for a socket that fails to open. That also bounds how long the provider's first reads wait.
10
+
11
+ **Read after the feed's position, with no request of your own for it.** Opened without `since`, `liveChanges()` and `changes()` hand the position they start from to `reached` before anything after it — the edge's `feed.opened` or first poll's answer; `/v1/changes`' first answer. `ChangesOptions` gains `reached` for this; `changes()` calls it once, with that head, and never with a later page's cursor — so `followChanges()` over `changes()` now reopens from that head after a failure before the first change, rather than refetching everything. Read what you show after that call, and a publish your read did not see is announced. With `followChanges()`, which hands the source its own `reached`, call both:
12
+
13
+ ```ts
14
+ let positioned!: () => void
15
+ const position = new Promise<void>(resolve => (positioned = resolve))
16
+ const following = followChanges(
17
+ from =>
18
+ relyio.liveChanges({
19
+ ...from,
20
+ ids: [id],
21
+ reached: cursor => {
22
+ from.reached(cursor)
23
+ positioned()
24
+ },
25
+ }),
26
+ { signal, onChange, onResync, onState: state => state.kind !== 'following' && positioned() },
27
+ )
28
+ await position
29
+ const post = await relyio.content('post').findById(id).execute()
30
+ ```
31
+
32
+ `changesHead()` still takes the position with a request of its own, for a feed you open later.
33
+
34
+ ## 0.8.0
35
+
36
+ ### Minor Changes
37
+
38
+ - 5207d3f: **`liveChanges()` follows a dataset's published changes at RelyIO's live edge, with a publishable key.** It opens one WebSocket to the edge and yields each change as ids — `{ entryId, contentType, locale, kind, cursor, fresh }` — the same `FeedChange` `changes()` now yields. Where a socket cannot open (a network that blocks sockets, or Node 18 and 20, which have no global `WebSocket`) it asks the edge for changes about every fifteen seconds instead, and tries a socket again about once a minute. Hand it to `followChanges()`, which reconnects from the last change after a dropped connection — the edge drops every connection when it deploys — with a random, growing wait:
39
+
40
+ ```ts
41
+ import { createClient, followChanges } from '@relyio/sdk'
42
+
43
+ const relyio = createClient({ apiKey: 'rly_pub_…' })
44
+ const controller = new AbortController()
45
+ await followChanges(from => relyio.liveChanges({ ...from, types: ['post'] }), {
46
+ signal: controller.signal,
47
+ onChange: change =>
48
+ relyio.content(change.contentType).findById(change.entryId).fresh(change.fresh).execute(),
49
+ onResync: resync =>
50
+ refetchEverything(resync.cause === 'expired' ? resync.expired.fresh : undefined),
51
+ })
52
+ ```
53
+
54
+ **Refetch with `fresh`.** Every change `changes()` and `liveChanges()` deliver carries `fresh`, the feed's position at or after the change: the change's own cursor on `changes()`, the cursor of its batch at the edge. `findById()`, `query()` and `search()` take it as `.fresh(position)`, and a singleton's `fetch()` as `{ fresh }`; the read is sent with `?_fresh=…`, which the Content API accepts on every read its edge cache may keep and which changes nothing about the answer. Your reads go through that edge and through the browser's own cache, and a change can reach you a moment before its old answer has left them; a request naming a position nobody asked for before the change is one no cache holds an answer to, so it is answered after the change, and every visitor refetching for the same change shares that answer. Use `fresh`, not `cursor` — at the edge an earlier change of a batch carries the position before its batch as `cursor`. An expired cursor's `ChangesExpiredError` carries `fresh` too: the position the feed started again from, for refetching everything. The iterators' element type is now `FeedChange`, the document's `Change` plus `fresh`; `followChanges()` and `ChangeSource` take the change type as a parameter, so a source of plain `Change`s still follows as before.
55
+
56
+ `onExpired` and `signal` mean what they mean for `changes()`. Only a publishable key is sent to the edge: with any other key, `liveChanges()` sends nothing and ends with a `LiveEdgeError`. On a server, keep following `changes()` with a secret or preview key.
57
+
58
+ **Every other end is a `LiveEdgeError` that says what to do next**, and `followChanges()`'s default classifier reads it: `stop` for a refusal the same request would meet again (an unknown or revoked key, an origin the key does not list, a dataset it does not reach), `retry` for a dropped connection, and `back-off` when the project, or the visitor's address, already holds as many live connections as it may — a wait of at least a minute, longer each time it repeats, up to fifteen, without polling meanwhile. `FollowVerdict` gains `backOffMs` for that schedule, and `FOLLOW_BACK_OFF_MAX_MS` is its cap.
59
+
60
+ **Two new options.** `liveUrl` is the live edge; with the default `baseUrl` it is `https://live.relyio.com` and need not be set, and for any other `baseUrl` it must be. `webSocket` is the `WebSocket` class to open the socket with; omitted, the runtime's global one.
61
+
62
+ `ChangesExpiredError.reason` can now also be `ahead-of-head`, `foreign-cursor` or `too-far-behind`, the edge's reasons to refetch everything. A `followChanges()` source may now call `reached(cursor)` with a position it holds without a change, so a connection dropped before its first change reopens from there rather than re-reading everything.
63
+
3
64
  ## 0.7.0
4
65
 
5
66
  ### Minor Changes