@isomorph.ai/app-sdk 1.7.0 → 1.8.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/README.md +87 -0
- package/dist/index.d.ts +153 -14
- package/dist/index.js +10 -4
- package/dist/integration-execute-input-rules.d.ts +193 -3
- package/dist/integration-execute-input-rules.js +60 -3
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -36,6 +36,93 @@ Replace the connection/resource with the app's existing approved integration. To
|
|
|
36
36
|
|
|
37
37
|
For images call `generateImage({model: gateway.image(process.env.ISOMORPH_AI_IMAGE_MODEL_ALIAS!), prompt: 'A blue notebook'})`. Editing uses `prompt: {text: 'Make it red', images: [inputBytes]}` only when `isomorph ai models` advertises edit. Reuse app files upload/signed reads for authorized input and output storage; respect existing transfer/action limits and never return credentials.
|
|
38
38
|
|
|
39
|
+
## Company integrations
|
|
40
|
+
|
|
41
|
+
`isomorph.integrations.execute(connection, request)` is one method over a closed
|
|
42
|
+
set of operations; the operation fixes whose identity it runs under, and the
|
|
43
|
+
contract's bounds are checked in the client before any request is sent (the
|
|
44
|
+
same sentence the platform answers with). The connection, operation, resource
|
|
45
|
+
and `mode` are literals in the call: the kit gate derives
|
|
46
|
+
`.isomorph/integrations.json` from them. `isomorph dev` without `--real`
|
|
47
|
+
answers every operation from a local fixture.
|
|
48
|
+
|
|
49
|
+
Slack. A history, post, edit or delete runs as the app or as the signed-in
|
|
50
|
+
person (`mode`, the one IT approved for the app); a DM and a user lookup run
|
|
51
|
+
as the app only, against the fixed `workspace` resource. A post or DM needs a
|
|
52
|
+
fresh UUID per explicit Send press.
|
|
53
|
+
|
|
54
|
+
```ts
|
|
55
|
+
// A thread reply in Slack mrkdwn
|
|
56
|
+
const page = await isomorph.integrations.execute('company-slack', {
|
|
57
|
+
operation: 'slack.channel.history', resource: 'team-updates', input: { limit: 100 },
|
|
58
|
+
});
|
|
59
|
+
await isomorph.integrations.execute('company-slack', {
|
|
60
|
+
operation: 'slack.message.post', resource: 'team-updates',
|
|
61
|
+
input: { text: '*Done* — see <https://example.com/report|the report>', format: 'mrkdwn', threadId: page.messages[0].id },
|
|
62
|
+
idempotencyKey: crypto.randomUUID(), mode: 'app',
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
// Paged history: hasMore and nextCursor say when and how to read the next (older) page
|
|
66
|
+
const older = await isomorph.integrations.execute('company-slack', {
|
|
67
|
+
operation: 'slack.channel.history', resource: 'team-updates', input: { limit: 100, cursor: page.nextCursor },
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
// Edit or delete a message the app posted (its ts is the messageId)
|
|
71
|
+
const posted = await isomorph.integrations.execute('company-slack', {
|
|
72
|
+
operation: 'slack.message.post', resource: 'team-updates', input: { text: 'Deploy starting' }, idempotencyKey: crypto.randomUUID(), mode: 'app',
|
|
73
|
+
});
|
|
74
|
+
await isomorph.integrations.execute('company-slack', {
|
|
75
|
+
operation: 'slack.message.update', resource: 'team-updates', input: { messageId: posted.messageId, text: 'Deploy finished' }, mode: 'app',
|
|
76
|
+
});
|
|
77
|
+
await isomorph.integrations.execute('company-slack', {
|
|
78
|
+
operation: 'slack.message.delete', resource: 'team-updates', input: { messageId: posted.messageId }, mode: 'app',
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
// A direct message by email (1 to 8 people; several open one group DM) and a user lookup
|
|
82
|
+
await isomorph.integrations.execute('company-slack', {
|
|
83
|
+
operation: 'slack.dm.send', resource: 'workspace', input: { to: ['jane@example.com'], text: 'Your review is due today.' },
|
|
84
|
+
idempotencyKey: crypto.randomUUID(),
|
|
85
|
+
});
|
|
86
|
+
const { userId } = await isomorph.integrations.execute('company-slack', {
|
|
87
|
+
operation: 'slack.user.lookup', resource: 'workspace', input: { email: 'jane@example.com' },
|
|
88
|
+
});
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
Gmail runs as the signed-in person after their consent, on the fixed `inbox`
|
|
92
|
+
resource.
|
|
93
|
+
|
|
94
|
+
```ts
|
|
95
|
+
// Search, then read a whole thread
|
|
96
|
+
const found = await isomorph.integrations.execute('company-gmail', {
|
|
97
|
+
operation: 'gmail.thread.list', resource: 'inbox', input: { q: 'from:jane newer_than:7d', limit: 50 },
|
|
98
|
+
});
|
|
99
|
+
const thread = await isomorph.integrations.execute('company-gmail', {
|
|
100
|
+
operation: 'gmail.thread.read', resource: 'inbox', input: { threadId: found.threads[0].id },
|
|
101
|
+
});
|
|
102
|
+
// found.nextPageToken, when hasMore, is the next read's pageToken
|
|
103
|
+
|
|
104
|
+
// An attachment: a message or thread read names them (id, filename, mimeType, size); this fetches the bytes
|
|
105
|
+
const file = thread.emails[0].attachments?.[0];
|
|
106
|
+
if (file) {
|
|
107
|
+
const { dataBase64 } = await isomorph.integrations.execute('company-gmail', {
|
|
108
|
+
operation: 'gmail.attachment.read', resource: 'inbox', input: { messageId: thread.emails[0].id, attachmentId: file.id },
|
|
109
|
+
});
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
// An HTML send: text stays the plain-text alternative; up to 5 attachments of 2 MiB each
|
|
113
|
+
await isomorph.integrations.execute('company-gmail', {
|
|
114
|
+
operation: 'gmail.message.send', resource: 'inbox',
|
|
115
|
+
input: {
|
|
116
|
+
to: ['jane@example.com'], subject: 'Weekly report', text: 'The report is attached.',
|
|
117
|
+
html: '<p>The report is <b>attached</b>.</p>',
|
|
118
|
+
attachments: [{ filename: 'report.csv', contentType: 'text/csv', dataBase64: csvBase64 }],
|
|
119
|
+
},
|
|
120
|
+
idempotencyKey: crypto.randomUUID(),
|
|
121
|
+
});
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
(1.8.0; contracts 0.71.0)
|
|
125
|
+
|
|
39
126
|
## Browser read tools
|
|
40
127
|
|
|
41
128
|
Install optional peers `ai` and `playwright-core@1.58.2`. Locally run `npx playwright-core install chromium --only-shell`; V2 packages Chromium in the existing app image. An existing system executable can be supplied as executablePath.
|
package/dist/index.d.ts
CHANGED
|
@@ -24,30 +24,94 @@ export interface IsomorphResult<T> {
|
|
|
24
24
|
data: T;
|
|
25
25
|
count?: number;
|
|
26
26
|
}
|
|
27
|
+
/** How Slack renders a message's `text`: plain text, or Slack mrkdwn (`*bold*`, `<url|label>`, `<@U123>`); plain when absent. */
|
|
28
|
+
export type SlackTextFormat = 'plain' | 'mrkdwn';
|
|
29
|
+
/**
|
|
30
|
+
* Which of the modes IT approved for this app a declared-mode Slack operation runs under: `app` (the company's
|
|
31
|
+
* Slack app, under the IT-approved per-app name and footer) or `user` (the signed-in person, after their consent).
|
|
32
|
+
* Leave it out when the app is approved for one mode; an app approved for both must say (MODE_REQUIRED), and a
|
|
33
|
+
* mode IT has not approved is refused with MODE_NOT_GRANTED — never served under the other mode.
|
|
34
|
+
*/
|
|
35
|
+
export type SlackIdentityMode = 'app' | 'user';
|
|
36
|
+
/** Reads a channel's newest messages as the signed-in person (or as the app, when declared so); pages backwards with `cursor`. */
|
|
27
37
|
export interface SlackChannelHistoryRequest {
|
|
28
38
|
operation: 'slack.channel.history';
|
|
29
39
|
resource: string;
|
|
30
40
|
input?: {
|
|
31
|
-
/** How many
|
|
41
|
+
/** How many messages to read: an integer from 1 to 100 (the platform's ceiling); default 15. */
|
|
32
42
|
limit?: number;
|
|
43
|
+
/** The `nextCursor` of the previous page; leave it out to read the newest page. */
|
|
44
|
+
cursor?: string;
|
|
45
|
+
/** Only messages after this Slack ts (a message id). */
|
|
46
|
+
oldest?: string;
|
|
47
|
+
/** Only messages before this Slack ts (a message id). */
|
|
48
|
+
latest?: string;
|
|
33
49
|
};
|
|
50
|
+
mode?: SlackIdentityMode;
|
|
34
51
|
}
|
|
52
|
+
/** Posts one message to a channel, as the app or as the signed-in person; `threadId` makes it a thread reply. */
|
|
35
53
|
export interface SlackMessagePostRequest {
|
|
36
54
|
operation: 'slack.message.post';
|
|
37
55
|
resource: string;
|
|
38
56
|
input: {
|
|
39
|
-
/**
|
|
57
|
+
/** The body, 1 to 4000 characters; plain text unless `format` is `mrkdwn`. Posted as the app (under the IT-approved per-app name) or as the signed-in person (their consent), the same text either way. */
|
|
40
58
|
text: string;
|
|
59
|
+
/** The Slack ts of the message this one replies to (a history message id), so the post lands in that thread. */
|
|
60
|
+
threadId?: string;
|
|
61
|
+
format?: SlackTextFormat;
|
|
41
62
|
};
|
|
42
63
|
/** A fresh UUID per explicit Send press, reused only to retry that press. */
|
|
43
64
|
idempotencyKey: string;
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
65
|
+
mode?: SlackIdentityMode;
|
|
66
|
+
}
|
|
67
|
+
/** Edits a message the app (or the signed-in person, in user mode) posted; the whole `text` is replaced. */
|
|
68
|
+
export interface SlackMessageUpdateRequest {
|
|
69
|
+
operation: 'slack.message.update';
|
|
70
|
+
resource: string;
|
|
71
|
+
input: {
|
|
72
|
+
/** The Slack ts a post or history result named as `messageId`. */
|
|
73
|
+
messageId: string;
|
|
74
|
+
/** The new body, 1 to 4000 characters. */
|
|
75
|
+
text: string;
|
|
76
|
+
format?: SlackTextFormat;
|
|
77
|
+
};
|
|
78
|
+
mode?: SlackIdentityMode;
|
|
79
|
+
}
|
|
80
|
+
/** Deletes a message the app (or the signed-in person, in user mode) posted. */
|
|
81
|
+
export interface SlackMessageDeleteRequest {
|
|
82
|
+
operation: 'slack.message.delete';
|
|
83
|
+
resource: string;
|
|
84
|
+
input: {
|
|
85
|
+
/** The Slack ts a post or history result named as `messageId`. */
|
|
86
|
+
messageId: string;
|
|
87
|
+
};
|
|
88
|
+
mode?: SlackIdentityMode;
|
|
89
|
+
}
|
|
90
|
+
/**
|
|
91
|
+
* Sends a direct message as the company's Slack app to workspace members named by email (always app mode; the
|
|
92
|
+
* resource is the fixed `workspace` alias). Several recipients open one group DM.
|
|
93
|
+
*/
|
|
94
|
+
export interface SlackDmSendRequest {
|
|
95
|
+
operation: 'slack.dm.send';
|
|
96
|
+
resource: string;
|
|
97
|
+
input: {
|
|
98
|
+
/** 1 to 8 email addresses of workspace members, no repeats. */
|
|
99
|
+
to: string[];
|
|
100
|
+
/** The body, 1 to 4000 characters. */
|
|
101
|
+
text: string;
|
|
102
|
+
format?: SlackTextFormat;
|
|
103
|
+
};
|
|
104
|
+
/** A fresh UUID per explicit Send press, reused only to retry that press. */
|
|
105
|
+
idempotencyKey: string;
|
|
106
|
+
}
|
|
107
|
+
/** Resolves a workspace member's email to their Slack user id, as the app (the resource is the fixed `workspace` alias). */
|
|
108
|
+
export interface SlackUserLookupRequest {
|
|
109
|
+
operation: 'slack.user.lookup';
|
|
110
|
+
resource: string;
|
|
111
|
+
input: {
|
|
112
|
+
/** The member's email address. */
|
|
113
|
+
email: string;
|
|
114
|
+
};
|
|
51
115
|
}
|
|
52
116
|
/**
|
|
53
117
|
* One condition on a warehouse read; a read's conditions are AND-ed. `isNull`/`isNotNull` take no value, `in`/`notIn`
|
|
@@ -93,12 +157,28 @@ export interface WarehouseViewReadRequest {
|
|
|
93
157
|
limit?: number;
|
|
94
158
|
};
|
|
95
159
|
}
|
|
96
|
-
/** Gmail runs as the signed-in person after their consent. Resources are approved mailbox aliases. */
|
|
160
|
+
/** Gmail runs as the signed-in person after their consent. Resources are approved mailbox aliases (`inbox`). */
|
|
97
161
|
export interface GmailThreadListRequest {
|
|
98
162
|
operation: 'gmail.thread.list';
|
|
99
163
|
resource: string;
|
|
100
164
|
input?: {
|
|
165
|
+
/** Newest matching threads, 1 to 50; default 15. */
|
|
101
166
|
limit?: number;
|
|
167
|
+
/** A Gmail search query in the search-box syntax, e.g. `from:jane newer_than:7d`, 1 to 256 characters. */
|
|
168
|
+
q?: string;
|
|
169
|
+
/** 1 to 5 labels a thread must carry (all of them); `['INBOX']` when absent. */
|
|
170
|
+
labelIds?: string[];
|
|
171
|
+
/** The `nextPageToken` of the previous page; leave it out to read the first page. */
|
|
172
|
+
pageToken?: string;
|
|
173
|
+
};
|
|
174
|
+
}
|
|
175
|
+
/** Reads every message of one thread, oldest first, as the signed-in person. */
|
|
176
|
+
export interface GmailThreadReadRequest {
|
|
177
|
+
operation: 'gmail.thread.read';
|
|
178
|
+
resource: string;
|
|
179
|
+
input: {
|
|
180
|
+
/** The thread id a `gmail.thread.list` result named. */
|
|
181
|
+
threadId: string;
|
|
102
182
|
};
|
|
103
183
|
}
|
|
104
184
|
export interface GmailMessageReadRequest {
|
|
@@ -108,24 +188,47 @@ export interface GmailMessageReadRequest {
|
|
|
108
188
|
messageId: string;
|
|
109
189
|
};
|
|
110
190
|
}
|
|
191
|
+
/** Reads one attachment's bytes (at most 2 MiB) as the signed-in person; the ids come from a message or thread read. */
|
|
192
|
+
export interface GmailAttachmentReadRequest {
|
|
193
|
+
operation: 'gmail.attachment.read';
|
|
194
|
+
resource: string;
|
|
195
|
+
input: {
|
|
196
|
+
messageId: string;
|
|
197
|
+
/** The `id` of an entry in the message's `attachments`. */
|
|
198
|
+
attachmentId: string;
|
|
199
|
+
};
|
|
200
|
+
}
|
|
201
|
+
/** One file to attach to a send, carried inline as base64: at most 2 MiB of bytes (2,796,203 base64 characters). */
|
|
202
|
+
export interface GmailAttachmentInput {
|
|
203
|
+
/** The name the recipient sees, 1 to 255 characters. */
|
|
204
|
+
filename: string;
|
|
205
|
+
/** The MIME type, e.g. `application/pdf`. */
|
|
206
|
+
contentType: string;
|
|
207
|
+
/** The file bytes, standard base64. */
|
|
208
|
+
dataBase64: string;
|
|
209
|
+
}
|
|
111
210
|
export interface GmailMessageSendRequest {
|
|
112
211
|
operation: 'gmail.message.send';
|
|
113
212
|
resource: string;
|
|
114
213
|
input: {
|
|
115
|
-
/** 1 to 50 email addresses per recipient list. */
|
|
214
|
+
/** 1 to 50 email addresses per recipient list (at most 50 across to, cc and bcc). */
|
|
116
215
|
to: string[];
|
|
117
216
|
cc?: string[];
|
|
118
217
|
bcc?: string[];
|
|
119
218
|
/** 1 to 256 characters. */
|
|
120
219
|
subject: string;
|
|
121
|
-
/** Plain text, 1 to 4000 characters
|
|
220
|
+
/** Plain text, 1 to 4000 characters; the plain-text alternative when `html` is sent too. */
|
|
122
221
|
text: string;
|
|
222
|
+
/** An HTML body sent beside `text`, at most 64 KiB. */
|
|
223
|
+
html?: string;
|
|
224
|
+
/** 1 to 5 files, each at most 2 MiB. */
|
|
225
|
+
attachments?: GmailAttachmentInput[];
|
|
123
226
|
replyToMessageId?: string;
|
|
124
227
|
};
|
|
125
228
|
/** A fresh UUID per authorized Send action, reused only to retry that action. */
|
|
126
229
|
idempotencyKey: string;
|
|
127
230
|
}
|
|
128
|
-
export type IntegrationRequest = SlackChannelHistoryRequest | SlackMessagePostRequest | WarehouseViewReadRequest | GmailThreadListRequest | GmailMessageReadRequest | GmailMessageSendRequest;
|
|
231
|
+
export type IntegrationRequest = SlackChannelHistoryRequest | SlackMessagePostRequest | SlackMessageUpdateRequest | SlackMessageDeleteRequest | SlackDmSendRequest | SlackUserLookupRequest | WarehouseViewReadRequest | GmailThreadListRequest | GmailThreadReadRequest | GmailMessageReadRequest | GmailAttachmentReadRequest | GmailMessageSendRequest;
|
|
129
232
|
export type IntegrationOperation = IntegrationRequest['operation'];
|
|
130
233
|
export interface SlackMessage {
|
|
131
234
|
id: string;
|
|
@@ -140,10 +243,23 @@ export interface SlackMessage {
|
|
|
140
243
|
export interface SlackChannelHistoryResult {
|
|
141
244
|
messages: SlackMessage[];
|
|
142
245
|
hasMore: boolean;
|
|
246
|
+
/** The cursor of the next (older) page, present only when `hasMore`; pass it as the next read's `cursor`. */
|
|
247
|
+
nextCursor?: string;
|
|
143
248
|
}
|
|
249
|
+
/** The Slack ts of the message posted, edited or deleted. */
|
|
144
250
|
export interface SlackMessagePostResult {
|
|
145
251
|
messageId: string;
|
|
146
252
|
}
|
|
253
|
+
export interface SlackDmSendResult {
|
|
254
|
+
messageId: string;
|
|
255
|
+
/** The DM channel the message went to. */
|
|
256
|
+
channelId: string;
|
|
257
|
+
}
|
|
258
|
+
export interface SlackUserLookupResult {
|
|
259
|
+
userId: string;
|
|
260
|
+
/** The member's display name, or real name when they set none; may be empty. */
|
|
261
|
+
displayName: string;
|
|
262
|
+
}
|
|
147
263
|
export interface WarehouseViewReadResult {
|
|
148
264
|
rows: Array<Record<string, unknown>>;
|
|
149
265
|
/** True when more rows matched than `limit`: the rows are incomplete, so never total or average them in code. */
|
|
@@ -160,6 +276,16 @@ export interface GmailThreadSummary {
|
|
|
160
276
|
export interface GmailThreadListResult {
|
|
161
277
|
threads: GmailThreadSummary[];
|
|
162
278
|
hasMore: boolean;
|
|
279
|
+
/** The token of the next page, present only when `hasMore`; pass it as the next read's `pageToken`. */
|
|
280
|
+
nextPageToken?: string;
|
|
281
|
+
}
|
|
282
|
+
/** One attachment of a message, named without its bytes; `gmail.attachment.read` fetches them by `id`. */
|
|
283
|
+
export interface GmailAttachment {
|
|
284
|
+
id: string;
|
|
285
|
+
filename: string;
|
|
286
|
+
mimeType: string;
|
|
287
|
+
/** Bytes. */
|
|
288
|
+
size: number;
|
|
163
289
|
}
|
|
164
290
|
export interface GmailMessageReadResult {
|
|
165
291
|
id: string;
|
|
@@ -168,14 +294,27 @@ export interface GmailMessageReadResult {
|
|
|
168
294
|
from: string;
|
|
169
295
|
to: string;
|
|
170
296
|
date: string;
|
|
297
|
+
/** The plain text with HTML stripped, cut at 16 KiB with `truncated` set. */
|
|
171
298
|
text: string;
|
|
172
299
|
truncated: boolean;
|
|
300
|
+
attachments?: GmailAttachment[];
|
|
301
|
+
}
|
|
302
|
+
/** Every message of the thread, oldest first, each in the `gmail.message.read` shape. */
|
|
303
|
+
export interface GmailThreadReadResult {
|
|
304
|
+
id: string;
|
|
305
|
+
emails: GmailMessageReadResult[];
|
|
306
|
+
}
|
|
307
|
+
export interface GmailAttachmentReadResult {
|
|
308
|
+
filename: string;
|
|
309
|
+
mimeType: string;
|
|
310
|
+
size: number;
|
|
311
|
+
dataBase64: string;
|
|
173
312
|
}
|
|
174
313
|
export interface GmailMessageSendResult {
|
|
175
314
|
messageId: string;
|
|
176
315
|
threadId: string;
|
|
177
316
|
}
|
|
178
|
-
export type IntegrationResult<R extends IntegrationRequest> = R extends SlackChannelHistoryRequest ? SlackChannelHistoryResult : R extends SlackMessagePostRequest ? SlackMessagePostResult : R extends WarehouseViewReadRequest ? WarehouseViewReadResult : R extends GmailThreadListRequest ? GmailThreadListResult : R extends GmailMessageReadRequest ? GmailMessageReadResult : R extends GmailMessageSendRequest ? GmailMessageSendResult : never;
|
|
317
|
+
export type IntegrationResult<R extends IntegrationRequest> = R extends SlackChannelHistoryRequest ? SlackChannelHistoryResult : R extends SlackMessagePostRequest | SlackMessageUpdateRequest | SlackMessageDeleteRequest ? SlackMessagePostResult : R extends SlackDmSendRequest ? SlackDmSendResult : R extends SlackUserLookupRequest ? SlackUserLookupResult : R extends WarehouseViewReadRequest ? WarehouseViewReadResult : R extends GmailThreadListRequest ? GmailThreadListResult : R extends GmailThreadReadRequest ? GmailThreadReadResult : R extends GmailMessageReadRequest ? GmailMessageReadResult : R extends GmailAttachmentReadRequest ? GmailAttachmentReadResult : R extends GmailMessageSendRequest ? GmailMessageSendResult : never;
|
|
179
318
|
export type IntegrationConnectResult = {
|
|
180
319
|
status: 'connected';
|
|
181
320
|
accountLabel: string;
|
package/dist/index.js
CHANGED
|
@@ -28,6 +28,13 @@ function warehouseReadProblem({ columns, aggregates }) {
|
|
|
28
28
|
return { field: 'columns', message: 'columns may use "*" only as the sole entry' };
|
|
29
29
|
return undefined;
|
|
30
30
|
}
|
|
31
|
+
// The operations whose request carries a `mode`, and the only ones it is sent
|
|
32
|
+
// for (every other operation's mode is fixed, and the platform refuses the
|
|
33
|
+
// field there). The key type is derived from the union, so adding or removing
|
|
34
|
+
// `mode` on a request without changing this table does not compile.
|
|
35
|
+
const declaredModeOperations = {
|
|
36
|
+
'slack.channel.history': true, 'slack.message.post': true, 'slack.message.update': true, 'slack.message.delete': true,
|
|
37
|
+
};
|
|
31
38
|
/** The AI reason code carried by an error, when it is a governed-AI refusal. */
|
|
32
39
|
export function aiErrorCode(error) {
|
|
33
40
|
const code = error instanceof IsomorphError ? error.details?.code : undefined;
|
|
@@ -676,12 +683,11 @@ export function createClient(options = {}) {
|
|
|
676
683
|
if (problem) {
|
|
677
684
|
return Promise.reject(new IsomorphError('VALIDATION_FAILED', problem.message, undefined, { code: 'INPUT_INVALID', field: problem.field }));
|
|
678
685
|
}
|
|
679
|
-
// Exactly the contract's fields go on the wire: `mode`
|
|
680
|
-
//
|
|
681
|
-
// `identity` or any other stray field is not sent.
|
|
686
|
+
// Exactly the contract's fields go on the wire: `mode` only for the
|
|
687
|
+
// declaredModeOperations; an `identity` or any other stray field is not sent.
|
|
682
688
|
const { operation, resource, input, idempotencyKey, mode } = request;
|
|
683
689
|
return transport.json('/_harbour/integrations/execute', {
|
|
684
|
-
method: 'POST', body: JSON.stringify({ connection, operation, resource, ...(input !== undefined ? { input } : {}), ...(idempotencyKey !== undefined ? { idempotencyKey } : {}), ...(operation
|
|
690
|
+
method: 'POST', body: JSON.stringify({ connection, operation, resource, ...(input !== undefined ? { input } : {}), ...(idempotencyKey !== undefined ? { idempotencyKey } : {}), ...(operation in declaredModeOperations && mode !== undefined ? { mode } : {}) }),
|
|
685
691
|
});
|
|
686
692
|
},
|
|
687
693
|
// In a browser the consent returns the person to this page (without its
|
|
@@ -28,6 +28,7 @@ export type IntegrationExecuteInputFieldRule = {
|
|
|
28
28
|
maxLength?: number;
|
|
29
29
|
pattern?: string;
|
|
30
30
|
};
|
|
31
|
+
default?: readonly string[];
|
|
31
32
|
} | {
|
|
32
33
|
kind: 'enum';
|
|
33
34
|
required: boolean;
|
|
@@ -64,9 +65,27 @@ export declare const IntegrationExecuteInputRules: {
|
|
|
64
65
|
readonly kind: "integer";
|
|
65
66
|
readonly required: false;
|
|
66
67
|
readonly minimum: 1;
|
|
67
|
-
readonly maximum:
|
|
68
|
+
readonly maximum: 100;
|
|
68
69
|
readonly default: 15;
|
|
69
70
|
};
|
|
71
|
+
readonly cursor: {
|
|
72
|
+
readonly kind: "string";
|
|
73
|
+
readonly required: false;
|
|
74
|
+
readonly minLength: 1;
|
|
75
|
+
readonly maxLength: 256;
|
|
76
|
+
};
|
|
77
|
+
readonly oldest: {
|
|
78
|
+
readonly kind: "string";
|
|
79
|
+
readonly required: false;
|
|
80
|
+
readonly minLength: 1;
|
|
81
|
+
readonly maxLength: 64;
|
|
82
|
+
};
|
|
83
|
+
readonly latest: {
|
|
84
|
+
readonly kind: "string";
|
|
85
|
+
readonly required: false;
|
|
86
|
+
readonly minLength: 1;
|
|
87
|
+
readonly maxLength: 64;
|
|
88
|
+
};
|
|
70
89
|
};
|
|
71
90
|
};
|
|
72
91
|
readonly "slack.message.post": {
|
|
@@ -78,6 +97,93 @@ export declare const IntegrationExecuteInputRules: {
|
|
|
78
97
|
readonly minLength: 1;
|
|
79
98
|
readonly maxLength: 4000;
|
|
80
99
|
};
|
|
100
|
+
readonly threadId: {
|
|
101
|
+
readonly kind: "string";
|
|
102
|
+
readonly required: false;
|
|
103
|
+
readonly minLength: 0;
|
|
104
|
+
readonly pattern: "^[A-Za-z0-9._-]{1,64}$";
|
|
105
|
+
};
|
|
106
|
+
readonly format: {
|
|
107
|
+
readonly kind: "enum";
|
|
108
|
+
readonly required: false;
|
|
109
|
+
readonly values: readonly ["plain", "mrkdwn"];
|
|
110
|
+
readonly default: "plain";
|
|
111
|
+
};
|
|
112
|
+
};
|
|
113
|
+
};
|
|
114
|
+
readonly "slack.message.update": {
|
|
115
|
+
readonly idempotencyKey: false;
|
|
116
|
+
readonly fields: {
|
|
117
|
+
readonly messageId: {
|
|
118
|
+
readonly kind: "string";
|
|
119
|
+
readonly required: true;
|
|
120
|
+
readonly minLength: 0;
|
|
121
|
+
readonly pattern: "^[A-Za-z0-9._-]{1,64}$";
|
|
122
|
+
};
|
|
123
|
+
readonly text: {
|
|
124
|
+
readonly kind: "string";
|
|
125
|
+
readonly required: true;
|
|
126
|
+
readonly minLength: 1;
|
|
127
|
+
readonly maxLength: 4000;
|
|
128
|
+
};
|
|
129
|
+
readonly format: {
|
|
130
|
+
readonly kind: "enum";
|
|
131
|
+
readonly required: false;
|
|
132
|
+
readonly values: readonly ["plain", "mrkdwn"];
|
|
133
|
+
readonly default: "plain";
|
|
134
|
+
};
|
|
135
|
+
};
|
|
136
|
+
};
|
|
137
|
+
readonly "slack.message.delete": {
|
|
138
|
+
readonly idempotencyKey: false;
|
|
139
|
+
readonly fields: {
|
|
140
|
+
readonly messageId: {
|
|
141
|
+
readonly kind: "string";
|
|
142
|
+
readonly required: true;
|
|
143
|
+
readonly minLength: 0;
|
|
144
|
+
readonly pattern: "^[A-Za-z0-9._-]{1,64}$";
|
|
145
|
+
};
|
|
146
|
+
};
|
|
147
|
+
};
|
|
148
|
+
readonly "slack.dm.send": {
|
|
149
|
+
readonly idempotencyKey: true;
|
|
150
|
+
readonly fields: {
|
|
151
|
+
readonly to: {
|
|
152
|
+
readonly kind: "string[]";
|
|
153
|
+
readonly required: true;
|
|
154
|
+
readonly minItems: 1;
|
|
155
|
+
readonly maxItems: 8;
|
|
156
|
+
readonly uniqueItems: true;
|
|
157
|
+
readonly item: {
|
|
158
|
+
readonly minLength: 3;
|
|
159
|
+
readonly maxLength: 254;
|
|
160
|
+
readonly pattern: "^[^\\s@<>,;\"]+@[^\\s@<>,;\"]+\\.[^\\s@<>,;\"]+$";
|
|
161
|
+
};
|
|
162
|
+
};
|
|
163
|
+
readonly text: {
|
|
164
|
+
readonly kind: "string";
|
|
165
|
+
readonly required: true;
|
|
166
|
+
readonly minLength: 1;
|
|
167
|
+
readonly maxLength: 4000;
|
|
168
|
+
};
|
|
169
|
+
readonly format: {
|
|
170
|
+
readonly kind: "enum";
|
|
171
|
+
readonly required: false;
|
|
172
|
+
readonly values: readonly ["plain", "mrkdwn"];
|
|
173
|
+
readonly default: "plain";
|
|
174
|
+
};
|
|
175
|
+
};
|
|
176
|
+
};
|
|
177
|
+
readonly "slack.user.lookup": {
|
|
178
|
+
readonly idempotencyKey: false;
|
|
179
|
+
readonly fields: {
|
|
180
|
+
readonly email: {
|
|
181
|
+
readonly kind: "string";
|
|
182
|
+
readonly required: true;
|
|
183
|
+
readonly minLength: 3;
|
|
184
|
+
readonly maxLength: 254;
|
|
185
|
+
readonly pattern: "^[^\\s@<>,;\"]+@[^\\s@<>,;\"]+\\.[^\\s@<>,;\"]+$";
|
|
186
|
+
};
|
|
81
187
|
};
|
|
82
188
|
};
|
|
83
189
|
readonly "gmail.thread.list": {
|
|
@@ -87,9 +193,44 @@ export declare const IntegrationExecuteInputRules: {
|
|
|
87
193
|
readonly kind: "integer";
|
|
88
194
|
readonly required: false;
|
|
89
195
|
readonly minimum: 1;
|
|
90
|
-
readonly maximum:
|
|
196
|
+
readonly maximum: 50;
|
|
91
197
|
readonly default: 15;
|
|
92
198
|
};
|
|
199
|
+
readonly q: {
|
|
200
|
+
readonly kind: "string";
|
|
201
|
+
readonly required: false;
|
|
202
|
+
readonly minLength: 1;
|
|
203
|
+
readonly maxLength: 256;
|
|
204
|
+
};
|
|
205
|
+
readonly labelIds: {
|
|
206
|
+
readonly kind: "string[]";
|
|
207
|
+
readonly required: false;
|
|
208
|
+
readonly minItems: 1;
|
|
209
|
+
readonly maxItems: 5;
|
|
210
|
+
readonly uniqueItems: true;
|
|
211
|
+
readonly item: {
|
|
212
|
+
readonly minLength: 1;
|
|
213
|
+
readonly maxLength: 64;
|
|
214
|
+
};
|
|
215
|
+
readonly default: readonly ["INBOX"];
|
|
216
|
+
};
|
|
217
|
+
readonly pageToken: {
|
|
218
|
+
readonly kind: "string";
|
|
219
|
+
readonly required: false;
|
|
220
|
+
readonly minLength: 1;
|
|
221
|
+
readonly maxLength: 256;
|
|
222
|
+
};
|
|
223
|
+
};
|
|
224
|
+
};
|
|
225
|
+
readonly "gmail.thread.read": {
|
|
226
|
+
readonly idempotencyKey: false;
|
|
227
|
+
readonly fields: {
|
|
228
|
+
readonly threadId: {
|
|
229
|
+
readonly kind: "string";
|
|
230
|
+
readonly required: true;
|
|
231
|
+
readonly minLength: 0;
|
|
232
|
+
readonly pattern: "^[A-Za-z0-9._-]{1,64}$";
|
|
233
|
+
};
|
|
93
234
|
};
|
|
94
235
|
};
|
|
95
236
|
readonly "gmail.message.read": {
|
|
@@ -99,7 +240,7 @@ export declare const IntegrationExecuteInputRules: {
|
|
|
99
240
|
readonly kind: "string";
|
|
100
241
|
readonly required: true;
|
|
101
242
|
readonly minLength: 0;
|
|
102
|
-
readonly pattern: "^[A-Za-z0-
|
|
243
|
+
readonly pattern: "^[A-Za-z0-9._-]{1,64}$";
|
|
103
244
|
};
|
|
104
245
|
};
|
|
105
246
|
};
|
|
@@ -154,6 +295,38 @@ export declare const IntegrationExecuteInputRules: {
|
|
|
154
295
|
readonly minLength: 1;
|
|
155
296
|
readonly maxLength: 4000;
|
|
156
297
|
};
|
|
298
|
+
readonly html: {
|
|
299
|
+
readonly kind: "string";
|
|
300
|
+
readonly required: false;
|
|
301
|
+
readonly minLength: 1;
|
|
302
|
+
readonly maxLength: 65536;
|
|
303
|
+
};
|
|
304
|
+
readonly attachments: {
|
|
305
|
+
readonly kind: "object[]";
|
|
306
|
+
readonly required: false;
|
|
307
|
+
readonly minItems: 1;
|
|
308
|
+
readonly maxItems: 5;
|
|
309
|
+
readonly fields: {
|
|
310
|
+
readonly filename: {
|
|
311
|
+
readonly kind: "string";
|
|
312
|
+
readonly required: true;
|
|
313
|
+
readonly minLength: 1;
|
|
314
|
+
readonly maxLength: 255;
|
|
315
|
+
};
|
|
316
|
+
readonly contentType: {
|
|
317
|
+
readonly kind: "string";
|
|
318
|
+
readonly required: true;
|
|
319
|
+
readonly minLength: 1;
|
|
320
|
+
readonly maxLength: 128;
|
|
321
|
+
};
|
|
322
|
+
readonly dataBase64: {
|
|
323
|
+
readonly kind: "string";
|
|
324
|
+
readonly required: true;
|
|
325
|
+
readonly minLength: 1;
|
|
326
|
+
readonly maxLength: 2796203;
|
|
327
|
+
};
|
|
328
|
+
};
|
|
329
|
+
};
|
|
157
330
|
readonly replyToMessageId: {
|
|
158
331
|
readonly kind: "string";
|
|
159
332
|
readonly required: false;
|
|
@@ -162,6 +335,23 @@ export declare const IntegrationExecuteInputRules: {
|
|
|
162
335
|
};
|
|
163
336
|
};
|
|
164
337
|
};
|
|
338
|
+
readonly "gmail.attachment.read": {
|
|
339
|
+
readonly idempotencyKey: false;
|
|
340
|
+
readonly fields: {
|
|
341
|
+
readonly messageId: {
|
|
342
|
+
readonly kind: "string";
|
|
343
|
+
readonly required: true;
|
|
344
|
+
readonly minLength: 0;
|
|
345
|
+
readonly pattern: "^[A-Za-z0-9._-]{1,64}$";
|
|
346
|
+
};
|
|
347
|
+
readonly attachmentId: {
|
|
348
|
+
readonly kind: "string";
|
|
349
|
+
readonly required: true;
|
|
350
|
+
readonly minLength: 1;
|
|
351
|
+
readonly maxLength: 512;
|
|
352
|
+
};
|
|
353
|
+
};
|
|
354
|
+
};
|
|
165
355
|
readonly "warehouse.view.read": {
|
|
166
356
|
readonly idempotencyKey: false;
|
|
167
357
|
readonly fields: {
|
|
@@ -4,25 +4,67 @@ export const IntegrationExecuteInputRules = {
|
|
|
4
4
|
"slack.channel.history": {
|
|
5
5
|
idempotencyKey: false,
|
|
6
6
|
fields: {
|
|
7
|
-
limit: { kind: "integer", required: false, minimum: 1, maximum:
|
|
7
|
+
limit: { kind: "integer", required: false, minimum: 1, maximum: 100, default: 15 },
|
|
8
|
+
cursor: { kind: "string", required: false, minLength: 1, maxLength: 256 },
|
|
9
|
+
oldest: { kind: "string", required: false, minLength: 1, maxLength: 64 },
|
|
10
|
+
latest: { kind: "string", required: false, minLength: 1, maxLength: 64 },
|
|
8
11
|
},
|
|
9
12
|
},
|
|
10
13
|
"slack.message.post": {
|
|
11
14
|
idempotencyKey: true,
|
|
12
15
|
fields: {
|
|
13
16
|
text: { kind: "string", required: true, minLength: 1, maxLength: 4000 },
|
|
17
|
+
threadId: { kind: "string", required: false, minLength: 0, pattern: "^[A-Za-z0-9._-]{1,64}$" },
|
|
18
|
+
format: { kind: "enum", required: false, values: ["plain", "mrkdwn"], default: "plain" },
|
|
19
|
+
},
|
|
20
|
+
},
|
|
21
|
+
"slack.message.update": {
|
|
22
|
+
idempotencyKey: false,
|
|
23
|
+
fields: {
|
|
24
|
+
messageId: { kind: "string", required: true, minLength: 0, pattern: "^[A-Za-z0-9._-]{1,64}$" },
|
|
25
|
+
text: { kind: "string", required: true, minLength: 1, maxLength: 4000 },
|
|
26
|
+
format: { kind: "enum", required: false, values: ["plain", "mrkdwn"], default: "plain" },
|
|
27
|
+
},
|
|
28
|
+
},
|
|
29
|
+
"slack.message.delete": {
|
|
30
|
+
idempotencyKey: false,
|
|
31
|
+
fields: {
|
|
32
|
+
messageId: { kind: "string", required: true, minLength: 0, pattern: "^[A-Za-z0-9._-]{1,64}$" },
|
|
33
|
+
},
|
|
34
|
+
},
|
|
35
|
+
"slack.dm.send": {
|
|
36
|
+
idempotencyKey: true,
|
|
37
|
+
fields: {
|
|
38
|
+
to: { kind: "string[]", required: true, minItems: 1, maxItems: 8, uniqueItems: true, item: { minLength: 3, maxLength: 254, pattern: "^[^\\s@<>,;\"]+@[^\\s@<>,;\"]+\\.[^\\s@<>,;\"]+$" } },
|
|
39
|
+
text: { kind: "string", required: true, minLength: 1, maxLength: 4000 },
|
|
40
|
+
format: { kind: "enum", required: false, values: ["plain", "mrkdwn"], default: "plain" },
|
|
41
|
+
},
|
|
42
|
+
},
|
|
43
|
+
"slack.user.lookup": {
|
|
44
|
+
idempotencyKey: false,
|
|
45
|
+
fields: {
|
|
46
|
+
email: { kind: "string", required: true, minLength: 3, maxLength: 254, pattern: "^[^\\s@<>,;\"]+@[^\\s@<>,;\"]+\\.[^\\s@<>,;\"]+$" },
|
|
14
47
|
},
|
|
15
48
|
},
|
|
16
49
|
"gmail.thread.list": {
|
|
17
50
|
idempotencyKey: false,
|
|
18
51
|
fields: {
|
|
19
|
-
limit: { kind: "integer", required: false, minimum: 1, maximum:
|
|
52
|
+
limit: { kind: "integer", required: false, minimum: 1, maximum: 50, default: 15 },
|
|
53
|
+
q: { kind: "string", required: false, minLength: 1, maxLength: 256 },
|
|
54
|
+
labelIds: { kind: "string[]", required: false, minItems: 1, maxItems: 5, uniqueItems: true, item: { minLength: 1, maxLength: 64 }, default: ["INBOX"] },
|
|
55
|
+
pageToken: { kind: "string", required: false, minLength: 1, maxLength: 256 },
|
|
56
|
+
},
|
|
57
|
+
},
|
|
58
|
+
"gmail.thread.read": {
|
|
59
|
+
idempotencyKey: false,
|
|
60
|
+
fields: {
|
|
61
|
+
threadId: { kind: "string", required: true, minLength: 0, pattern: "^[A-Za-z0-9._-]{1,64}$" },
|
|
20
62
|
},
|
|
21
63
|
},
|
|
22
64
|
"gmail.message.read": {
|
|
23
65
|
idempotencyKey: false,
|
|
24
66
|
fields: {
|
|
25
|
-
messageId: { kind: "string", required: true, minLength: 0, pattern: "^[A-Za-z0-
|
|
67
|
+
messageId: { kind: "string", required: true, minLength: 0, pattern: "^[A-Za-z0-9._-]{1,64}$" },
|
|
26
68
|
},
|
|
27
69
|
},
|
|
28
70
|
"gmail.message.send": {
|
|
@@ -33,9 +75,22 @@ export const IntegrationExecuteInputRules = {
|
|
|
33
75
|
bcc: { kind: "string[]", required: false, minItems: 1, maxItems: 50, uniqueItems: true, item: { minLength: 3, maxLength: 254, pattern: "^[^\\s@<>,;\"]+@[^\\s@<>,;\"]+\\.[^\\s@<>,;\"]+$" } },
|
|
34
76
|
subject: { kind: "string", required: true, minLength: 1, maxLength: 256 },
|
|
35
77
|
text: { kind: "string", required: true, minLength: 1, maxLength: 4000 },
|
|
78
|
+
html: { kind: "string", required: false, minLength: 1, maxLength: 65536 },
|
|
79
|
+
attachments: { kind: "object[]", required: false, minItems: 1, maxItems: 5, fields: {
|
|
80
|
+
filename: { kind: "string", required: true, minLength: 1, maxLength: 255 },
|
|
81
|
+
contentType: { kind: "string", required: true, minLength: 1, maxLength: 128 },
|
|
82
|
+
dataBase64: { kind: "string", required: true, minLength: 1, maxLength: 2796203 },
|
|
83
|
+
} },
|
|
36
84
|
replyToMessageId: { kind: "string", required: false, minLength: 0, pattern: "^[A-Za-z0-9_-]{1,64}$" },
|
|
37
85
|
},
|
|
38
86
|
},
|
|
87
|
+
"gmail.attachment.read": {
|
|
88
|
+
idempotencyKey: false,
|
|
89
|
+
fields: {
|
|
90
|
+
messageId: { kind: "string", required: true, minLength: 0, pattern: "^[A-Za-z0-9._-]{1,64}$" },
|
|
91
|
+
attachmentId: { kind: "string", required: true, minLength: 1, maxLength: 512 },
|
|
92
|
+
},
|
|
93
|
+
},
|
|
39
94
|
"warehouse.view.read": {
|
|
40
95
|
idempotencyKey: false,
|
|
41
96
|
fields: {
|
|
@@ -105,6 +160,8 @@ function checkFields(operation, prefix, fields, given, nullIsAbsent) {
|
|
|
105
160
|
return refuse(path, `${path} is required for ${operation}`);
|
|
106
161
|
if ((field.kind === 'integer' || field.kind === 'enum') && field.default !== undefined)
|
|
107
162
|
out[name] = field.default;
|
|
163
|
+
if (field.kind === 'string[]' && field.default !== undefined)
|
|
164
|
+
out[name] = [...field.default];
|
|
108
165
|
continue;
|
|
109
166
|
}
|
|
110
167
|
const checked = checkField(operation, path, name, field, value);
|