@relyio/sdk 0.6.0 → 0.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +370 -0
- package/dist/.tsbuildinfo +1 -1
- package/dist/{chunk-DY2XT7OF.cjs → chunk-72UK5U7T.cjs} +70 -60
- package/dist/{chunk-7ABNXVWL.js → chunk-GYHAVYND.js} +777 -399
- package/dist/{chunk-I3ESNANB.js → chunk-H6IBQSMM.js} +2 -2
- package/dist/{chunk-HT6NFRM2.cjs → chunk-TZL374LS.cjs} +2 -2
- package/dist/{chunk-GCMQUM6N.cjs → chunk-VH2DE7HZ.cjs} +776 -402
- package/dist/{chunk-XJVY2CLY.js → chunk-YEJUMW4X.js} +70 -60
- package/dist/client/index.cjs +4 -3
- package/dist/client/index.d.cts +4 -4
- package/dist/client/index.d.ts +4 -4
- package/dist/client/index.js +2 -1
- package/dist/{index-d1Kz1e19.d.cts → define-content-type-DiVpbwNs.d.cts} +1615 -684
- package/dist/{index-yPbhZc6q.d.ts → define-content-type-DiVpbwNs.d.ts} +1615 -684
- package/dist/{define-schema-1kr2HIzN.d.cts → define-schema-C-5an28C.d.ts} +189 -85
- package/dist/{define-schema-ChvAzY8H.d.ts → define-schema-D7MButVt.d.cts} +189 -85
- package/dist/{index-XqoGUaAY.d.ts → index-1G97sNA8.d.ts} +489 -267
- package/dist/index-BOQIHnsh.d.ts +818 -0
- package/dist/index-DbsTUw3Q.d.cts +818 -0
- package/dist/{index-Df1VsfJR.d.cts → index-M0X45k7J.d.cts} +489 -267
- package/dist/index.cjs +98 -114
- package/dist/index.d.cts +31 -31
- package/dist/index.d.ts +31 -31
- package/dist/index.js +8 -8
- package/dist/schema/index.cjs +35 -35
- package/dist/schema/index.d.cts +13 -11
- package/dist/schema/index.d.ts +13 -11
- package/dist/schema/index.js +1 -1
- package/dist/types/index.d.cts +2 -2
- package/dist/types/index.d.ts +2 -2
- package/dist/utils/index.cjs +19 -19
- package/dist/utils/index.d.cts +10 -1
- package/dist/utils/index.d.ts +10 -1
- package/dist/utils/index.js +1 -1
- package/package.json +4 -3
- package/dist/define-content-type-CDB3kyeA.d.cts +0 -1173
- package/dist/define-content-type-CDB3kyeA.d.ts +0 -1173
|
@@ -0,0 +1,818 @@
|
|
|
1
|
+
import { aJ as paths, Q as EntryMetadata, aK as components } from './define-content-type-DiVpbwNs.js';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Where the SDK's transport gets its truth.
|
|
5
|
+
*
|
|
6
|
+
* Every route this client calls is named by its **path template as the
|
|
7
|
+
* committed document spells it**, and the body type it expects is read out of
|
|
8
|
+
* that document rather than written down here. `api-schema.ts` beside this file
|
|
9
|
+
* is generated by `pnpm api:types` from `apps/services/api/openapi-spec.json`,
|
|
10
|
+
* which is itself generated from that service's `RouteContract`s
|
|
11
|
+
* (`INV-API-DOCUMENT-IS-DERIVED`). So the chain is: one declaration in
|
|
12
|
+
* `routes.ts` → the serializer that produces the body → the document → these
|
|
13
|
+
* types → the method that reads it.
|
|
14
|
+
*
|
|
15
|
+
* That chain is the answer to the question this phase exists to close. Before
|
|
16
|
+
* it, six SDK methods read a key the server does not send; each had no caller
|
|
17
|
+
* and no test, so nothing could see them. Now:
|
|
18
|
+
*
|
|
19
|
+
* - A route that changes **kind** — `collection` to `object`, say — changes
|
|
20
|
+
* whether the document's 200 body carries `page` and whether `data` is an
|
|
21
|
+
* array. `Wire<…>` follows, `readPayload` follows, and the method's declared
|
|
22
|
+
* return type stops matching. `tsc` names the method.
|
|
23
|
+
* - A route that changes **path** or is deleted stops being a `keyof paths`, so
|
|
24
|
+
* the call site fails on the literal.
|
|
25
|
+
* - A route that changes **dialect** changes `page` from `CursorPage` to
|
|
26
|
+
* `OffsetPage`, and anything reading `nextCursor` fails.
|
|
27
|
+
*
|
|
28
|
+
* `/v1` lives in these templates because it lives in the document
|
|
29
|
+
* (`INV-API-VERSION-IS-DECIDED-ONCE`). Before this phase the SDK's base URL was
|
|
30
|
+
* `https://api.relyio.com` and its paths were `/{contentType}`, so every
|
|
31
|
+
* versioned request went to a path the router does not serve.
|
|
32
|
+
*
|
|
33
|
+
* Spec: `devtools/docs/guide/engineering/api-contract-spec.md`,
|
|
34
|
+
* `INV-API-CLIENT-IS-DERIVED`.
|
|
35
|
+
*/
|
|
36
|
+
|
|
37
|
+
/** Every path the committed document declares. */
|
|
38
|
+
type ApiPath = keyof paths;
|
|
39
|
+
type Verb = 'get' | 'post' | 'put' | 'patch' | 'delete';
|
|
40
|
+
/** The verbs the document declares on this path — not the seven HTTP has. */
|
|
41
|
+
type ApiMethod<P extends ApiPath> = {
|
|
42
|
+
[M in Verb]: M extends keyof paths[P] ? paths[P][M] extends {
|
|
43
|
+
responses: unknown;
|
|
44
|
+
} ? M : never : never;
|
|
45
|
+
}[Verb];
|
|
46
|
+
type Operation<P extends ApiPath, M extends ApiMethod<P>> = M extends keyof paths[P] ? paths[P][M] : never;
|
|
47
|
+
type JsonBody<R> = R extends {
|
|
48
|
+
content: {
|
|
49
|
+
'application/json': infer B;
|
|
50
|
+
};
|
|
51
|
+
} ? B : never;
|
|
52
|
+
/**
|
|
53
|
+
* The success body the document declares, preferring 200 where a route serves
|
|
54
|
+
* both. A route with neither — `kind: 'empty'`, which serves 204 — resolves to
|
|
55
|
+
* `never`, and `readPayload` cannot be called on it.
|
|
56
|
+
*/
|
|
57
|
+
type ApiSuccessBody<P extends ApiPath, M extends ApiMethod<P>> = Operation<P, M> extends {
|
|
58
|
+
responses: infer R;
|
|
59
|
+
} ? 200 extends keyof R ? JsonBody<R[200]> : 201 extends keyof R ? JsonBody<R[201]> : never : never;
|
|
60
|
+
/**
|
|
61
|
+
* The document's success body with the payload typed as the caller's `T`.
|
|
62
|
+
*
|
|
63
|
+
* The array arm is tested **first** on purpose: `{ data: unknown[] }` also
|
|
64
|
+
* satisfies `{ data: unknown }`, so the object arm would swallow every
|
|
65
|
+
* collection and hand a list route's caller a single entity — which is the
|
|
66
|
+
* exact defect class (`ContentClient.children` reading `.data.items` from an
|
|
67
|
+
* object route) that this phase makes unrepresentable.
|
|
68
|
+
*/
|
|
69
|
+
type Wire<P extends ApiPath, M extends ApiMethod<P>, T> = ApiSuccessBody<P, M> extends {
|
|
70
|
+
data: unknown[];
|
|
71
|
+
} ? Omit<ApiSuccessBody<P, M>, 'data'> & {
|
|
72
|
+
data: T[];
|
|
73
|
+
} : ApiSuccessBody<P, M> extends {
|
|
74
|
+
data: unknown;
|
|
75
|
+
} ? Omit<ApiSuccessBody<P, M>, 'data'> & {
|
|
76
|
+
data: T;
|
|
77
|
+
} : ApiSuccessBody<P, M>;
|
|
78
|
+
/**
|
|
79
|
+
* The path parameters the document declares for this operation, or `never`
|
|
80
|
+
* where it declares none.
|
|
81
|
+
*
|
|
82
|
+
* `never` rather than `{}`: a route with no segments must **forbid** a `path`
|
|
83
|
+
* argument, not merely tolerate an empty one, and `keyof Record<string, never>`
|
|
84
|
+
* is `string`, so an empty-object encoding cannot be told apart from a real
|
|
85
|
+
* one at the type level.
|
|
86
|
+
*/
|
|
87
|
+
type PathParamsOf<P extends ApiPath, M extends ApiMethod<P>> = Operation<P, M> extends {
|
|
88
|
+
parameters: {
|
|
89
|
+
path?: infer X;
|
|
90
|
+
};
|
|
91
|
+
} ? [X] extends [undefined] ? never : NonNullable<X> : never;
|
|
92
|
+
/** What {@link matchRoute} recovers from a URL the client emitted. */
|
|
93
|
+
interface RouteMatch {
|
|
94
|
+
/** The document's path template — the value a tag translator switches on. */
|
|
95
|
+
path: ApiPath;
|
|
96
|
+
/** The template's `{name}` segments, decoded: the inverse of {@link fillPath}. */
|
|
97
|
+
params: Record<string, string>;
|
|
98
|
+
/** The query string; a key that appears more than once comes back as an array. */
|
|
99
|
+
query: Record<string, string | string[]>;
|
|
100
|
+
}
|
|
101
|
+
interface MatchRouteOptions {
|
|
102
|
+
/**
|
|
103
|
+
* The client's `baseUrl`, when it carries a path prefix of its own
|
|
104
|
+
* (`https://example.com/relyio`). Its path is removed before the template is
|
|
105
|
+
* matched. Unneeded for the default `https://api.relyio.com`.
|
|
106
|
+
*/
|
|
107
|
+
baseUrl?: string;
|
|
108
|
+
}
|
|
109
|
+
/**
|
|
110
|
+
* Recover the route a URL names: the inverse of {@link fillPath}, over the same
|
|
111
|
+
* committed document (`INV-API-CLIENT-IS-DERIVED`).
|
|
112
|
+
*
|
|
113
|
+
* `matchRoute(url).path` is an `ApiPath`, so anything that keys behaviour on a
|
|
114
|
+
* request — a cache-tag translator, say — switches on the document's templates
|
|
115
|
+
* and never on a route table of its own.
|
|
116
|
+
*
|
|
117
|
+
* Returns `null` for a URL no declared template matches — a presigned storage
|
|
118
|
+
* URL, or a path outside `baseUrl` — rather than a guess.
|
|
119
|
+
*/
|
|
120
|
+
declare function matchRoute(url: string | URL, options?: MatchRouteOptions): RouteMatch | null;
|
|
121
|
+
|
|
122
|
+
/**
|
|
123
|
+
* Configuration types for the RelyIO SDK
|
|
124
|
+
*/
|
|
125
|
+
|
|
126
|
+
interface RelyIOConfig {
|
|
127
|
+
/**
|
|
128
|
+
* API key for authentication, sent as `X-API-Key`.
|
|
129
|
+
*
|
|
130
|
+
* Required by every request, not by construction: a client built without one — a module-scope
|
|
131
|
+
* client whose `process.env` variable is unset during a build — constructs, and each request it
|
|
132
|
+
* makes rejects with `MissingApiKeyError` before anything is sent. Under `relyio dev` the
|
|
133
|
+
* sandbox's key is used instead, so none need be configured.
|
|
134
|
+
*/
|
|
135
|
+
apiKey: string;
|
|
136
|
+
/** Base URL for the RelyIO API */
|
|
137
|
+
baseUrl?: string;
|
|
138
|
+
/** Dataset to use */
|
|
139
|
+
dataset?: string;
|
|
140
|
+
/**
|
|
141
|
+
* Default locale for all content queries
|
|
142
|
+
* Can be overridden per-query using .locale() or .withLocale()
|
|
143
|
+
*
|
|
144
|
+
* @example
|
|
145
|
+
* const client = createClient({
|
|
146
|
+
* apiKey: '...',
|
|
147
|
+
* locale: 'en-US', // All queries default to English
|
|
148
|
+
* })
|
|
149
|
+
*/
|
|
150
|
+
locale?: string;
|
|
151
|
+
/**
|
|
152
|
+
* How long one request may take, in milliseconds, before it ends as a `TimeoutError` (defaults
|
|
153
|
+
* to 30000). A number from 1 to 2147483647, the longest a timer waits; `0`, `NaN`, `Infinity` or
|
|
154
|
+
* anything larger is refused, and no value turns the timeout off.
|
|
155
|
+
*/
|
|
156
|
+
timeout?: number;
|
|
157
|
+
/**
|
|
158
|
+
* The most attempts one request makes, the first included (defaults to 3). `1` turns retrying
|
|
159
|
+
* off; a value that is not a whole number of at least 1 is refused. Only a failure a later attempt
|
|
160
|
+
* can change is retried, and only where sending the request twice cannot apply it twice.
|
|
161
|
+
*/
|
|
162
|
+
retryAttempts?: number;
|
|
163
|
+
/** Whether to use HTTPS (defaults to true) */
|
|
164
|
+
useSsl?: boolean;
|
|
165
|
+
/** Environment mode */
|
|
166
|
+
environment?: 'development' | 'staging' | 'production';
|
|
167
|
+
/**
|
|
168
|
+
* Cache configuration.
|
|
169
|
+
*
|
|
170
|
+
* Declarative only: the client keeps no response cache, and nothing in it reads
|
|
171
|
+
* these values, so every call goes to `fetch`. There is no SDK-side cache to
|
|
172
|
+
* turn off under a framework that caches `fetch` itself (Next.js).
|
|
173
|
+
*/
|
|
174
|
+
cache?: CacheConfig;
|
|
175
|
+
/** Real-time configuration */
|
|
176
|
+
realtime?: RealtimeConfig;
|
|
177
|
+
/** Debug configuration */
|
|
178
|
+
debug?: DebugConfig;
|
|
179
|
+
/** Custom headers to include with requests */
|
|
180
|
+
headers?: Record<string, string>;
|
|
181
|
+
/**
|
|
182
|
+
* The function every request to the RelyIO API is sent through. Omit it and the
|
|
183
|
+
* client calls the runtime's `globalThis.fetch`, looked up per request. File
|
|
184
|
+
* bytes an asset upload PUTs straight to a presigned storage URL do not pass
|
|
185
|
+
* through it.
|
|
186
|
+
*
|
|
187
|
+
* The client still builds every request — URL, method, headers, body, signal —
|
|
188
|
+
* and hands this function that `RequestInit` unchanged, retries included, so an
|
|
189
|
+
* `Idempotency-Key` minted for a write is the same on every attempt. Supply one
|
|
190
|
+
* to add what your framework reads off the `RequestInit`, for example Next.js
|
|
191
|
+
* cache tags:
|
|
192
|
+
*
|
|
193
|
+
* @example
|
|
194
|
+
* const client = createClient({
|
|
195
|
+
* apiKey: '...',
|
|
196
|
+
* fetch: (input, init) => fetch(input, { ...init, next: { tags: ['posts'] } }),
|
|
197
|
+
* })
|
|
198
|
+
*/
|
|
199
|
+
fetch?: typeof globalThis.fetch;
|
|
200
|
+
/** Browser-specific configuration */
|
|
201
|
+
browser?: BrowserConfig;
|
|
202
|
+
/**
|
|
203
|
+
* `relyio dev` sandbox detection.
|
|
204
|
+
*
|
|
205
|
+
* When developing under `relyio dev`, the CLI injects an ephemeral sandbox's dataset + scoped
|
|
206
|
+
* preview API key (via env vars or a local `.relyio/dev.json`). The SDK then transparently
|
|
207
|
+
* targets that sandbox instead of production — no code changes required.
|
|
208
|
+
*
|
|
209
|
+
* - Omit (default): detection runs automatically unless `NODE_ENV === 'production'`, so
|
|
210
|
+
* `relyio dev -- pnpm dev` "just works" while production builds stay untouched.
|
|
211
|
+
* - `false`: disable detection entirely (force production targeting even in a dev shell).
|
|
212
|
+
* - `true`: force detection even when `NODE_ENV === 'production'`.
|
|
213
|
+
*/
|
|
214
|
+
dev?: boolean;
|
|
215
|
+
}
|
|
216
|
+
/**
|
|
217
|
+
* What `updateConfig()` takes: any option of {@link RelyIOConfig}, each laid over the value the
|
|
218
|
+
* client holds rather than over its default.
|
|
219
|
+
*
|
|
220
|
+
* An option left out or given as `undefined` keeps the value the client holds, and a bag of options
|
|
221
|
+
* you name keeps every field you do not name. `null` removes — "`undefined` keeps, `null` removes" —
|
|
222
|
+
* and the types accept it only where a value can go: an option with no default (`locale`,
|
|
223
|
+
* `dataset`, `fetch`, `browser.onCorsError`, `debug.logger`, …), which is then absent, and a header,
|
|
224
|
+
* which is then no longer sent. An option with a default takes no `null`: removed, it would leave
|
|
225
|
+
* nothing but that default. `createClient()` has nothing to remove, so its options take no `null`.
|
|
226
|
+
*
|
|
227
|
+
* Which options have no default is read off {@link UndefaultedOption} and {@link UndefaultedField} —
|
|
228
|
+
* the names {@link ConfigDefaults} leaves optional — so an option that gains or loses a default
|
|
229
|
+
* changes what `null` can clear with no edit here.
|
|
230
|
+
*/
|
|
231
|
+
type RelyIOConfigUpdate = Update<Omit<RelyIOConfig, OptionBag | 'headers'>, UndefaultedOption> & {
|
|
232
|
+
browser?: Update<BrowserConfig, UndefaultedField['browser']>;
|
|
233
|
+
cache?: Update<CacheConfig, UndefaultedField['cache']>;
|
|
234
|
+
realtime?: Update<RealtimeConfig, UndefaultedField['realtime']>;
|
|
235
|
+
debug?: Update<DebugConfig, UndefaultedField['debug']>;
|
|
236
|
+
/** Headers to add or replace, each kept until named again; `null` stops sending that header. */
|
|
237
|
+
headers?: Record<string, string | null>;
|
|
238
|
+
};
|
|
239
|
+
/** `T`'s options, each one optional, with `null` accepted for the ones named in `Clearable`. */
|
|
240
|
+
type Update<T, Clearable extends keyof T> = {
|
|
241
|
+
[K in keyof T]?: K extends Clearable ? T[K] | null : T[K];
|
|
242
|
+
};
|
|
243
|
+
interface CacheConfig {
|
|
244
|
+
/** Whether caching is enabled (defaults to true) */
|
|
245
|
+
enabled?: boolean;
|
|
246
|
+
/** Cache TTL in milliseconds (defaults to 5 minutes) */
|
|
247
|
+
ttl?: number;
|
|
248
|
+
/** Maximum number of cache entries (defaults to 1000) */
|
|
249
|
+
maxEntries?: number;
|
|
250
|
+
/** Maximum cache size in bytes (defaults to 50MB) */
|
|
251
|
+
maxSize?: number;
|
|
252
|
+
/** Cache strategies to use */
|
|
253
|
+
strategies?: CacheStrategy[];
|
|
254
|
+
}
|
|
255
|
+
interface RealtimeConfig {
|
|
256
|
+
/** Whether real-time features are enabled (defaults to true) */
|
|
257
|
+
enabled?: boolean;
|
|
258
|
+
/** WebSocket URL for real-time connections */
|
|
259
|
+
wsUrl?: string;
|
|
260
|
+
/** Maximum number of reconnection attempts (defaults to 10) */
|
|
261
|
+
maxReconnectAttempts?: number;
|
|
262
|
+
/** Reconnection delay in milliseconds (defaults to 1000) */
|
|
263
|
+
reconnectDelay?: number;
|
|
264
|
+
/** Heartbeat interval in milliseconds (defaults to 30000) */
|
|
265
|
+
heartbeatInterval?: number;
|
|
266
|
+
}
|
|
267
|
+
interface DebugConfig {
|
|
268
|
+
/** Whether debug mode is enabled (defaults to false) */
|
|
269
|
+
enabled?: boolean;
|
|
270
|
+
/** Whether to log network requests (defaults to false) */
|
|
271
|
+
logRequests?: boolean;
|
|
272
|
+
/** Whether to log cache operations (defaults to false) */
|
|
273
|
+
logCache?: boolean;
|
|
274
|
+
/** Whether to log WebSocket events (defaults to false) */
|
|
275
|
+
logWebSocket?: boolean;
|
|
276
|
+
/** Custom logger function */
|
|
277
|
+
logger?: (_level: LogLevel, _message: string, _data?: unknown) => void;
|
|
278
|
+
}
|
|
279
|
+
/**
|
|
280
|
+
* The numbers the retry backoff is computed from.
|
|
281
|
+
*
|
|
282
|
+
* There is no `retryCondition` here any more. It was a predicate over
|
|
283
|
+
* `(error, attempt)`, which is structurally unable to see the request — so the
|
|
284
|
+
* only rule it could express is "is this error retryable", and the rule that
|
|
285
|
+
* matters is "may this request be sent twice". That question, and the answer,
|
|
286
|
+
* live in `RetryPolicy` (`http/retry-policy.ts`).
|
|
287
|
+
*/
|
|
288
|
+
interface RetryConfig {
|
|
289
|
+
/** Maximum number of attempts, including the first. */
|
|
290
|
+
maxAttempts: number;
|
|
291
|
+
/** Base delay between retries in milliseconds */
|
|
292
|
+
baseDelay: number;
|
|
293
|
+
/** Maximum delay between retries in milliseconds */
|
|
294
|
+
maxDelay: number;
|
|
295
|
+
/** Backoff factor for exponential backoff */
|
|
296
|
+
backoffFactor: number;
|
|
297
|
+
/** Whether to add jitter to retry delays */
|
|
298
|
+
jitter: boolean;
|
|
299
|
+
}
|
|
300
|
+
interface RequestConfig {
|
|
301
|
+
/** HTTP method */
|
|
302
|
+
method: 'GET' | 'POST' | 'PUT' | 'DELETE' | 'PATCH';
|
|
303
|
+
/** Request URL */
|
|
304
|
+
url: string;
|
|
305
|
+
/** Request headers */
|
|
306
|
+
headers?: Record<string, string>;
|
|
307
|
+
/** Request body */
|
|
308
|
+
body?: unknown;
|
|
309
|
+
/** Query parameters */
|
|
310
|
+
params?: Record<string, unknown>;
|
|
311
|
+
/** Request timeout in milliseconds */
|
|
312
|
+
timeout?: number;
|
|
313
|
+
/** Whether to retry this request on failure */
|
|
314
|
+
retryable?: boolean;
|
|
315
|
+
/** Abort signal for canceling requests */
|
|
316
|
+
signal?: AbortSignal;
|
|
317
|
+
/** Additional metadata for the request */
|
|
318
|
+
metadata?: Record<string, unknown>;
|
|
319
|
+
}
|
|
320
|
+
interface ApiResponse<T = unknown> {
|
|
321
|
+
/**
|
|
322
|
+
* The response body exactly as the route's contract declares it —
|
|
323
|
+
* `{ data }`, `{ data, page }`, `{ data, page, meta }` or `{ empty }`.
|
|
324
|
+
*
|
|
325
|
+
* Named `body`, not `data`, because the envelope's own key is `data`: the
|
|
326
|
+
* incumbent `response.data.data` read like a typo and was one 33 times, in
|
|
327
|
+
* six methods that read a key the server never sent. One `data` in the
|
|
328
|
+
* spelling means `readPayload(response.body)` is the only way to reach a
|
|
329
|
+
* payload (`INV-API-CLIENT-IS-DERIVED`).
|
|
330
|
+
*/
|
|
331
|
+
body: T;
|
|
332
|
+
/** HTTP status code */
|
|
333
|
+
status: number;
|
|
334
|
+
/** HTTP status text */
|
|
335
|
+
statusText: string;
|
|
336
|
+
/** Response headers */
|
|
337
|
+
headers: Record<string, string>;
|
|
338
|
+
/** Request configuration that generated this response */
|
|
339
|
+
config: RequestConfig;
|
|
340
|
+
/** Whether this response came from cache */
|
|
341
|
+
fromCache?: boolean;
|
|
342
|
+
/** Cache metadata */
|
|
343
|
+
cacheMetadata?: {
|
|
344
|
+
key: string;
|
|
345
|
+
timestamp: number;
|
|
346
|
+
ttl: number;
|
|
347
|
+
};
|
|
348
|
+
}
|
|
349
|
+
interface BrowserConfig {
|
|
350
|
+
/**
|
|
351
|
+
* Whether requests the client sends from a web page carry the browser's cookies to another
|
|
352
|
+
* origin (defaults to `false`).
|
|
353
|
+
*
|
|
354
|
+
* `false` — and leaving it out, or writing `undefined` — sends every request with
|
|
355
|
+
* `credentials: 'same-origin'`: nothing to another origin, and a same-origin request — the API
|
|
356
|
+
* behind a proxy on your own host — sent as the platform's own `fetch` would send it. `true`
|
|
357
|
+
* sends it with `credentials: 'include'`, which a browser completes only when the answer names
|
|
358
|
+
* your page's origin in `Access-Control-Allow-Origin` (not `*`) and carries
|
|
359
|
+
* `Access-Control-Allow-Credentials: true`. The RelyIO API authenticates with your API key and
|
|
360
|
+
* reads no cookie, so set `true` only for a server of your own in front of it that does.
|
|
361
|
+
*
|
|
362
|
+
* Applied only in a web page. On a server, in an edge runtime or in a Web Worker the client sets
|
|
363
|
+
* no `credentials` mode at all, whatever this says.
|
|
364
|
+
*/
|
|
365
|
+
includeCredentials?: boolean;
|
|
366
|
+
/** Whether to use relative URLs for same-origin requests (defaults to false) */
|
|
367
|
+
useRelativeUrls?: boolean;
|
|
368
|
+
/** Custom CORS error handling */
|
|
369
|
+
onCorsError?: (_error: Error, _context: unknown) => void;
|
|
370
|
+
}
|
|
371
|
+
type CacheStrategy = 'memory' | 'persistent' | 'network-first' | 'cache-first' | 'stale-while-revalidate';
|
|
372
|
+
type LogLevel = 'debug' | 'info' | 'warn' | 'error';
|
|
373
|
+
type Environment = 'development' | 'staging' | 'production';
|
|
374
|
+
/**
|
|
375
|
+
* `T` once its defaults are applied: every field present, except the ones named in `NoDefault`,
|
|
376
|
+
* which stay as optional as `T` declares them because nothing stands in for them.
|
|
377
|
+
*/
|
|
378
|
+
type WithDefaults<T, NoDefault extends keyof T = never> = Required<Omit<T, NoDefault>> & Pick<T, NoDefault>;
|
|
379
|
+
/**
|
|
380
|
+
* The options with no default, absent from a merged configuration until the caller gives one.
|
|
381
|
+
* `fetch` among them: absent means the runtime's own, and that is read per request rather than
|
|
382
|
+
* captured here.
|
|
383
|
+
*/
|
|
384
|
+
type UndefaultedOption = 'apiKey' | 'dataset' | 'locale' | 'dev' | 'fetch';
|
|
385
|
+
/** The options that are themselves bags of options, each merged field by field. */
|
|
386
|
+
type OptionBag = 'browser' | 'cache' | 'realtime' | 'debug';
|
|
387
|
+
/**
|
|
388
|
+
* The fields of each bag with no default, absent from a merged configuration until the caller gives
|
|
389
|
+
* one — and the fields of a bag `updateConfig()` can clear with `null`.
|
|
390
|
+
*/
|
|
391
|
+
interface UndefaultedField {
|
|
392
|
+
browser: 'onCorsError';
|
|
393
|
+
cache: never;
|
|
394
|
+
realtime: 'wsUrl';
|
|
395
|
+
debug: 'logger';
|
|
396
|
+
}
|
|
397
|
+
/**
|
|
398
|
+
* The value each option takes when the caller gives none, down to each field of each bag — so a
|
|
399
|
+
* field of a merged configuration that has a default is never `undefined`, and a reader does not
|
|
400
|
+
* restate the default. A field with no default (`onCorsError`, `wsUrl`, `logger`) is named in
|
|
401
|
+
* {@link UndefaultedField}; a field added to a bag without one fails to compile at
|
|
402
|
+
* {@link DEFAULT_CONFIG} until it gets a default or a name there.
|
|
403
|
+
*/
|
|
404
|
+
type ConfigDefaults = WithDefaults<Omit<RelyIOConfig, UndefaultedOption | OptionBag>> & {
|
|
405
|
+
browser: WithDefaults<BrowserConfig, UndefaultedField['browser']>;
|
|
406
|
+
cache: WithDefaults<CacheConfig, UndefaultedField['cache']>;
|
|
407
|
+
realtime: WithDefaults<RealtimeConfig, UndefaultedField['realtime']>;
|
|
408
|
+
debug: WithDefaults<DebugConfig, UndefaultedField['debug']>;
|
|
409
|
+
};
|
|
410
|
+
/** The configuration a client runs on: each option the caller gave, and its default where not. */
|
|
411
|
+
type MergedRelyIOConfig = ConfigDefaults & Pick<RelyIOConfig, UndefaultedOption>;
|
|
412
|
+
|
|
413
|
+
/**
|
|
414
|
+
* Base error class for all RelyIO SDK errors
|
|
415
|
+
* Provides consistent error handling, context tracking, and retry logic
|
|
416
|
+
*/
|
|
417
|
+
declare abstract class SDKError extends Error {
|
|
418
|
+
/** Unique error code for programmatic error handling */
|
|
419
|
+
abstract readonly code: string;
|
|
420
|
+
/** Whether this error is retryable */
|
|
421
|
+
abstract readonly retryable: boolean;
|
|
422
|
+
/** HTTP status code if applicable */
|
|
423
|
+
readonly statusCode?: number;
|
|
424
|
+
/** Original error that caused this error */
|
|
425
|
+
readonly cause?: Error;
|
|
426
|
+
/** Additional context about the error */
|
|
427
|
+
readonly context?: Record<string, unknown>;
|
|
428
|
+
/** Timestamp when the error occurred */
|
|
429
|
+
readonly timestamp: number;
|
|
430
|
+
constructor(message: string, statusCode?: number, cause?: Error, context?: Record<string, unknown>);
|
|
431
|
+
/**
|
|
432
|
+
* Convert error to JSON for logging and debugging
|
|
433
|
+
*/
|
|
434
|
+
toJSON(): {
|
|
435
|
+
name: string;
|
|
436
|
+
message: string;
|
|
437
|
+
code: string;
|
|
438
|
+
statusCode: number | undefined;
|
|
439
|
+
retryable: boolean;
|
|
440
|
+
context: Record<string, unknown> | undefined;
|
|
441
|
+
timestamp: number;
|
|
442
|
+
stack: string | undefined;
|
|
443
|
+
};
|
|
444
|
+
/**
|
|
445
|
+
* Get a user-friendly error message
|
|
446
|
+
*/
|
|
447
|
+
getUserMessage(): string;
|
|
448
|
+
/**
|
|
449
|
+
* Get detailed error information for debugging
|
|
450
|
+
*/
|
|
451
|
+
getDebugInfo(): string;
|
|
452
|
+
/**
|
|
453
|
+
* Check if this error is of a specific type
|
|
454
|
+
*/
|
|
455
|
+
isType(errorType: string): boolean;
|
|
456
|
+
/**
|
|
457
|
+
* Create a new error with additional context
|
|
458
|
+
*/
|
|
459
|
+
withContext(context: Record<string, unknown>): this;
|
|
460
|
+
}
|
|
461
|
+
|
|
462
|
+
/**
|
|
463
|
+
* Validation errors and related interfaces.
|
|
464
|
+
*
|
|
465
|
+
* `SchemaValidationError`, `RequiredFieldError` and `InvalidFieldTypeError` were deleted by
|
|
466
|
+
* REL-1471: nothing constructed them. The API refuses content the schema does not accept with one
|
|
467
|
+
* code, `CONTENT_INVALID`, whose `details.issues` name each field and its problem — the mapper
|
|
468
|
+
* throws `ValidationError` for it, and a missing required field or a value of the wrong type is a
|
|
469
|
+
* `violations` entry whose `code` says which.
|
|
470
|
+
*/
|
|
471
|
+
interface ValidationViolation {
|
|
472
|
+
field: string;
|
|
473
|
+
message: string;
|
|
474
|
+
code: string;
|
|
475
|
+
value?: unknown;
|
|
476
|
+
path?: string[];
|
|
477
|
+
}
|
|
478
|
+
declare abstract class BaseValidationError extends SDKError {
|
|
479
|
+
readonly retryable = false;
|
|
480
|
+
readonly violations: ValidationViolation[];
|
|
481
|
+
/**
|
|
482
|
+
* @param context - Carried beside `violations`. The mapper passes the refusal body as
|
|
483
|
+
* `context.response`, as it does for every other contract code, so a consumer that hands the
|
|
484
|
+
* service's own words on can read them here too.
|
|
485
|
+
*/
|
|
486
|
+
constructor(message?: string, violations?: ValidationViolation[], statusCode?: number, cause?: Error, context?: Record<string, unknown>);
|
|
487
|
+
getUserMessage(): string;
|
|
488
|
+
/**
|
|
489
|
+
* Get violations for a specific field
|
|
490
|
+
*/
|
|
491
|
+
getViolationsForField(fieldName: string): ValidationViolation[];
|
|
492
|
+
/**
|
|
493
|
+
* Check if a specific field has validation errors
|
|
494
|
+
*/
|
|
495
|
+
hasFieldError(fieldName: string): boolean;
|
|
496
|
+
/**
|
|
497
|
+
* Get all validation messages as a formatted string
|
|
498
|
+
*/
|
|
499
|
+
getFormattedMessages(): string;
|
|
500
|
+
}
|
|
501
|
+
declare class ValidationError extends BaseValidationError {
|
|
502
|
+
readonly code = "VALIDATION_ERROR";
|
|
503
|
+
}
|
|
504
|
+
declare class InvalidPayloadError extends BaseValidationError {
|
|
505
|
+
readonly code = "INVALID_PAYLOAD";
|
|
506
|
+
constructor(message?: string, violations?: ValidationViolation[], cause?: Error, context?: Record<string, unknown>);
|
|
507
|
+
getUserMessage(): string;
|
|
508
|
+
}
|
|
509
|
+
|
|
510
|
+
/**
|
|
511
|
+
* Core type definitions for the RelyIO SDK
|
|
512
|
+
*/
|
|
513
|
+
|
|
514
|
+
/**
|
|
515
|
+
* Base interface for all content items
|
|
516
|
+
* Matches the actual RelyIO API structure with _metadata object
|
|
517
|
+
*/
|
|
518
|
+
interface BaseContent {
|
|
519
|
+
/**
|
|
520
|
+
* What the read reports about the entry beside its fields: the committed document's
|
|
521
|
+
* `EntryMetadata`, and nothing added to it. A `sourceDatasetId` was declared here for an entry
|
|
522
|
+
* inherited from a linked dataset's source; no read sends one.
|
|
523
|
+
*/
|
|
524
|
+
_metadata: EntryMetadata;
|
|
525
|
+
}
|
|
526
|
+
/**
|
|
527
|
+
* Whether `T` declares no field: `BaseContent`, the entry of a client with no schema registered.
|
|
528
|
+
*
|
|
529
|
+
* Every input that names fields — a condition, a sort, a selection, a write — takes any name then,
|
|
530
|
+
* because nothing declares which exist; the API checks them against the deployed schema. Before
|
|
531
|
+
* REL-1406 only `where` asked, so the others took no field at all from an unregistered client.
|
|
532
|
+
*/
|
|
533
|
+
type DeclaresNoField<T> = [Exclude<keyof T, '_metadata'>] extends [never] ? true : false;
|
|
534
|
+
/**
|
|
535
|
+
* The value a `where` condition compares a field against: one of the values the field holds.
|
|
536
|
+
*
|
|
537
|
+
* Not the field's read type. The REST dialect (`fromRestWhere` in `@relyio/entry-reads`) reads a
|
|
538
|
+
* bare value as `eq`, an array as `in` and an object as operators — refusing an object with none —
|
|
539
|
+
* and drops `null`. So a list field (`tags`) is matched by one of its items, a reference by the
|
|
540
|
+
* target's id, and a component or an expanded entry is never a value to send.
|
|
541
|
+
*/
|
|
542
|
+
type WhereScalar<V> = Exclude<V extends readonly (infer Item)[] ? Item : V, null | undefined | object>;
|
|
543
|
+
/** One field's condition: a value (`eq`), a list of values (`in`), or operators. */
|
|
544
|
+
type WhereFieldCondition<V> = WhereScalar<V> | WhereScalar<V>[] | WhereOperator<WhereScalar<V>>;
|
|
545
|
+
/**
|
|
546
|
+
* Query conditions for filtering content: conditions on the fields the content type declares.
|
|
547
|
+
*
|
|
548
|
+
* Typed by the content type's fields where a schema types the client: each key is a declared field
|
|
549
|
+
* and each value one of that field's values. Untyped (`BaseContent`, which declares no fields), any
|
|
550
|
+
* field name is accepted — nothing declares which exist.
|
|
551
|
+
*
|
|
552
|
+
* Nothing under `_metadata` is a field. This type used to accept `{ _metadata: { status } }`, and
|
|
553
|
+
* the API filters on no `_metadata` key: measured through a real request (REL-1413), that condition
|
|
554
|
+
* answered `500 INTERNAL_ERROR` and a dotted `_metadata.status` an empty page. A typed client is now
|
|
555
|
+
* refused at compile time, and every client before sending (`refuseMetadataConditions`).
|
|
556
|
+
*/
|
|
557
|
+
type WhereClause<T extends BaseContent = BaseContent> = DeclaresNoField<T> extends true ? {
|
|
558
|
+
[field: string]: unknown;
|
|
559
|
+
} : {
|
|
560
|
+
[K in Exclude<keyof T, '_metadata'>]?: WhereFieldCondition<T[K]>;
|
|
561
|
+
};
|
|
562
|
+
/**
|
|
563
|
+
* Query operators for advanced filtering
|
|
564
|
+
*/
|
|
565
|
+
interface WhereOperator<T = unknown> {
|
|
566
|
+
/** Equals */
|
|
567
|
+
$eq?: T;
|
|
568
|
+
/** Not equals */
|
|
569
|
+
$ne?: T;
|
|
570
|
+
/** Greater than */
|
|
571
|
+
$gt?: T;
|
|
572
|
+
/** Greater than or equal */
|
|
573
|
+
$gte?: T;
|
|
574
|
+
/** Less than */
|
|
575
|
+
$lt?: T;
|
|
576
|
+
/** Less than or equal */
|
|
577
|
+
$lte?: T;
|
|
578
|
+
/** In array */
|
|
579
|
+
$in?: T[];
|
|
580
|
+
/** Not in array */
|
|
581
|
+
$nin?: T[];
|
|
582
|
+
/** Contains (for strings) */
|
|
583
|
+
$contains?: string;
|
|
584
|
+
/** Starts with (for strings) */
|
|
585
|
+
$startsWith?: string;
|
|
586
|
+
/** Ends with (for strings) */
|
|
587
|
+
$endsWith?: string;
|
|
588
|
+
/** Regular expression match */
|
|
589
|
+
$regex?: string;
|
|
590
|
+
/** Exists (field is not null/undefined) */
|
|
591
|
+
$exists?: boolean;
|
|
592
|
+
}
|
|
593
|
+
/**
|
|
594
|
+
* Sort options for query results: by a key `T` declares, or by any field where it declares none.
|
|
595
|
+
*/
|
|
596
|
+
type SortOptions<T extends BaseContent = BaseContent> = {
|
|
597
|
+
[K in DeclaresNoField<T> extends true ? string : keyof T]?: 'asc' | 'desc';
|
|
598
|
+
};
|
|
599
|
+
/**
|
|
600
|
+
* Query options for content requests
|
|
601
|
+
*
|
|
602
|
+
* `F` is the shape whose fields a condition or a sort names, which is the read's own except for an
|
|
603
|
+
* asset: its read adds the `file` and `urls` blocks beside the fields, and a condition on those
|
|
604
|
+
* would name no field — and would make an unregistered asset client refuse every field it was given.
|
|
605
|
+
*/
|
|
606
|
+
interface QueryOptions<T extends BaseContent = BaseContent, F extends BaseContent = T> {
|
|
607
|
+
/** Conditions to filter by */
|
|
608
|
+
where?: WhereClause<F>;
|
|
609
|
+
/** Fields to include in response: any, where nothing declares them */
|
|
610
|
+
select?: (DeclaresNoField<F> extends true ? string : keyof T)[];
|
|
611
|
+
/** Related content to include */
|
|
612
|
+
include?: string[];
|
|
613
|
+
/** Sort options */
|
|
614
|
+
sort?: SortOptions<F>;
|
|
615
|
+
/** Limit number of results */
|
|
616
|
+
limit?: number;
|
|
617
|
+
/** Offset for pagination */
|
|
618
|
+
offset?: number;
|
|
619
|
+
/** Cursor for cursor-based pagination */
|
|
620
|
+
cursor?: string;
|
|
621
|
+
}
|
|
622
|
+
/**
|
|
623
|
+
* The fields a write sends: the ones the content type declares, as it declares them — or, where
|
|
624
|
+
* nothing declares any (`BaseContent`, the entry of a client with no schema registered), any field.
|
|
625
|
+
* The API validates the body against the deployed schema, the only place those names exist.
|
|
626
|
+
*
|
|
627
|
+
* `Omit<BaseContent, '_metadata'>` declares no key at all, so before this every write from an
|
|
628
|
+
* unregistered client — `relyio.content('post').create({ title })`, the SDK without codegen — was
|
|
629
|
+
* TS2353, and so was every `batch()` operation, whose `content` is untyped for every client.
|
|
630
|
+
*/
|
|
631
|
+
type WriteFields<T extends BaseContent> = DeclaresNoField<T> extends true ? {
|
|
632
|
+
[field: string]: unknown;
|
|
633
|
+
} : Omit<T, '_metadata'>;
|
|
634
|
+
/**
|
|
635
|
+
* Content creation input - excludes metadata fields that are managed by the system
|
|
636
|
+
*/
|
|
637
|
+
type CreateInput<T extends BaseContent> = WriteFields<T> & {
|
|
638
|
+
/** Optional metadata that can be set during creation */
|
|
639
|
+
_metadata?: {
|
|
640
|
+
/** Parent ID for hierarchical content */
|
|
641
|
+
parentId?: string | null;
|
|
642
|
+
/** Content locale */
|
|
643
|
+
locale?: string;
|
|
644
|
+
};
|
|
645
|
+
};
|
|
646
|
+
/**
|
|
647
|
+
* Content update input - excludes system-managed metadata fields
|
|
648
|
+
*/
|
|
649
|
+
type UpdateInput<T extends BaseContent> = Partial<WriteFields<T>> & {
|
|
650
|
+
/** Optional metadata that can be updated */
|
|
651
|
+
_metadata?: {
|
|
652
|
+
/** Parent ID for hierarchical content */
|
|
653
|
+
parentId?: string | null;
|
|
654
|
+
/** Content locale */
|
|
655
|
+
locale?: string;
|
|
656
|
+
};
|
|
657
|
+
};
|
|
658
|
+
/**
|
|
659
|
+
* What the entry list serves — **read from the committed document**, not
|
|
660
|
+
* written down here.
|
|
661
|
+
*
|
|
662
|
+
* It used to be a hand-written `{ items, total?, hasMore, nextCursor }`, and
|
|
663
|
+
* every field of it was wrong after API2: the route is the cursor dialect, so
|
|
664
|
+
* the body is `{ data, page: { nextCursor, hasMore } }` and there is no total
|
|
665
|
+
* at all. `execute()` therefore resolved `result.items` — `undefined` — for
|
|
666
|
+
* every caller of the SDK's most-used method, and nothing could see it because
|
|
667
|
+
* the type said otherwise.
|
|
668
|
+
*
|
|
669
|
+
* Deriving it means the next dialect change is a compile error in user code
|
|
670
|
+
* rather than an `undefined` at runtime (`INV-API-CLIENT-IS-DERIVED`).
|
|
671
|
+
*/
|
|
672
|
+
type ContentQueryResult<T extends BaseContent = BaseContent> = Wire<'/v1/{contentType}', 'get', T>;
|
|
673
|
+
/**
|
|
674
|
+
* A singleton as `GET /v1/singletons/{key}` serves it: its fields at the top level and
|
|
675
|
+
* `_metadata`, the document's `Singleton` rather than a second declaration of it.
|
|
676
|
+
*/
|
|
677
|
+
type SingletonContent = components['schemas']['Singleton'];
|
|
678
|
+
/**
|
|
679
|
+
* Custom query definition
|
|
680
|
+
*/
|
|
681
|
+
interface CustomQuery<TVariables = unknown, TResponse = unknown> {
|
|
682
|
+
/** Query name */
|
|
683
|
+
name: string;
|
|
684
|
+
/** Query variables */
|
|
685
|
+
variables?: TVariables;
|
|
686
|
+
/** Expected response type */
|
|
687
|
+
response?: TResponse;
|
|
688
|
+
}
|
|
689
|
+
/**
|
|
690
|
+
* Content type schema information
|
|
691
|
+
*/
|
|
692
|
+
interface ContentTypeSchema {
|
|
693
|
+
/** Content type name */
|
|
694
|
+
name: string;
|
|
695
|
+
/** Display name */
|
|
696
|
+
displayName: string;
|
|
697
|
+
/** Description */
|
|
698
|
+
description?: string;
|
|
699
|
+
/** Field definitions */
|
|
700
|
+
fields: FieldDefinition[];
|
|
701
|
+
/** Whether this content type is hierarchical */
|
|
702
|
+
hierarchical?: boolean;
|
|
703
|
+
/** Preview configuration */
|
|
704
|
+
preview?: {
|
|
705
|
+
select?: Record<string, string>;
|
|
706
|
+
prepare?: (_selection: Record<string, unknown>) => Record<string, unknown>;
|
|
707
|
+
};
|
|
708
|
+
}
|
|
709
|
+
/**
|
|
710
|
+
* Field definition for content types
|
|
711
|
+
*/
|
|
712
|
+
interface FieldDefinition {
|
|
713
|
+
/** Field name */
|
|
714
|
+
name: string;
|
|
715
|
+
/** Field type */
|
|
716
|
+
type: FieldTypeName;
|
|
717
|
+
/** Display title */
|
|
718
|
+
title?: string;
|
|
719
|
+
/** Field description */
|
|
720
|
+
description?: string;
|
|
721
|
+
/** Whether field is required */
|
|
722
|
+
required?: boolean;
|
|
723
|
+
/** Default value */
|
|
724
|
+
default?: unknown;
|
|
725
|
+
/** Validation rules */
|
|
726
|
+
validation?: ValidationRule[];
|
|
727
|
+
/** Field options */
|
|
728
|
+
options?: FieldOptions;
|
|
729
|
+
}
|
|
730
|
+
/**
|
|
731
|
+
* Supported field type names (string identifiers)
|
|
732
|
+
*/
|
|
733
|
+
type FieldTypeName = 'string' | 'number' | 'boolean' | 'date' | 'datetime' | 'text' | 'markdown' | 'richtext' | 'slug' | 'url' | 'email' | 'image' | 'file' | 'reference' | 'array' | 'object' | 'geopoint' | 'color';
|
|
734
|
+
/**
|
|
735
|
+
* Field validation rules
|
|
736
|
+
*/
|
|
737
|
+
interface ValidationRule {
|
|
738
|
+
/** Rule type */
|
|
739
|
+
type: 'required' | 'min' | 'max' | 'pattern' | 'custom';
|
|
740
|
+
/** Rule value */
|
|
741
|
+
value?: unknown;
|
|
742
|
+
/** Error message */
|
|
743
|
+
message?: string;
|
|
744
|
+
}
|
|
745
|
+
/**
|
|
746
|
+
* Field-specific options
|
|
747
|
+
*/
|
|
748
|
+
interface FieldOptions {
|
|
749
|
+
/** For array fields */
|
|
750
|
+
of?: FieldDefinition;
|
|
751
|
+
/** For reference fields */
|
|
752
|
+
to?: string | string[];
|
|
753
|
+
/** For string fields */
|
|
754
|
+
list?: string[];
|
|
755
|
+
/** For number fields */
|
|
756
|
+
min?: number;
|
|
757
|
+
max?: number;
|
|
758
|
+
/** For image/file fields */
|
|
759
|
+
accept?: string[];
|
|
760
|
+
/** For rich text fields */
|
|
761
|
+
marks?: string[];
|
|
762
|
+
blocks?: string[];
|
|
763
|
+
}
|
|
764
|
+
/**
|
|
765
|
+
* Real-time event types
|
|
766
|
+
*/
|
|
767
|
+
type RealtimeEventType = 'created' | 'updated' | 'deleted' | 'published' | 'unpublished';
|
|
768
|
+
/**
|
|
769
|
+
* Real-time event payload
|
|
770
|
+
*/
|
|
771
|
+
interface RealtimeEvent<T extends BaseContent = BaseContent> {
|
|
772
|
+
/** Event type */
|
|
773
|
+
type: RealtimeEventType;
|
|
774
|
+
/** Content type */
|
|
775
|
+
contentType: string;
|
|
776
|
+
/** Content item (for created/updated events) */
|
|
777
|
+
item?: T;
|
|
778
|
+
/** Content ID (for deleted events) */
|
|
779
|
+
id?: string;
|
|
780
|
+
/** Timestamp */
|
|
781
|
+
timestamp: number;
|
|
782
|
+
/** Event metadata */
|
|
783
|
+
metadata?: Record<string, unknown>;
|
|
784
|
+
}
|
|
785
|
+
/**
|
|
786
|
+
* Subscription options
|
|
787
|
+
*/
|
|
788
|
+
interface SubscriptionOptions<T extends BaseContent = BaseContent> {
|
|
789
|
+
/** Content type to subscribe to */
|
|
790
|
+
contentType: string;
|
|
791
|
+
/** Filter conditions */
|
|
792
|
+
where?: WhereClause<T>;
|
|
793
|
+
/** Events to listen for */
|
|
794
|
+
events?: RealtimeEventType[];
|
|
795
|
+
/** Subscription metadata */
|
|
796
|
+
metadata?: Record<string, unknown>;
|
|
797
|
+
}
|
|
798
|
+
/**
|
|
799
|
+
* Performance metrics
|
|
800
|
+
*/
|
|
801
|
+
interface PerformanceMetrics {
|
|
802
|
+
/** Request duration in milliseconds */
|
|
803
|
+
duration: number;
|
|
804
|
+
/** Request timestamp */
|
|
805
|
+
timestamp: number;
|
|
806
|
+
/** Request URL */
|
|
807
|
+
url: string;
|
|
808
|
+
/** HTTP method */
|
|
809
|
+
method: string;
|
|
810
|
+
/** Response status code */
|
|
811
|
+
statusCode: number;
|
|
812
|
+
/** Whether response came from cache */
|
|
813
|
+
fromCache: boolean;
|
|
814
|
+
/** Cache hit ratio */
|
|
815
|
+
cacheHitRatio?: number;
|
|
816
|
+
}
|
|
817
|
+
|
|
818
|
+
export { type ApiPath as A, type BaseContent as B, type CreateInput as C, type DebugConfig as D, type Environment as E, type FieldTypeName as F, ValidationError as G, type MergedRelyIOConfig as H, InvalidPayloadError as I, type PathParamsOf as J, type FieldDefinition as K, type LogLevel as L, type MatchRouteOptions as M, type PerformanceMetrics as P, type QueryOptions as Q, type RelyIOConfig as R, SDKError as S, type UpdateInput as U, type ValidationRule as V, type Wire as W, type RelyIOConfigUpdate as a, type ApiMethod as b, type ApiSuccessBody as c, type RouteMatch as d, type WhereScalar as e, type WhereFieldCondition as f, type WhereClause as g, type WhereOperator as h, type SortOptions as i, type ContentQueryResult as j, type SingletonContent as k, type CustomQuery as l, matchRoute as m, type ContentTypeSchema as n, type FieldOptions as o, type RealtimeEventType as p, type RealtimeEvent as q, type SubscriptionOptions as r, type CacheConfig as s, type RealtimeConfig as t, type RequestConfig as u, type ApiResponse as v, type RetryConfig as w, type CacheStrategy as x, type ValidationViolation as y, BaseValidationError as z };
|