@slates/provider 1.0.0-rc.16 → 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.
package/dist/index.cjs CHANGED
@@ -46,26 +46,49 @@ __export(index_exports, {
46
46
  anyOf: () => anyOf,
47
47
  auth: () => auth,
48
48
  axios: () => axios,
49
+ buildApiServiceError: () => buildApiServiceError,
50
+ collectApiErrorDetails: () => collectApiErrorDetails,
49
51
  config: () => config,
52
+ createApiServiceError: () => createApiServiceError,
53
+ createAuthenticatedAxios: () => createAuthenticatedAxios,
50
54
  createAxios: () => createAxios,
51
55
  createBase64Attachment: () => createBase64Attachment,
56
+ createHmacSignature: () => createHmacSignature,
52
57
  createSlateError: () => createSlateError,
53
58
  createTextAttachment: () => createTextAttachment,
54
59
  createUrlAttachment: () => createUrlAttachment,
60
+ extractApiErrorMessage: () => extractApiErrorMessage,
61
+ getApiErrorResponse: () => getApiErrorResponse,
62
+ getApiErrorStatus: () => getApiErrorStatus,
63
+ getBase64ByteLength: () => getBase64ByteLength,
55
64
  getCurrentContext: () => getCurrentContext,
65
+ getGraphWebhookValidationResponse: () => getGraphWebhookValidationResponse,
66
+ getMetaWebhookVerificationResponse: () => getMetaWebhookVerificationResponse,
67
+ getOAuthExpiresAtFromExpiresIn: () => getOAuthExpiresAtFromExpiresIn,
68
+ getResponseHeaderValue: () => getResponseHeaderValue,
56
69
  getServiceErrorData: () => getServiceErrorData,
70
+ graphWebhookHttp: () => graphWebhookHttp,
57
71
  inferSlateErrorFromAxios: () => inferSlateErrorFromAxios,
72
+ isApiErrorRecord: () => isApiErrorRecord,
58
73
  isAxiosErrorLike: () => isAxiosErrorLike,
59
74
  isServiceErrorData: () => isServiceErrorData,
60
75
  mapServiceErrorCode: () => mapServiceErrorCode,
61
76
  mapServiceErrorToSlateErrorInput: () => mapServiceErrorToSlateErrorInput,
62
77
  mergeScopes: () => mergeScopes,
78
+ metaWebhookHttp: () => metaWebhookHttp,
79
+ normalizeOAuthTokenResponse: () => normalizeOAuthTokenResponse,
80
+ pickDefined: () => pickDefined,
81
+ requestAxios: () => requestAxios,
82
+ requestAxiosData: () => requestAxiosData,
63
83
  runWithContext: () => runWithContext,
64
84
  scopeClause: () => scopeClause,
85
+ setIfDefined: () => setIfDefined,
65
86
  spec: () => spec,
87
+ statusLabelForApiError: () => statusLabelForApiError,
66
88
  tool: () => tool,
67
89
  trigger: () => trigger,
68
- validateScopes: () => validateScopes
90
+ validateScopes: () => validateScopes,
91
+ verifyHmacSignature: () => verifyHmacSignature
69
92
  });
70
93
  module.exports = __toCommonJS(index_exports);
71
94
 
@@ -127,6 +150,124 @@ var SlateAction = class {
127
150
  }
128
151
  };
129
152
 
153
+ // src/error/api.ts
154
+ var import_error = require("@lowerdeck/error");
155
+ var DEFAULT_DETAIL_KEYS = ["message", "detail", "error", "error_description", "code"];
156
+ var DEFAULT_NESTED_KEYS = ["errors"];
157
+ var isApiErrorRecord = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
158
+ var pushDetail = (details, value, options = {}) => {
159
+ if (typeof value !== "string" && (options.includeNumbers === false || typeof value !== "number")) {
160
+ return;
161
+ }
162
+ let detail = String(value).trim();
163
+ if (detail && !details.includes(detail)) {
164
+ details.push(detail);
165
+ }
166
+ };
167
+ var collectApiErrorDetails = (value, details, options = {}) => {
168
+ if (Array.isArray(value)) {
169
+ for (let item of value) {
170
+ collectApiErrorDetails(item, details, options);
171
+ }
172
+ return;
173
+ }
174
+ if (!isApiErrorRecord(value)) {
175
+ pushDetail(details, value, options);
176
+ return;
177
+ }
178
+ for (let key of options.detailKeys ?? DEFAULT_DETAIL_KEYS) {
179
+ pushDetail(details, value[key], options);
180
+ }
181
+ for (let key of options.nestedKeys ?? DEFAULT_NESTED_KEYS) {
182
+ collectApiErrorDetails(value[key], details, options);
183
+ }
184
+ };
185
+ var getSlateErrorResponse = (error) => {
186
+ if (error.name !== "SlateError" || !isApiErrorRecord(error.data)) {
187
+ return void 0;
188
+ }
189
+ let data = error.data;
190
+ let upstream = isApiErrorRecord(data.upstream) ? data.upstream : void 0;
191
+ let status = upstream?.status ?? data.status;
192
+ return typeof status === "number" || typeof status === "string" ? { status } : void 0;
193
+ };
194
+ var getApiErrorResponse = (error) => {
195
+ if (!isApiErrorRecord(error)) {
196
+ return void 0;
197
+ }
198
+ if (!isApiErrorRecord(error.response)) {
199
+ return getSlateErrorResponse(error);
200
+ }
201
+ let response = error.response;
202
+ return {
203
+ status: typeof response.status === "number" || typeof response.status === "string" ? response.status : void 0,
204
+ statusText: typeof response.statusText === "string" ? response.statusText : void 0,
205
+ data: response.data
206
+ };
207
+ };
208
+ var getApiErrorStatus = (error) => getApiErrorResponse(error)?.status;
209
+ var statusLabelForApiError = (status, statusText) => status !== void 0 ? `HTTP ${status}${statusText ? ` ${statusText}` : ""}: ` : "";
210
+ var extractApiErrorMessage = (error, options = {}) => {
211
+ let details = [];
212
+ let response = options.response ?? getApiErrorResponse(error);
213
+ collectApiErrorDetails(response?.data, details, options);
214
+ if (details.length > 0) {
215
+ return details.join(options.separator ?? " - ");
216
+ }
217
+ if (error instanceof Error && error.message) {
218
+ return error.message;
219
+ }
220
+ return options.fallbackMessage ?? "Unknown error";
221
+ };
222
+ var createApiServiceError = (message, options = {}) => {
223
+ let error = new import_error.ServiceError((0, import_error.badRequestError)({ message }));
224
+ if (options.reason !== void 0) {
225
+ error.data.reason = options.reason;
226
+ }
227
+ if ("upstreamStatus" in options) {
228
+ error.data.upstreamStatus = options.upstreamStatus;
229
+ }
230
+ if ("upstreamCode" in options) {
231
+ error.data.upstreamCode = options.upstreamCode;
232
+ }
233
+ if (options.parent instanceof Error) {
234
+ error.setParent(options.parent);
235
+ }
236
+ return error;
237
+ };
238
+ var helpers = {
239
+ collectDetails: collectApiErrorDetails,
240
+ extractMessage: extractApiErrorMessage,
241
+ getResponse: getApiErrorResponse,
242
+ getStatus: getApiErrorStatus,
243
+ isRecord: isApiErrorRecord,
244
+ statusLabelFor: statusLabelForApiError
245
+ };
246
+ var buildApiServiceError = (error, options) => {
247
+ if (error instanceof import_error.ServiceError) {
248
+ return error;
249
+ }
250
+ let operation = options.operation ?? "request";
251
+ let response = options.extractResponse?.(error, helpers) ?? getApiErrorResponse(error);
252
+ let status = options.extractStatus?.(error, response, helpers) ?? response?.status;
253
+ let message = options.extractMessage?.(error, helpers) ?? extractApiErrorMessage(error, { ...options, response });
254
+ let statusLabel = statusLabelForApiError(status, response?.statusText);
255
+ let fullMessage = options.formatMessage?.({
256
+ providerLabel: options.providerLabel,
257
+ operation,
258
+ statusLabel,
259
+ message,
260
+ response,
261
+ status
262
+ }) ?? `${options.providerLabel} API ${operation} failed: ${statusLabel}${message}`;
263
+ return createApiServiceError(fullMessage, {
264
+ reason: options.reason,
265
+ upstreamStatus: status,
266
+ upstreamCode: options.extractUpstreamCode?.(error, response, helpers),
267
+ parent: options.parent ?? error
268
+ });
269
+ };
270
+
130
271
  // src/error/axios.ts
131
272
  var import_axios = require("axios");
132
273
 
@@ -468,7 +609,7 @@ var inferSlateErrorFromAxios = (error, options = {}) => {
468
609
  };
469
610
 
470
611
  // src/error/service.ts
471
- var import_error = require("@lowerdeck/error");
612
+ var import_error2 = require("@lowerdeck/error");
472
613
  var SERVICE_ERROR_DATA_RESERVED_KEYS = /* @__PURE__ */ new Set([
473
614
  "status",
474
615
  "code",
@@ -535,7 +676,7 @@ var getServiceErrorBaggage = (data) => {
535
676
  var mapServiceErrorCode = (code, status) => SERVICE_ERROR_CODE_MAP[code] ?? mapServiceErrorCodeByStatus(status);
536
677
  var isServiceErrorData = (value) => isRecord(value) && typeof value.status === "number" && typeof value.code === "string" && typeof value.message === "string" && !("config" in value) && !("request" in value) && !("response" in value) && !("isAxiosError" in value);
537
678
  var getServiceErrorData = (error) => {
538
- if ((0, import_error.isServiceError)(error)) {
679
+ if ((0, import_error2.isServiceError)(error)) {
539
680
  return isServiceErrorData(error.data) ? error.data : null;
540
681
  }
541
682
  if (isServiceErrorData(error)) {
@@ -816,7 +957,8 @@ var SlateActionBuilder = class {
816
957
  handleEvent: props.handleEvent,
817
958
  handleRequest: props.handleRequest,
818
959
  autoRegisterWebhook: props.autoRegisterWebhook,
819
- autoUnregisterWebhook: props.autoUnregisterWebhook
960
+ autoUnregisterWebhook: props.autoUnregisterWebhook,
961
+ http: props.http
820
962
  };
821
963
  return this;
822
964
  }
@@ -891,6 +1033,7 @@ var SlateTrigger = class _SlateTrigger extends SlateAction {
891
1033
  #polling;
892
1034
  #handleEvent;
893
1035
  #handleRequest;
1036
+ #http;
894
1037
  #pollEvents;
895
1038
  #autoRegisterWebhook;
896
1039
  #autoUnregisterWebhook;
@@ -902,6 +1045,7 @@ var SlateTrigger = class _SlateTrigger extends SlateAction {
902
1045
  };
903
1046
  this.#handleEvent = params.handleEvent;
904
1047
  this.#handleRequest = params.handleRequest;
1048
+ this.#http = params.http;
905
1049
  this.#pollEvents = params.pollEvents;
906
1050
  this.#autoRegisterWebhook = params.autoRegisterWebhook;
907
1051
  this.#autoUnregisterWebhook = params.autoUnregisterWebhook;
@@ -924,6 +1068,9 @@ var SlateTrigger = class _SlateTrigger extends SlateAction {
924
1068
  get handleRequest() {
925
1069
  return this.#handleRequest;
926
1070
  }
1071
+ get http() {
1072
+ return this.#http;
1073
+ }
927
1074
  get pollEvents() {
928
1075
  return this.#pollEvents;
929
1076
  }
@@ -1018,6 +1165,90 @@ var SlateAuth = class _SlateAuth {
1018
1165
  };
1019
1166
  var auth = () => SlateAuth.create();
1020
1167
 
1168
+ // src/auth/oauth.ts
1169
+ var isOAuthTokenResponse = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
1170
+ var formatOAuthTokenResponseMessage = (fieldLabel, providerLabel = "OAuth provider", operation = "token response") => `${providerLabel} OAuth ${operation} did not return ${fieldLabel}.`;
1171
+ var getOAuthExpiresAtFromExpiresIn = (expiresIn, options = {}) => {
1172
+ let {
1173
+ nowMs = Date.now(),
1174
+ providerLabel,
1175
+ operation,
1176
+ required = false,
1177
+ expiresInType = "numberLike",
1178
+ message
1179
+ } = options;
1180
+ if (expiresIn === void 0 || expiresIn === null || expiresIn === "") {
1181
+ if (required) {
1182
+ throw createApiServiceError(
1183
+ message ?? formatOAuthTokenResponseMessage(
1184
+ "a valid expires_in value",
1185
+ providerLabel,
1186
+ operation
1187
+ ),
1188
+ { reason: "oauth_token_response" }
1189
+ );
1190
+ }
1191
+ return void 0;
1192
+ }
1193
+ let seconds = typeof expiresIn === "number" ? expiresIn : expiresInType === "numberLike" && typeof expiresIn === "string" ? Number(expiresIn) : Number.NaN;
1194
+ if (!Number.isFinite(seconds)) {
1195
+ throw createApiServiceError(
1196
+ message ?? formatOAuthTokenResponseMessage("a valid expires_in value", providerLabel, operation),
1197
+ { reason: "oauth_token_response" }
1198
+ );
1199
+ }
1200
+ return new Date(nowMs + seconds * 1e3).toISOString();
1201
+ };
1202
+ var getRefreshToken = (refreshToken, previousRefreshToken, fallbackMode) => {
1203
+ if (typeof refreshToken === "string") {
1204
+ if (fallbackMode === "falsy" && !refreshToken) {
1205
+ return previousRefreshToken;
1206
+ }
1207
+ return refreshToken;
1208
+ }
1209
+ if (fallbackMode === "falsy" && !refreshToken) {
1210
+ return previousRefreshToken;
1211
+ }
1212
+ return refreshToken === void 0 || refreshToken === null ? previousRefreshToken : void 0;
1213
+ };
1214
+ var normalizeOAuthTokenResponse = (data, options = {}) => {
1215
+ let {
1216
+ providerLabel,
1217
+ operation,
1218
+ previousRefreshToken,
1219
+ refreshTokenFallbackMode = "nullish",
1220
+ accessTokenMessage,
1221
+ expiresInMessage,
1222
+ ...expiresAtOptions
1223
+ } = options;
1224
+ if (!isOAuthTokenResponse(data)) {
1225
+ throw createApiServiceError(
1226
+ accessTokenMessage ?? formatOAuthTokenResponseMessage("a token response object", providerLabel, operation),
1227
+ { reason: "oauth_token_response" }
1228
+ );
1229
+ }
1230
+ if (typeof data.access_token !== "string" || !data.access_token) {
1231
+ throw createApiServiceError(
1232
+ accessTokenMessage ?? formatOAuthTokenResponseMessage("an access token", providerLabel, operation),
1233
+ { reason: "oauth_token_response" }
1234
+ );
1235
+ }
1236
+ return {
1237
+ token: data.access_token,
1238
+ refreshToken: getRefreshToken(
1239
+ data.refresh_token,
1240
+ previousRefreshToken,
1241
+ refreshTokenFallbackMode
1242
+ ),
1243
+ expiresAt: getOAuthExpiresAtFromExpiresIn(data.expires_in, {
1244
+ ...expiresAtOptions,
1245
+ providerLabel,
1246
+ operation,
1247
+ message: expiresInMessage
1248
+ })
1249
+ };
1250
+ };
1251
+
1021
1252
  // src/axios/index.ts
1022
1253
  var import_axios3 = __toESM(require("axios"), 1);
1023
1254
 
@@ -1601,6 +1832,35 @@ var createAxios = (config2) => {
1601
1832
  applySlateInterceptors(instance, errorMapping);
1602
1833
  return instance;
1603
1834
  };
1835
+ var createAuthenticatedAxios = (config2) => {
1836
+ let { authHeader, contentType = "application/json", errorAdapter, ...axiosConfig } = config2;
1837
+ let instance = createAxios({
1838
+ ...axiosConfig,
1839
+ headers: {
1840
+ ...contentType === false ? {} : { "Content-Type": contentType },
1841
+ ...authHeader ? { [authHeader.name ?? "Authorization"]: authHeader.value } : {},
1842
+ ...axiosConfig.headers
1843
+ }
1844
+ });
1845
+ if (errorAdapter) {
1846
+ instance.interceptors.response.use(
1847
+ (response) => response,
1848
+ (error) => Promise.reject(errorAdapter(error))
1849
+ );
1850
+ }
1851
+ return instance;
1852
+ };
1853
+ var requestAxios = async (operation, request, errorAdapter) => {
1854
+ try {
1855
+ return await request();
1856
+ } catch (error) {
1857
+ throw errorAdapter(error, operation);
1858
+ }
1859
+ };
1860
+ var requestAxiosData = async (operation, request, errorAdapter) => {
1861
+ let response = await requestAxios(operation, request, errorAdapter);
1862
+ return response.data;
1863
+ };
1604
1864
  var axios = createAxios();
1605
1865
 
1606
1866
  // src/config/config.ts
@@ -1679,6 +1939,11 @@ var SlateContext = class {
1679
1939
  get request() {
1680
1940
  return Object.freeze(this.#input.request);
1681
1941
  }
1942
+ get registrationDetails() {
1943
+ return Object.freeze(
1944
+ this.#input.registrationDetails
1945
+ );
1946
+ }
1682
1947
  get auth() {
1683
1948
  return Object.freeze(this.#auth);
1684
1949
  }
@@ -1717,6 +1982,45 @@ var SlateContext = class {
1717
1982
  }
1718
1983
  };
1719
1984
 
1985
+ // src/crypto/hmac.ts
1986
+ var import_crypto = require("crypto");
1987
+ var createHmacSignature = ({
1988
+ secret,
1989
+ payload,
1990
+ algorithm = "sha256",
1991
+ digest,
1992
+ prefix = ""
1993
+ }) => `${prefix}${(0, import_crypto.createHmac)(algorithm, secret).update(payload).digest(digest)}`;
1994
+ var verifyHmacSignature = (options) => {
1995
+ let expected = createHmacSignature(options);
1996
+ let expectedBytes = Buffer.from(expected);
1997
+ let signatureBytes = Buffer.from(options.signature);
1998
+ let lengthsMatch = signatureBytes.length === expectedBytes.length;
1999
+ let comparableSignature = lengthsMatch ? signatureBytes : Buffer.alloc(expectedBytes.length);
2000
+ return (0, import_crypto.timingSafeEqual)(comparableSignature, expectedBytes) && lengthsMatch;
2001
+ };
2002
+
2003
+ // src/file/index.ts
2004
+ var getResponseHeaderValue = (headers, name) => {
2005
+ if (!headers) return void 0;
2006
+ if (typeof headers.get === "function") {
2007
+ let value = headers.get(name);
2008
+ return typeof value === "string" ? value : void 0;
2009
+ }
2010
+ if (typeof headers !== "object") return void 0;
2011
+ let lowerName = name.toLowerCase();
2012
+ for (let [headerName, value] of Object.entries(headers)) {
2013
+ if (headerName.toLowerCase() !== lowerName) continue;
2014
+ if (Array.isArray(value)) {
2015
+ let first = value.find((item) => typeof item === "string");
2016
+ return typeof first === "string" ? first : void 0;
2017
+ }
2018
+ return typeof value === "string" ? value : void 0;
2019
+ }
2020
+ return void 0;
2021
+ };
2022
+ var getBase64ByteLength = (contentBase64) => Buffer.from(contentBase64, "base64").byteLength;
2023
+
1720
2024
  // src/logger/logger.ts
1721
2025
  var import_toon = require("@toon-format/toon");
1722
2026
  var isRecord3 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
@@ -1812,6 +2116,17 @@ var SlateLogger = class {
1812
2116
  }
1813
2117
  };
1814
2118
 
2119
+ // src/object/index.ts
2120
+ var pickDefined = (value) => Object.fromEntries(
2121
+ Object.entries(value).filter(([, child]) => child !== void 0)
2122
+ );
2123
+ var setIfDefined = (target, key, value) => {
2124
+ if (value !== void 0) {
2125
+ target[key] = value;
2126
+ }
2127
+ return target;
2128
+ };
2129
+
1815
2130
  // src/specification/slate.ts
1816
2131
  var Slate = class _Slate {
1817
2132
  constructor(_spec, _actions) {
@@ -1874,6 +2189,59 @@ var SlateSpecification = class _SlateSpecification {
1874
2189
  }
1875
2190
  };
1876
2191
  var spec = (params) => SlateSpecification.create(params);
2192
+
2193
+ // src/webhook/graph.ts
2194
+ var graphWebhookHttp = {
2195
+ methods: ["POST"],
2196
+ sync: {
2197
+ mode: "match",
2198
+ match: [{ hasQueryParam: "validationToken" }]
2199
+ }
2200
+ };
2201
+ var getGraphWebhookValidationResponse = (request) => {
2202
+ let validationToken = new URL(request.url).searchParams.get("validationToken");
2203
+ if (validationToken === null) return null;
2204
+ return {
2205
+ status: 200,
2206
+ headers: { "content-type": "text/plain" },
2207
+ body: validationToken
2208
+ };
2209
+ };
2210
+
2211
+ // src/webhook/meta.ts
2212
+ var metaWebhookHttp = {
2213
+ methods: ["GET", "POST"],
2214
+ sync: {
2215
+ mode: "match",
2216
+ match: [{ method: "GET", hasQueryParam: "hub.mode" }]
2217
+ }
2218
+ };
2219
+ var getMetaWebhookVerificationResponse = (request, expectedVerifyToken) => {
2220
+ if (request.method !== "GET") return null;
2221
+ let url = new URL(request.url);
2222
+ let mode = url.searchParams.get("hub.mode");
2223
+ let verifyToken = url.searchParams.get("hub.verify_token");
2224
+ let challenge = url.searchParams.get("hub.challenge");
2225
+ if (mode !== "subscribe" || challenge === null) {
2226
+ return {
2227
+ status: 400,
2228
+ headers: { "content-type": "text/plain" },
2229
+ body: "invalid webhook verification request"
2230
+ };
2231
+ }
2232
+ if (expectedVerifyToken !== void 0 && verifyToken !== expectedVerifyToken) {
2233
+ return {
2234
+ status: 403,
2235
+ headers: { "content-type": "text/plain" },
2236
+ body: "invalid verify token"
2237
+ };
2238
+ }
2239
+ return {
2240
+ status: 200,
2241
+ headers: { "content-type": "text/plain" },
2242
+ body: challenge
2243
+ };
2244
+ };
1877
2245
  // Annotate the CommonJS export names for ESM import in node:
1878
2246
  0 && (module.exports = {
1879
2247
  Slate,
@@ -1892,24 +2260,47 @@ var spec = (params) => SlateSpecification.create(params);
1892
2260
  anyOf,
1893
2261
  auth,
1894
2262
  axios,
2263
+ buildApiServiceError,
2264
+ collectApiErrorDetails,
1895
2265
  config,
2266
+ createApiServiceError,
2267
+ createAuthenticatedAxios,
1896
2268
  createAxios,
1897
2269
  createBase64Attachment,
2270
+ createHmacSignature,
1898
2271
  createSlateError,
1899
2272
  createTextAttachment,
1900
2273
  createUrlAttachment,
2274
+ extractApiErrorMessage,
2275
+ getApiErrorResponse,
2276
+ getApiErrorStatus,
2277
+ getBase64ByteLength,
1901
2278
  getCurrentContext,
2279
+ getGraphWebhookValidationResponse,
2280
+ getMetaWebhookVerificationResponse,
2281
+ getOAuthExpiresAtFromExpiresIn,
2282
+ getResponseHeaderValue,
1902
2283
  getServiceErrorData,
2284
+ graphWebhookHttp,
1903
2285
  inferSlateErrorFromAxios,
2286
+ isApiErrorRecord,
1904
2287
  isAxiosErrorLike,
1905
2288
  isServiceErrorData,
1906
2289
  mapServiceErrorCode,
1907
2290
  mapServiceErrorToSlateErrorInput,
1908
2291
  mergeScopes,
2292
+ metaWebhookHttp,
2293
+ normalizeOAuthTokenResponse,
2294
+ pickDefined,
2295
+ requestAxios,
2296
+ requestAxiosData,
1909
2297
  runWithContext,
1910
2298
  scopeClause,
2299
+ setIfDefined,
1911
2300
  spec,
2301
+ statusLabelForApiError,
1912
2302
  tool,
1913
2303
  trigger,
1914
- validateScopes
2304
+ validateScopes,
2305
+ verifyHmacSignature
1915
2306
  });