@sentry/junior 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.
@@ -0,0 +1,323 @@
1
+ import {
2
+ hasRedisConfig
3
+ } from "./chunk-GDNDYMGX.js";
4
+
5
+ // src/chat/state.ts
6
+ import { createRedisState } from "@chat-adapter/state-redis";
7
+ import { createMemoryState } from "@chat-adapter/state-memory";
8
+ var MIN_LOCK_TTL_MS = 1e3 * 60 * 5;
9
+ var QUEUE_INGRESS_DEDUP_PREFIX = "junior:queue_ingress";
10
+ var QUEUE_MESSAGE_PROCESSING_PREFIX = "junior:queue_message";
11
+ var AGENT_TURN_SESSION_PREFIX = "junior:agent_turn_session";
12
+ var QUEUE_MESSAGE_PROCESSING_TTL_MS = 30 * 60 * 1e3;
13
+ var QUEUE_MESSAGE_COMPLETED_TTL_MS = 7 * 24 * 60 * 60 * 1e3;
14
+ var QUEUE_MESSAGE_FAILED_TTL_MS = 6 * 60 * 60 * 1e3;
15
+ var AGENT_TURN_SESSION_TTL_MS = 24 * 60 * 60 * 1e3;
16
+ var CLAIM_OR_RECLAIM_PROCESSING_SCRIPT = `
17
+ local key = KEYS[1]
18
+ local nowMs = tonumber(ARGV[1])
19
+ local ttlMs = tonumber(ARGV[2])
20
+ local payload = ARGV[3]
21
+ local current = redis.call("get", key)
22
+
23
+ if not current then
24
+ redis.call("set", key, payload, "PX", ttlMs)
25
+ return 1
26
+ end
27
+
28
+ local ok, parsed = pcall(cjson.decode, current)
29
+ if not ok or type(parsed) ~= "table" then
30
+ return 0
31
+ end
32
+
33
+ local status = parsed["status"]
34
+ if status == "failed" then
35
+ redis.call("set", key, payload, "PX", ttlMs)
36
+ return 3
37
+ end
38
+ if status ~= "processing" then
39
+ return 0
40
+ end
41
+
42
+ local updatedAtMs = tonumber(parsed["updatedAtMs"])
43
+ if not updatedAtMs then
44
+ return 0
45
+ end
46
+
47
+ if updatedAtMs + ttlMs < nowMs then
48
+ redis.call("set", key, payload, "PX", ttlMs)
49
+ return 2
50
+ end
51
+
52
+ return 0
53
+ `;
54
+ var UPDATE_PROCESSING_STATE_IF_OWNER_SCRIPT = `
55
+ local key = KEYS[1]
56
+ local ownerToken = ARGV[1]
57
+ local ttlMs = tonumber(ARGV[2])
58
+ local payload = ARGV[3]
59
+ local current = redis.call("get", key)
60
+
61
+ if not current then
62
+ return 0
63
+ end
64
+
65
+ local ok, parsed = pcall(cjson.decode, current)
66
+ if not ok or type(parsed) ~= "table" then
67
+ return 0
68
+ end
69
+
70
+ local currentOwner = parsed["ownerToken"]
71
+ local status = parsed["status"]
72
+ if currentOwner ~= ownerToken then
73
+ return 0
74
+ end
75
+ if status ~= "processing" then
76
+ return 0
77
+ end
78
+
79
+ redis.call("set", key, payload, "PX", ttlMs)
80
+ return 1
81
+ `;
82
+ function createQueuedStateAdapter(base) {
83
+ const acquireLock = async (threadId, ttlMs) => {
84
+ const effectiveTtlMs = Math.max(ttlMs, MIN_LOCK_TTL_MS);
85
+ const lock = await base.acquireLock(threadId, effectiveTtlMs);
86
+ return lock;
87
+ };
88
+ return {
89
+ connect: () => base.connect(),
90
+ disconnect: () => base.disconnect(),
91
+ subscribe: (threadId) => base.subscribe(threadId),
92
+ unsubscribe: (threadId) => base.unsubscribe(threadId),
93
+ isSubscribed: (threadId) => base.isSubscribed(threadId),
94
+ acquireLock,
95
+ releaseLock: (lock) => base.releaseLock(lock),
96
+ extendLock: (lock, ttlMs) => base.extendLock(lock, Math.max(ttlMs, MIN_LOCK_TTL_MS)),
97
+ get: (key) => base.get(key),
98
+ set: (key, value, ttlMs) => base.set(key, value, ttlMs),
99
+ setIfNotExists: (key, value, ttlMs) => base.setIfNotExists(key, value, ttlMs),
100
+ delete: (key) => base.delete(key)
101
+ };
102
+ }
103
+ function createStateAdapter() {
104
+ if (process.env.JUNIOR_STATE_ADAPTER?.trim().toLowerCase() === "memory") {
105
+ _redisStateAdapter = void 0;
106
+ return createQueuedStateAdapter(createMemoryState());
107
+ }
108
+ if (!hasRedisConfig()) {
109
+ throw new Error("REDIS_URL is required for durable Slack thread state");
110
+ }
111
+ const redisState = createRedisState({
112
+ url: process.env.REDIS_URL
113
+ });
114
+ _redisStateAdapter = redisState;
115
+ return createQueuedStateAdapter(redisState);
116
+ }
117
+ var _stateAdapter;
118
+ var _redisStateAdapter;
119
+ function getRedisStateAdapter() {
120
+ if (!_redisStateAdapter) {
121
+ getStateAdapter();
122
+ }
123
+ if (!_redisStateAdapter) {
124
+ throw new Error("Redis state adapter is unavailable for this runtime");
125
+ }
126
+ return _redisStateAdapter;
127
+ }
128
+ function queueMessageKey(rawKey) {
129
+ return `${QUEUE_MESSAGE_PROCESSING_PREFIX}:${rawKey}`;
130
+ }
131
+ function parseQueueMessageState(value) {
132
+ if (typeof value !== "string") {
133
+ return void 0;
134
+ }
135
+ try {
136
+ const parsed = JSON.parse(value);
137
+ if (!parsed || parsed.status !== "processing" && parsed.status !== "completed" && parsed.status !== "failed" || typeof parsed.updatedAtMs !== "number") {
138
+ return void 0;
139
+ }
140
+ return {
141
+ status: parsed.status,
142
+ updatedAtMs: parsed.updatedAtMs,
143
+ ...typeof parsed.ownerToken === "string" ? { ownerToken: parsed.ownerToken } : {},
144
+ ...typeof parsed.queueMessageId === "string" ? { queueMessageId: parsed.queueMessageId } : {},
145
+ ...typeof parsed.errorMessage === "string" ? { errorMessage: parsed.errorMessage } : {}
146
+ };
147
+ } catch {
148
+ return void 0;
149
+ }
150
+ }
151
+ function agentTurnSessionKey(conversationId, sessionId) {
152
+ return `${AGENT_TURN_SESSION_PREFIX}:${conversationId}:${sessionId}`;
153
+ }
154
+ function isRecord(value) {
155
+ return typeof value === "object" && value !== null;
156
+ }
157
+ function parseAgentTurnSessionCheckpoint(value) {
158
+ if (typeof value !== "string") {
159
+ return void 0;
160
+ }
161
+ try {
162
+ const parsed = JSON.parse(value);
163
+ if (!isRecord(parsed)) {
164
+ return void 0;
165
+ }
166
+ const status = parsed.state;
167
+ if (status !== "running" && status !== "awaiting_resume" && status !== "completed" && status !== "failed") {
168
+ return void 0;
169
+ }
170
+ const conversationId = parsed.conversationId;
171
+ const sessionId = parsed.sessionId;
172
+ const sliceId = parsed.sliceId;
173
+ const checkpointVersion = parsed.checkpointVersion;
174
+ const updatedAtMs = parsed.updatedAtMs;
175
+ if (typeof conversationId !== "string" || typeof sessionId !== "string" || typeof sliceId !== "number" || typeof checkpointVersion !== "number" || typeof updatedAtMs !== "number") {
176
+ return void 0;
177
+ }
178
+ return {
179
+ checkpointVersion,
180
+ conversationId,
181
+ sessionId,
182
+ sliceId,
183
+ state: status,
184
+ updatedAtMs,
185
+ piMessages: Array.isArray(parsed.piMessages) ? parsed.piMessages : [],
186
+ ...typeof parsed.errorMessage === "string" ? { errorMessage: parsed.errorMessage } : {},
187
+ ...typeof parsed.resumedFromSliceId === "number" ? { resumedFromSliceId: parsed.resumedFromSliceId } : {}
188
+ };
189
+ } catch {
190
+ return void 0;
191
+ }
192
+ }
193
+ function getStateAdapter() {
194
+ if (!_stateAdapter) {
195
+ _stateAdapter = createStateAdapter();
196
+ }
197
+ return _stateAdapter;
198
+ }
199
+ async function claimQueueIngressDedup(rawKey, ttlMs) {
200
+ await getStateAdapter().connect();
201
+ const key = `${QUEUE_INGRESS_DEDUP_PREFIX}:${rawKey}`;
202
+ const result = await getRedisStateAdapter().getClient().set(key, "1", {
203
+ NX: true,
204
+ PX: ttlMs
205
+ });
206
+ return result === "OK";
207
+ }
208
+ async function hasQueueIngressDedup(rawKey) {
209
+ await getStateAdapter().connect();
210
+ const key = `${QUEUE_INGRESS_DEDUP_PREFIX}:${rawKey}`;
211
+ const value = await getRedisStateAdapter().getClient().get(key);
212
+ return typeof value === "string" && value.length > 0;
213
+ }
214
+ async function getQueueMessageProcessingState(rawKey) {
215
+ await getStateAdapter().connect();
216
+ const state = await getStateAdapter().get(queueMessageKey(rawKey));
217
+ return parseQueueMessageState(state);
218
+ }
219
+ async function acquireQueueMessageProcessingOwnership(args) {
220
+ await getStateAdapter().connect();
221
+ const key = queueMessageKey(args.rawKey);
222
+ const nowMs = Date.now();
223
+ const payload = JSON.stringify({
224
+ status: "processing",
225
+ updatedAtMs: nowMs,
226
+ ownerToken: args.ownerToken,
227
+ ...args.queueMessageId ? { queueMessageId: args.queueMessageId } : {}
228
+ });
229
+ const result = await getRedisStateAdapter().getClient().eval(CLAIM_OR_RECLAIM_PROCESSING_SCRIPT, {
230
+ keys: [key],
231
+ arguments: [String(nowMs), String(QUEUE_MESSAGE_PROCESSING_TTL_MS), payload]
232
+ });
233
+ if (result === 1) {
234
+ return "acquired";
235
+ }
236
+ if (result === 2) {
237
+ return "reclaimed";
238
+ }
239
+ if (result === 3) {
240
+ return "recovered";
241
+ }
242
+ return "blocked";
243
+ }
244
+ async function refreshQueueMessageProcessingOwnership(args) {
245
+ await getStateAdapter().connect();
246
+ const nowMs = Date.now();
247
+ const payload = JSON.stringify({
248
+ status: "processing",
249
+ updatedAtMs: nowMs,
250
+ ownerToken: args.ownerToken,
251
+ ...args.queueMessageId ? { queueMessageId: args.queueMessageId } : {}
252
+ });
253
+ const result = await getRedisStateAdapter().getClient().eval(UPDATE_PROCESSING_STATE_IF_OWNER_SCRIPT, {
254
+ keys: [queueMessageKey(args.rawKey)],
255
+ arguments: [args.ownerToken, String(QUEUE_MESSAGE_PROCESSING_TTL_MS), payload]
256
+ });
257
+ return result === 1;
258
+ }
259
+ async function completeQueueMessageProcessingOwnership(args) {
260
+ await getStateAdapter().connect();
261
+ const payload = JSON.stringify({
262
+ status: "completed",
263
+ updatedAtMs: Date.now(),
264
+ ownerToken: args.ownerToken,
265
+ ...args.queueMessageId ? { queueMessageId: args.queueMessageId } : {}
266
+ });
267
+ const result = await getRedisStateAdapter().getClient().eval(UPDATE_PROCESSING_STATE_IF_OWNER_SCRIPT, {
268
+ keys: [queueMessageKey(args.rawKey)],
269
+ arguments: [args.ownerToken, String(QUEUE_MESSAGE_COMPLETED_TTL_MS), payload]
270
+ });
271
+ return result === 1;
272
+ }
273
+ async function failQueueMessageProcessingOwnership(args) {
274
+ await getStateAdapter().connect();
275
+ const payload = JSON.stringify({
276
+ status: "failed",
277
+ updatedAtMs: Date.now(),
278
+ ownerToken: args.ownerToken,
279
+ errorMessage: args.errorMessage,
280
+ ...args.queueMessageId ? { queueMessageId: args.queueMessageId } : {}
281
+ });
282
+ const result = await getRedisStateAdapter().getClient().eval(UPDATE_PROCESSING_STATE_IF_OWNER_SCRIPT, {
283
+ keys: [queueMessageKey(args.rawKey)],
284
+ arguments: [args.ownerToken, String(QUEUE_MESSAGE_FAILED_TTL_MS), payload]
285
+ });
286
+ return result === 1;
287
+ }
288
+ async function getAgentTurnSessionCheckpoint(conversationId, sessionId) {
289
+ await getStateAdapter().connect();
290
+ const value = await getStateAdapter().get(agentTurnSessionKey(conversationId, sessionId));
291
+ return parseAgentTurnSessionCheckpoint(value);
292
+ }
293
+ async function upsertAgentTurnSessionCheckpoint(args) {
294
+ await getStateAdapter().connect();
295
+ const existing = await getAgentTurnSessionCheckpoint(args.conversationId, args.sessionId);
296
+ const checkpoint = {
297
+ checkpointVersion: (existing?.checkpointVersion ?? 0) + 1,
298
+ conversationId: args.conversationId,
299
+ sessionId: args.sessionId,
300
+ sliceId: args.sliceId,
301
+ state: args.state,
302
+ updatedAtMs: Date.now(),
303
+ piMessages: Array.isArray(args.piMessages) ? args.piMessages : [],
304
+ ...args.errorMessage ? { errorMessage: args.errorMessage } : {},
305
+ ...typeof args.resumedFromSliceId === "number" ? { resumedFromSliceId: args.resumedFromSliceId } : {}
306
+ };
307
+ const ttlMs = Math.max(1, args.ttlMs ?? AGENT_TURN_SESSION_TTL_MS);
308
+ await getStateAdapter().set(agentTurnSessionKey(args.conversationId, args.sessionId), JSON.stringify(checkpoint), ttlMs);
309
+ return checkpoint;
310
+ }
311
+
312
+ export {
313
+ getStateAdapter,
314
+ claimQueueIngressDedup,
315
+ hasQueueIngressDedup,
316
+ getQueueMessageProcessingState,
317
+ acquireQueueMessageProcessingOwnership,
318
+ refreshQueueMessageProcessingOwnership,
319
+ completeQueueMessageProcessingOwnership,
320
+ failQueueMessageProcessingOwnership,
321
+ getAgentTurnSessionCheckpoint,
322
+ upsertAgentTurnSessionCheckpoint
323
+ };
@@ -0,0 +1,10 @@
1
+ import {
2
+ createQueueCallbackHandler,
3
+ enqueueThreadMessage,
4
+ getThreadMessageTopic
5
+ } from "./chunk-ZA2IDPVG.js";
6
+ export {
7
+ createQueueCallbackHandler,
8
+ enqueueThreadMessage,
9
+ getThreadMessageTopic
10
+ };
@@ -0,0 +1,3 @@
1
+ declare function GET(): Promise<Response>;
2
+
3
+ export { GET };
@@ -0,0 +1,6 @@
1
+ import {
2
+ GET
3
+ } from "../chunk-4RBEYCOG.js";
4
+ export {
5
+ GET
6
+ };
@@ -0,0 +1,3 @@
1
+ declare function POST(request: Request): Promise<Response>;
2
+
3
+ export { POST };
@@ -0,0 +1,272 @@
1
+ import {
2
+ createQueueCallbackHandler,
3
+ getThreadMessageTopic
4
+ } from "../chunk-ZA2IDPVG.js";
5
+ import {
6
+ acquireQueueMessageProcessingOwnership,
7
+ completeQueueMessageProcessingOwnership,
8
+ failQueueMessageProcessingOwnership,
9
+ getQueueMessageProcessingState,
10
+ getStateAdapter,
11
+ refreshQueueMessageProcessingOwnership
12
+ } from "../chunk-ZBFSIN6G.js";
13
+ import {
14
+ downloadPrivateSlackFile
15
+ } from "../chunk-GDNDYMGX.js";
16
+ import {
17
+ createRequestContext,
18
+ logError,
19
+ logException,
20
+ logWarn,
21
+ setSpanStatus,
22
+ withContext,
23
+ withSpan
24
+ } from "../chunk-BBOVH5RF.js";
25
+
26
+ // src/chat/queue/process-thread-message.ts
27
+ import { Message, ThreadImpl } from "chat";
28
+
29
+ // src/chat/thread-runtime/process-thread-message-runtime.ts
30
+ function rehydrateAttachmentFetchers(payload) {
31
+ for (const attachment of payload.message.attachments) {
32
+ if (!attachment.fetchData && attachment.url) {
33
+ attachment.fetchData = () => downloadPrivateSlackFile(attachment.url);
34
+ }
35
+ }
36
+ }
37
+ async function processThreadMessageRuntime(args) {
38
+ const { appSlackRuntime } = await import("../bot-DLML4Z7F.js");
39
+ const runtimePayload = {
40
+ message: args.message,
41
+ thread: args.thread
42
+ };
43
+ rehydrateAttachmentFetchers(runtimePayload);
44
+ if (args.kind === "new_mention") {
45
+ await appSlackRuntime.handleNewMention(args.thread, args.message, {
46
+ beforeFirstResponsePost: args.beforeFirstResponsePost
47
+ });
48
+ return;
49
+ }
50
+ if (args.kind === "subscribed_reply") {
51
+ await appSlackRuntime.handleSubscribedMessage(args.thread, args.message, {
52
+ beforeFirstResponsePost: args.beforeFirstResponsePost,
53
+ preApprovedReply: true
54
+ });
55
+ return;
56
+ }
57
+ await appSlackRuntime.handleSubscribedMessage(args.thread, args.message, {
58
+ beforeFirstResponsePost: args.beforeFirstResponsePost
59
+ });
60
+ }
61
+
62
+ // src/chat/queue/process-thread-message.ts
63
+ var stateAdapterConnected = false;
64
+ function isSerializedThread(thread) {
65
+ return typeof thread === "object" && thread !== null && thread._type === "chat:Thread";
66
+ }
67
+ function isSerializedMessage(message) {
68
+ return typeof message === "object" && message !== null && message._type === "chat:Message";
69
+ }
70
+ function getPayloadChannelId(payload) {
71
+ return payload.thread.channelId;
72
+ }
73
+ function getPayloadUserId(payload) {
74
+ return payload.message.author?.userId;
75
+ }
76
+ function createMessageOwnerToken() {
77
+ return `msg-${Date.now()}-${Math.random().toString(16).slice(2)}`;
78
+ }
79
+ var QueueMessageOwnershipError = class extends Error {
80
+ constructor(stage, dedupKey) {
81
+ super(`Queue message ownership lost during ${stage} for dedupKey=${dedupKey}`);
82
+ this.name = "QueueMessageOwnershipError";
83
+ }
84
+ };
85
+ var defaultProcessQueuedThreadMessageDeps = {
86
+ clearProcessingReaction: async ({ channelId, timestamp }) => {
87
+ const { removeReactionFromMessage } = await import("../channel-HJO33DGJ.js");
88
+ await removeReactionFromMessage({
89
+ channelId,
90
+ timestamp,
91
+ emoji: "eyes"
92
+ });
93
+ },
94
+ logWarn,
95
+ processRuntime: processThreadMessageRuntime
96
+ };
97
+ function deserializeThread(thread) {
98
+ if (isSerializedThread(thread)) {
99
+ return ThreadImpl.fromJSON(thread);
100
+ }
101
+ return thread;
102
+ }
103
+ function deserializeMessage(message) {
104
+ if (isSerializedMessage(message)) {
105
+ return Message.fromJSON(message);
106
+ }
107
+ return message;
108
+ }
109
+ async function logThreadMessageFailure(payload, errorMessage) {
110
+ logError(
111
+ "queue_message_failed",
112
+ {
113
+ slackThreadId: payload.normalizedThreadId,
114
+ slackChannelId: getPayloadChannelId(payload),
115
+ slackUserId: getPayloadUserId(payload)
116
+ },
117
+ {
118
+ "messaging.message.id": payload.message.id,
119
+ "app.queue.message_kind": payload.kind,
120
+ "app.queue.message_id": payload.queueMessageId,
121
+ "error.message": errorMessage
122
+ },
123
+ "Queue message processing failed"
124
+ );
125
+ }
126
+ async function processQueuedThreadMessage(payload, deps = defaultProcessQueuedThreadMessageDeps) {
127
+ const existingMessageState = await getQueueMessageProcessingState(payload.dedupKey);
128
+ if (existingMessageState?.status === "completed") {
129
+ return;
130
+ }
131
+ const ownerToken = createMessageOwnerToken();
132
+ const claimResult = await acquireQueueMessageProcessingOwnership({
133
+ rawKey: payload.dedupKey,
134
+ ownerToken,
135
+ queueMessageId: payload.queueMessageId
136
+ });
137
+ if (claimResult === "blocked") {
138
+ return;
139
+ }
140
+ const threadWasSerialized = isSerializedThread(payload.thread);
141
+ if (threadWasSerialized && !stateAdapterConnected) {
142
+ await getStateAdapter().connect();
143
+ stateAdapterConnected = true;
144
+ }
145
+ const runtimePayload = {
146
+ ...payload,
147
+ thread: deserializeThread(payload.thread),
148
+ message: deserializeMessage(payload.message)
149
+ };
150
+ try {
151
+ const refreshed = await refreshQueueMessageProcessingOwnership({
152
+ rawKey: payload.dedupKey,
153
+ ownerToken,
154
+ queueMessageId: payload.queueMessageId
155
+ });
156
+ if (!refreshed) {
157
+ throw new QueueMessageOwnershipError("refresh", payload.dedupKey);
158
+ }
159
+ let reactionCleared = false;
160
+ const clearReactionBeforeFirstResponsePost = async () => {
161
+ if (reactionCleared) {
162
+ return;
163
+ }
164
+ reactionCleared = true;
165
+ try {
166
+ await deps.clearProcessingReaction({
167
+ channelId: runtimePayload.thread.channelId,
168
+ timestamp: runtimePayload.message.id
169
+ });
170
+ } catch (error) {
171
+ const errorMessage = error instanceof Error ? error.message : String(error);
172
+ deps.logWarn(
173
+ "queue_processing_reaction_clear_failed",
174
+ {
175
+ slackThreadId: payload.normalizedThreadId,
176
+ slackChannelId: getPayloadChannelId(payload),
177
+ slackUserId: getPayloadUserId(payload)
178
+ },
179
+ {
180
+ "messaging.message.id": payload.message.id,
181
+ "app.queue.message_kind": payload.kind,
182
+ "app.queue.message_id": payload.queueMessageId,
183
+ "error.message": errorMessage
184
+ },
185
+ "Failed to remove processing reaction before sending queue response"
186
+ );
187
+ }
188
+ };
189
+ await deps.processRuntime({
190
+ kind: runtimePayload.kind,
191
+ thread: runtimePayload.thread,
192
+ message: runtimePayload.message,
193
+ beforeFirstResponsePost: clearReactionBeforeFirstResponsePost
194
+ });
195
+ const completed = await completeQueueMessageProcessingOwnership({
196
+ rawKey: payload.dedupKey,
197
+ ownerToken,
198
+ queueMessageId: payload.queueMessageId
199
+ });
200
+ if (!completed) {
201
+ throw new QueueMessageOwnershipError("complete", payload.dedupKey);
202
+ }
203
+ } catch (error) {
204
+ const errorMessage = error instanceof Error ? error.message : String(error);
205
+ await logThreadMessageFailure(payload, errorMessage);
206
+ const failed = await failQueueMessageProcessingOwnership({
207
+ rawKey: payload.dedupKey,
208
+ ownerToken,
209
+ errorMessage,
210
+ queueMessageId: payload.queueMessageId
211
+ });
212
+ if (!failed && !(error instanceof QueueMessageOwnershipError)) {
213
+ throw new Error(`Failed to persist queue message failure state for dedupKey=${payload.dedupKey}: ${errorMessage}`);
214
+ }
215
+ throw error;
216
+ }
217
+ }
218
+
219
+ // src/handlers/queue-callback.ts
220
+ var callbackHandler = createQueueCallbackHandler(async (message, metadata) => {
221
+ const payload = {
222
+ ...message,
223
+ queueMessageId: metadata.messageId
224
+ };
225
+ if (metadata.topicName !== getThreadMessageTopic()) {
226
+ throw new Error(`Unexpected queue topic: ${metadata.topicName}`);
227
+ }
228
+ await withSpan(
229
+ "queue.process_message",
230
+ "queue.process_message",
231
+ {
232
+ slackThreadId: payload.normalizedThreadId,
233
+ slackChannelId: payload.thread.channelId,
234
+ slackUserId: payload.message.author?.userId
235
+ },
236
+ async () => {
237
+ await processQueuedThreadMessage(payload);
238
+ },
239
+ {
240
+ "messaging.message.id": payload.message.id,
241
+ "app.queue.message_kind": payload.kind,
242
+ "app.queue.message_id": payload.queueMessageId,
243
+ "app.queue.delivery_count": metadata.deliveryCount,
244
+ "app.queue.topic": metadata.topicName
245
+ }
246
+ );
247
+ });
248
+ async function POST(request) {
249
+ const requestContext = createRequestContext(request, { platform: "queue" });
250
+ return withContext(requestContext, async () => {
251
+ try {
252
+ const response = await callbackHandler(request);
253
+ setSpanStatus(response.status >= 500 ? "error" : "ok");
254
+ return response;
255
+ } catch (error) {
256
+ const message = error instanceof Error ? error.message : String(error);
257
+ logError(
258
+ "queue_callback_failed",
259
+ {},
260
+ {
261
+ "error.message": message
262
+ },
263
+ "Queue callback processing failed"
264
+ );
265
+ logException(error, "queue_callback_failed");
266
+ throw error;
267
+ }
268
+ });
269
+ }
270
+ export {
271
+ POST
272
+ };
@@ -0,0 +1,9 @@
1
+ type RouteContext = {
2
+ params: Promise<{
3
+ path: string[];
4
+ }>;
5
+ };
6
+ declare function GET(request: Request, context: RouteContext): Promise<Response>;
7
+ declare function POST(request: Request, context: RouteContext): Promise<Response>;
8
+
9
+ export { GET, POST };
@@ -0,0 +1,53 @@
1
+ import {
2
+ POST
3
+ } from "../chunk-OD6TOSY4.js";
4
+ import {
5
+ GET
6
+ } from "../chunk-4RBEYCOG.js";
7
+ import "../chunk-BBOVH5RF.js";
8
+
9
+ // src/handlers/oauth-callback.ts
10
+ async function loadOAuthRoute() {
11
+ return import("../route-XLYK6CKP.js");
12
+ }
13
+ async function GET2(request, context) {
14
+ const route = await loadOAuthRoute();
15
+ return route.GET(request, context);
16
+ }
17
+
18
+ // src/handlers/router.ts
19
+ function normalizeRoutePath(pathParts) {
20
+ const route = pathParts.join("/").replace(/^\/+|\/+$/g, "");
21
+ return route.startsWith("api/") ? route.slice("api/".length) : route;
22
+ }
23
+ async function GET3(request, context) {
24
+ const { path } = await context.params;
25
+ const route = normalizeRoutePath(path);
26
+ if (route === "health") {
27
+ return GET();
28
+ }
29
+ const oauthCallbackMatch = route.match(/^oauth\/callback\/([^/]+)$/);
30
+ if (oauthCallbackMatch) {
31
+ const provider = oauthCallbackMatch[1];
32
+ return GET2(request, {
33
+ params: Promise.resolve({ provider })
34
+ });
35
+ }
36
+ return new Response("Not Found", { status: 404 });
37
+ }
38
+ async function POST2(request, context) {
39
+ const { path } = await context.params;
40
+ const route = normalizeRoutePath(path);
41
+ const webhookMatch = route.match(/^webhooks\/([^/]+)$/);
42
+ if (webhookMatch) {
43
+ const platform = webhookMatch[1];
44
+ return POST(request, {
45
+ params: Promise.resolve({ platform })
46
+ });
47
+ }
48
+ return new Response("Not Found", { status: 404 });
49
+ }
50
+ export {
51
+ GET3 as GET,
52
+ POST2 as POST
53
+ };
@@ -0,0 +1,8 @@
1
+ type WebhookRouteContext = {
2
+ params: Promise<{
3
+ platform: string;
4
+ }>;
5
+ };
6
+ declare function POST(request: Request, context: WebhookRouteContext): Promise<Response>;
7
+
8
+ export { POST };
@@ -0,0 +1,7 @@
1
+ import {
2
+ POST
3
+ } from "../chunk-OD6TOSY4.js";
4
+ import "../chunk-BBOVH5RF.js";
5
+ export {
6
+ POST
7
+ };