@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,8 +1,8 @@
1
- import * as telegrafModule from 'telegraf';
2
- import * as telegramHtml from '../../../adapters/telegram/telegramHtml.js';
3
- import { TelegramAdapter } from '../../../adapters/telegram/TelegramAdapter.js';
1
+ import * as telegrafModule from "telegraf";
2
+ import * as telegramHtml from "../../../adapters/telegram/telegramHtml.js";
3
+ import { TelegramAdapter } from "../../../adapters/telegram/TelegramAdapter.js";
4
4
  // Mock telegraf
5
- vi.mock('telegraf', ()=>{
5
+ vi.mock("telegraf", ()=>{
6
6
  const handlers = {};
7
7
  const mockBot = {
8
8
  launch: vi.fn().mockResolvedValue(undefined),
@@ -15,7 +15,7 @@ vi.mock('telegraf', ()=>{
15
15
  editMessageReplyMarkup: vi.fn().mockResolvedValue(undefined),
16
16
  answerCbQuery: vi.fn().mockResolvedValue(undefined),
17
17
  getMe: vi.fn().mockResolvedValue({
18
- username: 'test_bot'
18
+ username: "test_bot"
19
19
  })
20
20
  },
21
21
  _handlers: handlers,
@@ -33,15 +33,15 @@ vi.mock('telegraf', ()=>{
33
33
  },
34
34
  reply: vi.fn().mockResolvedValue(undefined)
35
35
  };
36
- if (handlers['text']) {
37
- await handlers['text'](ctx);
36
+ if (handlers["text"]) {
37
+ await handlers["text"](ctx);
38
38
  }
39
39
  return ctx;
40
40
  },
41
41
  _triggerCallback: async (chatId, userId, data, messageId = 555)=>{
42
42
  const ctx = {
43
43
  callbackQuery: {
44
- id: 'cbq-test',
44
+ id: "cbq-test",
45
45
  data,
46
46
  message: {
47
47
  message_id: messageId,
@@ -55,8 +55,8 @@ vi.mock('telegraf', ()=>{
55
55
  },
56
56
  answerCbQuery: vi.fn().mockResolvedValue(undefined)
57
57
  };
58
- if (handlers['callback_query']) {
59
- await handlers['callback_query'](ctx);
58
+ if (handlers["callback_query"]) {
59
+ await handlers["callback_query"](ctx);
60
60
  }
61
61
  return ctx;
62
62
  }
@@ -77,213 +77,213 @@ function expectHtmlChunkWithinLimit(chunk) {
77
77
  expect((chunk.match(/<pre>/g) ?? []).length).toBe((chunk.match(/<\/pre>/g) ?? []).length);
78
78
  expect((chunk.match(/<code(?: class="language-[^"]+")?>/g) ?? []).length).toBe((chunk.match(/<\/code>/g) ?? []).length);
79
79
  }
80
- describe('TelegramAdapter', ()=>{
80
+ describe("TelegramAdapter", ()=>{
81
81
  let adapter;
82
82
  beforeEach(()=>{
83
83
  vi.clearAllMocks();
84
84
  adapter = new TelegramAdapter({
85
- botToken: 'test-token-123'
85
+ botToken: "test-token-123"
86
86
  });
87
87
  });
88
- describe('type', ()=>{
88
+ describe("type", ()=>{
89
89
  it('should return "telegram"', ()=>{
90
- expect(adapter.type).toBe('telegram');
90
+ expect(adapter.type).toBe("telegram");
91
91
  });
92
92
  });
93
- describe('start', ()=>{
94
- it('should launch the telegraf bot', async ()=>{
93
+ describe("start", ()=>{
94
+ it("should launch the telegraf bot", async ()=>{
95
95
  const bot = getMockBot();
96
96
  await adapter.start();
97
97
  expect(bot.launch).toHaveBeenCalled();
98
98
  });
99
99
  });
100
- describe('stop', ()=>{
101
- it('should stop the telegraf bot', async ()=>{
100
+ describe("stop", ()=>{
101
+ it("should stop the telegraf bot", async ()=>{
102
102
  const bot = getMockBot();
103
103
  await adapter.start();
104
104
  await adapter.stop();
105
105
  expect(bot.stop).toHaveBeenCalled();
106
106
  });
107
107
  });
108
- describe('onMessage', ()=>{
109
- it('should silently ignore messages when no handler is registered', async ()=>{
108
+ describe("onMessage", ()=>{
109
+ it("should silently ignore messages when no handler is registered", async ()=>{
110
110
  // Don't register a handler
111
111
  await adapter.start();
112
112
  const bot = getMockBot();
113
- const ctx = await bot._triggerText(12345, 67890, 'hello');
113
+ const ctx = await bot._triggerText(12345, 67890, "hello");
114
114
  // Should not throw or reply
115
115
  expect(ctx.reply).not.toHaveBeenCalled();
116
116
  });
117
- it('should handle non-Error thrown by handler', async ()=>{
118
- const handler = vi.fn().mockRejectedValue('string error');
117
+ it("should handle non-Error thrown by handler", async ()=>{
118
+ const handler = vi.fn().mockRejectedValue("string error");
119
119
  adapter.onMessage(handler);
120
120
  await adapter.start();
121
121
  const bot = getMockBot();
122
- const ctx = await bot._triggerText(12345, 67890, 'hello');
123
- expect(ctx.reply).toHaveBeenCalledWith('Error processing message: Unknown error');
122
+ const ctx = await bot._triggerText(12345, 67890, "hello");
123
+ expect(ctx.reply).toHaveBeenCalledWith("Error processing message: Unknown error");
124
124
  });
125
- it('should call handler with IncomingMessage on incoming text', async ()=>{
125
+ it("should call handler with IncomingMessage on incoming text", async ()=>{
126
126
  const handler = vi.fn().mockResolvedValue(undefined);
127
127
  adapter.onMessage(handler);
128
128
  await adapter.start();
129
129
  const bot = getMockBot();
130
- await bot._triggerText(12345, 67890, 'hello agent');
130
+ await bot._triggerText(12345, 67890, "hello agent");
131
131
  expect(handler).toHaveBeenCalledTimes(1);
132
132
  const msg = handler.mock.calls[0][0];
133
- expect(msg.channelType).toBe('telegram');
134
- expect(msg.chatId).toBe('12345');
135
- expect(msg.userId).toBe('67890');
136
- expect(msg.text).toBe('hello agent');
133
+ expect(msg.channelType).toBe("telegram");
134
+ expect(msg.chatId).toBe("12345");
135
+ expect(msg.userId).toBe("67890");
136
+ expect(msg.text).toBe("hello agent");
137
137
  expect(msg.timestamp).toBeInstanceOf(Date);
138
138
  });
139
- it('should handle handler errors gracefully', async ()=>{
140
- const handler = vi.fn().mockRejectedValue(new Error('handler failed'));
139
+ it("should handle handler errors gracefully", async ()=>{
140
+ const handler = vi.fn().mockRejectedValue(new Error("handler failed"));
141
141
  adapter.onMessage(handler);
142
142
  await adapter.start();
143
143
  const bot = getMockBot();
144
- const ctx = await bot._triggerText(12345, 67890, 'hello');
144
+ const ctx = await bot._triggerText(12345, 67890, "hello");
145
145
  // Should not throw, and should reply with error
146
- expect(ctx.reply).toHaveBeenCalledWith(expect.stringContaining('Error processing message'));
146
+ expect(ctx.reply).toHaveBeenCalledWith(expect.stringContaining("Error processing message"));
147
147
  });
148
148
  });
149
- describe('sendMessage', ()=>{
150
- it('should send plain text with parse_mode HTML', async ()=>{
149
+ describe("sendMessage", ()=>{
150
+ it("should send plain text with parse_mode HTML", async ()=>{
151
151
  const bot = getMockBot();
152
- await adapter.sendMessage('12345', 'hello from bot');
153
- expect(bot.telegram.sendMessage).toHaveBeenCalledWith('12345', 'hello from bot', {
154
- parse_mode: 'HTML'
152
+ await adapter.sendMessage("12345", "hello from bot");
153
+ expect(bot.telegram.sendMessage).toHaveBeenCalledWith("12345", "hello from bot", {
154
+ parse_mode: "HTML"
155
155
  });
156
156
  });
157
- it('should render markdown as Telegram HTML', async ()=>{
157
+ it("should render markdown as Telegram HTML", async ()=>{
158
158
  const bot = getMockBot();
159
- await adapter.sendMessage('12345', '**bold** and *italic* and `code`');
159
+ await adapter.sendMessage("12345", "**bold** and *italic* and `code`");
160
160
  const sent = bot.telegram.sendMessage.mock.calls[0][1];
161
- expect(sent).toContain('<b>bold</b>');
162
- expect(sent).toContain('<i>italic</i>');
163
- expect(sent).toContain('<code>code</code>');
161
+ expect(sent).toContain("<b>bold</b>");
162
+ expect(sent).toContain("<i>italic</i>");
163
+ expect(sent).toContain("<code>code</code>");
164
164
  expect(bot.telegram.sendMessage.mock.calls[0][2]).toEqual({
165
- parse_mode: 'HTML'
165
+ parse_mode: "HTML"
166
166
  });
167
167
  });
168
- it('should chunk messages exceeding 4096 chars', async ()=>{
168
+ it("should chunk messages exceeding 4096 chars", async ()=>{
169
169
  const bot = getMockBot();
170
- const line = 'A'.repeat(100) + '\n';
170
+ const line = "A".repeat(100) + "\n";
171
171
  const longMessage = line.repeat(50); // 5050 chars
172
- await adapter.sendMessage('12345', longMessage);
172
+ await adapter.sendMessage("12345", longMessage);
173
173
  expect(bot.telegram.sendMessage.mock.calls.length).toBeGreaterThan(1);
174
174
  for (const call of bot.telegram.sendMessage.mock.calls){
175
175
  expect(call[1].length).toBeLessThanOrEqual(4096);
176
176
  expect(call[2]).toEqual({
177
- parse_mode: 'HTML'
177
+ parse_mode: "HTML"
178
178
  });
179
179
  }
180
180
  });
181
- it('should split long fenced code into independently valid HTML code blocks', async ()=>{
181
+ it("should split long fenced code into independently valid HTML code blocks", async ()=>{
182
182
  const bot = getMockBot();
183
183
  const codeLine = 'const sample = "<code>tag</code>" && value < limit;\n';
184
184
  const longMessage = `Before\n\n\`\`\`ts\n${codeLine.repeat(100)}\`\`\`\n\nAfter`;
185
- await adapter.sendMessage('12345', longMessage);
185
+ await adapter.sendMessage("12345", longMessage);
186
186
  expect(bot.telegram.sendMessage.mock.calls.length).toBeGreaterThan(1);
187
187
  for (const call of bot.telegram.sendMessage.mock.calls){
188
188
  const chunk = call[1];
189
189
  expect(call[2]).toEqual({
190
- parse_mode: 'HTML'
190
+ parse_mode: "HTML"
191
191
  });
192
192
  expectHtmlChunkWithinLimit(chunk);
193
- if (chunk.includes('<pre>')) {
193
+ if (chunk.includes("<pre>")) {
194
194
  expect(chunk).toContain('<code class="language-ts">');
195
- expect(chunk).toContain('&lt;code&gt;tag&lt;/code&gt;');
195
+ expect(chunk).toContain("&lt;code&gt;tag&lt;/code&gt;");
196
196
  }
197
197
  }
198
198
  });
199
- it('should split nested list fenced code without sending partial HTML tags', async ()=>{
199
+ it("should split nested list fenced code without sending partial HTML tags", async ()=>{
200
200
  const bot = getMockBot();
201
201
  const codeLine = ' return "<code>tag</code>" && input < output;\n';
202
202
  const longMessage = [
203
- '- parent item',
204
- ' - child item with code',
205
- ' ```js',
203
+ "- parent item",
204
+ " - child item with code",
205
+ " ```js",
206
206
  codeLine.repeat(100).trimEnd(),
207
- ' ```',
208
- '- final item'
209
- ].join('\n');
210
- await adapter.sendMessage('12345', longMessage);
207
+ " ```",
208
+ "- final item"
209
+ ].join("\n");
210
+ await adapter.sendMessage("12345", longMessage);
211
211
  expect(bot.telegram.sendMessage.mock.calls.length).toBeGreaterThan(1);
212
212
  for (const call of bot.telegram.sendMessage.mock.calls){
213
213
  const chunk = call[1];
214
214
  expect(call[2]).toEqual({
215
- parse_mode: 'HTML'
215
+ parse_mode: "HTML"
216
216
  });
217
217
  expectHtmlChunkWithinLimit(chunk);
218
218
  }
219
219
  });
220
- it('should split long paragraphs at readable markdown boundaries', async ()=>{
220
+ it("should split long paragraphs at readable markdown boundaries", async ()=>{
221
221
  const bot = getMockBot();
222
- const sentence = 'This is a long sentence with enough words to make chunking choose sentence boundaries. ';
222
+ const sentence = "This is a long sentence with enough words to make chunking choose sentence boundaries. ";
223
223
  const longMessage = sentence.repeat(80);
224
- await adapter.sendMessage('12345', longMessage);
224
+ await adapter.sendMessage("12345", longMessage);
225
225
  expect(bot.telegram.sendMessage.mock.calls.length).toBeGreaterThan(1);
226
226
  for (const call of bot.telegram.sendMessage.mock.calls){
227
227
  const chunk = call[1];
228
228
  expect(call[2]).toEqual({
229
- parse_mode: 'HTML'
229
+ parse_mode: "HTML"
230
230
  });
231
231
  expectHtmlChunkWithinLimit(chunk);
232
- expect(chunk.endsWith('.') || chunk.endsWith('.\n\n') || chunk.endsWith(' ')).toBe(true);
232
+ expect(chunk.endsWith(".") || chunk.endsWith(".\n\n") || chunk.endsWith(" ")).toBe(true);
233
233
  }
234
234
  });
235
- it('should keep emoji chunks within the configured JavaScript string length limit', async ()=>{
235
+ it("should keep emoji chunks within the configured JavaScript string length limit", async ()=>{
236
236
  const bot = getMockBot();
237
- const longMessage = `Status ${'🧪'.repeat(2500)}`;
238
- await adapter.sendMessage('12345', longMessage);
237
+ const longMessage = `Status ${"🧪".repeat(2500)}`;
238
+ await adapter.sendMessage("12345", longMessage);
239
239
  expect(bot.telegram.sendMessage.mock.calls.length).toBeGreaterThan(1);
240
240
  for (const call of bot.telegram.sendMessage.mock.calls){
241
241
  expect(call[1].length).toBeLessThanOrEqual(4096);
242
242
  expect(call[2]).toEqual({
243
- parse_mode: 'HTML'
243
+ parse_mode: "HTML"
244
244
  });
245
245
  }
246
246
  });
247
- it('should hard split at 4096 when no newlines available', async ()=>{
247
+ it("should hard split at 4096 when no newlines available", async ()=>{
248
248
  const bot = getMockBot();
249
- const longMessage = 'A'.repeat(5000);
250
- await adapter.sendMessage('12345', longMessage);
249
+ const longMessage = "A".repeat(5000);
250
+ await adapter.sendMessage("12345", longMessage);
251
251
  expect(bot.telegram.sendMessage.mock.calls.length).toBe(2);
252
252
  expect(bot.telegram.sendMessage.mock.calls[0][1].length).toBe(4096);
253
253
  expect(bot.telegram.sendMessage.mock.calls[1][1].length).toBe(904);
254
254
  });
255
- it('should prefer paragraph (\\n\\n) over single \\n when chunking', async ()=>{
255
+ it("should prefer paragraph (\\n\\n) over single \\n when chunking", async ()=>{
256
256
  const bot = getMockBot();
257
257
  // 4 paragraphs of ~1500 chars each, total > 4096
258
- const paragraph = 'A'.repeat(1500);
258
+ const paragraph = "A".repeat(1500);
259
259
  const message = `${paragraph}\n\n${paragraph}\n\n${paragraph}\n\n${paragraph}`;
260
- await adapter.sendMessage('12345', message);
260
+ await adapter.sendMessage("12345", message);
261
261
  // First chunk should end at a \n\n boundary, not mid-paragraph
262
262
  const firstChunk = bot.telegram.sendMessage.mock.calls[0][1];
263
263
  expect(firstChunk).toBe(`${paragraph}\n\n${paragraph}`);
264
264
  });
265
- it('should send short messages in a single call', async ()=>{
265
+ it("should send short messages in a single call", async ()=>{
266
266
  const bot = getMockBot();
267
- await adapter.sendMessage('12345', 'short message');
267
+ await adapter.sendMessage("12345", "short message");
268
268
  expect(bot.telegram.sendMessage).toHaveBeenCalledTimes(1);
269
269
  });
270
- it('should retry as plain text when Telegram rejects HTML with parse-entities error', async ()=>{
270
+ it("should retry as plain text when Telegram rejects HTML with parse-entities error", async ()=>{
271
271
  const bot = getMockBot();
272
- const parseError = Object.assign(new Error('400: Bad Request'), {
273
- description: "Bad Request: can't parse entities: Unsupported start tag \"foo\""
272
+ const parseError = Object.assign(new Error("400: Bad Request"), {
273
+ description: 'Bad Request: can\'t parse entities: Unsupported start tag "foo"'
274
274
  });
275
275
  bot.telegram.sendMessage.mockRejectedValueOnce(parseError).mockResolvedValueOnce(undefined);
276
- await adapter.sendMessage('12345', '**hello**');
276
+ await adapter.sendMessage("12345", "**hello**");
277
277
  expect(bot.telegram.sendMessage).toHaveBeenCalledTimes(2);
278
278
  // First call: rendered HTML with parse_mode
279
279
  const [, htmlChunk, htmlOpts] = bot.telegram.sendMessage.mock.calls[0];
280
- expect(htmlChunk).toContain('<b>hello</b>');
280
+ expect(htmlChunk).toContain("<b>hello</b>");
281
281
  expect(htmlOpts).toEqual({
282
- parse_mode: 'HTML'
282
+ parse_mode: "HTML"
283
283
  });
284
284
  // Second call: same content, plain text (tags stripped, no parse_mode)
285
285
  const [, plainChunk, plainOpts] = bot.telegram.sendMessage.mock.calls[1];
286
- expect(plainChunk).toBe('hello');
286
+ expect(plainChunk).toBe("hello");
287
287
  expect(plainOpts).toBeUndefined();
288
288
  });
289
289
  it('should detect parse-entities error from "message" field too', async ()=>{
@@ -291,86 +291,86 @@ describe('TelegramAdapter', ()=>{
291
291
  // Some error shapes carry the marker on `message` rather than `description`
292
292
  const parseError = new Error("can't parse entities");
293
293
  bot.telegram.sendMessage.mockRejectedValueOnce(parseError).mockResolvedValueOnce(undefined);
294
- await adapter.sendMessage('12345', '**hi**');
294
+ await adapter.sendMessage("12345", "**hi**");
295
295
  expect(bot.telegram.sendMessage).toHaveBeenCalledTimes(2);
296
296
  });
297
- it('should decode HTML entities when falling back to plain text', async ()=>{
297
+ it("should decode HTML entities when falling back to plain text", async ()=>{
298
298
  const bot = getMockBot();
299
- const parseError = Object.assign(new Error('400'), {
299
+ const parseError = Object.assign(new Error("400"), {
300
300
  description: "Bad Request: can't parse entities"
301
301
  });
302
302
  bot.telegram.sendMessage.mockRejectedValueOnce(parseError).mockResolvedValueOnce(undefined);
303
303
  // Source has chars that escapeHtml encodes; fallback should decode them
304
- await adapter.sendMessage('12345', 'a < b && c > d');
304
+ await adapter.sendMessage("12345", "a < b && c > d");
305
305
  const [, plainChunk] = bot.telegram.sendMessage.mock.calls[1];
306
- expect(plainChunk).toContain('a < b && c > d');
307
- expect(plainChunk).not.toContain('&lt;');
308
- expect(plainChunk).not.toContain('&amp;');
306
+ expect(plainChunk).toContain("a < b && c > d");
307
+ expect(plainChunk).not.toContain("&lt;");
308
+ expect(plainChunk).not.toContain("&amp;");
309
309
  });
310
- it('should deliver nested lists without throwing', async ()=>{
310
+ it("should deliver nested lists without throwing", async ()=>{
311
311
  const bot = getMockBot();
312
- await adapter.sendMessage('12345', '- agent\n - Status');
312
+ await adapter.sendMessage("12345", "- agent\n - Status");
313
313
  expect(bot.telegram.sendMessage).toHaveBeenCalledTimes(1);
314
- expect(bot.telegram.sendMessage.mock.calls[0][1]).toContain('Status');
314
+ expect(bot.telegram.sendMessage.mock.calls[0][1]).toContain("Status");
315
315
  });
316
- it('should send plain text when markdown formatting throws', async ()=>{
316
+ it("should send plain text when markdown formatting throws", async ()=>{
317
317
  const bot = getMockBot();
318
- const formatter = vi.spyOn(telegramHtml, 'markdownToTelegramHtml').mockImplementationOnce(()=>{
319
- throw new Error('formatter failed');
318
+ const formatter = vi.spyOn(telegramHtml, "markdownToTelegramHtml").mockImplementationOnce(()=>{
319
+ throw new Error("formatter failed");
320
320
  });
321
- await adapter.sendMessage('12345', '- agent\n - Status');
321
+ await adapter.sendMessage("12345", "- agent\n - Status");
322
322
  expect(bot.telegram.sendMessage).toHaveBeenCalledTimes(1);
323
- expect(bot.telegram.sendMessage.mock.calls[0][0]).toBe('12345');
324
- expect(bot.telegram.sendMessage.mock.calls[0][1]).toBe('- agent\n - Status');
323
+ expect(bot.telegram.sendMessage.mock.calls[0][0]).toBe("12345");
324
+ expect(bot.telegram.sendMessage.mock.calls[0][1]).toBe("- agent\n - Status");
325
325
  expect(bot.telegram.sendMessage.mock.calls[0][2]).toBeUndefined();
326
326
  formatter.mockRestore();
327
327
  });
328
- it('should propagate non-parse-entities errors without falling back', async ()=>{
328
+ it("should propagate non-parse-entities errors without falling back", async ()=>{
329
329
  const bot = getMockBot();
330
- const otherError = Object.assign(new Error('403'), {
331
- description: 'Forbidden: bot was blocked by the user'
330
+ const otherError = Object.assign(new Error("403"), {
331
+ description: "Forbidden: bot was blocked by the user"
332
332
  });
333
333
  bot.telegram.sendMessage.mockRejectedValueOnce(otherError);
334
- await expect(adapter.sendMessage('12345', 'hi')).rejects.toBe(otherError);
334
+ await expect(adapter.sendMessage("12345", "hi")).rejects.toBe(otherError);
335
335
  // Only the HTML attempt should have happened — no fallback retry
336
336
  expect(bot.telegram.sendMessage).toHaveBeenCalledTimes(1);
337
337
  });
338
338
  });
339
- describe('sendInlineKeyboard', ()=>{
340
- it('sends with parse_mode HTML and inline_keyboard payload, returns message_id', async ()=>{
339
+ describe("sendInlineKeyboard", ()=>{
340
+ it("sends with parse_mode HTML and inline_keyboard payload, returns message_id", async ()=>{
341
341
  const bot = getMockBot();
342
342
  bot.telegram.sendMessage.mockResolvedValueOnce({
343
343
  message_id: 42
344
344
  });
345
- const id = await adapter.sendInlineKeyboard('12345', '<b>hi</b>', [
345
+ const id = await adapter.sendInlineKeyboard("12345", "<b>hi</b>", [
346
346
  [
347
347
  {
348
- text: 'A',
349
- callbackData: 'q:1:o:0'
348
+ text: "A",
349
+ callbackData: "q:1:o:0"
350
350
  }
351
351
  ],
352
352
  [
353
353
  {
354
- text: 'B',
355
- callbackData: 'q:1:o:1'
354
+ text: "B",
355
+ callbackData: "q:1:o:1"
356
356
  }
357
357
  ]
358
358
  ]);
359
359
  expect(id).toBe(42);
360
- expect(bot.telegram.sendMessage).toHaveBeenCalledWith('12345', '<b>hi</b>', {
361
- parse_mode: 'HTML',
360
+ expect(bot.telegram.sendMessage).toHaveBeenCalledWith("12345", "<b>hi</b>", {
361
+ parse_mode: "HTML",
362
362
  reply_markup: {
363
363
  inline_keyboard: [
364
364
  [
365
365
  {
366
- text: 'A',
367
- callback_data: 'q:1:o:0'
366
+ text: "A",
367
+ callback_data: "q:1:o:0"
368
368
  }
369
369
  ],
370
370
  [
371
371
  {
372
- text: 'B',
373
- callback_data: 'q:1:o:1'
372
+ text: "B",
373
+ callback_data: "q:1:o:1"
374
374
  }
375
375
  ]
376
376
  ]
@@ -378,80 +378,80 @@ describe('TelegramAdapter', ()=>{
378
378
  });
379
379
  });
380
380
  });
381
- describe('editInlineKeyboard', ()=>{
382
- it('replaces the keyboard on an existing message', async ()=>{
381
+ describe("editInlineKeyboard", ()=>{
382
+ it("replaces the keyboard on an existing message", async ()=>{
383
383
  const bot = getMockBot();
384
- await adapter.editInlineKeyboard('12345', 99, [
384
+ await adapter.editInlineKeyboard("12345", 99, [
385
385
  [
386
386
  {
387
- text: '✓ A',
388
- callbackData: 'q:1:o:0'
387
+ text: "✓ A",
388
+ callbackData: "q:1:o:0"
389
389
  }
390
390
  ]
391
391
  ]);
392
- expect(bot.telegram.editMessageReplyMarkup).toHaveBeenCalledWith('12345', 99, undefined, {
392
+ expect(bot.telegram.editMessageReplyMarkup).toHaveBeenCalledWith("12345", 99, undefined, {
393
393
  inline_keyboard: [
394
394
  [
395
395
  {
396
- text: '✓ A',
397
- callback_data: 'q:1:o:0'
396
+ text: "✓ A",
397
+ callback_data: "q:1:o:0"
398
398
  }
399
399
  ]
400
400
  ]
401
401
  });
402
402
  });
403
- it('passes undefined reply_markup to remove the keyboard', async ()=>{
403
+ it("passes undefined reply_markup to remove the keyboard", async ()=>{
404
404
  const bot = getMockBot();
405
- await adapter.editInlineKeyboard('12345', 99, null);
406
- expect(bot.telegram.editMessageReplyMarkup).toHaveBeenCalledWith('12345', 99, undefined, undefined);
405
+ await adapter.editInlineKeyboard("12345", 99, null);
406
+ expect(bot.telegram.editMessageReplyMarkup).toHaveBeenCalledWith("12345", 99, undefined, undefined);
407
407
  });
408
408
  });
409
- describe('answerCallback', ()=>{
410
- it('forwards id and optional text to telegraf', async ()=>{
409
+ describe("answerCallback", ()=>{
410
+ it("forwards id and optional text to telegraf", async ()=>{
411
411
  const bot = getMockBot();
412
- await adapter.answerCallback('cbq-1', 'thanks');
413
- expect(bot.telegram.answerCbQuery).toHaveBeenCalledWith('cbq-1', 'thanks');
412
+ await adapter.answerCallback("cbq-1", "thanks");
413
+ expect(bot.telegram.answerCbQuery).toHaveBeenCalledWith("cbq-1", "thanks");
414
414
  });
415
415
  });
416
- describe('onCallback', ()=>{
417
- it('invokes the registered handler with a normalized IncomingCallback', async ()=>{
416
+ describe("onCallback", ()=>{
417
+ it("invokes the registered handler with a normalized IncomingCallback", async ()=>{
418
418
  const handler = vi.fn().mockResolvedValue(undefined);
419
419
  adapter.onCallback(handler);
420
420
  await adapter.start();
421
421
  const bot = getMockBot();
422
- await bot._triggerCallback(12345, 67890, 'q:abc:o:1', 777);
422
+ await bot._triggerCallback(12345, 67890, "q:abc:o:1", 777);
423
423
  expect(handler).toHaveBeenCalledOnce();
424
424
  const cb = handler.mock.calls[0][0];
425
- expect(cb.channelType).toBe('telegram');
426
- expect(cb.chatId).toBe('12345');
427
- expect(cb.userId).toBe('67890');
425
+ expect(cb.channelType).toBe("telegram");
426
+ expect(cb.chatId).toBe("12345");
427
+ expect(cb.userId).toBe("67890");
428
428
  expect(cb.messageId).toBe(777);
429
- expect(cb.callbackData).toBe('q:abc:o:1');
430
- expect(cb.callbackQueryId).toBe('cbq-test');
429
+ expect(cb.callbackData).toBe("q:abc:o:1");
430
+ expect(cb.callbackQueryId).toBe("cbq-test");
431
431
  });
432
- it('answers callbacks when no handler is registered', async ()=>{
432
+ it("answers callbacks when no handler is registered", async ()=>{
433
433
  await adapter.start();
434
434
  const bot = getMockBot();
435
- const ctx = await bot._triggerCallback(12345, 67890, 'q:x:o:0');
435
+ const ctx = await bot._triggerCallback(12345, 67890, "q:x:o:0");
436
436
  expect(ctx.answerCbQuery).toHaveBeenCalled();
437
437
  });
438
- it('answers with error when handler rejects', async ()=>{
439
- adapter.onCallback(vi.fn().mockRejectedValue(new Error('boom')));
438
+ it("answers with error when handler rejects", async ()=>{
439
+ adapter.onCallback(vi.fn().mockRejectedValue(new Error("boom")));
440
440
  await adapter.start();
441
441
  const bot = getMockBot();
442
- const ctx = await bot._triggerCallback(12345, 67890, 'q:x:o:0');
443
- expect(ctx.answerCbQuery).toHaveBeenCalledWith('Error');
442
+ const ctx = await bot._triggerCallback(12345, 67890, "q:x:o:0");
443
+ expect(ctx.answerCbQuery).toHaveBeenCalledWith("Error");
444
444
  });
445
445
  });
446
- describe('isHealthy', ()=>{
447
- it('should return true after start', async ()=>{
446
+ describe("isHealthy", ()=>{
447
+ it("should return true after start", async ()=>{
448
448
  await adapter.start();
449
449
  expect(await adapter.isHealthy()).toBe(true);
450
450
  });
451
- it('should return false before start', async ()=>{
451
+ it("should return false before start", async ()=>{
452
452
  expect(await adapter.isHealthy()).toBe(false);
453
453
  });
454
- it('should return false after stop', async ()=>{
454
+ it("should return false after stop", async ()=>{
455
455
  await adapter.start();
456
456
  await adapter.stop();
457
457
  expect(await adapter.isHealthy()).toBe(false);