@slates/client 1.0.0-rc.11
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/index.cjs +409 -0
- package/dist/index.d.cts +227 -0
- package/dist/index.d.ts +227 -0
- package/dist/index.module.js +383 -0
- package/package.json +43 -0
- package/src/client.test.ts +845 -0
- package/src/client.ts +401 -0
- package/src/error.ts +159 -0
- package/src/index.ts +10 -0
- package/src/transport.ts +57 -0
- package/src/types.ts +36 -0
package/src/client.ts
ADDED
|
@@ -0,0 +1,401 @@
|
|
|
1
|
+
import {
|
|
2
|
+
SLATES_PROTOCOL_VERSION,
|
|
3
|
+
type SlateAuthenticationMethod,
|
|
4
|
+
type SlatesAction,
|
|
5
|
+
type SlatesMessageActionGetResponse,
|
|
6
|
+
type SlatesMessageActionInvokeResponse,
|
|
7
|
+
type SlatesMessageActionsListResponse,
|
|
8
|
+
type SlatesMessageActionTriggerEventMapResponse,
|
|
9
|
+
type SlatesMessageActionTriggerWebhookHandleResponse,
|
|
10
|
+
type SlatesMessageActionTriggerWebhookRegisterResponse,
|
|
11
|
+
type SlatesMessageActionTriggerWebhookUnregisterResponse,
|
|
12
|
+
type SlatesMessageAuthAuthorizationUrlGetResponse,
|
|
13
|
+
type SlatesMessageAuthDefaultInputGetResponse,
|
|
14
|
+
type SlatesMessageAuthInputChangedResponse,
|
|
15
|
+
type SlatesMessageAuthMethodGetResponse,
|
|
16
|
+
type SlatesMessageAuthOutputGetResponse,
|
|
17
|
+
type SlatesMessageAuthProfileGetResponse,
|
|
18
|
+
type SlatesMessageAuthTokenRefreshHandleResponse,
|
|
19
|
+
type SlatesMessageConfigChangedResponse,
|
|
20
|
+
type SlatesMessageConfigDefaultGetResponse,
|
|
21
|
+
type SlatesMessageConfigSchemaGetResponse,
|
|
22
|
+
type SlatesMessageProviderIdentifyResponse,
|
|
23
|
+
type SlatesParticipant,
|
|
24
|
+
type SlatesRequests,
|
|
25
|
+
type SlatesResponsesByMethod
|
|
26
|
+
} from '@slates/proto';
|
|
27
|
+
import { randomUUID } from 'crypto';
|
|
28
|
+
import { SlateProtocolError } from './error';
|
|
29
|
+
import type { SlatesClientState, SlatesProtocolClientOptions } from './types';
|
|
30
|
+
|
|
31
|
+
let createDefaultParticipants = (): SlatesParticipant[] => [
|
|
32
|
+
{
|
|
33
|
+
type: 'consumer',
|
|
34
|
+
id: 'slates-client',
|
|
35
|
+
name: 'Slates Client'
|
|
36
|
+
}
|
|
37
|
+
];
|
|
38
|
+
|
|
39
|
+
export class SlatesProtocolClient {
|
|
40
|
+
readonly transport: SlatesProtocolClientOptions['transport'];
|
|
41
|
+
state: SlatesClientState;
|
|
42
|
+
|
|
43
|
+
constructor(opts: SlatesProtocolClientOptions) {
|
|
44
|
+
this.transport = opts.transport;
|
|
45
|
+
this.state = {
|
|
46
|
+
protocol: SLATES_PROTOCOL_VERSION,
|
|
47
|
+
participants: opts.participants ?? createDefaultParticipants(),
|
|
48
|
+
config: opts.state?.config ?? null,
|
|
49
|
+
auth: opts.state?.auth ?? null,
|
|
50
|
+
session: opts.state?.session ?? null
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
setParticipants(participants: SlatesParticipant[]) {
|
|
55
|
+
this.state.participants = participants;
|
|
56
|
+
return this;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
setConfig(config: Record<string, any> | null) {
|
|
60
|
+
this.state.config = config;
|
|
61
|
+
return this;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
setAuth(auth: SlatesClientState['auth']) {
|
|
65
|
+
this.state.auth = auth;
|
|
66
|
+
return this;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
clearAuth() {
|
|
70
|
+
this.state.auth = null;
|
|
71
|
+
return this;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
setSession(session: SlatesClientState['session']) {
|
|
75
|
+
this.state.session = session;
|
|
76
|
+
return this;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
ensureSession() {
|
|
80
|
+
if (!this.state.session) {
|
|
81
|
+
this.state.session = {
|
|
82
|
+
id: randomUUID(),
|
|
83
|
+
state: {}
|
|
84
|
+
};
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
return this.state.session;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
private buildStateMessages() {
|
|
91
|
+
return [
|
|
92
|
+
{
|
|
93
|
+
jsonrpc: '2.0' as const,
|
|
94
|
+
method: 'slates/hello' as const,
|
|
95
|
+
params: { protocol: this.state.protocol }
|
|
96
|
+
},
|
|
97
|
+
{
|
|
98
|
+
jsonrpc: '2.0' as const,
|
|
99
|
+
method: 'slates/participant.set' as const,
|
|
100
|
+
params: { participants: this.state.participants }
|
|
101
|
+
},
|
|
102
|
+
...(this.state.config
|
|
103
|
+
? [
|
|
104
|
+
{
|
|
105
|
+
jsonrpc: '2.0' as const,
|
|
106
|
+
method: 'slates/config.set' as const,
|
|
107
|
+
params: { config: this.state.config }
|
|
108
|
+
}
|
|
109
|
+
]
|
|
110
|
+
: []),
|
|
111
|
+
...(this.state.auth
|
|
112
|
+
? [
|
|
113
|
+
{
|
|
114
|
+
jsonrpc: '2.0' as const,
|
|
115
|
+
method: 'slates/auth.set' as const,
|
|
116
|
+
params: {
|
|
117
|
+
authenticationMethodId: this.state.auth.authenticationMethodId,
|
|
118
|
+
output: this.state.auth.output
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
]
|
|
122
|
+
: []),
|
|
123
|
+
...(this.state.session
|
|
124
|
+
? [
|
|
125
|
+
{
|
|
126
|
+
jsonrpc: '2.0' as const,
|
|
127
|
+
method: 'slates/session.start' as const,
|
|
128
|
+
params: {
|
|
129
|
+
sessionId: this.state.session.id,
|
|
130
|
+
state: this.state.session.state
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
]
|
|
134
|
+
: [])
|
|
135
|
+
];
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
async request<Key extends keyof SlatesResponsesByMethod & SlatesRequests['method']>(
|
|
139
|
+
method: Key,
|
|
140
|
+
params: Extract<SlatesRequests, { method: Key }>['params']
|
|
141
|
+
): Promise<SlatesResponsesByMethod[Key]['result']> {
|
|
142
|
+
let id = randomUUID();
|
|
143
|
+
let responses = await this.transport.send([
|
|
144
|
+
...this.buildStateMessages(),
|
|
145
|
+
{
|
|
146
|
+
jsonrpc: '2.0',
|
|
147
|
+
id,
|
|
148
|
+
method,
|
|
149
|
+
params
|
|
150
|
+
} as Extract<SlatesRequests, { method: Key }>
|
|
151
|
+
]);
|
|
152
|
+
|
|
153
|
+
let response = responses.find(message => 'id' in message && message.id === id) as
|
|
154
|
+
| { result?: any; error?: any }
|
|
155
|
+
| undefined;
|
|
156
|
+
|
|
157
|
+
if (!response) {
|
|
158
|
+
throw new Error(`No response was returned for method ${String(method)}.`);
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
if (response.error) {
|
|
162
|
+
throw SlateProtocolError.fromResponse(response.error);
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
return response.result;
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
async identify(): Promise<SlatesMessageProviderIdentifyResponse['result']> {
|
|
169
|
+
return this.request('slates/provider.identify', {});
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
async listActions(): Promise<SlatesMessageActionsListResponse['result']> {
|
|
173
|
+
return this.request('slates/actions.list', {});
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
async listTools(): Promise<SlatesAction[]> {
|
|
177
|
+
let result = await this.listActions();
|
|
178
|
+
return result.actions.filter(action => action.type === 'action.tool');
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
async listTriggers(): Promise<SlatesAction[]> {
|
|
182
|
+
let result = await this.listActions();
|
|
183
|
+
return result.actions.filter(action => action.type === 'action.trigger');
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
async getAction(actionId: string): Promise<SlatesMessageActionGetResponse['result']> {
|
|
187
|
+
return this.request('slates/action.get', { actionId });
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
async getTool(actionId: string) {
|
|
191
|
+
let result = await this.getAction(actionId);
|
|
192
|
+
if (result.action.type !== 'action.tool') {
|
|
193
|
+
throw new Error(`Action ${actionId} is not a tool.`);
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
return result.action;
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
async getTrigger(actionId: string) {
|
|
200
|
+
let result = await this.getAction(actionId);
|
|
201
|
+
if (result.action.type !== 'action.trigger') {
|
|
202
|
+
throw new Error(`Action ${actionId} is not a trigger.`);
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
return result.action;
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
async getConfigSchema(): Promise<SlatesMessageConfigSchemaGetResponse['result']> {
|
|
209
|
+
return this.request('slates/config.schema.get', {});
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
async getDefaultConfig(): Promise<SlatesMessageConfigDefaultGetResponse['result']> {
|
|
213
|
+
return this.request('slates/config.get_default', {});
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
async updateConfig(
|
|
217
|
+
previousConfig: Record<string, any> | null,
|
|
218
|
+
newConfig: Record<string, any>
|
|
219
|
+
): Promise<SlatesMessageConfigChangedResponse['result']> {
|
|
220
|
+
return this.request('slates/config.changed', {
|
|
221
|
+
previousConfig,
|
|
222
|
+
newConfig
|
|
223
|
+
});
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
async listAuthMethods(): Promise<{ authenticationMethods: SlateAuthenticationMethod[] }> {
|
|
227
|
+
return this.request('slates/auth.methods.list', {});
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
async getAuthMethod(
|
|
231
|
+
authenticationMethodId: string
|
|
232
|
+
): Promise<SlatesMessageAuthMethodGetResponse['result']> {
|
|
233
|
+
return this.request('slates/auth.method.get', {
|
|
234
|
+
authenticationMethodId
|
|
235
|
+
});
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
async getDefaultAuthInput(
|
|
239
|
+
authenticationMethodId: string
|
|
240
|
+
): Promise<SlatesMessageAuthDefaultInputGetResponse['result']> {
|
|
241
|
+
return this.request('slates/auth.input.get_default', {
|
|
242
|
+
authenticationMethodId
|
|
243
|
+
});
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
async updateAuthInput(d: {
|
|
247
|
+
authenticationMethodId: string;
|
|
248
|
+
previousInput: Record<string, any> | null;
|
|
249
|
+
newInput: Record<string, any>;
|
|
250
|
+
}): Promise<SlatesMessageAuthInputChangedResponse['result']> {
|
|
251
|
+
return this.request('slates/auth.input.changed', {
|
|
252
|
+
authenticationMethodId: d.authenticationMethodId,
|
|
253
|
+
previousInput: d.previousInput,
|
|
254
|
+
newInput: d.newInput
|
|
255
|
+
});
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
async getAuthOutput(d: {
|
|
259
|
+
authenticationMethodId: string;
|
|
260
|
+
input: Record<string, any>;
|
|
261
|
+
}): Promise<SlatesMessageAuthOutputGetResponse['result']> {
|
|
262
|
+
return this.request('slates/auth.output.get', {
|
|
263
|
+
authenticationMethodId: d.authenticationMethodId,
|
|
264
|
+
input: d.input
|
|
265
|
+
});
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
async getAuthorizationUrl(d: {
|
|
269
|
+
authenticationMethodId: string;
|
|
270
|
+
redirectUri: string;
|
|
271
|
+
state: string;
|
|
272
|
+
input: Record<string, any>;
|
|
273
|
+
clientId: string;
|
|
274
|
+
clientSecret: string;
|
|
275
|
+
scopes: string[];
|
|
276
|
+
}): Promise<SlatesMessageAuthAuthorizationUrlGetResponse['result']> {
|
|
277
|
+
return this.request('slates/auth.authorization_url.get', d);
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
async handleAuthorizationCallback(d: {
|
|
281
|
+
authenticationMethodId: string;
|
|
282
|
+
code: string;
|
|
283
|
+
state: string;
|
|
284
|
+
redirectUri: string;
|
|
285
|
+
input: Record<string, any>;
|
|
286
|
+
clientId: string;
|
|
287
|
+
clientSecret: string;
|
|
288
|
+
scopes: string[];
|
|
289
|
+
callbackParams?: Record<string, string>;
|
|
290
|
+
callbackState?: Record<string, any>;
|
|
291
|
+
}): Promise<{
|
|
292
|
+
output: Record<string, any>;
|
|
293
|
+
input?: Record<string, any>;
|
|
294
|
+
scopes?: string[];
|
|
295
|
+
}> {
|
|
296
|
+
return this.request('slates/auth.authorization_callback.handle', d);
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
async refreshToken(d: {
|
|
300
|
+
authenticationMethodId: string;
|
|
301
|
+
output: Record<string, any>;
|
|
302
|
+
input: Record<string, any>;
|
|
303
|
+
clientId: string;
|
|
304
|
+
clientSecret: string;
|
|
305
|
+
scopes: string[];
|
|
306
|
+
}): Promise<SlatesMessageAuthTokenRefreshHandleResponse['result']> {
|
|
307
|
+
return this.request('slates/auth.token_refresh.handle', d);
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
async getAuthProfile(d: {
|
|
311
|
+
authenticationMethodId: string;
|
|
312
|
+
output: Record<string, any>;
|
|
313
|
+
input: Record<string, any>;
|
|
314
|
+
scopes: string[];
|
|
315
|
+
}): Promise<SlatesMessageAuthProfileGetResponse['result']> {
|
|
316
|
+
return this.request('slates/auth.profile.get', d);
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
async invokeTool(
|
|
320
|
+
actionId: string,
|
|
321
|
+
input: Record<string, any>
|
|
322
|
+
): Promise<SlatesMessageActionInvokeResponse['result']> {
|
|
323
|
+
this.ensureSession();
|
|
324
|
+
return this.request('slates/action.tool.invoke', {
|
|
325
|
+
actionId,
|
|
326
|
+
input
|
|
327
|
+
});
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
async mapTriggerEvent(
|
|
331
|
+
actionId: string,
|
|
332
|
+
input: Record<string, any>
|
|
333
|
+
): Promise<SlatesMessageActionTriggerEventMapResponse['result']> {
|
|
334
|
+
this.ensureSession();
|
|
335
|
+
return this.request('slates/action.trigger.map_event', {
|
|
336
|
+
actionId,
|
|
337
|
+
input
|
|
338
|
+
});
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
async registerTriggerWebhook(
|
|
342
|
+
actionId: string,
|
|
343
|
+
webhookBaseUrl: string
|
|
344
|
+
): Promise<SlatesMessageActionTriggerWebhookRegisterResponse['result']> {
|
|
345
|
+
this.ensureSession();
|
|
346
|
+
return this.request('slates/action.trigger.webhook_register', {
|
|
347
|
+
actionId,
|
|
348
|
+
webhookBaseUrl
|
|
349
|
+
});
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
async handleTriggerWebhook(d: {
|
|
353
|
+
actionId: string;
|
|
354
|
+
url: string;
|
|
355
|
+
method: string;
|
|
356
|
+
headers?: Record<string, string>;
|
|
357
|
+
body?: string | Uint8Array | null;
|
|
358
|
+
state?: any;
|
|
359
|
+
}): Promise<SlatesMessageActionTriggerWebhookHandleResponse['result']> {
|
|
360
|
+
this.ensureSession();
|
|
361
|
+
let encodedBody =
|
|
362
|
+
typeof d.body === 'string'
|
|
363
|
+
? Buffer.from(d.body, 'utf-8').toString('base64')
|
|
364
|
+
: d.body
|
|
365
|
+
? Buffer.from(d.body).toString('base64')
|
|
366
|
+
: null;
|
|
367
|
+
|
|
368
|
+
return this.request('slates/action.trigger.webhook_handle', {
|
|
369
|
+
actionId: d.actionId,
|
|
370
|
+
url: d.url,
|
|
371
|
+
method: d.method,
|
|
372
|
+
headers: d.headers ?? {},
|
|
373
|
+
body: encodedBody
|
|
374
|
+
? {
|
|
375
|
+
encoding: 'base64',
|
|
376
|
+
content: encodedBody
|
|
377
|
+
}
|
|
378
|
+
: null,
|
|
379
|
+
state: d.state ?? null
|
|
380
|
+
});
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
async unregisterTriggerWebhook(d: {
|
|
384
|
+
actionId: string;
|
|
385
|
+
webhookBaseUrl: string;
|
|
386
|
+
registrationDetails: any;
|
|
387
|
+
state?: any;
|
|
388
|
+
}): Promise<SlatesMessageActionTriggerWebhookUnregisterResponse['result']> {
|
|
389
|
+
this.ensureSession();
|
|
390
|
+
return this.request('slates/action.trigger.webhook_unregister', {
|
|
391
|
+
actionId: d.actionId,
|
|
392
|
+
webhookBaseUrl: d.webhookBaseUrl,
|
|
393
|
+
registrationDetails: d.registrationDetails,
|
|
394
|
+
state: d.state ?? null
|
|
395
|
+
});
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
async close() {
|
|
399
|
+
await this.transport.close?.();
|
|
400
|
+
}
|
|
401
|
+
}
|
package/src/error.ts
ADDED
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
export type SlateProtocolErrorSource = 'provider' | 'transport';
|
|
2
|
+
|
|
3
|
+
export type SlateProtocolErrorKind =
|
|
4
|
+
| 'declaration'
|
|
5
|
+
| 'validation'
|
|
6
|
+
| 'request'
|
|
7
|
+
| 'auth'
|
|
8
|
+
| 'config'
|
|
9
|
+
| 'resource'
|
|
10
|
+
| 'payment'
|
|
11
|
+
| 'upstream'
|
|
12
|
+
| 'transport'
|
|
13
|
+
| 'internal';
|
|
14
|
+
|
|
15
|
+
export interface SlateProtocolErrorResponse {
|
|
16
|
+
code: string;
|
|
17
|
+
message: string;
|
|
18
|
+
kind: SlateProtocolErrorKind;
|
|
19
|
+
retryable?: boolean;
|
|
20
|
+
status?: number;
|
|
21
|
+
issues?: Record<string, unknown>[];
|
|
22
|
+
provider?: Record<string, unknown>;
|
|
23
|
+
upstream?: Record<string, unknown>;
|
|
24
|
+
baggage?: Record<string, unknown>;
|
|
25
|
+
requestTraces?: Record<string, unknown>[];
|
|
26
|
+
[key: string]: unknown;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
let isRecord = (value: unknown): value is Record<string, unknown> =>
|
|
30
|
+
typeof value === 'object' && value !== null && !Array.isArray(value);
|
|
31
|
+
|
|
32
|
+
let isSlateErrorResponse = (error: unknown): error is SlateProtocolErrorResponse =>
|
|
33
|
+
isRecord(error) && typeof error.code === 'string' && typeof error.message === 'string';
|
|
34
|
+
|
|
35
|
+
let inferKindFromCode = (code: string): SlateProtocolErrorKind => {
|
|
36
|
+
if (code.startsWith('declaration.')) return 'declaration';
|
|
37
|
+
if (code.startsWith('input.')) return 'validation';
|
|
38
|
+
if (code.startsWith('request.')) return 'request';
|
|
39
|
+
if (code.startsWith('config.')) return 'config';
|
|
40
|
+
if (code.startsWith('auth.') || code.startsWith('permission.')) return 'auth';
|
|
41
|
+
if (code.startsWith('resource.')) return 'resource';
|
|
42
|
+
if (code.startsWith('payment.')) return 'payment';
|
|
43
|
+
if (code.startsWith('transport.')) return 'transport';
|
|
44
|
+
if (code.startsWith('upstream.')) return 'upstream';
|
|
45
|
+
return 'internal';
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
let normalizeResponse = (
|
|
49
|
+
error: unknown,
|
|
50
|
+
source: SlateProtocolErrorSource,
|
|
51
|
+
defaults: Partial<SlateProtocolErrorResponse> = {}
|
|
52
|
+
): SlateProtocolErrorResponse => {
|
|
53
|
+
if (isSlateErrorResponse(error)) {
|
|
54
|
+
return {
|
|
55
|
+
...defaults,
|
|
56
|
+
...error,
|
|
57
|
+
kind: error.kind ?? inferKindFromCode(error.code)
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
if (isRecord(error) && typeof error.message === 'string') {
|
|
62
|
+
let code =
|
|
63
|
+
typeof error.code === 'string'
|
|
64
|
+
? error.code
|
|
65
|
+
: (defaults.code ??
|
|
66
|
+
(source === 'transport' ? 'transport.invoke_failed' : 'internal.unexpected'));
|
|
67
|
+
|
|
68
|
+
return {
|
|
69
|
+
...defaults,
|
|
70
|
+
...error,
|
|
71
|
+
code,
|
|
72
|
+
message: error.message,
|
|
73
|
+
kind:
|
|
74
|
+
(typeof error.kind === 'string'
|
|
75
|
+
? (error.kind as SlateProtocolErrorKind)
|
|
76
|
+
: defaults.kind) ?? inferKindFromCode(code)
|
|
77
|
+
};
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
let code =
|
|
81
|
+
defaults.code ??
|
|
82
|
+
(source === 'transport' ? 'transport.invoke_failed' : 'internal.unexpected');
|
|
83
|
+
|
|
84
|
+
return {
|
|
85
|
+
...defaults,
|
|
86
|
+
code,
|
|
87
|
+
message:
|
|
88
|
+
error instanceof Error
|
|
89
|
+
? error.message
|
|
90
|
+
: (defaults.message ?? 'The slate returned an unexpected error.'),
|
|
91
|
+
kind: defaults.kind ?? inferKindFromCode(code),
|
|
92
|
+
baggage: {
|
|
93
|
+
...(defaults.baggage ?? {}),
|
|
94
|
+
...(error instanceof Error ? { originalName: error.name } : { originalValue: error })
|
|
95
|
+
}
|
|
96
|
+
};
|
|
97
|
+
};
|
|
98
|
+
|
|
99
|
+
export class SlateProtocolError extends Error {
|
|
100
|
+
cause?: unknown;
|
|
101
|
+
data: SlateProtocolErrorResponse;
|
|
102
|
+
source: SlateProtocolErrorSource;
|
|
103
|
+
|
|
104
|
+
constructor(
|
|
105
|
+
data: SlateProtocolErrorResponse,
|
|
106
|
+
source: SlateProtocolErrorSource = 'provider',
|
|
107
|
+
cause?: unknown
|
|
108
|
+
) {
|
|
109
|
+
super(data.message);
|
|
110
|
+
this.name = 'SlateProtocolError';
|
|
111
|
+
this.data = data;
|
|
112
|
+
this.source = source;
|
|
113
|
+
this.cause = cause;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
get code() {
|
|
117
|
+
return this.data.code;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
get kind() {
|
|
121
|
+
return this.data.kind;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
get retryable() {
|
|
125
|
+
return this.data.retryable;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
get status() {
|
|
129
|
+
return this.data.status;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
toJSON() {
|
|
133
|
+
return {
|
|
134
|
+
...this.data,
|
|
135
|
+
source: this.source
|
|
136
|
+
};
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
static is(error: unknown): error is SlateProtocolError {
|
|
140
|
+
return error instanceof SlateProtocolError;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
static fromResponse(
|
|
144
|
+
error: unknown,
|
|
145
|
+
source: SlateProtocolErrorSource = 'provider'
|
|
146
|
+
): SlateProtocolError {
|
|
147
|
+
if (SlateProtocolError.is(error)) return error;
|
|
148
|
+
return new SlateProtocolError(normalizeResponse(error, source), source, error);
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
static fromUnknown(
|
|
152
|
+
error: unknown,
|
|
153
|
+
defaults: Partial<SlateProtocolErrorResponse> = {},
|
|
154
|
+
source: SlateProtocolErrorSource = 'transport'
|
|
155
|
+
) {
|
|
156
|
+
if (SlateProtocolError.is(error)) return error;
|
|
157
|
+
return new SlateProtocolError(normalizeResponse(error, source, defaults), source, error);
|
|
158
|
+
}
|
|
159
|
+
}
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { SlatesProtocolClient } from './client';
|
|
2
|
+
import type { SlatesProtocolClientOptions } from './types';
|
|
3
|
+
|
|
4
|
+
export * from './client';
|
|
5
|
+
export * from './error';
|
|
6
|
+
export * from './transport';
|
|
7
|
+
export * from './types';
|
|
8
|
+
|
|
9
|
+
export let createSlatesClient = (opts: SlatesProtocolClientOptions) =>
|
|
10
|
+
new SlatesProtocolClient(opts);
|
package/src/transport.ts
ADDED
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import {
|
|
2
|
+
type SlatesNotifications,
|
|
3
|
+
SlatesProviderProtoHandlerManager,
|
|
4
|
+
type SlatesRequests,
|
|
5
|
+
type SlatesResponses
|
|
6
|
+
} from '@slates/proto';
|
|
7
|
+
import type { Slate, SlateLogListener } from '@slates/provider';
|
|
8
|
+
import { createProviderHandler } from '@slates/provider-handler';
|
|
9
|
+
import { SlateProtocolError } from './error';
|
|
10
|
+
import type { SlatesMessageTransport } from './types';
|
|
11
|
+
|
|
12
|
+
type ProviderMessage = SlatesNotifications | SlatesRequests;
|
|
13
|
+
type ProviderResponse = SlatesNotifications | SlatesResponses;
|
|
14
|
+
|
|
15
|
+
let toTransportError = (value: unknown, defaultMessage: string) =>
|
|
16
|
+
SlateProtocolError.fromUnknown(
|
|
17
|
+
value,
|
|
18
|
+
{
|
|
19
|
+
code: 'transport.invoke_failed',
|
|
20
|
+
kind: 'transport',
|
|
21
|
+
message: defaultMessage,
|
|
22
|
+
retryable: true,
|
|
23
|
+
baggage: {
|
|
24
|
+
response: value as any
|
|
25
|
+
}
|
|
26
|
+
},
|
|
27
|
+
'transport'
|
|
28
|
+
);
|
|
29
|
+
|
|
30
|
+
export let createLocalSlateTransport = <ConfigType extends {}, AuthType extends {}>(d: {
|
|
31
|
+
slate: Slate<ConfigType, AuthType>;
|
|
32
|
+
listeners?: SlateLogListener[];
|
|
33
|
+
}): SlatesMessageTransport => {
|
|
34
|
+
let managerPromise = createProviderHandler(d.slate, d.listeners ?? []).run();
|
|
35
|
+
|
|
36
|
+
return {
|
|
37
|
+
async send(messages) {
|
|
38
|
+
let manager = await managerPromise;
|
|
39
|
+
let responses: ProviderResponse[] = [];
|
|
40
|
+
|
|
41
|
+
for (let message of messages as ProviderMessage[]) {
|
|
42
|
+
let response: any;
|
|
43
|
+
try {
|
|
44
|
+
response = await SlatesProviderProtoHandlerManager.handleInput(manager, message);
|
|
45
|
+
} catch (error) {
|
|
46
|
+
throw toTransportError(error, 'Local slate invocation failed');
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
if (response) {
|
|
50
|
+
responses.push(response as ProviderResponse);
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
return responses;
|
|
55
|
+
}
|
|
56
|
+
};
|
|
57
|
+
};
|
package/src/types.ts
ADDED
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
SlatesNotifications,
|
|
3
|
+
SlatesParticipant,
|
|
4
|
+
SlatesProtocolVersion,
|
|
5
|
+
SlatesRequests,
|
|
6
|
+
SlatesResponses
|
|
7
|
+
} from '@slates/proto';
|
|
8
|
+
|
|
9
|
+
export type SlatesJsonObject = Record<string, any>;
|
|
10
|
+
export type SlatesProtocolMessage = SlatesNotifications | SlatesRequests;
|
|
11
|
+
export type SlatesProtocolResponse = SlatesNotifications | SlatesResponses;
|
|
12
|
+
|
|
13
|
+
export interface SlatesClientState {
|
|
14
|
+
protocol: SlatesProtocolVersion;
|
|
15
|
+
participants: SlatesParticipant[];
|
|
16
|
+
config: SlatesJsonObject | null;
|
|
17
|
+
auth: {
|
|
18
|
+
authenticationMethodId: string;
|
|
19
|
+
output: SlatesJsonObject;
|
|
20
|
+
} | null;
|
|
21
|
+
session: {
|
|
22
|
+
id: string;
|
|
23
|
+
state: SlatesJsonObject;
|
|
24
|
+
} | null;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export interface SlatesMessageTransport {
|
|
28
|
+
send(messages: SlatesProtocolMessage[]): Promise<SlatesProtocolResponse[]>;
|
|
29
|
+
close?(): Promise<void> | void;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export interface SlatesProtocolClientOptions {
|
|
33
|
+
transport: SlatesMessageTransport;
|
|
34
|
+
participants?: SlatesParticipant[];
|
|
35
|
+
state?: Partial<SlatesClientState>;
|
|
36
|
+
}
|