@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.
@@ -1,6 +1,7 @@
1
- import { H as MergedRelyIOConfig, A as ApiPath, b as ApiMethod, J as PathParamsOf, v as ApiResponse, W as Wire, u as RequestConfig, B as BaseContent, g as WhereClause, Q as QueryOptions, i as SortOptions, j as ContentQueryResult, C as CreateInput, U as UpdateInput, S as SDKError, z as BaseValidationError, y as ValidationViolation, R as RelyIOConfig, a as RelyIOConfigUpdate } from './index-DbsTUw3Q.cjs';
2
- import { aK as components, aJ as paths, W as InferContentType, aa as InferAssetType, a3 as InferSingleton } from './define-content-type-DiVpbwNs.cjs';
3
- import { a as SchemaDefinition, b as SchemaContentTypeNames, j as SchemaEntryOf, ay as FilterOperator, h as SchemaAssetTypeNames, c as SchemaSingletonNames, g as SchemaSettingsGroupNames, I as InferSettingsGroup, e as SchemaQueryNames, f as SchemaLocaleCodes } from './define-schema-D7MButVt.cjs';
1
+ import { K as MergedRelyIOConfig, A as ApiPath, c as ApiMethod, N as PathParamsOf, w as ApiResponse, W as Wire, v as RequestConfig, B as BaseContent, h as WhereClause, Q as QueryOptions, j as SortOptions, k as ContentQueryResult, C as CreateInput, U as UpdateInput, S as SDKError, H as BaseValidationError, G as ValidationViolation, R as RelyIOConfig, a as RelyIOConfigUpdate } from './index-B_rIWO_l.cjs';
2
+ import { aK as components, aJ as paths, W as InferContentType, aa as InferAssetType, a3 as InferSingleton } from './define-content-type-cEGjIwT5.cjs';
3
+ import { a as SchemaDefinition, b as SchemaContentTypeNames, j as SchemaEntryOf, ay as FilterOperator, h as SchemaAssetTypeNames, c as SchemaSingletonNames, g as SchemaSettingsGroupNames, I as InferSettingsGroup, e as SchemaQueryNames, f as SchemaLocaleCodes } from './define-schema-DgbJ5Kwn.cjs';
4
+ import { VisitorResyncReason } from './live-visitor.js';
4
5
  import { DevUnreadableReason } from './dev-handshake/index.cjs';
5
6
 
6
7
  /**
@@ -301,11 +302,20 @@ declare class ContentQueryBuilder<T extends BaseContent = BaseContent, F extends
301
302
  private requestHeaders?;
302
303
  /** The client's locale, asked when the query is sent rather than copied when it was built. */
303
304
  private readonly clientLocale;
305
+ private freshValue?;
304
306
  /**
305
307
  * @param locale - The client's locale (`withLocale()`, `updateConfig({ locale })`), sent when the
306
308
  * query names none: the function `TypedRelyIOClient` hands over, or a locale fixed for it
307
309
  */
308
310
  constructor(httpClient: HttpClient, contentType: string, locale?: ClientLocale);
311
+ /**
312
+ * The feed position this read follows — a change's `fresh` (`FeedChange`), or a resync's. Sent as
313
+ * `?_fresh`, the one term the API declares for it: no cache — the Content API's edge or the
314
+ * browser's own — holds an answer for that position from before it, so the read is answered after
315
+ * the change, and every read naming the same position shares that answer. The answer is the
316
+ * read's own; omit it for a read no change prompted.
317
+ */
318
+ fresh(position: string): this;
309
319
  /**
310
320
  * Headers this query adds to the client's own, e.g. `X-RelyIO-Dataset` to list another dataset
311
321
  * the key can see. A name the client already sends is replaced for this query only; the client
@@ -516,11 +526,20 @@ declare class ContentByIdBuilder<T extends BaseContent = BaseContent> {
516
526
  private requestHeaders?;
517
527
  /** The client's locale, asked when the read is sent rather than copied when it was built. */
518
528
  private readonly clientLocale;
529
+ private freshValue?;
519
530
  /**
520
531
  * @param locale - The client's locale (`withLocale()`, `updateConfig({ locale })`), sent when the
521
532
  * read names none: the function `TypedRelyIOClient` hands over, or a locale fixed for it
522
533
  */
523
534
  constructor(httpClient: HttpClient, contentType: string, id: string, locale?: ClientLocale);
535
+ /**
536
+ * The feed position this read follows — a change's `fresh` (`FeedChange`), or a resync's. Sent as
537
+ * `?_fresh`, the one term the API declares for it: no cache — the Content API's edge or the
538
+ * browser's own — holds an answer for that position from before it, so the read is answered after
539
+ * the change, and every read naming the same position shares that answer. The answer is the
540
+ * read's own; omit it for a read no change prompted.
541
+ */
542
+ fresh(position: string): this;
524
543
  /**
525
544
  * Include related content in the response
526
545
  */
@@ -1352,6 +1371,12 @@ interface SingletonFetchOptions {
1352
1371
  locale?: string;
1353
1372
  /** Reference fields to populate */
1354
1373
  include?: string[];
1374
+ /**
1375
+ * The feed position this read follows — a change's `fresh` (`FeedChange`), or a resync's. Sent as
1376
+ * `?_fresh`: no cache holds an answer for it from before that position, so the read is answered
1377
+ * after the change. The answer is the read's own; omit it for a read no change prompted.
1378
+ */
1379
+ fresh?: string;
1355
1380
  }
1356
1381
  /**
1357
1382
  * Singleton client for managing singleton content operations
@@ -1738,6 +1763,7 @@ declare class SearchBuilder<T extends BaseContent = BaseContent, S extends AnySc
1738
1763
  private query;
1739
1764
  private searchOptions;
1740
1765
  private requestHeaders?;
1766
+ private freshValue?;
1741
1767
  /** The client's locale, asked when the query is sent rather than copied when it was built. */
1742
1768
  private readonly clientLocale;
1743
1769
  /**
@@ -1750,6 +1776,12 @@ declare class SearchBuilder<T extends BaseContent = BaseContent, S extends AnySc
1750
1776
  * dataset the key can see. A name the client already sends is replaced for this search only.
1751
1777
  */
1752
1778
  headers(headers: Record<string, string>): this;
1779
+ /**
1780
+ * The feed position this search follows — a change's `fresh` (`FeedChange`), or a resync's. Sent
1781
+ * as `?_fresh` on the query string beside the body: the edge keys a search on both, so no answer
1782
+ * it holds from before that position can answer it. The answer is the search's own.
1783
+ */
1784
+ fresh(position: string): this;
1753
1785
  /**
1754
1786
  * Search within a single content type. Where a schema types the client, the name is one of its
1755
1787
  * content types and the results are that type's entries.
@@ -2833,8 +2865,34 @@ type ChangesPageBody = PollChanges['responses'][200]['content']['application/jso
2833
2865
  * item, and fails if it is ever `unknown` again.
2834
2866
  */
2835
2867
  type Change = ChangesPageBody['data'][number];
2836
- /** Why a cursor could no longer be replayed. `unknown` when the message named neither. */
2837
- type ChangesExpiredReason = 'retention' | 'dataset-replaced' | 'unknown';
2868
+ /**
2869
+ * A change as `relyio.changes()` and `relyio.liveChanges()` deliver it: the document's
2870
+ * {@link Change}, and `fresh`.
2871
+ *
2872
+ * `fresh` is the feed position a refetch for this change names, as the one query term the API
2873
+ * declares on every read its edge may keep (`?_fresh`, `sdk-surfaces-spec.md` decision 32): hand it
2874
+ * to the read — `.fresh(change.fresh)` on `findById()`, `query()` and `search()`, `{ fresh }` to a
2875
+ * singleton's `fetch()`. A position no request named before the change is a key no cache holds a
2876
+ * body under, so the read is answered after the change — at the edge and in the browser, whether
2877
+ * or not the change's purge has reached them — and every later read naming it shares that answer.
2878
+ *
2879
+ * It is at or after the change, never before: on `/v1/changes` the change's own cursor, the ledger
2880
+ * row it came from; at the live edge the cursor of the batch it arrived in. It is not always
2881
+ * `cursor`: at the edge an earlier change of a batch carries the position **before** the batch as
2882
+ * its `cursor`, so a failure part-way through replays the batch, and a refetch naming that position
2883
+ * would be answered from before the change.
2884
+ */
2885
+ type FeedChange = Change & {
2886
+ /** The feed position a refetch for this change names (`?_fresh`); at or after the change. */
2887
+ readonly fresh: string;
2888
+ };
2889
+ /**
2890
+ * Why a cursor could no longer be replayed, or why everything must be refetched. `/v1/changes`
2891
+ * names `retention` and `dataset-replaced`; the live edge's `feed.resync` may name any reason its
2892
+ * wire declares (`VisitorResyncReason` in `@relyio/api-contract`, read from there rather than
2893
+ * restated). `unknown` when the message named none.
2894
+ */
2895
+ type ChangesExpiredReason = VisitorResyncReason | 'unknown';
2838
2896
  /**
2839
2897
  * `RESOURCE_EXPIRED` from the changes feed: the cursor names a position the
2840
2898
  * ledger can no longer replay from (`INV-SS-CURSOR-REPLAYS`). The consumer
@@ -2843,12 +2901,28 @@ type ChangesExpiredReason = 'retention' | 'dataset-replaced' | 'unknown';
2843
2901
  * A `ResourceExpiredError`, so code that already catches that still does. The
2844
2902
  * refusal declares no details, so `reason` is read from the message the api
2845
2903
  * writes (`apps/services/api/src/changes/changes-request.ts` `refusalOf`).
2904
+ *
2905
+ * The live edge's `feed.resync` — a socket's frame or a poll's answer — is the
2906
+ * same instruction and arrives as the same error ({@link ChangesExpiredError.resync}),
2907
+ * so `onExpired` means one thing on both transports.
2846
2908
  */
2847
2909
  declare class ChangesExpiredError extends ResourceExpiredError {
2848
2910
  readonly reason: ChangesExpiredReason;
2849
- /** The base class's signature, so `withContext` can rebuild one. */
2850
- constructor(message: string, statusCode?: number, cause?: Error, context?: Record<string, unknown>);
2851
- static of(expired: ResourceExpiredError): ChangesExpiredError;
2911
+ /**
2912
+ * The position the feed resynced to, which a refetch of everything names as `?_fresh`, as a
2913
+ * change's {@link FeedChange.fresh} is named: the head a `/v1/changes` resync took, or the cursor
2914
+ * the edge's `feed.resync` sent. `undefined` when there was no resync to take one — the error was
2915
+ * thrown (`onExpired: 'throw'`) — and on an error a caller built itself.
2916
+ */
2917
+ readonly fresh: string | undefined;
2918
+ /** The base class's signature, so `withContext` can rebuild one; then the position resynced to. */
2919
+ constructor(message: string, statusCode?: number, cause?: Error, context?: Record<string, unknown>, fresh?: string);
2920
+ /** The base class rebuilds from its four arguments, which would drop the position resynced to. */
2921
+ withContext(context: Record<string, unknown>): this;
2922
+ /** `/v1/changes`' refusal, with the head the feed resynced to when it did. */
2923
+ static of(expired: ResourceExpiredError, fresh?: string): ChangesExpiredError;
2924
+ /** The live edge's `feed.resync`: refetch everything, then continue from the cursor it sent. */
2925
+ static resync(reason: VisitorResyncReason, cursor: string): ChangesExpiredError;
2852
2926
  }
2853
2927
  interface ChangesOptions {
2854
2928
  /**
@@ -2882,15 +2956,152 @@ interface ChangesOptions {
2882
2956
  * - a function — resync, and call it with the error once the new position is
2883
2957
  * taken and before any change after it is yielded. This is the observable
2884
2958
  * form: whatever the consumer derived from the feed may be stale, so the
2885
- * function is where it invalidates everything. The iterator waits for it.
2959
+ * function is where it invalidates everything — naming the error's `fresh`,
2960
+ * the new position, on every refetch. The iterator waits for it.
2886
2961
  */
2887
2962
  onExpired?: 'throw' | 'resync' | ((expired: ChangesExpiredError) => void | Promise<void>);
2963
+ /**
2964
+ * Called once, when the feed was opened without `since`, with the position it starts from: the
2965
+ * first poll's answer, the dataset's head — before the next poll is sent. Read what you show
2966
+ * **after** it and a write your read did not see is after the position, so this feed delivers it,
2967
+ * with no second request to take the position first (`changesHead()` is that request) (REL-1513).
2968
+ *
2969
+ * `followChanges()` passes it (`ChangeSourceOptions.reached`), so a feed that drops before its first
2970
+ * change reopens from there rather than re-reading everything. Never called with a later empty
2971
+ * page's cursor: filtered, that cursor is past rows the filter hid, and a caller that widens the
2972
+ * filter must not reopen from it.
2973
+ */
2974
+ reached?: (cursor: string) => void;
2888
2975
  }
2889
2976
  interface ChangesHeadOptions {
2890
2977
  /** Cancels the request; the promise then rejects. */
2891
2978
  signal?: AbortSignal;
2892
2979
  }
2893
2980
 
2981
+ /**
2982
+ * `relyio.liveChanges()` — a dataset's published changes at RelyIO's live edge, for a publishable key
2983
+ * (`live-layer-spec.md` decision 20; LV5.2, REL-1158).
2984
+ *
2985
+ * The wire is `@relyio/api-contract`'s `live-visitor.ts`, read from there and never restated: the
2986
+ * path and query names, the frames and their reader, the keepalive, the poll's answer and its reader,
2987
+ * the poll interval, and what each close code and each refused poll means a client does next.
2988
+ *
2989
+ * **One open, two ways in.** A subscription opens a WebSocket to the edge. Where it cannot — the
2990
+ * runtime has no `WebSocket` (Node 18 and 20), constructing one throws, the socket fails before it
2991
+ * opens (a network that blocks sockets), or it has not opened and been caught up within
2992
+ * {@link LIVE_SOCKET_OPEN_TIMEOUT_MS} (a network that holds it) — it polls the edge instead, from the
2993
+ * same cursor, about every {@link VISITOR_POLL_INTERVAL_MS}, and tries a socket again about once a
2994
+ * minute. Both carry one cursor space, so a switch either way loses nothing.
2995
+ *
2996
+ * **Where it starts.** Without `since`, the edge answers the dataset's current position before
2997
+ * anything else — the socket's `feed.opened`, or the first poll's answer — and the subscription hands
2998
+ * it to `reached`. That call is the position, with no request of its own for it: a read sent after it
2999
+ * is answered at or after the position, and every change after it is delivered here, so a publish the
3000
+ * read did not see is announced (REL-1513). The edge counts the subscription as one live connection,
3001
+ * whichever way in it took (`INV-LV-LIVE-IS-METERED`).
3002
+ *
3003
+ * **It ends only when its signal aborts, and throws every other end** — a socket closed by a refusal
3004
+ * or a deploy, a refused poll, a keepalive nobody answered — as a `LiveEdgeError` saying what to do
3005
+ * next, so `followChanges()` reconnects, backs off or stops by the edge's own tables. That loop is
3006
+ * the one reconnect policy (`INV-SS-ONE-CORE`, REL-1215); this module keeps no back-off of its own.
3007
+ *
3008
+ * **Cursors.** The edge sends one cursor after a batch of changes. The last change of a batch carries
3009
+ * it; each earlier one carries the position before the batch, so a failure part-way through a batch
3010
+ * replays the whole batch rather than skipping its rest. Delivery is at-least-once: a replay and the
3011
+ * live frames after it may name one change twice, and a refetch makes a repeat cost a read, never a
3012
+ * wrong view (`INV-SS-CHANGE-IS-IDS`).
3013
+ *
3014
+ * **What a refetch names.** Every change of a batch carries the batch's own cursor as `fresh`
3015
+ * (`FeedChange`), the position its refetch names as `?_fresh` — not `cursor`, which for an earlier
3016
+ * change is the position before the batch: the previous batch's refetches were answered under that
3017
+ * one, before this batch, and a cache would hand the same answer back. A `feed.resync`'s error
3018
+ * carries the cursor it sent the same way.
3019
+ */
3020
+
3021
+ /**
3022
+ * How often an open socket sends the keepalive, which the edge's runtime answers without waking the
3023
+ * feed — the Studio's dataset room sends its own on the same interval. A socket that has heard
3024
+ * nothing by the next one, not even the answer, is presumed dead and reconnected: a client that only
3025
+ * receives never learns otherwise that a network change dropped its connection.
3026
+ */
3027
+ declare const LIVE_KEEPALIVE_INTERVAL_MS = 30000;
3028
+ /**
3029
+ * How long a socket has to open and be caught up — the feed's `feed.opened` — before the subscription
3030
+ * stops waiting on it, closes it and polls instead, as it does for a socket that fails before it opens
3031
+ * (REL-1513).
3032
+ *
3033
+ * The poll interval, because that is what a socket is for: telling the page sooner than a poll would.
3034
+ * A poll is answered at once and polled again within this interval, so a socket that has not caught
3035
+ * up by then has already served the page worse than polling would have. It is well past a healthy
3036
+ * open — the door's checks and one ledger read at the edge — and half the keepalive's verdict on an
3037
+ * open socket that has gone quiet ({@link LIVE_KEEPALIVE_INTERVAL_MS}, twice). It bounds how long
3038
+ * `<RelyIOProvider live>`'s first reads wait for a position on a network that neither opens a socket
3039
+ * nor refuses one — a proxy that holds the upgrade — which without it they would wait on for as long
3040
+ * as the page is open.
3041
+ */
3042
+ declare const LIVE_SOCKET_OPEN_TIMEOUT_MS = 15000;
3043
+ interface LiveChangesOptions {
3044
+ /**
3045
+ * A change's cursor from an earlier subscription. Omitted, the edge answers the dataset's current
3046
+ * position first and replays nothing, and the subscription hands that position to `reached` before
3047
+ * anything after it. Read what you show **after** that first call — a read sent before it, from a
3048
+ * subscription opened afterwards without `since`, leaves a publish between the two that neither
3049
+ * sees (REL-1513). With `followChanges()`, which hands the source its own `reached`, call both —
3050
+ * and let the read go if the loop fails first, so content never waits on a feed that is down:
3051
+ *
3052
+ * ```ts
3053
+ * let positioned!: () => void
3054
+ * const position = new Promise<void>(resolve => (positioned = resolve))
3055
+ * const following = followChanges(
3056
+ * from =>
3057
+ * relyio.liveChanges({
3058
+ * ...from,
3059
+ * ids: [id],
3060
+ * reached: cursor => {
3061
+ * from.reached(cursor)
3062
+ * positioned()
3063
+ * },
3064
+ * }),
3065
+ * { signal, onChange, onResync, onState: state => state.kind !== 'following' && positioned() },
3066
+ * )
3067
+ * await position
3068
+ * const post = await relyio.content('post').findById(id).execute()
3069
+ * ```
3070
+ *
3071
+ * The wait is bounded: a socket that has not opened and been told its position within
3072
+ * {@link LIVE_SOCKET_OPEN_TIMEOUT_MS} is given up for a poll, answered at once.
3073
+ */
3074
+ since?: string;
3075
+ /** Content types to keep. Omitted keeps every type; an empty list is refused. At most 50. */
3076
+ types?: readonly string[];
3077
+ /** Entry ids to keep. Omitted keeps every entry; an empty list is refused. At most 100. */
3078
+ ids?: readonly string[];
3079
+ /**
3080
+ * Ends the subscription. An abort — while connected, while polling, or between polls — makes the
3081
+ * `for await` **complete**, with the socket closed, no further request and nothing thrown.
3082
+ */
3083
+ signal?: AbortSignal;
3084
+ /**
3085
+ * What to do when the edge says to refetch everything (`feed.resync`: the cursor is too old, was
3086
+ * never this dataset's, or the dataset was replaced) — as `relyio.changes()` takes it: `'throw'`
3087
+ * (default) throws a `ChangesExpiredError`, `'resync'` carries on from the edge's position, and a
3088
+ * function is awaited with the error before any change after that position is yielded.
3089
+ */
3090
+ onExpired?: ChangesOptions['onExpired'];
3091
+ /**
3092
+ * Called when the edge has the subscription in hand — its `feed.opened` frame, or an answered
3093
+ * poll. `followChanges()` passes it to reset its back-off; see `ChangeSourceOptions.opened`.
3094
+ */
3095
+ opened?: () => void;
3096
+ /**
3097
+ * Called with a position reached without a change to carry it. `followChanges()` passes it so a
3098
+ * connection dropped before its first change reconnects from there; see
3099
+ * `ChangeSourceOptions.reached`. Without `since`, its first call is the position the subscription
3100
+ * starts from (see `since`).
3101
+ */
3102
+ reached?: (cursor: string) => void;
3103
+ }
3104
+
2894
3105
  /**
2895
3106
  * The key's project, as `GET /v1/project` describes it: organization, project, dataset and branch,
2896
3107
  * each by id and name, and `studioUrl` — the Studio root the preview toolbar deep-links from
@@ -2952,6 +3163,8 @@ declare class TypedRelyIOClient<TSchema extends AnySchemaDefinition | undefined
2952
3163
  * locale, or none after `locale: null` (REL-1453).
2953
3164
  */
2954
3165
  private readonly currentLocale;
3166
+ /** The live edge's poll client id: chosen once, when first needed, and kept for this client's life. */
3167
+ private _liveClientId;
2955
3168
  constructor(config: TypedClientConfig<TSchema>);
2956
3169
  /**
2957
3170
  * Get the schema definition
@@ -3114,13 +3327,15 @@ declare class TypedRelyIOClient<TSchema extends AnySchemaDefinition | undefined
3114
3327
  * Lazy — nothing is requested until the first `next()`. It runs until
3115
3328
  * `signal` aborts, which ends the `for await` cleanly rather than throwing.
3116
3329
  * A change names what changed; refetch it through the ordinary read path
3117
- * (`INV-SS-CHANGE-IS-IDS`).
3330
+ * (`INV-SS-CHANGE-IS-IDS`), naming its `fresh` so no cache answers with the
3331
+ * body from before it (`FeedChange`).
3118
3332
  *
3119
3333
  * @example
3120
3334
  * ```typescript
3121
3335
  * const controller = new AbortController()
3122
3336
  * for await (const change of relyio.changes({ since: cursor, types: ['post'], signal: controller.signal })) {
3123
- * // { entryId, contentType, locale, kind, cursor }
3337
+ * // { entryId, contentType, locale, kind, cursor, fresh }
3338
+ * await relyio.content(change.contentType).findById(change.entryId).fresh(change.fresh).execute()
3124
3339
  * }
3125
3340
  * ```
3126
3341
  *
@@ -3128,11 +3343,47 @@ declare class TypedRelyIOClient<TSchema extends AnySchemaDefinition | undefined
3128
3343
  * ```typescript
3129
3344
  * for await (const change of relyio.changes({
3130
3345
  * since: cursor,
3131
- * onExpired: async () => invalidateEverything(),
3346
+ * onExpired: async expired => refetchEverything(expired.fresh),
3132
3347
  * })) { … }
3133
3348
  * ```
3134
3349
  */
3135
- changes(options?: ChangesOptions): AsyncIterable<Change>;
3350
+ changes(options?: ChangesOptions): AsyncIterable<FeedChange>;
3351
+ /**
3352
+ * Follow the dataset's **published** changes at RelyIO's live edge, with a publishable key
3353
+ * (`rly_pub_…`): each change as ids, in order — the transport a browser follows changes with.
3354
+ *
3355
+ * A WebSocket to the edge, from `since` or the dataset's current position; where a socket cannot
3356
+ * open — the runtime has none, the network refuses it, or it has not opened and been told its
3357
+ * position within `LIVE_SOCKET_OPEN_TIMEOUT_MS` — it polls the edge instead, about every fifteen
3358
+ * seconds, from the same cursor. The edge is `liveUrl`, or derived from the default `baseUrl`
3359
+ * (see `RelyIOConfig.liveUrl`).
3360
+ *
3361
+ * Without `since` it starts at the dataset's current position, which it hands `reached` before
3362
+ * anything after it: read what you show after that call, and a publish your read did not see is
3363
+ * one this subscription announces (see `LiveChangesOptions.since`).
3364
+ *
3365
+ * One subscription ends only when `signal` aborts, which ends the `for await` cleanly; every other
3366
+ * end — a socket a deploy dropped, a refusal, a refused poll — is thrown as a `LiveEdgeError`
3367
+ * that says whether to stop, retry or back off. Hand it to `followChanges()`, the one loop that
3368
+ * reconnects from the last cursor with jittered back-off and honours those answers:
3369
+ *
3370
+ * @example
3371
+ * ```typescript
3372
+ * const relyio = createClient({ apiKey: 'rly_pub_…' })
3373
+ * const controller = new AbortController()
3374
+ * await followChanges(from => relyio.liveChanges({ ...from, types: ['post'] }), {
3375
+ * signal: controller.signal,
3376
+ * // `fresh` names a position no cache holds a body for from before the change.
3377
+ * onChange: change =>
3378
+ * relyio.content(change.contentType).findById(change.entryId).fresh(change.fresh).execute(),
3379
+ * onResync: resync => refetchEverything(resync.cause === 'expired' ? resync.expired.fresh : undefined),
3380
+ * })
3381
+ * ```
3382
+ *
3383
+ * A client without a publishable key sends nothing: a secret or preview key is never put in the
3384
+ * edge's URL. On a server, follow `changes()` with it instead.
3385
+ */
3386
+ liveChanges(options?: LiveChangesOptions): AsyncIterable<FeedChange>;
3136
3387
  /**
3137
3388
  * The dataset's current position in the changes feed, as a cursor to start
3138
3389
  * `changes({ since })` or `followChanges(…, { since })` from. One
@@ -3414,4 +3665,4 @@ declare function createClient<TSchema extends AnySchemaDefinition>(config: Typed
3414
3665
  */
3415
3666
  type ClientSchema<T extends TypedRelyIOClient<AnySchemaDefinition>> = T extends TypedRelyIOClient<infer TSchema> ? TSchema : never;
3416
3667
 
3417
- export { AdhocQueryClient as $, type ActiveSandbox as A, type SearchHitMetadata as B, ChangesExpiredError as C, type SearchResponse as D, type ExtendedQueryResult as E, type AutocompleteSuggestion as F, type AutocompleteResponse as G, HttpClient as H, type SimilarResult as I, type SimilarResponse as J, type SearchFilter as K, type SearchFields as L, type SearchSort as M, type SearchTypeName as N, type SearchEntry as O, type Project as P, BatchClient as Q, type Register as R, type SandboxState as S, TypedRelyIOClient as T, BatchBuilder as U, type BatchMethod as V, type BatchOperation as W, type BatchOptions as X, type BatchOperationResult as Y, type BatchSummary as Z, type BatchResponse as _, type Change as a, AdhocQueryBuilder as a0, type FetchBlock as a1, type AggregateBlock as a2, type AggregateMetric as a3, type SearchBlock as a4, type FilterValue as a5, type FullTextConfig as a6, type AdhocQueryDefinition as a7, type AdhocQueryOptions as a8, type QueryMetadata as a9, ResourceNotFoundError as aA, ResourceConflictError as aB, ResourceExpiredError as aC, isResourceNotFound as aD, type FileErrorCode as aE, type FileUploadErrorCode as aF, FileUploadError as aG, FileValidationError as aH, type FileValidationConstraints as aI, validateFile as aJ, type AdhocQueryResult as aa, AssetClient as ab, AssetFileBuilder as ac, AssetUploadBuilder as ad, isAssetEntry as ae, type AssetVisibility as af, type FileStatus as ag, type FileBackend as ah, type AssetFileBlock as ai, type AssetFileExtracted as aj, type FileStatusResult as ak, type AssetUrls as al, type AssetEntryResponse as am, type PopulatedAssetReference as an, type PresignInput as ao, type PresignResult as ap, type ConfirmInput as aq, type ConfirmResult as ar, type ResolveUrlInput as as, type ResolveUrlResult as at, type CreateAssetOptions as au, type WaitUntilReadyOptions as av, type MultipartCreateResult as aw, type MultipartPart as ax, type MultipartTarget as ay, type UploadProgress as az, type ChangesExpiredReason as b, createClient as c, type TypedClientConfig as d, type ClientSchema as e, type RegisteredSchema as f, type SendOptions as g, entityTagOf as h, type ConditionalWriteOptions as i, type RetryableWriteOptions as j, type ChangesHeadOptions as k, type ChangesOptions as l, ContentClient as m, SingletonClient as n, SettingsClient as o, ContentQueryBuilder as p, ContentByIdBuilder as q, type ChildrenOptions as r, type SingletonFetchOptions as s, type SettingsFetchOptions as t, SearchClient as u, SearchBuilder as v, AutocompleteBuilder as w, SimilarBuilder as x, type SearchMode as y, type SearchResultItem as z };
3668
+ export { type BatchOptions as $, type ActiveSandbox as A, type SearchMode as B, type Change as C, type SearchResultItem as D, type ExtendedQueryResult as E, type FeedChange as F, type SearchHitMetadata as G, HttpClient as H, type SearchResponse as I, type AutocompleteSuggestion as J, type AutocompleteResponse as K, LIVE_KEEPALIVE_INTERVAL_MS as L, type SimilarResult as M, type SimilarResponse as N, type SearchFilter as O, type Project as P, type SearchFields as Q, type Register as R, type SandboxState as S, TypedRelyIOClient as T, type SearchSort as U, type SearchTypeName as V, type SearchEntry as W, BatchClient as X, BatchBuilder as Y, type BatchMethod as Z, type BatchOperation as _, ChangesExpiredError as a, type BatchOperationResult as a0, type BatchSummary as a1, type BatchResponse as a2, AdhocQueryClient as a3, AdhocQueryBuilder as a4, type FetchBlock as a5, type AggregateBlock as a6, type AggregateMetric as a7, type SearchBlock as a8, type FilterValue as a9, type MultipartCreateResult as aA, type MultipartPart as aB, type MultipartTarget as aC, type UploadProgress as aD, ResourceNotFoundError as aE, ResourceConflictError as aF, ResourceExpiredError as aG, isResourceNotFound as aH, type FileErrorCode as aI, type FileUploadErrorCode as aJ, FileUploadError as aK, FileValidationError as aL, type FileValidationConstraints as aM, validateFile as aN, type FullTextConfig as aa, type AdhocQueryDefinition as ab, type AdhocQueryOptions as ac, type QueryMetadata as ad, type AdhocQueryResult as ae, AssetClient as af, AssetFileBuilder as ag, AssetUploadBuilder as ah, isAssetEntry as ai, type AssetVisibility as aj, type FileStatus as ak, type FileBackend as al, type AssetFileBlock as am, type AssetFileExtracted as an, type FileStatusResult as ao, type AssetUrls as ap, type AssetEntryResponse as aq, type PopulatedAssetReference as ar, type PresignInput as as, type PresignResult as at, type ConfirmInput as au, type ConfirmResult as av, type ResolveUrlInput as aw, type ResolveUrlResult as ax, type CreateAssetOptions as ay, type WaitUntilReadyOptions as az, type ChangesExpiredReason as b, createClient as c, type TypedClientConfig as d, type ClientSchema as e, type RegisteredSchema as f, type SendOptions as g, entityTagOf as h, type ConditionalWriteOptions as i, type RetryableWriteOptions as j, type ChangesHeadOptions as k, type ChangesOptions as l, LIVE_SOCKET_OPEN_TIMEOUT_MS as m, type LiveChangesOptions as n, ContentClient as o, SingletonClient as p, SettingsClient as q, ContentQueryBuilder as r, ContentByIdBuilder as s, type ChildrenOptions as t, type SingletonFetchOptions as u, type SettingsFetchOptions as v, SearchClient as w, SearchBuilder as x, AutocompleteBuilder as y, SimilarBuilder as z };
@@ -1,4 +1,4 @@
1
- import { aJ as paths, Q as EntryMetadata, aK as components } from './define-content-type-DiVpbwNs.cjs';
1
+ import { aJ as paths, Q as EntryMetadata, aK as components } from './define-content-type-cEGjIwT5.cjs';
2
2
 
3
3
  /**
4
4
  * Where the SDK's transport gets its truth.
@@ -197,6 +197,34 @@ interface RelyIOConfig {
197
197
  * })
198
198
  */
199
199
  fetch?: typeof globalThis.fetch;
200
+ /**
201
+ * RelyIO's live edge, which `liveChanges()` — and `<RelyIOProvider live>` from `@relyio/react`
202
+ * with a publishable key — follows a dataset's published changes at. An `https:` or `wss:` URL
203
+ * (`http:` or `ws:` for a local one); the socket and the poll each use the scheme they need.
204
+ *
205
+ * Omitted, it is derived from `baseUrl` for RelyIO's own API only: `https://api.relyio.com`
206
+ * (the default) follows `https://live.relyio.com`. For any other `baseUrl` there is no address to
207
+ * derive, so set this, or `liveChanges()` ends at once with a `LiveEdgeError` naming it and sends
208
+ * nothing.
209
+ *
210
+ * @example
211
+ * const relyio = createClient({
212
+ * apiKey: 'rly_pub_…',
213
+ * baseUrl: 'http://localhost:3000',
214
+ * liveUrl: 'http://localhost:8787',
215
+ * })
216
+ */
217
+ liveUrl?: string;
218
+ /**
219
+ * The `WebSocket` class `liveChanges()` opens its socket with. Omit it and the client uses the
220
+ * runtime's global `WebSocket` — every browser, and Node 22 or later — looked up per connection.
221
+ * Where there is none, as in Node 18 and 20, it polls the edge instead, about every fifteen
222
+ * seconds.
223
+ *
224
+ * Supply one to use a library's socket where the runtime has none, or to add what your runtime
225
+ * lets a socket carry.
226
+ */
227
+ webSocket?: LiveWebSocketConstructor;
200
228
  /** Browser-specific configuration */
201
229
  browser?: BrowserConfig;
202
230
  /**
@@ -213,6 +241,19 @@ interface RelyIOConfig {
213
241
  */
214
242
  dev?: boolean;
215
243
  }
244
+ /**
245
+ * The part of a `WebSocket` the live edge transport uses. A browser's `WebSocket` and Node's (22 and
246
+ * later) are assignable as they are; `src/__tests__/changes.types.ts` holds the platform's to it.
247
+ * Each event reaches its listener as `unknown` and is read field by field.
248
+ */
249
+ interface LiveWebSocket {
250
+ /** `0` connecting, `1` open, `2` closing, `3` closed. */
251
+ readonly readyState: number;
252
+ send(data: string): void;
253
+ close(code?: number, reason?: string): void;
254
+ addEventListener(type: 'open' | 'message' | 'close' | 'error', listener: (event: unknown) => void): void;
255
+ }
256
+ type LiveWebSocketConstructor = new (url: string) => LiveWebSocket;
216
257
  /**
217
258
  * What `updateConfig()` takes: any option of {@link RelyIOConfig}, each laid over the value the
218
259
  * client holds rather than over its default.
@@ -378,10 +419,11 @@ type Environment = 'development' | 'staging' | 'production';
378
419
  type WithDefaults<T, NoDefault extends keyof T = never> = Required<Omit<T, NoDefault>> & Pick<T, NoDefault>;
379
420
  /**
380
421
  * The options with no default, absent from a merged configuration until the caller gives one.
381
- * `fetch` among them: absent means the runtime's own, and that is read per request rather than
382
- * captured here.
422
+ * `fetch` and `webSocket` among them: absent means the runtime's own, and that is read per request
423
+ * or per connection rather than captured here. `liveUrl` too: absent is derived from `baseUrl` when
424
+ * the live transport opens, where RelyIO's own API names its edge, and is otherwise no address.
383
425
  */
384
- type UndefaultedOption = 'apiKey' | 'dataset' | 'locale' | 'dev' | 'fetch';
426
+ type UndefaultedOption = 'apiKey' | 'dataset' | 'locale' | 'dev' | 'fetch' | 'liveUrl' | 'webSocket';
385
427
  /** The options that are themselves bags of options, each merged field by field. */
386
428
  type OptionBag = 'browser' | 'cache' | 'realtime' | 'debug';
387
429
  /**
@@ -815,4 +857,4 @@ interface PerformanceMetrics {
815
857
  cacheHitRatio?: number;
816
858
  }
817
859
 
818
- export { type ApiPath as A, type BaseContent as B, type CreateInput as C, type DebugConfig as D, type Environment as E, type FieldTypeName as F, ValidationError as G, type MergedRelyIOConfig as H, InvalidPayloadError as I, type PathParamsOf as J, type FieldDefinition as K, type LogLevel as L, type MatchRouteOptions as M, type PerformanceMetrics as P, type QueryOptions as Q, type RelyIOConfig as R, SDKError as S, type UpdateInput as U, type ValidationRule as V, type Wire as W, type RelyIOConfigUpdate as a, type ApiMethod as b, type ApiSuccessBody as c, type RouteMatch as d, type WhereScalar as e, type WhereFieldCondition as f, type WhereClause as g, type WhereOperator as h, type SortOptions as i, type ContentQueryResult as j, type SingletonContent as k, type CustomQuery as l, matchRoute as m, type ContentTypeSchema as n, type FieldOptions as o, type RealtimeEventType as p, type RealtimeEvent as q, type SubscriptionOptions as r, type CacheConfig as s, type RealtimeConfig as t, type RequestConfig as u, type ApiResponse as v, type RetryConfig as w, type CacheStrategy as x, type ValidationViolation as y, BaseValidationError as z };
860
+ export { type ApiPath as A, type BaseContent as B, type CreateInput as C, type DebugConfig as D, type Environment as E, type FieldTypeName as F, type ValidationViolation as G, BaseValidationError as H, ValidationError as I, InvalidPayloadError as J, type MergedRelyIOConfig as K, type LiveWebSocket as L, type MatchRouteOptions as M, type PathParamsOf as N, type FieldDefinition as O, type PerformanceMetrics as P, type QueryOptions as Q, type RelyIOConfig as R, SDKError as S, type UpdateInput as U, type ValidationRule as V, type Wire as W, type RelyIOConfigUpdate as a, type LiveWebSocketConstructor as b, type ApiMethod as c, type ApiSuccessBody as d, type RouteMatch as e, type WhereScalar as f, type WhereFieldCondition as g, type WhereClause as h, type WhereOperator as i, type SortOptions as j, type ContentQueryResult as k, type SingletonContent as l, matchRoute as m, type CustomQuery as n, type ContentTypeSchema as o, type FieldOptions as p, type RealtimeEventType as q, type RealtimeEvent as r, type SubscriptionOptions as s, type CacheConfig as t, type RealtimeConfig as u, type RequestConfig as v, type ApiResponse as w, type RetryConfig as x, type CacheStrategy as y, type LogLevel as z };
@@ -1,4 +1,4 @@
1
- import { aJ as paths, Q as EntryMetadata, aK as components } from './define-content-type-DiVpbwNs.js';
1
+ import { aJ as paths, Q as EntryMetadata, aK as components } from './define-content-type-cEGjIwT5.js';
2
2
 
3
3
  /**
4
4
  * Where the SDK's transport gets its truth.
@@ -197,6 +197,34 @@ interface RelyIOConfig {
197
197
  * })
198
198
  */
199
199
  fetch?: typeof globalThis.fetch;
200
+ /**
201
+ * RelyIO's live edge, which `liveChanges()` — and `<RelyIOProvider live>` from `@relyio/react`
202
+ * with a publishable key — follows a dataset's published changes at. An `https:` or `wss:` URL
203
+ * (`http:` or `ws:` for a local one); the socket and the poll each use the scheme they need.
204
+ *
205
+ * Omitted, it is derived from `baseUrl` for RelyIO's own API only: `https://api.relyio.com`
206
+ * (the default) follows `https://live.relyio.com`. For any other `baseUrl` there is no address to
207
+ * derive, so set this, or `liveChanges()` ends at once with a `LiveEdgeError` naming it and sends
208
+ * nothing.
209
+ *
210
+ * @example
211
+ * const relyio = createClient({
212
+ * apiKey: 'rly_pub_…',
213
+ * baseUrl: 'http://localhost:3000',
214
+ * liveUrl: 'http://localhost:8787',
215
+ * })
216
+ */
217
+ liveUrl?: string;
218
+ /**
219
+ * The `WebSocket` class `liveChanges()` opens its socket with. Omit it and the client uses the
220
+ * runtime's global `WebSocket` — every browser, and Node 22 or later — looked up per connection.
221
+ * Where there is none, as in Node 18 and 20, it polls the edge instead, about every fifteen
222
+ * seconds.
223
+ *
224
+ * Supply one to use a library's socket where the runtime has none, or to add what your runtime
225
+ * lets a socket carry.
226
+ */
227
+ webSocket?: LiveWebSocketConstructor;
200
228
  /** Browser-specific configuration */
201
229
  browser?: BrowserConfig;
202
230
  /**
@@ -213,6 +241,19 @@ interface RelyIOConfig {
213
241
  */
214
242
  dev?: boolean;
215
243
  }
244
+ /**
245
+ * The part of a `WebSocket` the live edge transport uses. A browser's `WebSocket` and Node's (22 and
246
+ * later) are assignable as they are; `src/__tests__/changes.types.ts` holds the platform's to it.
247
+ * Each event reaches its listener as `unknown` and is read field by field.
248
+ */
249
+ interface LiveWebSocket {
250
+ /** `0` connecting, `1` open, `2` closing, `3` closed. */
251
+ readonly readyState: number;
252
+ send(data: string): void;
253
+ close(code?: number, reason?: string): void;
254
+ addEventListener(type: 'open' | 'message' | 'close' | 'error', listener: (event: unknown) => void): void;
255
+ }
256
+ type LiveWebSocketConstructor = new (url: string) => LiveWebSocket;
216
257
  /**
217
258
  * What `updateConfig()` takes: any option of {@link RelyIOConfig}, each laid over the value the
218
259
  * client holds rather than over its default.
@@ -378,10 +419,11 @@ type Environment = 'development' | 'staging' | 'production';
378
419
  type WithDefaults<T, NoDefault extends keyof T = never> = Required<Omit<T, NoDefault>> & Pick<T, NoDefault>;
379
420
  /**
380
421
  * The options with no default, absent from a merged configuration until the caller gives one.
381
- * `fetch` among them: absent means the runtime's own, and that is read per request rather than
382
- * captured here.
422
+ * `fetch` and `webSocket` among them: absent means the runtime's own, and that is read per request
423
+ * or per connection rather than captured here. `liveUrl` too: absent is derived from `baseUrl` when
424
+ * the live transport opens, where RelyIO's own API names its edge, and is otherwise no address.
383
425
  */
384
- type UndefaultedOption = 'apiKey' | 'dataset' | 'locale' | 'dev' | 'fetch';
426
+ type UndefaultedOption = 'apiKey' | 'dataset' | 'locale' | 'dev' | 'fetch' | 'liveUrl' | 'webSocket';
385
427
  /** The options that are themselves bags of options, each merged field by field. */
386
428
  type OptionBag = 'browser' | 'cache' | 'realtime' | 'debug';
387
429
  /**
@@ -815,4 +857,4 @@ interface PerformanceMetrics {
815
857
  cacheHitRatio?: number;
816
858
  }
817
859
 
818
- export { type ApiPath as A, type BaseContent as B, type CreateInput as C, type DebugConfig as D, type Environment as E, type FieldTypeName as F, ValidationError as G, type MergedRelyIOConfig as H, InvalidPayloadError as I, type PathParamsOf as J, type FieldDefinition as K, type LogLevel as L, type MatchRouteOptions as M, type PerformanceMetrics as P, type QueryOptions as Q, type RelyIOConfig as R, SDKError as S, type UpdateInput as U, type ValidationRule as V, type Wire as W, type RelyIOConfigUpdate as a, type ApiMethod as b, type ApiSuccessBody as c, type RouteMatch as d, type WhereScalar as e, type WhereFieldCondition as f, type WhereClause as g, type WhereOperator as h, type SortOptions as i, type ContentQueryResult as j, type SingletonContent as k, type CustomQuery as l, matchRoute as m, type ContentTypeSchema as n, type FieldOptions as o, type RealtimeEventType as p, type RealtimeEvent as q, type SubscriptionOptions as r, type CacheConfig as s, type RealtimeConfig as t, type RequestConfig as u, type ApiResponse as v, type RetryConfig as w, type CacheStrategy as x, type ValidationViolation as y, BaseValidationError as z };
860
+ export { type ApiPath as A, type BaseContent as B, type CreateInput as C, type DebugConfig as D, type Environment as E, type FieldTypeName as F, type ValidationViolation as G, BaseValidationError as H, ValidationError as I, InvalidPayloadError as J, type MergedRelyIOConfig as K, type LiveWebSocket as L, type MatchRouteOptions as M, type PathParamsOf as N, type FieldDefinition as O, type PerformanceMetrics as P, type QueryOptions as Q, type RelyIOConfig as R, SDKError as S, type UpdateInput as U, type ValidationRule as V, type Wire as W, type RelyIOConfigUpdate as a, type LiveWebSocketConstructor as b, type ApiMethod as c, type ApiSuccessBody as d, type RouteMatch as e, type WhereScalar as f, type WhereFieldCondition as g, type WhereClause as h, type WhereOperator as i, type SortOptions as j, type ContentQueryResult as k, type SingletonContent as l, matchRoute as m, type CustomQuery as n, type ContentTypeSchema as o, type FieldOptions as p, type RealtimeEventType as q, type RealtimeEvent as r, type SubscriptionOptions as s, type CacheConfig as t, type RealtimeConfig as u, type RequestConfig as v, type ApiResponse as w, type RetryConfig as x, type CacheStrategy as y, type LogLevel as z };