@slates/client 1.0.0-rc.3 → 1.0.0-rc.7

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.
@@ -170,6 +170,59 @@ let createTraceSlate = (baseUrl: string) => {
170
170
  });
171
171
  };
172
172
 
173
+ let createTraceErrorSlate = (baseUrl: string) => {
174
+ let config = SlateConfig.create(z.object({}));
175
+ let auth = SlateAuth.create<{}>().output(z.object({}));
176
+
177
+ let spec = SlateSpecification.create({
178
+ key: 'trace-error-slate',
179
+ name: 'Trace Error Slate',
180
+ description: 'A slate that traces failed HTTP requests',
181
+ config,
182
+ auth
183
+ });
184
+
185
+ let traceTool = SlateTool.create(spec, {
186
+ key: 'trace_http_error',
187
+ name: 'Trace HTTP Error'
188
+ })
189
+ .input(z.object({}))
190
+ .output(
191
+ z.object({
192
+ ok: z.boolean()
193
+ })
194
+ )
195
+ .handleInvocation(async () => {
196
+ await axios.post(
197
+ `${baseUrl}/trace-error?client_secret=request-secret`,
198
+ new URLSearchParams({
199
+ grant_type: 'authorization_code',
200
+ client_secret: 'client-secret'
201
+ }).toString(),
202
+ {
203
+ headers: {
204
+ 'Content-Type': 'application/x-www-form-urlencoded',
205
+ Authorization: 'Bearer client-token'
206
+ }
207
+ }
208
+ );
209
+
210
+ return {
211
+ output: {
212
+ ok: true
213
+ },
214
+ message: 'should not reach'
215
+ };
216
+ })
217
+ .build();
218
+
219
+ return Slate.create({
220
+ spec,
221
+ tools: [traceTool],
222
+ triggers: []
223
+ });
224
+ };
225
+
173
226
  let createTriggerTraceSlate = () => {
174
227
  let config = SlateConfig.create(z.object({}));
175
228
  let auth = SlateAuth.create<{}>().output(z.object({}));
@@ -546,7 +599,7 @@ describe('@slates/client local transport', () => {
546
599
  expect(trace?.request.url).not.toContain('request-secret');
547
600
  expect(trace?.request.headers).toMatchObject({
548
601
  accept: 'application/json, text/plain, */*',
549
- 'user-agent': 'slates.dev@1.0.0/trace-slate',
602
+ 'user-agent': 'slates.dev/1.0.0 trace-slate',
550
603
  'x-slates-provider': 'trace-slate'
551
604
  });
552
605
  expect(trace?.request.headers).not.toHaveProperty('authorization');
@@ -566,4 +619,56 @@ describe('@slates/client local transport', () => {
566
619
  });
567
620
  }
568
621
  });
622
+
623
+ it('returns sanitized request traces on provider errors', async () => {
624
+ let server = createServer((_req, res) => {
625
+ res.statusCode = 400;
626
+ res.setHeader('Content-Type', 'application/json');
627
+ res.end(
628
+ JSON.stringify({
629
+ error: 'invalid_grant',
630
+ client_secret: 'server-secret'
631
+ })
632
+ );
633
+ });
634
+
635
+ await new Promise<void>((resolve, reject) => {
636
+ server.once('error', reject);
637
+ server.listen(0, '127.0.0.1', () => resolve());
638
+ });
639
+
640
+ try {
641
+ let { port } = server.address() as AddressInfo;
642
+ let client = createSlatesClient({
643
+ transport: createLocalSlateTransport({
644
+ slate: createTraceErrorSlate(`http://127.0.0.1:${port}`)
645
+ }),
646
+ state: {
647
+ config: {}
648
+ }
649
+ });
650
+
651
+ let error = await client
652
+ .invokeTool('trace_http_error', {})
653
+ .then(() => null)
654
+ .catch(err => err as SlateProtocolError);
655
+
656
+ expect(error).toBeInstanceOf(SlateProtocolError);
657
+ expect(error?.data.code).toBe('upstream.invalid_request');
658
+ expect(error?.data.requestTraces).toHaveLength(1);
659
+
660
+ let trace = error?.data.requestTraces?.[0] as Record<string, any> | undefined;
661
+ expect(trace?.request.method).toBe('POST');
662
+ expect(trace?.request.url).not.toContain('request-secret');
663
+ expect(trace?.request.headers).not.toHaveProperty('authorization');
664
+ expect(trace?.request.body?.text).toContain('client_secret=[redacted]');
665
+ expect(trace?.response?.status).toBe(400);
666
+ expect(trace?.response?.body?.text).toContain('"client_secret":"[redacted]"');
667
+ expect(trace?.response?.body?.text).not.toContain('server-secret');
668
+ } finally {
669
+ await new Promise<void>((resolve, reject) => {
670
+ server.close(error => (error ? reject(error) : resolve()));
671
+ });
672
+ }
673
+ });
569
674
  });
package/src/client.ts CHANGED
@@ -1,13 +1,30 @@
1
1
  import {
2
2
  SLATES_PROTOCOL_VERSION,
3
- SlateAuthenticationMethod,
4
- SlatesAction,
5
3
  SlatesParticipant,
6
- slatesRequestsByMethod,
7
- slatesResponsesByMethod
4
+ type SlateAuthenticationMethod,
5
+ type SlatesAction,
6
+ type SlatesMessageActionGetResponse,
7
+ type SlatesMessageActionInvokeResponse,
8
+ type SlatesMessageActionsListResponse,
9
+ type SlatesMessageActionTriggerEventMapResponse,
10
+ type SlatesMessageActionTriggerWebhookHandleResponse,
11
+ type SlatesMessageActionTriggerWebhookRegisterResponse,
12
+ type SlatesMessageActionTriggerWebhookUnregisterResponse,
13
+ type SlatesMessageAuthAuthorizationUrlGetResponse,
14
+ type SlatesMessageAuthDefaultInputGetResponse,
15
+ type SlatesMessageAuthInputChangedResponse,
16
+ type SlatesMessageAuthMethodGetResponse,
17
+ type SlatesMessageAuthOutputGetResponse,
18
+ type SlatesMessageAuthProfileGetResponse,
19
+ type SlatesMessageAuthTokenRefreshHandleResponse,
20
+ type SlatesMessageConfigChangedResponse,
21
+ type SlatesMessageConfigDefaultGetResponse,
22
+ type SlatesMessageConfigSchemaGetResponse,
23
+ type SlatesMessageProviderIdentifyResponse,
24
+ type SlatesRequests,
25
+ type SlatesResponsesByMethod
8
26
  } from '@slates/proto';
9
27
  import { randomUUID } from 'crypto';
10
- import z from 'zod';
11
28
  import { SlateProtocolError } from './error';
12
29
  import { SlatesClientState, SlatesProtocolClientOptions } from './types';
13
30
 
@@ -118,10 +135,10 @@ export class SlatesProtocolClient {
118
135
  ];
119
136
  }
120
137
 
121
- async request<Key extends keyof typeof slatesRequestsByMethod>(
138
+ async request<Key extends keyof SlatesResponsesByMethod & SlatesRequests['method']>(
122
139
  method: Key,
123
- params: z.infer<(typeof slatesRequestsByMethod)[Key]>['params']
124
- ): Promise<z.infer<(typeof slatesResponsesByMethod)[Key]>['result']> {
140
+ params: Extract<SlatesRequests, { method: Key }>['params']
141
+ ): Promise<SlatesResponsesByMethod[Key]['result']> {
125
142
  let id = randomUUID();
126
143
  let responses = await this.transport.send([
127
144
  ...this.buildStateMessages(),
@@ -130,7 +147,7 @@ export class SlatesProtocolClient {
130
147
  id,
131
148
  method,
132
149
  params
133
- } as z.infer<(typeof slatesRequestsByMethod)[Key]>
150
+ } as Extract<SlatesRequests, { method: Key }>
134
151
  ]);
135
152
 
136
153
  let response = responses.find(message => 'id' in message && message.id === id) as
@@ -148,11 +165,11 @@ export class SlatesProtocolClient {
148
165
  return response.result;
149
166
  }
150
167
 
151
- async identify() {
168
+ async identify(): Promise<SlatesMessageProviderIdentifyResponse['result']> {
152
169
  return this.request('slates/provider.identify', {});
153
170
  }
154
171
 
155
- async listActions() {
172
+ async listActions(): Promise<SlatesMessageActionsListResponse['result']> {
156
173
  return this.request('slates/actions.list', {});
157
174
  }
158
175
 
@@ -166,7 +183,7 @@ export class SlatesProtocolClient {
166
183
  return result.actions.filter(action => action.type === 'action.trigger');
167
184
  }
168
185
 
169
- async getAction(actionId: string) {
186
+ async getAction(actionId: string): Promise<SlatesMessageActionGetResponse['result']> {
170
187
  return this.request('slates/action.get', { actionId });
171
188
  }
172
189
 
@@ -188,18 +205,18 @@ export class SlatesProtocolClient {
188
205
  return result.action;
189
206
  }
190
207
 
191
- async getConfigSchema() {
208
+ async getConfigSchema(): Promise<SlatesMessageConfigSchemaGetResponse['result']> {
192
209
  return this.request('slates/config.schema.get', {});
193
210
  }
194
211
 
195
- async getDefaultConfig() {
212
+ async getDefaultConfig(): Promise<SlatesMessageConfigDefaultGetResponse['result']> {
196
213
  return this.request('slates/config.get_default', {});
197
214
  }
198
215
 
199
216
  async updateConfig(
200
217
  previousConfig: Record<string, any> | null,
201
218
  newConfig: Record<string, any>
202
- ) {
219
+ ): Promise<SlatesMessageConfigChangedResponse['result']> {
203
220
  return this.request('slates/config.changed', {
204
221
  previousConfig,
205
222
  newConfig
@@ -210,13 +227,17 @@ export class SlatesProtocolClient {
210
227
  return this.request('slates/auth.methods.list', {});
211
228
  }
212
229
 
213
- async getAuthMethod(authenticationMethodId: string) {
230
+ async getAuthMethod(
231
+ authenticationMethodId: string
232
+ ): Promise<SlatesMessageAuthMethodGetResponse['result']> {
214
233
  return this.request('slates/auth.method.get', {
215
234
  authenticationMethodId
216
235
  });
217
236
  }
218
237
 
219
- async getDefaultAuthInput(authenticationMethodId: string) {
238
+ async getDefaultAuthInput(
239
+ authenticationMethodId: string
240
+ ): Promise<SlatesMessageAuthDefaultInputGetResponse['result']> {
220
241
  return this.request('slates/auth.input.get_default', {
221
242
  authenticationMethodId
222
243
  });
@@ -226,7 +247,7 @@ export class SlatesProtocolClient {
226
247
  authenticationMethodId: string;
227
248
  previousInput: Record<string, any> | null;
228
249
  newInput: Record<string, any>;
229
- }) {
250
+ }): Promise<SlatesMessageAuthInputChangedResponse['result']> {
230
251
  return this.request('slates/auth.input.changed', {
231
252
  authenticationMethodId: d.authenticationMethodId,
232
253
  previousInput: d.previousInput,
@@ -234,7 +255,10 @@ export class SlatesProtocolClient {
234
255
  });
235
256
  }
236
257
 
237
- async getAuthOutput(d: { authenticationMethodId: string; input: Record<string, any> }) {
258
+ async getAuthOutput(d: {
259
+ authenticationMethodId: string;
260
+ input: Record<string, any>;
261
+ }): Promise<SlatesMessageAuthOutputGetResponse['result']> {
238
262
  return this.request('slates/auth.output.get', {
239
263
  authenticationMethodId: d.authenticationMethodId,
240
264
  input: d.input
@@ -249,7 +273,7 @@ export class SlatesProtocolClient {
249
273
  clientId: string;
250
274
  clientSecret: string;
251
275
  scopes: string[];
252
- }) {
276
+ }): Promise<SlatesMessageAuthAuthorizationUrlGetResponse['result']> {
253
277
  return this.request('slates/auth.authorization_url.get', d);
254
278
  }
255
279
 
@@ -278,7 +302,7 @@ export class SlatesProtocolClient {
278
302
  clientId: string;
279
303
  clientSecret: string;
280
304
  scopes: string[];
281
- }) {
305
+ }): Promise<SlatesMessageAuthTokenRefreshHandleResponse['result']> {
282
306
  return this.request('slates/auth.token_refresh.handle', d);
283
307
  }
284
308
 
@@ -287,11 +311,14 @@ export class SlatesProtocolClient {
287
311
  output: Record<string, any>;
288
312
  input: Record<string, any>;
289
313
  scopes: string[];
290
- }) {
314
+ }): Promise<SlatesMessageAuthProfileGetResponse['result']> {
291
315
  return this.request('slates/auth.profile.get', d);
292
316
  }
293
317
 
294
- async invokeTool(actionId: string, input: Record<string, any>) {
318
+ async invokeTool(
319
+ actionId: string,
320
+ input: Record<string, any>
321
+ ): Promise<SlatesMessageActionInvokeResponse['result']> {
295
322
  this.ensureSession();
296
323
  return this.request('slates/action.tool.invoke', {
297
324
  actionId,
@@ -299,7 +326,10 @@ export class SlatesProtocolClient {
299
326
  });
300
327
  }
301
328
 
302
- async mapTriggerEvent(actionId: string, input: Record<string, any>) {
329
+ async mapTriggerEvent(
330
+ actionId: string,
331
+ input: Record<string, any>
332
+ ): Promise<SlatesMessageActionTriggerEventMapResponse['result']> {
303
333
  this.ensureSession();
304
334
  return this.request('slates/action.trigger.map_event', {
305
335
  actionId,
@@ -307,7 +337,10 @@ export class SlatesProtocolClient {
307
337
  });
308
338
  }
309
339
 
310
- async registerTriggerWebhook(actionId: string, webhookBaseUrl: string) {
340
+ async registerTriggerWebhook(
341
+ actionId: string,
342
+ webhookBaseUrl: string
343
+ ): Promise<SlatesMessageActionTriggerWebhookRegisterResponse['result']> {
311
344
  this.ensureSession();
312
345
  return this.request('slates/action.trigger.webhook_register', {
313
346
  actionId,
@@ -322,7 +355,7 @@ export class SlatesProtocolClient {
322
355
  headers?: Record<string, string>;
323
356
  body?: string | Uint8Array | null;
324
357
  state?: any;
325
- }) {
358
+ }): Promise<SlatesMessageActionTriggerWebhookHandleResponse['result']> {
326
359
  this.ensureSession();
327
360
  let encodedBody =
328
361
  typeof d.body === 'string'
@@ -351,7 +384,7 @@ export class SlatesProtocolClient {
351
384
  webhookBaseUrl: string;
352
385
  registrationDetails: any;
353
386
  state?: any;
354
- }) {
387
+ }): Promise<SlatesMessageActionTriggerWebhookUnregisterResponse['result']> {
355
388
  this.ensureSession();
356
389
  return this.request('slates/action.trigger.webhook_unregister', {
357
390
  actionId: d.actionId,
package/src/error.ts CHANGED
@@ -22,6 +22,7 @@ export interface SlateProtocolErrorResponse {
22
22
  provider?: Record<string, unknown>;
23
23
  upstream?: Record<string, unknown>;
24
24
  baggage?: Record<string, unknown>;
25
+ requestTraces?: Array<Record<string, unknown>>;
25
26
  [key: string]: unknown;
26
27
  }
27
28