@antasphere/chassis-contract 1.5.1 → 1.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/fleet.d.ts +10 -0
- package/dist/fleet.d.ts.map +1 -0
- package/dist/fleet.js +10 -0
- package/dist/fleet.js.map +1 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -1
- package/dist/routes/index.d.ts +1490 -9
- package/dist/routes/index.d.ts.map +1 -1
- package/dist/routes/index.js +155 -0
- package/dist/routes/index.js.map +1 -1
- package/dist/schemas/connections.d.ts +416 -0
- package/dist/schemas/connections.d.ts.map +1 -0
- package/dist/schemas/connections.js +141 -0
- package/dist/schemas/connections.js.map +1 -0
- package/package.json +3 -3
- package/src/fleet.ts +9 -0
- package/src/index.ts +2 -0
- package/src/routes/index.ts +204 -0
- package/src/schemas/connections.ts +174 -0
package/src/routes/index.ts
CHANGED
|
@@ -5,6 +5,17 @@ import { apiErrorSchema, cursorPageQuerySchema } from '../schemas/common.js';
|
|
|
5
5
|
import { instanceInfoSchema } from '../schemas/instance.js';
|
|
6
6
|
import { appsListSchema } from '../schemas/apps.js';
|
|
7
7
|
import { setupRequestSchema, setupResponseSchema } from '../schemas/setup.js';
|
|
8
|
+
import {
|
|
9
|
+
connectionDeliveriesListQuerySchema,
|
|
10
|
+
connectionDeliveriesListSchema,
|
|
11
|
+
connectionFieldsUpdateSchema,
|
|
12
|
+
connectionParamsSchema,
|
|
13
|
+
connectionSchema,
|
|
14
|
+
connectionSecretSchema,
|
|
15
|
+
connectionsListSchema,
|
|
16
|
+
connectionTestResultSchema,
|
|
17
|
+
connectionUrlMadeSchema
|
|
18
|
+
} from '../schemas/connections.js';
|
|
8
19
|
import { onboardingDismissedSchema } from '../schemas/me.js';
|
|
9
20
|
import {
|
|
10
21
|
defaultWorkspaceSchema,
|
|
@@ -1232,6 +1243,199 @@ export interface ChassisRoutesCopy {
|
|
|
1232
1243
|
* from its wording of the document — no module-level state, one instantiation
|
|
1233
1244
|
* per tool, and no route object that no tool serves.
|
|
1234
1245
|
*/
|
|
1246
|
+
// ── Connections (PRDCT-3773) ──────────────────────────────────────────────────
|
|
1247
|
+
// Every inbound webhook source and outbound key a tool holds, declared by the
|
|
1248
|
+
// tool and stored by the chassis: one card per source on Settings › Connections.
|
|
1249
|
+
// The reads are every non-guest member's; the writes an owner's or an admin's.
|
|
1250
|
+
// No answer carries a secret; the receiving URL is answered once, when made.
|
|
1251
|
+
|
|
1252
|
+
/** The refusals every connection route shares. */
|
|
1253
|
+
const connectionErrors = {
|
|
1254
|
+
401: errorResponses[401],
|
|
1255
|
+
403: jsonBody(apiErrorSchema, 'guest_forbidden, forbidden (not an owner or admin), or demo_session'),
|
|
1256
|
+
404: jsonBody(apiErrorSchema, 'not_found: no such source is declared by this instance')
|
|
1257
|
+
};
|
|
1258
|
+
|
|
1259
|
+
export const connectionsListRoute = createRoute({
|
|
1260
|
+
method: 'get',
|
|
1261
|
+
path: '/connections',
|
|
1262
|
+
tags: ['connections'],
|
|
1263
|
+
summary: 'Every connection source this instance declares, with its state in the workspace (never a secret)',
|
|
1264
|
+
responses: {
|
|
1265
|
+
200: jsonBody(connectionsListSchema, 'One entry per declared source, in the declared order'),
|
|
1266
|
+
401: errorResponses[401],
|
|
1267
|
+
403: jsonBody(apiErrorSchema, 'guest_forbidden')
|
|
1268
|
+
}
|
|
1269
|
+
});
|
|
1270
|
+
|
|
1271
|
+
export const connectionGetRoute = createRoute({
|
|
1272
|
+
method: 'get',
|
|
1273
|
+
path: '/connections/{source}',
|
|
1274
|
+
tags: ['connections'],
|
|
1275
|
+
summary: 'One connection source and its state in the workspace (never a secret, never the receiving URL)',
|
|
1276
|
+
request: { params: connectionParamsSchema },
|
|
1277
|
+
responses: { 200: jsonBody(connectionSchema, 'The connection'), ...connectionErrors }
|
|
1278
|
+
});
|
|
1279
|
+
|
|
1280
|
+
export const connectionConnectRoute = createRoute({
|
|
1281
|
+
method: 'post',
|
|
1282
|
+
path: '/connections/{source}',
|
|
1283
|
+
tags: ['connections'],
|
|
1284
|
+
summary:
|
|
1285
|
+
'Make the receiving URL of an inbound source, or rotate it (owner or admin): the URL is answered this once; the previous one answers 404 from now on; the secret stays',
|
|
1286
|
+
request: { params: connectionParamsSchema },
|
|
1287
|
+
responses: {
|
|
1288
|
+
200: jsonBody(connectionUrlMadeSchema, 'The connection and the receiving URL, shown once'),
|
|
1289
|
+
...connectionErrors,
|
|
1290
|
+
409: jsonBody(apiErrorSchema, 'not_inbound: a key source has no receiving URL')
|
|
1291
|
+
}
|
|
1292
|
+
});
|
|
1293
|
+
|
|
1294
|
+
export const connectionSecretRoute = createRoute({
|
|
1295
|
+
method: 'put',
|
|
1296
|
+
path: '/connections/{source}/secret',
|
|
1297
|
+
tags: ['connections'],
|
|
1298
|
+
summary:
|
|
1299
|
+
"Set the source's signing secret or key (owner or admin): stored sealed, never read back; a key source is probed first and refused when the source refuses the key",
|
|
1300
|
+
request: {
|
|
1301
|
+
params: connectionParamsSchema,
|
|
1302
|
+
body: jsonRequestBody(connectionSecretSchema, 'The secret or the key, as the source showed it')
|
|
1303
|
+
},
|
|
1304
|
+
responses: {
|
|
1305
|
+
200: jsonBody(connectionSchema, 'The connection'),
|
|
1306
|
+
400: jsonBody(
|
|
1307
|
+
apiErrorSchema,
|
|
1308
|
+
'Validation error, secret_invalid (not the shape the source declares) or secret_refused (the source refused the key; the detail says what it answered)'
|
|
1309
|
+
),
|
|
1310
|
+
...connectionErrors,
|
|
1311
|
+
409: jsonBody(apiErrorSchema, 'not_connected: make the receiving URL first'),
|
|
1312
|
+
502: jsonBody(apiErrorSchema, 'source_unavailable: the source did not answer the probe')
|
|
1313
|
+
}
|
|
1314
|
+
});
|
|
1315
|
+
|
|
1316
|
+
export const connectionFieldsRoute = createRoute({
|
|
1317
|
+
method: 'patch',
|
|
1318
|
+
path: '/connections/{source}',
|
|
1319
|
+
tags: ['connections'],
|
|
1320
|
+
summary:
|
|
1321
|
+
"Store the source's declared fields (owner or admin): a key absent keeps its value, null clears it",
|
|
1322
|
+
request: {
|
|
1323
|
+
params: connectionParamsSchema,
|
|
1324
|
+
body: jsonRequestBody(connectionFieldsUpdateSchema, 'The fields, by declared key')
|
|
1325
|
+
},
|
|
1326
|
+
responses: {
|
|
1327
|
+
200: jsonBody(connectionSchema, 'The connection'),
|
|
1328
|
+
400: jsonBody(apiErrorSchema, 'Validation error, unknown_field or field_required'),
|
|
1329
|
+
...connectionErrors,
|
|
1330
|
+
409: jsonBody(apiErrorSchema, 'not_connected: connect the source first')
|
|
1331
|
+
}
|
|
1332
|
+
});
|
|
1333
|
+
|
|
1334
|
+
export const connectionTestRoute = createRoute({
|
|
1335
|
+
method: 'post',
|
|
1336
|
+
path: '/connections/{source}/test',
|
|
1337
|
+
tags: ['connections'],
|
|
1338
|
+
summary:
|
|
1339
|
+
'Test the connection (owner or admin): a key source is probed with the stored key, an inbound source checks that its URL is made and its secret opens; the result is kept as the last check',
|
|
1340
|
+
request: { params: connectionParamsSchema },
|
|
1341
|
+
responses: {
|
|
1342
|
+
200: jsonBody(connectionTestResultSchema, 'What the test found'),
|
|
1343
|
+
...connectionErrors,
|
|
1344
|
+
409: jsonBody(apiErrorSchema, 'not_connected: nothing to test yet')
|
|
1345
|
+
}
|
|
1346
|
+
});
|
|
1347
|
+
|
|
1348
|
+
export const connectionDisconnectRoute = createRoute({
|
|
1349
|
+
method: 'delete',
|
|
1350
|
+
path: '/connections/{source}',
|
|
1351
|
+
tags: ['connections'],
|
|
1352
|
+
summary:
|
|
1353
|
+
'Disconnect the source (owner or admin): the secret, the receiving URL and the delivery log of the workspace are gone',
|
|
1354
|
+
request: { params: connectionParamsSchema },
|
|
1355
|
+
responses: { 200: jsonBody(connectionSchema, 'The connection, disconnected'), ...connectionErrors }
|
|
1356
|
+
});
|
|
1357
|
+
|
|
1358
|
+
export const connectionDeliveriesRoute = createRoute({
|
|
1359
|
+
method: 'get',
|
|
1360
|
+
path: '/connections/{source}/deliveries',
|
|
1361
|
+
tags: ['connections'],
|
|
1362
|
+
summary:
|
|
1363
|
+
'The deliveries the door took for this source, newest first (every member but a guest; cursor-paginated)',
|
|
1364
|
+
request: { params: connectionParamsSchema, query: connectionDeliveriesListQuerySchema },
|
|
1365
|
+
responses: {
|
|
1366
|
+
200: jsonBody(connectionDeliveriesListSchema, 'Deliveries, newest first'),
|
|
1367
|
+
400: errorResponses[400],
|
|
1368
|
+
...connectionErrors
|
|
1369
|
+
}
|
|
1370
|
+
});
|
|
1371
|
+
|
|
1372
|
+
/**
|
|
1373
|
+
* The sibling connect (PRDCT-3774): a source that declares a sibling tool of
|
|
1374
|
+
* the same hub is connected AS THE PERSON, in one call. The instance turns
|
|
1375
|
+
* the person's own hub grant into a two-minute token audienced to the
|
|
1376
|
+
* sibling (`POST /sso/connect-token` on the hub, scope `sso:connect`),
|
|
1377
|
+
* presents it at the sibling's `/sso/cli-connect` for a key of the person
|
|
1378
|
+
* there, registers its receiving URL as a webhook endpoint of the sibling
|
|
1379
|
+
* workspace that projects the same hub organization, stores the returned
|
|
1380
|
+
* signing secret sealed and the endpoint id, and revokes the key. Nothing of
|
|
1381
|
+
* the person outlives the call on the sibling; the person never sees a URL
|
|
1382
|
+
* or a secret. A re-run replaces the endpoint, the URL and the secret.
|
|
1383
|
+
*/
|
|
1384
|
+
const siblingRefusals = jsonBody(
|
|
1385
|
+
apiErrorSchema,
|
|
1386
|
+
'sibling_unknown (the hub lists no tool of that slug), sibling_scope_missing (the person must sign in again so their grant carries sso:connect), ' +
|
|
1387
|
+
'sibling_grant_missing (the person never opened the sibling: open it once, then retry), sibling_access_denied (the hub does not let this account use the sibling), ' +
|
|
1388
|
+
'sibling_workspace_missing (no workspace of the sibling projects this organization), sibling_refused (the sibling refused the endpoint: its message says why)'
|
|
1389
|
+
);
|
|
1390
|
+
|
|
1391
|
+
export const connectionSiblingConnectRoute = createRoute({
|
|
1392
|
+
method: 'post',
|
|
1393
|
+
path: '/connections/{source}/sibling',
|
|
1394
|
+
tags: ['connections'],
|
|
1395
|
+
summary:
|
|
1396
|
+
'Connect a sibling tool as the person (owner or admin): the endpoint is registered on the sibling and the secret stored, nothing shown; a re-run replaces them',
|
|
1397
|
+
request: { params: connectionParamsSchema },
|
|
1398
|
+
responses: {
|
|
1399
|
+
200: jsonBody(connectionSchema, 'The connection, with the endpoint registered on the sibling'),
|
|
1400
|
+
400: siblingRefusals,
|
|
1401
|
+
401: jsonBody(apiErrorSchema, 'unauthenticated, or hub_reauth_required (the person must sign in again)'),
|
|
1402
|
+
403: connectionErrors[403],
|
|
1403
|
+
404: connectionErrors[404],
|
|
1404
|
+
409: jsonBody(
|
|
1405
|
+
apiErrorSchema,
|
|
1406
|
+
'not_sibling (the source declares no sibling), not_inbound, sibling_unavailable (this instance is not on a hub), or sibling_busy (another connect of this source is running: retry in a moment)'
|
|
1407
|
+
),
|
|
1408
|
+
502: jsonBody(
|
|
1409
|
+
apiErrorSchema,
|
|
1410
|
+
'source_unavailable: the hub or the sibling did not answer; nothing changed'
|
|
1411
|
+
)
|
|
1412
|
+
}
|
|
1413
|
+
});
|
|
1414
|
+
|
|
1415
|
+
export const connectionSiblingDisconnectRoute = createRoute({
|
|
1416
|
+
method: 'delete',
|
|
1417
|
+
path: '/connections/{source}/sibling',
|
|
1418
|
+
tags: ['connections'],
|
|
1419
|
+
summary:
|
|
1420
|
+
"Disconnect a sibling source as the person (owner or admin): the endpoint is deleted on the sibling, then the connection's secret, URL and log here",
|
|
1421
|
+
request: { params: connectionParamsSchema },
|
|
1422
|
+
responses: {
|
|
1423
|
+
200: jsonBody(connectionSchema, 'The connection, disconnected on both sides'),
|
|
1424
|
+
400: siblingRefusals,
|
|
1425
|
+
401: jsonBody(apiErrorSchema, 'unauthenticated, or hub_reauth_required (the person must sign in again)'),
|
|
1426
|
+
403: connectionErrors[403],
|
|
1427
|
+
404: connectionErrors[404],
|
|
1428
|
+
409: jsonBody(
|
|
1429
|
+
apiErrorSchema,
|
|
1430
|
+
'not_sibling, sibling_unavailable (this instance is not on a hub), or sibling_busy (another connect of this source is running: retry in a moment)'
|
|
1431
|
+
),
|
|
1432
|
+
502: jsonBody(
|
|
1433
|
+
apiErrorSchema,
|
|
1434
|
+
'source_unavailable: the hub or the sibling did not answer; nothing changed'
|
|
1435
|
+
)
|
|
1436
|
+
}
|
|
1437
|
+
});
|
|
1438
|
+
|
|
1235
1439
|
export function defineChassisRoutes<TScope extends string>(
|
|
1236
1440
|
contract: ChassisContract<TScope>,
|
|
1237
1441
|
identity: Pick<ToolIdentity, 'cliKeyScopesLabel'> & { scopes: Pick<ToolIdentity['scopes'], 'dataExport'> },
|
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import { cursorPageQuerySchema, hasControlChars, plainText } from './common.js';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Connections (PRDCT-3773): every inbound webhook source a tool receives from
|
|
6
|
+
* and every outbound key it holds, declared by the tool (the `connections`
|
|
7
|
+
* slot: a key, a name, a kind, a verifier, the fields the source carries, a
|
|
8
|
+
* handler), stored by the chassis (one row per workspace and source: the
|
|
9
|
+
* sealed secret or key, the receiving token's hash, who connected it and
|
|
10
|
+
* when, the declared fields, the last delivery), served on ONE page
|
|
11
|
+
* (Settings › Connections, one card per source), one CLI group and one set
|
|
12
|
+
* of MCP tools. The door, `POST /hooks/{source}/{token}`, lives outside
|
|
13
|
+
* `/api/v1`: the signature is the credential.
|
|
14
|
+
*
|
|
15
|
+
* No answer ever carries a secret, a key or the receiving URL's token: the
|
|
16
|
+
* URL is answered once, when it is made or rotated, and the secret never.
|
|
17
|
+
*/
|
|
18
|
+
|
|
19
|
+
/** A source key: lowercase, digits and underscores, starting with a letter (`meet`, `lemlist`). */
|
|
20
|
+
export const connectionSourceKeySchema = z
|
|
21
|
+
.string()
|
|
22
|
+
.min(1)
|
|
23
|
+
.max(40)
|
|
24
|
+
.regex(/^[a-z][a-z0-9_]*$/, 'lowercase letters, digits and underscores, starting with a letter');
|
|
25
|
+
|
|
26
|
+
/** `inbound`: a source that posts to this instance; `key`: a key this instance holds; `both`. */
|
|
27
|
+
export const connectionKindSchema = z.enum(['inbound', 'key', 'both']);
|
|
28
|
+
export type ConnectionKind = z.infer<typeof connectionKindSchema>;
|
|
29
|
+
|
|
30
|
+
/** The path of the door, outside `/api/v1`: `<PUBLIC_BASE_URL>/hooks/<source>/<token>`. */
|
|
31
|
+
export const CONNECTION_HOOK_PATH = '/hooks';
|
|
32
|
+
|
|
33
|
+
/** One declared field of a source, as the page and the CLI show it. */
|
|
34
|
+
export const connectionFieldDeclarationSchema = z.object({
|
|
35
|
+
key: z.string(),
|
|
36
|
+
label: z.string(),
|
|
37
|
+
description: z.string().nullable(),
|
|
38
|
+
required: z.boolean(),
|
|
39
|
+
/** The value is a list of strings rather than one string. */
|
|
40
|
+
multiple: z.boolean()
|
|
41
|
+
});
|
|
42
|
+
export type ConnectionFieldDeclaration = z.infer<typeof connectionFieldDeclarationSchema>;
|
|
43
|
+
|
|
44
|
+
/** What the tool declared about a source: the static half of a card. */
|
|
45
|
+
export const connectionDeclaredSchema = z.object({
|
|
46
|
+
description: z.string().nullable(),
|
|
47
|
+
/** `standard-webhooks`, `stripe`, or `custom` (the tool's own function); null for a key source. */
|
|
48
|
+
verifier: z.string().nullable(),
|
|
49
|
+
/** The event types the source sends (an inbound source); informative. */
|
|
50
|
+
events: z.array(z.string()),
|
|
51
|
+
/** What the secret looks like, for the form's placeholder (`whsec_…`); never a value. */
|
|
52
|
+
secretHint: z.string().nullable(),
|
|
53
|
+
fields: z.array(connectionFieldDeclarationSchema),
|
|
54
|
+
/** The source is a sibling tool of the same hub (PRDCT-3774): its registry slug and the events to subscribe. */
|
|
55
|
+
sibling: z.object({ toolSlug: z.string(), events: z.array(z.string()) }).nullable()
|
|
56
|
+
});
|
|
57
|
+
export type ConnectionDeclared = z.infer<typeof connectionDeclaredSchema>;
|
|
58
|
+
|
|
59
|
+
const member = z.object({ id: z.string(), name: z.string(), email: z.string() });
|
|
60
|
+
|
|
61
|
+
/** A field's stored value: a string, a list of strings, or null (unset). */
|
|
62
|
+
export const connectionFieldValueSchema = z.union([
|
|
63
|
+
plainText(0, 2000),
|
|
64
|
+
z.array(plainText(1, 2000)).max(200),
|
|
65
|
+
z.null()
|
|
66
|
+
]);
|
|
67
|
+
export type ConnectionFieldValue = z.infer<typeof connectionFieldValueSchema>;
|
|
68
|
+
|
|
69
|
+
/** The stored fields of a connection, by declared key. */
|
|
70
|
+
export const connectionFieldsSchema = z.record(connectionSourceKeySchema, connectionFieldValueSchema);
|
|
71
|
+
export type ConnectionFields = z.infer<typeof connectionFieldsSchema>;
|
|
72
|
+
|
|
73
|
+
/** The outcome of one delivery the door took. */
|
|
74
|
+
export const connectionDeliveryOutcomeSchema = z.enum(['processed', 'ignored', 'failed']);
|
|
75
|
+
export type ConnectionDeliveryOutcome = z.infer<typeof connectionDeliveryOutcomeSchema>;
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* One source as the page, the CLI and an agent read it: the declaration,
|
|
79
|
+
* whether it is connected, who made the receiving URL and when (never the
|
|
80
|
+
* URL), whether the secret is set and readable, by whom and when (never the
|
|
81
|
+
* secret), the stored fields, the last delivery and the last test.
|
|
82
|
+
*/
|
|
83
|
+
export const connectionSchema = z.object({
|
|
84
|
+
source: connectionSourceKeySchema,
|
|
85
|
+
name: z.string(),
|
|
86
|
+
kind: connectionKindSchema,
|
|
87
|
+
declared: connectionDeclaredSchema,
|
|
88
|
+
/** An inbound source: the URL is made and the secret set; a key source: the key is set. */
|
|
89
|
+
connected: z.boolean(),
|
|
90
|
+
/** The receiving URL's state (an inbound source): when the current one was made and by whom; null until made. Never the URL. */
|
|
91
|
+
receiving: z.object({ madeAt: z.string(), madeBy: member.nullable() }).nullable(),
|
|
92
|
+
/** The secret's state: never the secret. */
|
|
93
|
+
secretState: z.object({
|
|
94
|
+
set: z.boolean(),
|
|
95
|
+
/** False when the stored value no longer opens (the instance's auth secret changed): set it again. */
|
|
96
|
+
readable: z.boolean(),
|
|
97
|
+
setAt: z.string().nullable(),
|
|
98
|
+
setBy: member.nullable()
|
|
99
|
+
}),
|
|
100
|
+
fields: connectionFieldsSchema,
|
|
101
|
+
/** When the connection row was first made, by whom. */
|
|
102
|
+
connectedAt: z.string().nullable(),
|
|
103
|
+
connectedBy: member.nullable(),
|
|
104
|
+
lastDelivery: z
|
|
105
|
+
.object({ at: z.string(), type: z.string(), outcome: connectionDeliveryOutcomeSchema })
|
|
106
|
+
.nullable(),
|
|
107
|
+
/** The last test or probe: when, whether it passed, what it said. */
|
|
108
|
+
check: z.object({ at: z.string(), ok: z.boolean(), detail: z.string() }).nullable(),
|
|
109
|
+
/** A sibling source (PRDCT-3774): the endpoint registered on the sibling, null until connected as the person. */
|
|
110
|
+
sibling: z.object({ endpointId: z.string().nullable() }).nullable()
|
|
111
|
+
});
|
|
112
|
+
export type Connection = z.infer<typeof connectionSchema>;
|
|
113
|
+
|
|
114
|
+
export const connectionsListSchema = z.object({ connections: z.array(connectionSchema) });
|
|
115
|
+
export type ConnectionsList = z.infer<typeof connectionsListSchema>;
|
|
116
|
+
|
|
117
|
+
/** `POST /connections/{source}`: the connection and the receiving URL, shown this once. */
|
|
118
|
+
export const connectionUrlMadeSchema = z.object({
|
|
119
|
+
connection: connectionSchema,
|
|
120
|
+
/** `<PUBLIC_BASE_URL>/hooks/<source>/<token>`: only the token's hash is kept, so the URL is never shown again. */
|
|
121
|
+
receivingUrl: z.string()
|
|
122
|
+
});
|
|
123
|
+
export type ConnectionUrlMade = z.infer<typeof connectionUrlMadeSchema>;
|
|
124
|
+
|
|
125
|
+
/** `PUT /connections/{source}/secret`: the secret or the key as the source showed it. */
|
|
126
|
+
export const connectionSecretSchema = z.object({
|
|
127
|
+
secret: z
|
|
128
|
+
.string()
|
|
129
|
+
.trim()
|
|
130
|
+
.min(1)
|
|
131
|
+
.max(4096)
|
|
132
|
+
.refine((v) => !/\s/.test(v) && !hasControlChars(v), {
|
|
133
|
+
error: 'must not contain whitespace or control characters'
|
|
134
|
+
})
|
|
135
|
+
});
|
|
136
|
+
export type ConnectionSecret = z.input<typeof connectionSecretSchema>;
|
|
137
|
+
|
|
138
|
+
/** `PATCH /connections/{source}`: the declared fields to store (a key absent keeps its value; null clears it). */
|
|
139
|
+
export const connectionFieldsUpdateSchema = z.object({ fields: connectionFieldsSchema });
|
|
140
|
+
export type ConnectionFieldsUpdate = z.infer<typeof connectionFieldsUpdateSchema>;
|
|
141
|
+
|
|
142
|
+
/** `POST /connections/{source}/test`: what the test found, and the connection after it. */
|
|
143
|
+
export const connectionTestResultSchema = z.object({
|
|
144
|
+
ok: z.boolean(),
|
|
145
|
+
detail: z.string(),
|
|
146
|
+
connection: connectionSchema
|
|
147
|
+
});
|
|
148
|
+
export type ConnectionTestResult = z.infer<typeof connectionTestResultSchema>;
|
|
149
|
+
|
|
150
|
+
/** One delivery the door took for a source of a workspace. */
|
|
151
|
+
export const connectionDeliverySchema = z.object({
|
|
152
|
+
id: z.string(),
|
|
153
|
+
source: connectionSourceKeySchema,
|
|
154
|
+
/** The sender's own id of the delivery (the `webhook-id` header, Stripe's event id): the dedupe key. */
|
|
155
|
+
deliveryId: z.string(),
|
|
156
|
+
type: z.string(),
|
|
157
|
+
receivedAt: z.string(),
|
|
158
|
+
outcome: connectionDeliveryOutcomeSchema,
|
|
159
|
+
/** What the handler said, or null. */
|
|
160
|
+
detail: z.string().nullable(),
|
|
161
|
+
/** Why the handler failed, or null. */
|
|
162
|
+
error: z.string().nullable()
|
|
163
|
+
});
|
|
164
|
+
export type ConnectionDelivery = z.infer<typeof connectionDeliverySchema>;
|
|
165
|
+
|
|
166
|
+
export const connectionDeliveriesListSchema = z.object({
|
|
167
|
+
deliveries: z.array(connectionDeliverySchema),
|
|
168
|
+
nextCursor: z.string().nullable()
|
|
169
|
+
});
|
|
170
|
+
export type ConnectionDeliveriesList = z.infer<typeof connectionDeliveriesListSchema>;
|
|
171
|
+
|
|
172
|
+
export const connectionDeliveriesListQuerySchema = cursorPageQuerySchema;
|
|
173
|
+
|
|
174
|
+
export const connectionParamsSchema = z.object({ source: connectionSourceKeySchema });
|