@slates/provider-handler 1.0.0-rc.17 → 1.0.0-rc.19

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 CHANGED
@@ -33,7 +33,7 @@ __export(index_exports, {
33
33
  createProviderHandler: () => createProviderHandler
34
34
  });
35
35
  module.exports = __toCommonJS(index_exports);
36
- var import_error3 = require("@lowerdeck/error");
36
+ var import_error4 = require("@lowerdeck/error");
37
37
  var import_proto = require("@slates/proto");
38
38
  var import_provider2 = require("@slates/provider");
39
39
 
@@ -139,6 +139,7 @@ var mapAction = (_slate, a) => {
139
139
  tags: a.tags,
140
140
  metadata: a.metadata,
141
141
  scopes: a.scopes,
142
+ authMethods: a.authMethods,
142
143
  docs: a.docs ?? [],
143
144
  inputSchema: toJsonSchema(a.inputSchema),
144
145
  outputSchema: toJsonSchema(a.outputSchema)
@@ -160,7 +161,8 @@ var mapAction = (_slate, a) => {
160
161
  } : {
161
162
  type: "webhook",
162
163
  autoRegistration: !!a.autoRegisterWebhook,
163
- autoUnregistration: !!a.autoUnregisterWebhook
164
+ autoUnregistration: !!a.autoUnregisterWebhook,
165
+ http: a.http
164
166
  }
165
167
  };
166
168
  };
@@ -182,6 +184,60 @@ var State = class {
182
184
  }
183
185
  };
184
186
 
187
+ // src/webhook.ts
188
+ var import_error3 = require("@lowerdeck/error");
189
+ var SLATE_WEBHOOK_RESPONSE_MAX_BODY_BYTES = 1024 * 1024;
190
+ var serializeBody = (body) => {
191
+ if (body === null) return null;
192
+ if (body.byteLength > SLATE_WEBHOOK_RESPONSE_MAX_BODY_BYTES) {
193
+ throw new import_error3.ServiceError(
194
+ (0, import_error3.badRequestError)({
195
+ message: `Webhook response body exceeds the ${SLATE_WEBHOOK_RESPONSE_MAX_BODY_BYTES}-byte limit`
196
+ })
197
+ );
198
+ }
199
+ return {
200
+ encoding: "base64",
201
+ content: Buffer.from(body).toString("base64")
202
+ };
203
+ };
204
+ var headersToObject = (headers) => {
205
+ let result = {};
206
+ headers.forEach((value, key) => {
207
+ result[key] = value;
208
+ });
209
+ return result;
210
+ };
211
+ var validateStatus = (status) => {
212
+ if (!Number.isInteger(status) || status < 100 || status > 599) {
213
+ throw new import_error3.ServiceError(
214
+ (0, import_error3.badRequestError)({
215
+ message: "Webhook response status must be an integer between 100 and 599"
216
+ })
217
+ );
218
+ }
219
+ };
220
+ var serializeWebhookHttpResponse = async (response) => {
221
+ if (response instanceof Response) {
222
+ validateStatus(response.status);
223
+ return {
224
+ status: response.status,
225
+ headers: headersToObject(response.headers),
226
+ body: serializeBody(
227
+ response.body === null ? null : new Uint8Array(await response.arrayBuffer())
228
+ )
229
+ };
230
+ }
231
+ let status = response.status ?? 200;
232
+ validateStatus(status);
233
+ let body = typeof response.body === "string" ? new TextEncoder().encode(response.body) : response.body ?? null;
234
+ return {
235
+ status,
236
+ headers: response.headers ?? {},
237
+ body: serializeBody(body)
238
+ };
239
+ };
240
+
185
241
  // src/index.ts
186
242
  var isRecord = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
187
243
  var getObjectKeyCount = (value) => isRecord(value) ? Object.keys(value).length : void 0;
@@ -293,8 +349,8 @@ var createProviderHandler = (slate, listeners) => (0, import_proto.createSlatesP
293
349
  let currentProtocol = protocol.get();
294
350
  let currentParticipants = participants.get();
295
351
  if (!currentProtocol || !currentParticipants) {
296
- throw new import_error3.ServiceError(
297
- (0, import_error3.preconditionFailedError)({
352
+ throw new import_error4.ServiceError(
353
+ (0, import_error4.preconditionFailedError)({
298
354
  message: "Connection context has not been initialized"
299
355
  })
300
356
  );
@@ -310,8 +366,8 @@ var createProviderHandler = (slate, listeners) => (0, import_proto.createSlatesP
310
366
  let currentSession = session.get();
311
367
  let currentAuth = auth.get();
312
368
  if (!currentConfig || !currentSession || !currentAuth && slate.spec.auth.authStack.length > 0) {
313
- throw new import_error3.ServiceError(
314
- (0, import_error3.preconditionFailedError)({
369
+ throw new import_error4.ServiceError(
370
+ (0, import_error4.preconditionFailedError)({
315
371
  message: "Session context has not been initialized"
316
372
  })
317
373
  );
@@ -325,6 +381,7 @@ var createProviderHandler = (slate, listeners) => (0, import_proto.createSlatesP
325
381
  };
326
382
  let getAuthConfig = () => config.get()?.value ?? {};
327
383
  let getEmptyContext = () => new import_provider2.SlateContext({}, {}, {}, slate.spec, logger);
384
+ let getAuthContext = () => new import_provider2.SlateContext(getAuthConfig(), {}, {}, slate.spec, logger);
328
385
  let withRequestTraces = (context, result) => {
329
386
  let requestTraces = context.getHttpTraces();
330
387
  return requestTraces.length > 0 ? { ...result, requestTraces } : result;
@@ -334,8 +391,8 @@ var createProviderHandler = (slate, listeners) => (0, import_proto.createSlatesP
334
391
  });
335
392
  manager.onNotification("slates/participant.set", async ({ params }) => {
336
393
  if (!protocol.get()) {
337
- throw new import_error3.ServiceError(
338
- (0, import_error3.preconditionFailedError)({ message: "Connection protocol has not been initialized" })
394
+ throw new import_error4.ServiceError(
395
+ (0, import_error4.preconditionFailedError)({ message: "Connection protocol has not been initialized" })
339
396
  );
340
397
  }
341
398
  participants.set(params.participants);
@@ -542,7 +599,7 @@ var createProviderHandler = (slate, listeners) => (0, import_proto.createSlatesP
542
599
  );
543
600
  }
544
601
  if ("getOutput" in authMethod) {
545
- let context = getEmptyContext();
602
+ let context = getAuthContext();
546
603
  let outputRes = await traceProviderCall(
547
604
  {
548
605
  component: "auth",
@@ -555,12 +612,22 @@ var createProviderHandler = (slate, listeners) => (0, import_proto.createSlatesP
555
612
  inputKeyCount: getObjectKeyCount(input)
556
613
  },
557
614
  onSuccess: (result) => ({
558
- outputKeyCount: getObjectKeyCount(result.output)
615
+ outputKeyCount: getObjectKeyCount(result.output),
616
+ scopeCount: result.scopes?.length ?? 0
559
617
  })
560
618
  },
561
- () => (0, import_provider2.runWithContext)(context, () => authMethod.getOutput({ input }))
619
+ () => (0, import_provider2.runWithContext)(
620
+ context,
621
+ () => authMethod.getOutput({
622
+ input,
623
+ config: getAuthConfig()
624
+ })
625
+ )
562
626
  );
563
- return withRequestTraces(context, { output: outputRes.output });
627
+ return withRequestTraces(context, {
628
+ output: outputRes.output,
629
+ scopes: outputRes.scopes
630
+ });
564
631
  }
565
632
  return { output: input };
566
633
  });
@@ -609,8 +676,8 @@ var createProviderHandler = (slate, listeners) => (0, import_proto.createSlatesP
609
676
  scopes: callbackRes.scopes
610
677
  });
611
678
  }
612
- throw new import_error3.ServiceError(
613
- (0, import_error3.preconditionFailedError)({
679
+ throw new import_error4.ServiceError(
680
+ (0, import_error4.preconditionFailedError)({
614
681
  message: `Authentication method does not support authorization callback handling: ${params.authenticationMethodId}`
615
682
  })
616
683
  );
@@ -656,8 +723,8 @@ var createProviderHandler = (slate, listeners) => (0, import_proto.createSlatesP
656
723
  callbackState: urlRes.callbackState
657
724
  });
658
725
  }
659
- throw new import_error3.ServiceError(
660
- (0, import_error3.preconditionFailedError)({
726
+ throw new import_error4.ServiceError(
727
+ (0, import_error4.preconditionFailedError)({
661
728
  message: `Authentication method does not support authorization URL retrieval: ${params.authenticationMethodId}`
662
729
  })
663
730
  );
@@ -666,7 +733,7 @@ var createProviderHandler = (slate, listeners) => (0, import_proto.createSlatesP
666
733
  getContextBasic();
667
734
  let authMethod = getAuthMethod(slate, params.authenticationMethodId);
668
735
  if (authMethod.getProfile) {
669
- let context = getEmptyContext();
736
+ let context = getAuthContext();
670
737
  let profileRes = await traceProviderCall(
671
738
  {
672
739
  component: "auth",
@@ -695,7 +762,9 @@ var createProviderHandler = (slate, listeners) => (0, import_proto.createSlatesP
695
762
  }
696
763
  return authMethod.getProfile({
697
764
  output: params.output,
698
- input: params.input
765
+ input: params.input,
766
+ scopes: params.scopes,
767
+ config: getAuthConfig()
699
768
  });
700
769
  })
701
770
  );
@@ -703,8 +772,8 @@ var createProviderHandler = (slate, listeners) => (0, import_proto.createSlatesP
703
772
  profile: profileRes.profile
704
773
  });
705
774
  }
706
- throw new import_error3.ServiceError(
707
- (0, import_error3.preconditionFailedError)({
775
+ throw new import_error4.ServiceError(
776
+ (0, import_error4.preconditionFailedError)({
708
777
  message: `Authentication method does not support profile retrieval: ${params.authenticationMethodId}`
709
778
  })
710
779
  );
@@ -713,7 +782,7 @@ var createProviderHandler = (slate, listeners) => (0, import_proto.createSlatesP
713
782
  getContextBasic();
714
783
  let authMethod = getAuthMethod(slate, params.authenticationMethodId);
715
784
  if ("handleTokenRefresh" in authMethod && authMethod.handleTokenRefresh) {
716
- let context = getEmptyContext();
785
+ let context = getAuthContext();
717
786
  let refreshRes = await traceProviderCall(
718
787
  {
719
788
  component: "auth",
@@ -748,7 +817,8 @@ var createProviderHandler = (slate, listeners) => (0, import_proto.createSlatesP
748
817
  input: params.input,
749
818
  clientId: params.clientId,
750
819
  clientSecret: params.clientSecret,
751
- scopes: params.scopes
820
+ scopes: params.scopes,
821
+ config: getAuthConfig()
752
822
  });
753
823
  })
754
824
  );
@@ -757,8 +827,8 @@ var createProviderHandler = (slate, listeners) => (0, import_proto.createSlatesP
757
827
  input: refreshRes.input
758
828
  });
759
829
  }
760
- throw new import_error3.ServiceError(
761
- (0, import_error3.preconditionFailedError)({
830
+ throw new import_error4.ServiceError(
831
+ (0, import_error4.preconditionFailedError)({
762
832
  message: `Authentication method does not support token refresh handling: ${params.authenticationMethodId}`
763
833
  })
764
834
  );
@@ -851,8 +921,8 @@ var createProviderHandler = (slate, listeners) => (0, import_proto.createSlatesP
851
921
  let ctx = getContextFull();
852
922
  let action = getActionWithType(slate, "trigger", params.actionId);
853
923
  if (!action.pollEvents) {
854
- throw new import_error3.ServiceError(
855
- (0, import_error3.badRequestError)({
924
+ throw new import_error4.ServiceError(
925
+ (0, import_error4.badRequestError)({
856
926
  message: `Trigger action does not support polling: ${params.actionId}`
857
927
  })
858
928
  );
@@ -893,8 +963,8 @@ var createProviderHandler = (slate, listeners) => (0, import_proto.createSlatesP
893
963
  let ctx = getContextFull();
894
964
  let action = getActionWithType(slate, "trigger", params.actionId);
895
965
  if (!action.handleRequest) {
896
- throw new import_error3.ServiceError(
897
- (0, import_error3.badRequestError)({
966
+ throw new import_error4.ServiceError(
967
+ (0, import_error4.badRequestError)({
898
968
  message: `Trigger action does not support webhook requests: ${params.actionId}`
899
969
  })
900
970
  );
@@ -906,7 +976,11 @@ var createProviderHandler = (slate, listeners) => (0, import_proto.createSlatesP
906
976
  });
907
977
  let context = new import_provider2.SlateContext(
908
978
  ctx.config,
909
- { request: req, state: params.state },
979
+ {
980
+ request: req,
981
+ state: params.state,
982
+ registrationDetails: params.registrationDetails ?? null
983
+ },
910
984
  ctx.auth?.output,
911
985
  slate.spec,
912
986
  logger
@@ -928,22 +1002,25 @@ var createProviderHandler = (slate, listeners) => (0, import_proto.createSlatesP
928
1002
  },
929
1003
  onSuccess: (result) => ({
930
1004
  inputCount: result.inputs.length,
931
- hasUpdatedState: result.updatedState !== void 0
1005
+ hasUpdatedState: result.updatedState !== void 0,
1006
+ hasResponse: result.response !== void 0
932
1007
  })
933
1008
  },
934
1009
  () => (0, import_provider2.runWithContext)(context, () => action.handleRequest(context))
935
1010
  );
1011
+ let response = res.response === void 0 ? void 0 : await serializeWebhookHttpResponse(res.response);
936
1012
  return withRequestTraces(context, {
937
1013
  inputs: res.inputs,
938
- updatedState: res.updatedState
1014
+ updatedState: res.updatedState,
1015
+ response
939
1016
  });
940
1017
  });
941
1018
  manager.onRequest("slates/action.trigger.webhook_register", async ({ params }) => {
942
1019
  let ctx = getContextFull();
943
1020
  let action = getActionWithType(slate, "trigger", params.actionId);
944
1021
  if (!action.autoRegisterWebhook) {
945
- throw new import_error3.ServiceError(
946
- (0, import_error3.badRequestError)({
1022
+ throw new import_error4.ServiceError(
1023
+ (0, import_error4.badRequestError)({
947
1024
  message: `Trigger action does not support webhook auto-registration: ${params.actionId}`
948
1025
  })
949
1026
  );
@@ -983,8 +1060,8 @@ var createProviderHandler = (slate, listeners) => (0, import_proto.createSlatesP
983
1060
  let ctx = getContextFull();
984
1061
  let action = getActionWithType(slate, "trigger", params.actionId);
985
1062
  if (!action.autoUnregisterWebhook) {
986
- throw new import_error3.ServiceError(
987
- (0, import_error3.badRequestError)({
1063
+ throw new import_error4.ServiceError(
1064
+ (0, import_error4.badRequestError)({
988
1065
  message: `Trigger action does not support webhook auto-unregistration: ${params.actionId}`
989
1066
  })
990
1067
  );
@@ -1,5 +1,5 @@
1
1
  // src/index.ts
2
- import { badRequestError as badRequestError2, preconditionFailedError, ServiceError as ServiceError3 } from "@lowerdeck/error";
2
+ import { badRequestError as badRequestError3, preconditionFailedError, ServiceError as ServiceError4 } from "@lowerdeck/error";
3
3
  import {
4
4
  createSlatesProviderProtoHandler,
5
5
  SLATES_PROTOCOL_VERSION
@@ -112,6 +112,7 @@ var mapAction = (_slate, a) => {
112
112
  tags: a.tags,
113
113
  metadata: a.metadata,
114
114
  scopes: a.scopes,
115
+ authMethods: a.authMethods,
115
116
  docs: a.docs ?? [],
116
117
  inputSchema: toJsonSchema(a.inputSchema),
117
118
  outputSchema: toJsonSchema(a.outputSchema)
@@ -133,7 +134,8 @@ var mapAction = (_slate, a) => {
133
134
  } : {
134
135
  type: "webhook",
135
136
  autoRegistration: !!a.autoRegisterWebhook,
136
- autoUnregistration: !!a.autoUnregisterWebhook
137
+ autoUnregistration: !!a.autoUnregisterWebhook,
138
+ http: a.http
137
139
  }
138
140
  };
139
141
  };
@@ -155,6 +157,60 @@ var State = class {
155
157
  }
156
158
  };
157
159
 
160
+ // src/webhook.ts
161
+ import { badRequestError as badRequestError2, ServiceError as ServiceError3 } from "@lowerdeck/error";
162
+ var SLATE_WEBHOOK_RESPONSE_MAX_BODY_BYTES = 1024 * 1024;
163
+ var serializeBody = (body) => {
164
+ if (body === null) return null;
165
+ if (body.byteLength > SLATE_WEBHOOK_RESPONSE_MAX_BODY_BYTES) {
166
+ throw new ServiceError3(
167
+ badRequestError2({
168
+ message: `Webhook response body exceeds the ${SLATE_WEBHOOK_RESPONSE_MAX_BODY_BYTES}-byte limit`
169
+ })
170
+ );
171
+ }
172
+ return {
173
+ encoding: "base64",
174
+ content: Buffer.from(body).toString("base64")
175
+ };
176
+ };
177
+ var headersToObject = (headers) => {
178
+ let result = {};
179
+ headers.forEach((value, key) => {
180
+ result[key] = value;
181
+ });
182
+ return result;
183
+ };
184
+ var validateStatus = (status) => {
185
+ if (!Number.isInteger(status) || status < 100 || status > 599) {
186
+ throw new ServiceError3(
187
+ badRequestError2({
188
+ message: "Webhook response status must be an integer between 100 and 599"
189
+ })
190
+ );
191
+ }
192
+ };
193
+ var serializeWebhookHttpResponse = async (response) => {
194
+ if (response instanceof Response) {
195
+ validateStatus(response.status);
196
+ return {
197
+ status: response.status,
198
+ headers: headersToObject(response.headers),
199
+ body: serializeBody(
200
+ response.body === null ? null : new Uint8Array(await response.arrayBuffer())
201
+ )
202
+ };
203
+ }
204
+ let status = response.status ?? 200;
205
+ validateStatus(status);
206
+ let body = typeof response.body === "string" ? new TextEncoder().encode(response.body) : response.body ?? null;
207
+ return {
208
+ status,
209
+ headers: response.headers ?? {},
210
+ body: serializeBody(body)
211
+ };
212
+ };
213
+
158
214
  // src/index.ts
159
215
  var isRecord = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
160
216
  var getObjectKeyCount = (value) => isRecord(value) ? Object.keys(value).length : void 0;
@@ -266,7 +322,7 @@ var createProviderHandler = (slate, listeners) => createSlatesProviderProtoHandl
266
322
  let currentProtocol = protocol.get();
267
323
  let currentParticipants = participants.get();
268
324
  if (!currentProtocol || !currentParticipants) {
269
- throw new ServiceError3(
325
+ throw new ServiceError4(
270
326
  preconditionFailedError({
271
327
  message: "Connection context has not been initialized"
272
328
  })
@@ -283,7 +339,7 @@ var createProviderHandler = (slate, listeners) => createSlatesProviderProtoHandl
283
339
  let currentSession = session.get();
284
340
  let currentAuth = auth.get();
285
341
  if (!currentConfig || !currentSession || !currentAuth && slate.spec.auth.authStack.length > 0) {
286
- throw new ServiceError3(
342
+ throw new ServiceError4(
287
343
  preconditionFailedError({
288
344
  message: "Session context has not been initialized"
289
345
  })
@@ -298,6 +354,7 @@ var createProviderHandler = (slate, listeners) => createSlatesProviderProtoHandl
298
354
  };
299
355
  let getAuthConfig = () => config.get()?.value ?? {};
300
356
  let getEmptyContext = () => new SlateContext({}, {}, {}, slate.spec, logger);
357
+ let getAuthContext = () => new SlateContext(getAuthConfig(), {}, {}, slate.spec, logger);
301
358
  let withRequestTraces = (context, result) => {
302
359
  let requestTraces = context.getHttpTraces();
303
360
  return requestTraces.length > 0 ? { ...result, requestTraces } : result;
@@ -307,7 +364,7 @@ var createProviderHandler = (slate, listeners) => createSlatesProviderProtoHandl
307
364
  });
308
365
  manager.onNotification("slates/participant.set", async ({ params }) => {
309
366
  if (!protocol.get()) {
310
- throw new ServiceError3(
367
+ throw new ServiceError4(
311
368
  preconditionFailedError({ message: "Connection protocol has not been initialized" })
312
369
  );
313
370
  }
@@ -515,7 +572,7 @@ var createProviderHandler = (slate, listeners) => createSlatesProviderProtoHandl
515
572
  );
516
573
  }
517
574
  if ("getOutput" in authMethod) {
518
- let context = getEmptyContext();
575
+ let context = getAuthContext();
519
576
  let outputRes = await traceProviderCall(
520
577
  {
521
578
  component: "auth",
@@ -528,12 +585,22 @@ var createProviderHandler = (slate, listeners) => createSlatesProviderProtoHandl
528
585
  inputKeyCount: getObjectKeyCount(input)
529
586
  },
530
587
  onSuccess: (result) => ({
531
- outputKeyCount: getObjectKeyCount(result.output)
588
+ outputKeyCount: getObjectKeyCount(result.output),
589
+ scopeCount: result.scopes?.length ?? 0
532
590
  })
533
591
  },
534
- () => runWithContext(context, () => authMethod.getOutput({ input }))
592
+ () => runWithContext(
593
+ context,
594
+ () => authMethod.getOutput({
595
+ input,
596
+ config: getAuthConfig()
597
+ })
598
+ )
535
599
  );
536
- return withRequestTraces(context, { output: outputRes.output });
600
+ return withRequestTraces(context, {
601
+ output: outputRes.output,
602
+ scopes: outputRes.scopes
603
+ });
537
604
  }
538
605
  return { output: input };
539
606
  });
@@ -582,7 +649,7 @@ var createProviderHandler = (slate, listeners) => createSlatesProviderProtoHandl
582
649
  scopes: callbackRes.scopes
583
650
  });
584
651
  }
585
- throw new ServiceError3(
652
+ throw new ServiceError4(
586
653
  preconditionFailedError({
587
654
  message: `Authentication method does not support authorization callback handling: ${params.authenticationMethodId}`
588
655
  })
@@ -629,7 +696,7 @@ var createProviderHandler = (slate, listeners) => createSlatesProviderProtoHandl
629
696
  callbackState: urlRes.callbackState
630
697
  });
631
698
  }
632
- throw new ServiceError3(
699
+ throw new ServiceError4(
633
700
  preconditionFailedError({
634
701
  message: `Authentication method does not support authorization URL retrieval: ${params.authenticationMethodId}`
635
702
  })
@@ -639,7 +706,7 @@ var createProviderHandler = (slate, listeners) => createSlatesProviderProtoHandl
639
706
  getContextBasic();
640
707
  let authMethod = getAuthMethod(slate, params.authenticationMethodId);
641
708
  if (authMethod.getProfile) {
642
- let context = getEmptyContext();
709
+ let context = getAuthContext();
643
710
  let profileRes = await traceProviderCall(
644
711
  {
645
712
  component: "auth",
@@ -668,7 +735,9 @@ var createProviderHandler = (slate, listeners) => createSlatesProviderProtoHandl
668
735
  }
669
736
  return authMethod.getProfile({
670
737
  output: params.output,
671
- input: params.input
738
+ input: params.input,
739
+ scopes: params.scopes,
740
+ config: getAuthConfig()
672
741
  });
673
742
  })
674
743
  );
@@ -676,7 +745,7 @@ var createProviderHandler = (slate, listeners) => createSlatesProviderProtoHandl
676
745
  profile: profileRes.profile
677
746
  });
678
747
  }
679
- throw new ServiceError3(
748
+ throw new ServiceError4(
680
749
  preconditionFailedError({
681
750
  message: `Authentication method does not support profile retrieval: ${params.authenticationMethodId}`
682
751
  })
@@ -686,7 +755,7 @@ var createProviderHandler = (slate, listeners) => createSlatesProviderProtoHandl
686
755
  getContextBasic();
687
756
  let authMethod = getAuthMethod(slate, params.authenticationMethodId);
688
757
  if ("handleTokenRefresh" in authMethod && authMethod.handleTokenRefresh) {
689
- let context = getEmptyContext();
758
+ let context = getAuthContext();
690
759
  let refreshRes = await traceProviderCall(
691
760
  {
692
761
  component: "auth",
@@ -721,7 +790,8 @@ var createProviderHandler = (slate, listeners) => createSlatesProviderProtoHandl
721
790
  input: params.input,
722
791
  clientId: params.clientId,
723
792
  clientSecret: params.clientSecret,
724
- scopes: params.scopes
793
+ scopes: params.scopes,
794
+ config: getAuthConfig()
725
795
  });
726
796
  })
727
797
  );
@@ -730,7 +800,7 @@ var createProviderHandler = (slate, listeners) => createSlatesProviderProtoHandl
730
800
  input: refreshRes.input
731
801
  });
732
802
  }
733
- throw new ServiceError3(
803
+ throw new ServiceError4(
734
804
  preconditionFailedError({
735
805
  message: `Authentication method does not support token refresh handling: ${params.authenticationMethodId}`
736
806
  })
@@ -824,8 +894,8 @@ var createProviderHandler = (slate, listeners) => createSlatesProviderProtoHandl
824
894
  let ctx = getContextFull();
825
895
  let action = getActionWithType(slate, "trigger", params.actionId);
826
896
  if (!action.pollEvents) {
827
- throw new ServiceError3(
828
- badRequestError2({
897
+ throw new ServiceError4(
898
+ badRequestError3({
829
899
  message: `Trigger action does not support polling: ${params.actionId}`
830
900
  })
831
901
  );
@@ -866,8 +936,8 @@ var createProviderHandler = (slate, listeners) => createSlatesProviderProtoHandl
866
936
  let ctx = getContextFull();
867
937
  let action = getActionWithType(slate, "trigger", params.actionId);
868
938
  if (!action.handleRequest) {
869
- throw new ServiceError3(
870
- badRequestError2({
939
+ throw new ServiceError4(
940
+ badRequestError3({
871
941
  message: `Trigger action does not support webhook requests: ${params.actionId}`
872
942
  })
873
943
  );
@@ -879,7 +949,11 @@ var createProviderHandler = (slate, listeners) => createSlatesProviderProtoHandl
879
949
  });
880
950
  let context = new SlateContext(
881
951
  ctx.config,
882
- { request: req, state: params.state },
952
+ {
953
+ request: req,
954
+ state: params.state,
955
+ registrationDetails: params.registrationDetails ?? null
956
+ },
883
957
  ctx.auth?.output,
884
958
  slate.spec,
885
959
  logger
@@ -901,22 +975,25 @@ var createProviderHandler = (slate, listeners) => createSlatesProviderProtoHandl
901
975
  },
902
976
  onSuccess: (result) => ({
903
977
  inputCount: result.inputs.length,
904
- hasUpdatedState: result.updatedState !== void 0
978
+ hasUpdatedState: result.updatedState !== void 0,
979
+ hasResponse: result.response !== void 0
905
980
  })
906
981
  },
907
982
  () => runWithContext(context, () => action.handleRequest(context))
908
983
  );
984
+ let response = res.response === void 0 ? void 0 : await serializeWebhookHttpResponse(res.response);
909
985
  return withRequestTraces(context, {
910
986
  inputs: res.inputs,
911
- updatedState: res.updatedState
987
+ updatedState: res.updatedState,
988
+ response
912
989
  });
913
990
  });
914
991
  manager.onRequest("slates/action.trigger.webhook_register", async ({ params }) => {
915
992
  let ctx = getContextFull();
916
993
  let action = getActionWithType(slate, "trigger", params.actionId);
917
994
  if (!action.autoRegisterWebhook) {
918
- throw new ServiceError3(
919
- badRequestError2({
995
+ throw new ServiceError4(
996
+ badRequestError3({
920
997
  message: `Trigger action does not support webhook auto-registration: ${params.actionId}`
921
998
  })
922
999
  );
@@ -956,8 +1033,8 @@ var createProviderHandler = (slate, listeners) => createSlatesProviderProtoHandl
956
1033
  let ctx = getContextFull();
957
1034
  let action = getActionWithType(slate, "trigger", params.actionId);
958
1035
  if (!action.autoUnregisterWebhook) {
959
- throw new ServiceError3(
960
- badRequestError2({
1036
+ throw new ServiceError4(
1037
+ badRequestError3({
961
1038
  message: `Trigger action does not support webhook auto-unregistration: ${params.actionId}`
962
1039
  })
963
1040
  );
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@slates/provider-handler",
3
- "version": "1.0.0-rc.17",
3
+ "version": "1.0.0-rc.19",
4
4
  "publishConfig": {
5
5
  "access": "public"
6
6
  },
@@ -32,8 +32,8 @@
32
32
  },
33
33
  "dependencies": {
34
34
  "@lowerdeck/error": "^1.1.0",
35
- "@slates/proto": "1.0.0-rc.11",
36
- "@slates/provider": "1.0.0-rc.15",
35
+ "@slates/proto": "1.0.0-rc.13",
36
+ "@slates/provider": "1.0.0-rc.17",
37
37
  "zod": "^4.2.1"
38
38
  },
39
39
  "devDependencies": {
package/src/index.ts CHANGED
@@ -15,6 +15,7 @@ import {
15
15
  import { getAction, getActionWithType, getAuthMethod, mapAction, mapAuthMethod } from './spec';
16
16
  import { State } from './state';
17
17
  import { toJsonSchema, validate } from './validation';
18
+ import { serializeWebhookHttpResponse } from './webhook';
18
19
 
19
20
  let isRecord = (value: unknown): value is Record<string, unknown> =>
20
21
  typeof value === 'object' && value !== null && !Array.isArray(value);
@@ -228,6 +229,8 @@ export let createProviderHandler = <ConfigType extends {}, AuthType extends {}>(
228
229
  let getAuthConfig = (): Record<string, any> => config.get()?.value ?? {};
229
230
 
230
231
  let getEmptyContext = () => new SlateContext({}, {}, {}, slate.spec as any, logger);
232
+ let getAuthContext = () =>
233
+ new SlateContext(getAuthConfig(), {}, {}, slate.spec as any, logger);
231
234
  let withRequestTraces = <Result extends Record<string, any>>(
232
235
  context: SlateContext<any, any, any>,
233
236
  result: Result
@@ -487,7 +490,7 @@ export let createProviderHandler = <ConfigType extends {}, AuthType extends {}>(
487
490
  }
488
491
 
489
492
  if ('getOutput' in authMethod) {
490
- let context = getEmptyContext();
493
+ let context = getAuthContext();
491
494
  let outputRes = await traceProviderCall(
492
495
  {
493
496
  component: 'auth',
@@ -500,12 +503,22 @@ export let createProviderHandler = <ConfigType extends {}, AuthType extends {}>(
500
503
  inputKeyCount: getObjectKeyCount(input)
501
504
  },
502
505
  onSuccess: result => ({
503
- outputKeyCount: getObjectKeyCount(result.output)
506
+ outputKeyCount: getObjectKeyCount(result.output),
507
+ scopeCount: result.scopes?.length ?? 0
504
508
  })
505
509
  },
506
- () => runWithContext(context, () => authMethod.getOutput({ input }))
510
+ () =>
511
+ runWithContext(context, () =>
512
+ authMethod.getOutput({
513
+ input,
514
+ config: getAuthConfig()
515
+ })
516
+ )
507
517
  );
508
- return withRequestTraces(context, { output: outputRes.output });
518
+ return withRequestTraces(context, {
519
+ output: outputRes.output,
520
+ scopes: outputRes.scopes
521
+ });
509
522
  }
510
523
 
511
524
  return { output: input as any };
@@ -622,7 +635,7 @@ export let createProviderHandler = <ConfigType extends {}, AuthType extends {}>(
622
635
  let authMethod = getAuthMethod(slate, params.authenticationMethodId);
623
636
 
624
637
  if (authMethod.getProfile) {
625
- let context = getEmptyContext();
638
+ let context = getAuthContext();
626
639
  let profileRes = await traceProviderCall(
627
640
  {
628
641
  component: 'auth',
@@ -653,7 +666,9 @@ export let createProviderHandler = <ConfigType extends {}, AuthType extends {}>(
653
666
 
654
667
  return authMethod.getProfile!({
655
668
  output: params.output as any,
656
- input: params.input
669
+ input: params.input,
670
+ scopes: params.scopes,
671
+ config: getAuthConfig()
657
672
  })!;
658
673
  })
659
674
  );
@@ -675,7 +690,7 @@ export let createProviderHandler = <ConfigType extends {}, AuthType extends {}>(
675
690
  let authMethod = getAuthMethod(slate, params.authenticationMethodId);
676
691
 
677
692
  if ('handleTokenRefresh' in authMethod && authMethod.handleTokenRefresh) {
678
- let context = getEmptyContext();
693
+ let context = getAuthContext();
679
694
  let refreshRes = await traceProviderCall(
680
695
  {
681
696
  component: 'auth',
@@ -712,7 +727,8 @@ export let createProviderHandler = <ConfigType extends {}, AuthType extends {}>(
712
727
  input: params.input,
713
728
  clientId: params.clientId,
714
729
  clientSecret: params.clientSecret,
715
- scopes: params.scopes
730
+ scopes: params.scopes,
731
+ config: getAuthConfig()
716
732
  });
717
733
  })
718
734
  );
@@ -896,7 +912,11 @@ export let createProviderHandler = <ConfigType extends {}, AuthType extends {}>(
896
912
 
897
913
  let context = new SlateContext(
898
914
  ctx.config,
899
- { request: req, state: params.state },
915
+ {
916
+ request: req,
917
+ state: params.state,
918
+ registrationDetails: params.registrationDetails ?? null
919
+ },
900
920
  ctx.auth?.output!,
901
921
  slate.spec,
902
922
  logger
@@ -919,15 +939,22 @@ export let createProviderHandler = <ConfigType extends {}, AuthType extends {}>(
919
939
  },
920
940
  onSuccess: result => ({
921
941
  inputCount: result.inputs.length,
922
- hasUpdatedState: result.updatedState !== undefined
942
+ hasUpdatedState: result.updatedState !== undefined,
943
+ hasResponse: result.response !== undefined
923
944
  })
924
945
  },
925
946
  () => runWithContext(context, () => action.handleRequest!(context))
926
947
  );
927
948
 
949
+ let response =
950
+ res.response === undefined
951
+ ? undefined
952
+ : await serializeWebhookHttpResponse(res.response);
953
+
928
954
  return withRequestTraces(context, {
929
955
  inputs: res.inputs,
930
- updatedState: res.updatedState
956
+ updatedState: res.updatedState,
957
+ response
931
958
  });
932
959
  });
933
960
 
package/src/spec.ts CHANGED
@@ -101,6 +101,7 @@ export let mapAction = <ConfigType extends {}, AuthType extends {}>(
101
101
  tags: a.tags,
102
102
  metadata: a.metadata,
103
103
  scopes: a.scopes,
104
+ authMethods: a.authMethods,
104
105
  docs: a.docs ?? [],
105
106
 
106
107
  inputSchema: toJsonSchema(a.inputSchema),
@@ -129,7 +130,8 @@ export let mapAction = <ConfigType extends {}, AuthType extends {}>(
129
130
  : {
130
131
  type: 'webhook',
131
132
  autoRegistration: !!a.autoRegisterWebhook,
132
- autoUnregistration: !!a.autoUnregisterWebhook
133
+ autoUnregistration: !!a.autoUnregisterWebhook,
134
+ http: a.http
133
135
  }
134
136
  };
135
137
  };
@@ -0,0 +1,65 @@
1
+ import { describe, expect, it } from 'vitest';
2
+ import {
3
+ SLATE_WEBHOOK_RESPONSE_MAX_BODY_BYTES,
4
+ serializeWebhookHttpResponse
5
+ } from './webhook';
6
+
7
+ describe('serializeWebhookHttpResponse', () => {
8
+ it('serializes a Response with status, headers, and body', async () => {
9
+ await expect(
10
+ serializeWebhookHttpResponse(
11
+ new Response('created', {
12
+ status: 201,
13
+ headers: {
14
+ 'content-type': 'text/plain',
15
+ 'x-webhook-result': 'accepted'
16
+ }
17
+ })
18
+ )
19
+ ).resolves.toEqual({
20
+ status: 201,
21
+ headers: {
22
+ 'content-type': 'text/plain',
23
+ 'x-webhook-result': 'accepted'
24
+ },
25
+ body: {
26
+ encoding: 'base64',
27
+ content: Buffer.from('created').toString('base64')
28
+ }
29
+ });
30
+ });
31
+
32
+ it('applies defaults to a plain response init', async () => {
33
+ await expect(serializeWebhookHttpResponse({ body: 'ok' })).resolves.toEqual({
34
+ status: 200,
35
+ headers: {},
36
+ body: {
37
+ encoding: 'base64',
38
+ content: Buffer.from('ok').toString('base64')
39
+ }
40
+ });
41
+ });
42
+
43
+ it('serializes binary response bodies without changing their bytes', async () => {
44
+ await expect(
45
+ serializeWebhookHttpResponse({
46
+ status: 202,
47
+ body: new Uint8Array([0, 127, 128, 255])
48
+ })
49
+ ).resolves.toMatchObject({
50
+ status: 202,
51
+ body: {
52
+ encoding: 'base64',
53
+ content: Buffer.from([0, 127, 128, 255]).toString('base64')
54
+ }
55
+ });
56
+ });
57
+
58
+ it('rejects bodies larger than one MiB', async () => {
59
+ await expect(
60
+ serializeWebhookHttpResponse({
61
+ body: new Uint8Array(SLATE_WEBHOOK_RESPONSE_MAX_BODY_BYTES + 1)
62
+ })
63
+ ).rejects.toThrow('Webhook response body exceeds');
64
+ });
65
+ });
package/src/webhook.ts ADDED
@@ -0,0 +1,69 @@
1
+ import { badRequestError, ServiceError } from '@lowerdeck/error';
2
+ import type { SlatesWebhookHttpResponse } from '@slates/proto';
3
+ import type { SlateWebhookHttpResponseInit } from '@slates/provider';
4
+
5
+ export let SLATE_WEBHOOK_RESPONSE_MAX_BODY_BYTES = 1024 * 1024;
6
+
7
+ let serializeBody = (body: Uint8Array | null): SlatesWebhookHttpResponse['body'] => {
8
+ if (body === null) return null;
9
+
10
+ if (body.byteLength > SLATE_WEBHOOK_RESPONSE_MAX_BODY_BYTES) {
11
+ throw new ServiceError(
12
+ badRequestError({
13
+ message: `Webhook response body exceeds the ${SLATE_WEBHOOK_RESPONSE_MAX_BODY_BYTES}-byte limit`
14
+ })
15
+ );
16
+ }
17
+
18
+ return {
19
+ encoding: 'base64',
20
+ content: Buffer.from(body).toString('base64')
21
+ };
22
+ };
23
+
24
+ let headersToObject = (headers: Headers) => {
25
+ let result: Record<string, string> = {};
26
+ headers.forEach((value, key) => {
27
+ result[key] = value;
28
+ });
29
+ return result;
30
+ };
31
+
32
+ let validateStatus = (status: number) => {
33
+ if (!Number.isInteger(status) || status < 100 || status > 599) {
34
+ throw new ServiceError(
35
+ badRequestError({
36
+ message: 'Webhook response status must be an integer between 100 and 599'
37
+ })
38
+ );
39
+ }
40
+ };
41
+
42
+ export let serializeWebhookHttpResponse = async (
43
+ response: Response | SlateWebhookHttpResponseInit
44
+ ): Promise<SlatesWebhookHttpResponse> => {
45
+ if (response instanceof Response) {
46
+ validateStatus(response.status);
47
+ return {
48
+ status: response.status,
49
+ headers: headersToObject(response.headers),
50
+ body: serializeBody(
51
+ response.body === null ? null : new Uint8Array(await response.arrayBuffer())
52
+ )
53
+ };
54
+ }
55
+
56
+ let status = response.status ?? 200;
57
+ validateStatus(status);
58
+
59
+ let body =
60
+ typeof response.body === 'string'
61
+ ? new TextEncoder().encode(response.body)
62
+ : (response.body ?? null);
63
+
64
+ return {
65
+ status,
66
+ headers: response.headers ?? {},
67
+ body: serializeBody(body)
68
+ };
69
+ };