@relyio/sdk 0.19.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.
Files changed (54) hide show
  1. package/CHANGELOG.md +10 -0
  2. package/dist/automations/index.cjs +1 -1
  3. package/dist/automations/index.d.cts +2 -1588
  4. package/dist/automations/index.d.ts +2 -1588
  5. package/dist/automations/index.js +1 -1
  6. package/dist/{chunk-ZO6QDYJG.cjs → chunk-5WGMIQBH.cjs} +2 -2
  7. package/dist/{chunk-W2A4OTCI.js → chunk-EQUZNVAR.js} +2 -2
  8. package/dist/{chunk-P23G5NXD.cjs → chunk-K66MBRI4.cjs} +68 -51
  9. package/dist/{chunk-356SF5Y2.js → chunk-L42C4G2S.js} +25 -8
  10. package/dist/{chunk-T7LGREB3.js → chunk-OUR7OUUZ.js} +3 -3
  11. package/dist/{chunk-NN45CSPZ.cjs → chunk-UO2QRYKV.cjs} +3 -3
  12. package/dist/client/index.cjs +5 -5
  13. package/dist/client/index.d.cts +4 -4
  14. package/dist/client/index.d.ts +4 -4
  15. package/dist/client/index.js +3 -3
  16. package/dist/{define-content-type-BZ6X8GvL.d.cts → define-content-type-C2HnCYYX.d.cts} +1 -1
  17. package/dist/{define-content-type-C12jh8cY.d.ts → define-content-type-CVLUusfx.d.ts} +1 -1
  18. package/dist/dev-handshake/index.cjs +8 -8
  19. package/dist/dev-handshake/index.d.cts +1 -1
  20. package/dist/dev-handshake/index.d.ts +1 -1
  21. package/dist/dev-handshake/index.js +1 -1
  22. package/dist/extensions/index.cjs +3 -3
  23. package/dist/extensions/index.d.cts +2 -4
  24. package/dist/extensions/index.d.ts +2 -4
  25. package/dist/extensions/index.js +1 -1
  26. package/dist/functions/index.cjs +1 -1
  27. package/dist/functions/index.d.cts +2 -4
  28. package/dist/functions/index.d.ts +2 -4
  29. package/dist/functions/index.js +1 -1
  30. package/dist/{index-D5jyIeQ5.d.cts → index-BLVNMYUU.d.cts} +54 -9
  31. package/dist/{index-C-MymZTe.d.ts → index-BY1xU93u.d.ts} +1 -1
  32. package/dist/{index-DN0l8RHw.d.cts → index-Cmup6HJY.d.cts} +1 -1
  33. package/dist/index-D1fiaQlK.d.ts +2284 -0
  34. package/dist/index-DImzg3ew.d.cts +2284 -0
  35. package/dist/{index-BxNnlc5D.d.ts → index-IjSyBr8f.d.ts} +54 -9
  36. package/dist/index.cjs +76 -76
  37. package/dist/index.d.cts +7 -8
  38. package/dist/index.d.ts +7 -8
  39. package/dist/index.js +4 -4
  40. package/dist/preview/index.cjs +3 -3
  41. package/dist/preview/index.js +1 -1
  42. package/dist/rich-text/index.d.cts +2 -2
  43. package/dist/rich-text/index.d.ts +2 -2
  44. package/dist/schema/index.cjs +1 -1
  45. package/dist/schema/index.d.cts +947 -4
  46. package/dist/schema/index.d.ts +947 -4
  47. package/dist/schema/index.js +1 -1
  48. package/dist/types/index.d.cts +1 -1
  49. package/dist/types/index.d.ts +1 -1
  50. package/package.json +4 -4
  51. package/dist/define-schema-BPlZl4a2.d.cts +0 -947
  52. package/dist/define-schema-CK7TtYot.d.ts +0 -947
  53. package/dist/register-BiMQKVp-.d.ts +0 -693
  54. package/dist/register-DeMsaurp.d.cts +0 -693
@@ -0,0 +1,2284 @@
1
+ import { cW as paths, cX as RetryConfig, cY as MergedRelyIOConfig, cZ as ApiResponse, ap as components, ar as FileUpload, cS as ConditionScalar, cT as ConditionTest, C as ConditionTree, cU as ConditionTests, s as KeyedMembers, an as FieldValues, ao as DeclaredFields, t as KeyedMembersOf, c_ as ExtensionSurfaces, c$ as ExtensionSettingField, d0 as ExtensionSurface, d1 as ActionField, d2 as ExtensionValueField, z as ReferenceConfig, p as KeyedFieldOptions, o as KeyedField } from './define-content-type-CVLUusfx.js';
2
+ import { SchemaDefinition, SchemaRecordTypeNames, InferRecordType, SchemaContentTypeNames, SchemaEntry, SchemaLocaleCodes, RecordWriteFieldsOf, RecordUpdateFieldsOf } from './schema/index.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
+ /** The End user's token function this view asks in place of its client's, when it names one. */
265
+ readonly getToken?: EndUserTokenSource;
266
+ };
267
+ constructor(config: MergedRelyIOConfig);
268
+ protected announce(): void;
269
+ /**
270
+ * Send bytes to an address that is not a RelyIO route — an asset upload's presigned `PUT` and its
271
+ * multipart parts — through the client's configured `fetch`, so a wrapper that records, proxies or
272
+ * forbids network access sees every request this client makes (REL-2671). Nothing else is added: no
273
+ * key, no retry, no timeout, because the address belongs to the storage service, not to RelyIO.
274
+ *
275
+ * Static so the instances a customer holds stay `send` and `fetchPayload` (REL-3107). The `fetch`
276
+ * is read per call, from the client a view sends through, like the requests are.
277
+ */
278
+ static transfer(client: HttpClient, url: string, init: RequestInit): Promise<Response>;
279
+ /**
280
+ * Call a route the committed document declares, and get back the body that
281
+ * document says it serves.
282
+ */
283
+ send<P extends ApiPath, M extends ApiMethod<P>, T = unknown>(path: P, method: M, options: SendOptions<P, M>): Promise<ApiResponse<Wire<P, M, T>>>;
284
+ /**
285
+ * Call a route and return its payload — `send` plus the one unwrap, which is
286
+ * what all but a handful of call sites want.
287
+ */
288
+ fetchPayload<P extends ApiPath, M extends ApiMethod<P>, T = unknown>(path: P, method: M, options: SendOptions<P, M>): Promise<Wire<P, M, T> extends {
289
+ data: unknown;
290
+ } ? Wire<P, M, T>['data'] : never>;
291
+ /**
292
+ * Make an HTTP request, retrying only where a retry is safe.
293
+ *
294
+ * The headers are merged **once**, before the first attempt, and every attempt
295
+ * sends that same object. That is what makes a minted `Idempotency-Key` stable
296
+ * across the retries of one logical write: the service sees one key and
297
+ * answers the replay from the original outcome instead of applying it twice.
298
+ *
299
+ * The loop always makes one attempt. The incumbent's `while (attempt <
300
+ * maxAttempts)` made none at `retryAttempts: 0` — a value the config validator
301
+ * then accepted, and now refuses — and then dereferenced an unassigned
302
+ * `lastError`. `retryAttempts` is the most attempts, the first included; an
303
+ * `HttpClient` constructed directly is not validated, so the first attempt
304
+ * stays unconditional.
305
+ *
306
+ * A request that would carry no `X-API-Key` makes none: it rejects with
307
+ * `MissingApiKeyError` before `fetch` is called, is not retried, and names
308
+ * `apiKey`. This is where a client built without a key learns it — the
309
+ * constructor accepts one so that a module-scope client can be evaluated by a
310
+ * build that holds no key (REL-1120) — and so does one whose `setApiKey` was
311
+ * given none.
312
+ *
313
+ * The End user's token is asked for once, after the key is known to be there and before the first
314
+ * attempt — of `getToken`, the nearest view's function, or the configuration's — so every retry
315
+ * of one call sends the token its first attempt sent. See `RelyIOConfig.getToken`.
316
+ */
317
+ private request;
318
+ /**
319
+ * One attempt.
320
+ *
321
+ * The caller's `signal` and the timeout both end it, through one controller.
322
+ * The incumbent handed `fetch` the caller's signal **instead of** its own, so a
323
+ * request that passed a signal had no timeout at all — and a caller abort then
324
+ * surfaced as `Request timed out`. `timedOut` is what tells the two apart: a
325
+ * timeout is a `TimeoutError`, a caller abort is the platform's `AbortError`,
326
+ * rethrown as it came.
327
+ */
328
+ private makeRequest;
329
+ /**
330
+ * The headers without those a browser controls itself, when this is a browser; unchanged when it
331
+ * is not.
332
+ *
333
+ * A name is matched without regard to case (RFC 9110 §5.1), because a browser refuses `Origin`
334
+ * whatever case a page spells it in. This matched only the all-lowercase and all-uppercase
335
+ * spellings, so `Origin` and `User-Agent` — how people write them — reached `fetch` (REL-1409).
336
+ */
337
+ private filterHeadersForBrowser;
338
+ /**
339
+ * Prepare request body based on content type and environment
340
+ */
341
+ private prepareRequestBody;
342
+ private parseResponse;
343
+ private resolveUrl;
344
+ private mergeRequestConfig;
345
+ private buildQueryString;
346
+ private headersToObject;
347
+ private log;
348
+ }
349
+ /**
350
+ * The client the SDK's own factory holds: {@link HttpClient} plus the three things only the owning
351
+ * client does to it — `configure` (`updateConfig()`), `withHeaders` (`with()`) and `setApiKey`
352
+ * (`setApiKey()`). It is not exported from the package, so a customer holding the client from
353
+ * `getHttpClient()` sees `send` and `fetchPayload` and nothing else (REL-3107).
354
+ */
355
+ declare class ControlledHttpClient extends HttpClient {
356
+ /**
357
+ * A view of this client that sends every request through it with `headers` added — beneath the
358
+ * request's own, over this client's — and changes nothing else: its key, base URL, timeout,
359
+ * retries and `fetch` are this client's as they are when each request is sent, so a later
360
+ * `configure()` or `setApiKey()` on this client reaches the view, and one on the view reaches
361
+ * this client. `client.with({ dataset })` is one (REL-2417).
362
+ *
363
+ * `options.getToken` is the End user's token function the view asks in place of this client's,
364
+ * and of any view it is made from: `client.with({ getToken })` (REL-3882).
365
+ */
366
+ withHeaders(headers: Readonly<Record<string, string>>, options?: {
367
+ readonly getToken?: EndUserTokenSource;
368
+ }): ControlledHttpClient;
369
+ /**
370
+ * Run on `config` from now on: every later request — whoever holds this client — is sent to its
371
+ * `baseUrl`, with its headers, key and dataset, its timeout and retries, through its `fetch`, and
372
+ * logged by its `debug` options.
373
+ *
374
+ * The client's own `updateConfig()` calls this rather than building a second client, so a
375
+ * `content()` or other sub-client taken before the update sends through the update too. Anything
376
+ * set on this client since it was last configured — `setApiKey()` — is replaced by what `config`
377
+ * says.
378
+ */
379
+ configure(config: MergedRelyIOConfig): void;
380
+ /**
381
+ * Replace the key every later request sends as `X-API-Key`, and the `apiKey` the configuration
382
+ * reports.
383
+ *
384
+ * The key is read the way construction reads it (`presentCredential`): an absent, empty or blank key is
385
+ * no key, so the header is dropped and the next request is refused with `MissingApiKeyError`
386
+ * before anything is sent. A key header of any other spelling — one given in `headers` — goes
387
+ * too, from the headers sent and from the headers the configuration reports, so the old key is
388
+ * never sent beside the new one.
389
+ *
390
+ * The configuration is the one the owning `TypedRelyIOClient` holds (it hands its own to the
391
+ * constructor), so its `getConfig().apiKey` — what a surface reads to decide a key's kind — is the
392
+ * key sent from here on.
393
+ *
394
+ * This replaced `setAuthToken`, which added `Authorization: Bearer` beside an `X-API-Key` it
395
+ * never touched: the API reads `X-API-Key` first, so the old key was the one used (REL-1385).
396
+ */
397
+ setApiKey(apiKey: string): void;
398
+ }
399
+ /** What `getToken` is: asked per call, answering the signed-in End user's token or `null`. */
400
+ type EndUserTokenSource = NonNullable<MergedRelyIOConfig['getToken']>;
401
+ /** Everything a request reads off the configuration, replaced whole whenever it changes. */
402
+ interface Connection {
403
+ /**
404
+ * The configuration itself, for what is read per request — `fetch`, the `browser` options,
405
+ * `debug` — and the object `setApiKey` writes the key into, which the owning client reports.
406
+ */
407
+ readonly config: MergedRelyIOConfig;
408
+ readonly baseUrl: string;
409
+ readonly defaultHeaders: Readonly<Record<string, string>>;
410
+ readonly timeout: number;
411
+ readonly retryPolicy: RetryPolicy;
412
+ }
413
+
414
+ /**
415
+ * Any schema the client can be typed by. `any` is deliberate: the client accepts whatever content
416
+ * types, singletons, queries and settings groups a customer declares.
417
+ *
418
+ * This is the constraint `TypedRelyIOClient`'s `TSchema` carries, so `RegisteredSchema` is inferred
419
+ * against the same one — `createClient` returns `TypedRelyIOClient<RegisteredSchema>`, and a schema
420
+ * inferred against any wider alias would not be provably inside the client's bound.
421
+ */
422
+ type AnySchemaDefinition = SchemaDefinition<any, any, any, any, any>;
423
+ /**
424
+ * The seam `relyio generate-types` fills (`INV-SS-TYPES-INFERRED`).
425
+ *
426
+ * Empty here on purpose. The generated `schema.ts` augments it —
427
+ *
428
+ * ```ts
429
+ * declare module '@relyio/sdk' {
430
+ * interface Register {
431
+ * schema: typeof schema
432
+ * }
433
+ * }
434
+ * ```
435
+ *
436
+ * — and from then on `createClient({ apiKey })`, with no generic and no `schema:`, is typed by that
437
+ * schema. The pattern is TanStack Router's. It is type-level only: nothing at runtime reads it.
438
+ */
439
+ interface Register {
440
+ }
441
+ /**
442
+ * The schema the project registered through `Register`, or `undefined` when nothing did — which is
443
+ * the untyped client, exactly as before the generator ran.
444
+ */
445
+ type RegisteredSchema = Register extends {
446
+ schema: infer S extends AnySchemaDefinition;
447
+ } ? S : undefined;
448
+
449
+ /**
450
+ * One Record type's Records over the Content API (spec 1, REL-3771): `relyio.records('order')`.
451
+ *
452
+ * A Record is an app's data — an order, a stock level, a cart — written by your server with a
453
+ * secret key and live the moment it lands: no draft, no publication, no locale, no version. A
454
+ * publishable key lists and gets the Records a Record type's read Policy grants it — anonymously, or
455
+ * as the End user whose token it carries (spec 4, REL-3883) — and creates, updates and deletes them
456
+ * only as that End user, as the type's write Policy allows (REL-3884): RelyIO sets the owner from
457
+ * the token. With no token it writes nothing (`PUBLISHABLE_KEY_REFUSED`).
458
+ *
459
+ * Every body is read from the committed document (`INV-API-CLIENT-IS-DERIVED`): the paths are the
460
+ * document's, and `RecordMetadata` is its schema by name.
461
+ */
462
+
463
+ /** What the API reports about a Record beside its fields. */
464
+ type RecordMetadata = components['schemas']['RecordMetadata'];
465
+ /** A Record as a read answers it: its fields at the top level, and `_metadata`. */
466
+ type BaseRecord = Record<string, unknown> & {
467
+ _metadata: RecordMetadata;
468
+ };
469
+ /** What a Record read and write take beside their own options. */
470
+ interface RecordCallOptions {
471
+ /** The dataset, by id or name, over the client's. */
472
+ dataset?: string;
473
+ signal?: AbortSignal;
474
+ }
475
+ /**
476
+ * What a Record read may expand (REL-3772): include paths, in the grammar `content().get` takes —
477
+ * `lines.product`, `convertedTo.lines.product(title)`. A reference to an entry expands into the
478
+ * entry as this key reads it — published with a secret key, its draft with a preview key — and one
479
+ * to a Record into the Record. A reference to an entry the key cannot see, or that is gone, reads
480
+ * as `null` — unless it is a pinned reference (`pin: 'version'`), which reads the entry as it was
481
+ * at the version the Record pinned, whatever it has done since, its `_metadata.pin` naming that
482
+ * version and the one the key reads now (REL-3776).
483
+ */
484
+ interface RecordReadOptions extends RecordCallOptions {
485
+ include?: readonly string[];
486
+ /**
487
+ * The locale each included entry is composed in, with its schema's fallback; the schema's
488
+ * default when absent. A Record has no locale of its own (REL-3776).
489
+ */
490
+ locale?: string;
491
+ }
492
+ /** What an update takes: the revision the change was made on. */
493
+ interface RecordUpdateOptions extends RecordCallOptions {
494
+ /**
495
+ * `_metadata.revision` of the read the change was made on. A Record written since is refused
496
+ * with `ResourceConflictError`, its `blockage` (`revision-stale`) naming the fields that changed,
497
+ * and nothing is written.
498
+ */
499
+ revision: number;
500
+ }
501
+ /** A list's options: one page of the Records `where` keeps, in one order. */
502
+ interface RecordListOptions<TFields> extends RecordReadOptions {
503
+ /** Field conditions, in the grammar `content().list({ where })` takes. */
504
+ where?: {
505
+ [K in keyof TFields]?: unknown;
506
+ } & Record<string, unknown>;
507
+ /** One key: `createdAt`, `updatedAt`, `id` or a field holding one value. Default `createdAt:desc`. */
508
+ sort?: `${'createdAt' | 'updatedAt' | 'id' | (keyof TFields & string)}:${'asc' | 'desc'}`;
509
+ limit?: number;
510
+ cursor?: string;
511
+ /** Answer `meta.count`: how many Records `where` keeps across every page. */
512
+ count?: boolean;
513
+ /** Count the values of each of these fields among the Records `where` keeps, under `meta.facets`. */
514
+ facets?: ReadonlyArray<keyof TFields & string>;
515
+ }
516
+ /** What an upload of a Record's File takes beside its bytes (REL-3824). */
517
+ interface RecordFileUploadOptions extends RecordCallOptions {
518
+ /** The File's name, as a read will answer it. */
519
+ name: string;
520
+ /** Its media type: one the field's `accept` admits. */
521
+ type: string;
522
+ }
523
+ /** The bytes of a File: what `fetch` sends as a body, of a length known before it is sent. */
524
+ type RecordFileBytes = Blob | ArrayBuffer | Uint8Array;
525
+ /** One facet's buckets: each value and how many Records hold it, most frequent first. */
526
+ interface RecordFacetBucket {
527
+ readonly value: unknown;
528
+ readonly count: number;
529
+ }
530
+ /** One page of a Record list, as the API answers it. */
531
+ interface RecordList<T> {
532
+ data: T[];
533
+ page: {
534
+ nextCursor: string | null;
535
+ hasMore: boolean;
536
+ };
537
+ meta: {
538
+ count?: number;
539
+ facets?: Record<string, RecordFacetBucket[]>;
540
+ };
541
+ }
542
+ /**
543
+ * One Record type's Records.
544
+ *
545
+ * @example
546
+ * ```typescript
547
+ * const orders = relyio.records('order')
548
+ * const order = await orders.create({ shopperEmail: 'ada@example.com', status: 'paid', total: 19 })
549
+ * const { data: paid, meta } = await orders.list({
550
+ * where: { status: 'paid' },
551
+ * sort: 'createdAt:asc',
552
+ * count: true,
553
+ * })
554
+ * ```
555
+ */
556
+ declare class RecordsClient<TRecord extends BaseRecord = BaseRecord, TInput = Record<string, unknown>,
557
+ /** What an update takes: the create's values without the type's Counters (REL-3765). */
558
+ TUpdate = TInput> {
559
+ private readonly httpClient;
560
+ readonly recordType: string;
561
+ constructor(httpClient: HttpClient, recordType: string);
562
+ /**
563
+ * Store a Record, live the moment it lands. Values that break the Record type's fields are
564
+ * refused with `ValidationError` naming each field, and nothing is stored. Values a unique set of
565
+ * the type claims, held by another Record, are refused with `ResourceConflictError` — its
566
+ * `blockage` is `{ kind: 'value-taken', facts: { fields, recordId } }` (REL-3782) — with no
567
+ * `recordId` for an End user's write, whose read Policy may hide the holder (REL-3863).
568
+ */
569
+ create(fields: TInput, options?: RecordCallOptions): Promise<TRecord>;
570
+ /** One page of the Records `where` keeps, newest first unless `sort` names one key. */
571
+ list(options?: RecordListOptions<Omit<TRecord, '_metadata'>>): Promise<RecordList<TRecord>>;
572
+ /**
573
+ * Upload a File for one of this Record type's file fields (REL-3824), and answer what a create or
574
+ * update sets the field to. The bytes go straight to object storage at the address the upload
575
+ * names, never through RelyIO; a File outside the field's `accept` or over its `maxSize` is
576
+ * refused with `ValidationError` naming the field before any byte is sent. An upload no write
577
+ * names within a day is deleted with its bytes. Server-side only, as every Record call is.
578
+ *
579
+ * @example
580
+ * ```typescript
581
+ * const invoice = await relyio.records('order').upload('invoice', pdfBytes, {
582
+ * name: 'invoice-1001.pdf',
583
+ * type: 'application/pdf',
584
+ * })
585
+ * await relyio.records('order').update(id, { invoice }, { revision })
586
+ * ```
587
+ */
588
+ upload(field: string, bytes: RecordFileBytes, options: RecordFileUploadOptions): Promise<FileUpload>;
589
+ /**
590
+ * One Record by its id, or `null` when none is stored. A Record type nobody deployed throws
591
+ * `ResourceNotFoundError` naming it, so it never reads as a missing Record.
592
+ */
593
+ get(id: string, options?: RecordReadOptions): Promise<TRecord | null>;
594
+ /**
595
+ * Change the fields named, against the revision the change was made on, and answer the Record at
596
+ * its next revision. A field given `null` is removed; one not named keeps its value. A Record
597
+ * written since `revision` is refused with `ResourceConflictError` — its `blockage` is
598
+ * `{ kind: 'revision-stale', facts: { changedFields } }` — and nothing is written. Values that
599
+ * break the Record type are refused with `ValidationError` naming each field, and values another
600
+ * Record holds under a unique set with `ResourceConflictError`, its blockage `value-taken`.
601
+ *
602
+ * @example
603
+ * ```typescript
604
+ * const order = await relyio.records('order').get(id)
605
+ * await relyio.records('order').update(id, { status: 'shipped' }, {
606
+ * revision: order._metadata.revision,
607
+ * })
608
+ * ```
609
+ */
610
+ update(id: string, fields: {
611
+ [K in keyof TUpdate]?: TUpdate[K] | null;
612
+ }, options: RecordUpdateOptions): Promise<TRecord>;
613
+ /**
614
+ * Remove a Record. Another Record holding it through a reference declared to block its delete —
615
+ * the default — is refused with `ResourceConflictError`, its `blockage`
616
+ * (`{ kind: 'referenced', facts: { referrers } }`) naming each; one declared `nullify` is cleared.
617
+ */
618
+ delete(id: string, options?: RecordCallOptions): Promise<void>;
619
+ }
620
+
621
+ /**
622
+ * `@relyio/sdk/automations` — an Automation, declared (spec 3, REL-3845; ADR 0021).
623
+ *
624
+ * One file per Automation at `relyio/automations/<name>.ts`, default-exporting what
625
+ * {@link defineAutomation} returns; the file's name is the Automation's. An Automation is a trigger
626
+ * and a sequence of steps, and all of it is data: `steps` is called **once**, when the Manifest
627
+ * loads, with typed references to the event and to earlier steps' outputs, and returns plain data.
628
+ * No code of yours runs when an event arrives, except inside a Function step.
629
+ *
630
+ * @example
631
+ * // relyio/automations/order-paid.ts
632
+ * import { defineAutomation, fields, or, records, tpl } from '@relyio/sdk/automations'
633
+ * import reserveStock from '../functions/reserve-stock'
634
+ *
635
+ * export default defineAutomation({
636
+ * on: records.order.updated({ where: { status: { becomes: 'paid' } } }),
637
+ * steps: ({ event, step }) => {
638
+ * const reserve = step.fn('reserve', reserveStock, { orderId: event.record._metadata.id })
639
+ * const fraud = step.agent('fraud', {
640
+ * instructions: tpl`Read order ${event.record.number} and the customer's past orders`,
641
+ * context: { order: event.record },
642
+ * output: { verdict: fields.enum({ options: { 'looks fine': 'Looks fine', 'needs review': 'Needs review' } }) },
643
+ * })
644
+ * return [reserve, fraud, step.if('ship', fraud.output.verdict.eq('looks fine'), { then: […] })]
645
+ * },
646
+ * })
647
+ *
648
+ * **Typed.** Once `relyio generate-types` has registered the schema (`Register`), `records.<type>`
649
+ * takes only the Record types it declares and `event.record` is typed by its fields. A Function
650
+ * step takes the Function by import, so its input is checked against the Function's input Fields and
651
+ * its `output` has exactly the Function's output Fields: a misspelt one is a type error, and so is a
652
+ * step reading one declared after it. The plan refuses both again, for a Manifest read as data.
653
+ *
654
+ * **Conditions** are the list grammar's operators (`eq`, `ne`, `in`, `gt`, `exists`, …) on a
655
+ * reference, combined with {@link and}, {@link or} and {@link not}. `becomes` — changed in this
656
+ * write and now equal — is a trigger's `where` alone.
657
+ *
658
+ * **Extensions** (REL-3846): `relyio/extensions.ts` declares the Extensions the Project uses with
659
+ * `defineExtensions` (`@relyio/sdk/extensions`), and hands out a typed handle for each. An Automation
660
+ * imports it: `on: stripe.paymentIntentSucceeded()` starts on one of its Triggers, and
661
+ * `step.action('email', resend.send, { … })` sends through one of its Actions — input and output
662
+ * typed by the Extension. Using an Extension the file does not declare is refused by the plan.
663
+ */
664
+
665
+ /** What a Record event says happened to one Record. */
666
+ type AutomationRecordAction = 'created' | 'updated' | 'deleted' | 'expired';
667
+ /** What an entry event says happened to one entry. */
668
+ type AutomationEntryAction = 'created' | 'updated' | 'published' | 'unpublished' | 'deleted' | 'archived' | 'restored';
669
+ /** A `becomes` test: changed in this write and now equal. A trigger's `where` alone holds one. */
670
+ interface AutomationBecomesTest {
671
+ readonly root: 'event';
672
+ readonly path: readonly string[];
673
+ readonly operator: 'becomes';
674
+ readonly value: ConditionScalar;
675
+ }
676
+ /** A trigger's `where`: a condition over `event`, whose tests may also be `becomes`. */
677
+ type AutomationWhere = ConditionTest | AutomationBecomesTest | {
678
+ readonly and: readonly AutomationWhere[];
679
+ } | {
680
+ readonly or: readonly AutomationWhere[];
681
+ } | {
682
+ readonly not: AutomationWhere;
683
+ };
684
+ /** An Automation's trigger, as data. */
685
+ type AutomationTrigger = {
686
+ readonly kind: 'record';
687
+ readonly recordType: string;
688
+ readonly action: AutomationRecordAction;
689
+ readonly where?: AutomationWhere;
690
+ } | {
691
+ readonly kind: 'entry';
692
+ readonly contentType: string;
693
+ readonly action: AutomationEntryAction;
694
+ readonly where?: AutomationWhere;
695
+ }
696
+ /**
697
+ * An entry of any content type: `where` is a plain condition over {@link AnyEntryEvent} — the
698
+ * entry's uid and content type, and what changed — with no `becomes`.
699
+ */
700
+ | {
701
+ readonly kind: 'anyEntry';
702
+ readonly action: AutomationEntryAction;
703
+ readonly where?: ConditionTree;
704
+ } | {
705
+ readonly kind: 'schedule';
706
+ readonly cron: string;
707
+ readonly timezone: string;
708
+ }
709
+ /** Another Automation's run failed; `where` is a plain condition over {@link RunFailedEvent}. */
710
+ | {
711
+ readonly kind: 'runFailed';
712
+ readonly where?: ConditionTree;
713
+ }
714
+ /**
715
+ * One of an Extension's Triggers: `trigger` is its slug in the Extension catalog. `where` is a plain
716
+ * condition over {@link ExtensionTriggerEvent}.
717
+ */
718
+ | {
719
+ readonly kind: 'extension';
720
+ readonly extension: string;
721
+ readonly trigger: string;
722
+ readonly where?: ConditionTree;
723
+ };
724
+ /** A JSON value, as a step's input holds it literally. */
725
+ type AutomationJson = string | number | boolean | null | readonly AutomationJson[] | {
726
+ readonly [key: string]: AutomationJson;
727
+ };
728
+ /** A reference: the value at `path` under `root` — `event`, or an earlier step's name. */
729
+ interface AutomationRef {
730
+ readonly kind: 'ref';
731
+ readonly root: string;
732
+ readonly path: readonly string[];
733
+ }
734
+ /** A value a step is handed, as data. */
735
+ type AutomationValue = {
736
+ readonly kind: 'literal';
737
+ readonly value: AutomationJson;
738
+ } | AutomationRef | {
739
+ readonly kind: 'template';
740
+ readonly parts: ReadonlyArray<string | AutomationRef>;
741
+ } | {
742
+ readonly kind: 'list';
743
+ readonly items: readonly AutomationValue[];
744
+ } | {
745
+ readonly kind: 'object';
746
+ readonly fields: Readonly<Record<string, AutomationValue>>;
747
+ };
748
+ /** A step's input, by key. */
749
+ type AutomationInput = Readonly<Record<string, AutomationValue>>;
750
+ /** A Wait's length: whole minutes, hours or days, at most 30 days. */
751
+ type AutomationDuration = `${number}m` | `${number}h` | `${number}d`;
752
+ /** One Record or entry by its uid: what a Look-up reads. */
753
+ type AutomationItemRef = {
754
+ readonly kind: 'record';
755
+ readonly recordType: string;
756
+ readonly uid: AutomationValue;
757
+ } | {
758
+ readonly kind: 'entry';
759
+ readonly contentType: string;
760
+ readonly uid: AutomationValue;
761
+ };
762
+ /** Which of an entry's states a Look-up reads: what readers see, or the editor's view. */
763
+ type AutomationLookupState = 'published' | 'draft';
764
+ /** A type an Agent step may propose a change to: a content type's entries, or a Record type's. */
765
+ type AutomationProposeTarget = {
766
+ readonly kind: 'entry';
767
+ readonly contentType: string;
768
+ } | {
769
+ readonly kind: 'record';
770
+ readonly recordType: string;
771
+ };
772
+ /** One Automation step, as data. */
773
+ type AutomationStep = {
774
+ readonly kind: 'function';
775
+ readonly name: string;
776
+ readonly function: string;
777
+ readonly input: AutomationInput;
778
+ } | {
779
+ readonly kind: 'agent';
780
+ readonly name: string;
781
+ readonly agent?: string;
782
+ readonly instructions: AutomationValue;
783
+ readonly context: AutomationInput;
784
+ readonly output: readonly object[];
785
+ readonly propose?: readonly AutomationProposeTarget[];
786
+ } | {
787
+ readonly kind: 'action';
788
+ readonly name: string;
789
+ readonly extension: string;
790
+ readonly action: string;
791
+ readonly input: AutomationInput;
792
+ } | {
793
+ readonly kind: 'lookup';
794
+ readonly name: string;
795
+ readonly item: Extract<AutomationItemRef, {
796
+ readonly kind: 'record';
797
+ }>;
798
+ } | {
799
+ readonly kind: 'lookup';
800
+ readonly name: string;
801
+ readonly item: Extract<AutomationItemRef, {
802
+ readonly kind: 'entry';
803
+ }>;
804
+ readonly state: AutomationLookupState;
805
+ } | {
806
+ readonly kind: 'if';
807
+ readonly name: string;
808
+ readonly condition: ConditionTree;
809
+ readonly then: readonly AutomationStep[];
810
+ readonly else: readonly AutomationStep[];
811
+ } | {
812
+ readonly kind: 'wait';
813
+ readonly name: string;
814
+ readonly duration: AutomationDuration;
815
+ } | {
816
+ readonly kind: 'ask';
817
+ readonly name: string;
818
+ readonly to: readonly string[];
819
+ readonly question: AutomationValue;
820
+ readonly choices: readonly string[];
821
+ readonly timeout: AutomationDuration;
822
+ readonly onTimeout?: string;
823
+ readonly sample?: string;
824
+ readonly approve?: AutomationRef;
825
+ };
826
+ /** What an Automation declares, as data: what the Manifest carries for one file. */
827
+ interface AutomationDefinition {
828
+ readonly description?: string;
829
+ readonly on: AutomationTrigger;
830
+ /** What each run is about: one Record or entry (REL-3853). */
831
+ readonly about?: AutomationItemRef;
832
+ readonly steps: readonly AutomationStep[];
833
+ }
834
+ /** Where a reference's value type rides, for the compiler alone. */
835
+ declare const REF_VALUE: unique symbol;
836
+ /** A list is compared by its members, as a condition compares a list. */
837
+ type MemberOf<T> = T extends ReadonlyArray<infer Member> ? Member : T;
838
+ /** The values a path holding `T` is compared with. */
839
+ type ComparedWith<T> = unknown extends T ? ConditionScalar : MemberOf<NonNullable<T>> extends infer Member ? Member extends ConditionScalar ? Member : never : never;
840
+ /** `becomes`, offered on a trigger's `where` alone. */
841
+ interface BecomesTest<T> {
842
+ /** Changed in this write, and now equal to `value`. */
843
+ becomes(value: ComparedWith<T>): AutomationBecomesTest;
844
+ }
845
+ /**
846
+ * The paths under a reference to `T`. Where its shape is not known — no schema registered — a
847
+ * reference is `any`: an index signature would read every step as possibly undefined under
848
+ * `noUncheckedIndexedAccess`, and a typed project registers its schema.
849
+ */
850
+ type RefChildren<T, B extends boolean> = unknown extends T ? any : NonNullable<T> extends readonly unknown[] ? unknown : NonNullable<T> extends object ? {
851
+ readonly [K in keyof NonNullable<T> & string]-?: Ref<NonNullable<T>[K], B>;
852
+ } : unknown;
853
+ /**
854
+ * A reference to a value of type `T` when the step runs: hand it to a step's input, interpolate it
855
+ * with {@link tpl}, or test it (`.eq(…)`, `.in([…])`, `.exists()`) for a condition. A field named like
856
+ * a test (`eq`, `in`) is not reachable as a path. `B`: whether `becomes` is offered (a `where`).
857
+ */
858
+ type Ref<T, B extends boolean = false> = {
859
+ readonly [REF_VALUE]: T;
860
+ } & ConditionTests<T> & (B extends true ? BecomesTest<T> : unknown) & RefChildren<T, B>;
861
+ /** A reference whose value is a `T`: what a step's input takes where it takes a `T`. */
862
+ type RefTo<T> = {
863
+ readonly [REF_VALUE]: T;
864
+ };
865
+ /** Where a template's parts ride at run time. */
866
+ declare const TEMPLATE: unique symbol;
867
+ /** Text written with {@link tpl}: literal parts and references, rendered when the step runs. */
868
+ interface AutomationTemplate {
869
+ readonly [TEMPLATE]: ReadonlyArray<string | AutomationRef>;
870
+ }
871
+ /** What a step's input takes where its Field takes a `T`. */
872
+ type Accepts<T> = T | RefTo<T> | (string extends NonNullable<T> ? AutomationTemplate : never) | AcceptsWithin<NonNullable<T>>;
873
+ /**
874
+ * A list or object a step's input takes, references at any depth. Distributed over each member of
875
+ * `T`, so a union — `AutomationJson`, a webhook's body — takes a list or an object holding
876
+ * references, as one member alone does.
877
+ */
878
+ type AcceptsWithin<T> = T extends ReadonlyArray<infer Member> ? ReadonlyArray<Accepts<Member>> : T extends object ? {
879
+ readonly [K in keyof T]: Accepts<T[K]>;
880
+ } : never;
881
+ /** A step's input: each key as its Field types it, a value or a reference to one. */
882
+ type StepInput<V> = {
883
+ readonly [K in keyof V]: Accepts<V[K]>;
884
+ };
885
+ /**
886
+ * Text with references in it, rendered when the step runs.
887
+ *
888
+ * @example
889
+ * tpl`Order ${event.record.number} held: ${fraud.output.reason}`
890
+ */
891
+ declare function tpl(strings: TemplateStringsArray, ...values: ReadonlyArray<RefTo<unknown> | ConditionScalar>): AutomationTemplate;
892
+ /** Every test holds. */
893
+ declare function and(...conditions: readonly ConditionTree[]): ConditionTree;
894
+ declare function and(...conditions: readonly AutomationWhere[]): AutomationWhere;
895
+ /** Some test holds. */
896
+ declare function or(...conditions: readonly ConditionTree[]): ConditionTree;
897
+ declare function or(...conditions: readonly AutomationWhere[]): AutomationWhere;
898
+ /** The test does not hold. */
899
+ declare function not(condition: ConditionTree): ConditionTree;
900
+ declare function not(condition: AutomationWhere): AutomationWhere;
901
+ /** Where a trigger's event type rides, for the compiler alone. */
902
+ declare const EVENT_TYPE: unique symbol;
903
+ /** A trigger, as `on` takes it: its data, and the type of the event it hands `steps`. */
904
+ interface AutomationTriggerHandle<E> {
905
+ readonly trigger: AutomationTrigger;
906
+ readonly [EVENT_TYPE]?: E;
907
+ }
908
+ /** The Record type names a schema declares, or any name without one. */
909
+ type RecordTypeNameIn$1<S> = S extends AnySchemaDefinition ? SchemaRecordTypeNames<S> : string;
910
+ type ContentTypeNameIn<S> = S extends AnySchemaDefinition ? SchemaContentTypeNames<S> : string;
911
+ /** An entry's values, of one content type, typed by the registered schema when there is one. */
912
+ type EntryValuesIn<S, K> = S extends AnySchemaDefinition ? Omit<SchemaEntry<S, K & SchemaContentTypeNames<S>>, '_metadata'> : Record<string, unknown>;
913
+ /** A Record of one Record type, typed by the registered schema when there is one. */
914
+ type RecordIn$1<S, K> = S extends AnySchemaDefinition ? InferRecordType<NonNullable<S['recordTypes']>[K & keyof NonNullable<S['recordTypes']>]> : BaseRecord;
915
+ /** A Record event: the Record as the write left it, and the names of the fields it changed. */
916
+ interface RecordEvent<R> {
917
+ readonly record: R;
918
+ readonly changed: readonly string[];
919
+ }
920
+ /** An entry event: the entry as the write left it, and the names of the fields it changed. */
921
+ interface EntryEvent {
922
+ readonly entry: unknown;
923
+ readonly changed: readonly string[];
924
+ }
925
+ /** One field's `where`: its value, or the list grammar's operators and `becomes`. */
926
+ type FieldWhere<T> = ComparedWith<T> | ({
927
+ readonly becomes?: ComparedWith<T>;
928
+ } & {
929
+ readonly [Op in 'eq' | 'ne' | 'gt' | 'gte' | 'lt' | 'lte']?: ComparedWith<T>;
930
+ } & {
931
+ readonly in?: ReadonlyArray<ComparedWith<T>>;
932
+ readonly nin?: ReadonlyArray<ComparedWith<T>>;
933
+ readonly exists?: boolean;
934
+ });
935
+ /**
936
+ * A trigger's `where`: field by field over the item — `{ status: { becomes: 'paid' } }`, every field
937
+ * holding — or a condition over the event, written with `becomes` and the list grammar.
938
+ */
939
+ type TriggerWhere<E, Item> = {
940
+ readonly [F in keyof Item & string]?: FieldWhere<Item[F]>;
941
+ } | ((event: Ref<E, true>) => AutomationWhere);
942
+ /** Where a type's item rides at run time: `records.order` says it is the Record type `order`. */
943
+ declare const ITEM: unique symbol;
944
+ /** Where one item by uid rides at run time: what `<type>.ref(uid)` returns. */
945
+ declare const ITEM_REF: unique symbol;
946
+ /** Where a type's or an item's value type rides, for the compiler alone. */
947
+ declare const ITEM_TYPE: unique symbol;
948
+ /** What a type, or one item of it, holds: a Record of `R`, or an entry with the values `V`. */
949
+ type ItemType<R = unknown, V = unknown> = {
950
+ readonly kind: 'record';
951
+ readonly record: R;
952
+ } | {
953
+ readonly kind: 'entry';
954
+ readonly values: V;
955
+ };
956
+ /** The type a Record or entry type's handle names, as data. */
957
+ type ItemTypeOf = {
958
+ readonly kind: 'record';
959
+ readonly recordType: string;
960
+ } | {
961
+ readonly kind: 'entry';
962
+ readonly contentType: string;
963
+ };
964
+ /**
965
+ * A uid: a reference to one — `event.record._metadata.id`, a reference field, an earlier step's
966
+ * output — or a uid written out.
967
+ */
968
+ type UidRef = string | RefTo<string | null | undefined>;
969
+ /**
970
+ * One Record or entry, by its uid, typed by its type: `records.order.ref(event.record._metadata.id)`.
971
+ * `step.lookup` reads one, and a run may be declared to be about one (REL-4152).
972
+ */
973
+ interface AutomationItemHandle<I extends ItemType = ItemType> {
974
+ readonly [ITEM_REF]: AutomationItemRef;
975
+ readonly [ITEM_TYPE]?: I;
976
+ }
977
+ /** The triggers of one Record type. */
978
+ interface RecordTriggers<R> {
979
+ /** A typed reference to one Record of this type, by its uid. */
980
+ ref(uid: UidRef): AutomationItemHandle<{
981
+ readonly kind: 'record';
982
+ readonly record: R;
983
+ }>;
984
+ readonly [ITEM]: ItemTypeOf;
985
+ readonly [ITEM_TYPE]?: {
986
+ readonly kind: 'record';
987
+ readonly record: R;
988
+ };
989
+ created(options?: {
990
+ where?: TriggerWhere<RecordEvent<R>, R>;
991
+ }): AutomationTriggerHandle<RecordEvent<R>>;
992
+ updated(options?: {
993
+ where?: TriggerWhere<RecordEvent<R>, R>;
994
+ }): AutomationTriggerHandle<RecordEvent<R>>;
995
+ deleted(options?: {
996
+ where?: TriggerWhere<RecordEvent<R>, R>;
997
+ }): AutomationTriggerHandle<RecordEvent<R>>;
998
+ expired(options?: {
999
+ where?: TriggerWhere<RecordEvent<R>, R>;
1000
+ }): AutomationTriggerHandle<RecordEvent<R>>;
1001
+ }
1002
+ /** The triggers of one content type, whose entries hold the values `V`. */
1003
+ type EntryTriggers<V = Record<string, unknown>> = {
1004
+ readonly [A in AutomationEntryAction]: (options?: {
1005
+ where?: TriggerWhere<EntryEvent, Record<string, unknown>>;
1006
+ }) => AutomationTriggerHandle<EntryEvent>;
1007
+ } & {
1008
+ /** A typed reference to one entry of this type, by its uid. */
1009
+ ref(uid: UidRef): AutomationItemHandle<{
1010
+ readonly kind: 'entry';
1011
+ readonly values: V;
1012
+ }>;
1013
+ readonly [ITEM]: ItemTypeOf;
1014
+ readonly [ITEM_TYPE]?: {
1015
+ readonly kind: 'entry';
1016
+ readonly values: V;
1017
+ };
1018
+ };
1019
+ /** Each Record type's triggers, by name: typed by the registered schema, any name without one. */
1020
+ type RecordTriggerTable = string extends RecordTypeNameIn$1<RegisteredSchema> ? {
1021
+ readonly [recordType: string]: any;
1022
+ } : {
1023
+ readonly [K in RecordTypeNameIn$1<RegisteredSchema>]: RecordTriggers<RecordIn$1<RegisteredSchema, K>>;
1024
+ };
1025
+ /** Each content type's triggers, by name. */
1026
+ type EntryTriggerTable = string extends ContentTypeNameIn<RegisteredSchema> ? {
1027
+ readonly [contentType: string]: any;
1028
+ } : {
1029
+ readonly [K in ContentTypeNameIn<RegisteredSchema>]: EntryTriggers<EntryValuesIn<RegisteredSchema, K>>;
1030
+ };
1031
+ /**
1032
+ * `records.<type>.created | updated | deleted | expired`: a Record event of one Record type.
1033
+ * `records.<type>` itself names the type to `step.lookup`, and `records.<type>.ref(uid)` one Record.
1034
+ */
1035
+ declare const records: RecordTriggerTable;
1036
+ /**
1037
+ * `entries.<type>.created | updated | published | …`: an entry event of one content type.
1038
+ * `entries.<type>` itself names the type to `step.lookup`, and `entries.<type>.ref(uid)` one entry.
1039
+ */
1040
+ declare const entries: EntryTriggerTable;
1041
+ /**
1042
+ * What a run started by an entry of any content type reads under `event` (REL-3861): what every
1043
+ * entry event holds whatever its type — the entry's uid and content type, and the names of the
1044
+ * fields the write changed. No field of the entry: no one content type declares it.
1045
+ */
1046
+ interface AnyEntryEvent {
1047
+ readonly entry: {
1048
+ readonly _metadata: {
1049
+ readonly id: string;
1050
+ readonly type: string;
1051
+ };
1052
+ };
1053
+ readonly changed: readonly string[];
1054
+ }
1055
+ /** The triggers of an entry of any content type, one per entry action. */
1056
+ type AnyEntryTriggers = {
1057
+ readonly [A in AutomationEntryAction]: (options?: {
1058
+ readonly where?: (event: Ref<AnyEntryEvent>) => ConditionTree;
1059
+ }) => AutomationTriggerHandle<AnyEntryEvent>;
1060
+ };
1061
+ /**
1062
+ * `anyEntry.created | updated | published | …`: an entry event of any content type — what a site
1063
+ * that revalidates every page it renders listens to. `where` is a function of the event, which holds
1064
+ * the entry's `_metadata.id` and `_metadata.type` and `changed` alone, and no `becomes`:
1065
+ * `anyEntry.published({ where: event => event.entry._metadata.type.in(['post']) })`.
1066
+ */
1067
+ declare const anyEntry: AnyEntryTriggers;
1068
+ /** A clock: `schedule.cron('0 8 * * 1', { timezone: 'Europe/Berlin' })`, on Production only. */
1069
+ declare const schedule: {
1070
+ cron(cron: string, options: {
1071
+ readonly timezone: string;
1072
+ }): AutomationTriggerHandle<Record<string, never>>;
1073
+ };
1074
+ /**
1075
+ * What a run started by another run's failure reads under `event` (REL-4152): the failed run, its
1076
+ * Automation and Branch, what it was about, the step it failed at, the error, and the event that set
1077
+ * it off by type and id. Never that event's body: a Record's values stay on the failed run.
1078
+ */
1079
+ interface RunFailedEvent {
1080
+ readonly runId: string;
1081
+ /** The Automation whose run failed. */
1082
+ readonly automation: string;
1083
+ readonly branchId: string;
1084
+ /** The Record or entry the failed run was about — and so the one this run is about. */
1085
+ readonly about: {
1086
+ readonly kind: 'record' | 'entry';
1087
+ readonly type: string;
1088
+ readonly uid: string;
1089
+ } | null;
1090
+ readonly step: {
1091
+ readonly name: string;
1092
+ /** `function`, `agent`, `action`, `if` or `wait`. */
1093
+ readonly kind: string;
1094
+ readonly attempts: number;
1095
+ };
1096
+ readonly error: {
1097
+ readonly code: string;
1098
+ readonly message: string;
1099
+ };
1100
+ readonly trigger: {
1101
+ readonly eventType: string;
1102
+ readonly eventId: string;
1103
+ };
1104
+ }
1105
+ /** One value's `where` with no `becomes`: the value, or the list grammar's operators. */
1106
+ type ValueWhere<T> = ComparedWith<T> | ({
1107
+ readonly [Op in 'eq' | 'ne' | 'gt' | 'gte' | 'lt' | 'lte']?: ComparedWith<T>;
1108
+ } & {
1109
+ readonly in?: ReadonlyArray<ComparedWith<T>>;
1110
+ readonly nin?: ReadonlyArray<ComparedWith<T>>;
1111
+ readonly exists?: boolean;
1112
+ });
1113
+ /** Where each name a failed run's `where` shorthand filters on sits under `event`. */
1114
+ declare const RUN_FAILED_PATHS: {
1115
+ readonly automation: readonly ["automation"];
1116
+ readonly step: readonly ["step", "name"];
1117
+ readonly code: readonly ["error", "code"];
1118
+ };
1119
+ /**
1120
+ * A failed run's `where`: by its Automation, the step it failed at and the error's code —
1121
+ * `{ automation: 'order-paid', code: { in: ['send-refused'] } }`, every one holding — or a condition
1122
+ * over {@link RunFailedEvent}. No `becomes`: a failure changes no field.
1123
+ */
1124
+ type RunFailedWhere = {
1125
+ readonly [K in keyof typeof RUN_FAILED_PATHS]?: ValueWhere<string>;
1126
+ } | ((event: Ref<RunFailedEvent>) => ConditionTree);
1127
+ /**
1128
+ * Another Automation's run failed: `automations.runFailed({ where })`, on the failed run's Branch.
1129
+ * An Automation never hears its own failure, and a run a failure started never announces its own.
1130
+ */
1131
+ declare const automations: {
1132
+ runFailed(options?: {
1133
+ readonly where?: RunFailedWhere;
1134
+ }): AutomationTriggerHandle<RunFailedEvent>;
1135
+ };
1136
+ /**
1137
+ * What a run started by an Extension's Trigger reads under `event`: what the third party sent, as the
1138
+ * Trigger's payload fields declare it (`event.payload.data.object.id`).
1139
+ */
1140
+ interface ExtensionTriggerEvent<P> {
1141
+ readonly payload: P;
1142
+ }
1143
+ /**
1144
+ * One of an Extension's Triggers, as its handle in `relyio/extensions.ts` builds it: the Extension by
1145
+ * slug, the Trigger by its slug in the catalog, and `where`, a plain condition over the event — no
1146
+ * `becomes`, since the third party's event names no field RelyIO saw change. Call the handle
1147
+ * (`stripe.paymentIntentSucceeded({ where })`) rather than this.
1148
+ */
1149
+ declare function extensionTrigger<P>(extension: string, trigger: string, options?: {
1150
+ readonly where?: (event: Ref<ExtensionTriggerEvent<P>>) => ConditionTree;
1151
+ }): AutomationTriggerHandle<ExtensionTriggerEvent<P>>;
1152
+ /**
1153
+ * Every trigger, under one name: `on.records.order.updated(…)`, as `records.order.updated(…)`. An
1154
+ * Extension's Trigger is its handle's: `on: stripe.paymentIntentSucceeded()`.
1155
+ */
1156
+ declare const on: {
1157
+ readonly records: {
1158
+ readonly [recordType: string]: any;
1159
+ };
1160
+ readonly entries: {
1161
+ readonly [contentType: string]: any;
1162
+ };
1163
+ readonly anyEntry: AnyEntryTriggers;
1164
+ readonly schedule: {
1165
+ cron(cron: string, options: {
1166
+ readonly timezone: string;
1167
+ }): AutomationTriggerHandle<Record<string, never>>;
1168
+ };
1169
+ readonly automations: {
1170
+ runFailed(options?: {
1171
+ readonly where?: RunFailedWhere;
1172
+ }): AutomationTriggerHandle<RunFailedEvent>;
1173
+ };
1174
+ };
1175
+ /** An Extension's Action, as `relyio/extensions.ts` hands one out (REL-3846): input and output typed. */
1176
+ interface AutomationAction<I = Record<string, unknown>, O = Record<string, unknown>, E extends string = string, N extends string = string> {
1177
+ readonly _tag: 'action';
1178
+ /** The Extension, by slug: a handle from `relyio/extensions.ts` carries it as a literal type. */
1179
+ readonly extension: E;
1180
+ /**
1181
+ * The Action, by its slug in the Extension catalog: what the step stores, and what a Function's
1182
+ * `actions.<extension>.<action>` names it by (REL-3856).
1183
+ */
1184
+ readonly action: N;
1185
+ /** Never present at run time: the Action's input and output, as types. */
1186
+ readonly [REF_VALUE]?: {
1187
+ readonly input: I;
1188
+ readonly output: O;
1189
+ };
1190
+ }
1191
+ /** A step as `steps` builds it: its data, a Function step's Function still the imported value. */
1192
+ type AutomationStepDraft = (Omit<Extract<AutomationStep, {
1193
+ kind: 'function';
1194
+ }>, 'function'> & {
1195
+ readonly function: FunctionDefinition;
1196
+ }) | Exclude<AutomationStep, {
1197
+ kind: 'function' | 'if';
1198
+ }> | (Omit<Extract<AutomationStep, {
1199
+ kind: 'if';
1200
+ }>, 'then' | 'else'> & {
1201
+ readonly then: readonly AutomationStepDraft[];
1202
+ readonly else: readonly AutomationStepDraft[];
1203
+ });
1204
+ /** Where a step handle's data rides at run time. */
1205
+ declare const STEP: unique symbol;
1206
+ /** A step, as `steps` returns it and an If's branches list it. */
1207
+ interface StepHandle {
1208
+ readonly name: string;
1209
+ readonly [STEP]: AutomationStepDraft;
1210
+ }
1211
+ /** A step whose output later steps read: `<step>.output.<field>`. */
1212
+ interface StepWithOutput<O> extends StepHandle {
1213
+ readonly output: Ref<O>;
1214
+ }
1215
+ /** A Field set's values, as a Function's and an Agent step's output Fields declare them. */
1216
+ type ValuesOf$1<F extends KeyedMembers> = FieldValues<DeclaredFields<{
1217
+ readonly fields: readonly KeyedMembersOf<F>[];
1218
+ }>>;
1219
+ /** What `step.agent` takes. */
1220
+ interface AgentStepConfig<O extends KeyedMembers> {
1221
+ /** The agent, built-in or the project's, by id or slug; RelyIO's default when left out. */
1222
+ readonly agent?: string;
1223
+ readonly instructions: string | AutomationTemplate;
1224
+ /** What the agent is handed to read, by key. */
1225
+ readonly context?: {
1226
+ readonly [key: string]: unknown;
1227
+ };
1228
+ /** The Fields its answer must fill: a mismatch fails the step. */
1229
+ readonly output: O;
1230
+ }
1231
+ /**
1232
+ * What a proposing `step.agent` takes besides (REL-4154): the types it may propose a change to —
1233
+ * `entries.productPage`, `records.order`. It is offered ai-gateway's proposal tools for those types
1234
+ * alone and makes at most one proposal, which nothing applies until an Ask approves it.
1235
+ */
1236
+ interface ProposingAgentStepConfig<O extends KeyedMembers> extends AgentStepConfig<O> {
1237
+ readonly propose: readonly [LookupType, ...LookupType[]];
1238
+ }
1239
+ /** The proposal a proposing Agent step made: what an Ask's `approve` names. */
1240
+ interface AutomationProposal {
1241
+ readonly id: string;
1242
+ /** The proposal's kind: `field_update`, `operation`, `record_update`, … */
1243
+ readonly kind: string;
1244
+ /** How many units it changes: entries, or Records. */
1245
+ readonly counts: {
1246
+ readonly units: number;
1247
+ };
1248
+ }
1249
+ /** A proposing Agent step's output: its Fields, and the proposal — `null` when it proposed none. */
1250
+ type ProposingOutput<V> = V & {
1251
+ readonly proposal: AutomationProposal | null;
1252
+ };
1253
+ /**
1254
+ * What a Look-up of a Record answers: `found`, and the Record as it stands now when it was. Read
1255
+ * `found` before the Record: an item deleted, never there, or a reference with no value is
1256
+ * `{ found: false }`.
1257
+ */
1258
+ interface LookedUpRecord<R> {
1259
+ readonly found: boolean;
1260
+ readonly record?: R;
1261
+ }
1262
+ /** The locale codes the registered schema declares, or any code without one. */
1263
+ type LocaleCodeIn<S> = S extends AnySchemaDefinition ? SchemaLocaleCodes<S> : string;
1264
+ /** An entry as a Look-up reads it: its shared values, and each locale it admits with its own. */
1265
+ interface LookedUpEntryItem<V> {
1266
+ readonly _metadata: {
1267
+ readonly id: string;
1268
+ readonly type: string;
1269
+ };
1270
+ /** The values of the fields that are not localized. */
1271
+ readonly fields: Partial<V>;
1272
+ /** Each locale the state admits: its publication status and its localized values. */
1273
+ readonly locales: {
1274
+ readonly [L in LocaleCodeIn<RegisteredSchema>]?: {
1275
+ readonly status: 'published' | 'draft';
1276
+ readonly fields: Partial<V>;
1277
+ };
1278
+ };
1279
+ }
1280
+ /** What a Look-up of an entry answers: `found`, and the entry when it was. */
1281
+ interface LookedUpEntry<V> {
1282
+ readonly found: boolean;
1283
+ readonly entry?: LookedUpEntryItem<V>;
1284
+ }
1285
+ type IsAny<T> = 0 extends 1 & T ? true : false;
1286
+ /** What a Look-up of the type or item `H` answers. */
1287
+ type LookedUp<H> = IsAny<H> extends true ? {
1288
+ readonly found: boolean;
1289
+ readonly record?: any;
1290
+ readonly entry?: any;
1291
+ } : H extends {
1292
+ readonly [ITEM_TYPE]?: infer I;
1293
+ } ? I extends {
1294
+ readonly kind: 'record';
1295
+ readonly record: infer R;
1296
+ } ? LookedUpRecord<R> : I extends {
1297
+ readonly kind: 'entry';
1298
+ readonly values: infer V;
1299
+ } ? LookedUpEntry<V> : never : never;
1300
+ /** What a Look-up of `H` takes beside it: an entry's `state`; a Record has no draft. */
1301
+ type LookupOptions<H> = IsAny<H> extends true ? {
1302
+ readonly state?: AutomationLookupState;
1303
+ } : H extends {
1304
+ readonly [ITEM_TYPE]?: {
1305
+ readonly kind: 'entry';
1306
+ };
1307
+ } ? {
1308
+ /** `published` (the default): what readers see. `draft`: the editor's view. */
1309
+ readonly state?: AutomationLookupState;
1310
+ } : never;
1311
+ /** A Record or entry type, as `step.lookup` takes it: `records.order`, `entries.product`. */
1312
+ type LookupType = {
1313
+ readonly [ITEM]: ItemTypeOf;
1314
+ };
1315
+ /** What `step.ask` takes. */
1316
+ interface AskStepConfig<C extends string> {
1317
+ /**
1318
+ * The roles asked: `viewer`, `editor`, `admin` or a custom role the schema declares, as a
1319
+ * workflow stage's `allowedRoles`. Editors and Admins when left out. People only, never an agent.
1320
+ */
1321
+ readonly to?: readonly string[];
1322
+ /** The question, rendered when the step runs: text, or {@link tpl} over the event and earlier outputs. */
1323
+ readonly question: string | AutomationTemplate;
1324
+ /** The answers a person may give. */
1325
+ readonly choices: readonly C[];
1326
+ /** How long the question stays open, up to 30 days; 7 days when left out. */
1327
+ readonly timeout?: AutomationDuration;
1328
+ /** The answer the step continues with once the timeout falls; without one, the step fails then. */
1329
+ readonly onTimeout?: NoInfer<C>;
1330
+ /** The answer a pull request's Preview run and an editor's test give at once; the first choice by default. */
1331
+ readonly sample?: NoInfer<C>;
1332
+ }
1333
+ /** What an Ask answers later steps: the choice made, and by whom, or that the timeout fell. */
1334
+ interface AskOutput<C extends string> {
1335
+ readonly answer: C;
1336
+ /** What the person wrote beside the choice, at most 2,000 characters. */
1337
+ readonly note?: string;
1338
+ /** The user who answered; absent on a timeout and a Preview's sample. */
1339
+ readonly answeredBy?: string;
1340
+ /** When, as ISO-8601; absent on a timeout and a Preview's sample. */
1341
+ readonly answeredAt?: string;
1342
+ /** Whether the step continued with `onTimeout` because nobody answered in time. */
1343
+ readonly timedOut: boolean;
1344
+ }
1345
+ /** An approving Ask's choices, in order: a Preview's sample is the first. */
1346
+ declare const APPROVE_CHOICES: readonly ["approve", "reject"];
1347
+ type ApproveChoice = (typeof APPROVE_CHOICES)[number];
1348
+ /**
1349
+ * What an approving `step.ask` takes (REL-4154): `approve` names an earlier Agent step's proposal,
1350
+ * and the choices are `approve` and `reject`. Only members holding `to`'s roles and the authority
1351
+ * the proposal's apply needs are asked or may answer. A timeout never approves.
1352
+ */
1353
+ interface ApproveStepConfig {
1354
+ readonly to?: readonly string[];
1355
+ readonly question: string | AutomationTemplate;
1356
+ /** The proposal: `<agent step>.output.proposal`. */
1357
+ readonly approve: Ref<AutomationProposal | null>;
1358
+ readonly timeout?: AutomationDuration;
1359
+ /** What the step continues with once the timeout falls: only ever `reject`. */
1360
+ readonly onTimeout?: 'reject';
1361
+ /** What a Preview answers at once: `approve` by default. */
1362
+ readonly sample?: ApproveChoice;
1363
+ }
1364
+ /** What became of an approved — or not — proposal, by its units. */
1365
+ interface ApproveCounts {
1366
+ readonly applied: number;
1367
+ readonly stale: number;
1368
+ readonly failed: number;
1369
+ }
1370
+ /**
1371
+ * An approving Ask's output: the answer, and what became of the proposal — `applied`; `rejected`,
1372
+ * answered so or timed out; `stale`, a Record that moved since the agent read it; or `failed`.
1373
+ */
1374
+ interface ApproveOutput extends AskOutput<ApproveChoice> {
1375
+ readonly outcome: 'applied' | 'rejected' | 'stale' | 'failed';
1376
+ readonly counts: ApproveCounts;
1377
+ }
1378
+ /** What `steps` is handed to build steps with. */
1379
+ interface StepBuilder {
1380
+ /** Run one of the project's Functions, taken by import, with its input. */
1381
+ fn<F extends FunctionDefinition>(name: string, fn: F, input: StepInput<FunctionInputOf<F>>): StepWithOutput<FunctionOutputOf<F>>;
1382
+ /**
1383
+ * Ask an agent to read, answer the declared Fields and propose one change to the types `propose`
1384
+ * names. Nothing it proposes is written until an Ask approves it (`step.ask(…, { approve })`).
1385
+ */
1386
+ agent<const O extends KeyedMembers>(name: string, config: ProposingAgentStepConfig<O>): StepWithOutput<ProposingOutput<ValuesOf$1<O>>>;
1387
+ /** Ask an agent to read and answer the declared Fields. It reads; it never writes or sends. */
1388
+ agent<const O extends KeyedMembers>(name: string, config: AgentStepConfig<O>): StepWithOutput<ValuesOf$1<O>>;
1389
+ /** Send through an Extension's Action, from the events worker; Recorded in a Preview without its Connection. */
1390
+ action<I, O>(name: string, action: AutomationAction<I, O>, input: StepInput<I>): StepWithOutput<O>;
1391
+ /**
1392
+ * Read one Record or entry again, as it stands now, by its uid — `found`, or not: an item deleted,
1393
+ * never there, or a reference with no value is `{ found: false }`, never a failed step. An entry
1394
+ * reads `published` by default, or the editor's view with `{ state: 'draft' }`. One item, never a
1395
+ * query; unmetered.
1396
+ *
1397
+ * @example
1398
+ * const recheck = step.lookup('recheck', records.order, event.record._metadata.id)
1399
+ * step.if('still-unpaid', recheck.output.record.status.ne('paid'), { then: [remind] })
1400
+ */
1401
+ lookup<H extends LookupType>(name: string, type: H, uid: UidRef, options?: LookupOptions<H>): StepWithOutput<LookedUp<H>>;
1402
+ /** Read one item, named by `<type>.ref(uid)`, again as it stands now. */
1403
+ lookup<H extends AutomationItemHandle>(name: string, item: H, options?: LookupOptions<H>): StepWithOutput<LookedUp<H>>;
1404
+ /** Then one step list or the other, by a condition over the event and earlier outputs. */
1405
+ if(name: string, condition: ConditionTree, branches: {
1406
+ readonly then?: readonly StepHandle[];
1407
+ readonly else?: readonly StepHandle[];
1408
+ }): StepHandle;
1409
+ /** Hold the run, up to 30 days: `'90m'`, `'2h'`, `'3d'`. */
1410
+ wait(name: string, duration: AutomationDuration): StepHandle;
1411
+ /**
1412
+ * Ask the people holding `to`'s roles, through the Studio inbox and an email, and continue with
1413
+ * the first answer — one of `choices` — or with `onTimeout` once `timeout` falls.
1414
+ */
1415
+ ask<const C extends string>(name: string, config: AskStepConfig<C>): StepWithOutput<AskOutput<C>>;
1416
+ /**
1417
+ * Ask whether to apply an Agent step's proposal: `approve` applies it through ai-gateway's apply
1418
+ * path as the person who answered, and the run continues once it is applied, with the outcome.
1419
+ *
1420
+ * @example
1421
+ * const approval = step.ask('approve', { to: ['editor'], question: 'Publish the page?', approve: draft.output.proposal, onTimeout: 'reject' })
1422
+ * step.if('moved', approval.output.outcome.eq('stale'), { then: [tellOps] })
1423
+ */
1424
+ ask(name: string, config: ApproveStepConfig): StepWithOutput<ApproveOutput>;
1425
+ }
1426
+ /** What `defineAutomation` takes. */
1427
+ interface AutomationConfig<E> {
1428
+ readonly description?: string;
1429
+ /**
1430
+ * The trigger: `records.<type>.<action>(…)`, `entries.<type>.<action>(…)`, `anyEntry.<action>(…)`,
1431
+ * `schedule.cron(…)`,
1432
+ * `automations.runFailed(…)`, or an Extension's Trigger from `relyio/extensions.ts`.
1433
+ */
1434
+ readonly on: AutomationTriggerHandle<E>;
1435
+ /**
1436
+ * What each run is about, so the runs about one order are found across Automations: one Record or
1437
+ * entry, by a uid the event names — `({ event }) =>
1438
+ * records.order.ref(event.payload.data.object.metadata.orderId)`. Left out, a run is about what its
1439
+ * trigger names: the Record or entry of a RelyIO event, the outside object of an Extension's
1440
+ * Trigger (Stripe's payment intent), the failed run's item, or nothing for a clock.
1441
+ */
1442
+ readonly about?: (context: {
1443
+ readonly event: Ref<E>;
1444
+ }) => AutomationItemHandle;
1445
+ /**
1446
+ * Called once, when the Manifest loads, with a reference to the event and the step builder;
1447
+ * returns the steps in the order a run takes them.
1448
+ */
1449
+ readonly steps: (context: {
1450
+ readonly event: Ref<E>;
1451
+ readonly step: StepBuilder;
1452
+ }) => readonly StepHandle[];
1453
+ }
1454
+ /**
1455
+ * What an Automation file default-exports: its definition as data, each Function step still naming
1456
+ * the Function it imported. The CLI names those by their files ({@link resolveAutomation}).
1457
+ */
1458
+ interface AutomationFile {
1459
+ readonly _tag: 'automation';
1460
+ readonly description?: string;
1461
+ readonly on: AutomationTrigger;
1462
+ readonly about?: AutomationItemRef;
1463
+ readonly steps: readonly AutomationStepDraft[];
1464
+ }
1465
+ /**
1466
+ * Declare an Automation. The file's name is its name; `relyio deploy` reads it into the Manifest,
1467
+ * plans it and ships it with the rest.
1468
+ */
1469
+ declare function defineAutomation<E>(config: AutomationConfig<E>): AutomationFile;
1470
+ /** Whether `value` is what `defineAutomation` returns: how the CLI reads an Automation file. */
1471
+ declare function isAutomationFile(value: unknown): value is AutomationFile;
1472
+ /**
1473
+ * The Automation as the Manifest carries it: each Function step's Function named by `nameOf` — the
1474
+ * CLI's, which knows which file under `relyio/functions/` exported it. A Function no file exported
1475
+ * is refused by name.
1476
+ */
1477
+ declare function resolveAutomation(file: AutomationFile, nameOf: (fn: FunctionDefinition) => string | undefined): AutomationDefinition;
1478
+
1479
+ /**
1480
+ * `@relyio/sdk/extensions` — the Extensions a Project uses (spec 3, REL-3846; ADR 0019).
1481
+ *
1482
+ * One file, `relyio/extensions.ts`, names each Extension the Project uses and its settings — set once
1483
+ * for the Project, never per Environment — and hands out a typed handle for each. An Automation
1484
+ * imports the handle: its Triggers start an Automation (`on: vercel.deploymentSucceeded()`), and its
1485
+ * Actions are what `step.action` sends through, their input and output typed by the Extension.
1486
+ *
1487
+ * @example
1488
+ * // relyio/extensions.ts
1489
+ * import { defineExtensions } from '@relyio/sdk/extensions'
1490
+ *
1491
+ * const extensions = defineExtensions({
1492
+ * slack: { defaultChannel: '#orders' },
1493
+ * vercel: {},
1494
+ * })
1495
+ * export const { slack, vercel } = extensions
1496
+ * export default extensions
1497
+ *
1498
+ * **Never a secret here.** A Connection — an API key, a signing secret, a webhook URL — is made in
1499
+ * the Dashboard, once per Environment, and sealed. A setting is text anyone reading the repository
1500
+ * may see. Declaring an Extension connects nothing: `relyio plan` says which still need connecting
1501
+ * in Production, and which Automations will deploy paused there until they are. Preview never pauses:
1502
+ * an Action with no Preview Connection is Recorded with its sample result.
1503
+ *
1504
+ * **Typed.** Each Extension's settings, Actions and Triggers are RelyIO's own declaration of it
1505
+ * (`@relyio/api-contract`'s `EXTENSION_SURFACES`, which the Extension catalog delivers): an Extension
1506
+ * RelyIO does not have, a setting it does not declare, or a value its setting does not take — a
1507
+ * Slack channel without its `#` — is a type error, and the plan refuses it again for a Manifest read
1508
+ * as data.
1509
+ */
1510
+
1511
+ /** One Extension a Project uses: its settings, by key. */
1512
+ interface ExtensionUse {
1513
+ readonly settings: Readonly<Record<string, string>>;
1514
+ }
1515
+ /** What `relyio/extensions.ts` declares, as data: every Extension the Project uses, by slug. */
1516
+ type ExtensionsDeclaration = Readonly<Record<string, ExtensionUse>>;
1517
+ /** Every Extension a Project may declare, and what its code may name of each. */
1518
+ type Surfaces = ExtensionSurfaces;
1519
+ /** The value one setting takes: one of its options, text starting with its prefix, or text. */
1520
+ type SettingValue<F extends ExtensionSettingField> = F extends {
1521
+ readonly type: 'select';
1522
+ readonly options: ReadonlyArray<infer Option>;
1523
+ } ? Option : F extends {
1524
+ readonly type: 'text';
1525
+ readonly prefix: infer Prefix extends string;
1526
+ } ? `${Prefix}${string}` : string;
1527
+ type Flatten<T> = {
1528
+ readonly [K in keyof T]: T[K];
1529
+ };
1530
+ /** The settings an Extension takes: its required ones required, the rest optional, nothing else. */
1531
+ type SettingsFrom<F extends ExtensionSettingField> = Flatten<{
1532
+ readonly [Field in F as Field extends {
1533
+ readonly required: true;
1534
+ } ? Field['key'] : never]: SettingValue<Field>;
1535
+ } & {
1536
+ readonly [Field in F as Field extends {
1537
+ readonly required: true;
1538
+ } ? never : Field['key']]?: SettingValue<Field>;
1539
+ }>;
1540
+ /**
1541
+ * The settings `defineExtensions` takes for Extension `S`. An Extension with none takes `{}` and
1542
+ * nothing in it.
1543
+ */
1544
+ type ExtensionSettings<S extends keyof Surfaces> = [
1545
+ Surfaces[S]['settings'][number]
1546
+ ] extends [never] ? Readonly<Record<string, never>> : SettingsFrom<Surfaces[S]['settings'][number]>;
1547
+ /** What `defineExtensions` takes: each Extension the Project uses, by slug, with its settings. */
1548
+ type ExtensionsConfig = {
1549
+ readonly [S in keyof Surfaces]?: ExtensionSettings<S>;
1550
+ };
1551
+ /** A value field's JSON type, as TypeScript reads it. */
1552
+ type JsonOf<T extends ExtensionValueField['type']> = T extends 'string' ? string : T extends 'number' ? number : T extends 'boolean' ? boolean : T extends 'array' ? readonly unknown[] : Readonly<Record<string, unknown>>;
1553
+ /** A dot-separated path's first segment. */
1554
+ type Head<P extends string> = P extends `${infer First}.${string}` ? First : P;
1555
+ /** The fields whose path starts with segment `K`. */
1556
+ type Under<F extends ExtensionValueField, K extends string> = F extends ExtensionValueField ? Head<F['path']> extends K ? F : never : never;
1557
+ /** The fields under `K`, their paths with `K.` taken off; never for the field at `K` itself. */
1558
+ type Rebased<F extends ExtensionValueField, K extends string> = F extends ExtensionValueField ? F['path'] extends `${K}.${infer Rest}` ? Omit<F, 'path'> & {
1559
+ readonly path: Rest;
1560
+ } : never : never;
1561
+ /** A field's value: its JSON type, and an object of strings for one declaring string values. */
1562
+ type FieldValueOf<F extends ExtensionValueField> = F extends {
1563
+ readonly values: 'string';
1564
+ } ? Readonly<Record<string, string>> : JsonOf<F['type']>;
1565
+ /** The value at `K`: the field there, or an object of the fields under it. */
1566
+ type ValueAt<F extends ExtensionValueField, K extends string> = [
1567
+ Extract<F, {
1568
+ readonly path: K;
1569
+ }>
1570
+ ] extends [never] ? ValuesOf<Rebased<F, K>> : FieldValueOf<Extract<F, {
1571
+ readonly path: K;
1572
+ }>>;
1573
+ /**
1574
+ * Value fields as an object: `payload.data.object.id` a `string` at `payload.data.object.id`. A
1575
+ * segment is required when a field under it is.
1576
+ */
1577
+ type ValuesOf<F extends ExtensionValueField> = Flatten<{
1578
+ readonly [K in Head<F['path']> as true extends Under<F, K>['required'] ? K : never]: ValueAt<F, K>;
1579
+ } & {
1580
+ readonly [K in Head<F['path']> as true extends Under<F, K>['required'] ? never : K]?: ValueAt<F, K>;
1581
+ }>;
1582
+ /** A Trigger's payload, as an object; one that declares nothing, `{}`. */
1583
+ type ValuesOfList<L extends readonly ExtensionValueField[]> = [L[number]] extends [never] ? Record<string, never> : ValuesOf<L[number]>;
1584
+ /** An action field's value, as TypeScript reads it (`@relyio/api-contract`'s `ACTION_FIELD_TYPES`). */
1585
+ type ActionValueOf<T extends ActionField['type']> = T extends 'text' | 'url' ? string : T extends 'number' ? number : T extends 'boolean' ? boolean : T extends 'headers' ? Readonly<Record<string, string>> : AutomationJson;
1586
+ /** An Action's input or output fields as an object: required ones required, the rest optional. */
1587
+ type ActionValuesOf<L extends readonly ActionField[]> = Flatten<{
1588
+ readonly [F in L[number] as F extends {
1589
+ readonly required: true;
1590
+ } ? F['key'] : never]: ActionValueOf<F['type']>;
1591
+ } & {
1592
+ readonly [F in L[number] as F extends {
1593
+ readonly required: true;
1594
+ } ? never : F['key']]?: ActionValueOf<F['type']>;
1595
+ }>;
1596
+ /** A Trigger's handle: call it for the Automation's `on`, with a `where` over its event if needed. */
1597
+ type ExtensionTriggerHandle<P> = (options?: {
1598
+ readonly where?: (event: Ref<ExtensionTriggerEvent<P>>) => ConditionTree;
1599
+ }) => AutomationTriggerHandle<ExtensionTriggerEvent<P>>;
1600
+ /**
1601
+ * The handle an Extension's declaration gives: each of its Actions and Triggers, by the name it gives
1602
+ * them, typed by their fields.
1603
+ */
1604
+ type HandleOf<X extends ExtensionSurface, S extends string = string> = {
1605
+ readonly [A in keyof X['actions']]: X['actions'][A] extends {
1606
+ readonly action: infer N extends string;
1607
+ readonly step: {
1608
+ readonly input: infer I extends readonly ActionField[];
1609
+ readonly output: infer O extends readonly ActionField[];
1610
+ };
1611
+ } ? AutomationAction<ActionValuesOf<I>, ActionValuesOf<O>, S, N> : never;
1612
+ } & {
1613
+ readonly [T in keyof X['triggers']]: X['triggers'][T] extends {
1614
+ readonly payload: infer P extends readonly ExtensionValueField[];
1615
+ } ? ExtensionTriggerHandle<ValuesOfList<P>> : never;
1616
+ };
1617
+ /**
1618
+ * A contributed field kind's options (spec 4, REL-3894): its base reference's, without `to`, which
1619
+ * the kind names, and never `localized` — a user is no translation.
1620
+ */
1621
+ type ContributedFieldOptions<TTo extends string, TMulti extends boolean, TRequired extends boolean> = Omit<ReferenceConfig<TTo, TMulti>, 'to'> & {
1622
+ readonly required?: TRequired;
1623
+ readonly to?: never;
1624
+ } & KeyedFieldOptions<never>;
1625
+ /**
1626
+ * A contributed field kind's builder — `clerk.userRef()`: a reference to the Extension's Managed
1627
+ * Record type, typed exactly as `fields.reference` types a reference to it, which the Studio picks through
1628
+ * the kind's picker. The schema, reads, writes and Policies treat it as that reference.
1629
+ */
1630
+ type ContributedFieldKindBuilder<TTo extends string> = <TMulti extends boolean = false, TRequired extends boolean = false>(options?: ContributedFieldOptions<TTo, TMulti, TRequired>) => KeyedField<'reference', ReferenceConfig<TTo, TMulti> & {
1631
+ required?: TRequired;
1632
+ }>;
1633
+ /** An Extension's contributed field kinds, by the name its declaration gives them. */
1634
+ type FieldKindsOf<X extends ExtensionSurface> = {
1635
+ readonly [K in keyof X['fieldKinds']]: X['fieldKinds'][K] extends {
1636
+ readonly base: {
1637
+ readonly to: infer To extends string;
1638
+ };
1639
+ } ? ContributedFieldKindBuilder<To> : never;
1640
+ };
1641
+ /** An Extension's Managed Record types, by the name its declaration gives them (REL-3863). */
1642
+ type RecordTypesOf<X extends ExtensionSurface> = {
1643
+ readonly [K in keyof X['recordTypes']]: X['recordTypes'][K];
1644
+ };
1645
+ /** Extension `S`'s handle. */
1646
+ type ExtensionHandle<S extends keyof Surfaces> = HandleOf<Surfaces[S], S & string> & FieldKindsOf<Surfaces[S]> & RecordTypesOf<Surfaces[S]>;
1647
+ /** Where the declaration rides on what `defineExtensions` returns. */
1648
+ declare const DECLARED = "declared";
1649
+ /**
1650
+ * Where an {@link ExtensionsFile}'s type keeps the settings it was declared with, for
1651
+ * {@link ClaimsOf} to read. Type-level only: no value carries it.
1652
+ */
1653
+ declare const DECLARED_SETTINGS: unique symbol;
1654
+ /**
1655
+ * What `relyio/extensions.ts` default-exports: the declaration the CLI reads into the Manifest, and
1656
+ * a handle for each Extension it declares.
1657
+ */
1658
+ type ExtensionsFile<C> = ExtensionsFileData & {
1659
+ readonly [S in keyof C & keyof Surfaces]: ExtensionHandle<S>;
1660
+ } & {
1661
+ readonly [DECLARED_SETTINGS]?: C;
1662
+ };
1663
+ /** The setting an identity Extension declares its Claims in (`@relyio/api-contract`'s `IDENTITY_CLAIMS_SETTING`). */
1664
+ type ClaimsSetting = 'claims';
1665
+ /** `S` without the whitespace `String.prototype.trim` takes off either end. */
1666
+ type Trimmed<S extends string> = S extends `${' ' | '\n' | '\t' | '\r'}${infer Rest}` ? Trimmed<Rest> : S extends `${infer Rest}${' ' | '\n' | '\t' | '\r'}` ? Trimmed<Rest> : S;
1667
+ /**
1668
+ * The Claim names a `claims` setting declares, read as `claimNamesOf` reads it at deploy: split on
1669
+ * commas, each trimmed, empty ones dropped. Text TypeScript does not know (`string`) may name any.
1670
+ */
1671
+ type ClaimNames<S extends string> = string extends S ? string : S extends `${infer First},${infer Rest}` ? ClaimNames<First> | ClaimNames<Rest> : Trimmed<S> extends '' ? never : Trimmed<S>;
1672
+ /** Every Claim name the Extensions in `C` declare: an identity Extension's `claims` setting. */
1673
+ type DeclaredClaimNames<C> = {
1674
+ readonly [S in keyof C]: C[S] extends {
1675
+ readonly [K in ClaimsSetting]: infer T extends string;
1676
+ } ? ClaimNames<T> : never;
1677
+ }[keyof C];
1678
+ /**
1679
+ * The Claims a Project relies on, typed from its `relyio/extensions.ts`: `ClaimsOf<typeof
1680
+ * extensions>` is an object with one optional key per Claim its identity Extension's `claims`
1681
+ * setting names — `claims: 'plan, role'` is `{ plan?: unknown; role?: unknown }`. A Claim named and
1682
+ * missing from a token is absent, and a setting is text, so each value is `unknown`: what a Claim may
1683
+ * hold is for the Policy that tests it to say.
1684
+ *
1685
+ * Type a Policy's `claims` root with it, so a misspelt Claim is a compile error:
1686
+ *
1687
+ * @example
1688
+ * import type { ClaimsOf } from '@relyio/sdk/extensions'
1689
+ * import { conditionRoot } from '@relyio/sdk/schema'
1690
+ * import extensions from './extensions'
1691
+ *
1692
+ * const claims = conditionRoot<ClaimsOf<typeof extensions>>('claims')
1693
+ * claims.plan.eq('paid') // ✓
1694
+ * claims.plna.eq('paid') // ✗ `plna` is no Claim the Project declares
1695
+ */
1696
+ type ClaimsOf<F> = F extends {
1697
+ readonly [DECLARED_SETTINGS]?: infer C;
1698
+ } ? {
1699
+ readonly [N in DeclaredClaimNames<C>]?: unknown;
1700
+ } : never;
1701
+ /** `C`, refusing a key no Extension declares — at the top and in each Extension's settings. */
1702
+ type Exactly<C> = {
1703
+ readonly [S in keyof C]: S extends keyof Surfaces ? C[S] & {
1704
+ readonly [K in Exclude<keyof C[S], keyof ExtensionSettings<S>>]: never;
1705
+ } : never;
1706
+ };
1707
+ /**
1708
+ * Declare the Extensions the Project uses, with their settings, and take a typed handle for each.
1709
+ * The file is `relyio/extensions.ts`; `relyio deploy` reads its default export into the Manifest.
1710
+ */
1711
+ declare function defineExtensions<const C extends ExtensionsConfig>(config: C & Exactly<C>): ExtensionsFile<C>;
1712
+ /** What the CLI reads of `relyio/extensions.ts`' default export, whatever it declares. */
1713
+ interface ExtensionsFileData {
1714
+ readonly _tag: 'extensions';
1715
+ readonly [DECLARED]: ExtensionsDeclaration;
1716
+ }
1717
+ /** Whether `value` is what `defineExtensions` returns: how the CLI reads `relyio/extensions.ts`. */
1718
+ declare function isExtensionsFile(value: unknown): value is ExtensionsFileData;
1719
+ /** The declaration as the Manifest carries it. */
1720
+ declare function extensionsDeclarationOf(file: ExtensionsFileData): ExtensionsDeclaration;
1721
+
1722
+ /**
1723
+ * The atomic Record batch (spec 1, REL-3774): `relyio.records.batch([...])` — an ordered list of
1724
+ * Record operations across Record types, run in one transaction, all or none.
1725
+ *
1726
+ * Each operation is `create`, `update`, `delete`, `increment` or `decrement`, and names its Record by
1727
+ * `id`, by `where` (the one Record of its type whose top-level fields have those values), or both —
1728
+ * `where` then a precondition. A refused operation rolls the whole batch back, and the refusal names
1729
+ * it: {@link refusedOperationOf} reads its index off the error. A counter moved past its bound is a
1730
+ * `ResourceConflictError` whose `blockage` is `counter-out-of-bounds`, naming the field, the bound and
1731
+ * the value it holds.
1732
+ *
1733
+ * Every batch is sent with an `Idempotency-Key` — yours, or one minted for the call — so a batch
1734
+ * retried after a lost answer is answered as done, never written twice.
1735
+ */
1736
+
1737
+ /** A condition's value: what one top-level field holds, compared for equality. */
1738
+ type RecordConditionValue = string | number | boolean | null;
1739
+ /**
1740
+ * Which stored Record an operation lands on: by `id`, by `where`, or both. Every `where` is read
1741
+ * before the batch's first operation runs, so it names a Record stored before the batch — never one
1742
+ * an earlier operation of the same batch creates.
1743
+ */
1744
+ type RecordBatchTarget = {
1745
+ id: string;
1746
+ where?: Record<string, RecordConditionValue>;
1747
+ } | {
1748
+ id?: undefined;
1749
+ where: Record<string, RecordConditionValue>;
1750
+ };
1751
+ /** One operation of a batch. `type` is a Record type the schema declares. */
1752
+ type RecordBatchOperation<TType extends string = string> = {
1753
+ op: 'create';
1754
+ type: TType;
1755
+ fields: Record<string, unknown>;
1756
+ } | ({
1757
+ op: 'update';
1758
+ type: TType;
1759
+ /** The fields to change; one given `null` is removed. A counter is never set. */
1760
+ fields: Record<string, unknown>;
1761
+ /** The revision the change was made on; absent, the update writes over the one stored. */
1762
+ revision?: number;
1763
+ } & RecordBatchTarget) | ({
1764
+ op: 'delete';
1765
+ type: TType;
1766
+ } & RecordBatchTarget) | ({
1767
+ op: 'increment' | 'decrement';
1768
+ type: TType;
1769
+ /** The counter. */
1770
+ field: string;
1771
+ /** A whole number above zero. */
1772
+ by: number;
1773
+ } & RecordBatchTarget);
1774
+ /** What a batch takes beside its operations. */
1775
+ interface RecordBatchOptions extends RecordCallOptions {
1776
+ /**
1777
+ * The key naming this batch. Sent again — by you, or by a retry — the batch is answered as the
1778
+ * first was and written once; with other operations it is refused. Minted for you when absent.
1779
+ */
1780
+ idempotencyKey?: string;
1781
+ }
1782
+ /** What a committed batch answers: each operation, in order, and whether this is a replay. */
1783
+ type RecordBatchResult = components['schemas']['RecordBatchResult'];
1784
+ /** What one operation of a committed batch did. */
1785
+ type RecordBatchOperationResult = components['schemas']['RecordBatchOperationResult'];
1786
+ /**
1787
+ * The index, from 0, of the batch operation a refusal names: a `ResourceConflictError`'s
1788
+ * `blockage.facts.operation`, or the first violation under `operations[<index>]` of a
1789
+ * `ValidationError`. `undefined` for an error that names none — one about the whole batch, such as
1790
+ * an allowance, or no batch refusal at all.
1791
+ */
1792
+ declare function refusedOperationOf(error: unknown): number | undefined;
1793
+
1794
+ /**
1795
+ * `@relyio/sdk/functions` — a Function, declared (spec 2, REL-3819; ADR 0021).
1796
+ *
1797
+ * One file per Function at `relyio/functions/<name>.ts`, default-exporting what
1798
+ * {@link defineFunction} returns; the file's name is the Function's. Everything but `run` is data —
1799
+ * its input and output Fields, its reach, its Secrets and its Ways in — which `relyio deploy` reads
1800
+ * into the Manifest and plans. `run` is the code: the CLI bundles the file and uploads the bundle.
1801
+ *
1802
+ * @example
1803
+ * // relyio/functions/checkout.ts
1804
+ * import { defineFunction, fields } from '@relyio/sdk/functions'
1805
+ *
1806
+ * export default defineFunction({
1807
+ * input: { cartId: fields.shortText({ required: true }) },
1808
+ * output: { orderId: fields.shortText({ required: true }) },
1809
+ * records: { read: ['cart', 'product'], write: ['order', 'inventory'] },
1810
+ * network: [{ host: 'api.stripe.com' }],
1811
+ * secrets: { STRIPE_SECRET_KEY: { hosts: ['api.stripe.com'] } },
1812
+ * route: { method: 'POST', path: '/checkout' },
1813
+ * async run({ input, records }) {
1814
+ * const cart = await records.cart.get(input.cartId)
1815
+ * // records.review here is a type error: checkout does not declare it
1816
+ * …
1817
+ * },
1818
+ * })
1819
+ *
1820
+ * **Typed from the schema.** Once `relyio generate-types` has registered the schema (`Register`), a
1821
+ * Record type the reach names must be one the schema declares, and `records.<type>` is typed by its
1822
+ * fields. A type the reach does not name is not on `records` at all.
1823
+ *
1824
+ * The capability object's runtime is the Functions runner's (spec 2, F2); this module declares its
1825
+ * types and the data `defineFunction` evaluates to.
1826
+ */
1827
+
1828
+ /** Where a Function runs: a Dynamic Worker by default, a Container when it says so. */
1829
+ type FunctionRuntime = 'isolate' | 'container';
1830
+ /** A Container's instance type (Cloudflare's names): up to `standard-4`, 4 vCPU and 12 GiB. */
1831
+ type FunctionContainerSize = 'lite' | 'basic' | 'standard-1' | 'standard-2' | 'standard-3' | 'standard-4';
1832
+ /** A whole number of seconds or minutes: `'30s'`, `'5m'`. */
1833
+ type FunctionDuration = `${number}s` | `${number}m`;
1834
+ /** The methods a route answers. */
1835
+ type FunctionRouteMethod = 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE';
1836
+ /** A host a Function may call. `preview: 'send'`: a Preview branch's request to it is sent, not Recorded. */
1837
+ interface FunctionNetworkHost {
1838
+ readonly host: string;
1839
+ readonly preview?: 'send';
1840
+ }
1841
+ /**
1842
+ * An Extension's Action a Function may send through (spec 3, REL-3856), as the declaration carries
1843
+ * it: the Extension's and the Action's catalog slugs.
1844
+ */
1845
+ interface FunctionActionReach {
1846
+ readonly extension: string;
1847
+ readonly action: string;
1848
+ }
1849
+ /**
1850
+ * An Action handle `reach.actions` takes: what `relyio/extensions.ts`' handles hand out
1851
+ * (`resend.send`), its Extension and Action slugs and its input and output carried as types.
1852
+ */
1853
+ type FunctionActionHandle = AutomationAction<unknown, unknown>;
1854
+ /** A Secret a Function declares: the hosts it may be sent to. Its value is set per Environment. */
1855
+ interface FunctionSecretDeclaration {
1856
+ readonly hosts: readonly string[];
1857
+ }
1858
+ /** An HTTP route: static segments and `:params` — `'/orders/:id'`. */
1859
+ interface FunctionRoute {
1860
+ readonly method: FunctionRouteMethod;
1861
+ readonly path: string;
1862
+ /** Present: a Public route on the Project's host, called without a key, 60 calls a minute per address unless `perIp` says otherwise. */
1863
+ readonly public?: {
1864
+ readonly perIp?: number;
1865
+ /** A Secret this Function declares, holding the Turnstile secret key a call's token is verified with. */
1866
+ readonly turnstile?: string;
1867
+ };
1868
+ /** Browser origins allowed to call the route: `['https://shop.example.com']`. */
1869
+ readonly cors?: readonly string[];
1870
+ }
1871
+ /** An agent tool, offered in the AI Workspace and over MCP. */
1872
+ interface FunctionTool {
1873
+ readonly description: string;
1874
+ }
1875
+ /** A schedule, to the minute, on Production only: `{ cron: '0 8 * * 1', timezone: 'Europe/Berlin' }`. */
1876
+ interface FunctionSchedule {
1877
+ readonly cron: string;
1878
+ readonly timezone: string;
1879
+ }
1880
+ /**
1881
+ * A stored Field, as the content model compiles a keyed one. Plain data: what the Manifest carries
1882
+ * and `@relyio/manifest` validates.
1883
+ */
1884
+ type FunctionFieldDeclaration = object;
1885
+ /**
1886
+ * What a Function declares besides its code, every default filled: the Functions area's data shape
1887
+ * (`@relyio/manifest`'s `FunctionDeclaration`, which the CLI's type test holds this to).
1888
+ */
1889
+ interface FunctionDeclaration {
1890
+ readonly description?: string;
1891
+ readonly runtime: FunctionRuntime;
1892
+ readonly container?: {
1893
+ readonly size: FunctionContainerSize;
1894
+ };
1895
+ readonly limits?: {
1896
+ readonly cpu?: FunctionDuration;
1897
+ readonly duration?: FunctionDuration;
1898
+ };
1899
+ readonly input: readonly FunctionFieldDeclaration[];
1900
+ readonly output?: readonly FunctionFieldDeclaration[];
1901
+ readonly records: {
1902
+ readonly read: readonly string[];
1903
+ readonly write: readonly string[];
1904
+ };
1905
+ readonly content: 'published' | 'none';
1906
+ readonly network: readonly FunctionNetworkHost[];
1907
+ readonly secrets: Readonly<Record<string, FunctionSecretDeclaration>>;
1908
+ /** The Actions it may send (REL-3856); absent when it declares none. */
1909
+ readonly actions?: readonly FunctionActionReach[];
1910
+ /** Who besides a secret key may call its SDK Way in (spec 4, REL-3892); absent when undeclared. */
1911
+ readonly endUsers?: FunctionEndUsers;
1912
+ readonly route?: FunctionRoute;
1913
+ readonly tool?: FunctionTool;
1914
+ readonly schedule?: FunctionSchedule;
1915
+ }
1916
+ /**
1917
+ * The Record type names a schema declares, or any name without one. Generic over the schema on
1918
+ * purpose: written over `RegisteredSchema` directly, the conditional was resolved once, before the
1919
+ * program's `Register` augmentation was in view, and answered `string` (measured).
1920
+ */
1921
+ type RecordTypeNameIn<S> = S extends AnySchemaDefinition ? SchemaRecordTypeNames<S> : string;
1922
+ /** The Record type names the registered schema declares, or any name before one is registered. */
1923
+ type FunctionRecordTypeName = RecordTypeNameIn<RegisteredSchema>;
1924
+ /** A Record type of a schema, by name. */
1925
+ type RecordTypeIn<S, K> = S extends AnySchemaDefinition ? NonNullable<S['recordTypes']>[K & keyof NonNullable<S['recordTypes']>] : never;
1926
+ /** A Record of one Record type as a read answers it, typed by the schema when there is one. */
1927
+ type RecordIn<S, K> = S extends AnySchemaDefinition ? InferRecordType<RecordTypeIn<S, K>> : BaseRecord;
1928
+ /** What a create of one Record type takes. */
1929
+ type WriteIn<S, K> = S extends AnySchemaDefinition ? RecordWriteFieldsOf<RecordTypeIn<S, K>> : Record<string, unknown>;
1930
+ /** What an update of one Record type takes: the create's values without its Counters. */
1931
+ type UpdateIn<S, K> = S extends AnySchemaDefinition ? RecordUpdateFieldsOf<RecordTypeIn<S, K>> : Record<string, unknown>;
1932
+ /** A Record of one Record type of the registered schema. */
1933
+ type RecordOf<K> = RecordIn<RegisteredSchema, K>;
1934
+ /**
1935
+ * A File a run sets (REL-3832), as `records.file(bytes, { name })` answers it: its name, media type
1936
+ * and size, and the bytes held beside it until the write naming it is sent. Set it as the value of
1937
+ * a File field in a create, an update or a batch; the run asks RelyIO for the upload, puts the bytes
1938
+ * and sends the write naming it, so a retried run names the File its first attempt stored.
1939
+ */
1940
+ interface FunctionFile {
1941
+ readonly name: string;
1942
+ /** The media type: as given, else read off the name's extension, else `application/octet-stream`. */
1943
+ readonly type: string;
1944
+ /** In bytes. */
1945
+ readonly size: number;
1946
+ }
1947
+ /** What a write takes: a File field takes a {@link FunctionFile} where it takes an upload. */
1948
+ type WithFunctionFiles<T> = {
1949
+ [F in keyof T]: FileUpload extends NonNullable<T[F]> ? T[F] | FunctionFile : T[F];
1950
+ };
1951
+ /** A Counter move in a run: the Record by `id`, `where` or both, the Counter, and how far. */
1952
+ type FunctionCounterMove = RecordBatchTarget & {
1953
+ readonly field: string;
1954
+ /** A whole number above zero. */
1955
+ readonly by: number;
1956
+ };
1957
+ /**
1958
+ * What a run may do with a Record type it only reads (REL-3822), on the run's Dataset, through spec
1959
+ * 1's Record reader. `get` answers `null` for a Record not stored. A Record type the reach does not
1960
+ * name is refused at run time too, not only by these types.
1961
+ */
1962
+ interface FunctionRecordReader<K> {
1963
+ get(id: string): Promise<RecordOf<K> | null>;
1964
+ list(options?: {
1965
+ /** Field conditions, in the grammar `relyio.records(type).list({ where })` takes. */
1966
+ where?: Record<string, unknown>;
1967
+ /** One key and its direction: `createdAt:desc` when absent. */
1968
+ sort?: `${string}:${'asc' | 'desc'}`;
1969
+ /** At most 100. */
1970
+ limit?: number;
1971
+ cursor?: string;
1972
+ }): Promise<{
1973
+ data: RecordOf<K>[];
1974
+ page: {
1975
+ nextCursor: string | null;
1976
+ hasMore: boolean;
1977
+ };
1978
+ }>;
1979
+ count(options?: {
1980
+ where?: Record<string, unknown>;
1981
+ }): Promise<number>;
1982
+ }
1983
+ /**
1984
+ * What a run may do with a Record type it writes: read it, and write it one operation at a time —
1985
+ * each sent as a batch of one, keyed as every batch of the run is, so a retried run writes it once.
1986
+ * A refusal throws {@link FunctionBatchRefused} naming operation 0.
1987
+ */
1988
+ interface FunctionRecordWriter<K> extends FunctionRecordReader<K> {
1989
+ create(fields: WithFunctionFiles<WriteIn<RegisteredSchema, K>>): Promise<RecordOf<K>>;
1990
+ update(id: string, fields: {
1991
+ [F in keyof UpdateIn<RegisteredSchema, K>]?: WithFunctionFiles<UpdateIn<RegisteredSchema, K>>[F] | null;
1992
+ }, options?: {
1993
+ revision?: number;
1994
+ }): Promise<RecordOf<K>>;
1995
+ delete(id: string): Promise<void>;
1996
+ increment(move: FunctionCounterMove): Promise<RecordOf<K>>;
1997
+ decrement(move: FunctionCounterMove): Promise<RecordOf<K>>;
1998
+ }
1999
+ /**
2000
+ * The synchronous collector `records.batch` hands its callback: each call adds one operation, over
2001
+ * the Record types the Function writes, and nothing is read inside it. The batch is sent once the
2002
+ * callback returns — all or none. Each answers `undefined`, and the callback must too: one that
2003
+ * awaits returns a promise, which is a type error here and refused at run time.
2004
+ */
2005
+ interface FunctionRecordBatch<W extends string> {
2006
+ create(type: W, fields: Record<string, unknown>): undefined;
2007
+ update(type: W, target: RecordBatchTarget & {
2008
+ fields: Record<string, unknown>;
2009
+ revision?: number;
2010
+ }): undefined;
2011
+ delete(type: W, target: RecordBatchTarget): undefined;
2012
+ increment(type: W, move: FunctionCounterMove): undefined;
2013
+ decrement(type: W, move: FunctionCounterMove): undefined;
2014
+ }
2015
+ /** What a committed batch answers: the Records it created, in order, and every operation's outcome. */
2016
+ type FunctionBatchResult = RecordBatchResult & {
2017
+ /** The Records its creates stored, in the order the batch sent them. */
2018
+ readonly created: BaseRecord[];
2019
+ };
2020
+ /**
2021
+ * What a refused batch throws in a run (REL-3822), a one-operation write's included: nothing of the
2022
+ * batch was written. `operation` is the refused operation's index from 0, `code` the API's code
2023
+ * (`RESOURCE_CONFLICT`, `CONTENT_INVALID`, `ACCESS_DENIED`, …), `reason` what it named — a
2024
+ * blockage's kind such as `counter-out-of-bounds`, or an issue's code — and `field` the Record field
2025
+ * it is about, when it names one: which line of a checkout ran out of stock.
2026
+ */
2027
+ interface FunctionBatchRefused extends Error {
2028
+ readonly name: 'BatchRefused';
2029
+ readonly operation: number | null;
2030
+ readonly code: string;
2031
+ readonly reason: string | null;
2032
+ readonly field: string | null;
2033
+ readonly details: Readonly<Record<string, unknown>>;
2034
+ }
2035
+ /** Whether `error` is a run's refused batch — what `records.batch` and every single write throw. */
2036
+ declare function isBatchRefused(error: unknown): error is FunctionBatchRefused;
2037
+ /** `records` in a run: exactly the Record types the reach names, and a batch over those it writes. */
2038
+ type FunctionRecords<R extends string, W extends string> = {
2039
+ readonly [K in R | W]: K extends W ? FunctionRecordWriter<K> : FunctionRecordReader<K>;
2040
+ } & {
2041
+ batch(collect: (batch: FunctionRecordBatch<W>) => undefined): Promise<FunctionBatchResult>;
2042
+ /**
2043
+ * A File for a File field of a Record type the Function writes (REL-3832): the bytes and the
2044
+ * name, and the media type when the name's extension does not say it. Answers at once — inside a
2045
+ * batch's collector too — and is set by the write that names it.
2046
+ */
2047
+ file(bytes: Uint8Array | ArrayBuffer, options: {
2048
+ readonly name: string;
2049
+ readonly type?: string;
2050
+ }): FunctionFile;
2051
+ };
2052
+ /**
2053
+ * Which Way in started a run, or `editor`: a test run from the Function editor, which always runs in
2054
+ * the person's own Preview branch.
2055
+ */
2056
+ type FunctionWayIn = 'sdk' | 'route' | 'tool' | 'schedule' | 'automation' | 'editor';
2057
+ /**
2058
+ * Whether a publishable key may call a Function from a browser (spec 4, REL-3892): `'required'` for
2059
+ * a signed-in End user only — the call carries their token — or `'optional'` with or without one.
2060
+ */
2061
+ type FunctionEndUsers = 'required' | 'optional';
2062
+ /**
2063
+ * The Claims a run's End user carries: those the registered `relyio/extensions.ts` declares
2064
+ * (`ClaimsOf`), once `Register` names it — `declare module '@relyio/sdk' { interface Register {
2065
+ * extensions: typeof extensions } }` — else any name, each `unknown`.
2066
+ */
2067
+ type FunctionEndUserClaims = Register extends {
2068
+ extensions: infer F;
2069
+ } ? ClaimsOf<F> : {
2070
+ readonly [claim: string]: unknown;
2071
+ };
2072
+ /**
2073
+ * The End user a run's call carried a verified token for (spec 4, REL-3892): their subject — the
2074
+ * `sub` an Owner field stores — and the Claims the Project declares, as the token carries them. A
2075
+ * Record the run creates naming no owner is theirs; what the run may touch is still its reach, never
2076
+ * their Policies.
2077
+ */
2078
+ interface FunctionEndUser {
2079
+ readonly subject: string;
2080
+ readonly claims: FunctionEndUserClaims;
2081
+ }
2082
+ /** What a run knows of itself: its id survives every retry. */
2083
+ interface FunctionRunInfo {
2084
+ readonly id: string;
2085
+ readonly attempt: number;
2086
+ readonly wayIn: FunctionWayIn;
2087
+ /**
2088
+ * Who called, as the run record names them: `api-key:<keyId>`, `task:<taskId>` for a scheduled or
2089
+ * Automation run, or `user:<userId>` for a person's tool call or test run.
2090
+ */
2091
+ readonly caller: string;
2092
+ readonly environment: 'production' | 'preview';
2093
+ /**
2094
+ * The Branch the run runs on (spec 3, REL-3853): what a checkout hands Stripe as
2095
+ * `metadata.relyio_branch`, so a Preview payment's events run that Branch's Automations.
2096
+ */
2097
+ readonly branchId: string;
2098
+ /**
2099
+ * What started a run no request started: a schedule's firing — `{ type: 'function.schedule',
2100
+ * scheduledFor }`, the UTC instant it was for — or the event an Automation step reacts to.
2101
+ * Absent from a run an SDK call, a route or a tool started.
2102
+ */
2103
+ readonly event?: Readonly<Record<string, unknown>>;
2104
+ /**
2105
+ * The End user whose token the call carried, verified (spec 4, REL-3892) — with any key on the SDK
2106
+ * Way in or a keyed route, and on a Public route that was sent one. Absent otherwise.
2107
+ */
2108
+ readonly endUser?: FunctionEndUser;
2109
+ }
2110
+ /** Published content, read through the read core: present when the reach declares `content: 'published'`. */
2111
+ interface FunctionContent {
2112
+ get(type: string, id: string): Promise<Record<string, unknown> | null>;
2113
+ list(type: string, options?: {
2114
+ where?: Record<string, RecordConditionValue>;
2115
+ limit?: number;
2116
+ }): Promise<{
2117
+ data: Record<string, unknown>[];
2118
+ }>;
2119
+ }
2120
+ /**
2121
+ * A Function's input or output values, as its Fields declare them: what its run is handed and
2122
+ * returns, and what `relyio.functions.<name>(input)` takes and answers once `Register` names it.
2123
+ */
2124
+ type FunctionFieldValues<F extends KeyedMembers> = FieldValues<DeclaredFields<{
2125
+ readonly fields: readonly KeyedMembersOf<F>[];
2126
+ }>>;
2127
+ /** What one Action handle's call takes and answers. */
2128
+ type ActionCallOf<H> = H extends AutomationAction<infer I, infer O, string, string> ? (input: I) => Promise<O> : never;
2129
+ /**
2130
+ * `actions` in a run (REL-3856): `actions.<extension>.<action>(input)` for exactly the Actions the
2131
+ * reach declares, typed by the Extension's own declaration. A call is sent through the events
2132
+ * worker's one send point with the run's Environment — the run holds no key — and answers the
2133
+ * Action's result; in a Preview without a Preview Connection, the Action's sample, the call Recorded
2134
+ * on the run. A refusal throws {@link FunctionActionRefused}.
2135
+ */
2136
+ type FunctionActions<A extends FunctionActionHandle> = {
2137
+ readonly [E in A['extension']]: {
2138
+ readonly [N in Extract<A, {
2139
+ readonly extension: E;
2140
+ }>['action']]: ActionCallOf<Extract<A, {
2141
+ readonly extension: E;
2142
+ readonly action: N;
2143
+ }>>;
2144
+ };
2145
+ };
2146
+ /**
2147
+ * What a refused Action call throws in a run (REL-3856): nothing stands sent. `code` names why —
2148
+ * `extension-not-connected` (the Extension has no Connection in the run's Environment: Functions are
2149
+ * never paused, their call fails), `action-input-invalid`, `action-send-failed`, `action-refused`
2150
+ * (its third party refused it, with that party's message), `action-send-in-doubt` (it may have
2151
+ * arrived), or the API's own (`ACCESS_DENIED` for an Action outside the reach) — and `retryable`
2152
+ * whether another attempt could send it. A refused or in-doubt call is answered the same on the
2153
+ * run's next attempt, and never sent again.
2154
+ */
2155
+ interface FunctionActionRefused extends Error {
2156
+ readonly name: 'ActionRefused';
2157
+ readonly code: string;
2158
+ readonly retryable: boolean;
2159
+ readonly extension: string;
2160
+ readonly action: string;
2161
+ }
2162
+ /** Whether `error` is a run's refused Action call. */
2163
+ declare function isActionRefused(error: unknown): error is FunctionActionRefused;
2164
+ /** Everything a run is handed (spec 2, "The capability object"). No key, no project id. */
2165
+ interface FunctionCapabilities<I extends KeyedMembers, R extends string, W extends string, C extends 'published' | 'none', S extends string, A extends FunctionActionHandle = never> {
2166
+ readonly input: FunctionFieldValues<I>;
2167
+ readonly records: FunctionRecords<R, W>;
2168
+ /** The Actions the reach declares, each sent without the run holding its key (REL-3856). */
2169
+ readonly actions: FunctionActions<A>;
2170
+ /** Published entries; `undefined` unless the reach declares `content: 'published'`. */
2171
+ readonly content: C extends 'published' ? FunctionContent : undefined;
2172
+ /** The gateway's fetch: only the hosts the reach declares answer. */
2173
+ readonly fetch: typeof globalThis.fetch;
2174
+ /** Each declared Secret as a placeholder the gateway swaps for its value on the way to its hosts. */
2175
+ readonly secrets: {
2176
+ readonly [K in S]: string;
2177
+ };
2178
+ readonly run: FunctionRunInfo;
2179
+ /**
2180
+ * The HTTP request, for a run a route started. Its body is the bytes the caller sent, unparsed —
2181
+ * `await request.text()` or `arrayBuffer()` is what a webhook's sender signed.
2182
+ */
2183
+ readonly request: Request | undefined;
2184
+ }
2185
+ /**
2186
+ * A response a route Function shapes itself (REL-3829): return `respond({ … })` from `run` and the
2187
+ * route answers with that status, those headers and that body — a redirect after a form post, a
2188
+ * `201`, a webhook's plain-text `ok`. A string body is sent as it is; any other value as JSON;
2189
+ * `null` or none sends no body. Any other output a route Function returns is answered as JSON with
2190
+ * `200`. The hop's own headers (`content-length`, `transfer-encoding`, …) are RelyIO's to set.
2191
+ */
2192
+ interface FunctionResponse {
2193
+ readonly _tag: 'function-response';
2194
+ readonly status: number;
2195
+ readonly headers: Readonly<Record<string, string>>;
2196
+ readonly body: unknown;
2197
+ }
2198
+ /**
2199
+ * Shape a route Function's response.
2200
+ *
2201
+ * @example
2202
+ * return respond({ status: 303, headers: { location: 'https://acme.example/thanks' } })
2203
+ */
2204
+ declare function respond(response: {
2205
+ /** 200–599. */
2206
+ readonly status: number;
2207
+ readonly headers?: Readonly<Record<string, string>>;
2208
+ readonly body?: unknown;
2209
+ }): FunctionResponse;
2210
+ /** What `defineFunction` takes. Every part but `run` is data. */
2211
+ interface FunctionConfig<I extends KeyedMembers, O extends KeyedMembers, R extends FunctionRecordTypeName, W extends FunctionRecordTypeName, C extends 'published' | 'none', S extends string, A extends FunctionActionHandle = never> {
2212
+ description?: string;
2213
+ /** `'isolate'` when left out. */
2214
+ runtime?: FunctionRuntime;
2215
+ container?: {
2216
+ size: FunctionContainerSize;
2217
+ };
2218
+ limits?: {
2219
+ cpu?: FunctionDuration;
2220
+ duration?: FunctionDuration;
2221
+ };
2222
+ /** The input's Fields, under their ids, as the content model's `fields.*` write them. */
2223
+ input?: I;
2224
+ output?: O;
2225
+ /** Reach: the Record types it reads and writes, by name. None when left out. */
2226
+ records?: {
2227
+ read?: readonly R[];
2228
+ write?: readonly W[];
2229
+ };
2230
+ /** Reach: `'published'` to read published content. `'none'` when left out. */
2231
+ content?: C;
2232
+ /** Reach: the hosts it calls. None when left out. */
2233
+ network?: readonly FunctionNetworkHost[];
2234
+ secrets?: {
2235
+ readonly [K in S]: FunctionSecretDeclaration;
2236
+ };
2237
+ /**
2238
+ * Reach: the Extensions' Actions it may send, by the handles `relyio/extensions.ts` gives —
2239
+ * `actions: [resend.send]`. None when left out; `actions.<extension>.<action>` for any other is a
2240
+ * type error, and refused at run time.
2241
+ */
2242
+ actions?: readonly A[];
2243
+ /**
2244
+ * Who besides a secret key may call it through `relyio.functions.<name>(input)` (spec 4,
2245
+ * REL-3892): `'required'` lets a publishable key call it for a signed-in End user, whose token it
2246
+ * carries; `'optional'` lets a publishable key call it with or without one. Left out, a secret key
2247
+ * alone. Either way the run is bound by its reach, and `run.endUser` names who called.
2248
+ */
2249
+ endUsers?: FunctionEndUsers;
2250
+ route?: FunctionRoute;
2251
+ tool?: FunctionTool;
2252
+ schedule?: FunctionSchedule;
2253
+ run(capabilities: FunctionCapabilities<I, R, W, C, S, A>): Promise<FunctionFieldValues<O> | FunctionResponse | void> | FunctionFieldValues<O> | FunctionResponse | void;
2254
+ }
2255
+ /** Where a Function's input and output types ride on its definition, for the compiler alone. */
2256
+ declare const FUNCTION_FIELDS: unique symbol;
2257
+ /**
2258
+ * What a Function file default-exports: its declaration as data, and its code. `I` and `O` are its
2259
+ * input and output Fields, carried for the compiler alone, so an Automation's Function step that
2260
+ * takes it by import types its input and its output from them (spec 3, REL-3845).
2261
+ */
2262
+ interface FunctionDefinition<I extends KeyedMembers = KeyedMembers, O extends KeyedMembers = KeyedMembers> {
2263
+ readonly _tag: 'function';
2264
+ readonly declaration: FunctionDeclaration;
2265
+ readonly run: (capabilities: never) => unknown;
2266
+ /** Never present at run time: the input and output Fields, as types. */
2267
+ readonly [FUNCTION_FIELDS]?: {
2268
+ readonly input: I;
2269
+ readonly output: O;
2270
+ };
2271
+ }
2272
+ /** A Function's input values, as its Fields declare them. */
2273
+ type FunctionInputOf<F> = F extends FunctionDefinition<infer I, KeyedMembers> ? FunctionFieldValues<I> : never;
2274
+ /** A Function's output values, as its Fields declare them. */
2275
+ type FunctionOutputOf<F> = F extends FunctionDefinition<KeyedMembers, infer O> ? FunctionFieldValues<O> : never;
2276
+ /**
2277
+ * Declare a Function. The file's name is its name; `relyio deploy` reads the declaration, bundles the
2278
+ * file and ships both with the schema.
2279
+ */
2280
+ declare function defineFunction<const I extends KeyedMembers = Record<never, never>, const O extends KeyedMembers = Record<never, never>, const R extends FunctionRecordTypeName = never, const W extends FunctionRecordTypeName = never, const C extends 'published' | 'none' = 'none', const S extends string = never, const A extends FunctionActionHandle = never>(config: FunctionConfig<I, O, R, W, C, S, A>): FunctionDefinition<I, O>;
2281
+ /** Whether `value` is what `defineFunction` returns: how the CLI reads a Function file's default export. */
2282
+ declare function isFunctionDefinition(value: unknown): value is FunctionDefinition;
2283
+
2284
+ export { type AutomationTemplate as $, type AnySchemaDefinition as A, type BaseRecord as B, type RecordConditionValue as C, DefaultRetryPolicy as D, type AutomationRecordAction as E, type FunctionFieldValues as F, type AutomationEntryAction as G, HttpClient as H, type AutomationBecomesTest as I, type AutomationWhere as J, type AutomationTrigger as K, type AutomationJson as L, type MatchRouteOptions as M, type AutomationRef as N, type AutomationValue as O, type AutomationInput as P, type QueryOf as Q, type Register as R, type SendOptions as S, type AutomationDuration as T, type AutomationItemRef as U, type AutomationLookupState as V, type Wire as W, type AutomationProposeTarget as X, type AutomationStep as Y, type AutomationDefinition as Z, type Ref as _, type RegisteredSchema as a, isExtensionsFile as a$, type Accepts as a0, type StepInput as a1, tpl as a2, and as a3, or as a4, not as a5, type AutomationTriggerHandle as a6, type RecordEvent as a7, type EntryEvent as a8, type TriggerWhere as a9, type LookedUpEntry as aA, type LookedUp as aB, type LookupOptions as aC, type AskStepConfig as aD, type AskOutput as aE, type ApproveStepConfig as aF, type ApproveCounts as aG, type ApproveOutput as aH, type StepBuilder as aI, type AutomationConfig as aJ, type AutomationFile as aK, defineAutomation as aL, isAutomationFile as aM, resolveAutomation as aN, type ExtensionUse as aO, type ExtensionsDeclaration as aP, type ExtensionSettings as aQ, type ExtensionsConfig as aR, type ExtensionTriggerHandle as aS, type HandleOf as aT, type ContributedFieldOptions as aU, type ContributedFieldKindBuilder as aV, type ExtensionHandle as aW, type ExtensionsFile as aX, type ClaimsOf as aY, defineExtensions as aZ, type ExtensionsFileData as a_, type UidRef as aa, type AutomationItemHandle as ab, type RecordTriggers as ac, type EntryTriggers as ad, records as ae, entries as af, type AnyEntryEvent as ag, type AnyEntryTriggers as ah, anyEntry as ai, schedule as aj, type RunFailedEvent as ak, type RunFailedWhere as al, automations as am, type ExtensionTriggerEvent as an, extensionTrigger as ao, on as ap, type AutomationAction as aq, type AutomationStepDraft as ar, type StepHandle as as, type StepWithOutput as at, type AgentStepConfig as au, type ProposingAgentStepConfig as av, type AutomationProposal as aw, type ProposingOutput as ax, type LookedUpRecord as ay, type LookedUpEntryItem as az, type ApiPath as b, extensionsDeclarationOf as b0, type FunctionRuntime as b1, type FunctionContainerSize as b2, type FunctionDuration as b3, type FunctionRouteMethod as b4, type FunctionNetworkHost as b5, type FunctionActionReach as b6, type FunctionActionHandle as b7, type FunctionSecretDeclaration as b8, type FunctionRoute as b9, type FunctionConfig as bA, type FunctionDefinition as bB, type FunctionInputOf as bC, type FunctionOutputOf as bD, defineFunction as bE, isFunctionDefinition as bF, type FunctionTool as ba, type FunctionSchedule as bb, type FunctionFieldDeclaration as bc, type FunctionDeclaration as bd, type FunctionRecordTypeName as be, type FunctionFile as bf, type FunctionCounterMove as bg, type FunctionRecordReader as bh, type FunctionRecordWriter as bi, type FunctionRecordBatch as bj, type FunctionBatchResult as bk, type FunctionBatchRefused as bl, isBatchRefused as bm, type FunctionRecords as bn, type FunctionWayIn as bo, type FunctionEndUsers as bp, type FunctionEndUserClaims as bq, type FunctionEndUser as br, type FunctionRunInfo as bs, type FunctionContent as bt, type FunctionActions as bu, type FunctionActionRefused as bv, isActionRefused as bw, type FunctionCapabilities as bx, type FunctionResponse as by, respond as bz, type ApiMethod as c, type ApiSuccessBody as d, type RouteMatch as e, type RetryPolicy as f, type RetryableRequest as g, RecordsClient as h, isReplaySafe as i, type RecordCallOptions as j, type RecordFacetBucket as k, type RecordFileBytes as l, matchRoute as m, type RecordFileUploadOptions as n, type RecordList as o, type RecordListOptions as p, type RecordMetadata as q, retryPolicyFor as r, type RecordReadOptions as s, type RecordUpdateOptions as t, refusedOperationOf as u, type RecordBatchOperation as v, type RecordBatchOperationResult as w, type RecordBatchOptions as x, type RecordBatchResult as y, type RecordBatchTarget as z };