@relyio/sdk 0.18.0 → 0.20.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 +30 -0
- package/dist/automations/index.cjs +18 -17
- package/dist/automations/index.d.cts +2 -1307
- package/dist/automations/index.d.ts +2 -1307
- package/dist/automations/index.js +3 -2
- package/dist/{chunk-ZAMAR3X2.cjs → chunk-5F3YYPLZ.cjs} +11249 -10771
- package/dist/{chunk-ZO6QDYJG.cjs → chunk-5WGMIQBH.cjs} +2 -2
- package/dist/{chunk-LSI26ZN2.cjs → chunk-DBYMG6MQ.cjs} +5 -5
- package/dist/{chunk-W2A4OTCI.js → chunk-EQUZNVAR.js} +2 -2
- package/dist/{chunk-IFKF72VJ.cjs → chunk-K66MBRI4.cjs} +147 -71
- package/dist/{chunk-ZHTJ7GXH.js → chunk-L42C4G2S.js} +106 -31
- package/dist/{chunk-EDE2H37D.js → chunk-OUR7OUUZ.js} +174 -12
- package/dist/{chunk-W7JFHSFM.js → chunk-PZCDJFMA.js} +11249 -10772
- package/dist/{chunk-SHSK7UUY.cjs → chunk-UO2QRYKV.cjs} +177 -11
- package/dist/{chunk-R6E4HE7R.js → chunk-W2GRVXDG.js} +3 -3
- package/dist/client/index.cjs +5 -5
- package/dist/client/index.d.cts +4 -4
- package/dist/client/index.d.ts +4 -4
- package/dist/client/index.js +3 -3
- package/dist/{define-content-type-NEQDrvdq.d.cts → define-content-type-C2HnCYYX.d.cts} +1324 -156
- package/dist/{define-content-type-fb-WCD5e.d.ts → define-content-type-CVLUusfx.d.ts} +1324 -156
- package/dist/dev-handshake/index.cjs +8 -8
- package/dist/dev-handshake/index.d.cts +1 -1
- package/dist/dev-handshake/index.d.ts +1 -1
- package/dist/dev-handshake/index.js +1 -1
- package/dist/extensions/index.cjs +26 -6
- package/dist/extensions/index.d.cts +3 -791
- package/dist/extensions/index.d.ts +3 -791
- package/dist/extensions/index.js +24 -4
- package/dist/functions/index.cjs +7 -4
- package/dist/functions/index.d.cts +2 -4
- package/dist/functions/index.d.ts +2 -4
- package/dist/functions/index.js +5 -2
- package/dist/{index-DwHcL6x5.d.cts → index-BLVNMYUU.d.cts} +142 -79
- package/dist/{index-C4LqrNd1.d.ts → index-BY1xU93u.d.ts} +1 -1
- package/dist/{index-Dnmt_UOo.d.cts → index-Cmup6HJY.d.cts} +1 -1
- package/dist/index-D1fiaQlK.d.ts +2284 -0
- package/dist/index-DImzg3ew.d.cts +2284 -0
- package/dist/{index-M3luH06A.d.ts → index-IjSyBr8f.d.ts} +142 -79
- package/dist/index.cjs +89 -85
- package/dist/index.d.cts +64 -15
- package/dist/index.d.ts +64 -15
- package/dist/index.js +5 -5
- package/dist/preview/index.cjs +3 -3
- package/dist/preview/index.js +1 -1
- package/dist/rich-text/index.d.cts +2 -2
- package/dist/rich-text/index.d.ts +2 -2
- package/dist/schema/index.cjs +41 -40
- package/dist/schema/index.d.cts +948 -118
- package/dist/schema/index.d.ts +948 -118
- package/dist/schema/index.js +2 -1
- package/dist/types/index.d.cts +1 -1
- package/dist/types/index.d.ts +1 -1
- package/package.json +5 -5
- package/dist/define-schema-DZhtvLiK.d.cts +0 -846
- package/dist/define-schema-Dlwh6l_J.d.ts +0 -846
- package/dist/register-B53i97A9.d.ts +0 -677
- package/dist/register-BYCs0FpF.d.cts +0 -677
|
@@ -1,677 +0,0 @@
|
|
|
1
|
-
import { cF as paths, cG as RetryConfig, cH as MergedRelyIOConfig, cI as ApiResponse, cw as components, b as FileUpload } from './define-content-type-fb-WCD5e.js';
|
|
2
|
-
import { g as SchemaDefinition } from './define-schema-Dlwh6l_J.js';
|
|
3
|
-
|
|
4
|
-
/**
|
|
5
|
-
* Where the SDK's transport gets its truth.
|
|
6
|
-
*
|
|
7
|
-
* Every route this client calls is named by its **path template as the
|
|
8
|
-
* committed document spells it**, and the body type it expects is read out of
|
|
9
|
-
* that document rather than written down here. `api-schema.ts` beside this file
|
|
10
|
-
* is generated by `pnpm api:types` from `apps/services/api/openapi-spec.json`,
|
|
11
|
-
* which is itself generated from that service's `RouteContract`s
|
|
12
|
-
* (`INV-API-DOCUMENT-IS-DERIVED`). So the chain is: one declaration in
|
|
13
|
-
* `routes.ts` → the serializer that produces the body → the document → these
|
|
14
|
-
* types → the method that reads it.
|
|
15
|
-
*
|
|
16
|
-
* That chain is the answer to the question this phase exists to close. Before
|
|
17
|
-
* it, six SDK methods read a key the server does not send; each had no caller
|
|
18
|
-
* and no test, so nothing could see them. Now:
|
|
19
|
-
*
|
|
20
|
-
* - A route that changes **kind** — `collection` to `object`, say — changes
|
|
21
|
-
* whether the document's 200 body carries `page` and whether `data` is an
|
|
22
|
-
* array. `Wire<…>` follows, `readPayload` follows, and the method's declared
|
|
23
|
-
* return type stops matching. `tsc` names the method.
|
|
24
|
-
* - A route that changes **path** or is deleted stops being a `keyof paths`, so
|
|
25
|
-
* the call site fails on the literal.
|
|
26
|
-
* - A route that changes **dialect** changes `page` from `CursorPage` to
|
|
27
|
-
* `OffsetPage`, and anything reading `nextCursor` fails.
|
|
28
|
-
*
|
|
29
|
-
* `/v1` lives in these templates because it lives in the document
|
|
30
|
-
* (`INV-API-VERSION-IS-DECIDED-ONCE`). Before this phase the SDK's base URL was
|
|
31
|
-
* `https://api.relyio.com` and its paths were `/{contentType}`, so every
|
|
32
|
-
* versioned request went to a path the router does not serve.
|
|
33
|
-
*
|
|
34
|
-
* Spec: `devtools/docs/guide/engineering/api-contract-spec.md`,
|
|
35
|
-
* `INV-API-CLIENT-IS-DERIVED`.
|
|
36
|
-
*/
|
|
37
|
-
|
|
38
|
-
/** Every path the committed document declares. */
|
|
39
|
-
type ApiPath = keyof paths;
|
|
40
|
-
type Verb = 'get' | 'post' | 'put' | 'patch' | 'delete';
|
|
41
|
-
/** The verbs the document declares on this path — not the seven HTTP has. */
|
|
42
|
-
type ApiMethod<P extends ApiPath> = {
|
|
43
|
-
[M in Verb]: M extends keyof paths[P] ? paths[P][M] extends {
|
|
44
|
-
responses: unknown;
|
|
45
|
-
} ? M : never : never;
|
|
46
|
-
}[Verb];
|
|
47
|
-
type Operation<P extends ApiPath, M extends ApiMethod<P>> = M extends keyof paths[P] ? paths[P][M] : never;
|
|
48
|
-
type JsonBody<R> = R extends {
|
|
49
|
-
content: {
|
|
50
|
-
'application/json': infer B;
|
|
51
|
-
};
|
|
52
|
-
} ? B : never;
|
|
53
|
-
/**
|
|
54
|
-
* The success body the document declares, preferring 200 where a route serves
|
|
55
|
-
* both. A route with neither — `kind: 'empty'`, which serves 204 — resolves to
|
|
56
|
-
* `never`, and `readPayload` cannot be called on it.
|
|
57
|
-
*/
|
|
58
|
-
type ApiSuccessBody<P extends ApiPath, M extends ApiMethod<P>> = Operation<P, M> extends {
|
|
59
|
-
responses: infer R;
|
|
60
|
-
} ? 200 extends keyof R ? JsonBody<R[200]> : 201 extends keyof R ? JsonBody<R[201]> : never : never;
|
|
61
|
-
/**
|
|
62
|
-
* The document's success body with the payload typed as the caller's `T`.
|
|
63
|
-
*
|
|
64
|
-
* The array arm is tested **first** on purpose: `{ data: unknown[] }` also
|
|
65
|
-
* satisfies `{ data: unknown }`, so the object arm would swallow every
|
|
66
|
-
* collection and hand a list route's caller a single entity — which is the
|
|
67
|
-
* exact defect class (`ContentClient.children` reading `.data.items` from an
|
|
68
|
-
* object route) that this phase makes unrepresentable.
|
|
69
|
-
*/
|
|
70
|
-
type Wire<P extends ApiPath, M extends ApiMethod<P>, T> = ApiSuccessBody<P, M> extends {
|
|
71
|
-
data: unknown[];
|
|
72
|
-
} ? Omit<ApiSuccessBody<P, M>, 'data'> & {
|
|
73
|
-
data: T[];
|
|
74
|
-
} : ApiSuccessBody<P, M> extends {
|
|
75
|
-
data: unknown;
|
|
76
|
-
} ? Omit<ApiSuccessBody<P, M>, 'data'> & {
|
|
77
|
-
data: T;
|
|
78
|
-
} : ApiSuccessBody<P, M>;
|
|
79
|
-
/**
|
|
80
|
-
* The path parameters the document declares for this operation, or `never`
|
|
81
|
-
* where it declares none.
|
|
82
|
-
*
|
|
83
|
-
* `never` rather than `{}`: a route with no segments must **forbid** a `path`
|
|
84
|
-
* argument, not merely tolerate an empty one, and `keyof Record<string, never>`
|
|
85
|
-
* is `string`, so an empty-object encoding cannot be told apart from a real
|
|
86
|
-
* one at the type level.
|
|
87
|
-
*/
|
|
88
|
-
type PathParamsOf<P extends ApiPath, M extends ApiMethod<P>> = Operation<P, M> extends {
|
|
89
|
-
parameters: {
|
|
90
|
-
path?: infer X;
|
|
91
|
-
};
|
|
92
|
-
} ? [X] extends [undefined] ? never : NonNullable<X> : never;
|
|
93
|
-
/**
|
|
94
|
-
* The query parameters the document declares for this operation, or `never` where it declares
|
|
95
|
-
* none (REL-687).
|
|
96
|
-
*
|
|
97
|
-
* The service declares them beside each route — a query DTO's whitelisted properties, the names a
|
|
98
|
-
* handler reads, `?_fresh` on a read the edge keeps — and `apps/services/api`'s
|
|
99
|
-
* `contract/query-parameters.spec.ts` holds the document to what each handler reads. So a name the
|
|
100
|
-
* API stops reading stops compiling here, and a name it refuses — `offset` on the entry list, which
|
|
101
|
-
* the SDK once sent on every `.offset()` and the API answered with a 400 — never compiled at all.
|
|
102
|
-
*
|
|
103
|
-
* `never` for the same reason {@link PathParamsOf} uses it: a route with no query must forbid one.
|
|
104
|
-
*/
|
|
105
|
-
type QueryOf<P extends ApiPath, M extends ApiMethod<P>> = Operation<P, M> extends {
|
|
106
|
-
parameters: {
|
|
107
|
-
query?: infer Q;
|
|
108
|
-
};
|
|
109
|
-
} ? [Q] extends [undefined] ? never : NonNullable<Q> : never;
|
|
110
|
-
/** What {@link matchRoute} recovers from a URL the client emitted. */
|
|
111
|
-
interface RouteMatch {
|
|
112
|
-
/** The document's path template — the value a tag translator switches on. */
|
|
113
|
-
path: ApiPath;
|
|
114
|
-
/** The template's `{name}` segments, decoded: the inverse of {@link fillPath}. */
|
|
115
|
-
params: Record<string, string>;
|
|
116
|
-
/** The query string; a key that appears more than once comes back as an array. */
|
|
117
|
-
query: Record<string, string | string[]>;
|
|
118
|
-
}
|
|
119
|
-
interface MatchRouteOptions {
|
|
120
|
-
/**
|
|
121
|
-
* The client's `baseUrl`, when it carries a path prefix of its own
|
|
122
|
-
* (`https://example.com/relyio`). Its path is removed before the template is
|
|
123
|
-
* matched. Unneeded for the default `https://api.relyio.com`.
|
|
124
|
-
*/
|
|
125
|
-
baseUrl?: string;
|
|
126
|
-
}
|
|
127
|
-
/**
|
|
128
|
-
* Recover the route a URL names: the inverse of {@link fillPath}, over the same
|
|
129
|
-
* committed document (`INV-API-CLIENT-IS-DERIVED`).
|
|
130
|
-
*
|
|
131
|
-
* `matchRoute(url).path` is an `ApiPath`, so anything that keys behaviour on a
|
|
132
|
-
* request — a cache-tag translator, say — switches on the document's templates
|
|
133
|
-
* and never on a route table of its own.
|
|
134
|
-
*
|
|
135
|
-
* Returns `null` for a URL no declared template matches — a presigned storage
|
|
136
|
-
* URL, or a path outside `baseUrl` — rather than a guess.
|
|
137
|
-
*/
|
|
138
|
-
declare function matchRoute(url: string | URL, options?: MatchRouteOptions): RouteMatch | null;
|
|
139
|
-
|
|
140
|
-
/**
|
|
141
|
-
* The part of a request a retry decision is allowed to look at.
|
|
142
|
-
*
|
|
143
|
-
* `method` is typed as a plain `string` rather than as `RequestConfig['method']`
|
|
144
|
-
* on purpose. The five verbs that union names are the five the typed transport
|
|
145
|
-
* can express today; the question "may this be sent twice?" is a fact about
|
|
146
|
-
* HTTP, and an unrecognised verb has to reach the conservative arm rather than
|
|
147
|
-
* be unrepresentable.
|
|
148
|
-
*/
|
|
149
|
-
interface RetryableRequest {
|
|
150
|
-
method: string;
|
|
151
|
-
headers?: Record<string, string>;
|
|
152
|
-
}
|
|
153
|
-
/**
|
|
154
|
-
* Whether a failed request may be sent again, and after how long.
|
|
155
|
-
*
|
|
156
|
-
* The two halves are separate because they fail separately. The incumbent loop
|
|
157
|
-
* had neither: it asked `error.retryable` and nothing else, so a `POST` that met
|
|
158
|
-
* a 500 was sent again twice — and `SERVICE_INDETERMINATE`, whose whole meaning
|
|
159
|
-
* is "the service could not decide", is marked retryable, which means the write
|
|
160
|
-
* may already have landed. Meanwhile the `Retry-After` the service sent was
|
|
161
|
-
* collected by the transport and read by nobody, so a 429 whose window is
|
|
162
|
-
* measured in minutes was retried after an exponential guess capped at ten
|
|
163
|
-
* seconds.
|
|
164
|
-
*
|
|
165
|
-
* `error` is `Error`, not `SDKError`, because that is what the loop actually
|
|
166
|
-
* holds: a `TypeError: fetch failed` is only mapped to an `SDKError` **after**
|
|
167
|
-
* the loop gives up, so a policy typed to `SDKError` could not see the transport
|
|
168
|
-
* failures it most needs to retry.
|
|
169
|
-
*/
|
|
170
|
-
interface RetryPolicy {
|
|
171
|
-
/**
|
|
172
|
-
* Never replays a request that could apply twice: a write is retried only
|
|
173
|
-
* when it carries `Idempotency-Key`, or the verb is idempotent.
|
|
174
|
-
*
|
|
175
|
-
* @param attempt - How many attempts have been made, 1 for the first.
|
|
176
|
-
*/
|
|
177
|
-
shouldRetry(attempt: number, error: Error, request: RetryableRequest): boolean;
|
|
178
|
-
/** Honours `Retry-After` / `RateLimitError.resetAt` over exponential backoff. */
|
|
179
|
-
delayFor(attempt: number, error: Error): number;
|
|
180
|
-
}
|
|
181
|
-
/**
|
|
182
|
-
* Whether sending this request a second time can apply it a second time.
|
|
183
|
-
*
|
|
184
|
-
* Two ways to be safe, and they are genuinely different questions: the verb may
|
|
185
|
-
* be idempotent by definition, or the request may carry an `Idempotency-Key`
|
|
186
|
-
* that makes the service answer a replay from the original outcome rather than
|
|
187
|
-
* applying it again.
|
|
188
|
-
*/
|
|
189
|
-
declare function isReplaySafe(request: RetryableRequest): boolean;
|
|
190
|
-
/**
|
|
191
|
-
* The policy the SDK ships with.
|
|
192
|
-
*
|
|
193
|
-
* Construction takes the same `RetryConfig` the SDK has always exported; what
|
|
194
|
-
* it no longer takes is a `retryCondition`, because a caller-supplied predicate
|
|
195
|
-
* over `(error, attempt)` structurally **cannot** see the request and so cannot
|
|
196
|
-
* express the rule this class exists for.
|
|
197
|
-
*/
|
|
198
|
-
declare class DefaultRetryPolicy implements RetryPolicy {
|
|
199
|
-
private readonly config;
|
|
200
|
-
constructor(config: RetryConfig);
|
|
201
|
-
shouldRetry(attempt: number, error: Error, request: RetryableRequest): boolean;
|
|
202
|
-
delayFor(attempt: number, error: Error): number;
|
|
203
|
-
}
|
|
204
|
-
/**
|
|
205
|
-
* The retry policy a client sends with, at `maxAttempts` attempts (the first included): the SDK's
|
|
206
|
-
* own, from `retryAttempts`, and the CLI's (REL-721).
|
|
207
|
-
*
|
|
208
|
-
* The backoff numbers are stated here once. The CLI ran a second policy beside this one —
|
|
209
|
-
* `axios-retry` with its own delays and its own reading of a refusal, which retried by numeric
|
|
210
|
-
* status — so a refusal one client gave up on, the other sent again. Now both ask this function, and
|
|
211
|
-
* a change to when a request is replayed reaches both.
|
|
212
|
-
*/
|
|
213
|
-
declare function retryPolicyFor(maxAttempts: number): RetryPolicy;
|
|
214
|
-
|
|
215
|
-
/**
|
|
216
|
-
* What a call site supplies beside the route it named.
|
|
217
|
-
*
|
|
218
|
-
* `path` is required exactly when the document's template has segments, and
|
|
219
|
-
* forbidden when it does not — so a call that forgot to scope a request, or one
|
|
220
|
-
* that scoped a request the route does not scope, is a compile error rather
|
|
221
|
-
* than a URL with a literal `{id}` in it.
|
|
222
|
-
*/
|
|
223
|
-
type SendOptions<P extends ApiPath, M extends ApiMethod<P>> = {
|
|
224
|
-
/**
|
|
225
|
-
* Query string values: exactly the names the document declares for this route, at the types it
|
|
226
|
-
* declares them (`QueryOf`, REL-687). An undeclared name is a compile error, not a 400.
|
|
227
|
-
*/
|
|
228
|
-
query?: QueryOf<P, M>;
|
|
229
|
-
body?: unknown;
|
|
230
|
-
/** Request headers this call adds, e.g. `If-Match` / `Idempotency-Key` (API4). */
|
|
231
|
-
headers?: Record<string, string>;
|
|
232
|
-
signal?: AbortSignal;
|
|
233
|
-
timeout?: number;
|
|
234
|
-
} & ([PathParamsOf<P, M>] extends [never] ? {
|
|
235
|
-
path?: never;
|
|
236
|
-
} : {
|
|
237
|
-
path: PathParamsOf<P, M>;
|
|
238
|
-
});
|
|
239
|
-
/**
|
|
240
|
-
* Core HTTP client for the RelyIO SDK.
|
|
241
|
-
*
|
|
242
|
-
* `send` is **the** typed entry: it names a route by the path template the
|
|
243
|
-
* committed document declares, and returns that operation's body type with the
|
|
244
|
-
* payload typed as `T`. `readPayload` from `@relyio/api-contract/client` is
|
|
245
|
-
* then the one unwrap — there is no second spelling and no runtime test of
|
|
246
|
-
* which envelope arrived (`INV-API-CLIENT-IS-DERIVED`).
|
|
247
|
-
*
|
|
248
|
-
* What the class makes public is what something outside it calls: `send` and `fetchPayload` are
|
|
249
|
-
* the requests; `configure`, `withHeaders` and `setApiKey` are the owning client's (`updateConfig()`,
|
|
250
|
-
* `with()`, `setApiKey()`). There are no verb helpers, no header or base-URL accessors and no
|
|
251
|
-
* untyped `request` (REL-717): a route is named by its document path, or it is not called.
|
|
252
|
-
*/
|
|
253
|
-
declare class HttpClient {
|
|
254
|
-
/** What every request reads off the configuration, derived by {@link configure} and nowhere else. */
|
|
255
|
-
protected connection: Connection;
|
|
256
|
-
/**
|
|
257
|
-
* Set on a view {@link withHeaders} made: the client every request is sent through, and the
|
|
258
|
-
* headers laid beneath each request's own. A view keeps no connection of its own that matters —
|
|
259
|
-
* everything it is asked to change, it changes on that client.
|
|
260
|
-
*/
|
|
261
|
-
protected view?: {
|
|
262
|
-
readonly client: ControlledHttpClient;
|
|
263
|
-
readonly headers: Readonly<Record<string, string>>;
|
|
264
|
-
};
|
|
265
|
-
constructor(config: MergedRelyIOConfig);
|
|
266
|
-
protected announce(): void;
|
|
267
|
-
/**
|
|
268
|
-
* Send bytes to an address that is not a RelyIO route — an asset upload's presigned `PUT` and its
|
|
269
|
-
* multipart parts — through the client's configured `fetch`, so a wrapper that records, proxies or
|
|
270
|
-
* forbids network access sees every request this client makes (REL-2671). Nothing else is added: no
|
|
271
|
-
* key, no retry, no timeout, because the address belongs to the storage service, not to RelyIO.
|
|
272
|
-
*
|
|
273
|
-
* Static so the instances a customer holds stay `send` and `fetchPayload` (REL-3107). The `fetch`
|
|
274
|
-
* is read per call, from the client a view sends through, like the requests are.
|
|
275
|
-
*/
|
|
276
|
-
static transfer(client: HttpClient, url: string, init: RequestInit): Promise<Response>;
|
|
277
|
-
/**
|
|
278
|
-
* Call a route the committed document declares, and get back the body that
|
|
279
|
-
* document says it serves.
|
|
280
|
-
*/
|
|
281
|
-
send<P extends ApiPath, M extends ApiMethod<P>, T = unknown>(path: P, method: M, options: SendOptions<P, M>): Promise<ApiResponse<Wire<P, M, T>>>;
|
|
282
|
-
/**
|
|
283
|
-
* Call a route and return its payload — `send` plus the one unwrap, which is
|
|
284
|
-
* what all but a handful of call sites want.
|
|
285
|
-
*/
|
|
286
|
-
fetchPayload<P extends ApiPath, M extends ApiMethod<P>, T = unknown>(path: P, method: M, options: SendOptions<P, M>): Promise<Wire<P, M, T> extends {
|
|
287
|
-
data: unknown;
|
|
288
|
-
} ? Wire<P, M, T>['data'] : never>;
|
|
289
|
-
/**
|
|
290
|
-
* Make an HTTP request, retrying only where a retry is safe.
|
|
291
|
-
*
|
|
292
|
-
* The headers are merged **once**, before the first attempt, and every attempt
|
|
293
|
-
* sends that same object. That is what makes a minted `Idempotency-Key` stable
|
|
294
|
-
* across the retries of one logical write: the service sees one key and
|
|
295
|
-
* answers the replay from the original outcome instead of applying it twice.
|
|
296
|
-
*
|
|
297
|
-
* The loop always makes one attempt. The incumbent's `while (attempt <
|
|
298
|
-
* maxAttempts)` made none at `retryAttempts: 0` — a value the config validator
|
|
299
|
-
* then accepted, and now refuses — and then dereferenced an unassigned
|
|
300
|
-
* `lastError`. `retryAttempts` is the most attempts, the first included; an
|
|
301
|
-
* `HttpClient` constructed directly is not validated, so the first attempt
|
|
302
|
-
* stays unconditional.
|
|
303
|
-
*
|
|
304
|
-
* A request that would carry no `X-API-Key` makes none: it rejects with
|
|
305
|
-
* `MissingApiKeyError` before `fetch` is called, is not retried, and names
|
|
306
|
-
* `apiKey`. This is where a client built without a key learns it — the
|
|
307
|
-
* constructor accepts one so that a module-scope client can be evaluated by a
|
|
308
|
-
* build that holds no key (REL-1120) — and so does one whose `setApiKey` was
|
|
309
|
-
* given none.
|
|
310
|
-
*/
|
|
311
|
-
private request;
|
|
312
|
-
/**
|
|
313
|
-
* One attempt.
|
|
314
|
-
*
|
|
315
|
-
* The caller's `signal` and the timeout both end it, through one controller.
|
|
316
|
-
* The incumbent handed `fetch` the caller's signal **instead of** its own, so a
|
|
317
|
-
* request that passed a signal had no timeout at all — and a caller abort then
|
|
318
|
-
* surfaced as `Request timed out`. `timedOut` is what tells the two apart: a
|
|
319
|
-
* timeout is a `TimeoutError`, a caller abort is the platform's `AbortError`,
|
|
320
|
-
* rethrown as it came.
|
|
321
|
-
*/
|
|
322
|
-
private makeRequest;
|
|
323
|
-
/**
|
|
324
|
-
* The headers without those a browser controls itself, when this is a browser; unchanged when it
|
|
325
|
-
* is not.
|
|
326
|
-
*
|
|
327
|
-
* A name is matched without regard to case (RFC 9110 §5.1), because a browser refuses `Origin`
|
|
328
|
-
* whatever case a page spells it in. This matched only the all-lowercase and all-uppercase
|
|
329
|
-
* spellings, so `Origin` and `User-Agent` — how people write them — reached `fetch` (REL-1409).
|
|
330
|
-
*/
|
|
331
|
-
private filterHeadersForBrowser;
|
|
332
|
-
/**
|
|
333
|
-
* Prepare request body based on content type and environment
|
|
334
|
-
*/
|
|
335
|
-
private prepareRequestBody;
|
|
336
|
-
private parseResponse;
|
|
337
|
-
private resolveUrl;
|
|
338
|
-
private mergeRequestConfig;
|
|
339
|
-
private buildQueryString;
|
|
340
|
-
private headersToObject;
|
|
341
|
-
private log;
|
|
342
|
-
}
|
|
343
|
-
/**
|
|
344
|
-
* The client the SDK's own factory holds: {@link HttpClient} plus the three things only the owning
|
|
345
|
-
* client does to it — `configure` (`updateConfig()`), `withHeaders` (`with()`) and `setApiKey`
|
|
346
|
-
* (`setApiKey()`). It is not exported from the package, so a customer holding the client from
|
|
347
|
-
* `getHttpClient()` sees `send` and `fetchPayload` and nothing else (REL-3107).
|
|
348
|
-
*/
|
|
349
|
-
declare class ControlledHttpClient extends HttpClient {
|
|
350
|
-
/**
|
|
351
|
-
* A view of this client that sends every request through it with `headers` added — beneath the
|
|
352
|
-
* request's own, over this client's — and changes nothing else: its key, base URL, timeout,
|
|
353
|
-
* retries and `fetch` are this client's as they are when each request is sent, so a later
|
|
354
|
-
* `configure()` or `setApiKey()` on this client reaches the view, and one on the view reaches
|
|
355
|
-
* this client. `client.with({ dataset })` is one (REL-2417).
|
|
356
|
-
*/
|
|
357
|
-
withHeaders(headers: Readonly<Record<string, string>>): ControlledHttpClient;
|
|
358
|
-
/**
|
|
359
|
-
* Run on `config` from now on: every later request — whoever holds this client — is sent to its
|
|
360
|
-
* `baseUrl`, with its headers, key and dataset, its timeout and retries, through its `fetch`, and
|
|
361
|
-
* logged by its `debug` options.
|
|
362
|
-
*
|
|
363
|
-
* The client's own `updateConfig()` calls this rather than building a second client, so a
|
|
364
|
-
* `content()` or other sub-client taken before the update sends through the update too. Anything
|
|
365
|
-
* set on this client since it was last configured — `setApiKey()` — is replaced by what `config`
|
|
366
|
-
* says.
|
|
367
|
-
*/
|
|
368
|
-
configure(config: MergedRelyIOConfig): void;
|
|
369
|
-
/**
|
|
370
|
-
* Replace the key every later request sends as `X-API-Key`, and the `apiKey` the configuration
|
|
371
|
-
* reports.
|
|
372
|
-
*
|
|
373
|
-
* The key is read the way construction reads it (`presentKey`): an absent, empty or blank key is
|
|
374
|
-
* no key, so the header is dropped and the next request is refused with `MissingApiKeyError`
|
|
375
|
-
* before anything is sent. A key header of any other spelling — one given in `headers` — goes
|
|
376
|
-
* too, from the headers sent and from the headers the configuration reports, so the old key is
|
|
377
|
-
* never sent beside the new one.
|
|
378
|
-
*
|
|
379
|
-
* The configuration is the one the owning `TypedRelyIOClient` holds (it hands its own to the
|
|
380
|
-
* constructor), so its `getConfig().apiKey` — what a surface reads to decide a key's kind — is the
|
|
381
|
-
* key sent from here on.
|
|
382
|
-
*
|
|
383
|
-
* This replaced `setAuthToken`, which added `Authorization: Bearer` beside an `X-API-Key` it
|
|
384
|
-
* never touched: the API reads `X-API-Key` first, so the old key was the one used (REL-1385).
|
|
385
|
-
*/
|
|
386
|
-
setApiKey(apiKey: string): void;
|
|
387
|
-
}
|
|
388
|
-
/** Everything a request reads off the configuration, replaced whole whenever it changes. */
|
|
389
|
-
interface Connection {
|
|
390
|
-
/**
|
|
391
|
-
* The configuration itself, for what is read per request — `fetch`, the `browser` options,
|
|
392
|
-
* `debug` — and the object `setApiKey` writes the key into, which the owning client reports.
|
|
393
|
-
*/
|
|
394
|
-
readonly config: MergedRelyIOConfig;
|
|
395
|
-
readonly baseUrl: string;
|
|
396
|
-
readonly defaultHeaders: Readonly<Record<string, string>>;
|
|
397
|
-
readonly timeout: number;
|
|
398
|
-
readonly retryPolicy: RetryPolicy;
|
|
399
|
-
}
|
|
400
|
-
|
|
401
|
-
/**
|
|
402
|
-
* One Record type's Records over the Content API (spec 1, REL-3771): `relyio.records('order')`.
|
|
403
|
-
*
|
|
404
|
-
* A Record is an app's data — an order, a stock level, a cart — written by your server with a
|
|
405
|
-
* secret key and live the moment it lands: no draft, no publication, no locale, no version. A
|
|
406
|
-
* publishable key is refused on every Record route (`PUBLISHABLE_KEY_REFUSED`), so call this from a
|
|
407
|
-
* server or a Function, never a browser.
|
|
408
|
-
*
|
|
409
|
-
* Every body is read from the committed document (`INV-API-CLIENT-IS-DERIVED`): the paths are the
|
|
410
|
-
* document's, and `RecordMetadata` is its schema by name.
|
|
411
|
-
*/
|
|
412
|
-
|
|
413
|
-
/** What the API reports about a Record beside its fields. */
|
|
414
|
-
type RecordMetadata = components['schemas']['RecordMetadata'];
|
|
415
|
-
/** A Record as a read answers it: its fields at the top level, and `_metadata`. */
|
|
416
|
-
type BaseRecord = Record<string, unknown> & {
|
|
417
|
-
_metadata: RecordMetadata;
|
|
418
|
-
};
|
|
419
|
-
/** What a Record read and write take beside their own options. */
|
|
420
|
-
interface RecordCallOptions {
|
|
421
|
-
/** The dataset, by id or name, over the client's. */
|
|
422
|
-
dataset?: string;
|
|
423
|
-
signal?: AbortSignal;
|
|
424
|
-
}
|
|
425
|
-
/**
|
|
426
|
-
* What a Record read may expand (REL-3772): include paths, in the grammar `content().get` takes —
|
|
427
|
-
* `lines.product`, `convertedTo.lines.product(title)`. A reference to an entry expands into the
|
|
428
|
-
* entry as this key reads it — published with a secret key, its draft with a preview key — and one
|
|
429
|
-
* to a Record into the Record. A reference to an entry the key cannot see, or that is gone, reads
|
|
430
|
-
* as `null` — unless it is a pinned reference (`pin: 'version'`), which reads the entry as it was
|
|
431
|
-
* at the version the Record pinned, whatever it has done since, its `_metadata.pin` naming that
|
|
432
|
-
* version and the one the key reads now (REL-3776).
|
|
433
|
-
*/
|
|
434
|
-
interface RecordReadOptions extends RecordCallOptions {
|
|
435
|
-
include?: readonly string[];
|
|
436
|
-
/**
|
|
437
|
-
* The locale each included entry is composed in, with its schema's fallback; the schema's
|
|
438
|
-
* default when absent. A Record has no locale of its own (REL-3776).
|
|
439
|
-
*/
|
|
440
|
-
locale?: string;
|
|
441
|
-
}
|
|
442
|
-
/** What an update takes: the revision the change was made on. */
|
|
443
|
-
interface RecordUpdateOptions extends RecordCallOptions {
|
|
444
|
-
/**
|
|
445
|
-
* `_metadata.revision` of the read the change was made on. A Record written since is refused
|
|
446
|
-
* with `ResourceConflictError`, its `blockage` (`revision-stale`) naming the fields that changed,
|
|
447
|
-
* and nothing is written.
|
|
448
|
-
*/
|
|
449
|
-
revision: number;
|
|
450
|
-
}
|
|
451
|
-
/** A list's options: one page of the Records `where` keeps, in one order. */
|
|
452
|
-
interface RecordListOptions<TFields> extends RecordReadOptions {
|
|
453
|
-
/** Field conditions, in the grammar `content().list({ where })` takes. */
|
|
454
|
-
where?: {
|
|
455
|
-
[K in keyof TFields]?: unknown;
|
|
456
|
-
} & Record<string, unknown>;
|
|
457
|
-
/** One key: `createdAt`, `updatedAt`, `id` or a field holding one value. Default `createdAt:desc`. */
|
|
458
|
-
sort?: `${'createdAt' | 'updatedAt' | 'id' | (keyof TFields & string)}:${'asc' | 'desc'}`;
|
|
459
|
-
limit?: number;
|
|
460
|
-
cursor?: string;
|
|
461
|
-
/** Answer `meta.count`: how many Records `where` keeps across every page. */
|
|
462
|
-
count?: boolean;
|
|
463
|
-
/** Count the values of each of these fields among the Records `where` keeps, under `meta.facets`. */
|
|
464
|
-
facets?: ReadonlyArray<keyof TFields & string>;
|
|
465
|
-
}
|
|
466
|
-
/** What an upload of a Record's File takes beside its bytes (REL-3824). */
|
|
467
|
-
interface RecordFileUploadOptions extends RecordCallOptions {
|
|
468
|
-
/** The File's name, as a read will answer it. */
|
|
469
|
-
name: string;
|
|
470
|
-
/** Its media type: one the field's `accept` admits. */
|
|
471
|
-
type: string;
|
|
472
|
-
}
|
|
473
|
-
/** The bytes of a File: what `fetch` sends as a body, of a length known before it is sent. */
|
|
474
|
-
type RecordFileBytes = Blob | ArrayBuffer | Uint8Array;
|
|
475
|
-
/** One facet's buckets: each value and how many Records hold it, most frequent first. */
|
|
476
|
-
interface RecordFacetBucket {
|
|
477
|
-
readonly value: unknown;
|
|
478
|
-
readonly count: number;
|
|
479
|
-
}
|
|
480
|
-
/** One page of a Record list, as the API answers it. */
|
|
481
|
-
interface RecordList<T> {
|
|
482
|
-
data: T[];
|
|
483
|
-
page: {
|
|
484
|
-
nextCursor: string | null;
|
|
485
|
-
hasMore: boolean;
|
|
486
|
-
};
|
|
487
|
-
meta: {
|
|
488
|
-
count?: number;
|
|
489
|
-
facets?: Record<string, RecordFacetBucket[]>;
|
|
490
|
-
};
|
|
491
|
-
}
|
|
492
|
-
/**
|
|
493
|
-
* One Record type's Records.
|
|
494
|
-
*
|
|
495
|
-
* @example
|
|
496
|
-
* ```typescript
|
|
497
|
-
* const orders = relyio.records('order')
|
|
498
|
-
* const order = await orders.create({ shopperEmail: 'ada@example.com', status: 'paid', total: 19 })
|
|
499
|
-
* const { data: paid, meta } = await orders.list({
|
|
500
|
-
* where: { status: 'paid' },
|
|
501
|
-
* sort: 'createdAt:asc',
|
|
502
|
-
* count: true,
|
|
503
|
-
* })
|
|
504
|
-
* ```
|
|
505
|
-
*/
|
|
506
|
-
declare class RecordsClient<TRecord extends BaseRecord = BaseRecord, TInput = Record<string, unknown>,
|
|
507
|
-
/** What an update takes: the create's values without the type's Counters (REL-3765). */
|
|
508
|
-
TUpdate = TInput> {
|
|
509
|
-
private readonly httpClient;
|
|
510
|
-
readonly recordType: string;
|
|
511
|
-
constructor(httpClient: HttpClient, recordType: string);
|
|
512
|
-
/**
|
|
513
|
-
* Store a Record, live the moment it lands. Values that break the Record type's fields are
|
|
514
|
-
* refused with `ValidationError` naming each field, and nothing is stored. Values a unique set of
|
|
515
|
-
* the type claims, held by another Record, are refused with `ResourceConflictError` — its
|
|
516
|
-
* `blockage` is `{ kind: 'value-taken', facts: { fields, recordId } }` (REL-3782).
|
|
517
|
-
*/
|
|
518
|
-
create(fields: TInput, options?: RecordCallOptions): Promise<TRecord>;
|
|
519
|
-
/** One page of the Records `where` keeps, newest first unless `sort` names one key. */
|
|
520
|
-
list(options?: RecordListOptions<Omit<TRecord, '_metadata'>>): Promise<RecordList<TRecord>>;
|
|
521
|
-
/**
|
|
522
|
-
* Upload a File for one of this Record type's file fields (REL-3824), and answer what a create or
|
|
523
|
-
* update sets the field to. The bytes go straight to object storage at the address the upload
|
|
524
|
-
* names, never through RelyIO; a File outside the field's `accept` or over its `maxSize` is
|
|
525
|
-
* refused with `ValidationError` naming the field before any byte is sent. An upload no write
|
|
526
|
-
* names within a day is deleted with its bytes. Server-side only, as every Record call is.
|
|
527
|
-
*
|
|
528
|
-
* @example
|
|
529
|
-
* ```typescript
|
|
530
|
-
* const invoice = await relyio.records('order').upload('invoice', pdfBytes, {
|
|
531
|
-
* name: 'invoice-1001.pdf',
|
|
532
|
-
* type: 'application/pdf',
|
|
533
|
-
* })
|
|
534
|
-
* await relyio.records('order').update(id, { invoice }, { revision })
|
|
535
|
-
* ```
|
|
536
|
-
*/
|
|
537
|
-
upload(field: string, bytes: RecordFileBytes, options: RecordFileUploadOptions): Promise<FileUpload>;
|
|
538
|
-
/**
|
|
539
|
-
* One Record by its id, or `null` when none is stored. A Record type nobody deployed throws
|
|
540
|
-
* `ResourceNotFoundError` naming it, so it never reads as a missing Record.
|
|
541
|
-
*/
|
|
542
|
-
get(id: string, options?: RecordReadOptions): Promise<TRecord | null>;
|
|
543
|
-
/**
|
|
544
|
-
* Change the fields named, against the revision the change was made on, and answer the Record at
|
|
545
|
-
* its next revision. A field given `null` is removed; one not named keeps its value. A Record
|
|
546
|
-
* written since `revision` is refused with `ResourceConflictError` — its `blockage` is
|
|
547
|
-
* `{ kind: 'revision-stale', facts: { changedFields } }` — and nothing is written. Values that
|
|
548
|
-
* break the Record type are refused with `ValidationError` naming each field, and values another
|
|
549
|
-
* Record holds under a unique set with `ResourceConflictError`, its blockage `value-taken`.
|
|
550
|
-
*
|
|
551
|
-
* @example
|
|
552
|
-
* ```typescript
|
|
553
|
-
* const order = await relyio.records('order').get(id)
|
|
554
|
-
* await relyio.records('order').update(id, { status: 'shipped' }, {
|
|
555
|
-
* revision: order._metadata.revision,
|
|
556
|
-
* })
|
|
557
|
-
* ```
|
|
558
|
-
*/
|
|
559
|
-
update(id: string, fields: {
|
|
560
|
-
[K in keyof TUpdate]?: TUpdate[K] | null;
|
|
561
|
-
}, options: RecordUpdateOptions): Promise<TRecord>;
|
|
562
|
-
/**
|
|
563
|
-
* Remove a Record. Another Record holding it through a reference declared to block its delete —
|
|
564
|
-
* the default — is refused with `ResourceConflictError`, its `blockage`
|
|
565
|
-
* (`{ kind: 'referenced', facts: { referrers } }`) naming each; one declared `nullify` is cleared.
|
|
566
|
-
*/
|
|
567
|
-
delete(id: string, options?: RecordCallOptions): Promise<void>;
|
|
568
|
-
}
|
|
569
|
-
|
|
570
|
-
/**
|
|
571
|
-
* The atomic Record batch (spec 1, REL-3774): `relyio.records.batch([...])` — an ordered list of
|
|
572
|
-
* Record operations across Record types, run in one transaction, all or none.
|
|
573
|
-
*
|
|
574
|
-
* Each operation is `create`, `update`, `delete`, `increment` or `decrement`, and names its Record by
|
|
575
|
-
* `id`, by `where` (the one Record of its type whose top-level fields have those values), or both —
|
|
576
|
-
* `where` then a precondition. A refused operation rolls the whole batch back, and the refusal names
|
|
577
|
-
* it: {@link refusedOperationOf} reads its index off the error. A counter moved past its bound is a
|
|
578
|
-
* `ResourceConflictError` whose `blockage` is `counter-out-of-bounds`, naming the field, the bound and
|
|
579
|
-
* the value it holds.
|
|
580
|
-
*
|
|
581
|
-
* Every batch is sent with an `Idempotency-Key` — yours, or one minted for the call — so a batch
|
|
582
|
-
* retried after a lost answer is answered as done, never written twice.
|
|
583
|
-
*/
|
|
584
|
-
|
|
585
|
-
/** A condition's value: what one top-level field holds, compared for equality. */
|
|
586
|
-
type RecordConditionValue = string | number | boolean | null;
|
|
587
|
-
/**
|
|
588
|
-
* Which stored Record an operation lands on: by `id`, by `where`, or both. Every `where` is read
|
|
589
|
-
* before the batch's first operation runs, so it names a Record stored before the batch — never one
|
|
590
|
-
* an earlier operation of the same batch creates.
|
|
591
|
-
*/
|
|
592
|
-
type RecordBatchTarget = {
|
|
593
|
-
id: string;
|
|
594
|
-
where?: Record<string, RecordConditionValue>;
|
|
595
|
-
} | {
|
|
596
|
-
id?: undefined;
|
|
597
|
-
where: Record<string, RecordConditionValue>;
|
|
598
|
-
};
|
|
599
|
-
/** One operation of a batch. `type` is a Record type the schema declares. */
|
|
600
|
-
type RecordBatchOperation<TType extends string = string> = {
|
|
601
|
-
op: 'create';
|
|
602
|
-
type: TType;
|
|
603
|
-
fields: Record<string, unknown>;
|
|
604
|
-
} | ({
|
|
605
|
-
op: 'update';
|
|
606
|
-
type: TType;
|
|
607
|
-
/** The fields to change; one given `null` is removed. A counter is never set. */
|
|
608
|
-
fields: Record<string, unknown>;
|
|
609
|
-
/** The revision the change was made on; absent, the update writes over the one stored. */
|
|
610
|
-
revision?: number;
|
|
611
|
-
} & RecordBatchTarget) | ({
|
|
612
|
-
op: 'delete';
|
|
613
|
-
type: TType;
|
|
614
|
-
} & RecordBatchTarget) | ({
|
|
615
|
-
op: 'increment' | 'decrement';
|
|
616
|
-
type: TType;
|
|
617
|
-
/** The counter. */
|
|
618
|
-
field: string;
|
|
619
|
-
/** A whole number above zero. */
|
|
620
|
-
by: number;
|
|
621
|
-
} & RecordBatchTarget);
|
|
622
|
-
/** What a batch takes beside its operations. */
|
|
623
|
-
interface RecordBatchOptions extends RecordCallOptions {
|
|
624
|
-
/**
|
|
625
|
-
* The key naming this batch. Sent again — by you, or by a retry — the batch is answered as the
|
|
626
|
-
* first was and written once; with other operations it is refused. Minted for you when absent.
|
|
627
|
-
*/
|
|
628
|
-
idempotencyKey?: string;
|
|
629
|
-
}
|
|
630
|
-
/** What a committed batch answers: each operation, in order, and whether this is a replay. */
|
|
631
|
-
type RecordBatchResult = components['schemas']['RecordBatchResult'];
|
|
632
|
-
/** What one operation of a committed batch did. */
|
|
633
|
-
type RecordBatchOperationResult = components['schemas']['RecordBatchOperationResult'];
|
|
634
|
-
/**
|
|
635
|
-
* The index, from 0, of the batch operation a refusal names: a `ResourceConflictError`'s
|
|
636
|
-
* `blockage.facts.operation`, or the first violation under `operations[<index>]` of a
|
|
637
|
-
* `ValidationError`. `undefined` for an error that names none — one about the whole batch, such as
|
|
638
|
-
* an allowance, or no batch refusal at all.
|
|
639
|
-
*/
|
|
640
|
-
declare function refusedOperationOf(error: unknown): number | undefined;
|
|
641
|
-
|
|
642
|
-
/**
|
|
643
|
-
* Any schema the client can be typed by. `any` is deliberate: the client accepts whatever content
|
|
644
|
-
* types, singletons, queries and settings groups a customer declares.
|
|
645
|
-
*
|
|
646
|
-
* This is the constraint `TypedRelyIOClient`'s `TSchema` carries, so `RegisteredSchema` is inferred
|
|
647
|
-
* against the same one — `createClient` returns `TypedRelyIOClient<RegisteredSchema>`, and a schema
|
|
648
|
-
* inferred against any wider alias would not be provably inside the client's bound.
|
|
649
|
-
*/
|
|
650
|
-
type AnySchemaDefinition = SchemaDefinition<any, any, any, any, any>;
|
|
651
|
-
/**
|
|
652
|
-
* The seam `relyio generate-types` fills (`INV-SS-TYPES-INFERRED`).
|
|
653
|
-
*
|
|
654
|
-
* Empty here on purpose. The generated `schema.ts` augments it —
|
|
655
|
-
*
|
|
656
|
-
* ```ts
|
|
657
|
-
* declare module '@relyio/sdk' {
|
|
658
|
-
* interface Register {
|
|
659
|
-
* schema: typeof schema
|
|
660
|
-
* }
|
|
661
|
-
* }
|
|
662
|
-
* ```
|
|
663
|
-
*
|
|
664
|
-
* — and from then on `createClient({ apiKey })`, with no generic and no `schema:`, is typed by that
|
|
665
|
-
* schema. The pattern is TanStack Router's. It is type-level only: nothing at runtime reads it.
|
|
666
|
-
*/
|
|
667
|
-
interface Register {
|
|
668
|
-
}
|
|
669
|
-
/**
|
|
670
|
-
* The schema the project registered through `Register`, or `undefined` when nothing did — which is
|
|
671
|
-
* the untyped client, exactly as before the generator ran.
|
|
672
|
-
*/
|
|
673
|
-
type RegisteredSchema = Register extends {
|
|
674
|
-
schema: infer S extends AnySchemaDefinition;
|
|
675
|
-
} ? S : undefined;
|
|
676
|
-
|
|
677
|
-
export { type AnySchemaDefinition as A, type BaseRecord as B, type RecordBatchOptions as C, DefaultRetryPolicy as D, HttpClient as H, type MatchRouteOptions as M, type QueryOf as Q, type RegisteredSchema as R, type SendOptions as S, type Wire as W, type RecordBatchTarget as a, type RecordBatchResult as b, type RecordConditionValue as c, type Register as d, type ApiPath as e, type ApiMethod as f, type ApiSuccessBody as g, type RouteMatch as h, isReplaySafe as i, type RetryPolicy as j, type RetryableRequest as k, RecordsClient as l, matchRoute as m, type RecordCallOptions as n, type RecordFacetBucket as o, type RecordFileBytes as p, type RecordFileUploadOptions as q, retryPolicyFor as r, type RecordList as s, type RecordListOptions as t, type RecordMetadata as u, type RecordReadOptions as v, type RecordUpdateOptions as w, refusedOperationOf as x, type RecordBatchOperation as y, type RecordBatchOperationResult as z };
|