@slates/provider 1.0.0-rc.15 → 1.0.0-rc.17

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.
@@ -48,11 +48,132 @@ var SlateAction = class {
48
48
  get scopes() {
49
49
  return this._params.scopes;
50
50
  }
51
+ get authMethods() {
52
+ return this._params.authMethods;
53
+ }
51
54
  get docs() {
52
55
  return this._params.docs;
53
56
  }
54
57
  };
55
58
 
59
+ // src/error/api.ts
60
+ import { badRequestError, ServiceError } from "@lowerdeck/error";
61
+ var DEFAULT_DETAIL_KEYS = ["message", "detail", "error", "error_description", "code"];
62
+ var DEFAULT_NESTED_KEYS = ["errors"];
63
+ var isApiErrorRecord = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
64
+ var pushDetail = (details, value, options = {}) => {
65
+ if (typeof value !== "string" && (options.includeNumbers === false || typeof value !== "number")) {
66
+ return;
67
+ }
68
+ let detail = String(value).trim();
69
+ if (detail && !details.includes(detail)) {
70
+ details.push(detail);
71
+ }
72
+ };
73
+ var collectApiErrorDetails = (value, details, options = {}) => {
74
+ if (Array.isArray(value)) {
75
+ for (let item of value) {
76
+ collectApiErrorDetails(item, details, options);
77
+ }
78
+ return;
79
+ }
80
+ if (!isApiErrorRecord(value)) {
81
+ pushDetail(details, value, options);
82
+ return;
83
+ }
84
+ for (let key of options.detailKeys ?? DEFAULT_DETAIL_KEYS) {
85
+ pushDetail(details, value[key], options);
86
+ }
87
+ for (let key of options.nestedKeys ?? DEFAULT_NESTED_KEYS) {
88
+ collectApiErrorDetails(value[key], details, options);
89
+ }
90
+ };
91
+ var getSlateErrorResponse = (error) => {
92
+ if (error.name !== "SlateError" || !isApiErrorRecord(error.data)) {
93
+ return void 0;
94
+ }
95
+ let data = error.data;
96
+ let upstream = isApiErrorRecord(data.upstream) ? data.upstream : void 0;
97
+ let status = upstream?.status ?? data.status;
98
+ return typeof status === "number" || typeof status === "string" ? { status } : void 0;
99
+ };
100
+ var getApiErrorResponse = (error) => {
101
+ if (!isApiErrorRecord(error)) {
102
+ return void 0;
103
+ }
104
+ if (!isApiErrorRecord(error.response)) {
105
+ return getSlateErrorResponse(error);
106
+ }
107
+ let response = error.response;
108
+ return {
109
+ status: typeof response.status === "number" || typeof response.status === "string" ? response.status : void 0,
110
+ statusText: typeof response.statusText === "string" ? response.statusText : void 0,
111
+ data: response.data
112
+ };
113
+ };
114
+ var getApiErrorStatus = (error) => getApiErrorResponse(error)?.status;
115
+ var statusLabelForApiError = (status, statusText) => status !== void 0 ? `HTTP ${status}${statusText ? ` ${statusText}` : ""}: ` : "";
116
+ var extractApiErrorMessage = (error, options = {}) => {
117
+ let details = [];
118
+ let response = options.response ?? getApiErrorResponse(error);
119
+ collectApiErrorDetails(response?.data, details, options);
120
+ if (details.length > 0) {
121
+ return details.join(options.separator ?? " - ");
122
+ }
123
+ if (error instanceof Error && error.message) {
124
+ return error.message;
125
+ }
126
+ return options.fallbackMessage ?? "Unknown error";
127
+ };
128
+ var createApiServiceError = (message, options = {}) => {
129
+ let error = new ServiceError(badRequestError({ message }));
130
+ if (options.reason !== void 0) {
131
+ error.data.reason = options.reason;
132
+ }
133
+ if ("upstreamStatus" in options) {
134
+ error.data.upstreamStatus = options.upstreamStatus;
135
+ }
136
+ if ("upstreamCode" in options) {
137
+ error.data.upstreamCode = options.upstreamCode;
138
+ }
139
+ if (options.parent instanceof Error) {
140
+ error.setParent(options.parent);
141
+ }
142
+ return error;
143
+ };
144
+ var helpers = {
145
+ collectDetails: collectApiErrorDetails,
146
+ extractMessage: extractApiErrorMessage,
147
+ getResponse: getApiErrorResponse,
148
+ getStatus: getApiErrorStatus,
149
+ isRecord: isApiErrorRecord,
150
+ statusLabelFor: statusLabelForApiError
151
+ };
152
+ var buildApiServiceError = (error, options) => {
153
+ if (error instanceof ServiceError) {
154
+ return error;
155
+ }
156
+ let operation = options.operation ?? "request";
157
+ let response = options.extractResponse?.(error, helpers) ?? getApiErrorResponse(error);
158
+ let status = options.extractStatus?.(error, response, helpers) ?? response?.status;
159
+ let message = options.extractMessage?.(error, helpers) ?? extractApiErrorMessage(error, { ...options, response });
160
+ let statusLabel = statusLabelForApiError(status, response?.statusText);
161
+ let fullMessage = options.formatMessage?.({
162
+ providerLabel: options.providerLabel,
163
+ operation,
164
+ statusLabel,
165
+ message,
166
+ response,
167
+ status
168
+ }) ?? `${options.providerLabel} API ${operation} failed: ${statusLabel}${message}`;
169
+ return createApiServiceError(fullMessage, {
170
+ reason: options.reason,
171
+ upstreamStatus: status,
172
+ upstreamCode: options.extractUpstreamCode?.(error, response, helpers),
173
+ parent: options.parent ?? error
174
+ });
175
+ };
176
+
56
177
  // src/error/axios.ts
57
178
  import { isAxiosError } from "axios";
58
179
 
@@ -694,6 +815,7 @@ var SlateActionBuilder = class {
694
815
  #inputSchema = null;
695
816
  #outputSchema = null;
696
817
  #scopes;
818
+ #authMethods;
697
819
  #toolParams = null;
698
820
  #triggerParams = null;
699
821
  input(schema) {
@@ -709,6 +831,18 @@ var SlateActionBuilder = class {
709
831
  this.#scopes = scopes;
710
832
  return this;
711
833
  }
834
+ authMethods(authMethods) {
835
+ this.#authMethods = this.validateAuthMethods(authMethods);
836
+ return this;
837
+ }
838
+ validateAuthMethods(authMethods) {
839
+ if (!authMethods) return void 0;
840
+ let normalized = [...new Set(authMethods.map((authMethod) => authMethod.trim()))];
841
+ if (normalized.some((authMethod) => !authMethod)) {
842
+ throw new SlateDeclarationError("Auth method IDs must be non-empty strings");
843
+ }
844
+ return normalized;
845
+ }
712
846
  handleInvocation(handler) {
713
847
  if (this.type !== "tool") {
714
848
  throw new SlateDeclarationError("handleInvocation can only be set for tool actions");
@@ -729,7 +863,8 @@ var SlateActionBuilder = class {
729
863
  handleEvent: props.handleEvent,
730
864
  handleRequest: props.handleRequest,
731
865
  autoRegisterWebhook: props.autoRegisterWebhook,
732
- autoUnregisterWebhook: props.autoUnregisterWebhook
866
+ autoUnregisterWebhook: props.autoUnregisterWebhook,
867
+ http: props.http
733
868
  };
734
869
  return this;
735
870
  }
@@ -751,6 +886,7 @@ var SlateActionBuilder = class {
751
886
  if (scopes) {
752
887
  validateScopes(scopes);
753
888
  }
889
+ let authMethods = this.validateAuthMethods(this.#authMethods ?? this.params.authMethods);
754
890
  if (!this.#inputSchema) {
755
891
  throw new SlateDeclarationError("Input schema is not defined");
756
892
  }
@@ -766,6 +902,7 @@ var SlateActionBuilder = class {
766
902
  return this.factory({
767
903
  ...this.params,
768
904
  scopes,
905
+ authMethods,
769
906
  configSchema: this.#configSchema,
770
907
  authSchema: this.#authSchema,
771
908
  inputSchema: this.#inputSchema,
@@ -802,6 +939,7 @@ var SlateTrigger = class _SlateTrigger extends SlateAction {
802
939
  #polling;
803
940
  #handleEvent;
804
941
  #handleRequest;
942
+ #http;
805
943
  #pollEvents;
806
944
  #autoRegisterWebhook;
807
945
  #autoUnregisterWebhook;
@@ -813,6 +951,7 @@ var SlateTrigger = class _SlateTrigger extends SlateAction {
813
951
  };
814
952
  this.#handleEvent = params.handleEvent;
815
953
  this.#handleRequest = params.handleRequest;
954
+ this.#http = params.http;
816
955
  this.#pollEvents = params.pollEvents;
817
956
  this.#autoRegisterWebhook = params.autoRegisterWebhook;
818
957
  this.#autoUnregisterWebhook = params.autoUnregisterWebhook;
@@ -835,6 +974,9 @@ var SlateTrigger = class _SlateTrigger extends SlateAction {
835
974
  get handleRequest() {
836
975
  return this.#handleRequest;
837
976
  }
977
+ get http() {
978
+ return this.#http;
979
+ }
838
980
  get pollEvents() {
839
981
  return this.#pollEvents;
840
982
  }
@@ -929,6 +1071,90 @@ var SlateAuth = class _SlateAuth {
929
1071
  };
930
1072
  var auth = () => SlateAuth.create();
931
1073
 
1074
+ // src/auth/oauth.ts
1075
+ var isOAuthTokenResponse = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
1076
+ var formatOAuthTokenResponseMessage = (fieldLabel, providerLabel = "OAuth provider", operation = "token response") => `${providerLabel} OAuth ${operation} did not return ${fieldLabel}.`;
1077
+ var getOAuthExpiresAtFromExpiresIn = (expiresIn, options = {}) => {
1078
+ let {
1079
+ nowMs = Date.now(),
1080
+ providerLabel,
1081
+ operation,
1082
+ required = false,
1083
+ expiresInType = "numberLike",
1084
+ message
1085
+ } = options;
1086
+ if (expiresIn === void 0 || expiresIn === null || expiresIn === "") {
1087
+ if (required) {
1088
+ throw createApiServiceError(
1089
+ message ?? formatOAuthTokenResponseMessage(
1090
+ "a valid expires_in value",
1091
+ providerLabel,
1092
+ operation
1093
+ ),
1094
+ { reason: "oauth_token_response" }
1095
+ );
1096
+ }
1097
+ return void 0;
1098
+ }
1099
+ let seconds = typeof expiresIn === "number" ? expiresIn : expiresInType === "numberLike" && typeof expiresIn === "string" ? Number(expiresIn) : Number.NaN;
1100
+ if (!Number.isFinite(seconds)) {
1101
+ throw createApiServiceError(
1102
+ message ?? formatOAuthTokenResponseMessage("a valid expires_in value", providerLabel, operation),
1103
+ { reason: "oauth_token_response" }
1104
+ );
1105
+ }
1106
+ return new Date(nowMs + seconds * 1e3).toISOString();
1107
+ };
1108
+ var getRefreshToken = (refreshToken, previousRefreshToken, fallbackMode) => {
1109
+ if (typeof refreshToken === "string") {
1110
+ if (fallbackMode === "falsy" && !refreshToken) {
1111
+ return previousRefreshToken;
1112
+ }
1113
+ return refreshToken;
1114
+ }
1115
+ if (fallbackMode === "falsy" && !refreshToken) {
1116
+ return previousRefreshToken;
1117
+ }
1118
+ return refreshToken === void 0 || refreshToken === null ? previousRefreshToken : void 0;
1119
+ };
1120
+ var normalizeOAuthTokenResponse = (data, options = {}) => {
1121
+ let {
1122
+ providerLabel,
1123
+ operation,
1124
+ previousRefreshToken,
1125
+ refreshTokenFallbackMode = "nullish",
1126
+ accessTokenMessage,
1127
+ expiresInMessage,
1128
+ ...expiresAtOptions
1129
+ } = options;
1130
+ if (!isOAuthTokenResponse(data)) {
1131
+ throw createApiServiceError(
1132
+ accessTokenMessage ?? formatOAuthTokenResponseMessage("a token response object", providerLabel, operation),
1133
+ { reason: "oauth_token_response" }
1134
+ );
1135
+ }
1136
+ if (typeof data.access_token !== "string" || !data.access_token) {
1137
+ throw createApiServiceError(
1138
+ accessTokenMessage ?? formatOAuthTokenResponseMessage("an access token", providerLabel, operation),
1139
+ { reason: "oauth_token_response" }
1140
+ );
1141
+ }
1142
+ return {
1143
+ token: data.access_token,
1144
+ refreshToken: getRefreshToken(
1145
+ data.refresh_token,
1146
+ previousRefreshToken,
1147
+ refreshTokenFallbackMode
1148
+ ),
1149
+ expiresAt: getOAuthExpiresAtFromExpiresIn(data.expires_in, {
1150
+ ...expiresAtOptions,
1151
+ providerLabel,
1152
+ operation,
1153
+ message: expiresInMessage
1154
+ })
1155
+ };
1156
+ };
1157
+
932
1158
  // src/axios/index.ts
933
1159
  import baseAxios, { getAdapter, isAxiosError as isAxiosError2 } from "axios";
934
1160
 
@@ -1512,6 +1738,35 @@ var createAxios = (config2) => {
1512
1738
  applySlateInterceptors(instance, errorMapping);
1513
1739
  return instance;
1514
1740
  };
1741
+ var createAuthenticatedAxios = (config2) => {
1742
+ let { authHeader, contentType = "application/json", errorAdapter, ...axiosConfig } = config2;
1743
+ let instance = createAxios({
1744
+ ...axiosConfig,
1745
+ headers: {
1746
+ ...contentType === false ? {} : { "Content-Type": contentType },
1747
+ ...authHeader ? { [authHeader.name ?? "Authorization"]: authHeader.value } : {},
1748
+ ...axiosConfig.headers
1749
+ }
1750
+ });
1751
+ if (errorAdapter) {
1752
+ instance.interceptors.response.use(
1753
+ (response) => response,
1754
+ (error) => Promise.reject(errorAdapter(error))
1755
+ );
1756
+ }
1757
+ return instance;
1758
+ };
1759
+ var requestAxios = async (operation, request, errorAdapter) => {
1760
+ try {
1761
+ return await request();
1762
+ } catch (error) {
1763
+ throw errorAdapter(error, operation);
1764
+ }
1765
+ };
1766
+ var requestAxiosData = async (operation, request, errorAdapter) => {
1767
+ let response = await requestAxios(operation, request, errorAdapter);
1768
+ return response.data;
1769
+ };
1515
1770
  var axios = createAxios();
1516
1771
 
1517
1772
  // src/config/config.ts
@@ -1590,6 +1845,11 @@ var SlateContext = class {
1590
1845
  get request() {
1591
1846
  return Object.freeze(this.#input.request);
1592
1847
  }
1848
+ get registrationDetails() {
1849
+ return Object.freeze(
1850
+ this.#input.registrationDetails
1851
+ );
1852
+ }
1593
1853
  get auth() {
1594
1854
  return Object.freeze(this.#auth);
1595
1855
  }
@@ -1628,6 +1888,45 @@ var SlateContext = class {
1628
1888
  }
1629
1889
  };
1630
1890
 
1891
+ // src/crypto/hmac.ts
1892
+ import { createHmac, timingSafeEqual } from "crypto";
1893
+ var createHmacSignature = ({
1894
+ secret,
1895
+ payload,
1896
+ algorithm = "sha256",
1897
+ digest,
1898
+ prefix = ""
1899
+ }) => `${prefix}${createHmac(algorithm, secret).update(payload).digest(digest)}`;
1900
+ var verifyHmacSignature = (options) => {
1901
+ let expected = createHmacSignature(options);
1902
+ let expectedBytes = Buffer.from(expected);
1903
+ let signatureBytes = Buffer.from(options.signature);
1904
+ let lengthsMatch = signatureBytes.length === expectedBytes.length;
1905
+ let comparableSignature = lengthsMatch ? signatureBytes : Buffer.alloc(expectedBytes.length);
1906
+ return timingSafeEqual(comparableSignature, expectedBytes) && lengthsMatch;
1907
+ };
1908
+
1909
+ // src/file/index.ts
1910
+ var getResponseHeaderValue = (headers, name) => {
1911
+ if (!headers) return void 0;
1912
+ if (typeof headers.get === "function") {
1913
+ let value = headers.get(name);
1914
+ return typeof value === "string" ? value : void 0;
1915
+ }
1916
+ if (typeof headers !== "object") return void 0;
1917
+ let lowerName = name.toLowerCase();
1918
+ for (let [headerName, value] of Object.entries(headers)) {
1919
+ if (headerName.toLowerCase() !== lowerName) continue;
1920
+ if (Array.isArray(value)) {
1921
+ let first = value.find((item) => typeof item === "string");
1922
+ return typeof first === "string" ? first : void 0;
1923
+ }
1924
+ return typeof value === "string" ? value : void 0;
1925
+ }
1926
+ return void 0;
1927
+ };
1928
+ var getBase64ByteLength = (contentBase64) => Buffer.from(contentBase64, "base64").byteLength;
1929
+
1631
1930
  // src/logger/logger.ts
1632
1931
  import { encode } from "@toon-format/toon";
1633
1932
  var isRecord3 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
@@ -1723,6 +2022,17 @@ var SlateLogger = class {
1723
2022
  }
1724
2023
  };
1725
2024
 
2025
+ // src/object/index.ts
2026
+ var pickDefined = (value) => Object.fromEntries(
2027
+ Object.entries(value).filter(([, child]) => child !== void 0)
2028
+ );
2029
+ var setIfDefined = (target, key, value) => {
2030
+ if (value !== void 0) {
2031
+ target[key] = value;
2032
+ }
2033
+ return target;
2034
+ };
2035
+
1726
2036
  // src/specification/slate.ts
1727
2037
  var Slate = class _Slate {
1728
2038
  constructor(_spec, _actions) {
@@ -1785,6 +2095,59 @@ var SlateSpecification = class _SlateSpecification {
1785
2095
  }
1786
2096
  };
1787
2097
  var spec = (params) => SlateSpecification.create(params);
2098
+
2099
+ // src/webhook/graph.ts
2100
+ var graphWebhookHttp = {
2101
+ methods: ["POST"],
2102
+ sync: {
2103
+ mode: "match",
2104
+ match: [{ hasQueryParam: "validationToken" }]
2105
+ }
2106
+ };
2107
+ var getGraphWebhookValidationResponse = (request) => {
2108
+ let validationToken = new URL(request.url).searchParams.get("validationToken");
2109
+ if (validationToken === null) return null;
2110
+ return {
2111
+ status: 200,
2112
+ headers: { "content-type": "text/plain" },
2113
+ body: validationToken
2114
+ };
2115
+ };
2116
+
2117
+ // src/webhook/meta.ts
2118
+ var metaWebhookHttp = {
2119
+ methods: ["GET", "POST"],
2120
+ sync: {
2121
+ mode: "match",
2122
+ match: [{ method: "GET", hasQueryParam: "hub.mode" }]
2123
+ }
2124
+ };
2125
+ var getMetaWebhookVerificationResponse = (request, expectedVerifyToken) => {
2126
+ if (request.method !== "GET") return null;
2127
+ let url = new URL(request.url);
2128
+ let mode = url.searchParams.get("hub.mode");
2129
+ let verifyToken = url.searchParams.get("hub.verify_token");
2130
+ let challenge = url.searchParams.get("hub.challenge");
2131
+ if (mode !== "subscribe" || challenge === null) {
2132
+ return {
2133
+ status: 400,
2134
+ headers: { "content-type": "text/plain" },
2135
+ body: "invalid webhook verification request"
2136
+ };
2137
+ }
2138
+ if (expectedVerifyToken !== void 0 && verifyToken !== expectedVerifyToken) {
2139
+ return {
2140
+ status: 403,
2141
+ headers: { "content-type": "text/plain" },
2142
+ body: "invalid verify token"
2143
+ };
2144
+ }
2145
+ return {
2146
+ status: 200,
2147
+ headers: { "content-type": "text/plain" },
2148
+ body: challenge
2149
+ };
2150
+ };
1788
2151
  export {
1789
2152
  Slate,
1790
2153
  SlateAction,
@@ -1802,24 +2165,47 @@ export {
1802
2165
  anyOf,
1803
2166
  auth,
1804
2167
  axios,
2168
+ buildApiServiceError,
2169
+ collectApiErrorDetails,
1805
2170
  config,
2171
+ createApiServiceError,
2172
+ createAuthenticatedAxios,
1806
2173
  createAxios,
1807
2174
  createBase64Attachment,
2175
+ createHmacSignature,
1808
2176
  createSlateError,
1809
2177
  createTextAttachment,
1810
2178
  createUrlAttachment,
2179
+ extractApiErrorMessage,
2180
+ getApiErrorResponse,
2181
+ getApiErrorStatus,
2182
+ getBase64ByteLength,
1811
2183
  getCurrentContext,
2184
+ getGraphWebhookValidationResponse,
2185
+ getMetaWebhookVerificationResponse,
2186
+ getOAuthExpiresAtFromExpiresIn,
2187
+ getResponseHeaderValue,
1812
2188
  getServiceErrorData,
2189
+ graphWebhookHttp,
1813
2190
  inferSlateErrorFromAxios,
2191
+ isApiErrorRecord,
1814
2192
  isAxiosErrorLike,
1815
2193
  isServiceErrorData,
1816
2194
  mapServiceErrorCode,
1817
2195
  mapServiceErrorToSlateErrorInput,
1818
2196
  mergeScopes,
2197
+ metaWebhookHttp,
2198
+ normalizeOAuthTokenResponse,
2199
+ pickDefined,
2200
+ requestAxios,
2201
+ requestAxiosData,
1819
2202
  runWithContext,
1820
2203
  scopeClause,
2204
+ setIfDefined,
1821
2205
  spec,
2206
+ statusLabelForApiError,
1822
2207
  tool,
1823
2208
  trigger,
1824
- validateScopes
2209
+ validateScopes,
2210
+ verifyHmacSignature
1825
2211
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@slates/provider",
3
- "version": "1.0.0-rc.15",
3
+ "version": "1.0.0-rc.17",
4
4
  "publishConfig": {
5
5
  "access": "public"
6
6
  },
@@ -42,4 +42,4 @@
42
42
  "typescript": "5.8.2",
43
43
  "vitest": "^3.1.2"
44
44
  }
45
- }
45
+ }
@@ -27,6 +27,7 @@ export interface SlateActionParameters {
27
27
  };
28
28
  metadata?: Record<string, any>;
29
29
  scopes?: SlateActionScopes;
30
+ authMethods?: string[];
30
31
  docs?: SlateActionDocsReference[];
31
32
  }
32
33
 
@@ -61,15 +62,58 @@ export type SlateTriggerPollingHandler<
61
62
  updatedState?: any;
62
63
  }>;
63
64
 
65
+ export interface SlateWebhookHttpResponseInit {
66
+ status?: number;
67
+ headers?: Record<string, string>;
68
+ body?: string | Uint8Array | null;
69
+ }
70
+
71
+ export type SlateWebhookHttpMethod =
72
+ | 'GET'
73
+ | 'POST'
74
+ | 'PUT'
75
+ | 'PATCH'
76
+ | 'DELETE'
77
+ | 'HEAD'
78
+ | 'OPTIONS';
79
+
80
+ export interface SlateWebhookRequestMatcher {
81
+ method?: string;
82
+ hasQueryParam?: string;
83
+ hasHeader?: string;
84
+ jsonBodyField?: {
85
+ path: string;
86
+ equals?: string;
87
+ };
88
+ formBodyField?: {
89
+ path: string;
90
+ equals?: string;
91
+ };
92
+ }
93
+
94
+ export interface SlateWebhookHttpOptions {
95
+ methods?: SlateWebhookHttpMethod[];
96
+ sync?: {
97
+ mode: 'never' | 'match' | 'always';
98
+ match?: SlateWebhookRequestMatcher[];
99
+ timeoutMs?: number;
100
+ };
101
+ }
102
+
64
103
  export type SlateTriggerWebhookRequestHandler<
65
104
  ConfigType extends {},
66
105
  AuthType extends {},
67
106
  InputType extends {}
68
107
  > = (
69
- context: SlateContext<ConfigType, AuthType, { request: Request; state: any | null }>
108
+ context: SlateContext<
109
+ ConfigType,
110
+ AuthType,
111
+ { request: Request; state: any | null; registrationDetails: any | null }
112
+ >
70
113
  ) => Promise<{
71
114
  inputs: InputType[];
72
115
  updatedState?: any;
116
+ response?: Response | SlateWebhookHttpResponseInit;
73
117
  }>;
74
118
 
75
119
  export type SlateTriggerWebhookAutoRegistrationHandler<
@@ -116,6 +160,7 @@ export interface SlateActionParametersTrigger<
116
160
  polling?: SlatePollingOptions;
117
161
  handleEvent: SlateTriggerMappingHandler<ConfigType, AuthType, InputType, OutputType>;
118
162
  handleRequest?: SlateTriggerWebhookRequestHandler<ConfigType, AuthType, InputType>;
163
+ http?: SlateWebhookHttpOptions;
119
164
  pollEvents?: SlateTriggerPollingHandler<ConfigType, AuthType, InputType>;
120
165
  autoRegisterWebhook?: SlateTriggerWebhookAutoRegistrationHandler<ConfigType, AuthType>;
121
166
  autoUnregisterWebhook?: SlateTriggerWebhookAutoUnregistrationHandler<ConfigType, AuthType>;
@@ -206,6 +251,10 @@ export abstract class SlateAction<
206
251
  return this._params.scopes;
207
252
  }
208
253
 
254
+ get authMethods() {
255
+ return this._params.authMethods;
256
+ }
257
+
209
258
  get docs() {
210
259
  return this._params.docs;
211
260
  }
@@ -15,7 +15,8 @@ import type {
15
15
  SlateTriggerPollingHandler,
16
16
  SlateTriggerWebhookAutoRegistrationHandler,
17
17
  SlateTriggerWebhookAutoUnregistrationHandler,
18
- SlateTriggerWebhookRequestHandler
18
+ SlateTriggerWebhookRequestHandler,
19
+ SlateWebhookHttpOptions
19
20
  } from './action';
20
21
  import { validateScopes } from './scopes';
21
22
 
@@ -32,6 +33,7 @@ export class SlateActionBuilder<
32
33
  #inputSchema: z.ZodType<InputType> | null = null;
33
34
  #outputSchema: z.ZodType<OutputType> | null = null;
34
35
  #scopes: SlateActionScopes | undefined;
36
+ #authMethods: string[] | undefined;
35
37
 
36
38
  #toolParams: SlateActionParametersTool<ConfigType, AuthType, InputType, OutputType> | null =
37
39
  null;
@@ -76,6 +78,24 @@ export class SlateActionBuilder<
76
78
  return this;
77
79
  }
78
80
 
81
+ authMethods(
82
+ authMethods: string[]
83
+ ): SlateActionBuilder<Type, ConfigType, AuthType, InputType, OutputType, Result> {
84
+ this.#authMethods = this.validateAuthMethods(authMethods);
85
+ return this;
86
+ }
87
+
88
+ private validateAuthMethods(authMethods: string[] | undefined) {
89
+ if (!authMethods) return undefined;
90
+
91
+ let normalized = [...new Set(authMethods.map(authMethod => authMethod.trim()))];
92
+ if (normalized.some(authMethod => !authMethod)) {
93
+ throw new SlateDeclarationError('Auth method IDs must be non-empty strings');
94
+ }
95
+
96
+ return normalized;
97
+ }
98
+
79
99
  handleInvocation(
80
100
  handler: SlateToolInvocationHandler<ConfigType, AuthType, InputType, OutputType>
81
101
  ): SlateActionBuilder<Type, ConfigType, AuthType, InputType, OutputType, Result> {
@@ -96,6 +116,7 @@ export class SlateActionBuilder<
96
116
  handleRequest: SlateTriggerWebhookRequestHandler<ConfigType, AuthType, InputType>;
97
117
  autoRegisterWebhook?: SlateTriggerWebhookAutoRegistrationHandler<ConfigType, AuthType>;
98
118
  autoUnregisterWebhook?: SlateTriggerWebhookAutoUnregistrationHandler<ConfigType, AuthType>;
119
+ http?: SlateWebhookHttpOptions;
99
120
  }): SlateActionBuilder<Type, ConfigType, AuthType, InputType, OutputType, Result> {
100
121
  if (this.type !== 'trigger') {
101
122
  throw new SlateDeclarationError('handleEvent can only be set for trigger actions');
@@ -107,7 +128,8 @@ export class SlateActionBuilder<
107
128
  handleEvent: props.handleEvent,
108
129
  handleRequest: props.handleRequest,
109
130
  autoRegisterWebhook: props.autoRegisterWebhook,
110
- autoUnregisterWebhook: props.autoUnregisterWebhook
131
+ autoUnregisterWebhook: props.autoUnregisterWebhook,
132
+ http: props.http
111
133
  };
112
134
 
113
135
  return this;
@@ -138,6 +160,7 @@ export class SlateActionBuilder<
138
160
  if (scopes) {
139
161
  validateScopes(scopes);
140
162
  }
163
+ let authMethods = this.validateAuthMethods(this.#authMethods ?? this.params.authMethods);
141
164
 
142
165
  if (!this.#inputSchema) {
143
166
  throw new SlateDeclarationError('Input schema is not defined');
@@ -155,6 +178,7 @@ export class SlateActionBuilder<
155
178
  return this.factory({
156
179
  ...this.params,
157
180
  scopes,
181
+ authMethods,
158
182
  configSchema: this.#configSchema,
159
183
  authSchema: this.#authSchema,
160
184
  inputSchema: this.#inputSchema,