@sentry/junior 0.9.4 → 0.10.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.
@@ -4,10 +4,6 @@ import {
4
4
  withSpan
5
5
  } from "./chunk-MY7JNCS2.js";
6
6
 
7
- // src/chat/state/adapter.ts
8
- import { createMemoryState } from "@chat-adapter/state-memory";
9
- import { createRedisState } from "@chat-adapter/state-redis";
10
-
11
7
  // src/chat/optional-string.ts
12
8
  function toOptionalTrimmed(value) {
13
9
  if (!value) {
@@ -20,8 +16,8 @@ function toOptionalTrimmed(value) {
20
16
  // src/chat/config.ts
21
17
  var MIN_AGENT_TURN_TIMEOUT_MS = 10 * 1e3;
22
18
  var DEFAULT_AGENT_TURN_TIMEOUT_MS = 12 * 60 * 1e3;
23
- var DEFAULT_QUEUE_CALLBACK_MAX_DURATION_SECONDS = 800;
24
- var TURN_TIMEOUT_BUFFER_SECONDS = 20;
19
+ var DEFAULT_FUNCTION_MAX_DURATION_SECONDS = 800;
20
+ var FUNCTION_TIMEOUT_BUFFER_SECONDS = 20;
25
21
  function parseAgentTurnTimeoutMs(rawValue, maxTimeoutMs) {
26
22
  const value = Number.parseInt(rawValue ?? "", 10);
27
23
  if (Number.isNaN(value)) {
@@ -32,25 +28,21 @@ function parseAgentTurnTimeoutMs(rawValue, maxTimeoutMs) {
32
28
  }
33
29
  return Math.max(MIN_AGENT_TURN_TIMEOUT_MS, Math.min(value, maxTimeoutMs));
34
30
  }
35
- function resolveQueueCallbackMaxDurationSeconds(env) {
36
- const value = Number.parseInt(
37
- env.QUEUE_CALLBACK_MAX_DURATION_SECONDS ?? "",
38
- 10
39
- );
31
+ function resolveFunctionMaxDurationSeconds(env) {
32
+ const raw = env.FUNCTION_MAX_DURATION_SECONDS ?? env.QUEUE_CALLBACK_MAX_DURATION_SECONDS;
33
+ const value = Number.parseInt(raw ?? "", 10);
40
34
  if (Number.isNaN(value) || value <= 0) {
41
- return DEFAULT_QUEUE_CALLBACK_MAX_DURATION_SECONDS;
35
+ return DEFAULT_FUNCTION_MAX_DURATION_SECONDS;
42
36
  }
43
37
  return value;
44
38
  }
45
- function resolveMaxTurnTimeoutMs(queueCallbackMaxDurationSeconds) {
46
- const budgetSeconds = queueCallbackMaxDurationSeconds - TURN_TIMEOUT_BUFFER_SECONDS;
39
+ function resolveMaxTurnTimeoutMs(functionMaxDurationSeconds) {
40
+ const budgetSeconds = functionMaxDurationSeconds - FUNCTION_TIMEOUT_BUFFER_SECONDS;
47
41
  return Math.max(MIN_AGENT_TURN_TIMEOUT_MS, budgetSeconds * 1e3);
48
42
  }
49
43
  function readBotConfig(env) {
50
- const queueCallbackMaxDurationSeconds = resolveQueueCallbackMaxDurationSeconds(env);
51
- const maxTurnTimeoutMs = resolveMaxTurnTimeoutMs(
52
- queueCallbackMaxDurationSeconds
53
- );
44
+ const functionMaxDurationSeconds = resolveFunctionMaxDurationSeconds(env);
45
+ const maxTurnTimeoutMs = resolveMaxTurnTimeoutMs(functionMaxDurationSeconds);
54
46
  return {
55
47
  userName: env.JUNIOR_BOT_NAME ?? "junior",
56
48
  modelId: env.AI_MODEL ?? "anthropic/claude-sonnet-4.6",
@@ -64,9 +56,7 @@ function readBotConfig(env) {
64
56
  function readChatConfig(env = process.env) {
65
57
  return {
66
58
  bot: readBotConfig(env),
67
- queue: {
68
- callbackMaxDurationSeconds: resolveQueueCallbackMaxDurationSeconds(env)
69
- },
59
+ functionMaxDurationSeconds: resolveFunctionMaxDurationSeconds(env),
70
60
  slack: {
71
61
  botToken: toOptionalTrimmed(env.SLACK_BOT_TOKEN) ?? toOptionalTrimmed(env.SLACK_BOT_USER_TOKEN),
72
62
  signingSecret: toOptionalTrimmed(env.SLACK_SIGNING_SECRET),
@@ -103,6 +93,8 @@ function getRuntimeMetadata() {
103
93
  }
104
94
 
105
95
  // src/chat/state/adapter.ts
96
+ import { createMemoryState } from "@chat-adapter/state-memory";
97
+ import { createRedisState } from "@chat-adapter/state-redis";
106
98
  var MIN_LOCK_TTL_MS = 1e3 * 60 * 5;
107
99
  var stateAdapter;
108
100
  var redisStateAdapter;
@@ -122,6 +114,9 @@ function createQueuedStateAdapter(base) {
122
114
  releaseLock: (lock) => base.releaseLock(lock),
123
115
  extendLock: (lock, ttlMs) => base.extendLock(lock, Math.max(ttlMs, MIN_LOCK_TTL_MS)),
124
116
  forceReleaseLock: (threadId) => base.forceReleaseLock(threadId),
117
+ enqueue: (threadId, entry, maxSize) => base.enqueue(threadId, entry, maxSize),
118
+ dequeue: (threadId) => base.dequeue(threadId),
119
+ queueDepth: (threadId) => base.queueDepth(threadId),
125
120
  get: (key) => base.get(key),
126
121
  getList: (key) => base.getList(key),
127
122
  set: (key, value, ttlMs) => base.set(key, value, ttlMs),
@@ -144,18 +139,6 @@ function createStateAdapter() {
144
139
  redisStateAdapter = redisState;
145
140
  return createQueuedStateAdapter(redisState);
146
141
  }
147
- function getOptionalRedisStateAdapter() {
148
- getStateAdapter();
149
- return redisStateAdapter;
150
- }
151
- async function getConnectedStateContext() {
152
- const adapter = getStateAdapter();
153
- await adapter.connect();
154
- return {
155
- redisStateAdapter: getOptionalRedisStateAdapter(),
156
- stateAdapter: adapter
157
- };
158
- }
159
142
  function getStateAdapter() {
160
143
  if (!stateAdapter) {
161
144
  stateAdapter = createStateAdapter();
@@ -806,13 +789,13 @@ function isSnapshotMissingError(error) {
806
789
 
807
790
  export {
808
791
  toOptionalTrimmed,
792
+ getChatConfig,
809
793
  botConfig,
810
794
  getSlackBotToken,
811
795
  getSlackSigningSecret,
812
796
  getSlackClientId,
813
797
  getSlackClientSecret,
814
798
  getRuntimeMetadata,
815
- getConnectedStateContext,
816
799
  getStateAdapter,
817
800
  disconnectStateAdapter,
818
801
  SANDBOX_WORKSPACE_ROOT,
package/dist/cli/init.js CHANGED
@@ -2,19 +2,19 @@
2
2
  import fs from "fs";
3
3
  import path from "path";
4
4
  function writeRouteModule(filePath, exportLine) {
5
- fs.writeFileSync(filePath, `${exportLine}
5
+ fs.writeFileSync(
6
+ filePath,
7
+ `${exportLine}
6
8
  export const runtime = "nodejs";
7
- `);
9
+ `
10
+ );
8
11
  }
9
12
  function writeWrapperFiles(targetDir) {
10
13
  const routeDir = path.join(targetDir, "app", "api", "[...path]");
11
14
  fs.mkdirSync(routeDir, { recursive: true });
12
- writeRouteModule(path.join(routeDir, "route.js"), 'export { GET, POST } from "@sentry/junior/handler";');
13
- const queueRouteDir = path.join(targetDir, "app", "api", "queue", "callback");
14
- fs.mkdirSync(queueRouteDir, { recursive: true });
15
15
  writeRouteModule(
16
- path.join(queueRouteDir, "route.js"),
17
- 'export { POST } from "@sentry/junior/handlers/queue-callback";'
16
+ path.join(routeDir, "route.js"),
17
+ 'export { GET, POST } from "@sentry/junior/handler";'
18
18
  );
19
19
  fs.mkdirSync(path.join(targetDir, "app"), { recursive: true });
20
20
  fs.writeFileSync(
@@ -66,14 +66,20 @@ async function runInit(dir, log = console.log) {
66
66
  "@sentry/nextjs": "^10.0.0"
67
67
  }
68
68
  };
69
- fs.writeFileSync(path.join(target, "package.json"), `${JSON.stringify(pkg, null, 2)}
70
- `);
69
+ fs.writeFileSync(
70
+ path.join(target, "package.json"),
71
+ `${JSON.stringify(pkg, null, 2)}
72
+ `
73
+ );
71
74
  const dataDir = path.join(target, "app", "data");
72
75
  fs.mkdirSync(dataDir, { recursive: true });
73
- fs.writeFileSync(path.join(dataDir, "SOUL.md"), `# ${name}
76
+ fs.writeFileSync(
77
+ path.join(dataDir, "SOUL.md"),
78
+ `# ${name}
74
79
 
75
80
  You are ${name}, a helpful assistant.
76
- `);
81
+ `
82
+ );
77
83
  fs.writeFileSync(
78
84
  path.join(dataDir, "ABOUT.md"),
79
85
  `# About ${name}
@@ -87,7 +93,10 @@ Describe what ${name} helps users do.
87
93
  const pluginsDir = path.join(target, "app", "plugins");
88
94
  fs.mkdirSync(pluginsDir, { recursive: true });
89
95
  fs.writeFileSync(path.join(pluginsDir, ".gitkeep"), "");
90
- fs.writeFileSync(path.join(target, ".gitignore"), ["node_modules/", ".next/", ".env", ".env.local", ""].join("\n"));
96
+ fs.writeFileSync(
97
+ path.join(target, ".gitignore"),
98
+ ["node_modules/", ".next/", ".env", ".env.local", ""].join("\n")
99
+ );
91
100
  fs.writeFileSync(
92
101
  path.join(target, ".env.example"),
93
102
  [
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  disconnectStateAdapter,
3
3
  resolveRuntimeDependencySnapshot
4
- } from "../chunk-FS5Y4CF2.js";
4
+ } from "../chunk-BJ4EBVQK.js";
5
5
  import {
6
6
  getPluginProviders,
7
7
  getPluginRuntimeDependencies,
@@ -1,3 +1,5 @@
1
+ export { maxDuration } from './webhooks.js';
2
+
1
3
  type RouteContext = {
2
4
  params: Promise<unknown>;
3
5
  };
@@ -15,10 +17,6 @@ declare function GET(request: Request, context: RouteContext): Promise<Response>
15
17
  *
16
18
  * Supported routes:
17
19
  * - `api/webhooks/:platform`
18
- * - `api/queue/callback`
19
- *
20
- * `queue/callback` is routed here for local/dev parity, but production queue triggers
21
- * should still target the dedicated `app/api/queue/callback/route.ts` endpoint.
22
20
  */
23
21
  declare function POST(request: Request, context: RouteContext): Promise<Response>;
24
22
 
@@ -1,10 +1,5 @@
1
1
  import {
2
- POST as POST2
3
- } from "../chunk-SO4ORZJR.js";
4
- import {
5
- POST
6
- } from "../chunk-YGERYE7Y.js";
7
- import {
2
+ POST,
8
3
  buildConversationContext,
9
4
  buildSlackOutputMessage,
10
5
  coerceThreadArtifactsState,
@@ -16,14 +11,16 @@ import {
16
11
  formatProviderLabel,
17
12
  generateAssistantReply,
18
13
  generateConversationId,
14
+ getPersistedThreadState,
19
15
  getSlackClient,
20
16
  isRetryableTurnError,
21
17
  markConversationMessage,
22
18
  markTurnCompleted,
23
19
  markTurnFailed,
20
+ maxDuration,
24
21
  mergeArtifactsState,
25
22
  normalizeConversationText,
26
- persistThreadState,
23
+ persistThreadStateById,
27
24
  publishAppHomeView,
28
25
  resolveBaseUrl,
29
26
  resolveReplyDelivery,
@@ -31,15 +28,15 @@ import {
31
28
  updateConversationStats,
32
29
  uploadFilesToThread,
33
30
  upsertConversationMessage
34
- } from "../chunk-FLP5I4NM.js";
35
- import {
36
- botConfig,
37
- getStateAdapter
38
- } from "../chunk-FS5Y4CF2.js";
31
+ } from "../chunk-7RM2Y52T.js";
39
32
  import {
40
33
  GET
41
34
  } from "../chunk-4RBEYCOG.js";
42
35
  import "../chunk-WM66QDLA.js";
36
+ import {
37
+ botConfig,
38
+ getStateAdapter
39
+ } from "../chunk-BJ4EBVQK.js";
43
40
  import {
44
41
  buildOAuthTokenRequest,
45
42
  getPluginOAuthConfig,
@@ -53,7 +50,6 @@ import "../chunk-KCLEEKYX.js";
53
50
  // src/handlers/mcp-oauth-callback.ts
54
51
  import { Buffer } from "buffer";
55
52
  import { after } from "next/server";
56
- import { ThreadImpl } from "chat";
57
53
 
58
54
  // src/handlers/oauth-resume.ts
59
55
  function resolveReplyTimeoutMs(explicitTimeoutMs) {
@@ -328,15 +324,6 @@ async function deliverReplyToThread(channelId, threadTs, reply) {
328
324
  } catch {
329
325
  }
330
326
  }
331
- function createSlackThread(channelId, threadTs) {
332
- return ThreadImpl.fromJSON({
333
- _type: "chat:Thread",
334
- adapterName: "slack",
335
- channelId,
336
- id: `slack:${channelId}:${threadTs}`,
337
- isDM: channelId.startsWith("D")
338
- });
339
- }
340
327
  function buildDeterministicTurnId(messageId) {
341
328
  const sanitized = messageId.replace(/[^a-zA-Z0-9_-]/g, "_");
342
329
  return `turn_${sanitized}`;
@@ -354,16 +341,18 @@ function getUserMessageIdForTurn(conversation, sessionId) {
354
341
  return void 0;
355
342
  }
356
343
  async function buildResumeConversationContext(channelId, threadTs, sessionId) {
357
- const thread = createSlackThread(channelId, threadTs);
358
- const conversation = coerceThreadConversationState(await thread.state);
344
+ const threadId = `slack:${channelId}:${threadTs}`;
345
+ const conversation = coerceThreadConversationState(
346
+ await getPersistedThreadState(threadId)
347
+ );
359
348
  const userMessageId = getUserMessageIdForTurn(conversation, sessionId);
360
349
  return buildConversationContext(conversation, {
361
350
  excludeMessageId: userMessageId
362
351
  });
363
352
  }
364
353
  async function persistCompletedReplyState(channelId, threadTs, sessionId, reply) {
365
- const thread = createSlackThread(channelId, threadTs);
366
- const currentState = await thread.state;
354
+ const threadId = `slack:${channelId}:${threadTs}`;
355
+ const currentState = await getPersistedThreadState(threadId);
367
356
  const conversation = coerceThreadConversationState(currentState);
368
357
  const artifacts = coerceThreadArtifactsState(currentState);
369
358
  const nextArtifacts = reply.artifactStatePatch ? mergeArtifactsState(artifacts, reply.artifactStatePatch) : void 0;
@@ -390,7 +379,7 @@ async function persistCompletedReplyState(channelId, threadTs, sessionId, reply)
390
379
  nowMs: Date.now(),
391
380
  updateConversationStats
392
381
  });
393
- await persistThreadState(thread, {
382
+ await persistThreadStateById(threadId, {
394
383
  artifacts: nextArtifacts,
395
384
  conversation,
396
385
  sandboxId: reply.sandboxId,
@@ -398,8 +387,8 @@ async function persistCompletedReplyState(channelId, threadTs, sessionId, reply)
398
387
  });
399
388
  }
400
389
  async function persistFailedReplyState(channelId, threadTs, sessionId) {
401
- const thread = createSlackThread(channelId, threadTs);
402
- const currentState = await thread.state;
390
+ const threadId = `slack:${channelId}:${threadTs}`;
391
+ const currentState = await getPersistedThreadState(threadId);
403
392
  const conversation = coerceThreadConversationState(currentState);
404
393
  markTurnFailed({
405
394
  conversation,
@@ -408,7 +397,7 @@ async function persistFailedReplyState(channelId, threadTs, sessionId) {
408
397
  markConversationMessage,
409
398
  updateConversationStats
410
399
  });
411
- await persistThreadState(thread, {
400
+ await persistThreadStateById(threadId, {
412
401
  conversation
413
402
  });
414
403
  }
@@ -549,7 +538,6 @@ async function GET2(request, context) {
549
538
 
550
539
  // src/handlers/oauth-callback.ts
551
540
  import { after as after2 } from "next/server";
552
- import { ThreadImpl as ThreadImpl2 } from "chat";
553
541
  function htmlErrorResponse(title, message, status) {
554
542
  const safeTitle = escapeXml(title);
555
543
  const safeMessage = escapeXml(message);
@@ -569,18 +557,10 @@ function htmlErrorResponse(title, message, status) {
569
557
  headers: { "Content-Type": "text/html; charset=utf-8" }
570
558
  });
571
559
  }
572
- function createSlackThread2(channelId, threadTs) {
573
- return ThreadImpl2.fromJSON({
574
- _type: "chat:Thread",
575
- adapterName: "slack",
576
- channelId,
577
- id: `slack:${channelId}:${threadTs}`,
578
- isDM: channelId.startsWith("D")
579
- });
580
- }
581
560
  async function buildResumeConversationContext2(channelId, threadTs) {
582
- const thread = createSlackThread2(channelId, threadTs);
583
- const conversation = coerceThreadConversationState(await thread.state);
561
+ const conversation = coerceThreadConversationState(
562
+ await getPersistedThreadState(`slack:${channelId}:${threadTs}`)
563
+ );
584
564
  const latestUserMessageId = [...conversation.messages].reverse().find((message) => message.role === "user")?.id;
585
565
  return buildConversationContext(conversation, {
586
566
  excludeMessageId: latestUserMessageId
@@ -829,15 +809,12 @@ async function GET4(request, context) {
829
809
  }
830
810
  return new Response("Not Found", { status: 404 });
831
811
  }
832
- async function POST3(request, context) {
812
+ async function POST2(request, context) {
833
813
  const route = normalizeRoutePath(getRoutePathParts(await context.params));
834
- if (route === "queue/callback") {
835
- return POST(request);
836
- }
837
814
  const webhookMatch = route.match(/^webhooks\/([^/]+)$/);
838
815
  if (webhookMatch) {
839
816
  const platform = webhookMatch[1];
840
- return POST2(request, {
817
+ return POST(request, {
841
818
  params: Promise.resolve({ platform })
842
819
  });
843
820
  }
@@ -845,5 +822,6 @@ async function POST3(request, context) {
845
822
  }
846
823
  export {
847
824
  GET4 as GET,
848
- POST3 as POST
825
+ POST2 as POST,
826
+ maxDuration
849
827
  };
@@ -1,3 +1,10 @@
1
+ /**
2
+ * Vercel function timeout in seconds.
3
+ *
4
+ * Agent turns run inside `after()` which shares the function's timeout budget.
5
+ * This must be at least as large as the configured turn timeout plus buffer.
6
+ */
7
+ declare const maxDuration: number;
1
8
  /**
2
9
  * Webhook route contract for `@sentry/junior`.
3
10
  *
@@ -18,4 +25,4 @@ type WebhookRouteContext = {
18
25
  */
19
26
  declare function POST(request: Request, context: WebhookRouteContext): Promise<Response>;
20
27
 
21
- export { POST };
28
+ export { POST, maxDuration };
@@ -1,11 +1,12 @@
1
1
  import {
2
- POST
3
- } from "../chunk-SO4ORZJR.js";
4
- import "../chunk-FLP5I4NM.js";
5
- import "../chunk-FS5Y4CF2.js";
2
+ POST,
3
+ maxDuration
4
+ } from "../chunk-7RM2Y52T.js";
6
5
  import "../chunk-WM66QDLA.js";
6
+ import "../chunk-BJ4EBVQK.js";
7
7
  import "../chunk-MY7JNCS2.js";
8
8
  import "../chunk-KCLEEKYX.js";
9
9
  export {
10
- POST
10
+ POST,
11
+ maxDuration
11
12
  };
@@ -68,7 +68,6 @@ function applyJuniorConfig(nextConfig, options) {
68
68
  serverExternalPackages: Array.from(
69
69
  /* @__PURE__ */ new Set([
70
70
  ...nextConfig?.serverExternalPackages ?? [],
71
- "@vercel/queue",
72
71
  "@vercel/sandbox",
73
72
  "bash-tool",
74
73
  "just-bash",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sentry/junior",
3
- "version": "0.9.4",
3
+ "version": "0.10.0",
4
4
  "private": false,
5
5
  "publishConfig": {
6
6
  "access": "public"
@@ -12,7 +12,6 @@
12
12
  "exports": {
13
13
  "./handler": "./dist/handlers/router.js",
14
14
  "./handlers/webhooks": "./dist/handlers/webhooks.js",
15
- "./handlers/queue-callback": "./dist/handlers/queue-callback.js",
16
15
  "./handlers/health": "./dist/handlers/health.js",
17
16
  "./config": "./dist/next-config.js",
18
17
  "./instrumentation": "./dist/instrumentation.js",
@@ -23,23 +22,22 @@
23
22
  "bin"
24
23
  ],
25
24
  "dependencies": {
26
- "@ai-sdk/gateway": "^3.0.66",
27
- "@chat-adapter/slack": "4.20.2",
28
- "@chat-adapter/state-memory": "4.20.2",
29
- "@chat-adapter/state-redis": "4.20.2",
25
+ "@ai-sdk/gateway": "^3.0.80",
26
+ "@chat-adapter/slack": "4.22.0",
27
+ "@chat-adapter/state-memory": "4.22.0",
28
+ "@chat-adapter/state-redis": "4.22.0",
30
29
  "@mariozechner/pi-agent-core": "^0.59.0",
31
30
  "@mariozechner/pi-ai": "^0.59.0",
32
31
  "@modelcontextprotocol/sdk": "1.27.1",
33
32
  "@sinclair/typebox": "^0.34.48",
34
33
  "@slack/web-api": "^7.15.0",
35
- "@vercel/queue": "^0.1.4",
36
- "@vercel/sandbox": "^1.8.1",
37
- "ai": "^6.0.116",
34
+ "@vercel/sandbox": "^1.9.0",
35
+ "ai": "^6.0.138",
38
36
  "bash-tool": "^1.3.15",
39
- "chat": "4.20.2",
40
- "just-bash": "^2.13.1",
37
+ "chat": "4.22.0",
38
+ "just-bash": "^2.14.0",
41
39
  "node-html-markdown": "^2.0.0",
42
- "yaml": "^2.8.2",
40
+ "yaml": "^2.8.3",
43
41
  "zod": "^4.3.6"
44
42
  },
45
43
  "peerDependencies": {
@@ -49,18 +47,18 @@
49
47
  "react-dom": ">=19.0.0"
50
48
  },
51
49
  "devDependencies": {
52
- "@sentry/nextjs": "^10.44.0",
53
- "@types/node": "^25.3.5",
50
+ "@sentry/nextjs": "^10.46.0",
51
+ "@types/node": "^25.5.0",
54
52
  "@types/react": "^19.2.14",
55
53
  "@types/react-dom": "^19.2.3",
56
- "msw": "^2.12.13",
57
- "next": "^16.1.7",
54
+ "msw": "^2.12.14",
55
+ "next": "^16.2.1",
58
56
  "react": "^19.2.4",
59
57
  "react-dom": "^19.2.4",
60
58
  "tsup": "^8.5.1",
61
59
  "typescript": "^5.9.3",
62
- "vercel": "^50.32.5",
63
- "vitest": "^4.1.0"
60
+ "vercel": "^50.37.0",
61
+ "vitest": "^4.1.1"
64
62
  },
65
63
  "scripts": {
66
64
  "build": "tsup",
@@ -1,103 +0,0 @@
1
- import {
2
- getProductionBot
3
- } from "./chunk-FLP5I4NM.js";
4
- import {
5
- createRequestContext,
6
- logException,
7
- logWarn,
8
- setSpanAttributes,
9
- setSpanStatus,
10
- withContext,
11
- withSpan
12
- } from "./chunk-MY7JNCS2.js";
13
-
14
- // src/handlers/webhooks.ts
15
- import { after } from "next/server";
16
- import * as Sentry from "@sentry/nextjs";
17
- async function POST(request, context) {
18
- const bot = getProductionBot();
19
- const { platform } = await context.params;
20
- const handler = bot.webhooks[platform];
21
- const requestContext = createRequestContext(request, { platform });
22
- const requestUrl = new URL(request.url);
23
- return withContext(requestContext, async () => {
24
- if (!handler) {
25
- const error = new Error(`Unknown platform: ${platform}`);
26
- logException(
27
- error,
28
- "webhook_platform_unknown",
29
- {},
30
- {
31
- "http.response.status_code": 404
32
- },
33
- `Unknown platform: ${platform}`
34
- );
35
- return new Response(`Unknown platform: ${platform}`, { status: 404 });
36
- }
37
- try {
38
- return await withSpan(
39
- "http.server.request",
40
- "http.server",
41
- requestContext,
42
- async () => {
43
- try {
44
- const activeSpan = Sentry.getActiveSpan();
45
- const response = await handler(request, {
46
- waitUntil: (task) => after(() => {
47
- const runTask = () => {
48
- const taskOrFactory = task;
49
- return typeof taskOrFactory === "function" ? taskOrFactory() : taskOrFactory;
50
- };
51
- if (activeSpan) {
52
- return Sentry.withActiveSpan(activeSpan, runTask);
53
- }
54
- return runTask();
55
- })
56
- });
57
- if (response.status >= 400) {
58
- let responseBodySnippet;
59
- try {
60
- responseBodySnippet = (await response.clone().text()).slice(
61
- 0,
62
- 300
63
- );
64
- } catch {
65
- responseBodySnippet = void 0;
66
- }
67
- logWarn(
68
- "webhook_non_success_response",
69
- {},
70
- {
71
- "http.response.status_code": response.status,
72
- "http.request.header.x_slack_signature": request.headers.get("x-slack-signature") ?? void 0,
73
- "http.request.header.x_slack_request_timestamp": request.headers.get("x-slack-request-timestamp") ?? void 0,
74
- ...responseBodySnippet ? { "app.webhook.response_body": responseBodySnippet } : {}
75
- },
76
- `Webhook ${platform} returned ${response.status}`
77
- );
78
- }
79
- setSpanAttributes({
80
- "http.response.status_code": response.status
81
- });
82
- setSpanStatus(response.status >= 500 ? "error" : "ok");
83
- return response;
84
- } catch (error) {
85
- setSpanStatus("error");
86
- throw error;
87
- }
88
- },
89
- {
90
- "http.request.method": request.method,
91
- "url.path": requestUrl.pathname
92
- }
93
- );
94
- } catch (error) {
95
- logException(error, "webhook_handler_failed");
96
- throw error;
97
- }
98
- });
99
- }
100
-
101
- export {
102
- POST
103
- };