@ai-devkit/channel-connector 0.13.2 → 0.13.4

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.
Files changed (69) hide show
  1. package/dist/ChannelConfigRepository.d.ts +1 -1
  2. package/dist/ChannelConfigRepository.d.ts.map +1 -1
  3. package/dist/ChannelConfigRepository.js +13 -13
  4. package/dist/ChannelConfigRepository.js.map +1 -1
  5. package/dist/ChannelManager.d.ts +1 -1
  6. package/dist/ChannelManager.d.ts.map +1 -1
  7. package/dist/ChannelManager.js +9 -9
  8. package/dist/ChannelManager.js.map +1 -1
  9. package/dist/__tests__/ChannelConfigRepository.test.js +72 -72
  10. package/dist/__tests__/ChannelConfigRepository.test.js.map +1 -1
  11. package/dist/__tests__/ChannelManager.test.js +26 -26
  12. package/dist/__tests__/ChannelManager.test.js.map +1 -1
  13. package/dist/__tests__/adapters/slack/SlackAdapter.test.js +137 -137
  14. package/dist/__tests__/adapters/slack/SlackAdapter.test.js.map +1 -1
  15. package/dist/__tests__/adapters/slack/SlackMessageDelivery.test.js +36 -36
  16. package/dist/__tests__/adapters/slack/SlackMessageDelivery.test.js.map +1 -1
  17. package/dist/__tests__/adapters/slack/slackMarkdown.test.js +25 -25
  18. package/dist/__tests__/adapters/slack/slackMarkdown.test.js.map +1 -1
  19. package/dist/__tests__/adapters/slack/slackQuestion.test.js +41 -41
  20. package/dist/__tests__/adapters/slack/slackQuestion.test.js.map +1 -1
  21. package/dist/__tests__/adapters/telegram/TelegramAdapter.test.js +154 -154
  22. package/dist/__tests__/adapters/telegram/TelegramAdapter.test.js.map +1 -1
  23. package/dist/__tests__/adapters/telegram/telegramHtml.test.js +109 -109
  24. package/dist/__tests__/adapters/telegram/telegramHtml.test.js.map +1 -1
  25. package/dist/adapters/ChannelAdapter.d.ts +1 -1
  26. package/dist/adapters/ChannelAdapter.d.ts.map +1 -1
  27. package/dist/adapters/ChannelAdapter.js +1 -1
  28. package/dist/adapters/ChannelAdapter.js.map +1 -1
  29. package/dist/adapters/slack/SlackAdapter.d.ts +2 -2
  30. package/dist/adapters/slack/SlackAdapter.d.ts.map +1 -1
  31. package/dist/adapters/slack/SlackAdapter.js +17 -17
  32. package/dist/adapters/slack/SlackAdapter.js.map +1 -1
  33. package/dist/adapters/slack/SlackMessageDelivery.d.ts +1 -1
  34. package/dist/adapters/slack/SlackMessageDelivery.d.ts.map +1 -1
  35. package/dist/adapters/slack/SlackMessageDelivery.js +6 -6
  36. package/dist/adapters/slack/SlackMessageDelivery.js.map +1 -1
  37. package/dist/adapters/slack/slackMarkdown.d.ts.map +1 -1
  38. package/dist/adapters/slack/slackMarkdown.js +36 -36
  39. package/dist/adapters/slack/slackMarkdown.js.map +1 -1
  40. package/dist/adapters/slack/slackQuestion.d.ts +7 -7
  41. package/dist/adapters/slack/slackQuestion.d.ts.map +1 -1
  42. package/dist/adapters/slack/slackQuestion.js +13 -13
  43. package/dist/adapters/slack/slackQuestion.js.map +1 -1
  44. package/dist/adapters/telegram/TelegramAdapter.d.ts +2 -2
  45. package/dist/adapters/telegram/TelegramAdapter.d.ts.map +1 -1
  46. package/dist/adapters/telegram/TelegramAdapter.js +30 -27
  47. package/dist/adapters/telegram/TelegramAdapter.js.map +1 -1
  48. package/dist/adapters/telegram/TelegramMessageDelivery.d.ts.map +1 -1
  49. package/dist/adapters/telegram/TelegramMessageDelivery.js +2 -2
  50. package/dist/adapters/telegram/TelegramMessageDelivery.js.map +1 -1
  51. package/dist/adapters/telegram/telegramHtml.d.ts.map +1 -1
  52. package/dist/adapters/telegram/telegramHtml.js +16 -16
  53. package/dist/adapters/telegram/telegramHtml.js.map +1 -1
  54. package/dist/adapters/telegram/telegramKeyboard.d.ts +1 -1
  55. package/dist/adapters/telegram/telegramKeyboard.d.ts.map +1 -1
  56. package/dist/adapters/telegram/telegramKeyboard.js.map +1 -1
  57. package/dist/adapters/telegram/telegramMarkdown.d.ts.map +1 -1
  58. package/dist/adapters/telegram/telegramMarkdown.js +28 -28
  59. package/dist/adapters/telegram/telegramMarkdown.js.map +1 -1
  60. package/dist/index.d.ts +9 -9
  61. package/dist/index.d.ts.map +1 -1
  62. package/dist/index.js +5 -5
  63. package/dist/index.js.map +1 -1
  64. package/dist/types.d.ts +6 -6
  65. package/dist/types.d.ts.map +1 -1
  66. package/dist/types.js +1 -1
  67. package/dist/types.js.map +1 -1
  68. package/package.json +21 -22
  69. package/vitest.config.ts +16 -16
@@ -1,100 +1,100 @@
1
- import { EventEmitter } from 'node:events';
2
- import { SlackAdapter, validateSlackAppToken, validateSlackCredentials } from '../../../adapters/slack/SlackAdapter.js';
1
+ import { EventEmitter } from "node:events";
2
+ import { SlackAdapter, validateSlackAppToken, validateSlackCredentials } from "../../../adapters/slack/SlackAdapter.js";
3
3
  let FakeSocketClient = class FakeSocketClient extends EventEmitter {
4
4
  start = vi.fn().mockResolvedValue(undefined);
5
5
  disconnect = vi.fn().mockResolvedValue(undefined);
6
6
  };
7
7
  const config = {
8
- appToken: 'xapp-fake',
9
- botToken: 'xoxb-fake',
10
- appId: 'A123',
11
- botUserId: 'U-BOT',
12
- workspaceId: 'T123',
13
- transport: 'socket-mode',
14
- audience: 'dm'
8
+ appToken: "xapp-fake",
9
+ botToken: "xoxb-fake",
10
+ appId: "A123",
11
+ botUserId: "U-BOT",
12
+ workspaceId: "T123",
13
+ transport: "socket-mode",
14
+ audience: "dm"
15
15
  };
16
16
  function envelope(overrides = {}) {
17
17
  return {
18
- type: 'events_api',
18
+ type: "events_api",
19
19
  ack: vi.fn().mockResolvedValue(undefined),
20
20
  body: {
21
- team_id: 'T123',
22
- event_id: 'Ev123',
21
+ team_id: "T123",
22
+ event_id: "Ev123",
23
23
  event: {
24
- type: 'message',
25
- channel_type: 'im',
26
- channel: 'D123',
27
- user: 'U123',
28
- text: 'run tests',
29
- ts: '100.1',
30
- event_ts: '100.1',
24
+ type: "message",
25
+ channel_type: "im",
26
+ channel: "D123",
27
+ user: "U123",
28
+ text: "run tests",
29
+ ts: "100.1",
30
+ event_ts: "100.1",
31
31
  ...overrides
32
32
  }
33
33
  }
34
34
  };
35
35
  }
36
- describe('SlackAdapter', ()=>{
37
- it('validates bot identity without returning either credential', async ()=>{
36
+ describe("SlackAdapter", ()=>{
37
+ it("validates bot identity without returning either credential", async ()=>{
38
38
  const authTest = vi.fn().mockResolvedValue({
39
39
  ok: true,
40
- app_id: 'A123',
41
- user_id: 'U-BOT',
42
- team_id: 'T123',
43
- team: 'Sandbox'
40
+ app_id: "A123",
41
+ user_id: "U-BOT",
42
+ team_id: "T123",
43
+ team: "Sandbox"
44
44
  });
45
- await expect(validateSlackCredentials('xoxb-fake', {
45
+ await expect(validateSlackCredentials("xoxb-fake", {
46
46
  authTest
47
47
  })).resolves.toEqual({
48
- appId: 'A123',
49
- botUserId: 'U-BOT',
50
- workspaceId: 'T123',
51
- workspaceName: 'Sandbox'
48
+ appId: "A123",
49
+ botUserId: "U-BOT",
50
+ workspaceId: "T123",
51
+ workspaceName: "Sandbox"
52
52
  });
53
53
  });
54
- it('accepts Slack documented bot identity without an app ID', async ()=>{
54
+ it("accepts Slack documented bot identity without an app ID", async ()=>{
55
55
  const authTest = vi.fn().mockResolvedValue({
56
56
  ok: true,
57
- bot_id: 'B123',
58
- user_id: 'U-BOT',
59
- team_id: 'T123',
60
- team: 'Sandbox'
57
+ bot_id: "B123",
58
+ user_id: "U-BOT",
59
+ team_id: "T123",
60
+ team: "Sandbox"
61
61
  });
62
- await expect(validateSlackCredentials('xoxb-fake', {
62
+ await expect(validateSlackCredentials("xoxb-fake", {
63
63
  authTest
64
64
  })).resolves.toEqual({
65
- botUserId: 'U-BOT',
66
- workspaceId: 'T123',
67
- workspaceName: 'Sandbox'
65
+ botUserId: "U-BOT",
66
+ workspaceId: "T123",
67
+ workspaceName: "Sandbox"
68
68
  });
69
69
  });
70
- it('rejects incomplete Slack credential identity', async ()=>{
70
+ it("rejects incomplete Slack credential identity", async ()=>{
71
71
  const authTest = vi.fn().mockResolvedValue({
72
72
  ok: true,
73
- team_id: 'T123'
73
+ team_id: "T123"
74
74
  });
75
- await expect(validateSlackCredentials('xoxb-fake', {
75
+ await expect(validateSlackCredentials("xoxb-fake", {
76
76
  authTest
77
- })).rejects.toThrow('Slack bot token returned incomplete identity');
77
+ })).rejects.toThrow("Slack bot token returned incomplete identity");
78
78
  });
79
- it('validates an app token through apps.connections.open', async ()=>{
79
+ it("validates an app token through apps.connections.open", async ()=>{
80
80
  const open = vi.fn().mockResolvedValue({
81
81
  ok: true,
82
- url: 'wss://wss-primary.slack.com/link/'
82
+ url: "wss://wss-primary.slack.com/link/"
83
83
  });
84
- await expect(validateSlackAppToken('xapp-fake', {
84
+ await expect(validateSlackAppToken("xapp-fake", {
85
85
  open
86
86
  })).resolves.toBeUndefined();
87
87
  expect(open).toHaveBeenCalledOnce();
88
88
  });
89
- it('rejects an app token that cannot open Socket Mode', async ()=>{
89
+ it("rejects an app token that cannot open Socket Mode", async ()=>{
90
90
  const open = vi.fn().mockResolvedValue({
91
91
  ok: false
92
92
  });
93
- await expect(validateSlackAppToken('xapp-fake', {
93
+ await expect(validateSlackAppToken("xapp-fake", {
94
94
  open
95
- })).rejects.toThrow('Slack app token cannot open Socket Mode');
95
+ })).rejects.toThrow("Slack app token cannot open Socket Mode");
96
96
  });
97
- it('starts and stops the official Socket Mode client and reports health', async ()=>{
97
+ it("starts and stops the official Socket Mode client and reports health", async ()=>{
98
98
  const socket = new FakeSocketClient();
99
99
  const adapter = new SlackAdapter(config, {
100
100
  socketClient: socket,
@@ -112,7 +112,7 @@ describe('SlackAdapter', ()=>{
112
112
  expect(socket.disconnect).toHaveBeenCalledOnce();
113
113
  expect(await adapter.isHealthy()).toBe(false);
114
114
  });
115
- it('does not register duplicate listeners across restarts', async ()=>{
115
+ it("does not register duplicate listeners across restarts", async ()=>{
116
116
  const socket = new FakeSocketClient();
117
117
  const adapter = new SlackAdapter(config, {
118
118
  socketClient: socket,
@@ -125,14 +125,14 @@ describe('SlackAdapter', ()=>{
125
125
  await adapter.start();
126
126
  await adapter.stop();
127
127
  await adapter.start();
128
- expect(socket.listenerCount('slack_event')).toBe(1);
129
- expect(socket.listenerCount('interactive')).toBe(1);
130
- socket.emit('disconnected');
128
+ expect(socket.listenerCount("slack_event")).toBe(1);
129
+ expect(socket.listenerCount("interactive")).toBe(1);
130
+ socket.emit("disconnected");
131
131
  expect(await adapter.isHealthy()).toBe(false);
132
- socket.emit('connected');
132
+ socket.emit("connected");
133
133
  expect(await adapter.isHealthy()).toBe(true);
134
134
  });
135
- it('acknowledges before delivering one normalized workspace DM event', async ()=>{
135
+ it("acknowledges before delivering one normalized workspace DM event", async ()=>{
136
136
  const socket = new FakeSocketClient();
137
137
  const adapter = new SlackAdapter(config, {
138
138
  socketClient: socket,
@@ -144,71 +144,71 @@ describe('SlackAdapter', ()=>{
144
144
  });
145
145
  const order = [];
146
146
  adapter.onMessage(vi.fn(async (message)=>{
147
- order.push('handler');
147
+ order.push("handler");
148
148
  expect(message).toMatchObject({
149
- channelType: 'slack',
150
- chatId: 'D123',
151
- userId: 'U123',
152
- workspaceId: 'T123',
153
- messageId: 'Ev123',
154
- text: 'run tests'
149
+ channelType: "slack",
150
+ chatId: "D123",
151
+ userId: "U123",
152
+ workspaceId: "T123",
153
+ messageId: "Ev123",
154
+ text: "run tests"
155
155
  });
156
156
  }));
157
157
  await adapter.start();
158
158
  const item = envelope();
159
159
  item.ack.mockImplementation(async ()=>{
160
- order.push('ack');
160
+ order.push("ack");
161
161
  });
162
- socket.emit('slack_event', item);
162
+ socket.emit("slack_event", item);
163
163
  await vi.waitFor(()=>expect(order).toEqual([
164
- 'ack',
165
- 'handler'
164
+ "ack",
165
+ "handler"
166
166
  ]));
167
- socket.emit('slack_event', item);
167
+ socket.emit("slack_event", item);
168
168
  await new Promise((resolve)=>setTimeout(resolve, 0));
169
169
  expect(order).toEqual([
170
- 'ack',
171
- 'handler',
172
- 'ack'
170
+ "ack",
171
+ "handler",
172
+ "ack"
173
173
  ]);
174
174
  });
175
175
  it.each([
176
176
  [
177
- 'wrong workspace',
177
+ "wrong workspace",
178
178
  {},
179
179
  {
180
- team_id: 'T999'
180
+ team_id: "T999"
181
181
  }
182
182
  ],
183
183
  [
184
- 'bot message',
184
+ "bot message",
185
185
  {
186
- bot_id: 'B123'
186
+ bot_id: "B123"
187
187
  },
188
188
  {}
189
189
  ],
190
190
  [
191
- 'self message',
191
+ "self message",
192
192
  {
193
- user: 'U-BOT'
193
+ user: "U-BOT"
194
194
  },
195
195
  {}
196
196
  ],
197
197
  [
198
- 'subtype',
198
+ "subtype",
199
199
  {
200
- subtype: 'message_changed'
200
+ subtype: "message_changed"
201
201
  },
202
202
  {}
203
203
  ],
204
204
  [
205
- 'non-DM',
205
+ "non-DM",
206
206
  {
207
- channel_type: 'channel'
207
+ channel_type: "channel"
208
208
  },
209
209
  {}
210
210
  ]
211
- ])('ignores %s events', async (_name, eventOverrides, bodyOverrides)=>{
211
+ ])("ignores %s events", async (_name, eventOverrides, bodyOverrides)=>{
212
212
  const socket = new FakeSocketClient();
213
213
  const handler = vi.fn();
214
214
  const adapter = new SlackAdapter(config, {
@@ -223,11 +223,11 @@ describe('SlackAdapter', ()=>{
223
223
  await adapter.start();
224
224
  const item = envelope(eventOverrides);
225
225
  Object.assign(item.body, bodyOverrides);
226
- socket.emit('slack_event', item);
226
+ socket.emit("slack_event", item);
227
227
  await vi.waitFor(()=>expect(item.ack).toHaveBeenCalled());
228
228
  expect(handler).not.toHaveBeenCalled();
229
229
  });
230
- it('delivers an ordinary workspace DM immediately without pairing', async ()=>{
230
+ it("delivers an ordinary workspace DM immediately without pairing", async ()=>{
231
231
  const socket = new FakeSocketClient();
232
232
  const handler = vi.fn();
233
233
  const adapter = new SlackAdapter(config, {
@@ -240,21 +240,21 @@ describe('SlackAdapter', ()=>{
240
240
  });
241
241
  adapter.onMessage(handler);
242
242
  await adapter.start();
243
- socket.emit('slack_event', envelope({
244
- text: 'run tests'
243
+ socket.emit("slack_event", envelope({
244
+ text: "run tests"
245
245
  }));
246
246
  await vi.waitFor(()=>expect(handler).toHaveBeenCalledWith(expect.objectContaining({
247
- chatId: 'D123',
248
- userId: 'U123',
249
- text: 'run tests',
250
- workspaceId: 'T123'
247
+ chatId: "D123",
248
+ userId: "U123",
249
+ text: "run tests",
250
+ workspaceId: "T123"
251
251
  })));
252
252
  });
253
- it('sends an accessible Block Kit question and finalizes its actions', async ()=>{
253
+ it("sends an accessible Block Kit question and finalizes its actions", async ()=>{
254
254
  const socket = new FakeSocketClient();
255
255
  const postMessage = vi.fn().mockResolvedValue({
256
256
  ok: true,
257
- ts: '400.1'
257
+ ts: "400.1"
258
258
  });
259
259
  const update = vi.fn().mockResolvedValue({
260
260
  ok: true
@@ -268,37 +268,37 @@ describe('SlackAdapter', ()=>{
268
268
  }
269
269
  }
270
270
  });
271
- await expect(adapter.sendQuestion('D123', {
272
- id: 'q1',
273
- header: 'Scope',
274
- question: 'Choose one',
271
+ await expect(adapter.sendQuestion("D123", {
272
+ id: "q1",
273
+ header: "Scope",
274
+ question: "Choose one",
275
275
  allowSkip: true,
276
276
  options: [
277
277
  {
278
- label: 'Safe',
279
- description: 'Read only',
280
- value: '1'
278
+ label: "Safe",
279
+ description: "Read only",
280
+ value: "1"
281
281
  }
282
282
  ]
283
283
  })).resolves.toEqual({
284
- messageId: '400.1'
284
+ messageId: "400.1"
285
285
  });
286
286
  expect(postMessage).toHaveBeenCalledWith(expect.objectContaining({
287
- channel: 'D123',
288
- text: 'Scope: Choose one',
287
+ channel: "D123",
288
+ text: "Scope: Choose one",
289
289
  blocks: expect.any(Array)
290
290
  }));
291
- await adapter.finalizeInteraction('D123', '400.1');
291
+ await adapter.finalizeInteraction("D123", "400.1");
292
292
  expect(update).toHaveBeenCalledWith({
293
- channel: 'D123',
294
- ts: '400.1',
293
+ channel: "D123",
294
+ ts: "400.1",
295
295
  blocks: []
296
296
  });
297
297
  });
298
- it('escapes Slack control syntax in question fallback text', async ()=>{
298
+ it("escapes Slack control syntax in question fallback text", async ()=>{
299
299
  const postMessage = vi.fn().mockResolvedValue({
300
300
  ok: true,
301
- ts: '400.1'
301
+ ts: "400.1"
302
302
  });
303
303
  const adapter = new SlackAdapter(config, {
304
304
  socketClient: new FakeSocketClient(),
@@ -308,23 +308,23 @@ describe('SlackAdapter', ()=>{
308
308
  }
309
309
  }
310
310
  });
311
- await adapter.sendQuestion('D123', {
312
- id: 'q1',
313
- header: '<!channel>',
314
- question: 'Choose <one> & continue',
311
+ await adapter.sendQuestion("D123", {
312
+ id: "q1",
313
+ header: "<!channel>",
314
+ question: "Choose <one> & continue",
315
315
  allowSkip: false,
316
316
  options: [
317
317
  {
318
- label: 'Safe',
319
- value: '1'
318
+ label: "Safe",
319
+ value: "1"
320
320
  }
321
321
  ]
322
322
  });
323
323
  expect(postMessage).toHaveBeenCalledWith(expect.objectContaining({
324
- text: '&lt;!channel&gt;: Choose &lt;one&gt; &amp; continue'
324
+ text: "&lt;!channel&gt;: Choose &lt;one&gt; &amp; continue"
325
325
  }));
326
326
  });
327
- it('acknowledges and delivers one valid workspace block action', async ()=>{
327
+ it("acknowledges and delivers one valid workspace block action", async ()=>{
328
328
  const socket = new FakeSocketClient();
329
329
  const handler = vi.fn();
330
330
  const adapter = new SlackAdapter(config, {
@@ -340,44 +340,44 @@ describe('SlackAdapter', ()=>{
340
340
  const item = {
341
341
  ack: vi.fn().mockResolvedValue(undefined),
342
342
  body: {
343
- type: 'block_actions',
343
+ type: "block_actions",
344
344
  team: {
345
- id: 'T123'
345
+ id: "T123"
346
346
  },
347
347
  user: {
348
- id: 'U123'
348
+ id: "U123"
349
349
  },
350
350
  channel: {
351
- id: 'D123'
351
+ id: "D123"
352
352
  },
353
353
  container: {
354
- message_ts: '400.1'
354
+ message_ts: "400.1"
355
355
  },
356
356
  actions: [
357
357
  {
358
- action_id: 'ai_devkit_question',
359
- action_ts: '401.1',
360
- value: 'q1:1'
358
+ action_id: "ai_devkit_question",
359
+ action_ts: "401.1",
360
+ value: "q1:1"
361
361
  }
362
362
  ]
363
363
  }
364
364
  };
365
- socket.emit('interactive', item);
365
+ socket.emit("interactive", item);
366
366
  await vi.waitFor(()=>expect(handler).toHaveBeenCalledOnce());
367
367
  expect(item.ack).toHaveBeenCalledOnce();
368
368
  expect(handler).toHaveBeenCalledWith(expect.objectContaining({
369
- workspaceId: 'T123',
370
- chatId: 'D123',
371
- userId: 'U123',
372
- messageId: '400.1',
373
- value: 'q1:1'
369
+ workspaceId: "T123",
370
+ chatId: "D123",
371
+ userId: "U123",
372
+ messageId: "400.1",
373
+ value: "q1:1"
374
374
  }));
375
- socket.emit('interactive', item);
375
+ socket.emit("interactive", item);
376
376
  await new Promise((resolve)=>setTimeout(resolve, 0));
377
377
  expect(handler).toHaveBeenCalledOnce();
378
378
  expect(item.ack).toHaveBeenCalledTimes(2);
379
379
  });
380
- it('does not acknowledge interactive envelopes a second time on the generic event stream', async ()=>{
380
+ it("does not acknowledge interactive envelopes a second time on the generic event stream", async ()=>{
381
381
  const socket = new FakeSocketClient();
382
382
  const adapter = new SlackAdapter(config, {
383
383
  socketClient: socket,
@@ -389,15 +389,15 @@ describe('SlackAdapter', ()=>{
389
389
  });
390
390
  await adapter.start();
391
391
  const ack = vi.fn().mockResolvedValue(undefined);
392
- socket.emit('slack_event', {
393
- type: 'interactive',
392
+ socket.emit("slack_event", {
393
+ type: "interactive",
394
394
  ack,
395
395
  body: {}
396
396
  });
397
397
  await new Promise((resolve)=>setTimeout(resolve, 0));
398
398
  expect(ack).not.toHaveBeenCalled();
399
399
  });
400
- it('acknowledges malformed and wrong-workspace block actions without delivery', async ()=>{
400
+ it("acknowledges malformed and wrong-workspace block actions without delivery", async ()=>{
401
401
  const socket = new FakeSocketClient();
402
402
  const handler = vi.fn();
403
403
  const adapter = new SlackAdapter(config, {
@@ -411,12 +411,12 @@ describe('SlackAdapter', ()=>{
411
411
  adapter.onInteraction(handler);
412
412
  await adapter.start();
413
413
  const ack = vi.fn().mockResolvedValue(undefined);
414
- socket.emit('interactive', {
414
+ socket.emit("interactive", {
415
415
  ack,
416
416
  body: {
417
- type: 'block_actions',
417
+ type: "block_actions",
418
418
  team: {
419
- id: 'T999'
419
+ id: "T999"
420
420
  },
421
421
  actions: []
422
422
  }
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../../src/__tests__/adapters/slack/SlackAdapter.test.ts"],"sourcesContent":["import { EventEmitter } from 'node:events';\nimport { SlackAdapter, validateSlackAppToken, validateSlackCredentials } from '../../../adapters/slack/SlackAdapter.js';\nimport type { SlackConfig } from '../../../types.js';\n\nclass FakeSocketClient extends EventEmitter {\n start = vi.fn().mockResolvedValue(undefined);\n disconnect = vi.fn().mockResolvedValue(undefined);\n}\n\nconst config: SlackConfig = {\n appToken: 'xapp-fake',\n botToken: 'xoxb-fake',\n appId: 'A123',\n botUserId: 'U-BOT',\n workspaceId: 'T123',\n transport: 'socket-mode',\n audience: 'dm',\n};\n\nfunction envelope(overrides: Record<string, unknown> = {}) {\n return {\n type: 'events_api',\n ack: vi.fn().mockResolvedValue(undefined),\n body: {\n team_id: 'T123',\n event_id: 'Ev123',\n event: {\n type: 'message',\n channel_type: 'im',\n channel: 'D123',\n user: 'U123',\n text: 'run tests',\n ts: '100.1',\n event_ts: '100.1',\n ...overrides,\n },\n },\n };\n}\n\ndescribe('SlackAdapter', () => {\n it('validates bot identity without returning either credential', async () => {\n const authTest = vi.fn().mockResolvedValue({ ok: true, app_id: 'A123', user_id: 'U-BOT', team_id: 'T123', team: 'Sandbox' });\n await expect(validateSlackCredentials('xoxb-fake', { authTest })).resolves.toEqual({\n appId: 'A123', botUserId: 'U-BOT', workspaceId: 'T123', workspaceName: 'Sandbox',\n });\n });\n\n it('accepts Slack documented bot identity without an app ID', async () => {\n const authTest = vi.fn().mockResolvedValue({\n ok: true,\n bot_id: 'B123',\n user_id: 'U-BOT',\n team_id: 'T123',\n team: 'Sandbox',\n });\n await expect(validateSlackCredentials('xoxb-fake', { authTest })).resolves.toEqual({\n botUserId: 'U-BOT',\n workspaceId: 'T123',\n workspaceName: 'Sandbox',\n });\n });\n\n it('rejects incomplete Slack credential identity', async () => {\n const authTest = vi.fn().mockResolvedValue({ ok: true, team_id: 'T123' });\n await expect(validateSlackCredentials('xoxb-fake', { authTest })).rejects.toThrow('Slack bot token returned incomplete identity');\n });\n\n it('validates an app token through apps.connections.open', async () => {\n const open = vi.fn().mockResolvedValue({ ok: true, url: 'wss://wss-primary.slack.com/link/' });\n await expect(validateSlackAppToken('xapp-fake', { open })).resolves.toBeUndefined();\n expect(open).toHaveBeenCalledOnce();\n });\n\n it('rejects an app token that cannot open Socket Mode', async () => {\n const open = vi.fn().mockResolvedValue({ ok: false });\n await expect(validateSlackAppToken('xapp-fake', { open })).rejects.toThrow('Slack app token cannot open Socket Mode');\n });\n\n it('starts and stops the official Socket Mode client and reports health', async () => {\n const socket = new FakeSocketClient();\n const adapter = new SlackAdapter(config, { socketClient: socket, webClient: { chat: { postMessage: vi.fn() } } });\n expect(await adapter.isHealthy()).toBe(false);\n await adapter.start();\n expect(socket.start).toHaveBeenCalledOnce();\n expect(await adapter.isHealthy()).toBe(true);\n await adapter.stop();\n expect(socket.disconnect).toHaveBeenCalledOnce();\n expect(await adapter.isHealthy()).toBe(false);\n });\n\n it('does not register duplicate listeners across restarts', async () => {\n const socket = new FakeSocketClient();\n const adapter = new SlackAdapter(config, { socketClient: socket, webClient: { chat: { postMessage: vi.fn() } } });\n await adapter.start();\n await adapter.stop();\n await adapter.start();\n expect(socket.listenerCount('slack_event')).toBe(1);\n expect(socket.listenerCount('interactive')).toBe(1);\n socket.emit('disconnected');\n expect(await adapter.isHealthy()).toBe(false);\n socket.emit('connected');\n expect(await adapter.isHealthy()).toBe(true);\n });\n\n it('acknowledges before delivering one normalized workspace DM event', async () => {\n const socket = new FakeSocketClient();\n const adapter = new SlackAdapter(config, { socketClient: socket, webClient: { chat: { postMessage: vi.fn() } } });\n const order: string[] = [];\n adapter.onMessage(vi.fn(async (message) => {\n order.push('handler');\n expect(message).toMatchObject({\n channelType: 'slack', chatId: 'D123', userId: 'U123', workspaceId: 'T123', messageId: 'Ev123', text: 'run tests',\n });\n }));\n await adapter.start();\n const item = envelope();\n item.ack.mockImplementation(async () => { order.push('ack'); });\n socket.emit('slack_event', item);\n await vi.waitFor(() => expect(order).toEqual(['ack', 'handler']));\n socket.emit('slack_event', item);\n await new Promise((resolve) => setTimeout(resolve, 0));\n expect(order).toEqual(['ack', 'handler', 'ack']);\n });\n\n it.each([\n ['wrong workspace', {}, { team_id: 'T999' }],\n ['bot message', { bot_id: 'B123' }, {}],\n ['self message', { user: 'U-BOT' }, {}],\n ['subtype', { subtype: 'message_changed' }, {}],\n ['non-DM', { channel_type: 'channel' }, {}],\n ])('ignores %s events', async (_name, eventOverrides, bodyOverrides) => {\n const socket = new FakeSocketClient();\n const handler = vi.fn();\n const adapter = new SlackAdapter(config, { socketClient: socket, webClient: { chat: { postMessage: vi.fn() } } });\n adapter.onMessage(handler);\n await adapter.start();\n const item = envelope(eventOverrides);\n Object.assign(item.body, bodyOverrides);\n socket.emit('slack_event', item);\n await vi.waitFor(() => expect(item.ack).toHaveBeenCalled());\n expect(handler).not.toHaveBeenCalled();\n });\n\n it('delivers an ordinary workspace DM immediately without pairing', async () => {\n const socket = new FakeSocketClient();\n const handler = vi.fn();\n const adapter = new SlackAdapter(config, {\n socketClient: socket,\n webClient: { chat: { postMessage: vi.fn() } },\n });\n adapter.onMessage(handler);\n await adapter.start();\n\n socket.emit('slack_event', envelope({ text: 'run tests' }));\n\n await vi.waitFor(() => expect(handler).toHaveBeenCalledWith(expect.objectContaining({\n chatId: 'D123', userId: 'U123', text: 'run tests', workspaceId: 'T123',\n })));\n });\n\n it('sends an accessible Block Kit question and finalizes its actions', async () => {\n const socket = new FakeSocketClient();\n const postMessage = vi.fn().mockResolvedValue({ ok: true, ts: '400.1' });\n const update = vi.fn().mockResolvedValue({ ok: true });\n const adapter = new SlackAdapter(config, { socketClient: socket, webClient: { chat: { postMessage, update } } });\n await expect(adapter.sendQuestion('D123', {\n id: 'q1', header: 'Scope', question: 'Choose one', allowSkip: true,\n options: [{ label: 'Safe', description: 'Read only', value: '1' }],\n })).resolves.toEqual({ messageId: '400.1' });\n expect(postMessage).toHaveBeenCalledWith(expect.objectContaining({\n channel: 'D123', text: 'Scope: Choose one', blocks: expect.any(Array),\n }));\n await adapter.finalizeInteraction('D123', '400.1');\n expect(update).toHaveBeenCalledWith({ channel: 'D123', ts: '400.1', blocks: [] });\n });\n\n it('escapes Slack control syntax in question fallback text', async () => {\n const postMessage = vi.fn().mockResolvedValue({ ok: true, ts: '400.1' });\n const adapter = new SlackAdapter(config, {\n socketClient: new FakeSocketClient(),\n webClient: { chat: { postMessage } },\n });\n await adapter.sendQuestion('D123', {\n id: 'q1', header: '<!channel>', question: 'Choose <one> & continue', allowSkip: false,\n options: [{ label: 'Safe', value: '1' }],\n });\n expect(postMessage).toHaveBeenCalledWith(expect.objectContaining({\n text: '&lt;!channel&gt;: Choose &lt;one&gt; &amp; continue',\n }));\n });\n\n it('acknowledges and delivers one valid workspace block action', async () => {\n const socket = new FakeSocketClient();\n const handler = vi.fn();\n const adapter = new SlackAdapter(config, { socketClient: socket, webClient: { chat: { postMessage: vi.fn() } } });\n adapter.onInteraction(handler);\n await adapter.start();\n const item = {\n ack: vi.fn().mockResolvedValue(undefined),\n body: {\n type: 'block_actions', team: { id: 'T123' }, user: { id: 'U123' },\n channel: { id: 'D123' }, container: { message_ts: '400.1' },\n actions: [{ action_id: 'ai_devkit_question', action_ts: '401.1', value: 'q1:1' }],\n },\n };\n socket.emit('interactive', item);\n await vi.waitFor(() => expect(handler).toHaveBeenCalledOnce());\n expect(item.ack).toHaveBeenCalledOnce();\n expect(handler).toHaveBeenCalledWith(expect.objectContaining({\n workspaceId: 'T123', chatId: 'D123', userId: 'U123', messageId: '400.1', value: 'q1:1',\n }));\n socket.emit('interactive', item);\n await new Promise((resolve) => setTimeout(resolve, 0));\n expect(handler).toHaveBeenCalledOnce();\n expect(item.ack).toHaveBeenCalledTimes(2);\n });\n\n it('does not acknowledge interactive envelopes a second time on the generic event stream', async () => {\n const socket = new FakeSocketClient();\n const adapter = new SlackAdapter(config, { socketClient: socket, webClient: { chat: { postMessage: vi.fn() } } });\n await adapter.start();\n const ack = vi.fn().mockResolvedValue(undefined);\n socket.emit('slack_event', { type: 'interactive', ack, body: {} });\n await new Promise((resolve) => setTimeout(resolve, 0));\n expect(ack).not.toHaveBeenCalled();\n });\n\n it('acknowledges malformed and wrong-workspace block actions without delivery', async () => {\n const socket = new FakeSocketClient();\n const handler = vi.fn();\n const adapter = new SlackAdapter(config, { socketClient: socket, webClient: { chat: { postMessage: vi.fn() } } });\n adapter.onInteraction(handler);\n await adapter.start();\n const ack = vi.fn().mockResolvedValue(undefined);\n socket.emit('interactive', { ack, body: { type: 'block_actions', team: { id: 'T999' }, actions: [] } });\n await vi.waitFor(() => expect(ack).toHaveBeenCalled());\n expect(handler).not.toHaveBeenCalled();\n });\n});\n"],"names":["EventEmitter","SlackAdapter","validateSlackAppToken","validateSlackCredentials","FakeSocketClient","start","vi","fn","mockResolvedValue","undefined","disconnect","config","appToken","botToken","appId","botUserId","workspaceId","transport","audience","envelope","overrides","type","ack","body","team_id","event_id","event","channel_type","channel","user","text","ts","event_ts","describe","it","authTest","ok","app_id","user_id","team","expect","resolves","toEqual","workspaceName","bot_id","rejects","toThrow","open","url","toBeUndefined","toHaveBeenCalledOnce","socket","adapter","socketClient","webClient","chat","postMessage","isHealthy","toBe","stop","listenerCount","emit","order","onMessage","message","push","toMatchObject","channelType","chatId","userId","messageId","item","mockImplementation","waitFor","Promise","resolve","setTimeout","each","subtype","_name","eventOverrides","bodyOverrides","handler","Object","assign","toHaveBeenCalled","not","toHaveBeenCalledWith","objectContaining","update","sendQuestion","id","header","question","allowSkip","options","label","description","value","blocks","any","Array","finalizeInteraction","onInteraction","container","message_ts","actions","action_id","action_ts","toHaveBeenCalledTimes"],"mappings":"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"}
1
+ {"version":3,"sources":["../../../../src/__tests__/adapters/slack/SlackAdapter.test.ts"],"sourcesContent":["import { EventEmitter } from \"node:events\";\nimport {\n SlackAdapter,\n validateSlackAppToken,\n validateSlackCredentials,\n} from \"../../../adapters/slack/SlackAdapter.js\";\nimport type { SlackConfig } from \"../../../types.js\";\n\nclass FakeSocketClient extends EventEmitter {\n start = vi.fn().mockResolvedValue(undefined);\n disconnect = vi.fn().mockResolvedValue(undefined);\n}\n\nconst config: SlackConfig = {\n appToken: \"xapp-fake\",\n botToken: \"xoxb-fake\",\n appId: \"A123\",\n botUserId: \"U-BOT\",\n workspaceId: \"T123\",\n transport: \"socket-mode\",\n audience: \"dm\",\n};\n\nfunction envelope(overrides: Record<string, unknown> = {}) {\n return {\n type: \"events_api\",\n ack: vi.fn().mockResolvedValue(undefined),\n body: {\n team_id: \"T123\",\n event_id: \"Ev123\",\n event: {\n type: \"message\",\n channel_type: \"im\",\n channel: \"D123\",\n user: \"U123\",\n text: \"run tests\",\n ts: \"100.1\",\n event_ts: \"100.1\",\n ...overrides,\n },\n },\n };\n}\n\ndescribe(\"SlackAdapter\", () => {\n it(\"validates bot identity without returning either credential\", async () => {\n const authTest = vi.fn().mockResolvedValue({\n ok: true,\n app_id: \"A123\",\n user_id: \"U-BOT\",\n team_id: \"T123\",\n team: \"Sandbox\",\n });\n await expect(validateSlackCredentials(\"xoxb-fake\", { authTest })).resolves.toEqual({\n appId: \"A123\",\n botUserId: \"U-BOT\",\n workspaceId: \"T123\",\n workspaceName: \"Sandbox\",\n });\n });\n\n it(\"accepts Slack documented bot identity without an app ID\", async () => {\n const authTest = vi.fn().mockResolvedValue({\n ok: true,\n bot_id: \"B123\",\n user_id: \"U-BOT\",\n team_id: \"T123\",\n team: \"Sandbox\",\n });\n await expect(validateSlackCredentials(\"xoxb-fake\", { authTest })).resolves.toEqual({\n botUserId: \"U-BOT\",\n workspaceId: \"T123\",\n workspaceName: \"Sandbox\",\n });\n });\n\n it(\"rejects incomplete Slack credential identity\", async () => {\n const authTest = vi.fn().mockResolvedValue({ ok: true, team_id: \"T123\" });\n await expect(validateSlackCredentials(\"xoxb-fake\", { authTest })).rejects.toThrow(\n \"Slack bot token returned incomplete identity\",\n );\n });\n\n it(\"validates an app token through apps.connections.open\", async () => {\n const open = vi.fn().mockResolvedValue({ ok: true, url: \"wss://wss-primary.slack.com/link/\" });\n await expect(validateSlackAppToken(\"xapp-fake\", { open })).resolves.toBeUndefined();\n expect(open).toHaveBeenCalledOnce();\n });\n\n it(\"rejects an app token that cannot open Socket Mode\", async () => {\n const open = vi.fn().mockResolvedValue({ ok: false });\n await expect(validateSlackAppToken(\"xapp-fake\", { open })).rejects.toThrow(\n \"Slack app token cannot open Socket Mode\",\n );\n });\n\n it(\"starts and stops the official Socket Mode client and reports health\", async () => {\n const socket = new FakeSocketClient();\n const adapter = new SlackAdapter(config, {\n socketClient: socket,\n webClient: { chat: { postMessage: vi.fn() } },\n });\n expect(await adapter.isHealthy()).toBe(false);\n await adapter.start();\n expect(socket.start).toHaveBeenCalledOnce();\n expect(await adapter.isHealthy()).toBe(true);\n await adapter.stop();\n expect(socket.disconnect).toHaveBeenCalledOnce();\n expect(await adapter.isHealthy()).toBe(false);\n });\n\n it(\"does not register duplicate listeners across restarts\", async () => {\n const socket = new FakeSocketClient();\n const adapter = new SlackAdapter(config, {\n socketClient: socket,\n webClient: { chat: { postMessage: vi.fn() } },\n });\n await adapter.start();\n await adapter.stop();\n await adapter.start();\n expect(socket.listenerCount(\"slack_event\")).toBe(1);\n expect(socket.listenerCount(\"interactive\")).toBe(1);\n socket.emit(\"disconnected\");\n expect(await adapter.isHealthy()).toBe(false);\n socket.emit(\"connected\");\n expect(await adapter.isHealthy()).toBe(true);\n });\n\n it(\"acknowledges before delivering one normalized workspace DM event\", async () => {\n const socket = new FakeSocketClient();\n const adapter = new SlackAdapter(config, {\n socketClient: socket,\n webClient: { chat: { postMessage: vi.fn() } },\n });\n const order: string[] = [];\n adapter.onMessage(\n vi.fn(async (message) => {\n order.push(\"handler\");\n expect(message).toMatchObject({\n channelType: \"slack\",\n chatId: \"D123\",\n userId: \"U123\",\n workspaceId: \"T123\",\n messageId: \"Ev123\",\n text: \"run tests\",\n });\n }),\n );\n await adapter.start();\n const item = envelope();\n item.ack.mockImplementation(async () => {\n order.push(\"ack\");\n });\n socket.emit(\"slack_event\", item);\n await vi.waitFor(() => expect(order).toEqual([\"ack\", \"handler\"]));\n socket.emit(\"slack_event\", item);\n await new Promise((resolve) => setTimeout(resolve, 0));\n expect(order).toEqual([\"ack\", \"handler\", \"ack\"]);\n });\n\n it.each([\n [\"wrong workspace\", {}, { team_id: \"T999\" }],\n [\"bot message\", { bot_id: \"B123\" }, {}],\n [\"self message\", { user: \"U-BOT\" }, {}],\n [\"subtype\", { subtype: \"message_changed\" }, {}],\n [\"non-DM\", { channel_type: \"channel\" }, {}],\n ])(\"ignores %s events\", async (_name, eventOverrides, bodyOverrides) => {\n const socket = new FakeSocketClient();\n const handler = vi.fn();\n const adapter = new SlackAdapter(config, {\n socketClient: socket,\n webClient: { chat: { postMessage: vi.fn() } },\n });\n adapter.onMessage(handler);\n await adapter.start();\n const item = envelope(eventOverrides);\n Object.assign(item.body, bodyOverrides);\n socket.emit(\"slack_event\", item);\n await vi.waitFor(() => expect(item.ack).toHaveBeenCalled());\n expect(handler).not.toHaveBeenCalled();\n });\n\n it(\"delivers an ordinary workspace DM immediately without pairing\", async () => {\n const socket = new FakeSocketClient();\n const handler = vi.fn();\n const adapter = new SlackAdapter(config, {\n socketClient: socket,\n webClient: { chat: { postMessage: vi.fn() } },\n });\n adapter.onMessage(handler);\n await adapter.start();\n\n socket.emit(\"slack_event\", envelope({ text: \"run tests\" }));\n\n await vi.waitFor(() =>\n expect(handler).toHaveBeenCalledWith(\n expect.objectContaining({\n chatId: \"D123\",\n userId: \"U123\",\n text: \"run tests\",\n workspaceId: \"T123\",\n }),\n ),\n );\n });\n\n it(\"sends an accessible Block Kit question and finalizes its actions\", async () => {\n const socket = new FakeSocketClient();\n const postMessage = vi.fn().mockResolvedValue({ ok: true, ts: \"400.1\" });\n const update = vi.fn().mockResolvedValue({ ok: true });\n const adapter = new SlackAdapter(config, {\n socketClient: socket,\n webClient: { chat: { postMessage, update } },\n });\n await expect(\n adapter.sendQuestion(\"D123\", {\n id: \"q1\",\n header: \"Scope\",\n question: \"Choose one\",\n allowSkip: true,\n options: [{ label: \"Safe\", description: \"Read only\", value: \"1\" }],\n }),\n ).resolves.toEqual({ messageId: \"400.1\" });\n expect(postMessage).toHaveBeenCalledWith(\n expect.objectContaining({\n channel: \"D123\",\n text: \"Scope: Choose one\",\n blocks: expect.any(Array),\n }),\n );\n await adapter.finalizeInteraction(\"D123\", \"400.1\");\n expect(update).toHaveBeenCalledWith({ channel: \"D123\", ts: \"400.1\", blocks: [] });\n });\n\n it(\"escapes Slack control syntax in question fallback text\", async () => {\n const postMessage = vi.fn().mockResolvedValue({ ok: true, ts: \"400.1\" });\n const adapter = new SlackAdapter(config, {\n socketClient: new FakeSocketClient(),\n webClient: { chat: { postMessage } },\n });\n await adapter.sendQuestion(\"D123\", {\n id: \"q1\",\n header: \"<!channel>\",\n question: \"Choose <one> & continue\",\n allowSkip: false,\n options: [{ label: \"Safe\", value: \"1\" }],\n });\n expect(postMessage).toHaveBeenCalledWith(\n expect.objectContaining({\n text: \"&lt;!channel&gt;: Choose &lt;one&gt; &amp; continue\",\n }),\n );\n });\n\n it(\"acknowledges and delivers one valid workspace block action\", async () => {\n const socket = new FakeSocketClient();\n const handler = vi.fn();\n const adapter = new SlackAdapter(config, {\n socketClient: socket,\n webClient: { chat: { postMessage: vi.fn() } },\n });\n adapter.onInteraction(handler);\n await adapter.start();\n const item = {\n ack: vi.fn().mockResolvedValue(undefined),\n body: {\n type: \"block_actions\",\n team: { id: \"T123\" },\n user: { id: \"U123\" },\n channel: { id: \"D123\" },\n container: { message_ts: \"400.1\" },\n actions: [{ action_id: \"ai_devkit_question\", action_ts: \"401.1\", value: \"q1:1\" }],\n },\n };\n socket.emit(\"interactive\", item);\n await vi.waitFor(() => expect(handler).toHaveBeenCalledOnce());\n expect(item.ack).toHaveBeenCalledOnce();\n expect(handler).toHaveBeenCalledWith(\n expect.objectContaining({\n workspaceId: \"T123\",\n chatId: \"D123\",\n userId: \"U123\",\n messageId: \"400.1\",\n value: \"q1:1\",\n }),\n );\n socket.emit(\"interactive\", item);\n await new Promise((resolve) => setTimeout(resolve, 0));\n expect(handler).toHaveBeenCalledOnce();\n expect(item.ack).toHaveBeenCalledTimes(2);\n });\n\n it(\"does not acknowledge interactive envelopes a second time on the generic event stream\", async () => {\n const socket = new FakeSocketClient();\n const adapter = new SlackAdapter(config, {\n socketClient: socket,\n webClient: { chat: { postMessage: vi.fn() } },\n });\n await adapter.start();\n const ack = vi.fn().mockResolvedValue(undefined);\n socket.emit(\"slack_event\", { type: \"interactive\", ack, body: {} });\n await new Promise((resolve) => setTimeout(resolve, 0));\n expect(ack).not.toHaveBeenCalled();\n });\n\n it(\"acknowledges malformed and wrong-workspace block actions without delivery\", async () => {\n const socket = new FakeSocketClient();\n const handler = vi.fn();\n const adapter = new SlackAdapter(config, {\n socketClient: socket,\n webClient: { chat: { postMessage: vi.fn() } },\n });\n adapter.onInteraction(handler);\n await adapter.start();\n const ack = vi.fn().mockResolvedValue(undefined);\n socket.emit(\"interactive\", {\n ack,\n body: { type: \"block_actions\", team: { id: \"T999\" }, actions: [] },\n });\n await vi.waitFor(() => expect(ack).toHaveBeenCalled());\n expect(handler).not.toHaveBeenCalled();\n });\n});\n"],"names":["EventEmitter","SlackAdapter","validateSlackAppToken","validateSlackCredentials","FakeSocketClient","start","vi","fn","mockResolvedValue","undefined","disconnect","config","appToken","botToken","appId","botUserId","workspaceId","transport","audience","envelope","overrides","type","ack","body","team_id","event_id","event","channel_type","channel","user","text","ts","event_ts","describe","it","authTest","ok","app_id","user_id","team","expect","resolves","toEqual","workspaceName","bot_id","rejects","toThrow","open","url","toBeUndefined","toHaveBeenCalledOnce","socket","adapter","socketClient","webClient","chat","postMessage","isHealthy","toBe","stop","listenerCount","emit","order","onMessage","message","push","toMatchObject","channelType","chatId","userId","messageId","item","mockImplementation","waitFor","Promise","resolve","setTimeout","each","subtype","_name","eventOverrides","bodyOverrides","handler","Object","assign","toHaveBeenCalled","not","toHaveBeenCalledWith","objectContaining","update","sendQuestion","id","header","question","allowSkip","options","label","description","value","blocks","any","Array","finalizeInteraction","onInteraction","container","message_ts","actions","action_id","action_ts","toHaveBeenCalledTimes"],"mappings":"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"}