@vercel/slack-bolt 0.1.0
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/LICENSE +21 -0
- package/README.md +139 -0
- package/dist/index.d.mts +156 -0
- package/dist/index.d.ts +156 -0
- package/dist/index.js +390 -0
- package/dist/index.js.map +1 -0
- package/dist/index.mjs +387 -0
- package/dist/index.mjs.map +1 -0
- package/package.json +69 -0
package/dist/index.mjs
ADDED
|
@@ -0,0 +1,387 @@
|
|
|
1
|
+
import { waitUntil } from '@vercel/functions';
|
|
2
|
+
import { verifySlackRequest } from '@slack/bolt';
|
|
3
|
+
import { ConsoleLogger, LogLevel } from '@slack/logger';
|
|
4
|
+
|
|
5
|
+
var __defProp = Object.defineProperty;
|
|
6
|
+
var __defProps = Object.defineProperties;
|
|
7
|
+
var __getOwnPropDescs = Object.getOwnPropertyDescriptors;
|
|
8
|
+
var __getOwnPropSymbols = Object.getOwnPropertySymbols;
|
|
9
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
10
|
+
var __propIsEnum = Object.prototype.propertyIsEnumerable;
|
|
11
|
+
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
|
|
12
|
+
var __spreadValues = (a, b) => {
|
|
13
|
+
for (var prop in b || (b = {}))
|
|
14
|
+
if (__hasOwnProp.call(b, prop))
|
|
15
|
+
__defNormalProp(a, prop, b[prop]);
|
|
16
|
+
if (__getOwnPropSymbols)
|
|
17
|
+
for (var prop of __getOwnPropSymbols(b)) {
|
|
18
|
+
if (__propIsEnum.call(b, prop))
|
|
19
|
+
__defNormalProp(a, prop, b[prop]);
|
|
20
|
+
}
|
|
21
|
+
return a;
|
|
22
|
+
};
|
|
23
|
+
var __spreadProps = (a, b) => __defProps(a, __getOwnPropDescs(b));
|
|
24
|
+
var __async = (__this, __arguments, generator) => {
|
|
25
|
+
return new Promise((resolve, reject) => {
|
|
26
|
+
var fulfilled = (value) => {
|
|
27
|
+
try {
|
|
28
|
+
step(generator.next(value));
|
|
29
|
+
} catch (e) {
|
|
30
|
+
reject(e);
|
|
31
|
+
}
|
|
32
|
+
};
|
|
33
|
+
var rejected = (value) => {
|
|
34
|
+
try {
|
|
35
|
+
step(generator.throw(value));
|
|
36
|
+
} catch (e) {
|
|
37
|
+
reject(e);
|
|
38
|
+
}
|
|
39
|
+
};
|
|
40
|
+
var step = (x) => x.done ? resolve(x.value) : Promise.resolve(x.value).then(fulfilled, rejected);
|
|
41
|
+
step((generator = generator.apply(__this, __arguments)).next());
|
|
42
|
+
});
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
// src/errors.ts
|
|
46
|
+
var VercelReceiverError = class extends Error {
|
|
47
|
+
constructor(message, statusCode = 500) {
|
|
48
|
+
super(message);
|
|
49
|
+
this.statusCode = statusCode;
|
|
50
|
+
this.name = "VercelReceiverError";
|
|
51
|
+
}
|
|
52
|
+
};
|
|
53
|
+
var SignatureVerificationError = class extends VercelReceiverError {
|
|
54
|
+
constructor(message = "Invalid request signature") {
|
|
55
|
+
super(message, 401);
|
|
56
|
+
this.name = "SignatureVerificationError";
|
|
57
|
+
}
|
|
58
|
+
};
|
|
59
|
+
var RequestParsingError = class extends VercelReceiverError {
|
|
60
|
+
constructor(message = "Failed to parse request") {
|
|
61
|
+
super(message, 400);
|
|
62
|
+
this.name = "RequestParsingError";
|
|
63
|
+
}
|
|
64
|
+
};
|
|
65
|
+
var SCOPE = ["@vercel/slack-bolt"];
|
|
66
|
+
var ACK_TIMEOUT_MS = 3001;
|
|
67
|
+
var SLACK_RETRY_NUM_HEADER = "x-slack-retry-num";
|
|
68
|
+
var SLACK_RETRY_REASON_HEADER = "x-slack-retry-reason";
|
|
69
|
+
var SLACK_TIMESTAMP_HEADER = "x-slack-request-timestamp";
|
|
70
|
+
var SLACK_SIGNATURE_HEADER = "x-slack-signature";
|
|
71
|
+
var VercelReceiver = class {
|
|
72
|
+
/**
|
|
73
|
+
* Gets the logger instance used by this receiver.
|
|
74
|
+
* @returns The logger instance
|
|
75
|
+
*/
|
|
76
|
+
getLogger() {
|
|
77
|
+
return this.logger;
|
|
78
|
+
}
|
|
79
|
+
/**
|
|
80
|
+
* Creates a new VercelReceiver instance.
|
|
81
|
+
*
|
|
82
|
+
* @param options - Configuration options for the receiver
|
|
83
|
+
* @throws {VercelReceiverError} When signing secret is not provided
|
|
84
|
+
*
|
|
85
|
+
* @example
|
|
86
|
+
* ```typescript
|
|
87
|
+
* const receiver = new VercelReceiver();
|
|
88
|
+
* ```
|
|
89
|
+
*/
|
|
90
|
+
constructor({
|
|
91
|
+
signingSecret = process.env.SLACK_SIGNING_SECRET,
|
|
92
|
+
signatureVerification = true,
|
|
93
|
+
logger,
|
|
94
|
+
logLevel = LogLevel.INFO,
|
|
95
|
+
customPropertiesExtractor,
|
|
96
|
+
customResponseHandler
|
|
97
|
+
} = {}) {
|
|
98
|
+
if (!signingSecret) {
|
|
99
|
+
throw new VercelReceiverError(
|
|
100
|
+
"SLACK_SIGNING_SECRET is required for VercelReceiver"
|
|
101
|
+
);
|
|
102
|
+
}
|
|
103
|
+
this.signingSecret = signingSecret;
|
|
104
|
+
this.signatureVerification = signatureVerification;
|
|
105
|
+
this.logger = this.createScopedLogger(
|
|
106
|
+
logger != null ? logger : new ConsoleLogger(),
|
|
107
|
+
logLevel
|
|
108
|
+
);
|
|
109
|
+
this.customPropertiesExtractor = customPropertiesExtractor;
|
|
110
|
+
this.customResponseHandler = customResponseHandler;
|
|
111
|
+
this.logger.debug("VercelReceiver initialized");
|
|
112
|
+
}
|
|
113
|
+
/**
|
|
114
|
+
* Initializes the receiver with a Slack Bolt app instance.
|
|
115
|
+
* This method is called automatically by the Bolt framework.
|
|
116
|
+
*
|
|
117
|
+
* @param app - The Slack Bolt app instance
|
|
118
|
+
*/
|
|
119
|
+
init(app) {
|
|
120
|
+
this.app = app;
|
|
121
|
+
this.logger.debug("App initialized in VercelReceiver");
|
|
122
|
+
}
|
|
123
|
+
/**
|
|
124
|
+
* Starts the receiver and returns a handler function for processing requests.
|
|
125
|
+
* This method is called automatically by the Bolt framework.
|
|
126
|
+
*
|
|
127
|
+
* @returns A handler function that processes incoming Slack requests
|
|
128
|
+
*/
|
|
129
|
+
start() {
|
|
130
|
+
return __async(this, null, function* () {
|
|
131
|
+
this.logger.debug("VercelReceiver started");
|
|
132
|
+
return this.toHandler();
|
|
133
|
+
});
|
|
134
|
+
}
|
|
135
|
+
/**
|
|
136
|
+
* Stops the receiver. This method is called automatically by the Bolt framework.
|
|
137
|
+
*/
|
|
138
|
+
stop() {
|
|
139
|
+
return __async(this, null, function* () {
|
|
140
|
+
this.logger.debug("VercelReceiver stopped");
|
|
141
|
+
});
|
|
142
|
+
}
|
|
143
|
+
/**
|
|
144
|
+
* Creates a handler function that processes incoming Slack requests.
|
|
145
|
+
* This is the main entry point for handling Slack events in Vercel.
|
|
146
|
+
* It is called automatically by the Bolt framework in the start() method.
|
|
147
|
+
*
|
|
148
|
+
* @returns A handler function compatible with Vercel's function signature
|
|
149
|
+
*/
|
|
150
|
+
toHandler() {
|
|
151
|
+
return (req) => __async(this, null, function* () {
|
|
152
|
+
try {
|
|
153
|
+
if (!this.app) {
|
|
154
|
+
throw new VercelReceiverError("Slack app not initialized", 500);
|
|
155
|
+
}
|
|
156
|
+
const rawBody = yield req.text();
|
|
157
|
+
if (this.signatureVerification) {
|
|
158
|
+
yield this.verifySlackRequest(req, rawBody);
|
|
159
|
+
}
|
|
160
|
+
const body = yield this.parseRequestBody(req, rawBody);
|
|
161
|
+
if (body.type === "url_verification") {
|
|
162
|
+
this.logger.debug("Handling URL verification challenge");
|
|
163
|
+
return Response.json({ challenge: body.challenge });
|
|
164
|
+
}
|
|
165
|
+
return yield this.handleSlackEvent(req, body);
|
|
166
|
+
} catch (error) {
|
|
167
|
+
return this.handleError(error);
|
|
168
|
+
}
|
|
169
|
+
});
|
|
170
|
+
}
|
|
171
|
+
parseRequestBody(req, rawBody) {
|
|
172
|
+
return __async(this, null, function* () {
|
|
173
|
+
const contentType = req.headers.get("content-type");
|
|
174
|
+
try {
|
|
175
|
+
if (contentType === "application/x-www-form-urlencoded") {
|
|
176
|
+
const parsedBody = {};
|
|
177
|
+
const params = new URLSearchParams(rawBody);
|
|
178
|
+
for (const [key, value] of params.entries()) {
|
|
179
|
+
parsedBody[key] = value;
|
|
180
|
+
}
|
|
181
|
+
if (typeof parsedBody.payload === "string") {
|
|
182
|
+
return JSON.parse(parsedBody.payload);
|
|
183
|
+
}
|
|
184
|
+
return parsedBody;
|
|
185
|
+
}
|
|
186
|
+
if (contentType === "application/json") {
|
|
187
|
+
return JSON.parse(rawBody);
|
|
188
|
+
}
|
|
189
|
+
this.logger.warn(`Unexpected content-type detected: ${contentType}`);
|
|
190
|
+
return JSON.parse(rawBody);
|
|
191
|
+
} catch (e) {
|
|
192
|
+
throw new RequestParsingError(
|
|
193
|
+
`Failed to parse body as JSON data for content-type: ${contentType}. Error: ${e instanceof Error ? e.message : String(e)}`
|
|
194
|
+
);
|
|
195
|
+
}
|
|
196
|
+
});
|
|
197
|
+
}
|
|
198
|
+
handleSlackEvent(req, body) {
|
|
199
|
+
return __async(this, null, function* () {
|
|
200
|
+
if (!this.app) {
|
|
201
|
+
throw new VercelReceiverError("App not initialized", 500);
|
|
202
|
+
}
|
|
203
|
+
let isAcknowledged = false;
|
|
204
|
+
let responseResolver;
|
|
205
|
+
let responseRejecter;
|
|
206
|
+
const responsePromise = new Promise((resolve, reject) => {
|
|
207
|
+
responseResolver = resolve;
|
|
208
|
+
responseRejecter = reject;
|
|
209
|
+
});
|
|
210
|
+
const timeoutId = setTimeout(() => {
|
|
211
|
+
if (!isAcknowledged) {
|
|
212
|
+
this.logger.error("Event not acknowledged within timeout period");
|
|
213
|
+
const error = new VercelReceiverError("Request timeout", 408);
|
|
214
|
+
responseRejecter(error);
|
|
215
|
+
}
|
|
216
|
+
}, ACK_TIMEOUT_MS);
|
|
217
|
+
const ackFn = (ackResponse) => __async(this, null, function* () {
|
|
218
|
+
if (isAcknowledged) {
|
|
219
|
+
throw new Error("Cannot acknowledge an event multiple times");
|
|
220
|
+
}
|
|
221
|
+
isAcknowledged = true;
|
|
222
|
+
clearTimeout(timeoutId);
|
|
223
|
+
try {
|
|
224
|
+
let response;
|
|
225
|
+
if (this.customResponseHandler) {
|
|
226
|
+
const event2 = this.createSlackReceiverEvent({
|
|
227
|
+
body,
|
|
228
|
+
headers: req.headers,
|
|
229
|
+
ack: ackFn,
|
|
230
|
+
request: req
|
|
231
|
+
});
|
|
232
|
+
response = yield this.customResponseHandler(event2);
|
|
233
|
+
} else {
|
|
234
|
+
const responseBody = ackResponse || null;
|
|
235
|
+
const body2 = typeof responseBody === "string" ? responseBody : JSON.stringify(responseBody);
|
|
236
|
+
response = new Response(body2, {
|
|
237
|
+
status: 200,
|
|
238
|
+
headers: {
|
|
239
|
+
"Content-Type": "application/json"
|
|
240
|
+
}
|
|
241
|
+
});
|
|
242
|
+
}
|
|
243
|
+
responseResolver(response);
|
|
244
|
+
} catch (error) {
|
|
245
|
+
this.logger.error("Error in acknowledgment handler", error);
|
|
246
|
+
responseRejecter(
|
|
247
|
+
error instanceof Error ? error : new Error(String(error))
|
|
248
|
+
);
|
|
249
|
+
}
|
|
250
|
+
});
|
|
251
|
+
const event = this.createSlackReceiverEvent({
|
|
252
|
+
body,
|
|
253
|
+
headers: req.headers,
|
|
254
|
+
ack: ackFn,
|
|
255
|
+
request: req
|
|
256
|
+
});
|
|
257
|
+
waitUntil(this.app.processEvent(event));
|
|
258
|
+
try {
|
|
259
|
+
return yield responsePromise;
|
|
260
|
+
} catch (error) {
|
|
261
|
+
return this.handleError(error);
|
|
262
|
+
}
|
|
263
|
+
});
|
|
264
|
+
}
|
|
265
|
+
verifySlackRequest(req, rawBody) {
|
|
266
|
+
return __async(this, null, function* () {
|
|
267
|
+
const timestamp = req.headers.get(SLACK_TIMESTAMP_HEADER);
|
|
268
|
+
const signature = req.headers.get(SLACK_SIGNATURE_HEADER);
|
|
269
|
+
if (!timestamp) {
|
|
270
|
+
throw new SignatureVerificationError("Missing required timestamp header");
|
|
271
|
+
}
|
|
272
|
+
if (!signature) {
|
|
273
|
+
throw new SignatureVerificationError(
|
|
274
|
+
"Missing required signature headers"
|
|
275
|
+
);
|
|
276
|
+
}
|
|
277
|
+
try {
|
|
278
|
+
verifySlackRequest({
|
|
279
|
+
signingSecret: this.signingSecret,
|
|
280
|
+
body: rawBody,
|
|
281
|
+
headers: {
|
|
282
|
+
"x-slack-signature": signature,
|
|
283
|
+
"x-slack-request-timestamp": Number.parseInt(timestamp, 10)
|
|
284
|
+
},
|
|
285
|
+
logger: this.logger
|
|
286
|
+
});
|
|
287
|
+
} catch (error) {
|
|
288
|
+
this.logger.error("Slack request verification failed", error);
|
|
289
|
+
throw new SignatureVerificationError(
|
|
290
|
+
error instanceof Error ? error.message : "Signature verification failed"
|
|
291
|
+
);
|
|
292
|
+
}
|
|
293
|
+
});
|
|
294
|
+
}
|
|
295
|
+
createSlackReceiverEvent({
|
|
296
|
+
body,
|
|
297
|
+
headers,
|
|
298
|
+
ack,
|
|
299
|
+
request
|
|
300
|
+
}) {
|
|
301
|
+
const customProperties = this.customPropertiesExtractor ? this.customPropertiesExtractor(request) : {};
|
|
302
|
+
const retryNum = headers.get(SLACK_RETRY_NUM_HEADER) || "0";
|
|
303
|
+
const retryReason = headers.get(SLACK_RETRY_REASON_HEADER) || "";
|
|
304
|
+
return {
|
|
305
|
+
body,
|
|
306
|
+
ack,
|
|
307
|
+
retryNum: Number(retryNum),
|
|
308
|
+
retryReason,
|
|
309
|
+
customProperties
|
|
310
|
+
};
|
|
311
|
+
}
|
|
312
|
+
handleError(error) {
|
|
313
|
+
if (error instanceof VercelReceiverError) {
|
|
314
|
+
this.logger.error(`VercelReceiverError: ${error.message}`, {
|
|
315
|
+
statusCode: error.statusCode,
|
|
316
|
+
name: error.name
|
|
317
|
+
});
|
|
318
|
+
return new Response(
|
|
319
|
+
JSON.stringify({
|
|
320
|
+
error: error.message,
|
|
321
|
+
type: error.name
|
|
322
|
+
}),
|
|
323
|
+
{ status: error.statusCode }
|
|
324
|
+
);
|
|
325
|
+
}
|
|
326
|
+
this.logger.error("Unexpected error in VercelReceiver", error);
|
|
327
|
+
return new Response(
|
|
328
|
+
JSON.stringify({
|
|
329
|
+
error: "Internal server error",
|
|
330
|
+
type: "UnexpectedError"
|
|
331
|
+
}),
|
|
332
|
+
{ status: 500 }
|
|
333
|
+
);
|
|
334
|
+
}
|
|
335
|
+
createScopedLogger(logger, logLevel) {
|
|
336
|
+
const prefix = SCOPE.map((s) => `[${s}]`).join(" ");
|
|
337
|
+
logger.setLevel(logLevel);
|
|
338
|
+
return __spreadProps(__spreadValues({}, logger), {
|
|
339
|
+
error: (...args) => {
|
|
340
|
+
var _a;
|
|
341
|
+
return (_a = logger.error) == null ? void 0 : _a.call(logger, prefix, ...args);
|
|
342
|
+
},
|
|
343
|
+
warn: (...args) => {
|
|
344
|
+
var _a;
|
|
345
|
+
return (_a = logger.warn) == null ? void 0 : _a.call(logger, prefix, ...args);
|
|
346
|
+
},
|
|
347
|
+
info: (...args) => {
|
|
348
|
+
var _a;
|
|
349
|
+
return (_a = logger.info) == null ? void 0 : _a.call(logger, prefix, ...args);
|
|
350
|
+
},
|
|
351
|
+
debug: (...args) => {
|
|
352
|
+
var _a;
|
|
353
|
+
return (_a = logger.debug) == null ? void 0 : _a.call(logger, prefix, ...args);
|
|
354
|
+
},
|
|
355
|
+
setLevel: logger.setLevel,
|
|
356
|
+
getLevel: logger.getLevel
|
|
357
|
+
});
|
|
358
|
+
}
|
|
359
|
+
};
|
|
360
|
+
function createHandler(app, receiver) {
|
|
361
|
+
let initPromise = null;
|
|
362
|
+
return (req) => __async(null, null, function* () {
|
|
363
|
+
try {
|
|
364
|
+
if (!initPromise) {
|
|
365
|
+
initPromise = app.init();
|
|
366
|
+
}
|
|
367
|
+
yield initPromise;
|
|
368
|
+
receiver.init(app);
|
|
369
|
+
const handler = yield receiver.start();
|
|
370
|
+
return handler(req);
|
|
371
|
+
} catch (error) {
|
|
372
|
+
const logger = receiver.getLogger();
|
|
373
|
+
logger.error("Error in createHandler:", error);
|
|
374
|
+
return new Response(
|
|
375
|
+
JSON.stringify({
|
|
376
|
+
error: "Internal Server Error",
|
|
377
|
+
type: "HandlerError"
|
|
378
|
+
}),
|
|
379
|
+
{ status: 500 }
|
|
380
|
+
);
|
|
381
|
+
}
|
|
382
|
+
});
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
export { VercelReceiver, createHandler };
|
|
386
|
+
//# sourceMappingURL=index.mjs.map
|
|
387
|
+
//# sourceMappingURL=index.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/errors.ts","../src/index.ts"],"names":["event","body"],"mappings":"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","file":"index.mjs","sourcesContent":["export class VercelReceiverError extends Error {\n constructor(\n message: string,\n public readonly statusCode: number = 500,\n ) {\n super(message);\n this.name = \"VercelReceiverError\";\n }\n}\n\nexport class SignatureVerificationError extends VercelReceiverError {\n constructor(message: string = \"Invalid request signature\") {\n super(message, 401);\n this.name = \"SignatureVerificationError\";\n }\n}\n\nexport class RequestParsingError extends VercelReceiverError {\n constructor(message: string = \"Failed to parse request\") {\n super(message, 400);\n this.name = \"RequestParsingError\";\n }\n}\n","import {\n VercelReceiverError,\n RequestParsingError,\n SignatureVerificationError,\n} from \"./errors\";\nimport { waitUntil } from \"@vercel/functions\";\n\nimport {\n verifySlackRequest,\n type AckFn,\n type App,\n type Receiver,\n type ReceiverEvent,\n type StringIndexed,\n} from \"@slack/bolt\";\nimport { ConsoleLogger, type Logger, LogLevel } from \"@slack/logger\";\n\n// Types\n/**\n * A function to handle the request from the Slack app.\n * @param req - The request from the Slack app.\n * @returns A response object.\n */\nexport type VercelHandler = (req: Request) => Promise<Response>;\n\n/**\n * Configuration options for the VercelReceiver.\n * @property signingSecret - The signing secret for the Slack app.\n * @property signatureVerification - If true, verifies the Slack request signature.\n * @property logger - The logger to use for the VercelReceiver.\n * @property logLevel - The log level to use for the VercelReceiver.\n * @property customPropertiesExtractor - A function to extract custom properties from the request.\n * @property customResponseHandler - A function to handle the response from the Slack app.\n */\nexport interface VercelReceiverOptions {\n /**\n * The signing secret for the Slack app.\n * @default process.env.SLACK_SIGNING_SECRET\n */\n signingSecret?: string;\n /**\n * If true, verifies the Slack request signature.\n * @default true\n */\n signatureVerification?: boolean;\n /**\n * The logger to use for the VercelReceiver.\n * @default new ConsoleLogger()\n */\n logger?: Logger;\n /**\n * The log level to use for the VercelReceiver.\n * @default LogLevel.INFO\n */\n logLevel?: LogLevel;\n /**\n * A function to extract custom properties from incoming events.\n * @default undefined\n * @returns An object with custom properties.\n */\n customPropertiesExtractor?: (req: Request) => StringIndexed;\n /**\n * A function to handle the response from the Slack app.\n * @default undefined\n * @returns A response object.\n */\n customResponseHandler?: (event: ReceiverEvent) => Promise<Response>;\n}\n\nconst SCOPE = [\"@vercel/slack-bolt\"];\nconst ACK_TIMEOUT_MS = 3001;\nconst SLACK_RETRY_NUM_HEADER = \"x-slack-retry-num\";\nconst SLACK_RETRY_REASON_HEADER = \"x-slack-retry-reason\";\nconst SLACK_TIMESTAMP_HEADER = \"x-slack-request-timestamp\";\nconst SLACK_SIGNATURE_HEADER = \"x-slack-signature\";\n\n/**\n * A Slack Bolt receiver implementation designed for Vercel's serverless environment.\n * Handles Slack events, interactions, and slash commands with automatic request verification,\n * background processing, and timeout management.\n *\n * @example\n * ```typescript\n * import { App } from '@slack/bolt';\n * import { VercelReceiver, createHandler } from '@vercel/slack-bolt';\n *\n * const receiver = new VercelReceiver();\n *\n * const app = new App({\n * receiver,\n * token: process.env.SLACK_BOT_TOKEN,\n * signingSecret: process.env.SLACK_SIGNING_SECRET,\n * });\n * ```\n *\n */\nexport class VercelReceiver implements Receiver {\n private readonly signingSecret: string;\n private readonly signatureVerification: boolean;\n private readonly logger: Logger;\n private readonly customPropertiesExtractor?: (req: Request) => StringIndexed;\n private readonly customResponseHandler?: (\n event: ReceiverEvent\n ) => Promise<Response>;\n private app?: App;\n\n /**\n * Gets the logger instance used by this receiver.\n * @returns The logger instance\n */\n public getLogger(): Logger {\n return this.logger;\n }\n\n /**\n * Creates a new VercelReceiver instance.\n *\n * @param options - Configuration options for the receiver\n * @throws {VercelReceiverError} When signing secret is not provided\n *\n * @example\n * ```typescript\n * const receiver = new VercelReceiver();\n * ```\n */\n public constructor({\n signingSecret = process.env.SLACK_SIGNING_SECRET,\n signatureVerification = true,\n logger,\n logLevel = LogLevel.INFO,\n customPropertiesExtractor,\n customResponseHandler,\n }: VercelReceiverOptions = {}) {\n if (!signingSecret) {\n throw new VercelReceiverError(\n \"SLACK_SIGNING_SECRET is required for VercelReceiver\"\n );\n }\n\n this.signingSecret = signingSecret;\n this.signatureVerification = signatureVerification;\n this.logger = this.createScopedLogger(\n logger ?? new ConsoleLogger(),\n logLevel\n );\n this.customPropertiesExtractor = customPropertiesExtractor;\n this.customResponseHandler = customResponseHandler;\n\n this.logger.debug(\"VercelReceiver initialized\");\n }\n\n /**\n * Initializes the receiver with a Slack Bolt app instance.\n * This method is called automatically by the Bolt framework.\n *\n * @param app - The Slack Bolt app instance\n */\n public init(app: App): void {\n this.app = app;\n this.logger.debug(\"App initialized in VercelReceiver\");\n }\n\n /**\n * Starts the receiver and returns a handler function for processing requests.\n * This method is called automatically by the Bolt framework.\n *\n * @returns A handler function that processes incoming Slack requests\n */\n public async start(): Promise<VercelHandler> {\n this.logger.debug(\"VercelReceiver started\");\n return this.toHandler();\n }\n\n /**\n * Stops the receiver. This method is called automatically by the Bolt framework.\n */\n public async stop(): Promise<void> {\n this.logger.debug(\"VercelReceiver stopped\");\n }\n\n /**\n * Creates a handler function that processes incoming Slack requests.\n * This is the main entry point for handling Slack events in Vercel.\n * It is called automatically by the Bolt framework in the start() method.\n *\n * @returns A handler function compatible with Vercel's function signature\n */\n public toHandler(): VercelHandler {\n return async (req: Request): Promise<Response> => {\n try {\n if (!this.app) {\n throw new VercelReceiverError(\"Slack app not initialized\", 500);\n }\n\n const rawBody = await req.text();\n\n if (this.signatureVerification) {\n await this.verifySlackRequest(req, rawBody);\n }\n\n const body = await this.parseRequestBody(req, rawBody);\n\n if (body.type === \"url_verification\") {\n this.logger.debug(\"Handling URL verification challenge\");\n return Response.json({ challenge: body.challenge });\n }\n\n return await this.handleSlackEvent(req, body);\n } catch (error) {\n return this.handleError(error);\n }\n };\n }\n\n private async parseRequestBody(\n req: Request,\n rawBody: string\n ): Promise<StringIndexed> {\n const contentType = req.headers.get(\"content-type\");\n\n try {\n if (contentType === \"application/x-www-form-urlencoded\") {\n const parsedBody: StringIndexed = {};\n const params = new URLSearchParams(rawBody);\n\n for (const [key, value] of params.entries()) {\n parsedBody[key] = value;\n }\n\n if (typeof parsedBody.payload === \"string\") {\n return JSON.parse(parsedBody.payload);\n }\n return parsedBody;\n }\n if (contentType === \"application/json\") {\n return JSON.parse(rawBody);\n }\n\n this.logger.warn(`Unexpected content-type detected: ${contentType}`);\n\n return JSON.parse(rawBody);\n } catch (e) {\n throw new RequestParsingError(\n `Failed to parse body as JSON data for content-type: ${contentType}. Error: ${\n e instanceof Error ? e.message : String(e)\n }`\n );\n }\n }\n\n private async handleSlackEvent(\n req: Request,\n body: StringIndexed\n ): Promise<Response> {\n if (!this.app) {\n throw new VercelReceiverError(\"App not initialized\", 500);\n }\n\n let isAcknowledged = false;\n let responseResolver: (value: Response) => void;\n let responseRejecter: (error: Error) => void;\n\n const responsePromise = new Promise<Response>((resolve, reject) => {\n responseResolver = resolve;\n responseRejecter = reject;\n });\n\n // Slack requires an acknowledgment from your app within 3 seconds\n const timeoutId = setTimeout(() => {\n if (!isAcknowledged) {\n this.logger.error(\"Event not acknowledged within timeout period\");\n const error = new VercelReceiverError(\"Request timeout\", 408);\n responseRejecter(error);\n }\n }, ACK_TIMEOUT_MS);\n\n // Create an acknowledgment function to handle ack() calls from Bolt while waiting for the event to be processed\n const ackFn: AckFn<StringIndexed> = async (ackResponse) => {\n if (isAcknowledged) {\n throw new Error(\"Cannot acknowledge an event multiple times\");\n }\n\n isAcknowledged = true;\n clearTimeout(timeoutId);\n\n try {\n let response: Response;\n\n if (this.customResponseHandler) {\n const event = this.createSlackReceiverEvent({\n body,\n headers: req.headers,\n ack: ackFn,\n request: req,\n });\n response = await this.customResponseHandler(event);\n } else {\n const responseBody = ackResponse || null;\n const body =\n typeof responseBody === \"string\"\n ? responseBody\n : JSON.stringify(responseBody);\n response = new Response(body, {\n status: 200,\n headers: {\n \"Content-Type\": \"application/json\",\n },\n });\n }\n\n responseResolver(response);\n } catch (error) {\n this.logger.error(\"Error in acknowledgment handler\", error);\n responseRejecter(\n error instanceof Error ? error : new Error(String(error))\n );\n }\n };\n\n const event = this.createSlackReceiverEvent({\n body,\n headers: req.headers,\n ack: ackFn,\n request: req,\n });\n\n // Process event in background using waitUntil from Vercel Functions\n // https://vercel.com/docs/functions/functions-api-reference/vercel-functions-package#waituntil\n waitUntil(this.app.processEvent(event));\n\n try {\n return await responsePromise;\n } catch (error) {\n return this.handleError(error);\n }\n }\n\n private async verifySlackRequest(\n req: Request,\n rawBody: string\n ): Promise<void> {\n const timestamp = req.headers.get(SLACK_TIMESTAMP_HEADER);\n const signature = req.headers.get(SLACK_SIGNATURE_HEADER);\n\n if (!timestamp) {\n throw new SignatureVerificationError(\"Missing required timestamp header\");\n }\n\n if (!signature) {\n throw new SignatureVerificationError(\n \"Missing required signature headers\"\n );\n }\n\n try {\n verifySlackRequest({\n signingSecret: this.signingSecret,\n body: rawBody,\n headers: {\n \"x-slack-signature\": signature,\n \"x-slack-request-timestamp\": Number.parseInt(timestamp, 10),\n },\n logger: this.logger,\n });\n } catch (error) {\n this.logger.error(\"Slack request verification failed\", error);\n throw new SignatureVerificationError(\n error instanceof Error ? error.message : \"Signature verification failed\"\n );\n }\n }\n\n private createSlackReceiverEvent({\n body,\n headers,\n ack,\n request,\n }: {\n body: StringIndexed;\n headers: Headers;\n ack: AckFn<StringIndexed>;\n request: Request;\n }): ReceiverEvent {\n const customProperties = this.customPropertiesExtractor\n ? this.customPropertiesExtractor(request)\n : {};\n\n const retryNum = headers.get(SLACK_RETRY_NUM_HEADER) || \"0\";\n\n const retryReason = headers.get(SLACK_RETRY_REASON_HEADER) || \"\";\n\n return {\n body,\n ack,\n retryNum: Number(retryNum),\n retryReason,\n customProperties,\n };\n }\n\n private handleError(error: unknown): Response {\n if (error instanceof VercelReceiverError) {\n this.logger.error(`VercelReceiverError: ${error.message}`, {\n statusCode: error.statusCode,\n name: error.name,\n });\n return new Response(\n JSON.stringify({\n error: error.message,\n type: error.name,\n }),\n { status: error.statusCode }\n );\n }\n\n this.logger.error(\"Unexpected error in VercelReceiver\", error);\n return new Response(\n JSON.stringify({\n error: \"Internal server error\",\n type: \"UnexpectedError\",\n }),\n { status: 500 }\n );\n }\n\n private createScopedLogger(logger: Logger, logLevel: LogLevel): Logger {\n const prefix = SCOPE.map((s) => `[${s}]`).join(\" \");\n logger.setLevel(logLevel);\n\n return {\n ...logger,\n error: (...args) => logger.error?.(prefix, ...args),\n warn: (...args) => logger.warn?.(prefix, ...args),\n info: (...args) => logger.info?.(prefix, ...args),\n debug: (...args) => logger.debug?.(prefix, ...args),\n setLevel: logger.setLevel,\n getLevel: logger.getLevel,\n };\n }\n}\n\n/**\n * Creates a Vercel-compatible handler function for a Slack Bolt app.\n * This is the recommended way to create handlers for deployment on Vercel.\n *\n * @param {App} app - The initialized Slack Bolt app instance.\n * @param {VercelReceiver} receiver - The VercelReceiver instance.\n * @returns {VercelHandler} A handler function compatible with Vercel's function signature.\n *\n * @example\n * ```typescript\n * // api/events.ts\n * import { createHandler } from '@vercel/slack-bolt';\n * import { app, receiver } from '../app';\n *\n * const handler = createHandler(app, receiver);\n *\n * export const POST = async (req: Request) => {\n * return handler(req);\n * };\n * ```\n *\n * @throws {Error} If app initialization fails.\n * @throws {VercelReceiverError} If request processing fails.\n */\nexport function createHandler(\n app: App,\n receiver: VercelReceiver\n): VercelHandler {\n let initPromise: Promise<void> | null = null;\n\n return async (req: Request) => {\n try {\n if (!initPromise) {\n initPromise = app.init();\n }\n await initPromise;\n\n receiver.init(app);\n const handler = await receiver.start();\n return handler(req);\n } catch (error) {\n const logger = receiver.getLogger();\n logger.error(\"Error in createHandler:\", error);\n return new Response(\n JSON.stringify({\n error: \"Internal Server Error\",\n type: \"HandlerError\",\n }),\n { status: 500 }\n );\n }\n };\n}\n"]}
|
package/package.json
ADDED
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@vercel/slack-bolt",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "A Vercel receiver for building Slack apps with Bolt and deploying them to Vercel",
|
|
5
|
+
"main": "./dist/index.js",
|
|
6
|
+
"types": "./dist/index.d.ts",
|
|
7
|
+
"type": "commonjs",
|
|
8
|
+
"exports": {
|
|
9
|
+
".": {
|
|
10
|
+
"types": {
|
|
11
|
+
"import": "./dist/index.d.mts",
|
|
12
|
+
"require": "./dist/index.d.ts"
|
|
13
|
+
},
|
|
14
|
+
"import": "./dist/index.mjs",
|
|
15
|
+
"require": "./dist/index.js"
|
|
16
|
+
}
|
|
17
|
+
},
|
|
18
|
+
"files": [
|
|
19
|
+
"dist"
|
|
20
|
+
],
|
|
21
|
+
"repository": {
|
|
22
|
+
"type": "git",
|
|
23
|
+
"url": "https://github.com/vercel/vercel-bolt.git"
|
|
24
|
+
},
|
|
25
|
+
"bugs": {
|
|
26
|
+
"url": "https://github.com/vercel/vercel-bolt/issues"
|
|
27
|
+
},
|
|
28
|
+
"keywords": [
|
|
29
|
+
"vercel",
|
|
30
|
+
"bolt",
|
|
31
|
+
"slack"
|
|
32
|
+
],
|
|
33
|
+
"author": "Vercel",
|
|
34
|
+
"license": "MIT",
|
|
35
|
+
"dependencies": {
|
|
36
|
+
"@slack/logger": "^4.0.0",
|
|
37
|
+
"@vercel/functions": "^2.2.7",
|
|
38
|
+
"tsscmp": "^1.0.6"
|
|
39
|
+
},
|
|
40
|
+
"devDependencies": {
|
|
41
|
+
"@biomejs/biome": "2.1.3",
|
|
42
|
+
"@changesets/cli": "^2.29.5",
|
|
43
|
+
"@types/node": "^20.19.9",
|
|
44
|
+
"@types/tsscmp": "^1.0.2",
|
|
45
|
+
"tsup": "^8.5.0",
|
|
46
|
+
"typescript": "^5.9.2",
|
|
47
|
+
"vitest": "^3.2.4"
|
|
48
|
+
},
|
|
49
|
+
"peerDependencies": {
|
|
50
|
+
"@slack/bolt": "^4.4.0"
|
|
51
|
+
},
|
|
52
|
+
"peerDependenciesMeta": {
|
|
53
|
+
"@slack/bolt": {
|
|
54
|
+
"optional": false
|
|
55
|
+
}
|
|
56
|
+
},
|
|
57
|
+
"publishConfig": {
|
|
58
|
+
"access": "public"
|
|
59
|
+
},
|
|
60
|
+
"scripts": {
|
|
61
|
+
"build": "tsup",
|
|
62
|
+
"dev": "tsup --watch",
|
|
63
|
+
"test": "vitest run",
|
|
64
|
+
"test:watch": "vitest watch",
|
|
65
|
+
"version-packages": "changeset version",
|
|
66
|
+
"changeset": "changeset",
|
|
67
|
+
"release": "pnpm build && changeset publish"
|
|
68
|
+
}
|
|
69
|
+
}
|