@relyio/sdk 0.2.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 (45) hide show
  1. package/CHANGELOG.md +13 -0
  2. package/LICENSE +201 -0
  3. package/README.md +314 -0
  4. package/dist/.tsbuildinfo +1 -0
  5. package/dist/chunk-2QUK2ZGD.js +738 -0
  6. package/dist/chunk-7W4MJXQ4.cjs +4429 -0
  7. package/dist/chunk-FJK7ZWKK.cjs +776 -0
  8. package/dist/chunk-HT6NFRM2.cjs +196 -0
  9. package/dist/chunk-I3ESNANB.js +177 -0
  10. package/dist/chunk-IY5LSKXU.js +3 -0
  11. package/dist/chunk-MWMSFIWN.cjs +17 -0
  12. package/dist/chunk-PPA2WWOH.cjs +4 -0
  13. package/dist/chunk-RJBIJ2ZK.js +4375 -0
  14. package/dist/chunk-XQ4QHWZE.js +13 -0
  15. package/dist/client/index.cjs +17 -0
  16. package/dist/client/index.d.cts +3 -0
  17. package/dist/client/index.d.ts +3 -0
  18. package/dist/client/index.js +4 -0
  19. package/dist/define-schema-DHvUCKwj.d.cts +2101 -0
  20. package/dist/define-schema-DHvUCKwj.d.ts +2101 -0
  21. package/dist/index-B-KiFHbx.d.ts +2708 -0
  22. package/dist/index-BsUqCAu8.d.cts +7855 -0
  23. package/dist/index-BsUqCAu8.d.ts +7855 -0
  24. package/dist/index-DGWaawHt.d.cts +2708 -0
  25. package/dist/index.cjs +499 -0
  26. package/dist/index.d.cts +523 -0
  27. package/dist/index.d.ts +523 -0
  28. package/dist/index.js +148 -0
  29. package/dist/react/index.cjs +4 -0
  30. package/dist/react/index.d.cts +2 -0
  31. package/dist/react/index.d.ts +2 -0
  32. package/dist/react/index.js +3 -0
  33. package/dist/schema/index.cjs +157 -0
  34. package/dist/schema/index.d.cts +223 -0
  35. package/dist/schema/index.d.ts +223 -0
  36. package/dist/schema/index.js +4 -0
  37. package/dist/types/index.cjs +6 -0
  38. package/dist/types/index.d.cts +1 -0
  39. package/dist/types/index.d.ts +1 -0
  40. package/dist/types/index.js +3 -0
  41. package/dist/utils/index.cjs +81 -0
  42. package/dist/utils/index.d.cts +87 -0
  43. package/dist/utils/index.d.ts +87 -0
  44. package/dist/utils/index.js +4 -0
  45. package/package.json +159 -0
@@ -0,0 +1,2708 @@
1
+ import { M as MergedRelyIOConfig, A as ApiPath, a as ApiMethod, G as PathParamsOf, r as ApiResponse, W as Wire, q as RequestConfig, B as BaseContent, c as WhereClause, e as SortOptions, f as ContentQueryResult, Q as QueryOptions, C as CreateInput, U as UpdateInput, S as SDKError, v as BaseValidationError, u as ValidationViolation, H as components, R as RelyIOConfig } from './index-BsUqCAu8.js';
2
+ import { x as SchemaDefinition, y as SchemaContentTypeNames, a6 as InferContentType, K as SchemaAssetTypeNames, at as InferAssetType, z as SchemaSingletonNames, ac as InferSingleton, J as SchemaSettingsGroupNames, an as InferSettingsGroup, G as SchemaQueryNames, H as SchemaLocaleCodes } from './define-schema-DHvUCKwj.js';
3
+
4
+ /**
5
+ * What a call site supplies beside the route it named.
6
+ *
7
+ * `path` is required exactly when the document's template has segments, and
8
+ * forbidden when it does not — so a call that forgot to scope a request, or one
9
+ * that scoped a request the route does not scope, is a compile error rather
10
+ * than a URL with a literal `{id}` in it.
11
+ */
12
+ type SendOptions<P extends ApiPath, M extends ApiMethod<P>> = {
13
+ /** Query string values. The document declares none, so this stays untyped — see `api-routes.ts`. */
14
+ query?: Record<string, unknown>;
15
+ body?: unknown;
16
+ /** Request headers this call adds, e.g. `If-Match` / `Idempotency-Key` (API4). */
17
+ headers?: Record<string, string>;
18
+ signal?: AbortSignal;
19
+ timeout?: number;
20
+ } & ([PathParamsOf<P, M>] extends [never] ? {
21
+ path?: never;
22
+ } : {
23
+ path: PathParamsOf<P, M>;
24
+ });
25
+ /**
26
+ * Core HTTP client for the RelyIO SDK.
27
+ *
28
+ * `send` is **the** typed entry: it names a route by the path template the
29
+ * committed document declares, and returns that operation's body type with the
30
+ * payload typed as `T`. `readPayload` from `@relyio/api-contract/client` is
31
+ * then the one unwrap — there is no second spelling and no runtime test of
32
+ * which envelope arrived (`INV-API-CLIENT-IS-DERIVED`).
33
+ *
34
+ * The verb helpers below stay for the escape hatch `getHttpClient()` exposes;
35
+ * nothing inside the SDK uses them.
36
+ */
37
+ declare class HttpClient {
38
+ private baseUrl;
39
+ private defaultHeaders;
40
+ private timeout;
41
+ private retryConfig;
42
+ private debug;
43
+ private logger?;
44
+ private config;
45
+ constructor(config: MergedRelyIOConfig);
46
+ /**
47
+ * Call a route the committed document declares, and get back the body that
48
+ * document says it serves.
49
+ */
50
+ send<P extends ApiPath, M extends ApiMethod<P>, T = unknown>(path: P, method: M, options: SendOptions<P, M>): Promise<ApiResponse<Wire<P, M, T>>>;
51
+ /**
52
+ * Call a route and return its payload — `send` plus the one unwrap, which is
53
+ * what all but a handful of call sites want.
54
+ */
55
+ fetchPayload<P extends ApiPath, M extends ApiMethod<P>, T = unknown>(path: P, method: M, options: SendOptions<P, M>): Promise<Wire<P, M, T> extends {
56
+ data: unknown;
57
+ } ? Wire<P, M, T>['data'] : never>;
58
+ /**
59
+ * Make an HTTP request with retry logic
60
+ */
61
+ request<T = unknown>(config: RequestConfig): Promise<ApiResponse<T>>;
62
+ /**
63
+ * Make a GET request
64
+ */
65
+ get<T = unknown>(url: string, params?: Record<string, unknown>): Promise<ApiResponse<T>>;
66
+ /**
67
+ * Make a POST request
68
+ */
69
+ post<T = unknown>(url: string, data?: unknown, params?: Record<string, unknown>): Promise<ApiResponse<T>>;
70
+ /**
71
+ * Make a PUT request
72
+ */
73
+ put<T = unknown>(url: string, data?: unknown, params?: Record<string, unknown>): Promise<ApiResponse<T>>;
74
+ /**
75
+ * Make a DELETE request
76
+ */
77
+ delete<T = unknown>(url: string, params?: Record<string, unknown>): Promise<ApiResponse<T>>;
78
+ /**
79
+ * Make a PATCH request
80
+ */
81
+ patch<T = unknown>(url: string, data?: unknown, params?: Record<string, unknown>): Promise<ApiResponse<T>>;
82
+ /**
83
+ * Set default headers for all requests
84
+ */
85
+ setDefaultHeaders(headers: Record<string, string>): void;
86
+ /**
87
+ * Set authentication token
88
+ */
89
+ setAuthToken(token: string): void;
90
+ /**
91
+ * Get current default headers
92
+ */
93
+ getDefaultHeaders(): Record<string, string>;
94
+ /**
95
+ * Update base URL
96
+ */
97
+ setBaseUrl(baseUrl: string): void;
98
+ /**
99
+ * Get current base URL
100
+ */
101
+ getBaseUrl(): string;
102
+ private makeRequest;
103
+ /**
104
+ * Filter headers that browsers don't allow to be set manually
105
+ */
106
+ private filterHeadersForBrowser;
107
+ /**
108
+ * Prepare request body based on content type and environment
109
+ */
110
+ private prepareRequestBody;
111
+ private parseResponse;
112
+ private resolveUrl;
113
+ private mergeRequestConfig;
114
+ private buildQueryString;
115
+ private headersToObject;
116
+ private calculateRetryDelay;
117
+ private sleep;
118
+ private log;
119
+ }
120
+
121
+ /**
122
+ * Children population options
123
+ */
124
+ interface ChildrenOptions {
125
+ /** Content types of children to include */
126
+ contentTypes?: string[];
127
+ /** Maximum children per entry */
128
+ limit?: number;
129
+ /** Recursion depth */
130
+ depth?: number;
131
+ }
132
+ /**
133
+ * Retained as an alias of {@link ContentQueryResult}.
134
+ *
135
+ * It used to add an optional `pagination: { page, pageSize, pageCount, total }`
136
+ * that the list route emitted only when `withCount` arrived — "a shape no
137
+ * generated type can describe", as that controller's own comment now puts it.
138
+ * API2 made the customer list the **cursor** dialect unconditionally
139
+ * (`INV-API-ONE-CUSTOMER-PAGE-DIALECT`), so no total reaches this surface and
140
+ * the extra member described a body the service cannot send.
141
+ */
142
+ type ExtendedQueryResult<T extends BaseContent = BaseContent> = ContentQueryResult<T>;
143
+ /**
144
+ * Fluent query builder for content operations
145
+ * Provides a chainable API for building complex content queries
146
+ *
147
+ * @example
148
+ * ```typescript
149
+ * const products = await client.content('product')
150
+ * .query()
151
+ * .where({ status: 'active' })
152
+ * .locale('en')
153
+ * .fields(['id', 'title', 'price'])
154
+ * .include(['brand', 'categories'])
155
+ * .sort({ title: 'asc' })
156
+ * .limit(20)
157
+ * .execute()
158
+ * ```
159
+ */
160
+ declare class ContentQueryBuilder<T extends BaseContent = BaseContent> {
161
+ private readonly httpClient;
162
+ private readonly contentType;
163
+ private readonly defaultLocale?;
164
+ private queryOptions;
165
+ private localeValue?;
166
+ private localesArray?;
167
+ private fetchAllLocales;
168
+ private fieldsProjection?;
169
+ private childrenOptions?;
170
+ private pageNumber?;
171
+ private pageSizeValue?;
172
+ constructor(httpClient: HttpClient, contentType: string, defaultLocale?: string | undefined);
173
+ /**
174
+ * Add where conditions to the query
175
+ */
176
+ where(conditions: WhereClause<T>): this;
177
+ /**
178
+ * Select specific fields to return
179
+ */
180
+ select(fields: (keyof T)[]): this;
181
+ /**
182
+ * Include related content in the response
183
+ */
184
+ include(relations: string[]): this;
185
+ /**
186
+ * Add sorting to the query
187
+ *
188
+ * @example
189
+ * ```typescript
190
+ * // Object format
191
+ * .sort({ createdAt: 'desc', title: 'asc' })
192
+ *
193
+ * // Field and direction format
194
+ * .sort('createdAt', 'desc')
195
+ * ```
196
+ */
197
+ sort(field: string, direction: 'asc' | 'desc'): this;
198
+ sort(sortOptions: SortOptions<T>): this;
199
+ /**
200
+ * Sort by a specific field in ascending order
201
+ */
202
+ sortAsc(field: keyof T | string): this;
203
+ /**
204
+ * Sort by a specific field in descending order
205
+ */
206
+ sortDesc(field: keyof T | string): this;
207
+ /**
208
+ * Limit the number of results
209
+ */
210
+ limit(count: number): this;
211
+ /**
212
+ * Set offset for pagination
213
+ */
214
+ offset(count: number): this;
215
+ /**
216
+ * Set cursor for cursor-based pagination
217
+ */
218
+ cursor(cursor: string): this;
219
+ /**
220
+ * Set locale for localized content
221
+ * @param locale - Locale code (e.g., 'en-US', 'de-DE', 'fr-FR')
222
+ *
223
+ * @example
224
+ * ```typescript
225
+ * // Fetch in Spanish
226
+ * .locale('es-ES')
227
+ * ```
228
+ */
229
+ locale(locale: string): this;
230
+ /**
231
+ * Fetch content in multiple specific locales
232
+ * Returns primary locale at top level, others in _metadata.localizations
233
+ *
234
+ * @param localeCodes - Array of locale codes to fetch
235
+ *
236
+ * @example
237
+ * ```typescript
238
+ * const product = await client.content('product')
239
+ * .findById('prod_123')
240
+ * .locales(['en-US', 'es-ES', 'de-DE'])
241
+ * .execute()
242
+ *
243
+ * // Access: product.title (first locale)
244
+ * // Access: product._metadata.localizations['es-ES'].title
245
+ * ```
246
+ */
247
+ locales(localeCodes: string[]): this;
248
+ /**
249
+ * Fetch content in all available locales
250
+ * Returns default locale at top level, others in _metadata.localizations
251
+ *
252
+ * @example
253
+ * ```typescript
254
+ * const product = await client.content('product')
255
+ * .findById('prod_123')
256
+ * .allLocales()
257
+ * .execute()
258
+ *
259
+ * // Access: product.title (default locale)
260
+ * // Access: product._metadata.localizations['es-ES'].title
261
+ * // Access: product._metadata.availableLocales
262
+ * ```
263
+ */
264
+ allLocales(): this;
265
+ /**
266
+ * Select specific fields to return (projection)
267
+ * @param fieldNames - Array of field names to include
268
+ *
269
+ * @example
270
+ * ```typescript
271
+ * .fields(['id', 'title', 'price', 'brand.name'])
272
+ * ```
273
+ */
274
+ fields(fieldNames: string[]): this;
275
+ /**
276
+ * Include children in results (for hierarchical content)
277
+ * @param options - Children options or boolean to enable with defaults
278
+ *
279
+ * @example
280
+ * ```typescript
281
+ * // Include all children
282
+ * .children(true)
283
+ *
284
+ * // Include with options
285
+ * .children({ contentTypes: ['variant'], limit: 10 })
286
+ * ```
287
+ */
288
+ children(options: ChildrenOptions | boolean): this;
289
+ /**
290
+ * Set page number for page-based pagination
291
+ * @param pageNum - Page number (1-based)
292
+ */
293
+ page(pageNum: number): this;
294
+ /**
295
+ * Set page size for page-based pagination
296
+ * @param size - Items per page
297
+ */
298
+ pageSize(size: number): this;
299
+ /**
300
+ * Execute the query and return all matching results
301
+ */
302
+ execute(): Promise<T[]>;
303
+ /**
304
+ * Execute the query and return the whole body — the items under `data`, the
305
+ * cursor facts under `page`.
306
+ */
307
+ executeWithMeta(): Promise<ContentQueryResult<T>>;
308
+ /**
309
+ * Execute the query and return a single result (first match)
310
+ */
311
+ first(): Promise<T | null>;
312
+ /**
313
+ * Get paginated results
314
+ */
315
+ paginate(limit?: number): Promise<ContentQueryResult<T>>;
316
+ /**
317
+ * Create an async iterator for streaming results
318
+ */
319
+ [Symbol.asyncIterator](): AsyncIterableIterator<T>;
320
+ /**
321
+ * Execute the query and return all results as an array (for large datasets)
322
+ */
323
+ all(): Promise<T[]>;
324
+ /**
325
+ * Clone the query builder
326
+ */
327
+ clone(): ContentQueryBuilder<T>;
328
+ /**
329
+ * Get the current query options
330
+ */
331
+ getQueryOptions(): QueryOptions<T>;
332
+ /**
333
+ * Execute the query and return the whole body.
334
+ *
335
+ * Identical to {@link executeWithMeta} now that the list serves one shape;
336
+ * kept as the name callers already import.
337
+ */
338
+ executeWithMetaExtended(): Promise<ExtendedQueryResult<T>>;
339
+ /**
340
+ * `count()` and `exists()` are gone, and their absence is the honest answer
341
+ * rather than an omission (`INV-RD-CAPABILITY-IS-HONEST`).
342
+ *
343
+ * `count()` sent `withCount` and read `pagination.total`; API2 moved the
344
+ * customer list onto the **cursor** dialect, whose page facts are
345
+ * `{ nextCursor, hasMore }` and nothing else, so no total reaches this
346
+ * surface at all. Keeping the method would have meant either a request that
347
+ * always throws or a number assembled from a page the caller happened to
348
+ * fetch — a count of what was returned, presented as a count of what matched
349
+ * (rule 6). The plan's §3.3 listed `count` among six methods a type would
350
+ * repair; what the type actually shows is that the capability is no longer
351
+ * there. A caller that wants "is there at least one" fetches one and looks at
352
+ * `data.length`; a caller that wants a true total uses `POST /v1/search`,
353
+ * which is the offset dialect precisely because a facet-bearing result wants
354
+ * one (Decision 5).
355
+ */
356
+ /**
357
+ * Build query parameters for the HTTP request
358
+ */
359
+ private buildQueryParams;
360
+ }
361
+ /**
362
+ * Specialized query builder for finding by ID
363
+ */
364
+ declare class ContentByIdBuilder<T extends BaseContent = BaseContent> {
365
+ private readonly httpClient;
366
+ private readonly contentType;
367
+ private readonly id;
368
+ private readonly defaultLocale?;
369
+ private includeRelations?;
370
+ private localeValue?;
371
+ private localesArray?;
372
+ private fetchAllLocales;
373
+ private childrenOptions?;
374
+ constructor(httpClient: HttpClient, contentType: string, id: string, defaultLocale?: string | undefined);
375
+ /**
376
+ * Include related content in the response
377
+ */
378
+ include(relations: string[]): this;
379
+ /**
380
+ * Set locale for localized content
381
+ * @param locale - Locale code (e.g., 'en-US', 'de-DE', 'fr-FR')
382
+ */
383
+ locale(locale: string): this;
384
+ /**
385
+ * Fetch content in multiple specific locales
386
+ * Returns primary locale at top level, others in _metadata.localizations
387
+ *
388
+ * @param localeCodes - Array of locale codes to fetch
389
+ */
390
+ locales(localeCodes: string[]): this;
391
+ /**
392
+ * Fetch content in all available locales
393
+ * Returns default locale at top level, others in _metadata.localizations
394
+ */
395
+ allLocales(): this;
396
+ /**
397
+ * Include children in results (for hierarchical content)
398
+ * @param options - Children options or boolean to enable with defaults
399
+ */
400
+ children(options: ChildrenOptions | boolean): this;
401
+ /**
402
+ * Execute the query and return the content item
403
+ */
404
+ execute(): Promise<T | null>;
405
+ }
406
+
407
+ /**
408
+ * API4's two request headers, exposed the way the SDK's ergonomics already
409
+ * expose everything else: as options on the call, not as raw headers.
410
+ *
411
+ * `apps/services/api` declares them on exactly six and seven routes
412
+ * respectively (`CONDITIONAL_WRITE_HEADERS` / `RETRYABLE_WRITE_HEADERS` in that
413
+ * service's `contract/routes.ts`), and the API4 closeout is explicit that the
414
+ * ten `/file/*` routes and `POST /batch` declare **neither**, because they
415
+ * write through `@relyio/asset-files` and the batch planner and would silently
416
+ * ignore the header. So these options are on the six and seven methods that
417
+ * reach them and nowhere else: a client that could pass `ifMatch` to a route
418
+ * that ignores it has been told a lie no gate can catch
419
+ * (`INV-RD-CAPABILITY-IS-HONEST`).
420
+ *
421
+ * The tag itself comes from `X-Entity-Tag`, which the single-entity reads serve
422
+ * and the list routes deliberately do not — one header cannot name every
423
+ * member's per-locale version. `entityTagOf` reads it off the response the SDK
424
+ * already returns, so the round trip is "read one, write it back" with no
425
+ * assembly from `_metadata` in between
426
+ * (`INV-API-CONDITIONAL-WRITE-IS-REACHABLE`).
427
+ */
428
+ interface RetryableWriteOptions {
429
+ /**
430
+ * A caller-chosen key that makes a retry of **this** write safe.
431
+ *
432
+ * A replay inside the service's window returns the original outcome rather
433
+ * than applying twice. The service derives that response from committed state
434
+ * pinned to the recorded version — it does not store bytes — so a replay after
435
+ * the version has been pruned is refused `RESOURCE_EXPIRED` rather than
436
+ * answered from a stale copy.
437
+ */
438
+ idempotencyKey?: string;
439
+ }
440
+ /**
441
+ * The six routes that additionally take `If-Match`.
442
+ *
443
+ * A creation has no prior entity to precondition on, so `POST /v1/{contentType}`
444
+ * takes `Idempotency-Key` and not this — which is why the two option types are
445
+ * separate rather than one optional-everything bag.
446
+ */
447
+ interface ConditionalWriteOptions extends RetryableWriteOptions {
448
+ /**
449
+ * The `X-Entity-Tag` value a prior read served, sent back as `If-Match`.
450
+ *
451
+ * The write applies only if that locale of that entry is still at the version
452
+ * the tag names; otherwise the service refuses `RESOURCE_CONFLICT` and names
453
+ * both versions.
454
+ */
455
+ ifMatch?: string;
456
+ }
457
+ /**
458
+ * The entity tag a single-entity read served, or `undefined` where the route
459
+ * serves none — a list, or a cache HIT, which cannot know the current version
460
+ * and honestly says nothing rather than serving a stale tag.
461
+ */
462
+ declare function entityTagOf(response: {
463
+ headers: Record<string, string>;
464
+ }): string | undefined;
465
+
466
+ /**
467
+ * Content fetch options
468
+ */
469
+ interface ContentFetchOptions<T extends BaseContent = BaseContent> {
470
+ /** Filter conditions */
471
+ where?: WhereClause<T>;
472
+ /** Maximum entries to return */
473
+ limit?: number;
474
+ /** Entries to skip (offset pagination) */
475
+ offset?: number;
476
+ /** Sort configuration */
477
+ sort?: Record<string, 'asc' | 'desc'>;
478
+ /** Reference fields to populate */
479
+ include?: string[];
480
+ /** Locale for localized content */
481
+ locale?: string;
482
+ /** Fields to select (projection) */
483
+ fields?: string[];
484
+ /** Children population options */
485
+ children?: ChildrenOptions | boolean;
486
+ }
487
+ /**
488
+ * Content client for managing content operations
489
+ * Provides CRUD operations and query building for a specific content type
490
+ *
491
+ * @example
492
+ * ```typescript
493
+ * const products = client.content('product')
494
+ *
495
+ * // Query with fluent API
496
+ * const active = await products.query()
497
+ * .where({ status: 'active' })
498
+ * .locale('en')
499
+ * .include(['brand'])
500
+ * .limit(20)
501
+ * .execute()
502
+ *
503
+ * // Quick fetch
504
+ * const all = await products.fetchAll()
505
+ * ```
506
+ */
507
+ declare class ContentClient<T extends BaseContent = BaseContent> {
508
+ private readonly httpClient;
509
+ private readonly contentType;
510
+ private readonly defaultLocale?;
511
+ constructor(httpClient: HttpClient, contentType: string, defaultLocale?: string | undefined);
512
+ /**
513
+ * Create a new query builder for this content type
514
+ */
515
+ query(): ContentQueryBuilder<T>;
516
+ /**
517
+ * Find content by ID with fluent options
518
+ */
519
+ findById(id: string): ContentByIdBuilder<T>;
520
+ /**
521
+ * Fetch all content items
522
+ * @param locale - Optional locale for localized content
523
+ */
524
+ fetchAll(locale?: string): Promise<T[]>;
525
+ /**
526
+ * Fetch content with query options
527
+ */
528
+ fetch(options?: ContentFetchOptions<T>): Promise<T[]>;
529
+ /**
530
+ * Fetch a single content item by ID
531
+ * @param id - Entry ID or slug
532
+ * @param options - Fetch options (include, locale)
533
+ */
534
+ fetchOne(id: string, options?: {
535
+ include?: string[];
536
+ locale?: string;
537
+ }): Promise<T | null>;
538
+ /**
539
+ * Fetch one entry **and the entity tag that read served**.
540
+ *
541
+ * This is the read half of a conditional write. `X-Entity-Tag` is minted from
542
+ * `(entryUid, locale, localeVersion)` and is exactly what `If-Match` takes,
543
+ * so a caller reads here, edits, and hands the same string back to `update`
544
+ * — with nothing to assemble in between
545
+ * (`INV-API-CONDITIONAL-WRITE-IS-REACHABLE`).
546
+ *
547
+ * `entityTag` is `undefined` when the response came from the cache: a cached
548
+ * body cannot know the current version, and an absent tag beats a stale one
549
+ * that would refuse the next write.
550
+ */
551
+ fetchOneWithTag(id: string, options?: {
552
+ include?: string[];
553
+ locale?: string;
554
+ }): Promise<{
555
+ entry: T;
556
+ entityTag?: string;
557
+ }>;
558
+ /**
559
+ * Fetch the first content item matching the conditions
560
+ * @param where - Filter conditions
561
+ * @param locale - Optional locale
562
+ */
563
+ fetchFirst(where?: WhereClause<T>, locale?: string): Promise<T | null>;
564
+ /**
565
+ * Create a new content item
566
+ * @param data - Content data
567
+ * @param options - Create options (slug, locale, parentId, sortOrder)
568
+ */
569
+ create(data: CreateInput<T>, options?: {
570
+ slug?: string;
571
+ locale?: string;
572
+ parentId?: string;
573
+ sortOrder?: number;
574
+ } & RetryableWriteOptions): Promise<T>;
575
+ /**
576
+ * Update a content item by ID (partial update)
577
+ * @param id - Entry ID
578
+ * @param data - Fields to update
579
+ * @param options - Update options (slug, locale, parentId, sortOrder)
580
+ */
581
+ update(id: string, data: UpdateInput<T>, options?: {
582
+ slug?: string;
583
+ locale?: string;
584
+ parentId?: string;
585
+ sortOrder?: number;
586
+ } & ConditionalWriteOptions): Promise<T>;
587
+ /**
588
+ * Delete a content item by ID
589
+ */
590
+ delete(id: string, options?: ConditionalWriteOptions): Promise<void>;
591
+ /**
592
+ * Check if content exists by ID
593
+ */
594
+ exists(id: string): Promise<boolean>;
595
+ /**
596
+ * Get version history for one locale of an entry.
597
+ *
598
+ * Versions are numbered per locale, so this is one locale's timeline: `locale` defaults to the
599
+ * schema default, no fallback is applied, and a locale with no history returns an empty array
600
+ * rather than another locale's rows. Every row names the locale it belongs to.
601
+ */
602
+ versions(id: string, options?: {
603
+ limit?: number;
604
+ locale?: string;
605
+ }): Promise<Array<T & {
606
+ versionNumber: number;
607
+ locale: string;
608
+ }>>;
609
+ /**
610
+ * Get a specific version of an entry.
611
+ *
612
+ * `locale` defaults to the schema default. Version numbers are per locale, so version 2 is
613
+ * different content in each one.
614
+ */
615
+ version(id: string, versionNumber: number, options?: {
616
+ locale?: string;
617
+ }): Promise<T | null>;
618
+ /**
619
+ * Revert one locale of an entry to a previous version.
620
+ *
621
+ * `locale` is **required**, and that is not a convention: versions are numbered per locale, so
622
+ * `versionNumber` alone is ambiguous, and defaulting it would silently revert the wrong
623
+ * translation. Only that locale's fields change — shared fields and every other locale are left
624
+ * untouched.
625
+ *
626
+ * The restore is validated as strictly as a publish, so it cannot publish invalid content, and it
627
+ * records a new version of its own rather than rewinding the counter — so the revert is auditable
628
+ * and itself reversible.
629
+ */
630
+ restore(id: string, versionNumber: number, locale: string, options?: ConditionalWriteOptions): Promise<T>;
631
+ /**
632
+ * Get children of a hierarchical entry
633
+ * @param id - Parent entry ID
634
+ * @param options - Fetch options
635
+ */
636
+ children(id: string, options?: {
637
+ depth?: number;
638
+ include?: string[];
639
+ locale?: string;
640
+ }): Promise<T[]>;
641
+ /**
642
+ * Get the content type name
643
+ */
644
+ getContentType(): string;
645
+ /**
646
+ * Create a new ContentClient for a different content type
647
+ */
648
+ ofType<U extends BaseContent = BaseContent>(contentType: string): ContentClient<U>;
649
+ /**
650
+ * Create a new ContentClient with a different default locale
651
+ */
652
+ withLocale(locale: string): ContentClient<T>;
653
+ /**
654
+ * Get the current default locale
655
+ */
656
+ getDefaultLocale(): string | undefined;
657
+ }
658
+
659
+ /**
660
+ * Upload/processing error taxonomy (plan 17, 00b Step 4).
661
+ *
662
+ * A **thin customer-side mirror** of the realtime-contracts `FileErrorCode`
663
+ * (correction C6 — the SDK must not import the server/Studio contracts package).
664
+ * Keep the members in sync with `@relyio/realtime-contracts`.
665
+ */
666
+ type FileErrorCode = 'UPLOAD_TOO_LARGE' | 'UNSUPPORTED_MIME' | 'DIMENSION_OUT_OF_RANGE' | 'ASPECT_RATIO' | 'CORRUPT_FILE' | 'UNSUPPORTED_CODEC' | 'IMAGES_LIMIT_EXCEEDED' | 'STREAM_ENCODE_FAILED' | 'SCAN_INFECTED' | 'INTERNAL';
667
+ /**
668
+ * A failure during the upload protocol (presign / PUT / multipart / confirm) or
669
+ * surfaced by the server's processing pipeline. Carries a typed {@link FileErrorCode}
670
+ * and a `retriable` hint so callers (and `upload()`'s bounded auto-retry) can
671
+ * decide whether to retry on the same entry.
672
+ */
673
+ declare class FileUploadError extends SDKError {
674
+ readonly code: FileErrorCode;
675
+ readonly retryable: boolean;
676
+ constructor(code: FileErrorCode, message: string, options?: {
677
+ retriable?: boolean;
678
+ statusCode?: number;
679
+ cause?: Error;
680
+ });
681
+ getUserMessage(): string;
682
+ }
683
+ /**
684
+ * Client-side file validation failure (MIME/size/etc.) raised **before** any
685
+ * bytes leave the machine (UX only — the server presign stays authoritative).
686
+ */
687
+ declare class FileValidationError extends BaseValidationError {
688
+ readonly code = "FILE_VALIDATION_ERROR";
689
+ constructor(message: string, violations?: ValidationViolation[], cause?: Error);
690
+ getUserMessage(): string;
691
+ }
692
+ /** Constraints `validateFile` checks a file against (subset of the schema upload gate). */
693
+ interface FileValidationConstraints {
694
+ /** Allowed MIME types (exact match). */
695
+ allowedMimeTypes?: string[];
696
+ /** Max size in bytes. */
697
+ maxSizeBytes?: number;
698
+ }
699
+ /**
700
+ * Validate a file against explicit constraints, returning typed violations.
701
+ *
702
+ * The customer SDK does not carry a `SchemaReader` (that lives in the Studio
703
+ * layer), so callers pass the constraints they want enforced client-side; the
704
+ * server presign remains the authority. Returns an empty array when valid.
705
+ */
706
+ declare function validateFile(file: {
707
+ type?: string;
708
+ size?: number;
709
+ }, constraints?: FileValidationConstraints): ValidationViolation[];
710
+
711
+ /** Delivery visibility for a file binding (gateway-enforced, no byte relocation). */
712
+ type AssetVisibility = 'public' | 'protected' | 'private';
713
+ /** Binary lifecycle status — independent of the entry's publishing status. */
714
+ type FileStatus = 'pending_upload' | 'uploading' | 'processing' | 'ready' | 'failed';
715
+ /** Storage backend: `r2` = image + file kinds, `stream` = video kind. */
716
+ type FileBackend = 'r2' | 'stream';
717
+ /** Extracted media metadata (Phase 1 basics + Phase 2 enrichment, plans 15–19). */
718
+ interface AssetFileExtracted {
719
+ width?: number;
720
+ height?: number;
721
+ format?: string;
722
+ duration?: number;
723
+ hasAlpha?: boolean;
724
+ isAnimated?: boolean;
725
+ orientation?: number;
726
+ placeholder?: string;
727
+ dominantColor?: string;
728
+ palette?: string[];
729
+ exif?: Record<string, unknown>;
730
+ video?: {
731
+ codec?: string;
732
+ bitrate?: number;
733
+ framerate?: number;
734
+ audioTracks?: unknown[];
735
+ };
736
+ pageCount?: number;
737
+ text?: string;
738
+ safety?: {
739
+ nsfwScore?: number;
740
+ flagged?: boolean;
741
+ categories?: string[];
742
+ };
743
+ [key: string]: unknown;
744
+ }
745
+ /** Customer-facing file binding block on an asset entry. */
746
+ interface AssetFileBlock {
747
+ status: FileStatus;
748
+ backend: FileBackend;
749
+ fileName: string;
750
+ mimeType?: string | null;
751
+ size?: number | null;
752
+ /** Advisory client-computed SHA-256 (duplicate detection only). */
753
+ hash?: string | null;
754
+ visibility: AssetVisibility;
755
+ extracted?: AssetFileExtracted;
756
+ /** 0..1 normalized smart-crop anchor (written by plan 18). */
757
+ focalPoint?: {
758
+ x: number;
759
+ y: number;
760
+ };
761
+ /** Phase-2 enrichment status — independent of `status` (plan 15). */
762
+ enrichment?: {
763
+ status: 'pending' | 'running' | 'complete' | 'partial' | 'failed';
764
+ };
765
+ /** File-kind malware scan status (plan 19). */
766
+ scan?: {
767
+ status: 'pending' | 'clean' | 'infected' | 'error' | 'skipped';
768
+ };
769
+ /** Processing progress (e.g. video encoding %) + terminal error. */
770
+ processing?: {
771
+ stage?: string;
772
+ progress?: number;
773
+ error?: {
774
+ code?: string;
775
+ message?: string;
776
+ retriable?: boolean;
777
+ };
778
+ };
779
+ /** Video playback readiness. */
780
+ playback?: {
781
+ ready?: boolean;
782
+ };
783
+ }
784
+ /**
785
+ * Computed gateway delivery URLs (the reserved `urls` key). Always served from
786
+ * `cdn.relyio.net`. Image bindings expose `original` + `variants`; video bindings
787
+ * expose `playback`/`poster`/`download`. Present only when `file.status` is
788
+ * `ready` — otherwise the response omits `urls` and callers read `file.status`.
789
+ */
790
+ interface AssetUrls {
791
+ /** Image: canonical original delivery URL. */
792
+ original?: string;
793
+ /** Image: variant id → delivery URL map. */
794
+ variants?: Record<string, string>;
795
+ /** Video: HLS/DASH manifest URL. */
796
+ playback?: string;
797
+ /** Video: poster image URL. */
798
+ poster?: string;
799
+ /** Video: downloadable MP4 URL (when enabled). */
800
+ download?: string;
801
+ [key: string]: unknown;
802
+ }
803
+ /**
804
+ * An asset entry response: schema fields at the top level plus the reserved
805
+ * `file` + `urls` blocks and `_metadata`. `T` carries the inferred field shape.
806
+ */
807
+ type AssetEntryResponse<T extends BaseContent = BaseContent> = T & {
808
+ file?: AssetFileBlock;
809
+ urls?: AssetUrls;
810
+ };
811
+ /** Request a single upload target for the binding. */
812
+ interface PresignInput {
813
+ /** Original file name (required). */
814
+ fileName: string;
815
+ /** MIME type the client will upload with. */
816
+ contentType?: string;
817
+ /** File size in bytes (early upload-gate validation). */
818
+ size?: number;
819
+ /** Locale to target for file-localized asset types. */
820
+ locale?: string;
821
+ /** Advisory client-computed SHA-256 (duplicate detection). */
822
+ hash?: string;
823
+ }
824
+ /** Single upload target returned by `presign`. */
825
+ interface PresignResult {
826
+ backend: FileBackend;
827
+ /** `put` = single PUT / Stream POST; `multipart` = resumable R2 session (plan 17). */
828
+ mode?: 'put' | 'multipart';
829
+ /** R2 presigned PUT (image/file) or Stream direct-creator URL (video). */
830
+ uploadUrl?: string;
831
+ method: 'PUT' | 'POST';
832
+ headers?: Record<string, string>;
833
+ /** Object key for the binding — present for R2 (needed to drive multipart). */
834
+ objectKey?: string;
835
+ storageVersion?: number;
836
+ /** Multipart upload id — present only for `mode: 'multipart'`. */
837
+ uploadId?: string;
838
+ /** Multipart part size in bytes — present only for `mode: 'multipart'`. */
839
+ partSize?: number;
840
+ expiresAt: string;
841
+ locale?: string;
842
+ }
843
+ /** Result of opening a multipart session (`file(uid).createMultipart()`). */
844
+ interface MultipartCreateResult {
845
+ uploadId: string;
846
+ objectKey: string;
847
+ partSize: number;
848
+ storageVersion: number;
849
+ locale?: string;
850
+ }
851
+ /** A single uploaded part (`{ PartNumber, ETag }` ordering for `complete`). */
852
+ interface MultipartPart {
853
+ partNumber: number;
854
+ etag: string;
855
+ size?: number;
856
+ }
857
+ /** Target a multipart session in subsequent ops. */
858
+ interface MultipartTarget {
859
+ uploadId: string;
860
+ objectKey: string;
861
+ locale?: string;
862
+ }
863
+ /** Upload progress tick handed to `.onProgress()`. */
864
+ interface UploadProgress {
865
+ /** `upload` for client → storage bytes. */
866
+ phase: 'upload';
867
+ /** Bytes transferred so far. */
868
+ loaded: number;
869
+ /** Total bytes (when known). */
870
+ total: number;
871
+ /** 0–100 integer. */
872
+ progress: number;
873
+ }
874
+ /** Acknowledge a completed client upload. */
875
+ interface ConfirmInput {
876
+ locale?: string;
877
+ hash?: string;
878
+ }
879
+ /** Result of `confirm`. */
880
+ interface ConfirmResult {
881
+ accepted: boolean;
882
+ }
883
+ /** Resolve a single gateway delivery URL. */
884
+ interface ResolveUrlInput {
885
+ locale?: string;
886
+ /** Named image variant id. */
887
+ variantId?: string;
888
+ /** Video delivery target. */
889
+ purpose?: 'playback' | 'poster' | 'download';
890
+ }
891
+ /** Result of `url`. `expiresAt` is omitted for `public` bindings. */
892
+ interface ResolveUrlResult {
893
+ url: string;
894
+ expiresAt?: string;
895
+ }
896
+ /** Options when creating an asset entry. */
897
+ interface CreateAssetOptions {
898
+ slug?: string;
899
+ locale?: string;
900
+ /** Original file name (also settable at presign). */
901
+ fileName?: string;
902
+ /** Initial delivery visibility (default `public`). */
903
+ visibility?: AssetVisibility;
904
+ /** Auto-publish the entry when the file becomes ready (default `true`). */
905
+ publish?: boolean;
906
+ }
907
+ /** Options for {@link AssetClient.update}: the entry route takes both API4 headers. */
908
+ type UpdateAssetOptions = {
909
+ slug?: string;
910
+ locale?: string;
911
+ } & ConditionalWriteOptions;
912
+ /** Options for {@link AssetFileBuilder.waitUntilReady}. */
913
+ interface WaitUntilReadyOptions {
914
+ /** Locale of the binding to watch. */
915
+ locale?: string;
916
+ /** Total time to wait before giving up (ms). Default 60000. */
917
+ timeoutMs?: number;
918
+ /** Delay between polls (ms). Default 1500. */
919
+ pollIntervalMs?: number;
920
+ }
921
+ /**
922
+ * Operations on a single asset entry's file binding, backed by the
923
+ * `/{assetType}/{entryUid}/file/*` routes (plan 06 §2).
924
+ */
925
+ declare class AssetFileBuilder<T extends BaseContent = BaseContent> {
926
+ private readonly httpClient;
927
+ private readonly assetType;
928
+ private readonly entryUid;
929
+ private readonly defaultLocale?;
930
+ constructor(httpClient: HttpClient, assetType: string, entryUid: string, defaultLocale?: string | undefined);
931
+ /**
932
+ * The path parameters every `/file/*` route on this builder takes.
933
+ *
934
+ * The path *template* is the document's; only these two values are the
935
+ * builder's, so a route that gains or loses a segment is a compile error here
936
+ * rather than a hand-built string that silently misses.
937
+ */
938
+ private get target();
939
+ private withLocale;
940
+ /**
941
+ * Issue a single upload target — an R2 presigned PUT (image/file) or a
942
+ * Cloudflare Stream direct-creator URL (video). Resumable multipart is plan 17.
943
+ */
944
+ presign(input: PresignInput): Promise<PresignResult>;
945
+ /**
946
+ * Acknowledge a completed client upload (kicks off the fast-path intake for
947
+ * R2; a no-op for Stream, whose readiness is webhook-driven).
948
+ */
949
+ confirm(input?: ConfirmInput): Promise<ConfirmResult>;
950
+ /** Open an R2 multipart session for a large file (standalone of `presign`). */
951
+ createMultipart(input: PresignInput): Promise<MultipartCreateResult>;
952
+ /** Presign a single part PUT (1-based `partNumber`). */
953
+ signPart(target: MultipartTarget & {
954
+ partNumber: number;
955
+ }): Promise<{
956
+ url: string;
957
+ }>;
958
+ /** List parts already uploaded (cross-session resume). */
959
+ listParts(target: MultipartTarget): Promise<MultipartPart[]>;
960
+ /** Finalize a multipart session. */
961
+ completeMultipart(target: MultipartTarget & {
962
+ parts: MultipartPart[];
963
+ }): Promise<{
964
+ ok: boolean;
965
+ etag?: string;
966
+ }>;
967
+ /** Abort a multipart session, freeing staged parts. */
968
+ abortMultipart(target: MultipartTarget): Promise<{
969
+ aborted: boolean;
970
+ }>;
971
+ /**
972
+ * Resolve a single gateway delivery URL for the requested locale/variant/purpose.
973
+ * Throws if the binding is not `ready`.
974
+ */
975
+ url(input?: ResolveUrlInput): Promise<ResolveUrlResult>;
976
+ /**
977
+ * Update the binding's delivery visibility (token-based; no byte relocation —
978
+ * a downgrade purges the gateway cache). Returns the refreshed asset entry.
979
+ */
980
+ updateVisibility(visibility: AssetVisibility, options?: {
981
+ locale?: string;
982
+ }): Promise<AssetEntryResponse<T>>;
983
+ /**
984
+ * Set the binding's normalized (0..1, origin top-left) smart-crop focal point
985
+ * (plan 18) so `cover`/`crop` variants frame the subject. Per locale for
986
+ * file-localized asset types; token-based (no byte change). Returns the
987
+ * refreshed asset entry.
988
+ */
989
+ setFocalPoint(focalPoint: {
990
+ x: number;
991
+ y: number;
992
+ }, options?: {
993
+ locale?: string;
994
+ }): Promise<AssetEntryResponse<T>>;
995
+ /**
996
+ * Remove the file binding, clean up the underlying bytes, and purge the
997
+ * gateway cache. The asset entry itself is preserved.
998
+ */
999
+ delete(options?: {
1000
+ locale?: string;
1001
+ }): Promise<void>;
1002
+ /**
1003
+ * Poll the asset entry until its file binding reaches `ready` (resolves with
1004
+ * the ready entry) or `failed` (throws). Times out after `timeoutMs`.
1005
+ */
1006
+ waitUntilReady(options?: WaitUntilReadyOptions): Promise<AssetEntryResponse<T>>;
1007
+ }
1008
+ /**
1009
+ * High-level one-shot upload: create draft entry → presign → PUT bytes →
1010
+ * confirm → (optionally) wait for `ready`. Handles the common image/file flow
1011
+ * (single R2 PUT). For advanced/resumable flows (large files, video tus,
1012
+ * progress) use the lower-level `file(uid)` builder — full upload resilience is
1013
+ * plan 17.
1014
+ */
1015
+ declare class AssetUploadBuilder<T extends BaseContent = BaseContent> {
1016
+ private readonly httpClient;
1017
+ private readonly assetType;
1018
+ private readonly file;
1019
+ private readonly defaultLocale?;
1020
+ private _fileName?;
1021
+ private _visibility?;
1022
+ private _locale?;
1023
+ private _publish?;
1024
+ private _content;
1025
+ private _slug?;
1026
+ private _waitForReady;
1027
+ private _waitOptions;
1028
+ private _onProgress?;
1029
+ private _validate?;
1030
+ private _maxPartRetries;
1031
+ constructor(httpClient: HttpClient, assetType: string, file: Blob, defaultLocale?: string | undefined);
1032
+ /** Subscribe to client → storage upload progress (single PUT + multipart). */
1033
+ onProgress(callback: (_progress: UploadProgress) => void): this;
1034
+ /**
1035
+ * Validate the file client-side (UX only) against explicit constraints before
1036
+ * any bytes leave the machine. Rejects instantly with a {@link FileValidationError}.
1037
+ */
1038
+ validate(constraints: FileValidationConstraints): this;
1039
+ /** Bounded per-part retry attempts for multipart (default 3). */
1040
+ maxPartRetries(attempts: number): this;
1041
+ /** Set the original file name (defaults to `file.name` for `File` inputs). */
1042
+ filename(name: string): this;
1043
+ /** Set the initial delivery visibility (default `public`). */
1044
+ visibility(visibility: AssetVisibility): this;
1045
+ /** Target locale for file-localized asset types. */
1046
+ locale(locale: string): this;
1047
+ /** Suppress (or enable) auto-publish on ready. */
1048
+ publish(publish: boolean): this;
1049
+ /** Set the asset's metadata field values (e.g. `{ alt: '...' }`). */
1050
+ content(content: Record<string, unknown>): this;
1051
+ /** Set an explicit slug for the new entry. */
1052
+ slug(slug: string): this;
1053
+ /** Wait until the file binding becomes `ready` before resolving. */
1054
+ waitForReady(options?: boolean | Omit<WaitUntilReadyOptions, 'locale'>): this;
1055
+ /** Execute the full upload flow and resolve with the asset entry. */
1056
+ execute(): Promise<AssetEntryResponse<T>>;
1057
+ /** Single presigned PUT (≤ 50 MB / Stream POST). */
1058
+ private uploadSingle;
1059
+ /**
1060
+ * Resumable R2 multipart: slice into `partSize` chunks, skip already-uploaded
1061
+ * parts (resume via `list-parts`), PUT each remaining part with bounded retry,
1062
+ * then `complete`. Aborts the session on unrecoverable failure.
1063
+ */
1064
+ private uploadMultipart;
1065
+ /** PUT a single part, retrying transient failures up to `_maxPartRetries`. */
1066
+ private putPartWithRetry;
1067
+ private emitProgress;
1068
+ }
1069
+ /**
1070
+ * Client for a single asset type. Mirrors {@link ContentClient} (CRUD + query
1071
+ * over the same dynamic `/{assetType}` routes — assets are `entryType: 'asset'`
1072
+ * entries) and adds the `file(uid)` sub-resource builder + an `upload()` helper.
1073
+ */
1074
+ declare class AssetClient<T extends BaseContent = BaseContent> {
1075
+ private readonly httpClient;
1076
+ private readonly assetType;
1077
+ private readonly defaultLocale?;
1078
+ constructor(httpClient: HttpClient, assetType: string, defaultLocale?: string | undefined);
1079
+ /** Create a query builder for this asset type. */
1080
+ query(): ContentQueryBuilder<AssetEntryResponse<T>>;
1081
+ /** Find an asset entry by id (or slug) with fluent options. */
1082
+ findById(id: string): ContentByIdBuilder<AssetEntryResponse<T>>;
1083
+ /** Fetch a single asset entry by id (or slug), or `null` if not found. */
1084
+ fetchOne(id: string, options?: {
1085
+ include?: string[];
1086
+ locale?: string;
1087
+ }): Promise<AssetEntryResponse<T> | null>;
1088
+ /** Fetch the first asset entry matching the conditions, or `null`. */
1089
+ fetchFirst(where?: WhereClause<AssetEntryResponse<T>>, locale?: string): Promise<AssetEntryResponse<T> | null>;
1090
+ /**
1091
+ * Create a new asset entry as a draft. The binary is uploaded separately via
1092
+ * `file(uid)` (or use the high-level `upload()` helper), and the entry
1093
+ * auto-publishes when its file becomes ready (unless `publish: false`).
1094
+ */
1095
+ create(data: CreateInput<T>, options?: CreateAssetOptions & RetryableWriteOptions): Promise<AssetEntryResponse<T>>;
1096
+ /** Update an asset entry's metadata fields (and/or slug). */
1097
+ update(id: string, data: UpdateInput<T>, options?: UpdateAssetOptions): Promise<AssetEntryResponse<T>>;
1098
+ /** Delete an asset entry (cascades file-byte cleanup + gateway purge). */
1099
+ delete(id: string, options?: ConditionalWriteOptions): Promise<void>;
1100
+ /** Get the file sub-resource builder for a specific asset entry. */
1101
+ file(entryUid: string): AssetFileBuilder<T>;
1102
+ /** Start a high-level upload (create → presign → PUT → confirm). */
1103
+ upload(file: Blob): AssetUploadBuilder<T>;
1104
+ /** Get the asset type id. */
1105
+ getAssetType(): string;
1106
+ /** Get the current default locale. */
1107
+ getDefaultLocale(): string | undefined;
1108
+ /** Create a new AssetClient for this type with a different default locale. */
1109
+ withLocale(locale: string): AssetClient<T>;
1110
+ }
1111
+ /**
1112
+ * Narrow an entry (e.g. a populated reference from `include`) to an asset entry.
1113
+ * Relies on the `_metadata.entryType` discriminator the API emits (plan 06).
1114
+ *
1115
+ * @example
1116
+ * ```typescript
1117
+ * const article = await relyio.content('article').fetchOne(id, { include: ['hero'] })
1118
+ * if (isAssetEntry(article?.hero)) {
1119
+ * console.log(article.hero.urls?.original)
1120
+ * }
1121
+ * ```
1122
+ */
1123
+ declare function isAssetEntry<T extends BaseContent = BaseContent>(entry: unknown): entry is AssetEntryResponse<T>;
1124
+ /**
1125
+ * A reference field populated (via `include`) with a full asset entry. Identical
1126
+ * to {@link AssetEntryResponse}; named for readability at reference call sites.
1127
+ */
1128
+ type PopulatedAssetReference<T extends BaseContent = BaseContent> = AssetEntryResponse<T>;
1129
+
1130
+ /**
1131
+ * Singleton fetch options
1132
+ */
1133
+ interface SingletonFetchOptions {
1134
+ /** Locale for localized content */
1135
+ locale?: string;
1136
+ /** Reference fields to populate */
1137
+ include?: string[];
1138
+ }
1139
+ /**
1140
+ * Singleton client for managing singleton content operations
1141
+ * Provides a fluent API for singleton operations following the same pattern as ContentClient
1142
+ *
1143
+ * @example
1144
+ * ```typescript
1145
+ * const homepage = await client.singleton('homepage')
1146
+ * .include(['featuredProducts', 'heroImage'])
1147
+ * .locale('en')
1148
+ * .fetch()
1149
+ * ```
1150
+ */
1151
+ declare class SingletonClient<T = unknown> {
1152
+ private httpClient;
1153
+ private singletonKey;
1154
+ private includeFields?;
1155
+ private localeValue?;
1156
+ constructor(httpClient: HttpClient, singletonKey: string);
1157
+ /**
1158
+ * Include reference fields in the response
1159
+ * @param fields - Reference field names to populate
1160
+ */
1161
+ include(fields: string[]): this;
1162
+ /**
1163
+ * Set locale for localized content
1164
+ * @param locale - Locale code (e.g., 'en', 'de', 'fr')
1165
+ */
1166
+ locale(locale: string): this;
1167
+ /**
1168
+ * Fetch the singleton content
1169
+ * @param options - Optional fetch options (alternative to fluent methods)
1170
+ */
1171
+ fetch(options?: SingletonFetchOptions): Promise<T | null>;
1172
+ /**
1173
+ * Fetch the singleton **and the entity tag that read served**, which is what
1174
+ * `update`'s `ifMatch` takes (`INV-API-CONDITIONAL-WRITE-IS-REACHABLE`).
1175
+ *
1176
+ * `entityTag` is absent on a cache HIT: a cached body cannot know the current
1177
+ * version, and a stale tag would refuse the very next write.
1178
+ */
1179
+ fetchWithTag(options?: SingletonFetchOptions): Promise<{
1180
+ content: T;
1181
+ entityTag?: string;
1182
+ }>;
1183
+ /**
1184
+ * Update singleton content. The API auto-publishes on PATCH — there is no
1185
+ * separate publish route; calling `update()` writes and publishes.
1186
+ * @param data - The content fields to update
1187
+ * @param locale - Optional locale for localized content (overrides fluent locale)
1188
+ * @param options - `If-Match` / `Idempotency-Key`, both declared on this route
1189
+ */
1190
+ update(data: Partial<T>, locale?: string, options?: ConditionalWriteOptions): Promise<T>;
1191
+ /**
1192
+ * Check if singleton exists
1193
+ */
1194
+ exists(): Promise<boolean>;
1195
+ /**
1196
+ * Get version history for one locale of the singleton.
1197
+ *
1198
+ * Versions are numbered per locale, so this is one locale's timeline: `locale` defaults to the
1199
+ * schema default, no fallback is applied, and a locale with no history returns an empty array
1200
+ * rather than another locale's rows. Every row names the locale it belongs to.
1201
+ */
1202
+ versions(options?: {
1203
+ limit?: number;
1204
+ locale?: string;
1205
+ }): Promise<Array<T & {
1206
+ versionNumber: number;
1207
+ locale: string;
1208
+ }>>;
1209
+ /**
1210
+ * Get a specific version of the singleton.
1211
+ *
1212
+ * `locale` defaults to the schema default. Version numbers are per locale, so version 2 is
1213
+ * different content in each one.
1214
+ */
1215
+ version(versionNumber: number, options?: {
1216
+ locale?: string;
1217
+ }): Promise<T | null>;
1218
+ /**
1219
+ * Get the singleton key
1220
+ */
1221
+ getSingletonKey(): string;
1222
+ /**
1223
+ * Reset fluent options for reuse
1224
+ */
1225
+ reset(): this;
1226
+ }
1227
+
1228
+ /**
1229
+ * Settings fetch options
1230
+ */
1231
+ interface SettingsFetchOptions {
1232
+ /** Locale for localized settings */
1233
+ locale?: string;
1234
+ /** Reference fields to populate */
1235
+ include?: string[];
1236
+ }
1237
+ /**
1238
+ * Settings client for managing settings group operations
1239
+ * Provides a fluent API for settings operations following the same pattern as ContentClient
1240
+ *
1241
+ * @example
1242
+ * ```typescript
1243
+ * const emailConfig = await client.settings('email-config')
1244
+ * .include(['defaultTemplate'])
1245
+ * .locale('en')
1246
+ * .fetch()
1247
+ * ```
1248
+ */
1249
+ declare class SettingsClient<T = unknown> {
1250
+ private readonly httpClient;
1251
+ private readonly groupId;
1252
+ private includeFields?;
1253
+ private localeValue?;
1254
+ constructor(httpClient: HttpClient, groupId: string);
1255
+ /**
1256
+ * Include reference fields in the response
1257
+ * @param fields - Reference field names to populate
1258
+ */
1259
+ include(fields: string[]): this;
1260
+ /**
1261
+ * Set locale for localized settings
1262
+ * @param locale - Locale code (e.g., 'en', 'de', 'fr')
1263
+ */
1264
+ locale(locale: string): this;
1265
+ /**
1266
+ * Fetch the settings group
1267
+ * @param options - Optional fetch options (alternative to fluent methods)
1268
+ */
1269
+ fetch(options?: SettingsFetchOptions): Promise<T | null>;
1270
+ /**
1271
+ * Update settings group
1272
+ * @param settings - The settings fields to update
1273
+ * @param locale - Optional locale for localized settings (overrides fluent locale)
1274
+ */
1275
+ update(settings: Partial<T>, locale?: string, options?: ConditionalWriteOptions): Promise<T>;
1276
+ /**
1277
+ * Fetch the group **and the entity tag that read served**, which is what
1278
+ * `update`'s and `reset`'s `ifMatch` take
1279
+ * (`INV-API-CONDITIONAL-WRITE-IS-REACHABLE`).
1280
+ */
1281
+ fetchWithTag(options?: SettingsFetchOptions): Promise<{
1282
+ settings: T;
1283
+ entityTag?: string;
1284
+ }>;
1285
+ /**
1286
+ * Reset settings to default values
1287
+ * @param locale - Optional locale for localized settings
1288
+ */
1289
+ reset(locale?: string, options?: ConditionalWriteOptions): Promise<T>;
1290
+ /**
1291
+ * Get default settings for this group
1292
+ */
1293
+ getDefaults(): Promise<T>;
1294
+ /**
1295
+ * Check if settings group exists
1296
+ */
1297
+ exists(): Promise<boolean>;
1298
+ /**
1299
+ * Get version history for one locale of the settings group.
1300
+ *
1301
+ * Versions are numbered per locale, so this is one locale's timeline: `locale` defaults to the
1302
+ * schema default, no fallback is applied, and a locale with no history returns an empty array
1303
+ * rather than another locale's rows. Every row names the locale it belongs to.
1304
+ */
1305
+ versions(options?: {
1306
+ limit?: number;
1307
+ locale?: string;
1308
+ }): Promise<Array<T & {
1309
+ versionNumber: number;
1310
+ locale: string;
1311
+ }>>;
1312
+ /**
1313
+ * Get a specific version of the settings.
1314
+ *
1315
+ * `locale` defaults to the schema default. Version numbers are per locale, so version 2 is
1316
+ * different content in each one.
1317
+ */
1318
+ version(versionNumber: number, options?: {
1319
+ locale?: string;
1320
+ }): Promise<T | null>;
1321
+ /**
1322
+ * Get the settings group ID
1323
+ */
1324
+ getGroupId(): string;
1325
+ /**
1326
+ * Reset fluent options for reuse
1327
+ */
1328
+ resetOptions(): this;
1329
+ }
1330
+
1331
+ /**
1332
+ * Search mode for content queries
1333
+ */
1334
+ type SearchMode = 'keyword' | 'semantic' | 'hybrid';
1335
+ /**
1336
+ * One search hit as the route emits it: the entry fields at the top level,
1337
+ * plus optional score annotations. There is no per-item `data` wrapper.
1338
+ */
1339
+ type SearchResultItem<T = unknown> = T & {
1340
+ _score?: number;
1341
+ _scoreBreakdown?: {
1342
+ keyword?: number;
1343
+ semantic?: number;
1344
+ };
1345
+ _highlights?: Record<string, string[]>;
1346
+ };
1347
+ /**
1348
+ * Search response from API
1349
+ */
1350
+ /**
1351
+ * What `POST /v1/search` serves, **read from the committed document**.
1352
+ *
1353
+ * `{ data, page: { offset, limit, total }, meta }` — search is the offset
1354
+ * dialect precisely because a facet-bearing result wants a total and an offset
1355
+ * (Decision 5), and the facets, the strategy the engine chose and its warnings
1356
+ * ride the declared `meta` sibling because none of them is an item or a page
1357
+ * fact.
1358
+ *
1359
+ * The hand-written `{ items, total?, metadata? }` this replaces named three
1360
+ * keys the route does not send. `SearchBuilder.items()` returned `undefined`
1361
+ * and `.total` was always `undefined` — with nothing able to see either,
1362
+ * because the type asserted them.
1363
+ */
1364
+ type SearchResponse<T = unknown> = Wire<'/v1/search', 'post', SearchResultItem<T>>;
1365
+ /**
1366
+ * Autocomplete suggestion
1367
+ */
1368
+ interface AutocompleteSuggestion {
1369
+ /** Suggested text */
1370
+ text: string;
1371
+ /** Highlighted version */
1372
+ highlighted?: string;
1373
+ /** Score */
1374
+ score?: number;
1375
+ /** Source entry ID */
1376
+ entryId?: string;
1377
+ /** Content type */
1378
+ contentType?: string;
1379
+ }
1380
+ /**
1381
+ * Autocomplete response
1382
+ */
1383
+ /**
1384
+ * What `POST /v1/search/autocomplete` serves. The suggestions are the
1385
+ * collection's `data`; the query it completed and the field it matched on ride
1386
+ * `meta`. Autocomplete is unpaginated, so its page reports `offset: 0` and a
1387
+ * `total` equal to what it returned — the dialect is declared rather than
1388
+ * omitted so one collection shape covers every list.
1389
+ */
1390
+ type AutocompleteResponse = Wire<'/v1/search/autocomplete', 'post', AutocompleteSuggestion>;
1391
+ /**
1392
+ * One similar-content hit as the route emits it — a flat entry, no `data` wrapper.
1393
+ */
1394
+ type SimilarResult<T = unknown> = T & {
1395
+ _score?: number;
1396
+ };
1397
+ /**
1398
+ * Similar response
1399
+ */
1400
+ /** What `POST /v1/search/similar` serves. */
1401
+ type SimilarResponse<T = unknown> = Wire<'/v1/search/similar', 'post', SimilarResult<T>>;
1402
+ /**
1403
+ * Filter conditions for search
1404
+ */
1405
+ type SearchFilter = Record<string, unknown>;
1406
+ /**
1407
+ * Field projection for search results
1408
+ */
1409
+ type SearchFields<T> = {
1410
+ [K: string]: keyof T | string;
1411
+ };
1412
+ /**
1413
+ * Fluent builder for search queries
1414
+ *
1415
+ * @example
1416
+ * ```typescript
1417
+ * const results = await relyio.search('wireless headphones')
1418
+ * .in('product')
1419
+ * .where({ status: 'active' })
1420
+ * .searchFields(['title', 'description'])
1421
+ * .limit(20)
1422
+ * .includeScore()
1423
+ * .execute()
1424
+ * ```
1425
+ */
1426
+ declare class SearchBuilder<T extends BaseContent = BaseContent> {
1427
+ private readonly httpClient;
1428
+ private query;
1429
+ private searchOptions;
1430
+ constructor(httpClient: HttpClient, query: string);
1431
+ /**
1432
+ * Search within a single content type
1433
+ * @param contentType - Content type to search in
1434
+ */
1435
+ in<K extends string>(contentType: K): SearchBuilder<T>;
1436
+ /**
1437
+ * Search across multiple content types
1438
+ * @param contentTypes - Array of content types or '*' for all
1439
+ */
1440
+ across(contentTypes: string[] | '*'): SearchBuilder<T>;
1441
+ /**
1442
+ * Add filter conditions
1443
+ * @param conditions - Filter conditions
1444
+ */
1445
+ where(conditions: SearchFilter): this;
1446
+ /**
1447
+ * Specify which fields to search in
1448
+ * @param fields - Field names to search
1449
+ */
1450
+ searchFields(fields: string[]): this;
1451
+ /**
1452
+ * Project specific fields in results
1453
+ * @param projection - Field mapping
1454
+ */
1455
+ fields(projection: Record<string, string>): this;
1456
+ /**
1457
+ * Limit number of results
1458
+ * @param count - Maximum results
1459
+ */
1460
+ limit(count: number): this;
1461
+ /**
1462
+ * Skip results for pagination
1463
+ * @param count - Number to skip
1464
+ */
1465
+ offset(count: number): this;
1466
+ /**
1467
+ * Include relevance scores in results
1468
+ */
1469
+ includeScore(): this;
1470
+ /**
1471
+ * Include score breakdown explanation
1472
+ */
1473
+ explain(): this;
1474
+ /**
1475
+ * Set locale for localized content
1476
+ * @param locale - Locale code
1477
+ */
1478
+ locale(locale: string): this;
1479
+ /**
1480
+ * Execute the search query
1481
+ */
1482
+ execute(): Promise<SearchResponse<T>>;
1483
+ /**
1484
+ * Execute and return just the items (convenience method)
1485
+ */
1486
+ items(): Promise<Array<SearchResultItem<T>>>;
1487
+ /**
1488
+ * Get the first result
1489
+ */
1490
+ first(): Promise<SearchResultItem<T> | null>;
1491
+ }
1492
+ /**
1493
+ * Fluent builder for autocomplete queries
1494
+ *
1495
+ * @example
1496
+ * ```typescript
1497
+ * const suggestions = await relyio.autocomplete('wire')
1498
+ * .in('product')
1499
+ * .field('title')
1500
+ * .fuzzy()
1501
+ * .limit(10)
1502
+ * .execute()
1503
+ * ```
1504
+ */
1505
+ declare class AutocompleteBuilder {
1506
+ private readonly httpClient;
1507
+ private query;
1508
+ private autocompleteOptions;
1509
+ constructor(httpClient: HttpClient, query: string);
1510
+ /**
1511
+ * Autocomplete within a content type
1512
+ * @param contentType - Content type to search
1513
+ */
1514
+ in(contentType: string): this;
1515
+ /**
1516
+ * Field to autocomplete on
1517
+ * @param fieldName - Field name (supports nested like 'category.name')
1518
+ */
1519
+ field(fieldName: string): this;
1520
+ /**
1521
+ * Enable fuzzy matching for typo tolerance
1522
+ */
1523
+ fuzzy(): this;
1524
+ /**
1525
+ * Add filter conditions
1526
+ * @param conditions - Filter conditions
1527
+ */
1528
+ where(conditions: SearchFilter): this;
1529
+ /**
1530
+ * Limit number of suggestions
1531
+ * @param count - Maximum suggestions
1532
+ */
1533
+ limit(count: number): this;
1534
+ /**
1535
+ * Set locale for localized content
1536
+ * @param locale - Locale code
1537
+ */
1538
+ locale(locale: string): this;
1539
+ /**
1540
+ * Execute the autocomplete query
1541
+ */
1542
+ execute(): Promise<AutocompleteResponse>;
1543
+ /**
1544
+ * Get just the suggestion texts
1545
+ */
1546
+ texts(): Promise<string[]>;
1547
+ }
1548
+ /**
1549
+ * Fluent builder for finding similar content
1550
+ *
1551
+ * @example
1552
+ * ```typescript
1553
+ * const similar = await relyio.similar('entry_abc123')
1554
+ * .in('product')
1555
+ * .locale('fr-FR')
1556
+ * .limit(5)
1557
+ * .execute()
1558
+ * ```
1559
+ */
1560
+ declare class SimilarBuilder<T extends BaseContent = BaseContent> {
1561
+ private readonly httpClient;
1562
+ private sourceId;
1563
+ private similarOptions;
1564
+ constructor(httpClient: HttpClient, sourceId: string);
1565
+ /**
1566
+ * Find similar content within a content type
1567
+ * @param contentType - Content type to search
1568
+ */
1569
+ in(contentType: string): this;
1570
+ /**
1571
+ * Add filter conditions
1572
+ * @param conditions - Filter conditions
1573
+ */
1574
+ where(conditions: SearchFilter): this;
1575
+ /**
1576
+ * Limit number of results
1577
+ * @param count - Maximum results
1578
+ */
1579
+ limit(count: number): this;
1580
+ /**
1581
+ * Set locale for localized content
1582
+ * @param locale - Locale code
1583
+ */
1584
+ locale(locale: string): this;
1585
+ /**
1586
+ * Execute the similar query
1587
+ */
1588
+ execute(): Promise<SimilarResponse<T>>;
1589
+ /**
1590
+ * Get just the similar items
1591
+ */
1592
+ items(): Promise<Array<SimilarResult<T>>>;
1593
+ /**
1594
+ * Get the most similar item
1595
+ */
1596
+ first(): Promise<SimilarResult<T> | null>;
1597
+ }
1598
+ /**
1599
+ * Search client providing fluent access to all search capabilities
1600
+ *
1601
+ * @example
1602
+ * ```typescript
1603
+ * const searchClient = new SearchClient(httpClient)
1604
+ *
1605
+ * // Full-text/hybrid search
1606
+ * const products = await searchClient.search('wireless')
1607
+ * .in('product')
1608
+ * .limit(20)
1609
+ * .execute()
1610
+ *
1611
+ * // Autocomplete
1612
+ * const suggestions = await searchClient.autocomplete('wire')
1613
+ * .in('product')
1614
+ * .field('title')
1615
+ * .execute()
1616
+ *
1617
+ * // Find similar
1618
+ * const similar = await searchClient.similar('entry_123')
1619
+ * .in('product')
1620
+ * .execute()
1621
+ * ```
1622
+ */
1623
+ declare class SearchClient {
1624
+ private readonly httpClient;
1625
+ constructor(httpClient: HttpClient);
1626
+ /**
1627
+ * Start a search query
1628
+ * @param query - Search query string
1629
+ */
1630
+ search<T extends BaseContent = BaseContent>(query: string): SearchBuilder<T>;
1631
+ /**
1632
+ * Start an autocomplete query
1633
+ * @param query - Partial query to autocomplete
1634
+ */
1635
+ autocomplete(query: string): AutocompleteBuilder;
1636
+ /**
1637
+ * Find content similar to a given entry
1638
+ * @param sourceId - Entry ID to find similar content for
1639
+ */
1640
+ similar<T extends BaseContent = BaseContent>(sourceId: string): SimilarBuilder<T>;
1641
+ /**
1642
+ * Quick search helper - searches all content types
1643
+ * @param query - Search query
1644
+ * @param limit - Max results (default 20)
1645
+ */
1646
+ global<T extends BaseContent = BaseContent>(query: string, limit?: number): Promise<SearchResponse<T>>;
1647
+ }
1648
+
1649
+ /**
1650
+ * HTTP methods supported in batch operations
1651
+ */
1652
+ type BatchMethod = 'POST' | 'PATCH' | 'PUT' | 'DELETE';
1653
+ /**
1654
+ * A single batch operation
1655
+ */
1656
+ interface BatchOperation {
1657
+ /** Optional operation ID for tracking */
1658
+ id?: string;
1659
+ /** HTTP method */
1660
+ method: BatchMethod;
1661
+ /** Path to resource (e.g., '/product' or '/product/entry_123') */
1662
+ path: string;
1663
+ /** Request body for create/update operations */
1664
+ body?: Record<string, unknown>;
1665
+ }
1666
+ /**
1667
+ * Batch execution options
1668
+ */
1669
+ interface BatchOptions {
1670
+ /**
1671
+ * If true, the batch runs as one transaction: every operation lands or none does.
1672
+ *
1673
+ * Requires that all operations be the same kind of write — one transaction cannot span two
1674
+ * calls — so a batch mixing creates and deletes, or `update` and `replace`, is rejected with
1675
+ * a 400 before anything is attempted.
1676
+ *
1677
+ * @default false
1678
+ */
1679
+ atomic?: boolean;
1680
+ /**
1681
+ * If true, run the checks and write nothing.
1682
+ *
1683
+ * Every entry is resolved, authorized and validated against its schema, so what passes a dry
1684
+ * run is what a real write accepts.
1685
+ *
1686
+ * @default false
1687
+ */
1688
+ dryRun?: boolean;
1689
+ /**
1690
+ * If true, include full data in response
1691
+ * @default true
1692
+ */
1693
+ returnData?: boolean;
1694
+ /**
1695
+ * If true, continue executing after failures (non-atomic mode only)
1696
+ * @default true
1697
+ */
1698
+ continueOnError?: boolean;
1699
+ }
1700
+ /**
1701
+ * Result of a single operation in the batch
1702
+ */
1703
+ /**
1704
+ * One batch item, **as the committed document describes the `multi` kind**.
1705
+ *
1706
+ * Read from `api-schema.ts` rather than imported from `@relyio/api-contract`
1707
+ * for the same reason every other response type here is: the document is the
1708
+ * one source, and a type this package publishes must resolve for a customer who
1709
+ * installs `@relyio/sdk` alone. The contract's runtime half is bundled; its
1710
+ * `.d.ts` is not something a consumer can reach.
1711
+ *
1712
+ * The hand-written version carried `success: boolean` and a per-item `status`
1713
+ * number. Both are gone from the wire and neither loss is a gap: the presence
1714
+ * of `error` already says whether the item succeeded (Decision 2, gap 16), and
1715
+ * a sub-response carrying its own HTTP status is exactly the duplication
1716
+ * `INV-API-NO-DUPLICATE-STATUS` removes. What replaced them says more, not
1717
+ * less — `outcome` distinguishes `created` from `updated` from `unchanged`
1718
+ * from `duplicate` from `skipped`, which one boolean never could.
1719
+ */
1720
+ type BatchOperationResult = components['schemas']['MultiPayload']['results'][number];
1721
+ /** Summary of batch execution, from the contract. */
1722
+ type BatchSummary = components['schemas']['MultiSummary'];
1723
+ /**
1724
+ * The full batch payload: `{ results, summary }`.
1725
+ *
1726
+ * `transactionId`, `executionTimeMs` and `dryRun` were named here and are not
1727
+ * on the wire; nothing produced them and nothing could read them.
1728
+ */
1729
+ type BatchResponse = components['schemas']['MultiPayload'];
1730
+ /**
1731
+ * Fluent builder for batch operations
1732
+ *
1733
+ * @example
1734
+ * ```typescript
1735
+ * const result = await relyio.batch()
1736
+ * .create('product', { content: { title: 'iPhone', price: 999 } })
1737
+ * .update('product', 'entry_abc', { content: { price: 899 } })
1738
+ * .delete('product', 'entry_xyz')
1739
+ * .upsertSingleton('homepage', { content: { hero: 'Welcome!' } })
1740
+ * .execute({ atomic: true })
1741
+ *
1742
+ * console.log(result.summary.succeeded) // 4
1743
+ * ```
1744
+ */
1745
+ declare class BatchBuilder<TSchema = unknown> {
1746
+ private readonly httpClient;
1747
+ private operations;
1748
+ private defaultOptions;
1749
+ constructor(httpClient: HttpClient);
1750
+ /**
1751
+ * Add a create operation
1752
+ * @param contentType - Content type to create
1753
+ * @param data - Entry data with content field
1754
+ * @param id - Optional operation ID for tracking
1755
+ */
1756
+ create<T extends BaseContent = BaseContent>(contentType: string, data: {
1757
+ content: CreateInput<T>;
1758
+ slug?: string;
1759
+ locale?: string;
1760
+ parentId?: string;
1761
+ sortOrder?: number;
1762
+ }, id?: string): this;
1763
+ /**
1764
+ * Add an update operation (partial update).
1765
+ *
1766
+ * The submitted fields are merged into the entry and the rest are left as they are. The merge is
1767
+ * field-level rather than deep: sending an object-typed field replaces that field's whole value.
1768
+ *
1769
+ * @param contentType - Content type
1770
+ * @param entryId - Entry ID to update
1771
+ * @param data - Fields to update
1772
+ * @param id - Optional operation ID for tracking
1773
+ */
1774
+ update<T extends BaseContent = BaseContent>(contentType: string, entryId: string, data: {
1775
+ content?: UpdateInput<T>;
1776
+ slug?: string;
1777
+ locale?: string;
1778
+ parentId?: string;
1779
+ sortOrder?: number;
1780
+ }, id?: string): this;
1781
+ /**
1782
+ * Add a replace operation (full replacement).
1783
+ *
1784
+ * The payload becomes the entry's content for the addressed locale: fields you do not send are
1785
+ * cleared. Send the whole entry, or use {@link BatchClient.update} to change part of one.
1786
+ *
1787
+ * @param contentType - Content type
1788
+ * @param entryId - Entry ID to replace
1789
+ * @param data - Complete entry data
1790
+ * @param id - Optional operation ID for tracking
1791
+ */
1792
+ replace<T extends BaseContent = BaseContent>(contentType: string, entryId: string, data: {
1793
+ content: CreateInput<T>;
1794
+ slug?: string;
1795
+ locale?: string;
1796
+ parentId?: string;
1797
+ sortOrder?: number;
1798
+ }, id?: string): this;
1799
+ /**
1800
+ * Add a delete operation
1801
+ * @param contentType - Content type
1802
+ * @param entryId - Entry ID to delete
1803
+ * @param id - Optional operation ID for tracking
1804
+ */
1805
+ delete(contentType: string, entryId: string, id?: string): this;
1806
+ /**
1807
+ * Add a singleton upsert operation
1808
+ * @param singletonKey - Singleton key
1809
+ * @param data - Singleton content
1810
+ * @param id - Optional operation ID for tracking
1811
+ */
1812
+ upsertSingleton(singletonKey: string, data: {
1813
+ content: Record<string, unknown>;
1814
+ locale?: string;
1815
+ }, id?: string): this;
1816
+ /**
1817
+ * Add a singleton create operation (alias for upsert with POST)
1818
+ * @param singletonKey - Singleton key
1819
+ * @param data - Singleton content
1820
+ * @param id - Optional operation ID for tracking
1821
+ */
1822
+ createSingleton(singletonKey: string, data: {
1823
+ content: Record<string, unknown>;
1824
+ locale?: string;
1825
+ }, id?: string): this;
1826
+ /**
1827
+ * Add a raw operation (for advanced use cases)
1828
+ * @param operation - The operation to add
1829
+ */
1830
+ raw(operation: BatchOperation): this;
1831
+ /**
1832
+ * Add multiple raw operations
1833
+ * @param operations - Operations to add
1834
+ */
1835
+ rawMany(operations: BatchOperation[]): this;
1836
+ /**
1837
+ * Bulk create multiple entries of the same content type
1838
+ * @param contentType - Content type
1839
+ * @param items - Array of entry data
1840
+ */
1841
+ bulkCreate<T extends BaseContent = BaseContent>(contentType: string, items: Array<{
1842
+ content: CreateInput<T>;
1843
+ slug?: string;
1844
+ locale?: string;
1845
+ id?: string;
1846
+ }>): this;
1847
+ /**
1848
+ * Bulk update multiple entries
1849
+ * @param contentType - Content type
1850
+ * @param updates - Array of { entryId, data } pairs
1851
+ */
1852
+ bulkUpdate<T extends BaseContent = BaseContent>(contentType: string, updates: Array<{
1853
+ entryId: string;
1854
+ data: {
1855
+ content?: UpdateInput<T>;
1856
+ };
1857
+ id?: string;
1858
+ }>): this;
1859
+ /**
1860
+ * Bulk delete multiple entries
1861
+ * @param contentType - Content type
1862
+ * @param entryIds - Entry IDs to delete
1863
+ */
1864
+ bulkDelete(contentType: string, entryIds: string[]): this;
1865
+ /**
1866
+ * Configure default options for execution
1867
+ * @param options - Batch options
1868
+ */
1869
+ configure(options: BatchOptions): this;
1870
+ /**
1871
+ * Enable atomic mode (all-or-nothing)
1872
+ */
1873
+ atomic(): this;
1874
+ /**
1875
+ * Enable dry-run mode (validate without executing)
1876
+ */
1877
+ dryRun(): this;
1878
+ /**
1879
+ * Disable returning full data in response
1880
+ */
1881
+ noData(): this;
1882
+ /**
1883
+ * Stop on first error (non-atomic mode)
1884
+ */
1885
+ stopOnError(): this;
1886
+ /**
1887
+ * Get the current list of operations
1888
+ */
1889
+ getOperations(): BatchOperation[];
1890
+ /**
1891
+ * Get the number of operations
1892
+ */
1893
+ size(): number;
1894
+ /**
1895
+ * Clear all operations
1896
+ */
1897
+ clear(): this;
1898
+ /**
1899
+ * Clone the builder
1900
+ */
1901
+ clone(): BatchBuilder<TSchema>;
1902
+ /**
1903
+ * Execute the batch operations
1904
+ * @param options - Override default options
1905
+ */
1906
+ execute(options?: BatchOptions): Promise<BatchResponse>;
1907
+ /**
1908
+ * Validate operations without executing (convenience for dry-run)
1909
+ */
1910
+ validate(): Promise<BatchResponse>;
1911
+ /**
1912
+ * Execute and throw if any operation failed
1913
+ */
1914
+ executeOrThrow(options?: BatchOptions): Promise<BatchResponse>;
1915
+ }
1916
+ /**
1917
+ * Batch client for bulk operations
1918
+ *
1919
+ * @example
1920
+ * ```typescript
1921
+ * const batchClient = new BatchClient(httpClient)
1922
+ *
1923
+ * // Create a batch with fluent API
1924
+ * const result = await batchClient.create()
1925
+ * .create('product', { content: { title: 'New Product' } })
1926
+ * .update('product', 'entry_123', { content: { price: 99 } })
1927
+ * .execute()
1928
+ *
1929
+ * // Quick bulk create
1930
+ * const products = await batchClient.bulkCreate('product', [
1931
+ * { content: { title: 'Product 1' } },
1932
+ * { content: { title: 'Product 2' } },
1933
+ * ])
1934
+ * ```
1935
+ */
1936
+ declare class BatchClient<TSchema = unknown> {
1937
+ private readonly httpClient;
1938
+ constructor(httpClient: HttpClient);
1939
+ /**
1940
+ * Create a new batch builder
1941
+ */
1942
+ create(): BatchBuilder<TSchema>;
1943
+ /**
1944
+ * Quick bulk create entries
1945
+ * @param contentType - Content type
1946
+ * @param items - Array of entry data
1947
+ * @param options - Batch options
1948
+ */
1949
+ bulkCreate<T extends BaseContent = BaseContent>(contentType: string, items: Array<{
1950
+ content: CreateInput<T>;
1951
+ slug?: string;
1952
+ locale?: string;
1953
+ }>, options?: BatchOptions): Promise<BatchResponse>;
1954
+ /**
1955
+ * Quick bulk update entries
1956
+ * @param contentType - Content type
1957
+ * @param updates - Array of updates
1958
+ * @param options - Batch options
1959
+ */
1960
+ bulkUpdate<T extends BaseContent = BaseContent>(contentType: string, updates: Array<{
1961
+ entryId: string;
1962
+ data: {
1963
+ content?: UpdateInput<T>;
1964
+ };
1965
+ }>, options?: BatchOptions): Promise<BatchResponse>;
1966
+ /**
1967
+ * Quick bulk delete entries
1968
+ * @param contentType - Content type
1969
+ * @param entryIds - Entry IDs to delete
1970
+ * @param options - Batch options
1971
+ */
1972
+ bulkDelete(contentType: string, entryIds: string[], options?: BatchOptions): Promise<BatchResponse>;
1973
+ /**
1974
+ * Execute raw batch operations
1975
+ * @param operations - Array of operations
1976
+ * @param options - Batch options
1977
+ */
1978
+ execute(operations: BatchOperation[], options?: BatchOptions): Promise<BatchResponse>;
1979
+ }
1980
+
1981
+ /**
1982
+ * Fetch block for retrieving content
1983
+ */
1984
+ interface FetchBlock {
1985
+ /** Content type to fetch (mutually exclusive with singleton) */
1986
+ contentType?: string;
1987
+ /** Singleton key to fetch (mutually exclusive with contentType) */
1988
+ singleton?: string;
1989
+ /** Filter conditions */
1990
+ where?: Record<string, unknown>;
1991
+ /** Reference fields to populate */
1992
+ include?: string | string[];
1993
+ /** Sort specification */
1994
+ sort?: Record<string, 'asc' | 'desc'>;
1995
+ /** Maximum entries to return */
1996
+ limit?: number;
1997
+ /** Entries to skip */
1998
+ offset?: number;
1999
+ }
2000
+ /**
2001
+ * Aggregate block for computing metrics
2002
+ */
2003
+ interface AggregateBlock {
2004
+ /** Source content type or query reference */
2005
+ source: string;
2006
+ /** Field to group by */
2007
+ by?: string | string[];
2008
+ /** Metrics to compute */
2009
+ metrics: Record<string, AggregateMetric>;
2010
+ }
2011
+ /**
2012
+ * Aggregate metric definition
2013
+ */
2014
+ interface AggregateMetric {
2015
+ /** Aggregation function */
2016
+ fn: 'count' | 'sum' | 'avg' | 'min' | 'max' | 'first' | 'last';
2017
+ /** Field to aggregate (optional for count) */
2018
+ field?: string;
2019
+ }
2020
+ /**
2021
+ * Search block for full-text/semantic search
2022
+ */
2023
+ interface SearchBlock {
2024
+ /** Content type to search */
2025
+ contentType: string;
2026
+ /** Search query (can use ${param} syntax) */
2027
+ query: string;
2028
+ /** Search mode */
2029
+ mode?: 'keyword' | 'semantic' | 'hybrid';
2030
+ /** Fields to search */
2031
+ searchFields?: string[];
2032
+ /** Additional filters */
2033
+ where?: Record<string, unknown>;
2034
+ /** Maximum results */
2035
+ limit?: number;
2036
+ }
2037
+ /**
2038
+ * Similar block for vector similarity search
2039
+ */
2040
+ interface SimilarBlock {
2041
+ /** Content type */
2042
+ contentType: string;
2043
+ /** Source entry ID */
2044
+ source: string;
2045
+ /** Minimum similarity threshold */
2046
+ threshold?: number;
2047
+ /** Additional filters */
2048
+ where?: Record<string, unknown>;
2049
+ /** Maximum results */
2050
+ limit?: number;
2051
+ }
2052
+ /**
2053
+ * Post-fetch filters with parameter support
2054
+ */
2055
+ type FilterValue = string | number | boolean | {
2056
+ eq?: unknown;
2057
+ ne?: unknown;
2058
+ gt?: unknown;
2059
+ gte?: unknown;
2060
+ lt?: unknown;
2061
+ lte?: unknown;
2062
+ in?: unknown[];
2063
+ nin?: unknown[];
2064
+ contains?: unknown;
2065
+ startsWith?: string;
2066
+ endsWith?: string;
2067
+ };
2068
+ /**
2069
+ * Full-text search configuration
2070
+ */
2071
+ interface FullTextConfig {
2072
+ /** Search query (can use ${param} syntax) */
2073
+ query: string;
2074
+ /** Fields to search */
2075
+ fields?: string[];
2076
+ /** Minimum score threshold */
2077
+ minScore?: number;
2078
+ }
2079
+ /**
2080
+ * Ad-hoc query definition
2081
+ */
2082
+ interface AdhocQueryDefinition {
2083
+ /** Fetch block - retrieve content */
2084
+ fetch?: FetchBlock;
2085
+ /** Aggregate block - compute metrics */
2086
+ aggregate?: AggregateBlock;
2087
+ /** Search block - full-text/semantic search */
2088
+ search?: SearchBlock;
2089
+ /** Similar block - vector similarity */
2090
+ similar?: SimilarBlock;
2091
+ /** Field selection/transformation */
2092
+ select?: Record<string, string>;
2093
+ /** Post-fetch filters with parameter support */
2094
+ filters?: Record<string, FilterValue>;
2095
+ /** Full-text search configuration */
2096
+ fullText?: FullTextConfig;
2097
+ /** Cache policy */
2098
+ cache?: {
2099
+ ttl?: number;
2100
+ strategy?: 'stale-while-revalidate' | 'cache-first' | 'network-first';
2101
+ };
2102
+ }
2103
+ /**
2104
+ * Ad-hoc query execution options
2105
+ */
2106
+ interface AdhocQueryOptions {
2107
+ /** Include debug information in response */
2108
+ debug?: boolean;
2109
+ /** Bypass cache for this request */
2110
+ skipCache?: boolean;
2111
+ /** Custom timeout in milliseconds (1000-60000) */
2112
+ timeout?: number;
2113
+ }
2114
+ /**
2115
+ * Query execution metadata
2116
+ */
2117
+ interface QueryMetadata {
2118
+ /** Query identifier */
2119
+ queryId: string;
2120
+ /** Execution time in milliseconds */
2121
+ executionTimeMs: number;
2122
+ /** Number of results */
2123
+ resultCount: number;
2124
+ /** ISO timestamp */
2125
+ timestamp: string;
2126
+ /** Whether result was from cache */
2127
+ cacheHit?: boolean;
2128
+ /** Generated pipeline (if debug enabled) */
2129
+ pipeline?: unknown[];
2130
+ /** Resolved parameters (if debug enabled) */
2131
+ parameters?: Record<string, unknown>;
2132
+ /** Validation warnings */
2133
+ warnings?: string[];
2134
+ }
2135
+ /**
2136
+ * The payload `POST /v1/query` serves: `{ result, metadata }`.
2137
+ *
2138
+ * The route is an `object` kind, so there is one payload key whether the query
2139
+ * fetched a list or a singleton — `result` is the records either way. Three
2140
+ * members of the hand-written type are gone and each for a stated reason:
2141
+ * `success` because the status line says it, `items` because no contract shape
2142
+ * introduces that key (`INV-API-SUCCESS-HAS-ONE-SHAPE`), and `errors` because a
2143
+ * failed query is a **refusal**, which arrives as `{ error: { code, messages } }`
2144
+ * and is thrown as a typed SDK error rather than returned as a 200 with a flag.
2145
+ */
2146
+ interface AdhocQueryResult<T = unknown> {
2147
+ /** The records the query produced — an array for a list, one item for a singleton. */
2148
+ result?: T | T[];
2149
+ /** Execution metadata */
2150
+ metadata?: QueryMetadata;
2151
+ }
2152
+ /**
2153
+ * Fluent builder for ad-hoc queries
2154
+ *
2155
+ * @example
2156
+ * ```typescript
2157
+ * const result = await relyio.query.adhoc()
2158
+ * .fetch({
2159
+ * contentType: 'product',
2160
+ * where: { status: 'active' },
2161
+ * include: ['brand', 'categories'],
2162
+ * limit: 20
2163
+ * })
2164
+ * .select({
2165
+ * id: 'id',
2166
+ * title: 'title',
2167
+ * price: 'pricing.basePrice',
2168
+ * brandName: 'brand.name'
2169
+ * })
2170
+ * .filter('pricing.basePrice', { gte: '${minPrice}', lte: '${maxPrice}' })
2171
+ * .params({ minPrice: 100, maxPrice: 500 })
2172
+ * .debug()
2173
+ * .execute()
2174
+ * ```
2175
+ */
2176
+ declare class AdhocQueryBuilder<T = unknown> {
2177
+ private readonly httpClient;
2178
+ private queryDef;
2179
+ private parameters;
2180
+ private options;
2181
+ constructor(httpClient: HttpClient);
2182
+ /**
2183
+ * Add a fetch block to retrieve content
2184
+ * @param config - Fetch configuration
2185
+ */
2186
+ fetch(config: FetchBlock): this;
2187
+ /**
2188
+ * Fetch from a content type
2189
+ * @param contentType - Content type to fetch
2190
+ */
2191
+ from(contentType: string): this;
2192
+ /**
2193
+ * Fetch a singleton
2194
+ * @param singletonKey - Singleton key
2195
+ */
2196
+ singleton(singletonKey: string): this;
2197
+ /**
2198
+ * Add aggregate block
2199
+ * @param config - Aggregate configuration
2200
+ */
2201
+ aggregate(config: AggregateBlock): this;
2202
+ /**
2203
+ * Add search block
2204
+ * @param config - Search configuration
2205
+ */
2206
+ search(config: SearchBlock): this;
2207
+ /**
2208
+ * Add similar block
2209
+ * @param config - Similar configuration
2210
+ */
2211
+ similar(config: SimilarBlock): this;
2212
+ /**
2213
+ * Add where conditions to fetch
2214
+ * @param conditions - Filter conditions
2215
+ */
2216
+ where(conditions: Record<string, unknown>): this;
2217
+ /**
2218
+ * Add include for reference population
2219
+ * @param fields - Reference fields to include
2220
+ */
2221
+ include(fields: string | string[]): this;
2222
+ /**
2223
+ * Add sort specification
2224
+ * @param sort - Sort configuration
2225
+ */
2226
+ sort(sort: Record<string, 'asc' | 'desc'>): this;
2227
+ /**
2228
+ * Sort by field ascending
2229
+ * @param field - Field to sort by
2230
+ */
2231
+ sortAsc(field: string): this;
2232
+ /**
2233
+ * Sort by field descending
2234
+ * @param field - Field to sort by
2235
+ */
2236
+ sortDesc(field: string): this;
2237
+ /**
2238
+ * Limit results
2239
+ * @param count - Maximum results
2240
+ */
2241
+ limit(count: number): this;
2242
+ /**
2243
+ * Skip results
2244
+ * @param count - Number to skip
2245
+ */
2246
+ offset(count: number): this;
2247
+ /**
2248
+ * Select and transform fields in output
2249
+ * @param projection - Field mapping (outputField: sourceField)
2250
+ */
2251
+ select(projection: Record<string, string>): this;
2252
+ /**
2253
+ * Add a post-fetch filter (supports ${param} syntax)
2254
+ * @param field - Field path to filter
2255
+ * @param condition - Filter condition
2256
+ */
2257
+ filter(field: string, condition: FilterValue): this;
2258
+ /**
2259
+ * Add full-text search
2260
+ * @param config - Full-text configuration
2261
+ */
2262
+ fullText(config: FullTextConfig): this;
2263
+ /**
2264
+ * Set query parameters
2265
+ * @param params - Parameter values
2266
+ */
2267
+ params(params: Record<string, unknown>): this;
2268
+ /**
2269
+ * Set a single parameter
2270
+ * @param name - Parameter name
2271
+ * @param value - Parameter value
2272
+ */
2273
+ param(name: string, value: unknown): this;
2274
+ /**
2275
+ * Enable debug mode
2276
+ */
2277
+ debug(): this;
2278
+ /**
2279
+ * Skip cache for this request
2280
+ */
2281
+ skipCache(): this;
2282
+ /**
2283
+ * Set custom timeout
2284
+ * @param ms - Timeout in milliseconds (1000-60000)
2285
+ */
2286
+ timeout(ms: number): this;
2287
+ /**
2288
+ * Set cache policy
2289
+ * @param ttl - Cache TTL in seconds
2290
+ * @param strategy - Cache strategy
2291
+ */
2292
+ cache(ttl: number, strategy?: 'stale-while-revalidate' | 'cache-first' | 'network-first'): this;
2293
+ /**
2294
+ * Get the query definition (for debugging)
2295
+ */
2296
+ getDefinition(): AdhocQueryDefinition;
2297
+ /**
2298
+ * Get the parameters (for debugging)
2299
+ */
2300
+ getParameters(): Record<string, unknown>;
2301
+ /**
2302
+ * Execute the query
2303
+ */
2304
+ execute(): Promise<AdhocQueryResult<T>>;
2305
+ /**
2306
+ * Execute and return the records as an array (for list queries).
2307
+ */
2308
+ items(): Promise<T[]>;
2309
+ /**
2310
+ * Execute and return the single record (for singleton queries).
2311
+ */
2312
+ data(): Promise<T | undefined>;
2313
+ /**
2314
+ * Clone the builder
2315
+ */
2316
+ clone(): AdhocQueryBuilder<T>;
2317
+ }
2318
+ /**
2319
+ * Client for executing ad-hoc queries
2320
+ *
2321
+ * @example
2322
+ * ```typescript
2323
+ * const queryClient = new AdhocQueryClient(httpClient)
2324
+ *
2325
+ * // Fluent builder
2326
+ * const products = await queryClient.build()
2327
+ * .from('product')
2328
+ * .where({ status: 'active' })
2329
+ * .limit(20)
2330
+ * .execute()
2331
+ *
2332
+ * // Direct execution
2333
+ * const result = await queryClient.execute({
2334
+ * fetch: { contentType: 'product', limit: 10 }
2335
+ * })
2336
+ * ```
2337
+ */
2338
+ declare class AdhocQueryClient {
2339
+ private readonly httpClient;
2340
+ constructor(httpClient: HttpClient);
2341
+ /**
2342
+ * Create a new query builder
2343
+ */
2344
+ build<T = unknown>(): AdhocQueryBuilder<T>;
2345
+ /**
2346
+ * Execute a query definition directly
2347
+ * @param definition - Query definition
2348
+ * @param parameters - Parameter values
2349
+ * @param options - Execution options
2350
+ */
2351
+ execute<T = unknown>(definition: AdhocQueryDefinition, parameters?: Record<string, unknown>, options?: AdhocQueryOptions): Promise<AdhocQueryResult<T>>;
2352
+ /**
2353
+ * Quick fetch helper
2354
+ * @param contentType - Content type
2355
+ * @param options - Fetch options
2356
+ */
2357
+ fetch<T = unknown>(contentType: string, options?: Omit<FetchBlock, 'contentType'>): Promise<T[]>;
2358
+ /**
2359
+ * Quick singleton fetch helper
2360
+ * @param singletonKey - Singleton key
2361
+ */
2362
+ fetchSingleton<T = unknown>(singletonKey: string): Promise<T | undefined>;
2363
+ /**
2364
+ * Quick aggregate helper
2365
+ * @param source - Source content type
2366
+ * @param metrics - Metrics to compute
2367
+ * @param groupBy - Optional grouping field
2368
+ */
2369
+ aggregate<T = unknown>(source: string, metrics: Record<string, AggregateMetric>, groupBy?: string | string[]): Promise<T[]>;
2370
+ }
2371
+
2372
+ /**
2373
+ * Type alias for any schema definition.
2374
+ * Uses `any` intentionally to accept schemas with any content types, components, etc.
2375
+ * This is necessary for the SDK to be flexible with user-defined schemas.
2376
+ */
2377
+ type AnySchemaDefinition = SchemaDefinition<any, any, any, any, any>;
2378
+ /**
2379
+ * Client configuration with optional schema
2380
+ */
2381
+ interface TypedClientConfig<TSchema extends AnySchemaDefinition | undefined = undefined> extends RelyIOConfig {
2382
+ /** Schema definition for type inference (optional - omit for dynamic/untyped usage) */
2383
+ schema?: TSchema;
2384
+ }
2385
+ /**
2386
+ * Typed RelyIO client with full schema awareness
2387
+ *
2388
+ * @example
2389
+ * ```typescript
2390
+ * import { createClient } from '@relyio/sdk'
2391
+ * import { schema } from './schema'
2392
+ *
2393
+ * const relyio = createClient({
2394
+ * apiKey: process.env.RELYIO_API_KEY!,
2395
+ * schema,
2396
+ * })
2397
+ *
2398
+ * // Content operations
2399
+ * const products = await relyio.content('product').query().execute()
2400
+ *
2401
+ * // Search
2402
+ * const results = await relyio.search('wireless').in('product').execute()
2403
+ *
2404
+ * // Batch operations
2405
+ * const batch = await relyio.batch()
2406
+ * .create('product', { content: { title: 'New' } })
2407
+ * .execute()
2408
+ * ```
2409
+ */
2410
+ declare class TypedRelyIOClient<TSchema extends AnySchemaDefinition | undefined = undefined> {
2411
+ private httpClient;
2412
+ private config;
2413
+ private _search;
2414
+ private _batch;
2415
+ private _adhocQuery;
2416
+ private _schema;
2417
+ constructor(config: TypedClientConfig<TSchema>);
2418
+ /**
2419
+ * Get the schema definition
2420
+ */
2421
+ get schema(): TSchema;
2422
+ /**
2423
+ * Get the ad-hoc query client
2424
+ */
2425
+ get query(): AdhocQueryClient;
2426
+ /**
2427
+ * Get a content client for a specific content type
2428
+ * Type-safe when schema is provided: autocompletes content type names AND field names
2429
+ *
2430
+ * @example
2431
+ * ```typescript
2432
+ * const products = await relyio.content('product')
2433
+ * .query()
2434
+ * .where({ status: 'active' }) // ← Field names autocomplete with schema!
2435
+ * .locale('en-US')
2436
+ * .limit(20)
2437
+ * .execute()
2438
+ * ```
2439
+ */
2440
+ content<K extends TSchema extends AnySchemaDefinition ? SchemaContentTypeNames<TSchema> : string>(contentType: K): TSchema extends AnySchemaDefinition ? ContentClient<InferContentType<TSchema['contentTypes'][K & keyof TSchema['contentTypes']]>> : ContentClient<BaseContent>;
2441
+ /**
2442
+ * Get an asset client for a specific asset type.
2443
+ *
2444
+ * Mirrors `content()` but targets `entryType: 'asset'` entries on the same
2445
+ * dynamic `/{assetTypeId}` routes, and adds the `file(entryUid)` sub-resource
2446
+ * builder + a high-level `upload()` helper. Type-safe when a schema is
2447
+ * provided: autocompletes asset type names AND metadata field names.
2448
+ *
2449
+ * @example
2450
+ * ```typescript
2451
+ * const photo = await relyio.asset('productPhoto').upload(file)
2452
+ * .filename('shoe.jpg')
2453
+ * .visibility('public')
2454
+ * .execute()
2455
+ *
2456
+ * const heroUrl = await relyio.asset('productPhoto').file(photo._metadata.id).url({ variantId: 'hero' })
2457
+ * ```
2458
+ */
2459
+ asset<K extends TSchema extends AnySchemaDefinition ? SchemaAssetTypeNames<TSchema> : string>(assetType: K): TSchema extends AnySchemaDefinition ? AssetClient<InferAssetType<NonNullable<TSchema['assetTypes']>[K & keyof NonNullable<TSchema['assetTypes']>]>> : AssetClient<BaseContent>;
2460
+ /**
2461
+ * Get a singleton client for a specific singleton key
2462
+ * Type-safe when schema is provided: autocompletes singleton names AND field names
2463
+ *
2464
+ * @example
2465
+ * ```typescript
2466
+ * const homepage = await relyio.singleton('homepage')
2467
+ * .include(['featuredProducts'])
2468
+ * .locale('en')
2469
+ * .fetch()
2470
+ * ```
2471
+ */
2472
+ singleton<K extends TSchema extends AnySchemaDefinition ? SchemaSingletonNames<TSchema> : string>(key: K): TSchema extends AnySchemaDefinition ? SingletonClient<InferSingleton<TSchema['singletonContentTypes'][K & keyof TSchema['singletonContentTypes']]>> : SingletonClient<unknown>;
2473
+ /**
2474
+ * Get a settings client for a specific settings group
2475
+ * Type-safe when schema is provided: autocompletes settings group names AND field names
2476
+ *
2477
+ * @example
2478
+ * ```typescript
2479
+ * const emailConfig = await relyio.settings('email-config')
2480
+ * .include(['defaultTemplate'])
2481
+ * .fetch()
2482
+ * ```
2483
+ */
2484
+ settings<K extends TSchema extends AnySchemaDefinition ? SchemaSettingsGroupNames<TSchema> : string>(groupId: K): TSchema extends AnySchemaDefinition ? SettingsClient<InferSettingsGroup<NonNullable<TSchema['settingsGroups']>[K & keyof NonNullable<TSchema['settingsGroups']>]>> : SettingsClient<unknown>;
2485
+ /**
2486
+ * Start a search query with full-text, semantic, or hybrid search
2487
+ *
2488
+ * @example
2489
+ * ```typescript
2490
+ * const results = await relyio.search('wireless headphones')
2491
+ * .in('product')
2492
+ * .where({ status: 'active' })
2493
+ * .limit(20)
2494
+ * .includeScore()
2495
+ * .execute()
2496
+ * ```
2497
+ */
2498
+ search<T extends BaseContent = BaseContent>(query: string): SearchBuilder<T>;
2499
+ /**
2500
+ * Start an autocomplete query for search suggestions
2501
+ *
2502
+ * @example
2503
+ * ```typescript
2504
+ * const suggestions = await relyio.autocomplete('wire')
2505
+ * .in('product')
2506
+ * .field('title')
2507
+ * .fuzzy()
2508
+ * .limit(10)
2509
+ * .execute()
2510
+ * ```
2511
+ */
2512
+ autocomplete(query: string): AutocompleteBuilder;
2513
+ /**
2514
+ * Find content similar to a given entry using vector search
2515
+ *
2516
+ * @example
2517
+ * ```typescript
2518
+ * const similar = await relyio.similar('entry_abc123')
2519
+ * .in('product')
2520
+ * .locale('fr-FR')
2521
+ * .limit(5)
2522
+ * .execute()
2523
+ * ```
2524
+ */
2525
+ similar<T extends BaseContent = BaseContent>(sourceId: string): SimilarBuilder<T>;
2526
+ /**
2527
+ * Start a batch operation for bulk create/update/delete
2528
+ *
2529
+ * @example
2530
+ * ```typescript
2531
+ * const result = await relyio.batch()
2532
+ * .create('product', { content: { title: 'iPhone', price: 999 } })
2533
+ * .update('product', 'entry_abc', { content: { price: 899 } })
2534
+ * .delete('product', 'entry_xyz')
2535
+ * .upsertSingleton('homepage', { content: { hero: 'Welcome!' } })
2536
+ * .execute({ atomic: true })
2537
+ *
2538
+ * console.log(result.summary.succeeded)
2539
+ * ```
2540
+ */
2541
+ batch(): BatchBuilder<NonNullable<TSchema>>;
2542
+ /**
2543
+ * Build and execute an ad-hoc query dynamically
2544
+ *
2545
+ * @example
2546
+ * ```typescript
2547
+ * const result = await relyio.adhoc()
2548
+ * .from('product')
2549
+ * .where({ status: 'active' })
2550
+ * .include(['brand', 'categories'])
2551
+ * .select({
2552
+ * id: 'id',
2553
+ * title: 'title',
2554
+ * price: 'pricing.basePrice'
2555
+ * })
2556
+ * .limit(20)
2557
+ * .execute()
2558
+ * ```
2559
+ */
2560
+ adhoc<T = unknown>(): AdhocQueryBuilder<T>;
2561
+ /**
2562
+ * Execute a predefined query by name (from schema)
2563
+ * Type-safe when schema is provided: autocompletes query names
2564
+ *
2565
+ * @example
2566
+ * ```typescript
2567
+ * const featured = await relyio.executeQuery('featured-products', {
2568
+ * limit: 10,
2569
+ * category: 'electronics'
2570
+ * })
2571
+ * ```
2572
+ */
2573
+ executeQuery<K extends TSchema extends AnySchemaDefinition ? SchemaQueryNames<TSchema> : string>(queryName: K, params?: Record<string, unknown>): Promise<unknown[]>;
2574
+ /**
2575
+ * Get schema information from the API
2576
+ */
2577
+ getSchemaInfo(contentType?: string): Promise<unknown>;
2578
+ /**
2579
+ * List all content types from the API
2580
+ */
2581
+ listContentTypes(): Promise<Array<{
2582
+ id: string;
2583
+ name: string;
2584
+ type: 'content' | 'singleton';
2585
+ }>>;
2586
+ /**
2587
+ * Get API health status
2588
+ */
2589
+ health(): Promise<{
2590
+ status: 'ok' | 'degraded' | 'down';
2591
+ timestamp: string;
2592
+ }>;
2593
+ /**
2594
+ * Check if the API is ready
2595
+ */
2596
+ ready(): Promise<boolean>;
2597
+ /**
2598
+ * Test the connection to the API
2599
+ */
2600
+ testConnection(): Promise<boolean>;
2601
+ /**
2602
+ * Update the configuration and reinitialize all clients
2603
+ */
2604
+ updateConfig(newConfig: Partial<RelyIOConfig>): void;
2605
+ /**
2606
+ * Get the current configuration
2607
+ */
2608
+ getConfig(): Readonly<MergedRelyIOConfig>;
2609
+ /**
2610
+ * Set the API key
2611
+ */
2612
+ setApiKey(apiKey: string): void;
2613
+ /**
2614
+ * Get the HTTP client instance (for advanced usage)
2615
+ */
2616
+ getHttpClient(): HttpClient;
2617
+ /**
2618
+ * Create a locale-scoped client
2619
+ * All content operations on the returned client will use the specified locale
2620
+ *
2621
+ * Type-safe when schema is provided: autocompletes valid locale codes!
2622
+ *
2623
+ * @example
2624
+ * ```typescript
2625
+ * const spanish = relyio.withLocale('es-ES') // ← Autocompletes with schema!
2626
+ *
2627
+ * // All operations use Spanish
2628
+ * const products = await spanish.content('product').query().execute()
2629
+ * const homepage = await spanish.singleton('homepage').fetch()
2630
+ *
2631
+ * // Original client unchanged
2632
+ * const englishProducts = await relyio.content('product').query().execute()
2633
+ * ```
2634
+ */
2635
+ withLocale<L extends TSchema extends AnySchemaDefinition ? SchemaLocaleCodes<TSchema> : string>(locale: L): TypedRelyIOClient<TSchema>;
2636
+ /**
2637
+ * Get the current default locale
2638
+ */
2639
+ getLocale(): string | undefined;
2640
+ /**
2641
+ * Get available locales from the API
2642
+ *
2643
+ * @example
2644
+ * ```typescript
2645
+ * const { locales, defaultLocale } = await relyio.getLocales()
2646
+ *
2647
+ * // Build language switcher
2648
+ * const options = locales.map(l => ({
2649
+ * code: l.code,
2650
+ * label: l.displayName,
2651
+ * isDefault: l.default,
2652
+ * }))
2653
+ * ```
2654
+ */
2655
+ getLocales(search?: string): Promise<{
2656
+ locales: Array<{
2657
+ code: string;
2658
+ displayName?: string;
2659
+ default?: boolean;
2660
+ fallback?: string[];
2661
+ direction?: 'ltr' | 'rtl';
2662
+ active?: boolean;
2663
+ }>;
2664
+ defaultLocale: string | null;
2665
+ total: number;
2666
+ }>;
2667
+ /**
2668
+ * Get the default locale from the API
2669
+ */
2670
+ getDefaultLocale(): Promise<string | null>;
2671
+ }
2672
+ /**
2673
+ * Create a RelyIO client
2674
+ *
2675
+ * @example Without schema (dynamic usage)
2676
+ * ```typescript
2677
+ * import { createClient } from '@relyio/sdk'
2678
+ *
2679
+ * const relyio = createClient({
2680
+ * apiKey: process.env.RELYIO_API_KEY!,
2681
+ * })
2682
+ *
2683
+ * // Works but no autocomplete for content types
2684
+ * const products = await relyio.content('product').fetchAll()
2685
+ * ```
2686
+ *
2687
+ * @example With schema (fully typed)
2688
+ * ```typescript
2689
+ * import { createClient } from '@relyio/sdk'
2690
+ * import { schema } from './schema'
2691
+ *
2692
+ * const relyio = createClient({
2693
+ * apiKey: process.env.RELYIO_API_KEY!,
2694
+ * schema,
2695
+ * })
2696
+ *
2697
+ * // Content type names autocomplete!
2698
+ * const products = await relyio.content('product').fetchAll()
2699
+ * ```
2700
+ */
2701
+ declare function createClient<TSchema extends AnySchemaDefinition>(config: TypedClientConfig<TSchema>): TypedRelyIOClient<TSchema>;
2702
+ declare function createClient(config: RelyIOConfig): TypedRelyIOClient<undefined>;
2703
+ /**
2704
+ * Helper type to extract schema from client
2705
+ */
2706
+ type ClientSchema<T extends TypedRelyIOClient<AnySchemaDefinition>> = T extends TypedRelyIOClient<infer TSchema> ? TSchema : never;
2707
+
2708
+ export { AssetFileBuilder as $, AutocompleteBuilder as A, BatchClient as B, type ClientSchema as C, type BatchOperation as D, type ExtendedQueryResult as E, type BatchOptions as F, type BatchOperationResult as G, HttpClient as H, type BatchSummary as I, type BatchResponse as J, AdhocQueryClient as K, AdhocQueryBuilder as L, type FetchBlock as M, type AggregateBlock as N, type AggregateMetric as O, type SearchBlock as P, type SimilarBlock as Q, type RetryableWriteOptions as R, type SendOptions as S, TypedRelyIOClient as T, type FilterValue as U, type FullTextConfig as V, type AdhocQueryDefinition as W, type AdhocQueryOptions as X, type QueryMetadata as Y, type AdhocQueryResult as Z, AssetClient as _, type TypedClientConfig as a, AssetUploadBuilder as a0, isAssetEntry as a1, type AssetVisibility as a2, type FileStatus as a3, type FileBackend as a4, type AssetFileBlock as a5, type AssetFileExtracted as a6, type AssetUrls as a7, type AssetEntryResponse as a8, type PopulatedAssetReference as a9, type PresignInput as aa, type PresignResult as ab, type ConfirmInput as ac, type ConfirmResult as ad, type ResolveUrlInput as ae, type ResolveUrlResult as af, type CreateAssetOptions as ag, type WaitUntilReadyOptions as ah, type MultipartCreateResult as ai, type MultipartPart as aj, type MultipartTarget as ak, type UploadProgress as al, type FileErrorCode as am, FileUploadError as an, FileValidationError as ao, type FileValidationConstraints as ap, validateFile as aq, type ConditionalWriteOptions as b, createClient as c, ContentClient as d, entityTagOf as e, SingletonClient as f, SettingsClient as g, ContentQueryBuilder as h, ContentByIdBuilder as i, type ChildrenOptions as j, type SingletonFetchOptions as k, type SettingsFetchOptions as l, SearchClient as m, SearchBuilder as n, SimilarBuilder as o, type SearchMode as p, type SearchResultItem as q, type SearchResponse as r, type AutocompleteSuggestion as s, type AutocompleteResponse as t, type SimilarResult as u, type SimilarResponse as v, type SearchFilter as w, type SearchFields as x, BatchBuilder as y, type BatchMethod as z };