@relyio/sdk 0.12.0 → 0.14.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.
Files changed (48) hide show
  1. package/CHANGELOG.md +228 -0
  2. package/dist/{chunk-3DRDHXLY.js → chunk-5UMY6CS4.js} +435 -23
  3. package/dist/{chunk-FRJEUAAF.js → chunk-DBBZVZOS.js} +968 -2670
  4. package/dist/{chunk-DXCVO6M5.cjs → chunk-GUPV2M3X.cjs} +969 -2680
  5. package/dist/{chunk-POEEDYFM.js → chunk-PCFTJLEP.js} +543 -260
  6. package/dist/{chunk-FI5VIF4M.cjs → chunk-RSHMA4KT.cjs} +442 -22
  7. package/dist/{chunk-3MSFWXQL.cjs → chunk-X4KSS7PP.cjs} +542 -260
  8. package/dist/client/index.cjs +3 -3
  9. package/dist/client/index.d.cts +4 -5
  10. package/dist/client/index.d.ts +4 -5
  11. package/dist/client/index.js +1 -1
  12. package/dist/{include-paths-CL4gHv6I.d.cts → define-content-type-BUZeWtpk.d.cts} +4458 -1312
  13. package/dist/{include-paths-CrJNTsMl.d.ts → define-content-type-DOrZb_a_.d.ts} +4458 -1312
  14. package/dist/define-schema-5aHmsA8j.d.ts +699 -0
  15. package/dist/define-schema-nCfZeboB.d.cts +699 -0
  16. package/dist/index-B97ZDj44.d.ts +507 -0
  17. package/dist/index-C-pcllqY.d.cts +507 -0
  18. package/dist/index-DOhFQbv7.d.ts +3065 -0
  19. package/dist/index-DgVV5Nj7.d.cts +3065 -0
  20. package/dist/index.cjs +114 -147
  21. package/dist/index.d.cts +49 -87
  22. package/dist/index.d.ts +49 -87
  23. package/dist/index.js +5 -29
  24. package/dist/markdown/index.cjs +13750 -0
  25. package/dist/markdown/index.d.cts +111 -0
  26. package/dist/markdown/index.d.ts +111 -0
  27. package/dist/markdown/index.js +13742 -0
  28. package/dist/rich-text/index.cjs +26 -6
  29. package/dist/rich-text/index.d.cts +3 -148
  30. package/dist/rich-text/index.d.ts +3 -148
  31. package/dist/rich-text/index.js +1 -1
  32. package/dist/rich-text.d-BWc-5gX1.d.cts +326 -0
  33. package/dist/rich-text.d-BWc-5gX1.d.ts +326 -0
  34. package/dist/schema/index.cjs +33 -37
  35. package/dist/schema/index.d.cts +37 -191
  36. package/dist/schema/index.d.ts +37 -191
  37. package/dist/schema/index.js +1 -1
  38. package/dist/types/index.d.cts +2 -3
  39. package/dist/types/index.d.ts +2 -3
  40. package/package.json +14 -3
  41. package/dist/define-schema-DgctHlI2.d.cts +0 -1034
  42. package/dist/define-schema-mrEZbY1h.d.ts +0 -1034
  43. package/dist/index-B9RfZVF1.d.ts +0 -861
  44. package/dist/index-BrvEg6-A.d.cts +0 -3729
  45. package/dist/index-Pw0onkJe.d.ts +0 -3729
  46. package/dist/index-b-AkfTUv.d.cts +0 -861
  47. package/dist/rich-text.d-rKmpgdGd.d.cts +0 -200
  48. package/dist/rich-text.d-rKmpgdGd.d.ts +0 -200
@@ -1,3729 +0,0 @@
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-B9RfZVF1.js';
2
- import { W as Reading, _ as UntypedReading, M as CheckedIncludes, X as ReadWith, aX as components, aY as CheckedInclude, aW as paths, a6 as InferContentType, P as ContentReading, an as InferAssetType, J as AssetReading, ae as InferSingleton, Z as SingletonReading, aj as InferSettingsGroup, Y as SettingsReading } from './include-paths-CrJNTsMl.js';
3
- import { a as SchemaDefinition, b as SchemaContentTypeNames, j as SchemaEntryOf, al as FilterOperator, h as SchemaAssetTypeNames, c as SchemaSingletonNames, g as SchemaSettingsGroupNames, e as SchemaQueryNames, f as SchemaLocaleCodes } from './define-schema-mrEZbY1h.js';
4
- import { VisitorResyncReason } from './live-visitor.js';
5
- import { DevUnreadableReason } from './dev-handshake/index.js';
6
-
7
- /**
8
- * Dev-mode detection for `relyio dev` sandboxes — the SDK's I/O shell over `../dev-handshake`.
9
- *
10
- * Every rule about the handshake — the path, the env names, the required fields, the deadline —
11
- * lives in `@relyio/sdk/dev-handshake`, which `relyio dev` imports too. What is left here is the
12
- * part that is genuinely the SDK's: reading env and walking the filesystem from the cwd, both of
13
- * which have to happen without a static `node:*` import so the module stays browser-safe.
14
- *
15
- * Node access goes through `process.getBuiltinModule` (Node ≥ 20.16 / 22, and the platform targets
16
- * Node 24), which is absent in browsers and edge runtimes — so the resolver is inert everywhere it
17
- * should be, and a bundler never has to shim `fs`.
18
- *
19
- * **Order: env, then file.** The wrapped-child env is the primary carrier and wins. Before REL-647
20
- * it won *unconditionally*, because it had no deadline to lose on; it now goes through the same
21
- * rule the file does.
22
- */
23
-
24
- /**
25
- * What the handshake says about the sandbox, as the preview toolbar displays it
26
- * (`sdk-surfaces-spec.md` decision 22). `TypedRelyIOClient.sandbox` returns it.
27
- *
28
- * **Plain data, on purpose.** In Next the toolbar is rendered from a server layout, and a client
29
- * cannot cross the RSC boundary — while the browser cannot read the handshake at all, because this
30
- * module is inert without `process.getBuiltinModule`. So the server-created client's `sandbox` is the
31
- * prop the toolbar is handed, and it has to survive `JSON` unchanged: strings and `null`, dates as
32
- * the ISO strings the handshake carried, no class instances.
33
- *
34
- * **No key.** The handshake also carries the sandbox's preview key; that is the client's credential,
35
- * held beside this value in {@link DevResolution} and never inside it, so handing the state around
36
- * does not hand the key with it.
37
- *
38
- * - `active` — the client targets the sandbox. `branchId` is `null` from the env carrier, which names
39
- * no branch; only `.relyio/dev.json` does. `expiresAt` is `null` for an unbounded sandbox.
40
- * - `expired` / `unreadable` — a handshake was found and refused; the client targets the configured
41
- * dataset and key, and `mergeConfig` has said so on the console.
42
- * - `absent` — no handshake on either carrier. This is also what a browser or edge runtime reads,
43
- * since neither can see the env or the file.
44
- * - `disabled` — detection did not run: `dev: false` was passed, or `NODE_ENV === 'production'`
45
- * and `dev: true` was not.
46
- */
47
- type SandboxState = {
48
- state: 'active';
49
- datasetId: string;
50
- branchId: string | null;
51
- expiresAt: string | null;
52
- source: 'env' | 'file';
53
- } | {
54
- state: 'expired';
55
- expiredAt: string;
56
- } | {
57
- state: 'unreadable';
58
- reason: DevUnreadableReason;
59
- } | {
60
- state: 'absent';
61
- } | {
62
- state: 'disabled';
63
- };
64
- /** The sandbox the client targets, once a carrier has been accepted. */
65
- type ActiveSandbox = Extract<SandboxState, {
66
- state: 'active';
67
- }>;
68
-
69
- /**
70
- * What a call site supplies beside the route it named.
71
- *
72
- * `path` is required exactly when the document's template has segments, and
73
- * forbidden when it does not — so a call that forgot to scope a request, or one
74
- * that scoped a request the route does not scope, is a compile error rather
75
- * than a URL with a literal `{id}` in it.
76
- */
77
- type SendOptions<P extends ApiPath, M extends ApiMethod<P>> = {
78
- /** Query string values. The document declares none, so this stays untyped — see `api-routes.ts`. */
79
- query?: Record<string, unknown>;
80
- body?: unknown;
81
- /** Request headers this call adds, e.g. `If-Match` / `Idempotency-Key` (API4). */
82
- headers?: Record<string, string>;
83
- signal?: AbortSignal;
84
- timeout?: number;
85
- } & ([PathParamsOf<P, M>] extends [never] ? {
86
- path?: never;
87
- } : {
88
- path: PathParamsOf<P, M>;
89
- });
90
- /**
91
- * Core HTTP client for the RelyIO SDK.
92
- *
93
- * `send` is **the** typed entry: it names a route by the path template the
94
- * committed document declares, and returns that operation's body type with the
95
- * payload typed as `T`. `readPayload` from `@relyio/api-contract/client` is
96
- * then the one unwrap — there is no second spelling and no runtime test of
97
- * which envelope arrived (`INV-API-CLIENT-IS-DERIVED`).
98
- *
99
- * The verb helpers below stay for the escape hatch `getHttpClient()` exposes;
100
- * nothing inside the SDK uses them.
101
- */
102
- declare class HttpClient {
103
- /** What every request reads off the configuration, derived by {@link configure} and nowhere else. */
104
- private connection;
105
- constructor(config: MergedRelyIOConfig);
106
- /**
107
- * Run on `config` from now on: every later request — whoever holds this client — is sent to its
108
- * `baseUrl`, with its headers, key and dataset, its timeout and retries, through its `fetch`, and
109
- * logged by its `debug` options.
110
- *
111
- * The client's own `updateConfig()` calls this rather than building a second client, so a
112
- * `content()` or other sub-client taken before the update sends through the update too. Anything
113
- * set on this client since it was last configured — `setDefaultHeaders()`, `setBaseUrl()` — is
114
- * replaced by what `config` says.
115
- */
116
- configure(config: MergedRelyIOConfig): void;
117
- private announce;
118
- /**
119
- * Call a route the committed document declares, and get back the body that
120
- * document says it serves.
121
- */
122
- send<P extends ApiPath, M extends ApiMethod<P>, T = unknown>(path: P, method: M, options: SendOptions<P, M>): Promise<ApiResponse<Wire<P, M, T>>>;
123
- /**
124
- * Call a route and return its payload — `send` plus the one unwrap, which is
125
- * what all but a handful of call sites want.
126
- */
127
- fetchPayload<P extends ApiPath, M extends ApiMethod<P>, T = unknown>(path: P, method: M, options: SendOptions<P, M>): Promise<Wire<P, M, T> extends {
128
- data: unknown;
129
- } ? Wire<P, M, T>['data'] : never>;
130
- /**
131
- * Make an HTTP request, retrying only where a retry is safe.
132
- *
133
- * The headers are merged **once**, before the first attempt, and every attempt
134
- * sends that same object. That is what makes a minted `Idempotency-Key` stable
135
- * across the retries of one logical write: the service sees one key and
136
- * answers the replay from the original outcome instead of applying it twice.
137
- *
138
- * The loop always makes one attempt. The incumbent's `while (attempt <
139
- * maxAttempts)` made none at `retryAttempts: 0` — a value the config validator
140
- * then accepted, and now refuses — and then dereferenced an unassigned
141
- * `lastError`. `retryAttempts` is the most attempts, the first included; an
142
- * `HttpClient` constructed directly is not validated, so the first attempt
143
- * stays unconditional.
144
- *
145
- * A request that would carry no `X-API-Key` makes none: it rejects with
146
- * `MissingApiKeyError` before `fetch` is called, is not retried, and names
147
- * `apiKey`. This is where a client built without a key learns it — the
148
- * constructor accepts one so that a module-scope client can be evaluated by a
149
- * build that holds no key (REL-1120) — and so does one whose `setApiKey` was
150
- * given none.
151
- */
152
- request<T = unknown>(config: RequestConfig): Promise<ApiResponse<T>>;
153
- /**
154
- * Make a GET request
155
- */
156
- get<T = unknown>(url: string, params?: Record<string, unknown>): Promise<ApiResponse<T>>;
157
- /**
158
- * Make a POST request
159
- */
160
- post<T = unknown>(url: string, data?: unknown, params?: Record<string, unknown>): Promise<ApiResponse<T>>;
161
- /**
162
- * Make a PUT request
163
- */
164
- put<T = unknown>(url: string, data?: unknown, params?: Record<string, unknown>): Promise<ApiResponse<T>>;
165
- /**
166
- * Make a DELETE request
167
- */
168
- delete<T = unknown>(url: string, params?: Record<string, unknown>): Promise<ApiResponse<T>>;
169
- /**
170
- * Make a PATCH request
171
- */
172
- patch<T = unknown>(url: string, data?: unknown, params?: Record<string, unknown>): Promise<ApiResponse<T>>;
173
- /**
174
- * Add headers to every later request, each replacing any header of the same name — the name
175
- * matched without regard to case — until the client is next configured.
176
- */
177
- setDefaultHeaders(headers: Record<string, string>): void;
178
- /**
179
- * Replace the key every later request sends as `X-API-Key`, and the `apiKey` the configuration
180
- * reports.
181
- *
182
- * The key is read the way construction reads it (`presentKey`): an absent, empty or blank key is
183
- * no key, so the header is dropped and the next request is refused with `MissingApiKeyError`
184
- * before anything is sent. A key header of any other spelling — one given in `headers` — goes
185
- * too, from the headers sent and from the headers the configuration reports, so the old key is
186
- * never sent beside the new one.
187
- *
188
- * The configuration is the one the owning `TypedRelyIOClient` holds (it hands its own to the
189
- * constructor), so its `getConfig().apiKey` — what a surface reads to decide a key's kind — is the
190
- * key sent from here on.
191
- *
192
- * This replaced `setAuthToken`, which added `Authorization: Bearer` beside an `X-API-Key` it
193
- * never touched: the API reads `X-API-Key` first, so the old key was the one used (REL-1385).
194
- */
195
- setApiKey(apiKey: string): void;
196
- /**
197
- * Get current default headers
198
- */
199
- getDefaultHeaders(): Record<string, string>;
200
- /**
201
- * Update base URL
202
- */
203
- setBaseUrl(baseUrl: string): void;
204
- /**
205
- * Get current base URL
206
- */
207
- getBaseUrl(): string;
208
- /**
209
- * One attempt.
210
- *
211
- * The caller's `signal` and the timeout both end it, through one controller.
212
- * The incumbent handed `fetch` the caller's signal **instead of** its own, so a
213
- * request that passed a signal had no timeout at all — and a caller abort then
214
- * surfaced as `Request timed out`. `timedOut` is what tells the two apart: a
215
- * timeout is a `TimeoutError`, a caller abort is the platform's `AbortError`,
216
- * rethrown as it came.
217
- */
218
- private makeRequest;
219
- /**
220
- * The headers without those a browser controls itself, when this is a browser; unchanged when it
221
- * is not.
222
- *
223
- * A name is matched without regard to case (RFC 9110 §5.1), because a browser refuses `Origin`
224
- * whatever case a page spells it in. This matched only the all-lowercase and all-uppercase
225
- * spellings, so `Origin` and `User-Agent` — how people write them — reached `fetch` (REL-1409).
226
- */
227
- private filterHeadersForBrowser;
228
- /**
229
- * Prepare request body based on content type and environment
230
- */
231
- private prepareRequestBody;
232
- private parseResponse;
233
- private resolveUrl;
234
- private mergeRequestConfig;
235
- private buildQueryString;
236
- private headersToObject;
237
- private log;
238
- }
239
-
240
- /**
241
- * The client's locale as a sub-client holds it: a function it asks on every call, or a locale fixed
242
- * when it was built.
243
- *
244
- * `TypedRelyIOClient` hands every sub-client and builder a function over the configuration it holds,
245
- * so one taken before `updateConfig({ locale })` sends the locale the client holds when the call is
246
- * made, and none once `updateConfig({ locale: null })` has cleared it (REL-1453). A string is a locale
247
- * fixed at construction — a sub-client's own `withLocale()`, or one built directly — and stays what it
248
- * was.
249
- */
250
- type ClientLocale = string | (() => string | undefined);
251
-
252
- /**
253
- * Children population options
254
- */
255
- interface ChildrenOptions {
256
- /** Content types of children to include */
257
- contentTypes?: string[];
258
- /** Maximum children per entry */
259
- limit?: number;
260
- /** Recursion depth */
261
- depth?: number;
262
- }
263
- /**
264
- * Retained as an alias of {@link ContentQueryResult}.
265
- *
266
- * It used to add an optional `pagination: { page, pageSize, pageCount, total }`
267
- * that the list route emitted only when `withCount` arrived — "a shape no
268
- * generated type can describe", as that controller's own comment now puts it.
269
- * API2 made the customer list the **cursor** dialect unconditionally
270
- * (`INV-API-ONE-CUSTOMER-PAGE-DIALECT`), so no total reaches this surface and
271
- * the extra member described a body the service cannot send.
272
- */
273
- type ExtendedQueryResult<T extends BaseContent = BaseContent> = ContentQueryResult<T>;
274
- /**
275
- * Fluent query builder for content operations
276
- * Provides a chainable API for building complex content queries
277
- *
278
- * @example
279
- * ```typescript
280
- * const products = await client.content('product')
281
- * .query()
282
- * .where({ status: 'active' })
283
- * .locale('en')
284
- * .fields(['id', 'title', 'price'])
285
- * .include(['brand', 'categories'])
286
- * .sort({ title: 'asc' })
287
- * .limit(20)
288
- * .execute()
289
- * ```
290
- */
291
- declare class ContentQueryBuilder<T extends BaseContent = BaseContent, F extends BaseContent = T, R extends Reading = UntypedReading<T>, I extends string = never> {
292
- private readonly httpClient;
293
- private readonly contentType;
294
- private queryOptions;
295
- private localeValue?;
296
- private localesArray?;
297
- private fetchAllLocales;
298
- private fieldsProjection?;
299
- private childrenOptions?;
300
- private pageNumber?;
301
- private pageSizeValue?;
302
- private requestHeaders?;
303
- /** The client's locale, asked when the query is sent rather than copied when it was built. */
304
- private readonly clientLocale;
305
- private freshValue?;
306
- /**
307
- * @param locale - The client's locale (`withLocale()`, `updateConfig({ locale })`), sent when the
308
- * query names none: the function `TypedRelyIOClient` hands over, or a locale fixed for it
309
- */
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;
319
- /**
320
- * Headers this query adds to the client's own, e.g. `X-RelyIO-Dataset` to list another dataset
321
- * the key can see. A name the client already sends is replaced for this query only; the client
322
- * is not changed.
323
- */
324
- headers(headers: Record<string, string>): this;
325
- /**
326
- * Add where conditions to the query: conditions on the fields the content type declares. A
327
- * condition on `_metadata` is refused here, before anything is sent (`refuseMetadataConditions`).
328
- */
329
- where(conditions: WhereClause<F>): this;
330
- /**
331
- * Select specific fields to return
332
- */
333
- select(fields: NonNullable<QueryOptions<T, F>['select']>): this;
334
- /**
335
- * Expand references into the entries they point at: one include path per element (REL-1801).
336
- * A path names fields through lists, page builders, objects and references —
337
- * `'sections.features'`, `'sections.customers.logo'` — and may pick the fields each entry returns,
338
- * `'sections.features(name,slug)'`. Each is checked against the schema: a path that holds no
339
- * reference does not compile, and the read's type follows the paths — an expanded reference is the
340
- * entry, every other one its id.
341
- */
342
- include<const P extends readonly string[]>(paths: CheckedIncludes<R, P>): ContentQueryBuilder<Extract<ReadWith<R, I | P[number]>, BaseContent>, F, R, I | P[number]>;
343
- /**
344
- * Add sorting to the query
345
- *
346
- * @example
347
- * ```typescript
348
- * // Object format
349
- * .sort({ createdAt: 'desc', title: 'asc' })
350
- *
351
- * // Field and direction format
352
- * .sort('createdAt', 'desc')
353
- * ```
354
- */
355
- sort(field: string, direction: 'asc' | 'desc'): this;
356
- sort(sortOptions: SortOptions<F>): this;
357
- /**
358
- * Sort by a specific field in ascending order
359
- */
360
- sortAsc(field: keyof T | string): this;
361
- /**
362
- * Sort by a specific field in descending order
363
- */
364
- sortDesc(field: keyof T | string): this;
365
- /**
366
- * Limit the number of results
367
- */
368
- limit(count: number): this;
369
- /**
370
- * Set offset for pagination
371
- */
372
- offset(count: number): this;
373
- /**
374
- * Set cursor for cursor-based pagination
375
- */
376
- cursor(cursor: string): this;
377
- /**
378
- * Set locale for localized content
379
- * @param locale - Locale code (e.g., 'en-US', 'de-DE', 'fr-FR')
380
- *
381
- * @example
382
- * ```typescript
383
- * // Fetch in Spanish
384
- * .locale('es-ES')
385
- * ```
386
- */
387
- locale(locale: string): this;
388
- /**
389
- * Fetch content in multiple specific locales
390
- * Returns primary locale at top level, others in _metadata.localizations
391
- *
392
- * @param localeCodes - Array of locale codes to fetch
393
- *
394
- * @example
395
- * ```typescript
396
- * const product = await client.content('product')
397
- * .findById('prod_123')
398
- * .locales(['en-US', 'es-ES', 'de-DE'])
399
- * .execute()
400
- *
401
- * // Access: product.title (first locale)
402
- * // Access: product._metadata.localizations['es-ES'].title
403
- * ```
404
- */
405
- locales(localeCodes: string[]): this;
406
- /**
407
- * Fetch content in all available locales
408
- * Returns default locale at top level, others in _metadata.localizations
409
- *
410
- * @example
411
- * ```typescript
412
- * const product = await client.content('product')
413
- * .findById('prod_123')
414
- * .allLocales()
415
- * .execute()
416
- *
417
- * // Access: product.title (default locale)
418
- * // Access: product._metadata.localizations['es-ES'].title
419
- * // Access: product._metadata.availableLocales
420
- * ```
421
- */
422
- allLocales(): this;
423
- /**
424
- * Select specific fields to return (projection)
425
- * @param fieldNames - Array of field names to include
426
- *
427
- * @example
428
- * ```typescript
429
- * .fields(['id', 'title', 'price', 'brand.name'])
430
- * ```
431
- */
432
- fields(fieldNames: string[]): this;
433
- /**
434
- * Include children in results (for hierarchical content)
435
- * @param options - Children options or boolean to enable with defaults
436
- *
437
- * @example
438
- * ```typescript
439
- * // Include all children
440
- * .children(true)
441
- *
442
- * // Include with options
443
- * .children({ contentTypes: ['variant'], limit: 10 })
444
- * ```
445
- */
446
- children(options: ChildrenOptions | boolean): this;
447
- /**
448
- * Set page number for page-based pagination
449
- * @param pageNum - Page number (1-based)
450
- */
451
- page(pageNum: number): this;
452
- /**
453
- * Set page size for page-based pagination
454
- * @param size - Items per page
455
- */
456
- pageSize(size: number): this;
457
- /**
458
- * Execute the query and return all matching results
459
- */
460
- execute(): Promise<T[]>;
461
- /**
462
- * Execute the query and return the whole body — the items under `data`, the
463
- * cursor facts under `page`.
464
- */
465
- executeWithMeta(): Promise<ContentQueryResult<T>>;
466
- /**
467
- * Execute the query and return a single result (first match)
468
- */
469
- first(): Promise<T | null>;
470
- /**
471
- * Get paginated results
472
- */
473
- paginate(limit?: number): Promise<ContentQueryResult<T>>;
474
- /**
475
- * Create an async iterator for streaming results
476
- */
477
- [Symbol.asyncIterator](): AsyncIterableIterator<T>;
478
- /**
479
- * Execute the query and return all results as an array (for large datasets)
480
- */
481
- all(): Promise<T[]>;
482
- /**
483
- * Clone the query builder
484
- */
485
- clone(): ContentQueryBuilder<T, F, R, I>;
486
- /**
487
- * Get the current query options
488
- */
489
- getQueryOptions(): QueryOptions<T, F>;
490
- /**
491
- * Execute the query and return the whole body.
492
- *
493
- * Identical to {@link executeWithMeta} now that the list serves one shape;
494
- * kept as the name callers already import.
495
- */
496
- executeWithMetaExtended(): Promise<ExtendedQueryResult<T>>;
497
- /**
498
- * `count()` and `exists()` are gone, and their absence is the honest answer
499
- * rather than an omission (`INV-RD-CAPABILITY-IS-HONEST`).
500
- *
501
- * `count()` sent `withCount` and read `pagination.total`; API2 moved the
502
- * customer list onto the **cursor** dialect, whose page facts are
503
- * `{ nextCursor, hasMore }` and nothing else, so no total reaches this
504
- * surface at all. Keeping the method would have meant either a request that
505
- * always throws or a number assembled from a page the caller happened to
506
- * fetch — a count of what was returned, presented as a count of what matched
507
- * (rule 6). The plan's §3.3 listed `count` among six methods a type would
508
- * repair; what the type actually shows is that the capability is no longer
509
- * there. A caller that wants "is there at least one" fetches one and looks at
510
- * `data.length`; a caller that wants a true total uses `POST /v1/search`,
511
- * which is the offset dialect precisely because a facet-bearing result wants
512
- * one (Decision 5).
513
- */
514
- /**
515
- * Build query parameters for the HTTP request
516
- */
517
- private buildQueryParams;
518
- }
519
- /**
520
- * Specialized query builder for finding by ID
521
- */
522
- declare class ContentByIdBuilder<T extends BaseContent = BaseContent, R extends Reading = UntypedReading<T>, I extends string = never> {
523
- private readonly httpClient;
524
- private readonly contentType;
525
- private readonly id;
526
- private includeRelations?;
527
- private localeValue?;
528
- private localesArray?;
529
- private fetchAllLocales;
530
- private childrenOptions?;
531
- private requestHeaders?;
532
- /** The client's locale, asked when the read is sent rather than copied when it was built. */
533
- private readonly clientLocale;
534
- private freshValue?;
535
- /**
536
- * @param locale - The client's locale (`withLocale()`, `updateConfig({ locale })`), sent when the
537
- * read names none: the function `TypedRelyIOClient` hands over, or a locale fixed for it
538
- */
539
- constructor(httpClient: HttpClient, contentType: string, id: string, locale?: ClientLocale);
540
- /**
541
- * The feed position this read follows — a change's `fresh` (`FeedChange`), or a resync's. Sent as
542
- * `?_fresh`, the one term the API declares for it: no cache — the Content API's edge or the
543
- * browser's own — holds an answer for that position from before it, so the read is answered after
544
- * the change, and every read naming the same position shares that answer. The answer is the
545
- * read's own; omit it for a read no change prompted.
546
- */
547
- fresh(position: string): this;
548
- /**
549
- * Expand references into the entries they point at: one include path per element (REL-1801).
550
- * A path names fields through lists, page builders, objects and references —
551
- * `'sections.features'`, `'sections.customers.logo'` — and may pick the fields each entry returns,
552
- * `'sections.features(name,slug)'`. Each is checked against the schema: a path that holds no
553
- * reference does not compile, and the read's type follows the paths — an expanded reference is the
554
- * entry, every other one its id. A second call adds to the first.
555
- */
556
- include<const P extends readonly string[]>(paths: CheckedIncludes<R, P>): ContentByIdBuilder<Extract<ReadWith<R, I | P[number]>, BaseContent>, R, I | P[number]>;
557
- /**
558
- * Set locale for localized content
559
- * @param locale - Locale code (e.g., 'en-US', 'de-DE', 'fr-FR')
560
- */
561
- locale(locale: string): this;
562
- /**
563
- * Fetch content in multiple specific locales
564
- * Returns primary locale at top level, others in _metadata.localizations
565
- *
566
- * @param localeCodes - Array of locale codes to fetch
567
- */
568
- locales(localeCodes: string[]): this;
569
- /**
570
- * Headers this one read adds to the client's own, e.g. `X-RelyIO-Dataset` to read another dataset
571
- * the key can see. A name the client already sends is replaced for this request only; the client
572
- * is not changed.
573
- */
574
- headers(headers: Record<string, string>): this;
575
- /**
576
- * Fetch content in all available locales
577
- * Returns default locale at top level, others in _metadata.localizations
578
- */
579
- allLocales(): this;
580
- /**
581
- * Include children in results (for hierarchical content)
582
- * @param options - Children options or boolean to enable with defaults
583
- */
584
- children(options: ChildrenOptions | boolean): this;
585
- /**
586
- * Execute the query and return the content item
587
- */
588
- execute(): Promise<T | null>;
589
- }
590
-
591
- /**
592
- * API4's two request headers, exposed the way the SDK's ergonomics already
593
- * expose everything else: as options on the call, not as raw headers.
594
- *
595
- * `apps/services/api` declares them on exactly six and seven routes
596
- * respectively (`CONDITIONAL_WRITE_HEADERS` / `RETRYABLE_WRITE_HEADERS` in that
597
- * service's `contract/routes.ts`), and the API4 closeout is explicit that the
598
- * ten `/file/*` routes and `POST /batch` declare **neither**, because they
599
- * write through `@relyio/asset-files` and the batch planner and would silently
600
- * ignore the header. So these options are on the six and seven methods that
601
- * reach them and nowhere else: a client that could pass `ifMatch` to a route
602
- * that ignores it has been told a lie no gate can catch
603
- * (`INV-RD-CAPABILITY-IS-HONEST`).
604
- *
605
- * The tag itself comes from `X-Entity-Tag`, which the single-entity reads serve
606
- * and the list routes deliberately do not — one header cannot name every
607
- * member's per-locale version. `entityTagOf` reads it off the response the SDK
608
- * already returns, so the round trip is "read one, write it back" with no
609
- * assembly from `_metadata` in between
610
- * (`INV-API-CONDITIONAL-WRITE-IS-REACHABLE`).
611
- */
612
- interface RetryableWriteOptions {
613
- /**
614
- * A key that makes a retry of **this** write safe.
615
- *
616
- * A replay inside the service's window returns the original outcome rather
617
- * than applying twice. The service derives that response from committed state
618
- * pinned to the recorded version — it does not store bytes — so a replay after
619
- * the version has been pruned is refused `RESOURCE_EXPIRED` rather than
620
- * answered from a stale copy.
621
- *
622
- * **The SDK mints one when you do not.** It used to be caller-supplied and
623
- * nothing else, which meant the default write carried no key while the retry
624
- * loop replayed it anyway — the one mechanism that makes a replay safe was
625
- * optional on the one path that needed it. Supply your own to make a key
626
- * outlive the process: a job that may be restarted wants a key derived from
627
- * the work, not one minted per call.
628
- */
629
- idempotencyKey?: string;
630
- }
631
- /**
632
- * The six routes that additionally take `If-Match`.
633
- *
634
- * A creation has no prior entity to precondition on, so `POST /v1/{contentType}`
635
- * takes `Idempotency-Key` and not this — which is why the two option types are
636
- * separate rather than one optional-everything bag.
637
- */
638
- interface ConditionalWriteOptions extends RetryableWriteOptions {
639
- /**
640
- * The `X-Entity-Tag` value a prior read served, sent back as `If-Match`.
641
- *
642
- * The write applies only if that locale of that entry is still at the version
643
- * the tag names; otherwise the service refuses `RESOURCE_CONFLICT` and names
644
- * both versions.
645
- */
646
- ifMatch?: string;
647
- }
648
- /**
649
- * The entity tag a single-entity read served, or `undefined` where the route
650
- * serves none — a list, or a cache HIT, which cannot know the current version
651
- * and honestly says nothing rather than serving a stale tag.
652
- */
653
- declare function entityTagOf(response: {
654
- headers: Record<string, string>;
655
- }): string | undefined;
656
-
657
- /**
658
- * Content fetch options
659
- */
660
- interface ContentFetchOptions<T extends BaseContent = BaseContent> {
661
- /** Filter conditions */
662
- where?: WhereClause<T>;
663
- /** Maximum entries to return */
664
- limit?: number;
665
- /** Entries to skip (offset pagination) */
666
- offset?: number;
667
- /** Sort configuration */
668
- sort?: Record<string, 'asc' | 'desc'>;
669
- /**
670
- * Include paths: the references to expand (REL-1801) — see `ContentQueryBuilder.include`. Typed
671
- * where `fetch` is called with them inline.
672
- */
673
- include?: readonly string[];
674
- /** Locale for localized content */
675
- locale?: string;
676
- /** Fields to select (projection) */
677
- fields?: string[];
678
- /** Children population options */
679
- children?: ChildrenOptions | boolean;
680
- }
681
- /**
682
- * Content client for managing content operations
683
- * Provides CRUD operations and query building for a specific content type
684
- *
685
- * @example
686
- * ```typescript
687
- * const products = client.content('product')
688
- *
689
- * // Query with fluent API
690
- * const active = await products.query()
691
- * .where({ status: 'active' })
692
- * .locale('en')
693
- * .include(['brand'])
694
- * .limit(20)
695
- * .execute()
696
- *
697
- * // Quick fetch
698
- * const all = await products.fetchAll()
699
- * ```
700
- */
701
- declare class ContentClient<T extends BaseContent = BaseContent, R extends Reading = UntypedReading<T>> {
702
- private readonly httpClient;
703
- private readonly contentType;
704
- /** The client's locale, asked on every call rather than copied when this client was taken. */
705
- private readonly clientLocale;
706
- /**
707
- * @param locale - The client's locale: the function `TypedRelyIOClient` hands over, asked on
708
- * every call, or a locale fixed for this client
709
- */
710
- constructor(httpClient: HttpClient, contentType: string, locale?: ClientLocale);
711
- /**
712
- * Create a new query builder for this content type
713
- */
714
- query(): ContentQueryBuilder<T, T, R>;
715
- /**
716
- * Find content by ID with fluent options
717
- */
718
- findById(id: string): ContentByIdBuilder<T, R>;
719
- /**
720
- * Fetch all content items
721
- * @param locale - Optional locale for localized content
722
- */
723
- fetchAll(locale?: string): Promise<T[]>;
724
- /**
725
- * Fetch content with query options
726
- */
727
- fetch<const P extends readonly string[]>(options: Omit<ContentFetchOptions<T>, 'include'> & {
728
- include: CheckedIncludes<R, P>;
729
- }): Promise<Array<ReadWith<R, P[number]>>>;
730
- fetch(options?: Omit<ContentFetchOptions<T>, 'include'>): Promise<T[]>;
731
- /**
732
- * Fetch a single content item by ID
733
- * @param id - Entry ID or slug
734
- * @param options - Fetch options (include, locale, and `headers` this one request adds — e.g.
735
- * `{ 'X-RelyIO-Dataset': 'staging' }` to read a dataset other than the client's)
736
- */
737
- fetchOne<const P extends readonly string[]>(id: string, options: {
738
- include: CheckedIncludes<R, P>;
739
- locale?: string;
740
- headers?: Record<string, string>;
741
- }): Promise<ReadWith<R, P[number]> | null>;
742
- fetchOne(id: string, options?: {
743
- locale?: string;
744
- headers?: Record<string, string>;
745
- }): Promise<T | null>;
746
- /**
747
- * Fetch one entry **and the entity tag that read served**.
748
- *
749
- * This is the read half of a conditional write. `X-Entity-Tag` is minted from
750
- * `(entryUid, locale, localeVersion)` and is exactly what `If-Match` takes,
751
- * so a caller reads here, edits, and hands the same string back to `update`
752
- * — with nothing to assemble in between
753
- * (`INV-API-CONDITIONAL-WRITE-IS-REACHABLE`).
754
- *
755
- * `entityTag` is `undefined` when the response came from the cache: a cached
756
- * body cannot know the current version, and an absent tag beats a stale one
757
- * that would refuse the next write.
758
- *
759
- * Unlike {@link fetchOne}, a missing entry is not `null`: the service's
760
- * `RESOURCE_NOT_FOUND` is thrown as it arrived, with the `kind` that says
761
- * whether the entry, the content type or the schema was missing. `headers`
762
- * are the ones this read adds, as for `fetchOne`.
763
- */
764
- fetchOneWithTag<const P extends readonly string[]>(id: string, options: {
765
- include: CheckedIncludes<R, P>;
766
- locale?: string;
767
- headers?: Record<string, string>;
768
- }): Promise<{
769
- entry: ReadWith<R, P[number]>;
770
- entityTag?: string;
771
- }>;
772
- fetchOneWithTag(id: string, options?: {
773
- locale?: string;
774
- headers?: Record<string, string>;
775
- }): Promise<{
776
- entry: T;
777
- entityTag?: string;
778
- }>;
779
- /**
780
- * Fetch the first content item matching the conditions
781
- * @param where - Filter conditions
782
- * @param locale - Optional locale
783
- */
784
- fetchFirst(where?: WhereClause<T>, locale?: string): Promise<T | null>;
785
- /**
786
- * Create a new content item
787
- * @param data - Content data
788
- * @param options - Create options (slug, locale, parentId, sortOrder), and `headers` this one
789
- * request adds — e.g. `{ 'X-RelyIO-Dataset': 'staging' }` to write a dataset other than the
790
- * client's. They are sent, never put in the body; `idempotencyKey` still wins over an
791
- * `Idempotency-Key` among them. `locale` defaults to the client's own (`withLocale()`).
792
- */
793
- create(data: CreateInput<T>, options?: {
794
- slug?: string;
795
- locale?: string;
796
- parentId?: string;
797
- sortOrder?: number;
798
- headers?: Record<string, string>;
799
- } & RetryableWriteOptions): Promise<T>;
800
- /**
801
- * Update a content item by ID (partial update)
802
- * @param id - Entry ID
803
- * @param data - Fields to update
804
- * @param options - Update options (slug, locale, parentId, sortOrder), and `headers` this one
805
- * request adds, as for {@link create}. `ifMatch` and `idempotencyKey` win over the same
806
- * header among them. `locale` defaults to the client's own, the locale whose entity tag
807
- * {@link fetchOneWithTag} reads, so the tag it hands back is checked against the same locale.
808
- */
809
- update(id: string, data: UpdateInput<T>, options?: {
810
- slug?: string;
811
- locale?: string;
812
- parentId?: string;
813
- sortOrder?: number;
814
- headers?: Record<string, string>;
815
- } & ConditionalWriteOptions): Promise<T>;
816
- /**
817
- * Delete a content item by ID
818
- */
819
- delete(id: string, options?: ConditionalWriteOptions): Promise<void>;
820
- /**
821
- * Check if content exists by ID
822
- */
823
- exists(id: string): Promise<boolean>;
824
- /**
825
- * Get version history for one locale of an entry.
826
- *
827
- * Versions are numbered per locale, so this is one locale's timeline: `locale` defaults to the
828
- * client's own (`withLocale()`), else the schema default; no fallback is applied, and a locale
829
- * with no history returns an empty array rather than another locale's rows. Every row names the
830
- * locale it belongs to.
831
- */
832
- versions(id: string, options?: {
833
- limit?: number;
834
- locale?: string;
835
- }): Promise<Array<T & {
836
- versionNumber: number;
837
- locale: string;
838
- }>>;
839
- /**
840
- * Get a specific version of an entry.
841
- *
842
- * `locale` defaults to the client's own (`withLocale()`), else the schema default. Version
843
- * numbers are per locale, so version 2 is different content in each one.
844
- */
845
- version(id: string, versionNumber: number, options?: {
846
- locale?: string;
847
- }): Promise<T | null>;
848
- /**
849
- * Revert one locale of an entry to a previous version.
850
- *
851
- * `locale` is **required**, and that is not a convention: versions are numbered per locale, so
852
- * `versionNumber` alone is ambiguous, and defaulting it would silently revert the wrong
853
- * translation. Only that locale's fields change — shared fields and every other locale are left
854
- * untouched.
855
- *
856
- * The restore is validated as strictly as a publish, so it cannot publish invalid content, and it
857
- * records a new version of its own rather than rewinding the counter — so the revert is auditable
858
- * and itself reversible.
859
- */
860
- restore(id: string, versionNumber: number, locale: string, options?: ConditionalWriteOptions): Promise<T>;
861
- /**
862
- * Get children of a hierarchical entry
863
- * @param id - Parent entry ID
864
- * @param options - Fetch options
865
- */
866
- children(id: string, options?: {
867
- depth?: number;
868
- include?: string[];
869
- locale?: string;
870
- }): Promise<T[]>;
871
- /**
872
- * Get the content type name
873
- */
874
- getContentType(): string;
875
- /**
876
- * Create a new ContentClient for a different content type
877
- */
878
- ofType<U extends BaseContent = BaseContent>(contentType: string): ContentClient<U>;
879
- /**
880
- * Create a new ContentClient with a different default locale, fixed: an update of the client's
881
- * locale (`updateConfig({ locale })`) does not reach it
882
- */
883
- withLocale(locale: string): ContentClient<T, R>;
884
- /**
885
- * Get the default locale as it is now: the client's, or the one `withLocale()` fixed
886
- */
887
- getDefaultLocale(): string | undefined;
888
- }
889
-
890
- /**
891
- * Upload/processing error taxonomy (plan 17, 00b Step 4).
892
- *
893
- * A **thin customer-side mirror** of the live-contracts `FileErrorCode`
894
- * (correction C6 — the SDK must not import the server/Studio contracts package).
895
- * Keep the members in sync with `@relyio/live-contracts`.
896
- *
897
- * Written once, as values, because a processing failure's code arrives as a string the service
898
- * stored and has to be recognised at runtime; `FileErrorCode` is read off this list. The module is
899
- * internal — `errors/index.ts` does not re-export it — so the list is not a second public name for
900
- * the type.
901
- */
902
- declare const FILE_ERROR_CODES: readonly ["UPLOAD_TOO_LARGE", "UNSUPPORTED_MIME", "DIMENSION_OUT_OF_RANGE", "ASPECT_RATIO", "CORRUPT_FILE", "UNSUPPORTED_CODEC", "IMAGES_LIMIT_EXCEEDED", "STREAM_ENCODE_FAILED", "SCAN_INFECTED", "INTERNAL"];
903
-
904
- /**
905
- * Upload/processing error taxonomy (plan 17, 00b Step 4): a customer-side mirror of the
906
- * live-contracts `FileErrorCode`, read off the list in `./file-error-codes`.
907
- */
908
- type FileErrorCode = (typeof FILE_ERROR_CODES)[number];
909
- /**
910
- * The code a {@link FileUploadError} carries: a {@link FileErrorCode}, or `PROCESSING_FAILED` for a
911
- * processing failure this SDK cannot name — the service stored no code, as an image or file
912
- * processing failure is stored today, or one this version of the SDK predates. What the service
913
- * stored is in the error's `context.processingError` either way.
914
- */
915
- type FileUploadErrorCode = FileErrorCode | 'PROCESSING_FAILED';
916
- /**
917
- * A failure during the upload protocol (presign / PUT / multipart / confirm) or
918
- * surfaced by the server's processing pipeline. Carries a typed {@link FileUploadErrorCode}
919
- * and a `retriable` hint so callers (and `upload()`'s bounded auto-retry) can
920
- * decide whether to retry on the same entry.
921
- */
922
- declare class FileUploadError extends SDKError {
923
- readonly code: FileUploadErrorCode;
924
- readonly retryable: boolean;
925
- constructor(code: FileUploadErrorCode, message: string, options?: {
926
- retriable?: boolean;
927
- statusCode?: number;
928
- cause?: Error;
929
- context?: Record<string, unknown>;
930
- });
931
- getUserMessage(): string;
932
- }
933
- /**
934
- * Client-side file validation failure (MIME/size/etc.) raised **before** any
935
- * bytes leave the machine (UX only — the server presign stays authoritative).
936
- */
937
- declare class FileValidationError extends BaseValidationError {
938
- readonly code = "FILE_VALIDATION_ERROR";
939
- constructor(message: string, violations?: ValidationViolation[], cause?: Error);
940
- getUserMessage(): string;
941
- }
942
- /** Constraints `validateFile` checks a file against (subset of the schema upload gate). */
943
- interface FileValidationConstraints {
944
- /** Allowed MIME types (exact match). */
945
- allowedMimeTypes?: string[];
946
- /** Max size in bytes. */
947
- maxSizeBytes?: number;
948
- }
949
- /**
950
- * Validate a file against explicit constraints, returning typed violations.
951
- *
952
- * The customer SDK does not carry a `SchemaReader` (that lives in the Studio
953
- * layer), so callers pass the constraints they want enforced client-side; the
954
- * server presign remains the authority. Returns an empty array when valid.
955
- */
956
- declare function validateFile(file: {
957
- type?: string;
958
- size?: number;
959
- }, constraints?: FileValidationConstraints): ValidationViolation[];
960
-
961
- /** Delivery visibility for a file binding (gateway-enforced, no byte relocation). */
962
- type AssetVisibility = 'public' | 'protected' | 'private';
963
- /** Binary lifecycle status — independent of the entry's publishing status. */
964
- type FileStatus = 'pending_upload' | 'uploading' | 'processing' | 'ready' | 'failed';
965
- /** Storage backend: `r2` = image + file kinds, `stream` = video kind. */
966
- type FileBackend = 'r2' | 'stream';
967
- /**
968
- * Customer-facing file binding block on an asset entry: the document's `AssetFile` — status,
969
- * backend, file name, MIME type, size, advisory hash, visibility, extracted metadata, focal point,
970
- * enrichment and scan status, processing stage/progress/error, and video playback readiness.
971
- */
972
- type AssetFileBlock = components['schemas']['AssetFile'];
973
- /** Extracted media metadata (Phase 1 basics + Phase 2 enrichment, plans 15–19); open-ended. */
974
- type AssetFileExtracted = NonNullable<AssetFileBlock['extracted']>;
975
- /**
976
- * Where one file binding's upload got to, as {@link AssetFileBuilder.status} reads it
977
- * (`GET /v1/{assetType}/{id}/file`): the document's `FileStatus` — the binding's lifecycle and
978
- * nothing of the entry's content. `processing.error` is present exactly while `status` is `failed`.
979
- */
980
- type FileStatusResult = components['schemas']['FileStatus'];
981
- /**
982
- * Computed gateway delivery URLs (the reserved `urls` key). Always served from
983
- * `cdn.relyio.net`. Image bindings expose `original` + `variants`; video bindings
984
- * expose `playback`/`poster`/`download`. Present only when `file.status` is
985
- * `ready` — otherwise the response omits `urls` and callers read `file.status`.
986
- */
987
- interface AssetUrls {
988
- /** Image: canonical original delivery URL. */
989
- original?: string;
990
- /** Image: variant id → delivery URL map. */
991
- variants?: Record<string, string>;
992
- /** Video: HLS/DASH manifest URL. */
993
- playback?: string;
994
- /** Video: poster image URL. */
995
- poster?: string;
996
- /** Video: downloadable MP4 URL (when enabled). */
997
- download?: string;
998
- [key: string]: unknown;
999
- }
1000
- /**
1001
- * An asset entry response: schema fields at the top level plus the reserved
1002
- * `file` + `urls` blocks and `_metadata`. `T` carries the inferred field shape.
1003
- */
1004
- type AssetEntryResponse<T extends BaseContent = BaseContent> = T & {
1005
- file?: AssetFileBlock;
1006
- urls?: AssetUrls;
1007
- };
1008
- /** Request a single upload target for the binding. */
1009
- interface PresignInput {
1010
- /** Original file name (required). */
1011
- fileName: string;
1012
- /** MIME type the client will upload with. */
1013
- contentType?: string;
1014
- /** File size in bytes (early upload-gate validation). */
1015
- size?: number;
1016
- /** Locale to target for file-localized asset types. */
1017
- locale?: string;
1018
- /** Advisory client-computed SHA-256 (duplicate detection). */
1019
- hash?: string;
1020
- }
1021
- /** Single upload target returned by `presign`. */
1022
- interface PresignResult {
1023
- backend: FileBackend;
1024
- /** `put` = single PUT / Stream POST; `multipart` = resumable R2 session (plan 17). */
1025
- mode?: 'put' | 'multipart';
1026
- /** R2 presigned PUT (image/file) or Stream direct-creator URL (video). */
1027
- uploadUrl?: string;
1028
- method: 'PUT' | 'POST';
1029
- headers?: Record<string, string>;
1030
- /** Object key for the binding — present for R2 (needed to drive multipart). */
1031
- objectKey?: string;
1032
- storageVersion?: number;
1033
- /** Multipart upload id — present only for `mode: 'multipart'`. */
1034
- uploadId?: string;
1035
- /** Multipart part size in bytes — present only for `mode: 'multipart'`. */
1036
- partSize?: number;
1037
- expiresAt: string;
1038
- locale?: string;
1039
- }
1040
- /** Result of opening a multipart session (`file(uid).createMultipart()`). */
1041
- interface MultipartCreateResult {
1042
- uploadId: string;
1043
- objectKey: string;
1044
- partSize: number;
1045
- storageVersion: number;
1046
- locale?: string;
1047
- }
1048
- /** A single uploaded part (`{ PartNumber, ETag }` ordering for `complete`). */
1049
- interface MultipartPart {
1050
- partNumber: number;
1051
- etag: string;
1052
- size?: number;
1053
- }
1054
- /** Target a multipart session in subsequent ops. */
1055
- interface MultipartTarget {
1056
- uploadId: string;
1057
- objectKey: string;
1058
- locale?: string;
1059
- }
1060
- /** Upload progress tick handed to `.onProgress()`. */
1061
- interface UploadProgress {
1062
- /** `upload` for client → storage bytes. */
1063
- phase: 'upload';
1064
- /** Bytes transferred so far. */
1065
- loaded: number;
1066
- /** Total bytes (when known). */
1067
- total: number;
1068
- /** 0–100 integer. */
1069
- progress: number;
1070
- }
1071
- /** Acknowledge a completed client upload. */
1072
- interface ConfirmInput {
1073
- locale?: string;
1074
- hash?: string;
1075
- }
1076
- /** Result of `confirm`. */
1077
- interface ConfirmResult {
1078
- accepted: boolean;
1079
- }
1080
- /** Resolve a single gateway delivery URL. */
1081
- interface ResolveUrlInput {
1082
- locale?: string;
1083
- /** Named image variant id. */
1084
- variantId?: string;
1085
- /** Video delivery target. */
1086
- purpose?: 'playback' | 'poster' | 'download';
1087
- }
1088
- /** Result of `url`. `expiresAt` is omitted for `public` bindings. */
1089
- interface ResolveUrlResult {
1090
- url: string;
1091
- expiresAt?: string;
1092
- }
1093
- /** Options when creating an asset entry. */
1094
- interface CreateAssetOptions {
1095
- slug?: string;
1096
- locale?: string;
1097
- /** Original file name (also settable at presign). */
1098
- fileName?: string;
1099
- /** Initial delivery visibility (default `public`). */
1100
- visibility?: AssetVisibility;
1101
- /** Auto-publish the entry when the file becomes ready (default `true`). */
1102
- publish?: boolean;
1103
- }
1104
- /** Options for {@link AssetClient.update}: the entry route takes both API4 headers. */
1105
- type UpdateAssetOptions = {
1106
- slug?: string;
1107
- locale?: string;
1108
- } & ConditionalWriteOptions;
1109
- /** Options for {@link AssetFileBuilder.waitUntilReady}. */
1110
- interface WaitUntilReadyOptions {
1111
- /** Locale of the binding to watch. */
1112
- locale?: string;
1113
- /**
1114
- * Total time to wait before giving up (ms), from 0 to 2147483647. Default 60000. `0` reads the
1115
- * file once and rejects with a `TimeoutError` if it is not ready.
1116
- */
1117
- timeoutMs?: number;
1118
- /** Delay between polls (ms), from 1 to 2147483647. Default 1500. */
1119
- pollIntervalMs?: number;
1120
- }
1121
- /**
1122
- * Operations on a single asset entry's file binding, backed by the
1123
- * `/{assetType}/{entryUid}/file/*` routes (plan 06 §2).
1124
- */
1125
- declare class AssetFileBuilder<T extends BaseContent = BaseContent> {
1126
- private readonly httpClient;
1127
- private readonly assetType;
1128
- private readonly entryUid;
1129
- /** The client's locale, asked on every call rather than copied when this builder was taken. */
1130
- private readonly clientLocale;
1131
- /**
1132
- * @param locale - The client's locale, sent when a call names none: the function
1133
- * `TypedRelyIOClient` hands over, or a locale fixed for this builder
1134
- */
1135
- constructor(httpClient: HttpClient, assetType: string, entryUid: string, locale?: ClientLocale);
1136
- /**
1137
- * The path parameters every `/file/*` route on this builder takes.
1138
- *
1139
- * The path *template* is the document's; only these two values are the
1140
- * builder's, so a route that gains or loses a segment is a compile error here
1141
- * rather than a hand-built string that silently misses.
1142
- */
1143
- private get target();
1144
- private withLocale;
1145
- /**
1146
- * The `locale` body field of a multipart call: the session's, else the client's. The API finds
1147
- * the binding by it, so a session a `withLocale()` client opened must name the same locale on
1148
- * every later call or its `objectKey` is refused as another binding's.
1149
- */
1150
- private localeField;
1151
- /**
1152
- * Issue a single upload target — an R2 presigned PUT (image/file) or a
1153
- * Cloudflare Stream direct-creator URL (video). Resumable multipart is plan 17.
1154
- */
1155
- presign(input: PresignInput): Promise<PresignResult>;
1156
- /**
1157
- * Acknowledge a completed client upload (kicks off the fast-path intake for
1158
- * R2; a no-op for Stream, whose readiness is webhook-driven).
1159
- */
1160
- confirm(input?: ConfirmInput): Promise<ConfirmResult>;
1161
- /** Open an R2 multipart session for a large file (standalone of `presign`). */
1162
- createMultipart(input: PresignInput): Promise<MultipartCreateResult>;
1163
- /** Presign a single part PUT (1-based `partNumber`). */
1164
- signPart(target: MultipartTarget & {
1165
- partNumber: number;
1166
- }): Promise<{
1167
- url: string;
1168
- }>;
1169
- /** List parts already uploaded (cross-session resume). */
1170
- listParts(target: MultipartTarget): Promise<MultipartPart[]>;
1171
- /** Finalize a multipart session. */
1172
- completeMultipart(target: MultipartTarget & {
1173
- parts: MultipartPart[];
1174
- }): Promise<{
1175
- ok: boolean;
1176
- etag?: string;
1177
- }>;
1178
- /** Abort a multipart session, freeing staged parts. */
1179
- abortMultipart(target: MultipartTarget): Promise<{
1180
- aborted: boolean;
1181
- }>;
1182
- /**
1183
- * Resolve a single gateway delivery URL for the requested locale/variant/purpose.
1184
- * Throws if the binding is not `ready`.
1185
- */
1186
- url(input?: ResolveUrlInput): Promise<ResolveUrlResult>;
1187
- /**
1188
- * Read where this file's upload got to — `status`, and the processing stage, progress and error
1189
- * the pipeline recorded — whatever the asset entry is published in.
1190
- *
1191
- * An asset is a draft until its file is ready, and stays one when its file fails, so a key that
1192
- * reads published content finds no entry for an upload in flight or one that failed. This read
1193
- * answers for the binding itself. It takes the capability the upload calls take, so a read-only
1194
- * key is refused with `ReadOnlyKeyError`, and it never returns the entry's fields.
1195
- */
1196
- status(options?: {
1197
- locale?: string;
1198
- }): Promise<FileStatusResult>;
1199
- /**
1200
- * Update the binding's delivery visibility (token-based; no byte relocation —
1201
- * a downgrade purges the gateway cache). Returns the refreshed asset entry.
1202
- */
1203
- updateVisibility(visibility: AssetVisibility, options?: {
1204
- locale?: string;
1205
- }): Promise<AssetEntryResponse<T>>;
1206
- /**
1207
- * Set the binding's normalized (0..1, origin top-left) smart-crop focal point
1208
- * (plan 18) so `cover`/`crop` variants frame the subject. Per locale for
1209
- * file-localized asset types; token-based (no byte change). Returns the
1210
- * refreshed asset entry.
1211
- */
1212
- setFocalPoint(focalPoint: {
1213
- x: number;
1214
- y: number;
1215
- }, options?: {
1216
- locale?: string;
1217
- }): Promise<AssetEntryResponse<T>>;
1218
- /**
1219
- * Remove the file binding, clean up the underlying bytes, and purge the
1220
- * gateway cache. The asset entry itself is preserved.
1221
- */
1222
- delete(options?: {
1223
- locale?: string;
1224
- }): Promise<void>;
1225
- /**
1226
- * Poll the asset entry until its file binding reaches `ready`, and resolve with the ready entry.
1227
- *
1228
- * Each poll reads the entry through the key's lens. When that finds nothing — a key that reads
1229
- * published content, and an asset that is a draft until its file is ready and stays one when it
1230
- * fails — the poll reads the binding itself ({@link status}), so a failed upload rejects as one
1231
- * rather than running out the limit (REL-1481).
1232
- *
1233
- * @throws {FileUploadError} When processing failed. `code` is the processing code the service
1234
- * stored — `UNSUPPORTED_CODEC`, `CORRUPT_FILE` and so on — or `PROCESSING_FAILED` where it
1235
- * stored none this SDK knows; `context.processingError` holds what it stored, and `retryable`
1236
- * is its `retriable` hint.
1237
- * @throws {TimeoutError} When `timeoutMs` passes first. The file may still become ready; the
1238
- * error's `context.timeout` is the limit and `context.fileStatus` the status last seen. A file
1239
- * that is `ready` in an asset the key's lens never shows — created with `publish: false`, or
1240
- * awaiting a workflow — ends here too, and the message says so.
1241
- * @throws {ReadOnlyKeyError} When the key's lens does not show the asset and the key may not read
1242
- * its binding either: nothing it can read would ever report the file.
1243
- */
1244
- waitUntilReady(options?: WaitUntilReadyOptions): Promise<AssetEntryResponse<T>>;
1245
- }
1246
- /**
1247
- * High-level one-shot upload: create draft entry → presign → PUT bytes →
1248
- * confirm → (optionally) wait for `ready`. Handles the common image/file flow
1249
- * (single R2 PUT). For advanced/resumable flows (large files, video tus,
1250
- * progress) use the lower-level `file(uid)` builder — full upload resilience is
1251
- * plan 17.
1252
- */
1253
- declare class AssetUploadBuilder<T extends BaseContent = BaseContent> {
1254
- private readonly httpClient;
1255
- private readonly assetType;
1256
- private readonly file;
1257
- private _fileName?;
1258
- private _visibility?;
1259
- private _locale?;
1260
- private _publish?;
1261
- private _content;
1262
- private _slug?;
1263
- private _waitForReady;
1264
- private _waitOptions;
1265
- private _onProgress?;
1266
- private _validate?;
1267
- private _maxPartRetries;
1268
- /** The client's locale, read once when the upload starts. */
1269
- private readonly clientLocale;
1270
- /**
1271
- * @param locale - The client's locale, the upload's when `.locale()` names none: the function
1272
- * `TypedRelyIOClient` hands over, or a locale fixed for this upload
1273
- */
1274
- constructor(httpClient: HttpClient, assetType: string, file: Blob, locale?: ClientLocale);
1275
- /** Subscribe to client → storage upload progress (single PUT + multipart). */
1276
- onProgress(callback: (_progress: UploadProgress) => void): this;
1277
- /**
1278
- * Validate the file client-side (UX only) against explicit constraints before
1279
- * any bytes leave the machine. Rejects instantly with a {@link FileValidationError}.
1280
- */
1281
- validate(constraints: FileValidationConstraints): this;
1282
- /** Bounded per-part retry attempts for multipart (default 3). */
1283
- maxPartRetries(attempts: number): this;
1284
- /** Set the original file name (defaults to `file.name` for `File` inputs). */
1285
- filename(name: string): this;
1286
- /** Set the initial delivery visibility (default `public`). */
1287
- visibility(visibility: AssetVisibility): this;
1288
- /** Target locale for file-localized asset types. */
1289
- locale(locale: string): this;
1290
- /** Suppress (or enable) auto-publish on ready. */
1291
- publish(publish: boolean): this;
1292
- /** Set the asset's metadata field values (e.g. `{ alt: '...' }`). */
1293
- content(content: Record<string, unknown>): this;
1294
- /** Set an explicit slug for the new entry. */
1295
- slug(slug: string): this;
1296
- /**
1297
- * Wait until the file binding becomes `ready` before resolving.
1298
- *
1299
- * @throws {Error} When `timeoutMs` or `pollIntervalMs` is one the wait cannot honour — refused
1300
- * here, before anything is uploaded, rather than after the bytes are in.
1301
- */
1302
- waitForReady(options?: boolean | Omit<WaitUntilReadyOptions, 'locale'>): this;
1303
- /** Execute the full upload flow and resolve with the asset entry. */
1304
- execute(): Promise<AssetEntryResponse<T>>;
1305
- /** Single presigned PUT (≤ 50 MB / Stream POST). */
1306
- private uploadSingle;
1307
- /**
1308
- * Resumable R2 multipart: slice into `partSize` chunks, skip already-uploaded
1309
- * parts (resume via `list-parts`), PUT each remaining part with bounded retry,
1310
- * then `complete`. Aborts the session on unrecoverable failure.
1311
- */
1312
- private uploadMultipart;
1313
- /** PUT a single part, retrying transient failures up to `_maxPartRetries`. */
1314
- private putPartWithRetry;
1315
- private emitProgress;
1316
- }
1317
- /**
1318
- * Client for a single asset type. Mirrors {@link ContentClient} (CRUD + query
1319
- * over the same dynamic `/{assetType}` routes — assets are `entryType: 'asset'`
1320
- * entries) and adds the `file(uid)` sub-resource builder + an `upload()` helper.
1321
- */
1322
- /**
1323
- * An asset read typed by the asset type's reading `R`: what the type's include paths make of its
1324
- * fields, beside the `file` and `urls` blocks every asset read carries (REL-1801).
1325
- */
1326
- interface AssetResponseReading<R extends Reading> extends Reading {
1327
- readonly read: AssetEntryResponse<Extract<ReadWith<R, this['include']>, BaseContent>>;
1328
- readonly checked: CheckedInclude<R, this['path']>;
1329
- }
1330
- declare class AssetClient<T extends BaseContent = BaseContent, R extends Reading = UntypedReading<T>> {
1331
- private readonly httpClient;
1332
- private readonly assetType;
1333
- /** The client's locale, asked on every call rather than copied when this client was taken. */
1334
- private readonly clientLocale;
1335
- /**
1336
- * @param locale - The client's locale: the function `TypedRelyIOClient` hands over, asked on
1337
- * every call, or a locale fixed for this client
1338
- */
1339
- constructor(httpClient: HttpClient, assetType: string, locale?: ClientLocale);
1340
- /**
1341
- * Create a query builder for this asset type. It resolves asset reads; its conditions name the
1342
- * asset type's fields, which the `file` and `urls` blocks are not.
1343
- */
1344
- query(): ContentQueryBuilder<AssetEntryResponse<T>, T, AssetResponseReading<R>>;
1345
- /** Find an asset entry by id (or slug) with fluent options. */
1346
- findById(id: string): ContentByIdBuilder<AssetEntryResponse<T>, AssetResponseReading<R>>;
1347
- /** Fetch a single asset entry by id (or slug), or `null` if not found. */
1348
- fetchOne<const P extends readonly string[]>(id: string, options: {
1349
- include: CheckedIncludes<R, P>;
1350
- locale?: string;
1351
- }): Promise<ReadWith<AssetResponseReading<R>, P[number]> | null>;
1352
- fetchOne(id: string, options?: {
1353
- locale?: string;
1354
- }): Promise<AssetEntryResponse<T> | null>;
1355
- /** Fetch the first asset entry matching the conditions, or `null`. */
1356
- fetchFirst(where?: WhereClause<T>, locale?: string): Promise<AssetEntryResponse<T> | null>;
1357
- /**
1358
- * Create a new asset entry as a draft. The binary is uploaded separately via
1359
- * `file(uid)` (or use the high-level `upload()` helper), and the entry
1360
- * auto-publishes when its file becomes ready (unless `publish: false`).
1361
- * `locale` defaults to the client's own (`withLocale()`).
1362
- */
1363
- create(data: CreateInput<T>, options?: CreateAssetOptions & RetryableWriteOptions): Promise<AssetEntryResponse<T>>;
1364
- /**
1365
- * Update an asset entry's metadata fields (and/or slug). `locale` defaults to the client's own
1366
- * (`withLocale()`).
1367
- */
1368
- update(id: string, data: UpdateInput<T>, options?: UpdateAssetOptions): Promise<AssetEntryResponse<T>>;
1369
- /** Delete an asset entry (cascades file-byte cleanup + gateway purge). */
1370
- delete(id: string, options?: ConditionalWriteOptions): Promise<void>;
1371
- /** Get the file sub-resource builder for a specific asset entry. */
1372
- file(entryUid: string): AssetFileBuilder<T>;
1373
- /** Start a high-level upload (create → presign → PUT → confirm). */
1374
- upload(file: Blob): AssetUploadBuilder<T>;
1375
- /** Get the asset type id. */
1376
- getAssetType(): string;
1377
- /** Get the default locale as it is now: the client's, or the one `withLocale()` fixed. */
1378
- getDefaultLocale(): string | undefined;
1379
- /**
1380
- * Create a new AssetClient for this type with a different default locale, fixed: an update of the
1381
- * client's locale (`updateConfig({ locale })`) does not reach it.
1382
- */
1383
- withLocale(locale: string): AssetClient<T>;
1384
- }
1385
- /**
1386
- * Narrow an entry (e.g. a populated reference from `include`) to an asset entry.
1387
- * Relies on the `_metadata.entryType` discriminator the API emits (plan 06).
1388
- *
1389
- * @example
1390
- * ```typescript
1391
- * const article = await relyio.content('article').fetchOne(id, { include: ['hero'] })
1392
- * if (isAssetEntry(article?.hero)) {
1393
- * console.log(article.hero.urls?.original)
1394
- * }
1395
- * ```
1396
- */
1397
- declare function isAssetEntry<T extends BaseContent = BaseContent>(entry: unknown): entry is AssetEntryResponse<T>;
1398
- /**
1399
- * A reference field populated (via `include`) with a full asset entry. Identical
1400
- * to {@link AssetEntryResponse}; named for readability at reference call sites.
1401
- */
1402
- type PopulatedAssetReference<T extends BaseContent = BaseContent> = AssetEntryResponse<T>;
1403
-
1404
- /**
1405
- * Singleton fetch options
1406
- */
1407
- interface SingletonFetchOptions {
1408
- /** Locale for localized content */
1409
- locale?: string;
1410
- /**
1411
- * Include paths: the references to expand (REL-1801), added to the ones `include()` named — see
1412
- * `include()`. Typed where the call names them inline.
1413
- */
1414
- include?: readonly string[];
1415
- /**
1416
- * The feed position this read follows — a change's `fresh` (`FeedChange`), or a resync's. Sent as
1417
- * `?_fresh`: no cache holds an answer for it from before that position, so the read is answered
1418
- * after the change. The answer is the read's own; omit it for a read no change prompted.
1419
- */
1420
- fresh?: string;
1421
- }
1422
- /**
1423
- * Singleton client for managing singleton content operations
1424
- * Provides a fluent API for singleton operations following the same pattern as ContentClient
1425
- *
1426
- * Every call is in the locale it names — the argument (`fetch({ locale })`, `update(data, locale)`,
1427
- * `versions({ locale })`), then `.locale()` — and otherwise in the client's own (`withLocale()`,
1428
- * `updateConfig({ locale })`) as it is when the call is made, as `content()`'s are: reads, the write,
1429
- * and version history alike.
1430
- *
1431
- * @example
1432
- * ```typescript
1433
- * const homepage = await client.singleton('homepage')
1434
- * .include(['featuredProducts', 'heroImage'])
1435
- * .locale('en')
1436
- * .fetch()
1437
- * ```
1438
- */
1439
- declare class SingletonClient<T = unknown, R extends Reading = UntypedReading<T>, I extends string = never> {
1440
- private httpClient;
1441
- private singletonKey;
1442
- private includeFields?;
1443
- private localeValue?;
1444
- /** The client's locale, asked on every call rather than copied when this client was taken. */
1445
- private readonly clientLocale;
1446
- /**
1447
- * @param locale - The client's locale: the function `TypedRelyIOClient` hands over, asked on
1448
- * every call, or a locale fixed for this client
1449
- */
1450
- constructor(httpClient: HttpClient, singletonKey: string, locale?: ClientLocale);
1451
- /**
1452
- * Expand references into the entries they point at: one include path per element (REL-1801).
1453
- * A path names fields through lists, objects and references — `'hero.media.image'`,
1454
- * `'featured.author'` — and may pick the fields each entry returns, `'featured(title,slug)'`.
1455
- * Each is checked against the schema: a path that holds no reference does not compile, and the
1456
- * read's type follows the paths — an expanded reference is the entry, every other one its id. A
1457
- * second call adds to the first.
1458
- */
1459
- include<const P extends readonly string[]>(paths: CheckedIncludes<R, P>): SingletonClient<ReadWith<R, I | P[number]>, R, I | P[number]>;
1460
- /**
1461
- * Set locale for localized content
1462
- * @param locale - Locale code (e.g., 'en', 'de', 'fr')
1463
- */
1464
- locale(locale: string): this;
1465
- /**
1466
- * Fetch the singleton content
1467
- * @param options - Optional fetch options (alternative to fluent methods)
1468
- */
1469
- fetch<const P extends readonly string[]>(options: Omit<SingletonFetchOptions, 'include'> & {
1470
- include: CheckedIncludes<R, P>;
1471
- }): Promise<ReadWith<R, I | P[number]> | null>;
1472
- fetch(options?: Omit<SingletonFetchOptions, 'include'>): Promise<T | null>;
1473
- /**
1474
- * Fetch the singleton **and the entity tag that read served**, which is what
1475
- * `update`'s `ifMatch` takes (`INV-API-CONDITIONAL-WRITE-IS-REACHABLE`).
1476
- *
1477
- * `entityTag` is absent on a cache HIT: a cached body cannot know the current
1478
- * version, and a stale tag would refuse the very next write.
1479
- */
1480
- fetchWithTag(options?: SingletonFetchOptions): Promise<{
1481
- content: T;
1482
- entityTag?: string;
1483
- }>;
1484
- /**
1485
- * Update singleton content. The API auto-publishes on PATCH — there is no
1486
- * separate publish route; calling `update()` writes and publishes.
1487
- * @param data - The content fields to update
1488
- * @param locale - Optional locale for localized content (overrides the fluent locale, which
1489
- * overrides the client's own)
1490
- * @param options - `If-Match` / `Idempotency-Key`, both declared on this route
1491
- */
1492
- update(data: Partial<T>, locale?: string, options?: ConditionalWriteOptions): Promise<T>;
1493
- /**
1494
- * Check if singleton exists
1495
- */
1496
- exists(): Promise<boolean>;
1497
- /**
1498
- * Get version history for one locale of the singleton.
1499
- *
1500
- * Versions are numbered per locale, so this is one locale's timeline: `locale` defaults to the
1501
- * fluent `.locale()`, then the client's own (`withLocale()`), then the schema default; no
1502
- * fallback is applied, and a locale with no history returns an empty array rather than another
1503
- * locale's rows. Every row names the locale it belongs to.
1504
- */
1505
- versions(options?: {
1506
- limit?: number;
1507
- locale?: string;
1508
- }): Promise<Array<T & {
1509
- versionNumber: number;
1510
- locale: string;
1511
- }>>;
1512
- /**
1513
- * Get a specific version of the singleton.
1514
- *
1515
- * `locale` defaults to the fluent `.locale()`, then the client's own (`withLocale()`), then the
1516
- * schema default. Version numbers are per locale, so version 2 is different content in each one.
1517
- */
1518
- version(versionNumber: number, options?: {
1519
- locale?: string;
1520
- }): Promise<T | null>;
1521
- /** The locale a write or history read sends: the call's, the fluent one, else the client's. */
1522
- private localeOf;
1523
- /**
1524
- * Get the singleton key
1525
- */
1526
- getSingletonKey(): string;
1527
- /**
1528
- * Reset fluent options for reuse
1529
- */
1530
- reset(): SingletonClient<ReadWith<R, never>, R>;
1531
- }
1532
-
1533
- /**
1534
- * Settings fetch options
1535
- */
1536
- interface SettingsFetchOptions {
1537
- /** Locale for localized settings */
1538
- locale?: string;
1539
- /**
1540
- * Include paths: the references to expand (REL-1801), added to the ones `include()` named — see
1541
- * `include()`. Typed where the call names them inline.
1542
- */
1543
- include?: readonly string[];
1544
- }
1545
- /**
1546
- * Settings client for managing settings group operations
1547
- * Provides a fluent API for settings operations following the same pattern as ContentClient
1548
- *
1549
- * Every call is in the locale it names — the argument (`fetch({ locale })`, `update(settings,
1550
- * locale)`, `reset(locale)`, `versions({ locale })`), then `.locale()` — and otherwise in the
1551
- * client's own (`withLocale()`, `updateConfig({ locale })`) as it is when the call is made, as
1552
- * `content()`'s are: reads, writes and version history alike.
1553
- *
1554
- * @example
1555
- * ```typescript
1556
- * const emailConfig = await client.settings('email-config')
1557
- * .include(['defaultTemplate'])
1558
- * .locale('en')
1559
- * .fetch()
1560
- * ```
1561
- */
1562
- declare class SettingsClient<T = unknown, R extends Reading = UntypedReading<T>, I extends string = never> {
1563
- private readonly httpClient;
1564
- private readonly groupId;
1565
- private includeFields?;
1566
- private localeValue?;
1567
- /** The client's locale, asked on every call rather than copied when this client was taken. */
1568
- private readonly clientLocale;
1569
- /**
1570
- * @param locale - The client's locale: the function `TypedRelyIOClient` hands over, asked on
1571
- * every call, or a locale fixed for this client
1572
- */
1573
- constructor(httpClient: HttpClient, groupId: string, locale?: ClientLocale);
1574
- /**
1575
- * Expand references into the entries they point at: one include path per element (REL-1801).
1576
- * A path names fields through lists, objects and references — `'hero.media.image'`,
1577
- * `'featured.author'` — and may pick the fields each entry returns, `'featured(title,slug)'`.
1578
- * Each is checked against the schema: a path that holds no reference does not compile, and the
1579
- * read's type follows the paths — an expanded reference is the entry, every other one its id. A
1580
- * second call adds to the first.
1581
- */
1582
- include<const P extends readonly string[]>(paths: CheckedIncludes<R, P>): SettingsClient<ReadWith<R, I | P[number]>, R, I | P[number]>;
1583
- /**
1584
- * Set locale for localized settings
1585
- * @param locale - Locale code (e.g., 'en', 'de', 'fr')
1586
- */
1587
- locale(locale: string): this;
1588
- /**
1589
- * Fetch the settings group
1590
- * @param options - Optional fetch options (alternative to fluent methods)
1591
- */
1592
- fetch<const P extends readonly string[]>(options: Omit<SettingsFetchOptions, 'include'> & {
1593
- include: CheckedIncludes<R, P>;
1594
- }): Promise<ReadWith<R, I | P[number]> | null>;
1595
- fetch(options?: Omit<SettingsFetchOptions, 'include'>): Promise<T | null>;
1596
- /**
1597
- * Update settings group
1598
- *
1599
- * The fields travel as `content` beside an optional `locale`, the body the route validates
1600
- * (`UpdateSettingsDto`); any other top-level key is refused `REQUEST_INVALID`.
1601
- *
1602
- * @param settings - The settings fields to update
1603
- * @param locale - Optional locale for localized settings (overrides the fluent locale, which
1604
- * overrides the client's own)
1605
- */
1606
- update(settings: Partial<T>, locale?: string, options?: ConditionalWriteOptions): Promise<T>;
1607
- /**
1608
- * Fetch the group **and the entity tag that read served**, which is what
1609
- * `update`'s and `reset`'s `ifMatch` take
1610
- * (`INV-API-CONDITIONAL-WRITE-IS-REACHABLE`).
1611
- */
1612
- fetchWithTag(options?: SettingsFetchOptions): Promise<{
1613
- settings: T;
1614
- entityTag?: string;
1615
- }>;
1616
- /**
1617
- * Reset settings to default values
1618
- *
1619
- * The route reads its locale from `?locale=` and takes no body, so that is where it is sent.
1620
- *
1621
- * @param locale - Optional locale for localized settings (overrides the fluent locale, which
1622
- * overrides the client's own)
1623
- */
1624
- reset(locale?: string, options?: ConditionalWriteOptions): Promise<T>;
1625
- /**
1626
- * Get default settings for this group
1627
- */
1628
- getDefaults(): Promise<T>;
1629
- /**
1630
- * Check if settings group exists
1631
- */
1632
- exists(): Promise<boolean>;
1633
- /**
1634
- * Get version history for one locale of the settings group.
1635
- *
1636
- * Versions are numbered per locale, so this is one locale's timeline: `locale` defaults to the
1637
- * fluent `.locale()`, then the client's own (`withLocale()`), then the schema default; no
1638
- * fallback is applied, and a locale with no history returns an empty array rather than another
1639
- * locale's rows. Every row names the locale it belongs to.
1640
- */
1641
- versions(options?: {
1642
- limit?: number;
1643
- locale?: string;
1644
- }): Promise<Array<T & {
1645
- versionNumber: number;
1646
- locale: string;
1647
- }>>;
1648
- /**
1649
- * Get a specific version of the settings.
1650
- *
1651
- * `locale` defaults to the fluent `.locale()`, then the client's own (`withLocale()`), then the
1652
- * schema default. Version numbers are per locale, so version 2 is different content in each one.
1653
- */
1654
- version(versionNumber: number, options?: {
1655
- locale?: string;
1656
- }): Promise<T | null>;
1657
- /** The locale a write or history read sends: the call's, the fluent one, else the client's. */
1658
- private localeOf;
1659
- /**
1660
- * Get the settings group ID
1661
- */
1662
- getGroupId(): string;
1663
- /**
1664
- * Reset fluent options for reuse
1665
- */
1666
- resetOptions(): SettingsClient<ReadWith<R, never>, R>;
1667
- }
1668
-
1669
- /**
1670
- * Any schema the client can be typed by. `any` is deliberate: the client accepts whatever content
1671
- * types, singletons, queries and settings groups a customer declares.
1672
- *
1673
- * This is the constraint `TypedRelyIOClient`'s `TSchema` carries, so `RegisteredSchema` is inferred
1674
- * against the same one — `createClient` returns `TypedRelyIOClient<RegisteredSchema>`, and a schema
1675
- * inferred against any wider alias would not be provably inside the client's bound.
1676
- */
1677
- type AnySchemaDefinition = SchemaDefinition<any, any, any, any, any>;
1678
- /**
1679
- * The seam `relyio generate-types` fills (`INV-SS-TYPES-INFERRED`).
1680
- *
1681
- * Empty here on purpose. The generated `schema.ts` augments it —
1682
- *
1683
- * ```ts
1684
- * declare module '@relyio/sdk' {
1685
- * interface Register {
1686
- * schema: typeof schema
1687
- * }
1688
- * }
1689
- * ```
1690
- *
1691
- * — and from then on `createClient({ apiKey })`, with no generic and no `schema:`, is typed by that
1692
- * schema. The pattern is TanStack Router's. It is type-level only: nothing at runtime reads it.
1693
- */
1694
- interface Register {
1695
- }
1696
- /**
1697
- * The schema the project registered through `Register`, or `undefined` when nothing did — which is
1698
- * the untyped client, exactly as before the generator ran.
1699
- */
1700
- type RegisteredSchema = Register extends {
1701
- schema: infer S extends AnySchemaDefinition;
1702
- } ? S : undefined;
1703
-
1704
- /**
1705
- * Search mode for content queries
1706
- */
1707
- type SearchMode = 'keyword' | 'semantic' | 'hybrid';
1708
- /**
1709
- * What a search or similar hit reports beside its fields: the committed document's
1710
- * `SearchHitMetadata`, read through `http/api-schema.ts`.
1711
- *
1712
- * Narrower than an entry read's `EntryMetadata`, because a hit is not built by the entry read: it
1713
- * carries no `entryType`, `defaultLocale`, `availableLocales`, `refs`, `firstPublishedAt` or
1714
- * version history. Until REL-1413 a hit was typed as an entry and so promised all five — the
1715
- * locale helpers kept an optional chain after `availableLocales` for the hits that lacked it. Read
1716
- * the entry (`content(type).findById(hit._metadata.id)`) for those.
1717
- */
1718
- type SearchHitMetadata = components['schemas']['SearchHitMetadata'];
1719
- /** What a hit carries beside the entry's fields, as the document declares it. */
1720
- type SearchHitSystemFields = Pick<components['schemas']['SearchHit'], '_metadata' | '_score' | '_scoreBreakdown' | '_highlights'>;
1721
- /**
1722
- * One search hit as the route emits it — the document's `SearchHit`: the fields `T` declares at the
1723
- * top level (at `_metadata.locale`, or only the aliases a `fields` projection names), the hit's own
1724
- * `_metadata`, and the score annotations the request asked for. There is no per-item `data` wrapper.
1725
- *
1726
- * `T`'s `_metadata` is replaced, not kept: `T` is an entry type, and a hit is not an entry read.
1727
- * Every field is optional, a required one too: a `fields` projection names only some of them, and a
1728
- * hit is built from the search row, not by the read that fills a required field a draft holds empty
1729
- * (REL-1795). Distributes over a union of entry types, so a search across two types keeps each
1730
- * one's fields.
1731
- */
1732
- type SearchResultItem<T = unknown> = T extends unknown ? Partial<Omit<T, '_metadata'>> & SearchHitSystemFields : never;
1733
- /**
1734
- * Search response from API
1735
- */
1736
- /**
1737
- * What `POST /v1/search` serves, **read from the committed document**.
1738
- *
1739
- * `{ data, page: { offset, limit, total }, meta }` — search is the offset
1740
- * dialect precisely because a facet-bearing result wants a total and an offset
1741
- * (Decision 5), and the facets, the strategy the engine chose and its warnings
1742
- * ride the declared `meta` sibling because none of them is an item or a page
1743
- * fact.
1744
- *
1745
- * The hand-written `{ items, total?, metadata? }` this replaces named three
1746
- * keys the route does not send. `SearchBuilder.items()` returned `undefined`
1747
- * and `.total` was always `undefined` — with nothing able to see either,
1748
- * because the type asserted them.
1749
- */
1750
- type SearchResponse<T = unknown> = Wire<'/v1/search', 'post', SearchResultItem<T>>;
1751
- /**
1752
- * One autocomplete suggestion — the document's `AutocompleteSuggestion`: the value, its best score,
1753
- * an entry holding it (`id`), and the value with its match wrapped in `<em>` when Atlas highlighted
1754
- * it. The hand-written type this replaces named `highlighted`, `entryId` and `contentType`, none of
1755
- * which the route sends, and made `score` optional where it is always sent.
1756
- */
1757
- type AutocompleteSuggestion = components['schemas']['AutocompleteSuggestion'];
1758
- /**
1759
- * Autocomplete response
1760
- */
1761
- /**
1762
- * What `POST /v1/search/autocomplete` serves. The suggestions are the
1763
- * collection's `data`; the query it completed and the field it matched on ride
1764
- * `meta`. Autocomplete is unpaginated, so its page reports `offset: 0` and a
1765
- * `total` equal to what it returned — the dialect is declared rather than
1766
- * omitted so one collection shape covers every list.
1767
- */
1768
- type AutocompleteResponse = Wire<'/v1/search/autocomplete', 'post', AutocompleteSuggestion>;
1769
- /**
1770
- * One similar-content hit as the route emits it: the same `SearchHit` a search serves, built by
1771
- * the same transform, with `_score` on every hit.
1772
- */
1773
- type SimilarResult<T = unknown> = SearchResultItem<T>;
1774
- /**
1775
- * Similar response
1776
- */
1777
- /** What `POST /v1/search/similar` serves. */
1778
- type SimilarResponse<T = unknown> = Wire<'/v1/search/similar', 'post', SimilarResult<T>>;
1779
- /**
1780
- * Filter conditions for search
1781
- */
1782
- type SearchFilter = Record<string, unknown>;
1783
- /**
1784
- * The content type names a search may be scoped to: the schema's where one types the client, any
1785
- * name where none does.
1786
- */
1787
- type SearchTypeName<S extends AnySchemaDefinition | undefined> = S extends AnySchemaDefinition ? SchemaContentTypeNames<S> : string;
1788
- /**
1789
- * What a search over the content types `K` returns: their entries, typed by the schema
1790
- * (`INV-SS-TYPES-INFERRED`) — or `T` unchanged where no schema types the client.
1791
- */
1792
- type SearchEntry<S extends AnySchemaDefinition | undefined, K extends SearchTypeName<S> = SearchTypeName<S>, T extends BaseContent = BaseContent> = S extends AnySchemaDefinition ? SchemaEntryOf<S, K & SchemaContentTypeNames<S>> : T;
1793
- /**
1794
- * Field projection for search results
1795
- */
1796
- type SearchFields<T> = {
1797
- [K: string]: keyof T | string;
1798
- };
1799
- /**
1800
- * One ordering of `POST /v1/search`'s results, as the route's `sort` array takes it.
1801
- */
1802
- interface SearchSort {
1803
- /** Field path, or `_score` for relevance */
1804
- field: string;
1805
- /** Direction; the route's default is `desc` */
1806
- direction: 'asc' | 'desc';
1807
- }
1808
- /**
1809
- * Fluent builder for search queries
1810
- *
1811
- * @example
1812
- * ```typescript
1813
- * const results = await relyio.search('wireless headphones')
1814
- * .in('product')
1815
- * .where({ status: 'active' })
1816
- * .searchFields(['title', 'description'])
1817
- * .limit(20)
1818
- * .includeScore()
1819
- * .execute()
1820
- * ```
1821
- */
1822
- declare class SearchBuilder<T extends BaseContent = BaseContent, S extends AnySchemaDefinition | undefined = undefined> {
1823
- private readonly httpClient;
1824
- private query;
1825
- private searchOptions;
1826
- private requestHeaders?;
1827
- private freshValue?;
1828
- /** The client's locale, asked when the query is sent rather than copied when it was built. */
1829
- private readonly clientLocale;
1830
- /**
1831
- * @param locale - The client's locale (`withLocale()`, `updateConfig({ locale })`), sent when
1832
- * `.locale()` names none: the function `TypedRelyIOClient` hands over, or a locale fixed for it
1833
- */
1834
- constructor(httpClient: HttpClient, query: string, locale?: ClientLocale);
1835
- /**
1836
- * Headers this search adds to the client's own, e.g. `X-RelyIO-Dataset` to search another
1837
- * dataset the key can see. A name the client already sends is replaced for this search only.
1838
- */
1839
- headers(headers: Record<string, string>): this;
1840
- /**
1841
- * The feed position this search follows — a change's `fresh` (`FeedChange`), or a resync's. Sent
1842
- * as `?_fresh` on the query string beside the body: the edge keys a search on both, so no answer
1843
- * it holds from before that position can answer it. The answer is the search's own.
1844
- */
1845
- fresh(position: string): this;
1846
- /**
1847
- * Search within a single content type. Where a schema types the client, the name is one of its
1848
- * content types and the results are that type's entries.
1849
- * @param contentType - Content type to search in
1850
- */
1851
- in<K extends SearchTypeName<S>>(contentType: K): SearchBuilder<SearchEntry<S, K, T>, S>;
1852
- /**
1853
- * Search across multiple content types. Where a schema types the client, the names are its content
1854
- * types and the results are a union of their entries — every type's, for `'*'`.
1855
- * @param contentTypes - Array of content types or '*' for all
1856
- */
1857
- across<K extends SearchTypeName<S>>(contentTypes: K[] | '*'): SearchBuilder<SearchEntry<S, K, T>, S>;
1858
- /**
1859
- * Add filter conditions, on the fields the content types declare. A condition on `_metadata` is
1860
- * refused here, before anything is sent.
1861
- * @param conditions - Filter conditions
1862
- */
1863
- where(conditions: SearchFilter): this;
1864
- /**
1865
- * Specify which fields to search in
1866
- * @param fields - Field names to search
1867
- */
1868
- searchFields(fields: string[]): this;
1869
- /**
1870
- * Project specific fields in results
1871
- * @param projection - Field mapping
1872
- */
1873
- fields(projection: Record<string, string>): this;
1874
- /**
1875
- * Order the results by a field instead of by relevance. Called again, it adds a tie-breaker.
1876
- * `_score` names relevance itself.
1877
- * @param field - Field path to sort by (e.g. `updatedAt`, `pricing.basePrice`)
1878
- * @param direction - `asc` or `desc` (the route's default is `desc`)
1879
- */
1880
- sort(field: string, direction?: 'asc' | 'desc'): this;
1881
- /**
1882
- * Limit number of results
1883
- * @param count - Maximum results
1884
- */
1885
- limit(count: number): this;
1886
- /**
1887
- * Skip results for pagination
1888
- * @param count - Number to skip
1889
- */
1890
- offset(count: number): this;
1891
- /**
1892
- * Include relevance scores in results
1893
- */
1894
- includeScore(): this;
1895
- /**
1896
- * Include score breakdown explanation
1897
- */
1898
- explain(): this;
1899
- /**
1900
- * Set locale for localized content
1901
- * @param locale - Locale code
1902
- */
1903
- locale(locale: string): this;
1904
- /**
1905
- * Execute the search query
1906
- */
1907
- execute(): Promise<SearchResponse<T>>;
1908
- /**
1909
- * Execute and return just the items (convenience method)
1910
- */
1911
- items(): Promise<Array<SearchResultItem<T>>>;
1912
- /**
1913
- * Get the first result
1914
- */
1915
- first(): Promise<SearchResultItem<T> | null>;
1916
- }
1917
- /**
1918
- * Fluent builder for autocomplete queries
1919
- *
1920
- * @example
1921
- * ```typescript
1922
- * const suggestions = await relyio.autocomplete('wire')
1923
- * .in('product')
1924
- * .field('title')
1925
- * .fuzzy()
1926
- * .limit(10)
1927
- * .execute()
1928
- * ```
1929
- */
1930
- declare class AutocompleteBuilder {
1931
- private readonly httpClient;
1932
- private query;
1933
- private autocompleteOptions;
1934
- /** The client's locale, asked when the query is sent rather than copied when it was built. */
1935
- private readonly clientLocale;
1936
- /**
1937
- * @param locale - The client's locale (`withLocale()`, `updateConfig({ locale })`), sent when
1938
- * `.locale()` names none: the function `TypedRelyIOClient` hands over, or a locale fixed for it
1939
- */
1940
- constructor(httpClient: HttpClient, query: string, locale?: ClientLocale);
1941
- /**
1942
- * Autocomplete within a content type
1943
- * @param contentType - Content type to search
1944
- */
1945
- in(contentType: string): this;
1946
- /**
1947
- * Field to autocomplete on
1948
- * @param fieldName - Field name (supports nested like 'category.name')
1949
- */
1950
- field(fieldName: string): this;
1951
- /**
1952
- * Enable fuzzy matching for typo tolerance
1953
- */
1954
- fuzzy(): this;
1955
- /**
1956
- * Add filter conditions, on the fields the content types declare. A condition on `_metadata` is
1957
- * refused here, before anything is sent.
1958
- * @param conditions - Filter conditions
1959
- */
1960
- where(conditions: SearchFilter): this;
1961
- /**
1962
- * Limit number of suggestions
1963
- * @param count - Maximum suggestions
1964
- */
1965
- limit(count: number): this;
1966
- /**
1967
- * Set locale for localized content
1968
- * @param locale - Locale code
1969
- */
1970
- locale(locale: string): this;
1971
- /**
1972
- * Execute the autocomplete query
1973
- */
1974
- execute(): Promise<AutocompleteResponse>;
1975
- /**
1976
- * Get just the suggestion texts
1977
- */
1978
- texts(): Promise<string[]>;
1979
- }
1980
- /**
1981
- * Fluent builder for finding similar content
1982
- *
1983
- * @example
1984
- * ```typescript
1985
- * const similar = await relyio.similar('entry_abc123')
1986
- * .in('product')
1987
- * .locale('fr-FR')
1988
- * .limit(5)
1989
- * .execute()
1990
- * ```
1991
- */
1992
- declare class SimilarBuilder<T extends BaseContent = BaseContent> {
1993
- private readonly httpClient;
1994
- private sourceId;
1995
- private similarOptions;
1996
- private requestHeaders?;
1997
- /** The client's locale, asked when the read is sent rather than copied when it was built. */
1998
- private readonly clientLocale;
1999
- /**
2000
- * @param locale - The client's locale (`withLocale()`, `updateConfig({ locale })`), sent when
2001
- * `.locale()` names none: the function `TypedRelyIOClient` hands over, or a locale fixed for it
2002
- */
2003
- constructor(httpClient: HttpClient, sourceId: string, locale?: ClientLocale);
2004
- /**
2005
- * Headers this read adds to the client's own, e.g. `X-RelyIO-Dataset` to read another dataset
2006
- * the key can see. A name the client already sends is replaced for this read only.
2007
- */
2008
- headers(headers: Record<string, string>): this;
2009
- /**
2010
- * Find similar content within a content type
2011
- * @param contentType - Content type to search
2012
- */
2013
- in(contentType: string): this;
2014
- /**
2015
- * Find similar content across several content types, or `'*'` for every type
2016
- * @param contentTypes - Array of content types or '*' for all
2017
- */
2018
- across(contentTypes: string[] | '*'): this;
2019
- /**
2020
- * Limit number of results
2021
- * @param count - Maximum results
2022
- */
2023
- limit(count: number): this;
2024
- /**
2025
- * Set locale for localized content
2026
- * @param locale - Locale code
2027
- */
2028
- locale(locale: string): this;
2029
- /**
2030
- * Execute the similar query
2031
- */
2032
- execute(): Promise<SimilarResponse<T>>;
2033
- /**
2034
- * Get just the similar items
2035
- */
2036
- items(): Promise<Array<SimilarResult<T>>>;
2037
- /**
2038
- * Get the most similar item
2039
- */
2040
- first(): Promise<SimilarResult<T> | null>;
2041
- }
2042
- /**
2043
- * Search client providing fluent access to all search capabilities
2044
- *
2045
- * @example
2046
- * ```typescript
2047
- * const searchClient = new SearchClient(httpClient)
2048
- *
2049
- * // Full-text/hybrid search
2050
- * const products = await searchClient.search('wireless')
2051
- * .in('product')
2052
- * .limit(20)
2053
- * .execute()
2054
- *
2055
- * // Autocomplete
2056
- * const suggestions = await searchClient.autocomplete('wire')
2057
- * .in('product')
2058
- * .field('title')
2059
- * .execute()
2060
- *
2061
- * // Find similar
2062
- * const similar = await searchClient.similar('entry_123')
2063
- * .in('product')
2064
- * .execute()
2065
- * ```
2066
- */
2067
- declare class SearchClient {
2068
- private readonly httpClient;
2069
- /** The client's locale, handed to every builder here to ask when it sends. */
2070
- private readonly clientLocale;
2071
- /**
2072
- * @param locale - The client's locale (`withLocale()`, `updateConfig({ locale })`): every builder
2073
- * here sends it when the query names none, as `content()`'s reads do — the function
2074
- * `TypedRelyIOClient` hands over, or a locale fixed for this client
2075
- */
2076
- constructor(httpClient: HttpClient, locale?: ClientLocale);
2077
- /**
2078
- * Start a search query
2079
- * @param query - Search query string
2080
- */
2081
- search<T extends BaseContent = BaseContent, S extends AnySchemaDefinition | undefined = undefined>(query: string): SearchBuilder<T, S>;
2082
- /**
2083
- * Start an autocomplete query
2084
- * @param query - Partial query to autocomplete
2085
- */
2086
- autocomplete(query: string): AutocompleteBuilder;
2087
- /**
2088
- * Find content similar to a given entry
2089
- * @param sourceId - Entry ID to find similar content for
2090
- */
2091
- similar<T extends BaseContent = BaseContent>(sourceId: string): SimilarBuilder<T>;
2092
- }
2093
-
2094
- /**
2095
- * HTTP methods supported in batch operations
2096
- */
2097
- type BatchMethod = 'POST' | 'PATCH' | 'PUT' | 'DELETE';
2098
- /**
2099
- * A single batch operation
2100
- */
2101
- interface BatchOperation {
2102
- /** Optional operation ID for tracking */
2103
- id?: string;
2104
- /** HTTP method */
2105
- method: BatchMethod;
2106
- /** Path to resource (e.g., '/product' or '/product/entry_123') */
2107
- path: string;
2108
- /** Request body for create/update operations */
2109
- body?: Record<string, unknown>;
2110
- }
2111
- /**
2112
- * Batch execution options
2113
- */
2114
- interface BatchOptions {
2115
- /**
2116
- * If true, the batch runs as one transaction: every operation lands or none does.
2117
- *
2118
- * Requires that all operations be the same kind of write — one transaction cannot span two
2119
- * calls — so a batch mixing creates and deletes, or `update` and `replace`, is rejected with
2120
- * a 400 before anything is attempted: `execute()` rejects with `InvalidPayloadError`. A
2121
- * singleton write is a create when the dataset has never written that singleton and an update
2122
- * after, and an atomic batch cannot create one — that operation reports `REQUEST_INVALID` and
2123
- * every other `TRANSACTION_ROLLED_BACK` — so create it in a request of its own first.
2124
- *
2125
- * @default false
2126
- */
2127
- atomic?: boolean;
2128
- /**
2129
- * If true, run the checks and write nothing.
2130
- *
2131
- * Every entry is resolved, authorized and validated against its schema, so what passes a dry
2132
- * run is what a real write accepts.
2133
- *
2134
- * @default false
2135
- */
2136
- dryRun?: boolean;
2137
- /**
2138
- * If true, include full data in response
2139
- * @default true
2140
- */
2141
- returnData?: boolean;
2142
- /**
2143
- * If true, continue executing after failures (non-atomic mode only)
2144
- * @default true
2145
- */
2146
- continueOnError?: boolean;
2147
- }
2148
- /**
2149
- * Result of a single operation in the batch
2150
- */
2151
- /**
2152
- * One batch item, **as the committed document describes the `multi` kind**.
2153
- *
2154
- * Read from `api-schema.ts` rather than imported from `@relyio/api-contract`
2155
- * for the same reason every other response type here is: the document is the
2156
- * one source, and a type this package publishes must resolve for a customer who
2157
- * installs `@relyio/sdk` alone. The contract's runtime half is bundled; its
2158
- * `.d.ts` is not something a consumer can reach.
2159
- *
2160
- * The hand-written version carried `success: boolean` and a per-item `status`
2161
- * number. Both are gone from the wire and neither loss is a gap: the presence
2162
- * of `error` already says whether the item succeeded (Decision 2, gap 16), and
2163
- * a sub-response carrying its own HTTP status is exactly the duplication
2164
- * `INV-API-NO-DUPLICATE-STATUS` removes. What replaced them says more, not
2165
- * less — `outcome` distinguishes `created` from `updated` from `unchanged`
2166
- * from `duplicate` from `skipped`, which one boolean never could.
2167
- */
2168
- type BatchOperationResult = components['schemas']['MultiPayload']['results'][number];
2169
- /** Summary of batch execution, from the contract. */
2170
- type BatchSummary = components['schemas']['MultiSummary'];
2171
- /**
2172
- * The full batch payload: `{ results, summary }`.
2173
- *
2174
- * `transactionId`, `executionTimeMs` and `dryRun` were named here and are not
2175
- * on the wire; nothing produced them and nothing could read them.
2176
- */
2177
- type BatchResponse = components['schemas']['MultiPayload'];
2178
- /**
2179
- * Fluent builder for batch operations
2180
- *
2181
- * Every create, update, replace and singleton operation it builds is in the locale that operation
2182
- * names, and otherwise in the client's own (`withLocale()`), sent as that operation's
2183
- * `body.locale` — a batch is one request, but the API reads a locale per operation. A delete names
2184
- * none (it removes the whole entry), and a `raw()` operation is sent exactly as written.
2185
- *
2186
- * An atomic batch — `.atomic()` or `execute({ atomic: true })` — is one transaction: every
2187
- * operation lands or none does. The API accepts one only when every operation is the same kind of
2188
- * write and none writes a singleton that does not exist yet (see {@link BatchOptions.atomic}).
2189
- *
2190
- * @example
2191
- * ```typescript
2192
- * // Each operation lands or fails on its own
2193
- * const result = await relyio.batch()
2194
- * .create('product', { content: { title: 'iPhone', price: 999 } })
2195
- * .update('product', 'entry_abc', { content: { price: 899 } })
2196
- * .delete('product', 'entry_xyz')
2197
- * .upsertSingleton('homepage', { content: { hero: 'Welcome!' } })
2198
- * .execute()
2199
- *
2200
- * console.log(result.summary.succeeded)
2201
- *
2202
- * // All or nothing: one kind of write
2203
- * await relyio.batch()
2204
- * .create('product', { content: { title: 'iPhone', price: 999 } })
2205
- * .create('product', { content: { title: 'iPad', price: 799 } })
2206
- * .atomic()
2207
- * .execute()
2208
- * ```
2209
- */
2210
- declare class BatchBuilder<TSchema = unknown> {
2211
- private readonly httpClient;
2212
- private operations;
2213
- private defaultOptions;
2214
- private requestHeaders;
2215
- /** The client's locale, asked as each operation is added rather than copied when this was built. */
2216
- private readonly clientLocale;
2217
- /**
2218
- * @param locale - The client's locale (`withLocale()`, `updateConfig({ locale })`), each
2219
- * operation's when it names none: the function `TypedRelyIOClient` hands over, or a locale fixed
2220
- * for this batch
2221
- */
2222
- constructor(httpClient: HttpClient, locale?: ClientLocale);
2223
- /**
2224
- * An operation's body with its locale: the one it names, else the client's as it is when the
2225
- * operation is added.
2226
- */
2227
- private localized;
2228
- /**
2229
- * Add a create operation
2230
- * @param contentType - Content type to create
2231
- * @param data - Entry data with content field
2232
- * @param id - Optional operation ID for tracking
2233
- */
2234
- create<T extends BaseContent = BaseContent>(contentType: string, data: {
2235
- content: CreateInput<T>;
2236
- slug?: string;
2237
- locale?: string;
2238
- parentId?: string;
2239
- sortOrder?: number;
2240
- }, id?: string): this;
2241
- /**
2242
- * Add an update operation (partial update).
2243
- *
2244
- * The submitted fields are merged into the entry and the rest are left as they are. The merge is
2245
- * field-level rather than deep: sending an object-typed field replaces that field's whole value.
2246
- *
2247
- * @param contentType - Content type
2248
- * @param entryId - Entry ID to update
2249
- * @param data - Fields to update
2250
- * @param id - Optional operation ID for tracking
2251
- */
2252
- update<T extends BaseContent = BaseContent>(contentType: string, entryId: string, data: {
2253
- content?: UpdateInput<T>;
2254
- slug?: string;
2255
- locale?: string;
2256
- parentId?: string;
2257
- sortOrder?: number;
2258
- }, id?: string): this;
2259
- /**
2260
- * Add a replace operation (full replacement).
2261
- *
2262
- * The payload becomes the entry's content for the addressed locale: fields you do not send are
2263
- * cleared. Send the whole entry, or use {@link BatchClient.update} to change part of one.
2264
- *
2265
- * @param contentType - Content type
2266
- * @param entryId - Entry ID to replace
2267
- * @param data - Complete entry data
2268
- * @param id - Optional operation ID for tracking
2269
- */
2270
- replace<T extends BaseContent = BaseContent>(contentType: string, entryId: string, data: {
2271
- content: CreateInput<T>;
2272
- slug?: string;
2273
- locale?: string;
2274
- parentId?: string;
2275
- sortOrder?: number;
2276
- }, id?: string): this;
2277
- /**
2278
- * Add a delete operation
2279
- * @param contentType - Content type
2280
- * @param entryId - Entry ID to delete
2281
- * @param id - Optional operation ID for tracking
2282
- */
2283
- delete(contentType: string, entryId: string, id?: string): this;
2284
- /**
2285
- * Add a singleton upsert operation
2286
- * @param singletonKey - Singleton key
2287
- * @param data - Singleton content
2288
- * @param id - Optional operation ID for tracking
2289
- */
2290
- upsertSingleton(singletonKey: string, data: {
2291
- content: Record<string, unknown>;
2292
- locale?: string;
2293
- }, id?: string): this;
2294
- /**
2295
- * Add a singleton create operation (alias for upsert with POST)
2296
- * @param singletonKey - Singleton key
2297
- * @param data - Singleton content
2298
- * @param id - Optional operation ID for tracking
2299
- */
2300
- createSingleton(singletonKey: string, data: {
2301
- content: Record<string, unknown>;
2302
- locale?: string;
2303
- }, id?: string): this;
2304
- /**
2305
- * Add a raw operation (for advanced use cases). It is sent exactly as written: the client's
2306
- * locale is not added to it.
2307
- * @param operation - The operation to add
2308
- */
2309
- raw(operation: BatchOperation): this;
2310
- /**
2311
- * Add multiple raw operations
2312
- * @param operations - Operations to add
2313
- */
2314
- rawMany(operations: BatchOperation[]): this;
2315
- /**
2316
- * Bulk create multiple entries of the same content type
2317
- * @param contentType - Content type
2318
- * @param items - Array of entry data
2319
- */
2320
- bulkCreate<T extends BaseContent = BaseContent>(contentType: string, items: Array<{
2321
- content: CreateInput<T>;
2322
- slug?: string;
2323
- locale?: string;
2324
- id?: string;
2325
- }>): this;
2326
- /**
2327
- * Bulk update multiple entries
2328
- * @param contentType - Content type
2329
- * @param updates - Array of { entryId, data } pairs
2330
- */
2331
- bulkUpdate<T extends BaseContent = BaseContent>(contentType: string, updates: Array<{
2332
- entryId: string;
2333
- data: {
2334
- content?: UpdateInput<T>;
2335
- };
2336
- id?: string;
2337
- }>): this;
2338
- /**
2339
- * Bulk delete multiple entries
2340
- * @param contentType - Content type
2341
- * @param entryIds - Entry IDs to delete
2342
- */
2343
- bulkDelete(contentType: string, entryIds: string[]): this;
2344
- /**
2345
- * Configure default options for execution
2346
- * @param options - Batch options
2347
- */
2348
- configure(options: BatchOptions): this;
2349
- /**
2350
- * Enable atomic mode (all-or-nothing)
2351
- */
2352
- atomic(): this;
2353
- /**
2354
- * Enable dry-run mode (validate without executing)
2355
- */
2356
- dryRun(): this;
2357
- /**
2358
- * Disable returning full data in response
2359
- */
2360
- noData(): this;
2361
- /**
2362
- * Stop on first error (non-atomic mode)
2363
- */
2364
- stopOnError(): this;
2365
- /**
2366
- * Headers the batch request adds — e.g. `{ 'X-RelyIO-Dataset': 'staging' }` to write a dataset
2367
- * other than the client's. They replace the client's own header of the same name for this
2368
- * request alone, and apply to every operation in it: a batch is one request.
2369
- */
2370
- headers(headers: Record<string, string>): this;
2371
- /**
2372
- * Get the current list of operations
2373
- */
2374
- getOperations(): BatchOperation[];
2375
- /**
2376
- * Get the number of operations
2377
- */
2378
- size(): number;
2379
- /**
2380
- * Clear all operations
2381
- */
2382
- clear(): this;
2383
- /**
2384
- * Clone the builder
2385
- */
2386
- clone(): BatchBuilder<TSchema>;
2387
- /**
2388
- * Execute the batch operations
2389
- * @param options - Override default options
2390
- */
2391
- execute(options?: BatchOptions): Promise<BatchResponse>;
2392
- /**
2393
- * Validate operations without executing (convenience for dry-run)
2394
- */
2395
- validate(): Promise<BatchResponse>;
2396
- /**
2397
- * Execute and throw if any operation failed
2398
- */
2399
- executeOrThrow(options?: BatchOptions): Promise<BatchResponse>;
2400
- }
2401
- /**
2402
- * Batch client for bulk operations
2403
- *
2404
- * @example
2405
- * ```typescript
2406
- * const batchClient = new BatchClient(httpClient)
2407
- *
2408
- * // Create a batch with fluent API
2409
- * const result = await batchClient.create()
2410
- * .create('product', { content: { title: 'New Product' } })
2411
- * .update('product', 'entry_123', { content: { price: 99 } })
2412
- * .execute()
2413
- *
2414
- * // Quick bulk create
2415
- * const products = await batchClient.bulkCreate('product', [
2416
- * { content: { title: 'Product 1' } },
2417
- * { content: { title: 'Product 2' } },
2418
- * ])
2419
- * ```
2420
- */
2421
- declare class BatchClient<TSchema = unknown> {
2422
- private readonly httpClient;
2423
- /** The client's locale, handed to every batch this builds to ask as operations are added. */
2424
- private readonly clientLocale;
2425
- /**
2426
- * @param locale - The client's locale: the function `TypedRelyIOClient` hands over, or a locale
2427
- * fixed for this client
2428
- */
2429
- constructor(httpClient: HttpClient, locale?: ClientLocale);
2430
- /**
2431
- * Create a new batch builder, whose operations default to this client's locale
2432
- */
2433
- create(): BatchBuilder<TSchema>;
2434
- /**
2435
- * Quick bulk create entries
2436
- * @param contentType - Content type
2437
- * @param items - Array of entry data
2438
- * @param options - Batch options
2439
- */
2440
- bulkCreate<T extends BaseContent = BaseContent>(contentType: string, items: Array<{
2441
- content: CreateInput<T>;
2442
- slug?: string;
2443
- locale?: string;
2444
- }>, options?: BatchOptions): Promise<BatchResponse>;
2445
- /**
2446
- * Quick bulk update entries
2447
- * @param contentType - Content type
2448
- * @param updates - Array of updates
2449
- * @param options - Batch options
2450
- */
2451
- bulkUpdate<T extends BaseContent = BaseContent>(contentType: string, updates: Array<{
2452
- entryId: string;
2453
- data: {
2454
- content?: UpdateInput<T>;
2455
- };
2456
- }>, options?: BatchOptions): Promise<BatchResponse>;
2457
- /**
2458
- * Quick bulk delete entries
2459
- * @param contentType - Content type
2460
- * @param entryIds - Entry IDs to delete
2461
- * @param options - Batch options
2462
- */
2463
- bulkDelete(contentType: string, entryIds: string[], options?: BatchOptions): Promise<BatchResponse>;
2464
- /**
2465
- * Execute raw batch operations
2466
- * @param operations - Array of operations
2467
- * @param options - Batch options
2468
- */
2469
- execute(operations: BatchOperation[], options?: BatchOptions): Promise<BatchResponse>;
2470
- }
2471
-
2472
- /**
2473
- * Fetch block for retrieving content
2474
- */
2475
- interface FetchBlock {
2476
- /** Content type to fetch (mutually exclusive with singleton) */
2477
- contentType?: string;
2478
- /** Singleton key to fetch (mutually exclusive with contentType) */
2479
- singleton?: string;
2480
- /** Filter conditions */
2481
- where?: Record<string, unknown>;
2482
- /** Reference fields to populate */
2483
- include?: string | string[];
2484
- /** Sort specification */
2485
- sort?: Record<string, 'asc' | 'desc'>;
2486
- /** Maximum entries to return */
2487
- limit?: number;
2488
- /** Entries to skip */
2489
- offset?: number;
2490
- }
2491
- /**
2492
- * Aggregate block for computing metrics
2493
- */
2494
- interface AggregateBlock {
2495
- /** Source content type or query reference */
2496
- source: string;
2497
- /**
2498
- * Filter conditions applied before grouping, in the Query DSL's spelling — bare operator names
2499
- * (`{ price: { gte: 10 } }`), not the list route's `$gte`. A `$`-prefixed key is not an operator
2500
- * in this dialect: the service reads the object as a nested field and drops it.
2501
- */
2502
- where?: Record<string, unknown>;
2503
- /** Field to group by */
2504
- by?: string | string[];
2505
- /** Metrics to compute, keyed by the name each one answers under */
2506
- metrics: Record<string, AggregateMetric>;
2507
- }
2508
- /**
2509
- * One metric, as the Query DSL writes it: `<field>.<fn>`, where `fn` is `count`, `sum`, `avg`,
2510
- * `min`, `max`, `first` or `last` — `'id.count'`, `'pricing.basePrice.avg'`. The service
2511
- * validates every metric against that form (`INVALID_AGGREGATION_EXPRESSION`); `count` ignores the
2512
- * field it names. `first` and `last` were missing here while the service ran them (REL-1470).
2513
- *
2514
- * It used to be `{ fn, field? }`, a shape the service refuses on every request: the validator
2515
- * tests the metric as a string, so an object arrives as `[object Object]`.
2516
- */
2517
- type AggregateMetric = `${string}.${'count' | 'sum' | 'avg' | 'min' | 'max' | 'first' | 'last'}`;
2518
- /**
2519
- * Search block for full-text/semantic search
2520
- */
2521
- interface SearchBlock {
2522
- /** Content type to search */
2523
- contentType: string;
2524
- /** Search query (can use ${param} syntax) */
2525
- query: string;
2526
- /** Search mode */
2527
- mode?: 'keyword' | 'semantic' | 'hybrid';
2528
- /** Fields to search */
2529
- searchFields?: string[];
2530
- /** Additional filters */
2531
- where?: Record<string, unknown>;
2532
- /** Maximum results */
2533
- limit?: number;
2534
- }
2535
- /**
2536
- * One post-fetch filter: a parameter expression, or the operators every query clause compares with
2537
- * (`FilterOperator`, REL-1479). This kept a copy of eleven, without `exists` or `notExists`; and a
2538
- * bare number or boolean, which it took, the service does not read as a filter.
2539
- */
2540
- type FilterValue = string | FilterOperator;
2541
- /**
2542
- * Full-text search configuration
2543
- */
2544
- interface FullTextConfig {
2545
- /** Search query (can use ${param} syntax) */
2546
- query: string;
2547
- /** Fields to search */
2548
- fields?: string[];
2549
- /** Minimum score threshold */
2550
- minScore?: number;
2551
- }
2552
- /**
2553
- * Ad-hoc query definition
2554
- */
2555
- interface AdhocQueryDefinition {
2556
- /** Fetch block - retrieve content */
2557
- fetch?: FetchBlock;
2558
- /** Aggregate block - compute metrics */
2559
- aggregate?: AggregateBlock;
2560
- /** Search block - full-text/semantic search */
2561
- search?: SearchBlock;
2562
- /** Field selection/transformation */
2563
- select?: Record<string, string>;
2564
- /** Post-fetch filters with parameter support */
2565
- filters?: Record<string, FilterValue>;
2566
- /** Full-text search configuration */
2567
- fullText?: FullTextConfig;
2568
- /** Cache policy */
2569
- cache?: {
2570
- ttl?: number;
2571
- strategy?: 'stale-while-revalidate' | 'cache-first' | 'network-first';
2572
- };
2573
- }
2574
- /**
2575
- * Ad-hoc query execution options
2576
- */
2577
- interface AdhocQueryOptions {
2578
- /** Include debug information in response */
2579
- debug?: boolean;
2580
- /** Bypass cache for this request */
2581
- skipCache?: boolean;
2582
- /** Custom timeout in milliseconds (1000-60000) */
2583
- timeout?: number;
2584
- }
2585
- /**
2586
- * Query execution metadata
2587
- */
2588
- interface QueryMetadata {
2589
- /** Query identifier */
2590
- queryId: string;
2591
- /** Execution time in milliseconds */
2592
- executionTimeMs: number;
2593
- /** Number of results */
2594
- resultCount: number;
2595
- /** ISO timestamp */
2596
- timestamp: string;
2597
- /** Whether result was from cache */
2598
- cacheHit?: boolean;
2599
- /** Generated pipeline (if debug enabled) */
2600
- pipeline?: unknown[];
2601
- /** Resolved parameters (if debug enabled) */
2602
- parameters?: Record<string, unknown>;
2603
- /** Validation warnings */
2604
- warnings?: string[];
2605
- }
2606
- /**
2607
- * The payload `POST /v1/query` serves: `{ result, metadata }`.
2608
- *
2609
- * The route is an `object` kind, so there is one payload key whether the query
2610
- * fetched a list or a singleton — `result` is the records either way. Three
2611
- * members of the hand-written type are gone and each for a stated reason:
2612
- * `success` because the status line says it, `items` because no contract shape
2613
- * introduces that key (`INV-API-SUCCESS-HAS-ONE-SHAPE`), and `errors` because a
2614
- * failed query is a **refusal**, which arrives as `{ error: { code, messages } }`
2615
- * and is thrown as a typed SDK error rather than returned as a 200 with a flag.
2616
- */
2617
- interface AdhocQueryResult<T = unknown> {
2618
- /** The records the query produced — an array for a list, one item for a singleton. */
2619
- result?: T | T[];
2620
- /** Execution metadata */
2621
- metadata?: QueryMetadata;
2622
- }
2623
- /**
2624
- * Fluent builder for ad-hoc queries
2625
- *
2626
- * @example
2627
- * ```typescript
2628
- * const result = await relyio.query.adhoc()
2629
- * .fetch({
2630
- * contentType: 'product',
2631
- * where: { status: 'active' },
2632
- * include: ['brand', 'categories'],
2633
- * limit: 20
2634
- * })
2635
- * .select({
2636
- * id: 'id',
2637
- * title: 'title',
2638
- * price: 'pricing.basePrice',
2639
- * brandName: 'brand.name'
2640
- * })
2641
- * .filter('pricing.basePrice', { gte: '${minPrice}', lte: '${maxPrice}' })
2642
- * .params({ minPrice: 100, maxPrice: 500 })
2643
- * .debug()
2644
- * .execute()
2645
- * ```
2646
- */
2647
- declare class AdhocQueryBuilder<T = unknown> {
2648
- private readonly httpClient;
2649
- private queryDef;
2650
- private parameters;
2651
- private options;
2652
- /** The client's locale, asked when the query is sent rather than copied when it was built. */
2653
- private readonly clientLocale;
2654
- /**
2655
- * @param locale - The client's locale (`withLocale()`, `updateConfig({ locale })`), sent as the
2656
- * `locale` parameter when `.param('locale', …)` binds none: the function `TypedRelyIOClient`
2657
- * hands over, or a locale fixed for this builder
2658
- */
2659
- constructor(httpClient: HttpClient, locale?: ClientLocale);
2660
- /**
2661
- * Add a fetch block to retrieve content
2662
- * @param config - Fetch configuration
2663
- */
2664
- fetch(config: FetchBlock): this;
2665
- /**
2666
- * Fetch from a content type
2667
- * @param contentType - Content type to fetch
2668
- */
2669
- from(contentType: string): this;
2670
- /**
2671
- * Fetch a singleton
2672
- * @param singletonKey - Singleton key
2673
- */
2674
- singleton(singletonKey: string): this;
2675
- /**
2676
- * Add aggregate block
2677
- * @param config - Aggregate configuration
2678
- */
2679
- aggregate(config: AggregateBlock): this;
2680
- /**
2681
- * Add search block
2682
- * @param config - Search configuration
2683
- */
2684
- search(config: SearchBlock): this;
2685
- /**
2686
- * Add where conditions to fetch
2687
- * @param conditions - Filter conditions
2688
- */
2689
- where(conditions: Record<string, unknown>): this;
2690
- /**
2691
- * Add include for reference population
2692
- * @param fields - Reference fields to include
2693
- */
2694
- include(fields: string | string[]): this;
2695
- /**
2696
- * Add sort specification
2697
- * @param sort - Sort configuration
2698
- */
2699
- sort(sort: Record<string, 'asc' | 'desc'>): this;
2700
- /**
2701
- * Sort by field ascending
2702
- * @param field - Field to sort by
2703
- */
2704
- sortAsc(field: string): this;
2705
- /**
2706
- * Sort by field descending
2707
- * @param field - Field to sort by
2708
- */
2709
- sortDesc(field: string): this;
2710
- /**
2711
- * Limit results
2712
- * @param count - Maximum results
2713
- */
2714
- limit(count: number): this;
2715
- /**
2716
- * Skip results
2717
- * @param count - Number to skip
2718
- */
2719
- offset(count: number): this;
2720
- /**
2721
- * Select and transform fields in output
2722
- * @param projection - Field mapping (outputField: sourceField)
2723
- */
2724
- select(projection: Record<string, string>): this;
2725
- /**
2726
- * Add a post-fetch filter (supports ${param} syntax)
2727
- * @param field - Field path to filter
2728
- * @param condition - Filter condition
2729
- */
2730
- filter(field: string, condition: FilterValue): this;
2731
- /**
2732
- * Add full-text search
2733
- * @param config - Full-text configuration
2734
- */
2735
- fullText(config: FullTextConfig): this;
2736
- /**
2737
- * Set query parameters
2738
- * @param params - Parameter values
2739
- */
2740
- params(params: Record<string, unknown>): this;
2741
- /**
2742
- * Set a single parameter
2743
- * @param name - Parameter name
2744
- * @param value - Parameter value
2745
- */
2746
- param(name: string, value: unknown): this;
2747
- /**
2748
- * Enable debug mode
2749
- */
2750
- debug(): this;
2751
- /**
2752
- * Skip cache for this request
2753
- */
2754
- skipCache(): this;
2755
- /**
2756
- * Set custom timeout
2757
- * @param ms - Timeout in milliseconds (1000-60000)
2758
- */
2759
- timeout(ms: number): this;
2760
- /**
2761
- * Set cache policy
2762
- * @param ttl - Cache TTL in seconds
2763
- * @param strategy - Cache strategy
2764
- */
2765
- cache(ttl: number, strategy?: 'stale-while-revalidate' | 'cache-first' | 'network-first'): this;
2766
- /**
2767
- * Get the query definition (for debugging)
2768
- */
2769
- getDefinition(): AdhocQueryDefinition;
2770
- /**
2771
- * Get the parameters (for debugging)
2772
- */
2773
- getParameters(): Record<string, unknown>;
2774
- /**
2775
- * Execute the query
2776
- */
2777
- execute(): Promise<AdhocQueryResult<T>>;
2778
- /**
2779
- * Execute and return the records as an array (for list queries).
2780
- */
2781
- items(): Promise<T[]>;
2782
- /**
2783
- * Execute and return the single record (for singleton queries).
2784
- */
2785
- data(): Promise<T | undefined>;
2786
- /**
2787
- * Clone the builder
2788
- */
2789
- clone(): AdhocQueryBuilder<T>;
2790
- }
2791
- /**
2792
- * Client for executing ad-hoc queries
2793
- *
2794
- * @example
2795
- * ```typescript
2796
- * const queryClient = new AdhocQueryClient(httpClient)
2797
- *
2798
- * // Fluent builder
2799
- * const products = await queryClient.build()
2800
- * .from('product')
2801
- * .where({ status: 'active' })
2802
- * .limit(20)
2803
- * .execute()
2804
- *
2805
- * // Direct execution
2806
- * const result = await queryClient.execute({
2807
- * fetch: { contentType: 'product', limit: 10 }
2808
- * })
2809
- * ```
2810
- */
2811
- declare class AdhocQueryClient {
2812
- private readonly httpClient;
2813
- /** The client's locale, asked on every query rather than copied when this client was taken. */
2814
- private readonly clientLocale;
2815
- /**
2816
- * @param locale - The client's locale (`withLocale()`, `updateConfig({ locale })`), sent as the
2817
- * `locale` parameter of every query that binds none: the function `TypedRelyIOClient` hands
2818
- * over, or a locale fixed for this client
2819
- */
2820
- constructor(httpClient: HttpClient, locale?: ClientLocale);
2821
- /**
2822
- * Create a new query builder
2823
- */
2824
- build<T = unknown>(): AdhocQueryBuilder<T>;
2825
- /**
2826
- * Execute a query definition directly
2827
- * @param definition - Query definition
2828
- * @param parameters - Parameter values
2829
- * @param options - Execution options
2830
- * @param request - `headers` this one request adds to the client's own, e.g. `X-RelyIO-Dataset`
2831
- * to query another dataset the key can see. A name the client already sends is replaced for
2832
- * this request only.
2833
- */
2834
- execute<T = unknown>(definition: AdhocQueryDefinition, parameters?: Record<string, unknown>, options?: AdhocQueryOptions, request?: {
2835
- headers?: Record<string, string>;
2836
- }): Promise<AdhocQueryResult<T>>;
2837
- /**
2838
- * Quick fetch helper
2839
- * @param contentType - Content type
2840
- * @param options - Fetch options
2841
- */
2842
- fetch<T = unknown>(contentType: string, options?: Omit<FetchBlock, 'contentType'>): Promise<T[]>;
2843
- /**
2844
- * Quick singleton fetch helper
2845
- * @param singletonKey - Singleton key
2846
- */
2847
- fetchSingleton<T = unknown>(singletonKey: string): Promise<T | undefined>;
2848
- /**
2849
- * Quick aggregate helper
2850
- * @param source - Source content type
2851
- * @param metrics - Metrics to compute
2852
- * @param groupBy - Optional grouping field
2853
- */
2854
- aggregate<T = unknown>(source: string, metrics: Record<string, AggregateMetric>, groupBy?: string | string[]): Promise<T[]>;
2855
- }
2856
-
2857
- /**
2858
- * Content and resource errors.
2859
- *
2860
- * `ContentTypeNotFoundError` and `ContentNotFoundError` were deleted at API5.
2861
- * The mapper reached them by sniffing `CONTENT_TYPE_NOT_FOUND` and
2862
- * `CONTENT_NOT_FOUND`, neither of which is in the closed vocabulary, so neither
2863
- * class was reachable. Both cases are `RESOURCE_NOT_FOUND`, whose declared
2864
- * `details` are `{ kind, id? }` — the same two facts, from the one code a route
2865
- * can actually declare.
2866
- */
2867
- declare class ResourceNotFoundError extends SDKError {
2868
- readonly code = "RESOURCE_NOT_FOUND";
2869
- readonly retryable = false;
2870
- constructor(message?: string, statusCode?: number, cause?: Error, context?: Record<string, unknown>);
2871
- getUserMessage(): string;
2872
- }
2873
- declare class ResourceConflictError extends SDKError {
2874
- readonly code = "RESOURCE_CONFLICT";
2875
- readonly retryable = false;
2876
- constructor(message?: string, statusCode?: number, cause?: Error, context?: Record<string, unknown>);
2877
- getUserMessage(): string;
2878
- }
2879
- declare class ResourceExpiredError extends SDKError {
2880
- readonly code = "RESOURCE_EXPIRED";
2881
- readonly retryable = false;
2882
- constructor(message?: string, statusCode?: number, cause?: Error, context?: Record<string, unknown>);
2883
- getUserMessage(): string;
2884
- }
2885
- /**
2886
- * "Did the service say this resource does not exist?"
2887
- *
2888
- * Reads the **code**, not the status. Four contract codes are 4xx on a read and
2889
- * only one of them means "not there"; the incumbent checked `statusCode === 404`
2890
- * on the thrown value, and a second arm duck-typed a plain object for the same
2891
- * number so a hand-written test double would pass. Both are the branch
2892
- * `INV-API-CLIENT-IS-DERIVED` forbids where a code exists — and the second one
2893
- * only existed because the tests faked the boundary (rule 20). They now stub
2894
- * `fetch`, so the double is gone with the branch.
2895
- */
2896
- declare function isResourceNotFound(error: unknown): boolean;
2897
-
2898
- /**
2899
- * `relyio.changes()` — the changes feed as a typed long-poll iterator
2900
- * (`sdk-surfaces-spec.md` decisions 16–18).
2901
- *
2902
- * Long-poll only, on purpose. `GET /v1/changes` is an ordinary cursor-paged
2903
- * collection the committed document declares, so each poll is one `send` over
2904
- * the one HTTP client (`INV-SS-ONE-CORE`, `INV-API-CLIENT-IS-DERIVED`). The SSE
2905
- * branch the route also serves would be a second transport path inside that
2906
- * client, and `parseResponse` buffers a whole body; it stays for `EventSource`
2907
- * and curl consumers.
2908
- */
2909
-
2910
- type PollChanges = paths['/v1/changes']['get'];
2911
- /**
2912
- * The 200 body the document declares, read from its `application/json` content
2913
- * **by name**. The route also negotiates `text/event-stream`; naming the JSON
2914
- * content is what keeps that alternate from changing this type when the
2915
- * document gains it.
2916
- */
2917
- type ChangesPageBody = PollChanges['responses'][200]['content']['application/json'];
2918
- /**
2919
- * One change, as the committed document describes an item of the feed's `data`
2920
- * (`INV-SS-CHANGE-IS-IDS`: ids, content type, locale, kind and cursor — never a
2921
- * document).
2922
- *
2923
- * Read from the document, never written here: the api's route row names the
2924
- * `Change` component schema, and a change to it reaches this type when the
2925
- * document is regenerated. `changes.types.ts` holds it equal to the document's
2926
- * item, and fails if it is ever `unknown` again.
2927
- */
2928
- type Change = ChangesPageBody['data'][number];
2929
- /**
2930
- * A change as `relyio.changes()` and `relyio.liveChanges()` deliver it: the document's
2931
- * {@link Change}, and `fresh`.
2932
- *
2933
- * `fresh` is the feed position a refetch for this change names, as the one query term the API
2934
- * declares on every read its edge may keep (`?_fresh`, `sdk-surfaces-spec.md` decision 32): hand it
2935
- * to the read — `.fresh(change.fresh)` on `findById()`, `query()` and `search()`, `{ fresh }` to a
2936
- * singleton's `fetch()`. A position no request named before the change is a key no cache holds a
2937
- * body under, so the read is answered after the change — at the edge and in the browser, whether
2938
- * or not the change's purge has reached them — and every later read naming it shares that answer.
2939
- *
2940
- * It is at or after the change, never before: on `/v1/changes` the change's own cursor, the ledger
2941
- * row it came from; at the live edge the cursor of the batch it arrived in. It is not always
2942
- * `cursor`: at the edge an earlier change of a batch carries the position **before** the batch as
2943
- * its `cursor`, so a failure part-way through replays the batch, and a refetch naming that position
2944
- * would be answered from before the change.
2945
- */
2946
- type FeedChange = Change & {
2947
- /** The feed position a refetch for this change names (`?_fresh`); at or after the change. */
2948
- readonly fresh: string;
2949
- };
2950
- /**
2951
- * Why a cursor could no longer be replayed, or why everything must be refetched. `/v1/changes`
2952
- * names `retention` and `dataset-replaced`; the live edge's `feed.resync` may name any reason its
2953
- * wire declares (`VisitorResyncReason` in `@relyio/api-contract`, read from there rather than
2954
- * restated). `unknown` when the message named none.
2955
- */
2956
- type ChangesExpiredReason = VisitorResyncReason | 'unknown';
2957
- /**
2958
- * `RESOURCE_EXPIRED` from the changes feed: the cursor names a position the
2959
- * ledger can no longer replay from (`INV-SS-CURSOR-REPLAYS`). The consumer
2960
- * resyncs from the read path and starts again without a cursor.
2961
- *
2962
- * A `ResourceExpiredError`, so code that already catches that still does. The
2963
- * refusal declares no details, so `reason` is read from the message the api
2964
- * writes (`apps/services/api/src/changes/changes-request.ts` `refusalOf`).
2965
- *
2966
- * The live edge's `feed.resync` — a socket's frame or a poll's answer — is the
2967
- * same instruction and arrives as the same error ({@link ChangesExpiredError.resync}),
2968
- * so `onExpired` means one thing on both transports.
2969
- */
2970
- declare class ChangesExpiredError extends ResourceExpiredError {
2971
- readonly reason: ChangesExpiredReason;
2972
- /**
2973
- * The position the feed resynced to, which a refetch of everything names as `?_fresh`, as a
2974
- * change's {@link FeedChange.fresh} is named: the head a `/v1/changes` resync took, or the cursor
2975
- * the edge's `feed.resync` sent. `undefined` when there was no resync to take one — the error was
2976
- * thrown (`onExpired: 'throw'`) — and on an error a caller built itself.
2977
- */
2978
- readonly fresh: string | undefined;
2979
- /** The base class's signature, so `withContext` can rebuild one; then the position resynced to. */
2980
- constructor(message: string, statusCode?: number, cause?: Error, context?: Record<string, unknown>, fresh?: string);
2981
- /** The base class rebuilds from its four arguments, which would drop the position resynced to. */
2982
- withContext(context: Record<string, unknown>): this;
2983
- /** `/v1/changes`' refusal, with the head the feed resynced to when it did. */
2984
- static of(expired: ResourceExpiredError, fresh?: string): ChangesExpiredError;
2985
- /** The live edge's `feed.resync`: refetch everything, then continue from the cursor it sent. */
2986
- static resync(reason: VisitorResyncReason, cursor: string): ChangesExpiredError;
2987
- }
2988
- interface ChangesOptions {
2989
- /**
2990
- * A `page.nextCursor` — or a change's `cursor` — from an earlier poll. Omitted,
2991
- * the first poll answers the dataset's current position and no changes, at
2992
- * once, and the feed starts from there.
2993
- */
2994
- since?: string;
2995
- /** Content types to keep. Omitted keeps every type; an empty list is refused (400). */
2996
- types?: readonly string[];
2997
- /**
2998
- * Entry ids to keep. Omitted keeps every entry. The api refuses more than it
2999
- * allows (100 today) as `REQUEST_INVALID`, thrown from the iterator as an
3000
- * `InvalidPayloadError` on the first poll; the SDK does not restate the
3001
- * cap, so it cannot drift from the service's.
3002
- */
3003
- ids?: readonly string[];
3004
- /** Ledger rows one poll scans, 1 to 500; the api defaults to 100. */
3005
- limit?: number;
3006
- /**
3007
- * Ends the iteration. An abort — in flight, between polls, or while waiting
3008
- * out a `Retry-After` — makes the `for await` **complete**, with no further
3009
- * request and no `AbortError` thrown into the loop.
3010
- */
3011
- signal?: AbortSignal;
3012
- /**
3013
- * What to do when the cursor has expired.
3014
- *
3015
- * - `'throw'` (default) — the iterator throws {@link ChangesExpiredError}.
3016
- * - `'resync'` — restart from the dataset's current position and keep going.
3017
- * - a function — resync, and call it with the error once the new position is
3018
- * taken and before any change after it is yielded. This is the observable
3019
- * form: whatever the consumer derived from the feed may be stale, so the
3020
- * function is where it invalidates everything — naming the error's `fresh`,
3021
- * the new position, on every refetch. The iterator waits for it.
3022
- */
3023
- onExpired?: 'throw' | 'resync' | ((expired: ChangesExpiredError) => void | Promise<void>);
3024
- /**
3025
- * Called once, when the feed was opened without `since`, with the position it starts from: the
3026
- * first poll's answer, the dataset's head — before the next poll is sent. Read what you show
3027
- * **after** it and a write your read did not see is after the position, so this feed delivers it,
3028
- * with no second request to take the position first (`changesHead()` is that request) (REL-1513).
3029
- *
3030
- * `followChanges()` passes it (`ChangeSourceOptions.reached`), so a feed that drops before its first
3031
- * change reopens from there rather than re-reading everything. Never called with a later empty
3032
- * page's cursor: filtered, that cursor is past rows the filter hid, and a caller that widens the
3033
- * filter must not reopen from it.
3034
- */
3035
- reached?: (cursor: string) => void;
3036
- }
3037
- interface ChangesHeadOptions {
3038
- /** Cancels the request; the promise then rejects. */
3039
- signal?: AbortSignal;
3040
- }
3041
-
3042
- /**
3043
- * `relyio.liveChanges()` — a dataset's published changes at RelyIO's live edge, for a publishable key
3044
- * (`live-layer-spec.md` decision 20; LV5.2, REL-1158).
3045
- *
3046
- * The wire is `@relyio/api-contract`'s `live-visitor.ts`, read from there and never restated: the
3047
- * path and query names, the frames and their reader, the keepalive, the poll's answer and its reader,
3048
- * the poll interval, and what each close code and each refused poll means a client does next.
3049
- *
3050
- * **One open, two ways in.** A subscription opens a WebSocket to the edge. Where it cannot — the
3051
- * runtime has no `WebSocket` (Node 18 and 20), constructing one throws, the socket fails before it
3052
- * opens (a network that blocks sockets), or it has not opened and been caught up within
3053
- * {@link LIVE_SOCKET_OPEN_TIMEOUT_MS} (a network that holds it) — it polls the edge instead, from the
3054
- * same cursor, about every {@link VISITOR_POLL_INTERVAL_MS}, and tries a socket again about once a
3055
- * minute. Both carry one cursor space, so a switch either way loses nothing.
3056
- *
3057
- * **Where it starts.** Without `since`, the edge answers the dataset's current position before
3058
- * anything else — the socket's `feed.opened`, or the first poll's answer — and the subscription hands
3059
- * it to `reached`. That call is the position, with no request of its own for it: a read sent after it
3060
- * is answered at or after the position, and every change after it is delivered here, so a publish the
3061
- * read did not see is announced (REL-1513). The edge counts the subscription as one live connection,
3062
- * whichever way in it took (`INV-LV-LIVE-IS-METERED`).
3063
- *
3064
- * **It ends only when its signal aborts, and throws every other end** — a socket closed by a refusal
3065
- * or a deploy, a refused poll, a keepalive nobody answered — as a `LiveEdgeError` saying what to do
3066
- * next, so `followChanges()` reconnects, backs off or stops by the edge's own tables. That loop is
3067
- * the one reconnect policy (`INV-SS-ONE-CORE`, REL-1215); this module keeps no back-off of its own.
3068
- *
3069
- * **Cursors.** The edge sends one cursor after a batch of changes. The last change of a batch carries
3070
- * it; each earlier one carries the position before the batch, so a failure part-way through a batch
3071
- * replays the whole batch rather than skipping its rest. Delivery is at-least-once: a replay and the
3072
- * live frames after it may name one change twice, and a refetch makes a repeat cost a read, never a
3073
- * wrong view (`INV-SS-CHANGE-IS-IDS`).
3074
- *
3075
- * **What a refetch names.** Every change of a batch carries the batch's own cursor as `fresh`
3076
- * (`FeedChange`), the position its refetch names as `?_fresh` — not `cursor`, which for an earlier
3077
- * change is the position before the batch: the previous batch's refetches were answered under that
3078
- * one, before this batch, and a cache would hand the same answer back. A `feed.resync`'s error
3079
- * carries the cursor it sent the same way.
3080
- */
3081
-
3082
- /**
3083
- * How often an open socket sends the keepalive, which the edge's runtime answers without waking the
3084
- * feed — the Studio's dataset room sends its own on the same interval. A socket that has heard
3085
- * nothing by the next one, not even the answer, is presumed dead and reconnected: a client that only
3086
- * receives never learns otherwise that a network change dropped its connection.
3087
- */
3088
- declare const LIVE_KEEPALIVE_INTERVAL_MS = 30000;
3089
- /**
3090
- * How long a socket has to open and be caught up — the feed's `feed.opened` — before the subscription
3091
- * stops waiting on it, closes it and polls instead, as it does for a socket that fails before it opens
3092
- * (REL-1513).
3093
- *
3094
- * The poll interval, because that is what a socket is for: telling the page sooner than a poll would.
3095
- * A poll is answered at once and polled again within this interval, so a socket that has not caught
3096
- * up by then has already served the page worse than polling would have. It is well past a healthy
3097
- * open — the door's checks and one ledger read at the edge — and half the keepalive's verdict on an
3098
- * open socket that has gone quiet ({@link LIVE_KEEPALIVE_INTERVAL_MS}, twice). It bounds how long
3099
- * `<RelyIOProvider live>`'s first reads wait for a position on a network that neither opens a socket
3100
- * nor refuses one — a proxy that holds the upgrade — which without it they would wait on for as long
3101
- * as the page is open.
3102
- */
3103
- declare const LIVE_SOCKET_OPEN_TIMEOUT_MS = 15000;
3104
- interface LiveChangesOptions {
3105
- /**
3106
- * A change's cursor from an earlier subscription. Omitted, the edge answers the dataset's current
3107
- * position first and replays nothing, and the subscription hands that position to `reached` before
3108
- * anything after it. Read what you show **after** that first call — a read sent before it, from a
3109
- * subscription opened afterwards without `since`, leaves a publish between the two that neither
3110
- * sees (REL-1513). With `followChanges()`, which hands the source its own `reached`, call both —
3111
- * and let the read go if the loop fails first, so content never waits on a feed that is down:
3112
- *
3113
- * ```ts
3114
- * let positioned!: () => void
3115
- * const position = new Promise<void>(resolve => (positioned = resolve))
3116
- * const following = followChanges(
3117
- * from =>
3118
- * relyio.liveChanges({
3119
- * ...from,
3120
- * ids: [id],
3121
- * reached: cursor => {
3122
- * from.reached(cursor)
3123
- * positioned()
3124
- * },
3125
- * }),
3126
- * { signal, onChange, onResync, onState: state => state.kind !== 'following' && positioned() },
3127
- * )
3128
- * await position
3129
- * const post = await relyio.content('post').findById(id).execute()
3130
- * ```
3131
- *
3132
- * The wait is bounded: a socket that has not opened and been told its position within
3133
- * {@link LIVE_SOCKET_OPEN_TIMEOUT_MS} is given up for a poll, answered at once.
3134
- */
3135
- since?: string;
3136
- /** Content types to keep. Omitted keeps every type; an empty list is refused. At most 50. */
3137
- types?: readonly string[];
3138
- /** Entry ids to keep. Omitted keeps every entry; an empty list is refused. At most 100. */
3139
- ids?: readonly string[];
3140
- /**
3141
- * Ends the subscription. An abort — while connected, while polling, or between polls — makes the
3142
- * `for await` **complete**, with the socket closed, no further request and nothing thrown.
3143
- */
3144
- signal?: AbortSignal;
3145
- /**
3146
- * What to do when the edge says to refetch everything (`feed.resync`: the cursor is too old, was
3147
- * never this dataset's, or the dataset was replaced) — as `relyio.changes()` takes it: `'throw'`
3148
- * (default) throws a `ChangesExpiredError`, `'resync'` carries on from the edge's position, and a
3149
- * function is awaited with the error before any change after that position is yielded.
3150
- */
3151
- onExpired?: ChangesOptions['onExpired'];
3152
- /**
3153
- * Called when the edge has the subscription in hand — its `feed.opened` frame, or an answered
3154
- * poll. `followChanges()` passes it to reset its back-off; see `ChangeSourceOptions.opened`.
3155
- */
3156
- opened?: () => void;
3157
- /**
3158
- * Called with a position reached without a change to carry it. `followChanges()` passes it so a
3159
- * connection dropped before its first change reconnects from there; see
3160
- * `ChangeSourceOptions.reached`. Without `since`, its first call is the position the subscription
3161
- * starts from (see `since`).
3162
- */
3163
- reached?: (cursor: string) => void;
3164
- }
3165
-
3166
- /**
3167
- * The key's project, as `GET /v1/project` describes it: organization, project, dataset and branch,
3168
- * each by id and name, and `studioUrl` — the Studio root the preview toolbar deep-links from
3169
- * (`sdk-surfaces-spec.md` decision 24), or `null` when the service has no Dashboard address.
3170
- *
3171
- * Read from the committed document's `application/json` content by name, never written here
3172
- * (`INV-API-CLIENT-IS-DERIVED`); `derived-transport.types.ts` holds the two equal.
3173
- */
3174
- type Project = paths['/v1/project']['get']['responses'][200]['content']['application/json']['data'];
3175
- /**
3176
- * Client configuration with optional schema
3177
- */
3178
- interface TypedClientConfig<TSchema extends AnySchemaDefinition | undefined = undefined> extends RelyIOConfig {
3179
- /** Schema definition for type inference (optional - omit for dynamic/untyped usage) */
3180
- schema?: TSchema;
3181
- }
3182
- /**
3183
- * Typed RelyIO client with full schema awareness
3184
- *
3185
- * @example
3186
- * ```typescript
3187
- * import { createClient } from '@relyio/sdk'
3188
- * import { schema } from './schema'
3189
- *
3190
- * const relyio = createClient({
3191
- * apiKey: process.env.RELYIO_API_KEY!,
3192
- * schema,
3193
- * })
3194
- *
3195
- * // Content operations
3196
- * const products = await relyio.content('product').query().execute()
3197
- *
3198
- * // Search
3199
- * const results = await relyio.search('wireless').in('product').execute()
3200
- *
3201
- * // Batch operations
3202
- * const batch = await relyio.batch()
3203
- * .create('product', { content: { title: 'New' } })
3204
- * .execute()
3205
- * ```
3206
- */
3207
- declare class TypedRelyIOClient<TSchema extends AnySchemaDefinition | undefined = undefined> {
3208
- /**
3209
- * The one HTTP client every sub-client sends through. `updateConfig()` and `setApiKey()` change it
3210
- * in place rather than replacing it, so a sub-client taken before either sends through the change.
3211
- */
3212
- private readonly httpClient;
3213
- private config;
3214
- /**
3215
- * The configuration as `createClient()`, `setApiKey()` and `updateConfig()` gave it, before the
3216
- * `relyio dev` handshake: what the next update is laid over, and what `withLocale()` starts from.
3217
- */
3218
- private configured;
3219
- private _sandbox;
3220
- private _schema;
3221
- /**
3222
- * The client's locale as it is when a call is made. Every sub-client and builder is handed this
3223
- * rather than a copy of the locale, so one taken before `updateConfig({ locale })` sends the new
3224
- * locale, or none after `locale: null` (REL-1453).
3225
- */
3226
- private readonly currentLocale;
3227
- /** The live edge's poll client id: chosen once, when first needed, and kept for this client's life. */
3228
- private _liveClientId;
3229
- constructor(config: TypedClientConfig<TSchema>);
3230
- /**
3231
- * Get the schema definition
3232
- */
3233
- get schema(): TSchema;
3234
- /**
3235
- * The `relyio dev` sandbox this client resolved at construction, as the preview toolbar displays
3236
- * it (`sdk-surfaces-spec.md` decision 22).
3237
- *
3238
- * Plain, JSON-serializable data and no key, so a server component can hand it to a client
3239
- * component as a prop — the browser cannot read the handshake itself, so a client created there
3240
- * reports `absent`. See {@link SandboxState} for what each state means.
3241
- *
3242
- * @example
3243
- * ```tsx
3244
- * // app/layout.tsx — a server component handing the state to a client component
3245
- * <SandboxBadge sandbox={relyio.sandbox} />
3246
- * ```
3247
- */
3248
- get sandbox(): SandboxState;
3249
- /**
3250
- * Get the ad-hoc query client. Every query it sends is in the client's locale as it is then.
3251
- */
3252
- get query(): AdhocQueryClient;
3253
- /**
3254
- * Get a content client for a specific content type
3255
- * Type-safe when schema is provided: autocompletes content type names AND field names
3256
- *
3257
- * @example
3258
- * ```typescript
3259
- * const products = await relyio.content('product')
3260
- * .query()
3261
- * .where({ status: 'active' }) // ← Field names autocomplete with schema!
3262
- * .locale('en-US')
3263
- * .limit(20)
3264
- * .execute()
3265
- * ```
3266
- */
3267
- content<K extends TSchema extends AnySchemaDefinition ? SchemaContentTypeNames<TSchema> : string>(contentType: K): TSchema extends AnySchemaDefinition ? ContentClient<InferContentType<TSchema['contentTypes'][K & keyof TSchema['contentTypes']], TSchema>, ContentReading<TSchema['contentTypes'][K & keyof TSchema['contentTypes']], TSchema>> : ContentClient<BaseContent>;
3268
- /**
3269
- * Get an asset client for a specific asset type.
3270
- *
3271
- * Mirrors `content()` but targets `entryType: 'asset'` entries on the same
3272
- * dynamic `/{assetTypeId}` routes, and adds the `file(entryUid)` sub-resource
3273
- * builder + a high-level `upload()` helper. Type-safe when a schema is
3274
- * provided: autocompletes asset type names AND metadata field names.
3275
- *
3276
- * @example
3277
- * ```typescript
3278
- * const photo = await relyio.asset('productPhoto').upload(file)
3279
- * .filename('shoe.jpg')
3280
- * .visibility('public')
3281
- * .execute()
3282
- *
3283
- * const heroUrl = await relyio.asset('productPhoto').file(photo._metadata.id).url({ variantId: 'hero' })
3284
- * ```
3285
- */
3286
- asset<K extends TSchema extends AnySchemaDefinition ? SchemaAssetTypeNames<TSchema> : string>(assetType: K): TSchema extends AnySchemaDefinition ? AssetClient<InferAssetType<NonNullable<TSchema['assetTypes']>[K & keyof NonNullable<TSchema['assetTypes']>], TSchema>, AssetReading<NonNullable<TSchema['assetTypes']>[K & keyof NonNullable<TSchema['assetTypes']>], TSchema>> : AssetClient<BaseContent>;
3287
- /**
3288
- * Get a singleton client for a specific singleton key
3289
- * Type-safe when schema is provided: autocompletes singleton names AND field names
3290
- *
3291
- * @example
3292
- * ```typescript
3293
- * const homepage = await relyio.singleton('homepage')
3294
- * .include(['featuredProducts'])
3295
- * .locale('en')
3296
- * .fetch()
3297
- * ```
3298
- */
3299
- singleton<K extends TSchema extends AnySchemaDefinition ? SchemaSingletonNames<TSchema> : string>(key: K): TSchema extends AnySchemaDefinition ? SingletonClient<InferSingleton<TSchema['singletonContentTypes'][K & keyof TSchema['singletonContentTypes']], TSchema>, SingletonReading<TSchema['singletonContentTypes'][K & keyof TSchema['singletonContentTypes']], TSchema>> : SingletonClient<unknown>;
3300
- /**
3301
- * Get a settings client for a specific settings group
3302
- * Type-safe when schema is provided: autocompletes settings group names AND field names
3303
- *
3304
- * @example
3305
- * ```typescript
3306
- * const emailConfig = await relyio.settings('email-config')
3307
- * .include(['defaultTemplate'])
3308
- * .fetch()
3309
- * ```
3310
- */
3311
- settings<K extends TSchema extends AnySchemaDefinition ? SchemaSettingsGroupNames<TSchema> : string>(groupId: K): TSchema extends AnySchemaDefinition ? SettingsClient<InferSettingsGroup<NonNullable<TSchema['settingsGroups']>[K & keyof NonNullable<TSchema['settingsGroups']>], TSchema>, SettingsReading<NonNullable<TSchema['settingsGroups']>[K & keyof NonNullable<TSchema['settingsGroups']>], TSchema>> : SettingsClient<unknown>;
3312
- /**
3313
- * Start a search query with full-text, semantic, or hybrid search.
3314
- *
3315
- * Where a schema types the client, the results are its entries — every content type's until
3316
- * `in()` or `across()` narrows them (`INV-SS-TYPES-INFERRED`).
3317
- *
3318
- * @example
3319
- * ```typescript
3320
- * const results = await relyio.search('wireless headphones')
3321
- * .in('product')
3322
- * .where({ status: 'active' })
3323
- * .limit(20)
3324
- * .includeScore()
3325
- * .execute()
3326
- * ```
3327
- */
3328
- search<T extends BaseContent = SearchEntry<TSchema>>(query: string): SearchBuilder<T, TSchema>;
3329
- /**
3330
- * Start an autocomplete query for search suggestions
3331
- *
3332
- * @example
3333
- * ```typescript
3334
- * const suggestions = await relyio.autocomplete('wire')
3335
- * .in('product')
3336
- * .field('title')
3337
- * .fuzzy()
3338
- * .limit(10)
3339
- * .execute()
3340
- * ```
3341
- */
3342
- autocomplete(query: string): AutocompleteBuilder;
3343
- /**
3344
- * Find content similar to a given entry using vector search
3345
- *
3346
- * @example
3347
- * ```typescript
3348
- * const similar = await relyio.similar('entry_abc123')
3349
- * .in('product')
3350
- * .locale('fr-FR')
3351
- * .limit(5)
3352
- * .execute()
3353
- * ```
3354
- */
3355
- similar<T extends BaseContent = BaseContent>(sourceId: string): SimilarBuilder<T>;
3356
- /**
3357
- * Start a batch operation for bulk create/update/delete.
3358
- *
3359
- * By default each operation lands or fails on its own. `atomic: true` makes the batch one
3360
- * transaction, which the API accepts only when every operation is the same kind of write and none
3361
- * writes a singleton that does not exist yet.
3362
- *
3363
- * @example
3364
- * ```typescript
3365
- * const result = await relyio.batch()
3366
- * .create('product', { content: { title: 'iPhone', price: 999 } })
3367
- * .update('product', 'entry_abc', { content: { price: 899 } })
3368
- * .delete('product', 'entry_xyz')
3369
- * .upsertSingleton('homepage', { content: { hero: 'Welcome!' } })
3370
- * .execute()
3371
- *
3372
- * console.log(result.summary.succeeded)
3373
- *
3374
- * // All or nothing: one kind of write
3375
- * await relyio.batch()
3376
- * .update('product', 'entry_abc', { content: { price: 899 } })
3377
- * .update('product', 'entry_def', { content: { price: 799 } })
3378
- * .execute({ atomic: true })
3379
- * ```
3380
- */
3381
- batch(): BatchBuilder<NonNullable<TSchema>>;
3382
- /** The search client, whose builders send the client's locale as it is when each one is sent. */
3383
- private searchClient;
3384
- /**
3385
- * Follow the dataset's changes feed: each write this key can see, as ids, in
3386
- * order, long-polled from `GET /v1/changes`.
3387
- *
3388
- * Lazy — nothing is requested until the first `next()`. It runs until
3389
- * `signal` aborts, which ends the `for await` cleanly rather than throwing.
3390
- * A change names what changed; refetch it through the ordinary read path
3391
- * (`INV-SS-CHANGE-IS-IDS`), naming its `fresh` so no cache answers with the
3392
- * body from before it (`FeedChange`).
3393
- *
3394
- * @example
3395
- * ```typescript
3396
- * const controller = new AbortController()
3397
- * for await (const change of relyio.changes({ since: cursor, types: ['post'], signal: controller.signal })) {
3398
- * // { entryId, contentType, locale, kind, cursor, fresh }
3399
- * await relyio.content(change.contentType).findById(change.entryId).fresh(change.fresh).execute()
3400
- * }
3401
- * ```
3402
- *
3403
- * @example Resync when the cursor has expired
3404
- * ```typescript
3405
- * for await (const change of relyio.changes({
3406
- * since: cursor,
3407
- * onExpired: async expired => refetchEverything(expired.fresh),
3408
- * })) { … }
3409
- * ```
3410
- */
3411
- changes(options?: ChangesOptions): AsyncIterable<FeedChange>;
3412
- /**
3413
- * Follow the dataset's **published** changes at RelyIO's live edge, with a publishable key
3414
- * (`rly_pub_…`): each change as ids, in order — the transport a browser follows changes with.
3415
- *
3416
- * A WebSocket to the edge, from `since` or the dataset's current position; where a socket cannot
3417
- * open — the runtime has none, the network refuses it, or it has not opened and been told its
3418
- * position within `LIVE_SOCKET_OPEN_TIMEOUT_MS` — it polls the edge instead, about every fifteen
3419
- * seconds, from the same cursor. The edge is `liveUrl`, or derived from the default `baseUrl`
3420
- * (see `RelyIOConfig.liveUrl`).
3421
- *
3422
- * Without `since` it starts at the dataset's current position, which it hands `reached` before
3423
- * anything after it: read what you show after that call, and a publish your read did not see is
3424
- * one this subscription announces (see `LiveChangesOptions.since`).
3425
- *
3426
- * One subscription ends only when `signal` aborts, which ends the `for await` cleanly; every other
3427
- * end — a socket a deploy dropped, a refusal, a refused poll — is thrown as a `LiveEdgeError`
3428
- * that says whether to stop, retry or back off. Hand it to `followChanges()`, the one loop that
3429
- * reconnects from the last cursor with jittered back-off and honours those answers:
3430
- *
3431
- * @example
3432
- * ```typescript
3433
- * const relyio = createClient({ apiKey: 'rly_pub_…' })
3434
- * const controller = new AbortController()
3435
- * await followChanges(from => relyio.liveChanges({ ...from, types: ['post'] }), {
3436
- * signal: controller.signal,
3437
- * // `fresh` names a position no cache holds a body for from before the change.
3438
- * onChange: change =>
3439
- * relyio.content(change.contentType).findById(change.entryId).fresh(change.fresh).execute(),
3440
- * onResync: resync => refetchEverything(resync.cause === 'expired' ? resync.expired.fresh : undefined),
3441
- * })
3442
- * ```
3443
- *
3444
- * A client without a publishable key sends nothing: a secret or preview key is never put in the
3445
- * edge's URL. On a server, follow `changes()` with it instead.
3446
- */
3447
- liveChanges(options?: LiveChangesOptions): AsyncIterable<FeedChange>;
3448
- /**
3449
- * The dataset's current position in the changes feed, as a cursor to start
3450
- * `changes({ since })` or `followChanges(…, { since })` from. One
3451
- * `GET /v1/changes` without a cursor: answered at once, holding nothing.
3452
- *
3453
- * Take it **before** you read what you will follow. A write your read did not
3454
- * see is then after the position and replayed; opening the feed afterwards
3455
- * without a `since` starts after it and misses it.
3456
- *
3457
- * @example
3458
- * ```typescript
3459
- * const since = await relyio.changesHead()
3460
- * const post = await relyio.content('post').findById(id).execute()
3461
- * for await (const change of relyio.changes({ since, ids: [id] })) { … }
3462
- * ```
3463
- */
3464
- changesHead(options?: ChangesHeadOptions): Promise<string>;
3465
- /**
3466
- * Build and execute an ad-hoc query dynamically
3467
- *
3468
- * @example
3469
- * ```typescript
3470
- * const result = await relyio.adhoc()
3471
- * .from('product')
3472
- * .where({ status: 'active' })
3473
- * .include(['brand', 'categories'])
3474
- * .select({
3475
- * id: 'id',
3476
- * title: 'title',
3477
- * price: 'pricing.basePrice'
3478
- * })
3479
- * .limit(20)
3480
- * .execute()
3481
- * ```
3482
- */
3483
- adhoc<T = unknown>(): AdhocQueryBuilder<T>;
3484
- /**
3485
- * Execute a predefined query by name (from schema)
3486
- * Type-safe when schema is provided: autocompletes query names
3487
- *
3488
- * @example
3489
- * ```typescript
3490
- * const featured = await relyio.executeQuery('featured-products', {
3491
- * limit: 10,
3492
- * category: 'electronics'
3493
- * })
3494
- * ```
3495
- */
3496
- executeQuery<K extends TSchema extends AnySchemaDefinition ? SchemaQueryNames<TSchema> : string>(queryName: K, params?: Record<string, unknown>): Promise<unknown[]>;
3497
- /**
3498
- * Describe the project this key resolves to — `GET /v1/project`.
3499
- *
3500
- * `dataset` is the one this client's requests select (`dataset`, a sandbox from the `relyio dev`
3501
- * handshake, or the key's default), which is not always the project's default, so a Studio link
3502
- * built from `studioUrl` should carry it as `?dataset=`. `studioUrl` is `null` — a value, not an
3503
- * error — when the service has no Dashboard address configured.
3504
- *
3505
- * @example
3506
- * ```typescript
3507
- * const { studioUrl, dataset } = await relyio.project()
3508
- * if (studioUrl !== null) window.open(`${studioUrl}?dataset=${dataset.id}`)
3509
- * ```
3510
- */
3511
- project(): Promise<Project>;
3512
- /**
3513
- * Get schema information from the API
3514
- */
3515
- getSchemaInfo(contentType?: string): Promise<unknown>;
3516
- /**
3517
- * List all content types from the API
3518
- */
3519
- listContentTypes(): Promise<Array<{
3520
- id: string;
3521
- name: string;
3522
- type: 'content' | 'singleton';
3523
- }>>;
3524
- /**
3525
- * Get API health status
3526
- */
3527
- health(): Promise<{
3528
- status: 'ok' | 'degraded' | 'down';
3529
- timestamp: string;
3530
- }>;
3531
- /**
3532
- * Check if the API is ready
3533
- */
3534
- ready(): Promise<boolean>;
3535
- /**
3536
- * Test the connection to the API
3537
- */
3538
- testConnection(): Promise<boolean>;
3539
- /**
3540
- * Change some of the client's options and keep the rest.
3541
- *
3542
- * What you pass is laid over the configuration the client holds, field by field:
3543
- *
3544
- * - an option you name replaces the value the client holds;
3545
- * - an option left out, or given as `undefined`, keeps the value the client holds;
3546
- * - an option with no default given as `null` is cleared — `locale`, `dataset`, `apiKey`, `dev`,
3547
- * `fetch`, `browser.onCorsError`, `realtime.wsUrl`, `debug.logger` — so the client runs as though
3548
- * it had never been given one. An option with a default takes no `null`;
3549
- * - a bag of options you name — `browser`, `cache`, `realtime`, `debug` — keeps every field of it
3550
- * you do not name, so `updateConfig({ browser: { includeCredentials: true } })` keeps a
3551
- * configured `onCorsError`;
3552
- * - `headers` is laid over the headers the client sends, a name matched without regard to case: a
3553
- * header you name is added or replaces every spelling of it, every other one is still sent, and a
3554
- * header given as `null` is no longer sent;
3555
- * - naming `apiKey` — a key, or `null` — replaces every key the client holds, one given in
3556
- * `headers` included.
3557
- *
3558
- * The configuration it is laid over is what `createClient()`, `setApiKey()` and earlier updates
3559
- * gave the client. The `relyio dev` handshake is read again, as `createClient()` reads it: while a
3560
- * sandbox is active its key and dataset are sent over the configured ones, and once it has gone
3561
- * the configured ones are.
3562
- *
3563
- * The client's one HTTP client is changed in place, so every request from now on is sent as the
3564
- * update says — including one from a `content()`, `asset()`, `singleton()` or `settings()`
3565
- * sub-client, a `query` client, or a builder such as `search()` or `batch()`, taken before the
3566
- * call. The locale reaches them too: each reads the client's locale when it sends, so a new
3567
- * `locale` is the default of its next request and `locale: null` leaves that request with none. A
3568
- * locale the call names, and a sub-client's own `withLocale()`, still win over it. A client made
3569
- * by `withLocale()` is a client of its own: an update of this one does not reach it.
3570
- *
3571
- * @example
3572
- * ```typescript
3573
- * const relyio = createClient({ apiKey, headers: { 'X-Tenant': 'acme' }, browser: { onCorsError } })
3574
- *
3575
- * relyio.updateConfig({ headers: { 'X-Trace': traceId } }) // X-Tenant is still sent
3576
- * relyio.updateConfig({ browser: { includeCredentials: true } }) // onCorsError is kept
3577
- * relyio.updateConfig({ headers: { 'X-Tenant': null } }) // X-Tenant is no longer sent
3578
- * relyio.updateConfig({ locale: null }) // reads name no locale of the client's
3579
- * ```
3580
- */
3581
- updateConfig(update: RelyIOConfigUpdate): void;
3582
- /**
3583
- * Get the current configuration
3584
- */
3585
- getConfig(): Readonly<MergedRelyIOConfig>;
3586
- /**
3587
- * Replace the API key every later request sends as `X-API-Key`.
3588
- *
3589
- * It reaches every request this client makes from now on, including those of a `content()`,
3590
- * `asset()` or other sub-client taken before the call, and `getConfig().apiKey` reports it. A key
3591
- * given in `headers`, under any spelling, is replaced with it and does not come back on a later
3592
- * `updateConfig()`. A client already derived with `withLocale()` is a client of its own and keeps
3593
- * its key.
3594
- *
3595
- * An empty or blank key is no key, as it is when passed to `createClient`: the next request
3596
- * rejects with `MissingApiKeyError` and nothing is sent.
3597
- *
3598
- * Under an active `relyio dev` sandbox the sandbox's scoped key stands, as it does over the key
3599
- * passed to `createClient` — replacing the configured key does not retarget the client at the
3600
- * sandbox's dataset with a key the sandbox did not issue.
3601
- *
3602
- * @example
3603
- * ```typescript
3604
- * const relyio = createClient({ apiKey: process.env.RELYIO_API_KEY! })
3605
- * relyio.setApiKey(rotatedKey) // the next read sends `X-API-Key: <rotatedKey>`
3606
- * ```
3607
- */
3608
- setApiKey(apiKey: string): void;
3609
- /**
3610
- * Get the HTTP client instance (for advanced usage)
3611
- */
3612
- getHttpClient(): HttpClient;
3613
- /**
3614
- * Create a locale-scoped client
3615
- *
3616
- * Every call the returned client builds is in this locale unless the call names one of its own,
3617
- * which wins: every read — `content()`, `asset()`, `singleton()`, `settings()`, `search()`,
3618
- * `similar()`, `autocomplete()`, `executeQuery()` and the ad-hoc queries — and every write and
3619
- * version-history read — `create()` and `update()`, the singleton and settings writes, the asset
3620
- * file writes, `versions()` and `version()`, and each operation `batch()` builds. `restore()`
3621
- * always names its locale; a delete, and a `batch().raw()` operation, carry none of the client's.
3622
- *
3623
- * Type-safe when schema is provided: autocompletes valid locale codes!
3624
- *
3625
- * @example
3626
- * ```typescript
3627
- * const spanish = relyio.withLocale('es-ES') // ← Autocompletes with schema!
3628
- *
3629
- * // Reads and writes use Spanish
3630
- * const products = await spanish.content('product').query().execute()
3631
- * const homepage = await spanish.singleton('homepage').fetch()
3632
- * const results = await spanish.search('zapatos').execute()
3633
- * await spanish.content('product').update(id, { title: 'Zapatos' })
3634
- *
3635
- * // Original client unchanged
3636
- * const englishProducts = await relyio.content('product').query().execute()
3637
- * ```
3638
- */
3639
- withLocale<L extends TSchema extends AnySchemaDefinition ? SchemaLocaleCodes<TSchema> : string>(locale: L): TypedRelyIOClient<TSchema>;
3640
- /**
3641
- * Get the current default locale
3642
- */
3643
- getLocale(): string | undefined;
3644
- /**
3645
- * Get available locales from the API
3646
- *
3647
- * @example
3648
- * ```typescript
3649
- * const { locales, defaultLocale } = await relyio.getLocales()
3650
- *
3651
- * // Build language switcher
3652
- * const options = locales.map(l => ({
3653
- * code: l.code,
3654
- * label: l.displayName,
3655
- * isDefault: l.default,
3656
- * }))
3657
- * ```
3658
- */
3659
- getLocales(search?: string): Promise<{
3660
- locales: Array<{
3661
- code: string;
3662
- displayName?: string;
3663
- default?: boolean;
3664
- fallback?: string[];
3665
- direction?: 'ltr' | 'rtl';
3666
- active?: boolean;
3667
- }>;
3668
- defaultLocale: string | null;
3669
- total: number;
3670
- }>;
3671
- /**
3672
- * Get the default locale from the API
3673
- */
3674
- getDefaultLocale(): Promise<string | null>;
3675
- }
3676
- /**
3677
- * Create a RelyIO client
3678
- *
3679
- * @example Without schema (dynamic usage)
3680
- * ```typescript
3681
- * import { createClient } from '@relyio/sdk'
3682
- *
3683
- * const relyio = createClient({
3684
- * apiKey: process.env.RELYIO_API_KEY!,
3685
- * })
3686
- *
3687
- * // Works but no autocomplete for content types
3688
- * const products = await relyio.content('product').fetchAll()
3689
- * ```
3690
- *
3691
- * @example With schema (fully typed)
3692
- * ```typescript
3693
- * import { createClient } from '@relyio/sdk'
3694
- * import { schema } from './schema'
3695
- *
3696
- * const relyio = createClient({
3697
- * apiKey: process.env.RELYIO_API_KEY!,
3698
- * schema,
3699
- * })
3700
- *
3701
- * // Content type names autocomplete!
3702
- * const products = await relyio.content('product').fetchAll()
3703
- * ```
3704
- *
3705
- * @example After `relyio generate-types` (inferred, `INV-SS-TYPES-INFERRED`)
3706
- * ```typescript
3707
- * import { createClient } from '@relyio/sdk'
3708
- *
3709
- * // The generated `schema.ts` augments `Register`, so no generic and no `schema:` are needed
3710
- * const relyio = createClient({ apiKey: process.env.RELYIO_API_KEY! })
3711
- * ```
3712
- *
3713
- * The overload without a schema comes first on purpose. Behind the generic one it was never
3714
- * chosen: with no `schema` to infer from, `TSchema` fell back to its constraint and the call
3715
- * resolved to `TypedRelyIOClient<AnySchemaDefinition>`. First, it takes every call that passes no
3716
- * schema, and returns the registered schema's client — or the untyped one when nothing registered.
3717
- * A `schema:` value fails its `schema?: undefined`, and an explicit generic skips it because it has
3718
- * no type parameter, so both reach the generic overload unchanged.
3719
- */
3720
- declare function createClient(config: RelyIOConfig & {
3721
- schema?: undefined;
3722
- }): TypedRelyIOClient<RegisteredSchema>;
3723
- declare function createClient<TSchema extends AnySchemaDefinition>(config: TypedClientConfig<TSchema>): TypedRelyIOClient<TSchema>;
3724
- /**
3725
- * Helper type to extract schema from client
3726
- */
3727
- type ClientSchema<T extends TypedRelyIOClient<AnySchemaDefinition>> = T extends TypedRelyIOClient<infer TSchema> ? TSchema : never;
3728
-
3729
- 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 };