@relyio/sdk 0.16.0 → 0.18.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 (74) hide show
  1. package/CHANGELOG.md +23 -0
  2. package/dist/automations/index.cjs +70 -0
  3. package/dist/automations/index.d.cts +1309 -0
  4. package/dist/automations/index.d.ts +1309 -0
  5. package/dist/automations/index.js +5 -0
  6. package/dist/{chunk-RSHMA4KT.cjs → chunk-2SPPU2WG.cjs} +68 -68
  7. package/dist/chunk-7I3SC7SV.cjs +40 -0
  8. package/dist/chunk-DI6KFDBO.js +43 -0
  9. package/dist/chunk-EDE2H37D.js +686 -0
  10. package/dist/chunk-HDHBYYEY.js +36 -0
  11. package/dist/{chunk-RLLZUFDB.cjs → chunk-IFKF72VJ.cjs} +718 -331
  12. package/dist/chunk-L6DXCKVQ.cjs +50 -0
  13. package/dist/chunk-LSI26ZN2.cjs +535 -0
  14. package/dist/{chunk-TZL374LS.cjs → chunk-M7MWNV3Q.cjs} +21 -21
  15. package/dist/{chunk-H6IBQSMM.js → chunk-NJQK5STK.js} +3 -3
  16. package/dist/chunk-R6E4HE7R.js +520 -0
  17. package/dist/chunk-SHSK7UUY.cjs +706 -0
  18. package/dist/{chunk-BBUB7C2Z.js → chunk-W2A4OTCI.js} +3 -3
  19. package/dist/chunk-W7JFHSFM.js +18718 -0
  20. package/dist/{chunk-5UMY6CS4.js → chunk-ZAKEY6DY.js} +3 -3
  21. package/dist/chunk-ZAMAR3X2.cjs +18759 -0
  22. package/dist/{chunk-NIUOPIIB.js → chunk-ZHTJ7GXH.js} +400 -19
  23. package/dist/{chunk-LSRPMNPT.cjs → chunk-ZO6QDYJG.cjs} +7 -7
  24. package/dist/client/index.cjs +8 -7
  25. package/dist/client/index.d.cts +4 -3
  26. package/dist/client/index.d.ts +4 -3
  27. package/dist/client/index.js +6 -5
  28. package/dist/{define-content-type-LkI0rS2W.d.cts → define-content-type-NEQDrvdq.d.cts} +3420 -749
  29. package/dist/{define-content-type-CAWSlgn0.d.ts → define-content-type-fb-WCD5e.d.ts} +3420 -749
  30. package/dist/{define-schema-Dxu3RoHV.d.ts → define-schema-DZhtvLiK.d.cts} +157 -10
  31. package/dist/{define-schema-wlsJOrnY.d.cts → define-schema-Dlwh6l_J.d.ts} +157 -10
  32. package/dist/dev-handshake/index.cjs +9 -9
  33. package/dist/dev-handshake/index.js +2 -2
  34. package/dist/extensions/index.cjs +80 -0
  35. package/dist/extensions/index.d.cts +792 -0
  36. package/dist/extensions/index.d.ts +792 -0
  37. package/dist/extensions/index.js +76 -0
  38. package/dist/functions/index.cjs +68 -0
  39. package/dist/functions/index.d.cts +6 -0
  40. package/dist/functions/index.d.ts +6 -0
  41. package/dist/functions/index.js +59 -0
  42. package/dist/{index-CGtYxUuh.d.ts → index-C4LqrNd1.d.ts} +1 -1
  43. package/dist/{index-CcHmRALk.d.cts → index-Dnmt_UOo.d.cts} +1 -1
  44. package/dist/{index-D80MXUkf.d.cts → index-DwHcL6x5.d.cts} +103 -442
  45. package/dist/{index-BH8JksfG.d.ts → index-M3luH06A.d.ts} +103 -442
  46. package/dist/index.cjs +204 -185
  47. package/dist/index.d.cts +93 -13
  48. package/dist/index.d.ts +93 -13
  49. package/dist/index.js +11 -16
  50. package/dist/markdown/index.cjs +382 -382
  51. package/dist/markdown/index.js +1 -1
  52. package/dist/preview/index.cjs +11 -10
  53. package/dist/preview/index.js +3 -2
  54. package/dist/register-B53i97A9.d.ts +677 -0
  55. package/dist/register-BYCs0FpF.d.cts +677 -0
  56. package/dist/rich-text/index.cjs +12 -12
  57. package/dist/rich-text/index.d.cts +2 -2
  58. package/dist/rich-text/index.d.ts +2 -2
  59. package/dist/rich-text/index.js +2 -2
  60. package/dist/schema/index.cjs +62 -34
  61. package/dist/schema/index.d.cts +127 -5
  62. package/dist/schema/index.d.ts +127 -5
  63. package/dist/schema/index.js +2 -2
  64. package/dist/types/index.d.cts +1 -1
  65. package/dist/types/index.d.ts +1 -1
  66. package/dist/utils/index.cjs +20 -20
  67. package/dist/utils/index.js +2 -2
  68. package/package.json +21 -3
  69. package/dist/chunk-7BZW6XYJ.cjs +0 -433
  70. package/dist/chunk-CQHEG7QA.js +0 -411
  71. package/dist/chunk-DIZY6CKM.js +0 -950
  72. package/dist/chunk-GHYB6IZK.cjs +0 -983
  73. package/dist/chunk-MWMSFIWN.cjs +0 -17
  74. package/dist/chunk-XQ4QHWZE.js +0 -13
@@ -0,0 +1,792 @@
1
+ import { ConditionTree } from '../schema/index.js';
2
+ import { Ref, ExtensionTriggerEvent, AutomationTriggerHandle, AutomationAction, AutomationJson } from '../automations/index.js';
3
+ import '../define-schema-Dlwh6l_J.js';
4
+ import '../define-content-type-fb-WCD5e.js';
5
+ import '../rich-text.d-BWc-5gX1.js';
6
+ import '../register-B53i97A9.js';
7
+
8
+ /**
9
+ * ## What a Project's code may name of each Extension (spec 3, REL-3846; ADR 0019)
10
+ *
11
+ * `relyio/extensions.ts` names the Extensions a Project uses and their settings, and an Automation
12
+ * names an Extension's Trigger in its `on` and its Actions in `step.action` — each through a typed
13
+ * handle `@relyio/sdk/extensions` gives. That handle is typed by what is declared here: each
14
+ * Extension's **settings** (set once per Project, never a secret), its **Actions** (input, output and
15
+ * the sample result Preview hands on) and its **Triggers** (the payload the receiver keeps).
16
+ *
17
+ * Declared once, and here, because the SDK may import `@relyio/api-contract` and may not import
18
+ * `@relyio/events`, whose Extension catalog owns every Extension (the customer fence,
19
+ * `CUSTOMER_LAYER_CHOKEPOINT`). The catalog reads these same values — its settings point and its
20
+ * Triggers' payload fields are these arrays — and `@relyio/events`' `extension-surfaces.test.ts`
21
+ * holds every name here to a declared one of the catalog.
22
+ *
23
+ * One entry per Extension a Project may declare: GitHub's Connection is the Project's binding, made
24
+ * by installing the App and never declared in `relyio/extensions.ts`, so it has none. An Extension's
25
+ * Actions an Automation may name are its step Actions; a built-in-only one (a deploy hook) is never
26
+ * one of them.
27
+ */
28
+ /**
29
+ * One setting an Extension declares: text — starting with `prefix` when it must (Slack's channel,
30
+ * `#`), at most `maxLength` characters — or one of `options`. Never sensitive: a credential is a
31
+ * Connection's field, held per Environment and sealed.
32
+ */
33
+ type ExtensionSettingField = (ExtensionSettingFieldBase & {
34
+ readonly type: 'text';
35
+ readonly prefix?: string;
36
+ readonly maxLength: number;
37
+ }) | (ExtensionSettingFieldBase & {
38
+ readonly type: 'select';
39
+ readonly options: readonly string[];
40
+ });
41
+ interface ExtensionSettingFieldBase {
42
+ readonly key: string;
43
+ readonly label: string;
44
+ readonly required: boolean;
45
+ readonly helpText?: string;
46
+ }
47
+ /**
48
+ * One value an Action takes or answers, or a Trigger's payload holds: a dot-separated path, its
49
+ * JSON type, and whether it is always there. The shape of the catalog's `TriggerPayloadField`.
50
+ */
51
+ interface ExtensionValueField {
52
+ readonly path: string;
53
+ readonly type: 'string' | 'number' | 'boolean' | 'object' | 'array';
54
+ readonly required: boolean;
55
+ /**
56
+ * For an `object` whose keys are the third party's customer's own — Stripe's `metadata` — what
57
+ * every value is: `string`, so `metadata.orderId` reads as text though no key of it is declared.
58
+ * Absent, an object's undeclared keys are untyped. The receiver refuses an object whose values
59
+ * are not all of it.
60
+ */
61
+ readonly values?: 'string';
62
+ }
63
+ /**
64
+ * What an action field's value is: `text` a string, `url` an absolute `http(s)` URL, `number` a
65
+ * finite number, `boolean`, `json` any JSON value, `headers` an object of string values, `files` a
66
+ * list of Files a Record holds (spec 1's File, REL-3824), each as a run's event carries it —
67
+ * `{ id, name, size, type }`, never its object key.
68
+ *
69
+ * A `files` field is an input field only. The events worker's send point reads each File itself,
70
+ * from the run's Dataset, before the Extension's handler sees the step (REL-3854): a Recorded send
71
+ * is handed each File's name, size and type, a send its bytes too, so no customer code and no tasks
72
+ * worker handles a byte.
73
+ */
74
+ declare const ACTION_FIELD_TYPES: readonly ['text', 'url', 'number', 'boolean', 'json', 'headers', 'files'];
75
+ type ActionFieldType = (typeof ACTION_FIELD_TYPES)[number];
76
+ /** One field an action step takes or answers. */
77
+ interface ActionField {
78
+ readonly key: string;
79
+ readonly type: ActionFieldType;
80
+ readonly required: boolean;
81
+ readonly description: string;
82
+ /**
83
+ * The Extension's setting (its Settings point, `relyio/extensions.ts`) an input field takes when
84
+ * the step gives none — Resend's `from` (REL-3854). Such a field is optional on the step and still
85
+ * needed by the send: with neither the step's value nor the setting, the send point refuses it by
86
+ * name. An input field only, naming a setting the Extension declares (`extensionCatalogProblems`).
87
+ */
88
+ readonly setting?: string;
89
+ /**
90
+ * On a `files` field, and required there: the most bytes its Files may hold together, which the
91
+ * send point refuses past before it reads any of them — the third party's own limit (Resend's 40 MB
92
+ * an email once encoded). Never on another type (`extensionCatalogProblems`).
93
+ */
94
+ readonly maxBytes?: number;
95
+ }
96
+ /**
97
+ * An action as an Automation step calls it, through the events worker's one send point
98
+ * (`POST /internal/actions`, ADR 0026; REL-3849) — the catalog's `ActionDefinition.step`:
99
+ *
100
+ * - `input` — the fields the step hands it, checked before anything is sent;
101
+ * - `output` — the fields its result carries, which the steps after it read under
102
+ * `<step>.output`; a result that breaks them fails the step;
103
+ * - `sample` — the result a **Recorded** send hands on (spec 0's Preview rule): a Preview branch
104
+ * with no Preview Connection of the Extension sends nothing, and the steps after it still run on
105
+ * this. It satisfies `output` (`extensionCatalogProblems`). Declared by the Extension, never
106
+ * overridden by a step;
107
+ * - `idempotencyHeader` — the request header the third party dedupes on, which carries
108
+ * `<runId>:<step>`; `null` when it has none, and then a send whose outcome is unknown is never
109
+ * sent again.
110
+ */
111
+ interface ActionStep {
112
+ readonly input: readonly ActionField[];
113
+ readonly output: readonly ActionField[];
114
+ readonly sample: Readonly<Record<string, unknown>>;
115
+ readonly idempotencyHeader: string | null;
116
+ }
117
+ /**
118
+ * An Action an Automation step may name, under the name its handle gives it (`postMessage`): its
119
+ * slug in the Extension catalog, which the step stores, and its `step` — the very value the
120
+ * catalog's `ActionDefinition.step` is, so the handle's input and output are the send point's.
121
+ */
122
+ interface ExtensionActionSurface {
123
+ readonly action: string;
124
+ readonly step: ActionStep;
125
+ }
126
+ /** A Trigger an Automation may start on, under the name its handle gives it (`deploymentSucceeded`). */
127
+ interface ExtensionTriggerSurface {
128
+ /** The Trigger's slug in the Extension catalog, which the trigger stores (`trigger`). */
129
+ readonly trigger: string;
130
+ /** What the run reads under `event.payload`: the Trigger's payload fields. */
131
+ readonly payload: readonly ExtensionValueField[];
132
+ }
133
+ /** What a Project's code may name of one Extension. */
134
+ interface ExtensionSurface {
135
+ readonly settings: readonly ExtensionSettingField[];
136
+ readonly actions: Readonly<Record<string, ExtensionActionSurface>>;
137
+ readonly triggers: Readonly<Record<string, ExtensionTriggerSurface>>;
138
+ }
139
+ /**
140
+ * Every Extension a Project may declare in `relyio/extensions.ts`, by slug. An Extension's step
141
+ * Action is declared here and its catalog entry's `step` is this value (the webhook's `post`), and so
142
+ * is each Trigger's payload: one declaration types the SDK's handle and is what the catalog delivers.
143
+ * Resend's `send` (REL-3854) and Slack's `postMessage` (REL-3855) join the same way Stripe's
144
+ * Triggers (REL-3853) did; `@relyio/events`' `extension-surfaces.test.ts` fails on a step Action declared in the
145
+ * catalog and not here.
146
+ */
147
+ declare const EXTENSION_SURFACES: {
148
+ readonly webhook: {
149
+ readonly settings: readonly [];
150
+ readonly actions: {
151
+ readonly post: {
152
+ readonly action: 'post';
153
+ readonly step: {
154
+ readonly input: readonly [{
155
+ readonly key: 'url';
156
+ readonly type: 'url';
157
+ readonly required: true;
158
+ readonly description: 'Where to POST: an http(s) URL.';
159
+ }, {
160
+ readonly key: 'headers';
161
+ readonly type: 'headers';
162
+ readonly required: false;
163
+ readonly description: 'Request headers, sent after the Connection’s custom headers. The headers RelyIO signs with are its own.';
164
+ }, {
165
+ readonly key: 'body';
166
+ readonly type: 'json';
167
+ readonly required: false;
168
+ readonly description: 'The JSON body. Absent, the body is `null`.';
169
+ }];
170
+ readonly output: readonly [{
171
+ readonly key: 'status';
172
+ readonly type: 'number';
173
+ readonly required: true;
174
+ readonly description: 'The HTTP status the receiver answered.';
175
+ }, {
176
+ readonly key: 'body';
177
+ readonly type: 'text';
178
+ readonly required: false;
179
+ readonly description: 'The first kilobyte of the receiver’s answer, as text.';
180
+ }];
181
+ readonly sample: {
182
+ readonly status: 200;
183
+ readonly body: '';
184
+ };
185
+ readonly idempotencyHeader: "x-relyio-event-id";
186
+ };
187
+ };
188
+ };
189
+ readonly triggers: {};
190
+ };
191
+ readonly vercel: {
192
+ readonly settings: readonly [];
193
+ readonly actions: {};
194
+ readonly triggers: {
195
+ readonly deploymentSucceeded: {
196
+ readonly trigger: "deployment.succeeded";
197
+ readonly payload: readonly [{
198
+ readonly path: 'payload.deployment.id';
199
+ readonly type: 'string';
200
+ readonly required: true;
201
+ }, {
202
+ readonly path: 'payload.deployment.url';
203
+ readonly type: 'string';
204
+ readonly required: false;
205
+ }, {
206
+ readonly path: 'payload.deployment.meta.githubCommitSha';
207
+ readonly type: 'string';
208
+ readonly required: false;
209
+ }, {
210
+ readonly path: 'payload.deployment.meta.githubCommitRef';
211
+ readonly type: 'string';
212
+ readonly required: false;
213
+ }, {
214
+ readonly path: 'payload.project.id';
215
+ readonly type: 'string';
216
+ readonly required: true;
217
+ }, {
218
+ readonly path: 'payload.target';
219
+ readonly type: 'string';
220
+ readonly required: false;
221
+ }];
222
+ };
223
+ };
224
+ };
225
+ readonly netlify: {
226
+ readonly settings: readonly [];
227
+ readonly actions: {};
228
+ readonly triggers: {
229
+ readonly deploymentSucceeded: {
230
+ readonly trigger: "deployment.succeeded";
231
+ readonly payload: readonly [{
232
+ readonly path: 'id';
233
+ readonly type: 'string';
234
+ readonly required: true;
235
+ }, {
236
+ readonly path: 'site_id';
237
+ readonly type: 'string';
238
+ readonly required: true;
239
+ }, {
240
+ readonly path: 'state';
241
+ readonly type: 'string';
242
+ readonly required: true;
243
+ }, {
244
+ readonly path: 'context';
245
+ readonly type: 'string';
246
+ readonly required: false;
247
+ }, {
248
+ readonly path: 'branch';
249
+ readonly type: 'string';
250
+ readonly required: false;
251
+ }, {
252
+ readonly path: 'commit_ref';
253
+ readonly type: 'string';
254
+ readonly required: false;
255
+ }, {
256
+ readonly path: 'deploy_ssl_url';
257
+ readonly type: 'string';
258
+ readonly required: false;
259
+ }];
260
+ };
261
+ };
262
+ };
263
+ readonly stripe: {
264
+ readonly settings: readonly [];
265
+ readonly actions: {};
266
+ readonly triggers: {
267
+ readonly paymentIntentSucceeded: {
268
+ readonly trigger: 'payment_intent.succeeded';
269
+ readonly payload: readonly [{
270
+ readonly path: 'id';
271
+ readonly type: 'string';
272
+ readonly required: true;
273
+ }, {
274
+ readonly path: 'type';
275
+ readonly type: 'string';
276
+ readonly required: true;
277
+ }, {
278
+ readonly path: 'created';
279
+ readonly type: 'number';
280
+ readonly required: true;
281
+ }, {
282
+ readonly path: 'livemode';
283
+ readonly type: 'boolean';
284
+ readonly required: true;
285
+ }, {
286
+ readonly path: 'data.object.id';
287
+ readonly type: 'string';
288
+ readonly required: true;
289
+ }, {
290
+ readonly path: 'data.object.object';
291
+ readonly type: 'string';
292
+ readonly required: true;
293
+ }, {
294
+ readonly path: 'data.object.metadata';
295
+ readonly type: 'object';
296
+ readonly required: false;
297
+ readonly values: 'string';
298
+ }, {
299
+ readonly path: 'data.object.amount';
300
+ readonly type: 'number';
301
+ readonly required: true;
302
+ }, {
303
+ readonly path: 'data.object.amount_received';
304
+ readonly type: 'number';
305
+ readonly required: true;
306
+ }, {
307
+ readonly path: 'data.object.currency';
308
+ readonly type: 'string';
309
+ readonly required: true;
310
+ }, {
311
+ readonly path: 'data.object.status';
312
+ readonly type: 'string';
313
+ readonly required: true;
314
+ }, {
315
+ readonly path: 'data.object.customer';
316
+ readonly type: 'string';
317
+ readonly required: false;
318
+ }, {
319
+ readonly path: 'data.object.latest_charge';
320
+ readonly type: 'string';
321
+ readonly required: false;
322
+ }, {
323
+ readonly path: 'data.object.receipt_email';
324
+ readonly type: 'string';
325
+ readonly required: false;
326
+ }];
327
+ };
328
+ readonly paymentIntentPaymentFailed: {
329
+ readonly trigger: 'payment_intent.payment_failed';
330
+ readonly payload: readonly [{
331
+ readonly path: 'id';
332
+ readonly type: 'string';
333
+ readonly required: true;
334
+ }, {
335
+ readonly path: 'type';
336
+ readonly type: 'string';
337
+ readonly required: true;
338
+ }, {
339
+ readonly path: 'created';
340
+ readonly type: 'number';
341
+ readonly required: true;
342
+ }, {
343
+ readonly path: 'livemode';
344
+ readonly type: 'boolean';
345
+ readonly required: true;
346
+ }, {
347
+ readonly path: 'data.object.id';
348
+ readonly type: 'string';
349
+ readonly required: true;
350
+ }, {
351
+ readonly path: 'data.object.object';
352
+ readonly type: 'string';
353
+ readonly required: true;
354
+ }, {
355
+ readonly path: 'data.object.metadata';
356
+ readonly type: 'object';
357
+ readonly required: false;
358
+ readonly values: 'string';
359
+ }, {
360
+ readonly path: 'data.object.amount';
361
+ readonly type: 'number';
362
+ readonly required: true;
363
+ }, {
364
+ readonly path: 'data.object.currency';
365
+ readonly type: 'string';
366
+ readonly required: true;
367
+ }, {
368
+ readonly path: 'data.object.status';
369
+ readonly type: 'string';
370
+ readonly required: true;
371
+ }, {
372
+ readonly path: 'data.object.customer';
373
+ readonly type: 'string';
374
+ readonly required: false;
375
+ }, {
376
+ readonly path: 'data.object.last_payment_error.code';
377
+ readonly type: 'string';
378
+ readonly required: false;
379
+ }, {
380
+ readonly path: 'data.object.last_payment_error.message';
381
+ readonly type: 'string';
382
+ readonly required: false;
383
+ }];
384
+ };
385
+ readonly checkoutSessionCompleted: {
386
+ readonly trigger: 'checkout.session.completed';
387
+ readonly payload: readonly [{
388
+ readonly path: 'id';
389
+ readonly type: 'string';
390
+ readonly required: true;
391
+ }, {
392
+ readonly path: 'type';
393
+ readonly type: 'string';
394
+ readonly required: true;
395
+ }, {
396
+ readonly path: 'created';
397
+ readonly type: 'number';
398
+ readonly required: true;
399
+ }, {
400
+ readonly path: 'livemode';
401
+ readonly type: 'boolean';
402
+ readonly required: true;
403
+ }, {
404
+ readonly path: 'data.object.id';
405
+ readonly type: 'string';
406
+ readonly required: true;
407
+ }, {
408
+ readonly path: 'data.object.object';
409
+ readonly type: 'string';
410
+ readonly required: true;
411
+ }, {
412
+ readonly path: 'data.object.metadata';
413
+ readonly type: 'object';
414
+ readonly required: false;
415
+ readonly values: 'string';
416
+ }, {
417
+ readonly path: 'data.object.mode';
418
+ readonly type: 'string';
419
+ readonly required: true;
420
+ }, {
421
+ readonly path: 'data.object.payment_status';
422
+ readonly type: 'string';
423
+ readonly required: true;
424
+ }, {
425
+ readonly path: 'data.object.amount_total';
426
+ readonly type: 'number';
427
+ readonly required: false;
428
+ }, {
429
+ readonly path: 'data.object.currency';
430
+ readonly type: 'string';
431
+ readonly required: false;
432
+ }, {
433
+ readonly path: 'data.object.payment_intent';
434
+ readonly type: 'string';
435
+ readonly required: false;
436
+ }, {
437
+ readonly path: 'data.object.customer';
438
+ readonly type: 'string';
439
+ readonly required: false;
440
+ }, {
441
+ readonly path: 'data.object.customer_details.email';
442
+ readonly type: 'string';
443
+ readonly required: false;
444
+ }, {
445
+ readonly path: 'data.object.client_reference_id';
446
+ readonly type: 'string';
447
+ readonly required: false;
448
+ }];
449
+ };
450
+ readonly chargeRefunded: {
451
+ readonly trigger: 'charge.refunded';
452
+ readonly payload: readonly [{
453
+ readonly path: 'id';
454
+ readonly type: 'string';
455
+ readonly required: true;
456
+ }, {
457
+ readonly path: 'type';
458
+ readonly type: 'string';
459
+ readonly required: true;
460
+ }, {
461
+ readonly path: 'created';
462
+ readonly type: 'number';
463
+ readonly required: true;
464
+ }, {
465
+ readonly path: 'livemode';
466
+ readonly type: 'boolean';
467
+ readonly required: true;
468
+ }, {
469
+ readonly path: 'data.object.id';
470
+ readonly type: 'string';
471
+ readonly required: true;
472
+ }, {
473
+ readonly path: 'data.object.object';
474
+ readonly type: 'string';
475
+ readonly required: true;
476
+ }, {
477
+ readonly path: 'data.object.metadata';
478
+ readonly type: 'object';
479
+ readonly required: false;
480
+ readonly values: 'string';
481
+ }, {
482
+ readonly path: 'data.object.amount';
483
+ readonly type: 'number';
484
+ readonly required: true;
485
+ }, {
486
+ readonly path: 'data.object.amount_refunded';
487
+ readonly type: 'number';
488
+ readonly required: true;
489
+ }, {
490
+ readonly path: 'data.object.currency';
491
+ readonly type: 'string';
492
+ readonly required: true;
493
+ }, {
494
+ readonly path: 'data.object.refunded';
495
+ readonly type: 'boolean';
496
+ readonly required: true;
497
+ }, {
498
+ readonly path: 'data.object.payment_intent';
499
+ readonly type: 'string';
500
+ readonly required: false;
501
+ }];
502
+ };
503
+ };
504
+ };
505
+ readonly slack: {
506
+ readonly settings: readonly [{
507
+ readonly key: 'defaultChannel';
508
+ readonly label: 'Default channel';
509
+ readonly type: 'text';
510
+ readonly prefix: '#';
511
+ readonly maxLength: 80;
512
+ readonly required: false;
513
+ readonly helpText: 'The channel a message goes to when its step names none: #orders.';
514
+ }];
515
+ readonly actions: {
516
+ readonly postMessage: {
517
+ readonly action: 'postMessage';
518
+ readonly step: {
519
+ readonly input: readonly [{
520
+ readonly key: 'channel';
521
+ readonly type: 'text';
522
+ readonly required: false;
523
+ readonly setting: 'defaultChannel';
524
+ readonly description: 'The channel to post to, by name (#orders-review) or id. Absent, the defaultChannel setting.';
525
+ }, {
526
+ readonly key: 'text';
527
+ readonly type: 'text';
528
+ readonly required: true;
529
+ readonly description: 'The message, in Slack’s mrkdwn; also the notification text when blocks are sent.';
530
+ }, {
531
+ readonly key: 'blocks';
532
+ readonly type: 'json';
533
+ readonly required: false;
534
+ readonly description: 'Block Kit blocks to post instead of plain text.';
535
+ }];
536
+ readonly output: readonly [{
537
+ readonly key: 'ts';
538
+ readonly type: 'text';
539
+ readonly required: true;
540
+ readonly description: 'The posted message’s timestamp, Slack’s id for it in its channel.';
541
+ }, {
542
+ readonly key: 'channel';
543
+ readonly type: 'text';
544
+ readonly required: true;
545
+ readonly description: 'The channel the message was posted to, as Slack answered it.';
546
+ }];
547
+ readonly sample: {
548
+ readonly ts: 'sample';
549
+ readonly channel: 'sample';
550
+ };
551
+ readonly idempotencyHeader: null;
552
+ };
553
+ };
554
+ };
555
+ readonly triggers: {};
556
+ };
557
+ readonly resend: {
558
+ readonly settings: readonly [{
559
+ readonly key: 'from';
560
+ readonly label: 'From';
561
+ readonly type: 'text';
562
+ readonly maxLength: 320;
563
+ readonly required: false;
564
+ readonly helpText: 'Who an email is from when its step names no sender: an address on a domain verified in Resend, such as Acme <orders@acme.shop>.';
565
+ }];
566
+ readonly actions: {
567
+ readonly send: {
568
+ readonly action: 'send';
569
+ readonly step: {
570
+ readonly input: readonly [{
571
+ readonly key: 'to';
572
+ readonly type: 'text';
573
+ readonly required: true;
574
+ readonly description: 'Who the email is to: an address, or several separated by commas.';
575
+ }, {
576
+ readonly key: 'subject';
577
+ readonly type: 'text';
578
+ readonly required: true;
579
+ readonly description: 'The subject line.';
580
+ }, {
581
+ readonly key: 'html';
582
+ readonly type: 'text';
583
+ readonly required: false;
584
+ readonly description: 'The body as HTML. `html` or `text` is required, or both.';
585
+ }, {
586
+ readonly key: 'text';
587
+ readonly type: 'text';
588
+ readonly required: false;
589
+ readonly description: 'The body as plain text. `html` or `text` is required, or both.';
590
+ }, {
591
+ readonly key: 'from';
592
+ readonly type: 'text';
593
+ readonly required: false;
594
+ readonly setting: 'from';
595
+ readonly description: 'Who the email is from: an address on a domain verified in Resend. Absent, the Project’s `from` setting; with neither, the send is refused.';
596
+ }, {
597
+ readonly key: 'attachments';
598
+ readonly type: 'files';
599
+ readonly required: false;
600
+ readonly maxBytes: number;
601
+ readonly description: 'Files a Record of the run’s Dataset holds (`event.record.invoice`), attached under their names. The send point reads their bytes.';
602
+ }];
603
+ readonly output: readonly [{
604
+ readonly key: 'id';
605
+ readonly type: 'text';
606
+ readonly required: true;
607
+ readonly description: 'The id Resend gave the email.';
608
+ }];
609
+ readonly sample: {
610
+ readonly id: 'resend-sample';
611
+ };
612
+ readonly idempotencyHeader: "Idempotency-Key";
613
+ };
614
+ };
615
+ };
616
+ readonly triggers: {};
617
+ };
618
+ };
619
+ /** The Extensions a Project may declare, and what its code may name of each. */
620
+ type ExtensionSurfaces = typeof EXTENSION_SURFACES;
621
+
622
+ /**
623
+ * `@relyio/sdk/extensions` — the Extensions a Project uses (spec 3, REL-3846; ADR 0019).
624
+ *
625
+ * One file, `relyio/extensions.ts`, names each Extension the Project uses and its settings — set once
626
+ * for the Project, never per Environment — and hands out a typed handle for each. An Automation
627
+ * imports the handle: its Triggers start an Automation (`on: vercel.deploymentSucceeded()`), and its
628
+ * Actions are what `step.action` sends through, their input and output typed by the Extension.
629
+ *
630
+ * @example
631
+ * // relyio/extensions.ts
632
+ * import { defineExtensions } from '@relyio/sdk/extensions'
633
+ *
634
+ * const extensions = defineExtensions({
635
+ * slack: { defaultChannel: '#orders' },
636
+ * vercel: {},
637
+ * })
638
+ * export const { slack, vercel } = extensions
639
+ * export default extensions
640
+ *
641
+ * **Never a secret here.** A Connection — an API key, a signing secret, a webhook URL — is made in
642
+ * the Dashboard, once per Environment, and sealed. A setting is text anyone reading the repository
643
+ * may see. Declaring an Extension connects nothing: `relyio plan` says which still need connecting
644
+ * in Production, and which Automations will deploy paused there until they are. Preview never pauses:
645
+ * an Action with no Preview Connection is Recorded with its sample result.
646
+ *
647
+ * **Typed.** Each Extension's settings, Actions and Triggers are RelyIO's own declaration of it
648
+ * (`@relyio/api-contract`'s `EXTENSION_SURFACES`, which the Extension catalog delivers): an Extension
649
+ * RelyIO does not have, a setting it does not declare, or a value its setting does not take — a
650
+ * Slack channel without its `#` — is a type error, and the plan refuses it again for a Manifest read
651
+ * as data.
652
+ */
653
+
654
+ /** One Extension a Project uses: its settings, by key. */
655
+ interface ExtensionUse {
656
+ readonly settings: Readonly<Record<string, string>>;
657
+ }
658
+ /** What `relyio/extensions.ts` declares, as data: every Extension the Project uses, by slug. */
659
+ type ExtensionsDeclaration = Readonly<Record<string, ExtensionUse>>;
660
+ /** Every Extension a Project may declare, and what its code may name of each. */
661
+ type Surfaces = ExtensionSurfaces;
662
+ /** The value one setting takes: one of its options, text starting with its prefix, or text. */
663
+ type SettingValue<F extends ExtensionSettingField> = F extends {
664
+ readonly type: 'select';
665
+ readonly options: ReadonlyArray<infer Option>;
666
+ } ? Option : F extends {
667
+ readonly type: 'text';
668
+ readonly prefix: infer Prefix extends string;
669
+ } ? `${Prefix}${string}` : string;
670
+ type Flatten<T> = {
671
+ readonly [K in keyof T]: T[K];
672
+ };
673
+ /** The settings an Extension takes: its required ones required, the rest optional, nothing else. */
674
+ type SettingsFrom<F extends ExtensionSettingField> = Flatten<{
675
+ readonly [Field in F as Field extends {
676
+ readonly required: true;
677
+ } ? Field['key'] : never]: SettingValue<Field>;
678
+ } & {
679
+ readonly [Field in F as Field extends {
680
+ readonly required: true;
681
+ } ? never : Field['key']]?: SettingValue<Field>;
682
+ }>;
683
+ /**
684
+ * The settings `defineExtensions` takes for Extension `S`. An Extension with none takes `{}` and
685
+ * nothing in it.
686
+ */
687
+ type ExtensionSettings<S extends keyof Surfaces> = [
688
+ Surfaces[S]['settings'][number]
689
+ ] extends [never] ? Readonly<Record<string, never>> : SettingsFrom<Surfaces[S]['settings'][number]>;
690
+ /** What `defineExtensions` takes: each Extension the Project uses, by slug, with its settings. */
691
+ type ExtensionsConfig = {
692
+ readonly [S in keyof Surfaces]?: ExtensionSettings<S>;
693
+ };
694
+ /** A value field's JSON type, as TypeScript reads it. */
695
+ 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>>;
696
+ /** A dot-separated path's first segment. */
697
+ type Head<P extends string> = P extends `${infer First}.${string}` ? First : P;
698
+ /** The fields whose path starts with segment `K`. */
699
+ type Under<F extends ExtensionValueField, K extends string> = F extends ExtensionValueField ? Head<F['path']> extends K ? F : never : never;
700
+ /** The fields under `K`, their paths with `K.` taken off; never for the field at `K` itself. */
701
+ type Rebased<F extends ExtensionValueField, K extends string> = F extends ExtensionValueField ? F['path'] extends `${K}.${infer Rest}` ? Omit<F, 'path'> & {
702
+ readonly path: Rest;
703
+ } : never : never;
704
+ /** A field's value: its JSON type, and an object of strings for one declaring string values. */
705
+ type FieldValueOf<F extends ExtensionValueField> = F extends {
706
+ readonly values: 'string';
707
+ } ? Readonly<Record<string, string>> : JsonOf<F['type']>;
708
+ /** The value at `K`: the field there, or an object of the fields under it. */
709
+ type ValueAt<F extends ExtensionValueField, K extends string> = [
710
+ Extract<F, {
711
+ readonly path: K;
712
+ }>
713
+ ] extends [never] ? ValuesOf<Rebased<F, K>> : FieldValueOf<Extract<F, {
714
+ readonly path: K;
715
+ }>>;
716
+ /**
717
+ * Value fields as an object: `payload.data.object.id` a `string` at `payload.data.object.id`. A
718
+ * segment is required when a field under it is.
719
+ */
720
+ type ValuesOf<F extends ExtensionValueField> = Flatten<{
721
+ readonly [K in Head<F['path']> as true extends Under<F, K>['required'] ? K : never]: ValueAt<F, K>;
722
+ } & {
723
+ readonly [K in Head<F['path']> as true extends Under<F, K>['required'] ? never : K]?: ValueAt<F, K>;
724
+ }>;
725
+ /** A Trigger's payload, as an object; one that declares nothing, `{}`. */
726
+ type ValuesOfList<L extends readonly ExtensionValueField[]> = [L[number]] extends [never] ? Record<string, never> : ValuesOf<L[number]>;
727
+ /** An action field's value, as TypeScript reads it (`@relyio/api-contract`'s `ACTION_FIELD_TYPES`). */
728
+ 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;
729
+ /** An Action's input or output fields as an object: required ones required, the rest optional. */
730
+ type ActionValuesOf<L extends readonly ActionField[]> = Flatten<{
731
+ readonly [F in L[number] as F extends {
732
+ readonly required: true;
733
+ } ? F['key'] : never]: ActionValueOf<F['type']>;
734
+ } & {
735
+ readonly [F in L[number] as F extends {
736
+ readonly required: true;
737
+ } ? never : F['key']]?: ActionValueOf<F['type']>;
738
+ }>;
739
+ /** A Trigger's handle: call it for the Automation's `on`, with a `where` over its event if needed. */
740
+ type ExtensionTriggerHandle<P> = (options?: {
741
+ readonly where?: (event: Ref<ExtensionTriggerEvent<P>>) => ConditionTree;
742
+ }) => AutomationTriggerHandle<ExtensionTriggerEvent<P>>;
743
+ /**
744
+ * The handle an Extension's declaration gives: each of its Actions and Triggers, by the name it gives
745
+ * them, typed by their fields.
746
+ */
747
+ type HandleOf<X extends ExtensionSurface, S extends string = string> = {
748
+ readonly [A in keyof X['actions']]: X['actions'][A] extends {
749
+ readonly action: infer N extends string;
750
+ readonly step: {
751
+ readonly input: infer I extends readonly ActionField[];
752
+ readonly output: infer O extends readonly ActionField[];
753
+ };
754
+ } ? AutomationAction<ActionValuesOf<I>, ActionValuesOf<O>, S, N> : never;
755
+ } & {
756
+ readonly [T in keyof X['triggers']]: X['triggers'][T] extends {
757
+ readonly payload: infer P extends readonly ExtensionValueField[];
758
+ } ? ExtensionTriggerHandle<ValuesOfList<P>> : never;
759
+ };
760
+ /** Extension `S`'s handle. */
761
+ type ExtensionHandle<S extends keyof Surfaces> = HandleOf<Surfaces[S], S & string>;
762
+ /** Where the declaration rides on what `defineExtensions` returns. */
763
+ declare const DECLARED = "declared";
764
+ /**
765
+ * What `relyio/extensions.ts` default-exports: the declaration the CLI reads into the Manifest, and
766
+ * a handle for each Extension it declares.
767
+ */
768
+ type ExtensionsFile<C> = ExtensionsFileData & {
769
+ readonly [S in keyof C & keyof Surfaces]: ExtensionHandle<S>;
770
+ };
771
+ /** `C`, refusing a key no Extension declares — at the top and in each Extension's settings. */
772
+ type Exactly<C> = {
773
+ readonly [S in keyof C]: S extends keyof Surfaces ? C[S] & {
774
+ readonly [K in Exclude<keyof C[S], keyof ExtensionSettings<S>>]: never;
775
+ } : never;
776
+ };
777
+ /**
778
+ * Declare the Extensions the Project uses, with their settings, and take a typed handle for each.
779
+ * The file is `relyio/extensions.ts`; `relyio deploy` reads its default export into the Manifest.
780
+ */
781
+ declare function defineExtensions<const C extends ExtensionsConfig>(config: C & Exactly<C>): ExtensionsFile<C>;
782
+ /** What the CLI reads of `relyio/extensions.ts`' default export, whatever it declares. */
783
+ interface ExtensionsFileData {
784
+ readonly _tag: 'extensions';
785
+ readonly [DECLARED]: ExtensionsDeclaration;
786
+ }
787
+ /** Whether `value` is what `defineExtensions` returns: how the CLI reads `relyio/extensions.ts`. */
788
+ declare function isExtensionsFile(value: unknown): value is ExtensionsFileData;
789
+ /** The declaration as the Manifest carries it. */
790
+ declare function extensionsDeclarationOf(file: ExtensionsFileData): ExtensionsDeclaration;
791
+
792
+ export { type ExtensionHandle, type ExtensionSettings, type ExtensionTriggerHandle, type ExtensionUse, type ExtensionsConfig, type ExtensionsDeclaration, type ExtensionsFile, type ExtensionsFileData, type HandleOf, defineExtensions, extensionsDeclarationOf, isExtensionsFile };