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