@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.
- package/README.md +1 -1
- package/dist/ChannelConfigRepository.d.ts +1 -1
- package/dist/ChannelConfigRepository.d.ts.map +1 -1
- package/dist/ChannelConfigRepository.js +13 -13
- package/dist/ChannelConfigRepository.js.map +1 -1
- package/dist/ChannelManager.d.ts +1 -1
- package/dist/ChannelManager.d.ts.map +1 -1
- package/dist/ChannelManager.js +9 -9
- package/dist/ChannelManager.js.map +1 -1
- package/dist/__tests__/ChannelConfigRepository.test.js +72 -72
- package/dist/__tests__/ChannelConfigRepository.test.js.map +1 -1
- package/dist/__tests__/ChannelManager.test.js +26 -26
- package/dist/__tests__/ChannelManager.test.js.map +1 -1
- package/dist/__tests__/adapters/slack/SlackAdapter.test.js +137 -137
- package/dist/__tests__/adapters/slack/SlackAdapter.test.js.map +1 -1
- package/dist/__tests__/adapters/slack/SlackMessageDelivery.test.js +36 -36
- package/dist/__tests__/adapters/slack/SlackMessageDelivery.test.js.map +1 -1
- package/dist/__tests__/adapters/slack/slackMarkdown.test.js +25 -25
- package/dist/__tests__/adapters/slack/slackMarkdown.test.js.map +1 -1
- package/dist/__tests__/adapters/slack/slackQuestion.test.js +41 -41
- package/dist/__tests__/adapters/slack/slackQuestion.test.js.map +1 -1
- package/dist/__tests__/adapters/telegram/TelegramAdapter.test.js +177 -154
- package/dist/__tests__/adapters/telegram/TelegramAdapter.test.js.map +1 -1
- package/dist/__tests__/adapters/telegram/telegramHtml.test.js +109 -109
- package/dist/__tests__/adapters/telegram/telegramHtml.test.js.map +1 -1
- package/dist/adapters/ChannelAdapter.d.ts +1 -1
- package/dist/adapters/ChannelAdapter.d.ts.map +1 -1
- package/dist/adapters/ChannelAdapter.js +1 -1
- package/dist/adapters/ChannelAdapter.js.map +1 -1
- package/dist/adapters/slack/SlackAdapter.d.ts +2 -2
- package/dist/adapters/slack/SlackAdapter.d.ts.map +1 -1
- package/dist/adapters/slack/SlackAdapter.js +17 -17
- package/dist/adapters/slack/SlackAdapter.js.map +1 -1
- package/dist/adapters/slack/SlackMessageDelivery.d.ts +1 -1
- package/dist/adapters/slack/SlackMessageDelivery.d.ts.map +1 -1
- package/dist/adapters/slack/SlackMessageDelivery.js +6 -6
- package/dist/adapters/slack/SlackMessageDelivery.js.map +1 -1
- package/dist/adapters/slack/slackMarkdown.d.ts.map +1 -1
- package/dist/adapters/slack/slackMarkdown.js +36 -36
- package/dist/adapters/slack/slackMarkdown.js.map +1 -1
- package/dist/adapters/slack/slackQuestion.d.ts +7 -7
- package/dist/adapters/slack/slackQuestion.d.ts.map +1 -1
- package/dist/adapters/slack/slackQuestion.js +13 -13
- package/dist/adapters/slack/slackQuestion.js.map +1 -1
- package/dist/adapters/telegram/TelegramAdapter.d.ts +12 -2
- package/dist/adapters/telegram/TelegramAdapter.d.ts.map +1 -1
- package/dist/adapters/telegram/TelegramAdapter.js +38 -27
- package/dist/adapters/telegram/TelegramAdapter.js.map +1 -1
- package/dist/adapters/telegram/TelegramMessageDelivery.d.ts.map +1 -1
- package/dist/adapters/telegram/TelegramMessageDelivery.js +2 -2
- package/dist/adapters/telegram/TelegramMessageDelivery.js.map +1 -1
- package/dist/adapters/telegram/telegramHtml.d.ts.map +1 -1
- package/dist/adapters/telegram/telegramHtml.js +16 -16
- package/dist/adapters/telegram/telegramHtml.js.map +1 -1
- package/dist/adapters/telegram/telegramKeyboard.d.ts +1 -1
- package/dist/adapters/telegram/telegramKeyboard.d.ts.map +1 -1
- package/dist/adapters/telegram/telegramKeyboard.js.map +1 -1
- package/dist/adapters/telegram/telegramMarkdown.d.ts.map +1 -1
- package/dist/adapters/telegram/telegramMarkdown.js +28 -28
- package/dist/adapters/telegram/telegramMarkdown.js.map +1 -1
- package/dist/index.d.ts +9 -9
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +5 -5
- package/dist/index.js.map +1 -1
- package/dist/types.d.ts +6 -6
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js +1 -1
- package/dist/types.js.map +1 -1
- package/package.json +21 -22
- package/vitest.config.ts +16 -16
|
@@ -1,41 +1,41 @@
|
|
|
1
|
-
import { SlackMessageDelivery } from
|
|
2
|
-
describe(
|
|
3
|
-
it(
|
|
1
|
+
import { SlackMessageDelivery } from "../../../adapters/slack/SlackMessageDelivery.js";
|
|
2
|
+
describe("SlackMessageDelivery", ()=>{
|
|
3
|
+
it("serializes long output as a parent followed by threaded continuations", async ()=>{
|
|
4
4
|
const postMessage = vi.fn().mockResolvedValueOnce({
|
|
5
5
|
ok: true,
|
|
6
|
-
ts:
|
|
6
|
+
ts: "100.1"
|
|
7
7
|
}).mockResolvedValue({
|
|
8
8
|
ok: true,
|
|
9
|
-
ts:
|
|
9
|
+
ts: "100.2"
|
|
10
10
|
});
|
|
11
11
|
const queue = new SlackMessageDelivery({
|
|
12
12
|
postMessage
|
|
13
13
|
}, {
|
|
14
14
|
maxMessageLength: 20
|
|
15
15
|
});
|
|
16
|
-
const result = await queue.send(
|
|
16
|
+
const result = await queue.send("D123", "first paragraph\n\nsecond paragraph that is long");
|
|
17
17
|
expect(result).toEqual({
|
|
18
|
-
messageId:
|
|
19
|
-
threadId:
|
|
18
|
+
messageId: "100.1",
|
|
19
|
+
threadId: "100.1"
|
|
20
20
|
});
|
|
21
21
|
expect(postMessage).toHaveBeenCalledTimes(3);
|
|
22
|
-
expect(postMessage.mock.calls[0][0]).not.toHaveProperty(
|
|
22
|
+
expect(postMessage.mock.calls[0][0]).not.toHaveProperty("thread_ts");
|
|
23
23
|
expect(postMessage.mock.calls[1][0]).toMatchObject({
|
|
24
|
-
channel:
|
|
25
|
-
thread_ts:
|
|
24
|
+
channel: "D123",
|
|
25
|
+
thread_ts: "100.1"
|
|
26
26
|
});
|
|
27
27
|
expect(postMessage.mock.calls[2][0]).toMatchObject({
|
|
28
|
-
channel:
|
|
29
|
-
thread_ts:
|
|
28
|
+
channel: "D123",
|
|
29
|
+
thread_ts: "100.1"
|
|
30
30
|
});
|
|
31
31
|
});
|
|
32
|
-
it(
|
|
33
|
-
const error = Object.assign(new Error(
|
|
32
|
+
it("waits for retryAfter before retrying a rate-limited API call", async ()=>{
|
|
33
|
+
const error = Object.assign(new Error("rate limited"), {
|
|
34
34
|
retryAfter: 2
|
|
35
35
|
});
|
|
36
36
|
const postMessage = vi.fn().mockRejectedValueOnce(error).mockResolvedValue({
|
|
37
37
|
ok: true,
|
|
38
|
-
ts:
|
|
38
|
+
ts: "200.1"
|
|
39
39
|
});
|
|
40
40
|
const sleep = vi.fn().mockResolvedValue(undefined);
|
|
41
41
|
const queue = new SlackMessageDelivery({
|
|
@@ -43,20 +43,20 @@ describe('SlackMessageDelivery', ()=>{
|
|
|
43
43
|
}, {
|
|
44
44
|
sleep
|
|
45
45
|
});
|
|
46
|
-
await expect(queue.send(
|
|
47
|
-
messageId:
|
|
48
|
-
threadId:
|
|
46
|
+
await expect(queue.send("D123", "hello")).resolves.toEqual({
|
|
47
|
+
messageId: "200.1",
|
|
48
|
+
threadId: "200.1"
|
|
49
49
|
});
|
|
50
50
|
expect(sleep).toHaveBeenCalledWith(2000);
|
|
51
51
|
expect(postMessage).toHaveBeenCalledTimes(2);
|
|
52
52
|
});
|
|
53
|
-
it(
|
|
54
|
-
const error = Object.assign(new Error(
|
|
53
|
+
it("caps an excessive rate-limit delay", async ()=>{
|
|
54
|
+
const error = Object.assign(new Error("rate limited"), {
|
|
55
55
|
retryAfter: 86_400
|
|
56
56
|
});
|
|
57
57
|
const postMessage = vi.fn().mockRejectedValueOnce(error).mockResolvedValue({
|
|
58
58
|
ok: true,
|
|
59
|
-
ts:
|
|
59
|
+
ts: "200.1"
|
|
60
60
|
});
|
|
61
61
|
const sleep = vi.fn().mockResolvedValue(undefined);
|
|
62
62
|
const queue = new SlackMessageDelivery({
|
|
@@ -64,15 +64,15 @@ describe('SlackMessageDelivery', ()=>{
|
|
|
64
64
|
}, {
|
|
65
65
|
sleep
|
|
66
66
|
});
|
|
67
|
-
await queue.send(
|
|
67
|
+
await queue.send("D123", "hello");
|
|
68
68
|
expect(sleep).toHaveBeenCalledWith(60_000);
|
|
69
69
|
});
|
|
70
|
-
it(
|
|
70
|
+
it("rejects overflow while a conversation send is pending", async ()=>{
|
|
71
71
|
let release;
|
|
72
72
|
const pending = new Promise((resolve)=>{
|
|
73
73
|
release = ()=>resolve({
|
|
74
74
|
ok: true,
|
|
75
|
-
ts:
|
|
75
|
+
ts: "300.1"
|
|
76
76
|
});
|
|
77
77
|
});
|
|
78
78
|
const postMessage = vi.fn().mockReturnValue(pending);
|
|
@@ -81,34 +81,34 @@ describe('SlackMessageDelivery', ()=>{
|
|
|
81
81
|
}, {
|
|
82
82
|
maxQueueSize: 1
|
|
83
83
|
});
|
|
84
|
-
const first = queue.send(
|
|
85
|
-
await expect(queue.send(
|
|
84
|
+
const first = queue.send("D123", "first");
|
|
85
|
+
await expect(queue.send("D123", "second")).rejects.toThrow("Slack message delivery queue is full");
|
|
86
86
|
release();
|
|
87
87
|
await first;
|
|
88
88
|
});
|
|
89
|
-
it(
|
|
89
|
+
it("rejects empty output and responses without a timestamp", async ()=>{
|
|
90
90
|
const postMessage = vi.fn().mockResolvedValue({
|
|
91
91
|
ok: true
|
|
92
92
|
});
|
|
93
93
|
const queue = new SlackMessageDelivery({
|
|
94
94
|
postMessage
|
|
95
95
|
});
|
|
96
|
-
await expect(queue.send(
|
|
97
|
-
await expect(queue.send(
|
|
96
|
+
await expect(queue.send("D123", "")).rejects.toThrow("Cannot send an empty Slack message");
|
|
97
|
+
await expect(queue.send("D123", "hello")).rejects.toThrow("Slack did not return a message timestamp");
|
|
98
98
|
});
|
|
99
|
-
it(
|
|
99
|
+
it("propagates permanent and invalid rate-limit errors without sleeping", async ()=>{
|
|
100
100
|
const sleep = vi.fn();
|
|
101
|
-
const permanent = new Error(
|
|
101
|
+
const permanent = new Error("invalid_auth");
|
|
102
102
|
const postMessage = vi.fn().mockRejectedValue(permanent);
|
|
103
103
|
const queue = new SlackMessageDelivery({
|
|
104
104
|
postMessage
|
|
105
105
|
}, {
|
|
106
106
|
sleep
|
|
107
107
|
});
|
|
108
|
-
await expect(queue.send(
|
|
108
|
+
await expect(queue.send("D123", "hello")).rejects.toBe(permanent);
|
|
109
109
|
expect(sleep).not.toHaveBeenCalled();
|
|
110
110
|
});
|
|
111
|
-
it(
|
|
111
|
+
it("allows separate conversation queues to progress independently", async ()=>{
|
|
112
112
|
const postMessage = vi.fn().mockImplementation(async ({ channel })=>({
|
|
113
113
|
ok: true,
|
|
114
114
|
ts: `${channel}.1`
|
|
@@ -117,8 +117,8 @@ describe('SlackMessageDelivery', ()=>{
|
|
|
117
117
|
postMessage
|
|
118
118
|
});
|
|
119
119
|
await expect(Promise.all([
|
|
120
|
-
queue.send(
|
|
121
|
-
queue.send(
|
|
120
|
+
queue.send("D1", "one"),
|
|
121
|
+
queue.send("D2", "two")
|
|
122
122
|
])).resolves.toHaveLength(2);
|
|
123
123
|
expect(postMessage).toHaveBeenCalledTimes(2);
|
|
124
124
|
});
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../src/__tests__/adapters/slack/SlackMessageDelivery.test.ts"],"sourcesContent":["import { SlackMessageDelivery } from
|
|
1
|
+
{"version":3,"sources":["../../../../src/__tests__/adapters/slack/SlackMessageDelivery.test.ts"],"sourcesContent":["import { SlackMessageDelivery } from \"../../../adapters/slack/SlackMessageDelivery.js\";\n\ndescribe(\"SlackMessageDelivery\", () => {\n it(\"serializes long output as a parent followed by threaded continuations\", async () => {\n const postMessage = vi\n .fn()\n .mockResolvedValueOnce({ ok: true, ts: \"100.1\" })\n .mockResolvedValue({ ok: true, ts: \"100.2\" });\n const queue = new SlackMessageDelivery({ postMessage }, { maxMessageLength: 20 });\n const result = await queue.send(\"D123\", \"first paragraph\\n\\nsecond paragraph that is long\");\n expect(result).toEqual({ messageId: \"100.1\", threadId: \"100.1\" });\n expect(postMessage).toHaveBeenCalledTimes(3);\n expect(postMessage.mock.calls[0][0]).not.toHaveProperty(\"thread_ts\");\n expect(postMessage.mock.calls[1][0]).toMatchObject({ channel: \"D123\", thread_ts: \"100.1\" });\n expect(postMessage.mock.calls[2][0]).toMatchObject({ channel: \"D123\", thread_ts: \"100.1\" });\n });\n\n it(\"waits for retryAfter before retrying a rate-limited API call\", async () => {\n const error = Object.assign(new Error(\"rate limited\"), { retryAfter: 2 });\n const postMessage = vi\n .fn()\n .mockRejectedValueOnce(error)\n .mockResolvedValue({ ok: true, ts: \"200.1\" });\n const sleep = vi.fn().mockResolvedValue(undefined);\n const queue = new SlackMessageDelivery({ postMessage }, { sleep });\n await expect(queue.send(\"D123\", \"hello\")).resolves.toEqual({\n messageId: \"200.1\",\n threadId: \"200.1\",\n });\n expect(sleep).toHaveBeenCalledWith(2000);\n expect(postMessage).toHaveBeenCalledTimes(2);\n });\n\n it(\"caps an excessive rate-limit delay\", async () => {\n const error = Object.assign(new Error(\"rate limited\"), { retryAfter: 86_400 });\n const postMessage = vi\n .fn()\n .mockRejectedValueOnce(error)\n .mockResolvedValue({ ok: true, ts: \"200.1\" });\n const sleep = vi.fn().mockResolvedValue(undefined);\n const queue = new SlackMessageDelivery({ postMessage }, { sleep });\n await queue.send(\"D123\", \"hello\");\n expect(sleep).toHaveBeenCalledWith(60_000);\n });\n\n it(\"rejects overflow while a conversation send is pending\", async () => {\n let release!: () => void;\n const pending = new Promise<{ ok: true; ts: string }>((resolve) => {\n release = () => resolve({ ok: true, ts: \"300.1\" });\n });\n const postMessage = vi.fn().mockReturnValue(pending);\n const queue = new SlackMessageDelivery({ postMessage }, { maxQueueSize: 1 });\n const first = queue.send(\"D123\", \"first\");\n await expect(queue.send(\"D123\", \"second\")).rejects.toThrow(\n \"Slack message delivery queue is full\",\n );\n release();\n await first;\n });\n\n it(\"rejects empty output and responses without a timestamp\", async () => {\n const postMessage = vi.fn().mockResolvedValue({ ok: true });\n const queue = new SlackMessageDelivery({ postMessage });\n await expect(queue.send(\"D123\", \"\")).rejects.toThrow(\"Cannot send an empty Slack message\");\n await expect(queue.send(\"D123\", \"hello\")).rejects.toThrow(\n \"Slack did not return a message timestamp\",\n );\n });\n\n it(\"propagates permanent and invalid rate-limit errors without sleeping\", async () => {\n const sleep = vi.fn();\n const permanent = new Error(\"invalid_auth\");\n const postMessage = vi.fn().mockRejectedValue(permanent);\n const queue = new SlackMessageDelivery({ postMessage }, { sleep });\n await expect(queue.send(\"D123\", \"hello\")).rejects.toBe(permanent);\n expect(sleep).not.toHaveBeenCalled();\n });\n\n it(\"allows separate conversation queues to progress independently\", async () => {\n const postMessage = vi\n .fn()\n .mockImplementation(async ({ channel }) => ({ ok: true, ts: `${channel}.1` }));\n const queue = new SlackMessageDelivery({ postMessage });\n await expect(\n Promise.all([queue.send(\"D1\", \"one\"), queue.send(\"D2\", \"two\")]),\n ).resolves.toHaveLength(2);\n expect(postMessage).toHaveBeenCalledTimes(2);\n });\n});\n"],"names":["SlackMessageDelivery","describe","it","postMessage","vi","fn","mockResolvedValueOnce","ok","ts","mockResolvedValue","queue","maxMessageLength","result","send","expect","toEqual","messageId","threadId","toHaveBeenCalledTimes","mock","calls","not","toHaveProperty","toMatchObject","channel","thread_ts","error","Object","assign","Error","retryAfter","mockRejectedValueOnce","sleep","undefined","resolves","toHaveBeenCalledWith","release","pending","Promise","resolve","mockReturnValue","maxQueueSize","first","rejects","toThrow","permanent","mockRejectedValue","toBe","toHaveBeenCalled","mockImplementation","all","toHaveLength"],"mappings":"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"}
|
|
@@ -1,41 +1,41 @@
|
|
|
1
|
-
import { SLACK_MAX_MESSAGE_LENGTH, chunkMarkdownForSlack, markdownToSlackMrkdwn } from
|
|
2
|
-
describe(
|
|
3
|
-
it(
|
|
4
|
-
expect(markdownToSlackMrkdwn(
|
|
1
|
+
import { SLACK_MAX_MESSAGE_LENGTH, chunkMarkdownForSlack, markdownToSlackMrkdwn } from "../../../adapters/slack/slackMarkdown.js";
|
|
2
|
+
describe("Slack Markdown", ()=>{
|
|
3
|
+
it("renders common Markdown without activating broad mentions", ()=>{
|
|
4
|
+
expect(markdownToSlackMrkdwn("# Result\n\n**Passed** & <safe> @channel [docs](https://example.com)")).toBe("*Result*\n\n*Passed* & <safe> @channel <https://example.com|docs>");
|
|
5
5
|
});
|
|
6
|
-
it(
|
|
6
|
+
it("preserves inline and fenced code while escaping Slack control characters", ()=>{
|
|
7
7
|
expect(markdownToSlackMrkdwn('Use `<tag>`\n\n```ts\nconst x = "<ok>";\n```')).toContain('`<tag>`\n\n```\nconst x = "<ok>";\n```');
|
|
8
8
|
});
|
|
9
|
-
it(
|
|
10
|
-
const chunks = chunkMarkdownForSlack(`\`\`\`ts\n${
|
|
9
|
+
it("chunks long fenced code into independently fenced Slack messages", ()=>{
|
|
10
|
+
const chunks = chunkMarkdownForSlack(`\`\`\`ts\n${"const value = 1;\n".repeat(400)}\`\`\``);
|
|
11
11
|
expect(chunks.length).toBeGreaterThan(1);
|
|
12
12
|
expect(chunks.every((chunk)=>chunk.length <= SLACK_MAX_MESSAGE_LENGTH)).toBe(true);
|
|
13
|
-
expect(chunks.every((chunk)=>chunk.startsWith(
|
|
13
|
+
expect(chunks.every((chunk)=>chunk.startsWith("```") && chunk.endsWith("```"))).toBe(true);
|
|
14
14
|
});
|
|
15
|
-
it(
|
|
16
|
-
const chunks = chunkMarkdownForSlack(`\`\`\`\n${
|
|
15
|
+
it("accounts for escaped control-character expansion in code chunks", ()=>{
|
|
16
|
+
const chunks = chunkMarkdownForSlack(`\`\`\`\n${"<&>".repeat(2000)}\n\`\`\``);
|
|
17
17
|
expect(chunks.length).toBeGreaterThan(1);
|
|
18
18
|
expect(chunks.every((chunk)=>chunk.length <= SLACK_MAX_MESSAGE_LENGTH)).toBe(true);
|
|
19
|
-
expect(chunks.every((chunk)=>chunk.startsWith(
|
|
19
|
+
expect(chunks.every((chunk)=>chunk.startsWith("```") && chunk.endsWith("```"))).toBe(true);
|
|
20
20
|
});
|
|
21
|
-
it(
|
|
22
|
-
const chunks = chunkMarkdownForSlack(
|
|
23
|
-
expect(chunks.join(
|
|
21
|
+
it("splits Unicode text without breaking code points", ()=>{
|
|
22
|
+
const chunks = chunkMarkdownForSlack("🎉".repeat(5000));
|
|
23
|
+
expect(chunks.join("")).toBe("🎉".repeat(5000));
|
|
24
24
|
expect(chunks.every((chunk)=>chunk.length <= SLACK_MAX_MESSAGE_LENGTH)).toBe(true);
|
|
25
25
|
});
|
|
26
|
-
it(
|
|
27
|
-
const rendered = markdownToSlackMrkdwn(
|
|
28
|
-
expect(rendered).toContain(
|
|
29
|
-
expect(rendered).toContain(
|
|
30
|
-
expect(rendered).toContain(
|
|
31
|
-
expect(rendered).toContain(
|
|
32
|
-
expect(rendered).toContain(
|
|
26
|
+
it("renders lists, quotes, emphasis, strike, breaks, and rules conservatively", ()=>{
|
|
27
|
+
const rendered = markdownToSlackMrkdwn("1. _one_\n2. ~~two~~\n\n> quoted \n> next\n\n---");
|
|
28
|
+
expect(rendered).toContain("1. _one_");
|
|
29
|
+
expect(rendered).toContain("2. ~two~");
|
|
30
|
+
expect(rendered).toContain("> quoted");
|
|
31
|
+
expect(rendered).toContain("> next");
|
|
32
|
+
expect(rendered).toContain("—");
|
|
33
33
|
});
|
|
34
|
-
it(
|
|
35
|
-
expect(chunkMarkdownForSlack(
|
|
36
|
-
|
|
34
|
+
it("packs short semantic blocks and splits oversized plain paragraphs", ()=>{
|
|
35
|
+
expect(chunkMarkdownForSlack("one\n\ntwo", 20)).toEqual([
|
|
36
|
+
"one\n\ntwo"
|
|
37
37
|
]);
|
|
38
|
-
const chunks = chunkMarkdownForSlack(
|
|
38
|
+
const chunks = chunkMarkdownForSlack("word ".repeat(30), 25);
|
|
39
39
|
expect(chunks.length).toBeGreaterThan(1);
|
|
40
40
|
expect(chunks.every((chunk)=>chunk.length <= 25)).toBe(true);
|
|
41
41
|
});
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../src/__tests__/adapters/slack/slackMarkdown.test.ts"],"sourcesContent":["import {\n
|
|
1
|
+
{"version":3,"sources":["../../../../src/__tests__/adapters/slack/slackMarkdown.test.ts"],"sourcesContent":["import {\n SLACK_MAX_MESSAGE_LENGTH,\n chunkMarkdownForSlack,\n markdownToSlackMrkdwn,\n} from \"../../../adapters/slack/slackMarkdown.js\";\n\ndescribe(\"Slack Markdown\", () => {\n it(\"renders common Markdown without activating broad mentions\", () => {\n expect(\n markdownToSlackMrkdwn(\"# Result\\n\\n**Passed** & <safe> @channel [docs](https://example.com)\"),\n ).toBe(\"*Result*\\n\\n*Passed* & <safe> @channel <https://example.com|docs>\");\n });\n\n it(\"preserves inline and fenced code while escaping Slack control characters\", () => {\n expect(markdownToSlackMrkdwn('Use `<tag>`\\n\\n```ts\\nconst x = \"<ok>\";\\n```')).toContain(\n '`<tag>`\\n\\n```\\nconst x = \"<ok>\";\\n```',\n );\n });\n\n it(\"chunks long fenced code into independently fenced Slack messages\", () => {\n const chunks = chunkMarkdownForSlack(`\\`\\`\\`ts\\n${\"const value = 1;\\n\".repeat(400)}\\`\\`\\``);\n expect(chunks.length).toBeGreaterThan(1);\n expect(chunks.every((chunk) => chunk.length <= SLACK_MAX_MESSAGE_LENGTH)).toBe(true);\n expect(chunks.every((chunk) => chunk.startsWith(\"```\") && chunk.endsWith(\"```\"))).toBe(true);\n });\n\n it(\"accounts for escaped control-character expansion in code chunks\", () => {\n const chunks = chunkMarkdownForSlack(`\\`\\`\\`\\n${\"<&>\".repeat(2000)}\\n\\`\\`\\``);\n expect(chunks.length).toBeGreaterThan(1);\n expect(chunks.every((chunk) => chunk.length <= SLACK_MAX_MESSAGE_LENGTH)).toBe(true);\n expect(chunks.every((chunk) => chunk.startsWith(\"```\") && chunk.endsWith(\"```\"))).toBe(true);\n });\n\n it(\"splits Unicode text without breaking code points\", () => {\n const chunks = chunkMarkdownForSlack(\"🎉\".repeat(5000));\n expect(chunks.join(\"\")).toBe(\"🎉\".repeat(5000));\n expect(chunks.every((chunk) => chunk.length <= SLACK_MAX_MESSAGE_LENGTH)).toBe(true);\n });\n\n it(\"renders lists, quotes, emphasis, strike, breaks, and rules conservatively\", () => {\n const rendered = markdownToSlackMrkdwn(\"1. _one_\\n2. ~~two~~\\n\\n> quoted \\n> next\\n\\n---\");\n expect(rendered).toContain(\"1. _one_\");\n expect(rendered).toContain(\"2. ~two~\");\n expect(rendered).toContain(\"> quoted\");\n expect(rendered).toContain(\"> next\");\n expect(rendered).toContain(\"—\");\n });\n\n it(\"packs short semantic blocks and splits oversized plain paragraphs\", () => {\n expect(chunkMarkdownForSlack(\"one\\n\\ntwo\", 20)).toEqual([\"one\\n\\ntwo\"]);\n const chunks = chunkMarkdownForSlack(\"word \".repeat(30), 25);\n expect(chunks.length).toBeGreaterThan(1);\n expect(chunks.every((chunk) => chunk.length <= 25)).toBe(true);\n });\n});\n"],"names":["SLACK_MAX_MESSAGE_LENGTH","chunkMarkdownForSlack","markdownToSlackMrkdwn","describe","it","expect","toBe","toContain","chunks","repeat","length","toBeGreaterThan","every","chunk","startsWith","endsWith","join","rendered","toEqual"],"mappings":"AAAA,SACEA,wBAAwB,EACxBC,qBAAqB,EACrBC,qBAAqB,QAChB,2CAA2C;AAElDC,SAAS,kBAAkB;IACzBC,GAAG,6DAA6D;QAC9DC,OACEH,sBAAsB,yEACtBI,IAAI,CAAC;IACT;IAEAF,GAAG,4EAA4E;QAC7EC,OAAOH,sBAAsB,iDAAiDK,SAAS,CACrF;IAEJ;IAEAH,GAAG,oEAAoE;QACrE,MAAMI,SAASP,sBAAsB,CAAC,UAAU,EAAE,qBAAqBQ,MAAM,CAAC,KAAK,MAAM,CAAC;QAC1FJ,OAAOG,OAAOE,MAAM,EAAEC,eAAe,CAAC;QACtCN,OAAOG,OAAOI,KAAK,CAAC,CAACC,QAAUA,MAAMH,MAAM,IAAIV,2BAA2BM,IAAI,CAAC;QAC/ED,OAAOG,OAAOI,KAAK,CAAC,CAACC,QAAUA,MAAMC,UAAU,CAAC,UAAUD,MAAME,QAAQ,CAAC,SAAST,IAAI,CAAC;IACzF;IAEAF,GAAG,mEAAmE;QACpE,MAAMI,SAASP,sBAAsB,CAAC,QAAQ,EAAE,MAAMQ,MAAM,CAAC,MAAM,QAAQ,CAAC;QAC5EJ,OAAOG,OAAOE,MAAM,EAAEC,eAAe,CAAC;QACtCN,OAAOG,OAAOI,KAAK,CAAC,CAACC,QAAUA,MAAMH,MAAM,IAAIV,2BAA2BM,IAAI,CAAC;QAC/ED,OAAOG,OAAOI,KAAK,CAAC,CAACC,QAAUA,MAAMC,UAAU,CAAC,UAAUD,MAAME,QAAQ,CAAC,SAAST,IAAI,CAAC;IACzF;IAEAF,GAAG,oDAAoD;QACrD,MAAMI,SAASP,sBAAsB,KAAKQ,MAAM,CAAC;QACjDJ,OAAOG,OAAOQ,IAAI,CAAC,KAAKV,IAAI,CAAC,KAAKG,MAAM,CAAC;QACzCJ,OAAOG,OAAOI,KAAK,CAAC,CAACC,QAAUA,MAAMH,MAAM,IAAIV,2BAA2BM,IAAI,CAAC;IACjF;IAEAF,GAAG,6EAA6E;QAC9E,MAAMa,WAAWf,sBAAsB;QACvCG,OAAOY,UAAUV,SAAS,CAAC;QAC3BF,OAAOY,UAAUV,SAAS,CAAC;QAC3BF,OAAOY,UAAUV,SAAS,CAAC;QAC3BF,OAAOY,UAAUV,SAAS,CAAC;QAC3BF,OAAOY,UAAUV,SAAS,CAAC;IAC7B;IAEAH,GAAG,qEAAqE;QACtEC,OAAOJ,sBAAsB,cAAc,KAAKiB,OAAO,CAAC;YAAC;SAAa;QACtE,MAAMV,SAASP,sBAAsB,QAAQQ,MAAM,CAAC,KAAK;QACzDJ,OAAOG,OAAOE,MAAM,EAAEC,eAAe,CAAC;QACtCN,OAAOG,OAAOI,KAAK,CAAC,CAACC,QAAUA,MAAMH,MAAM,IAAI,KAAKJ,IAAI,CAAC;IAC3D;AACF"}
|
|
@@ -1,81 +1,81 @@
|
|
|
1
|
-
import { buildSlackQuestionMessage } from
|
|
2
|
-
describe(
|
|
3
|
-
it(
|
|
4
|
-
const message = buildSlackQuestionMessage(
|
|
5
|
-
id:
|
|
6
|
-
header:
|
|
7
|
-
question:
|
|
1
|
+
import { buildSlackQuestionMessage } from "../../../adapters/slack/slackQuestion.js";
|
|
2
|
+
describe("buildSlackQuestionMessage", ()=>{
|
|
3
|
+
it("builds an accessible Block Kit question with optional skip action", ()=>{
|
|
4
|
+
const message = buildSlackQuestionMessage("D123", {
|
|
5
|
+
id: "q1",
|
|
6
|
+
header: "Scope",
|
|
7
|
+
question: "Choose one",
|
|
8
8
|
allowSkip: true,
|
|
9
9
|
options: [
|
|
10
10
|
{
|
|
11
|
-
label:
|
|
12
|
-
description:
|
|
13
|
-
value:
|
|
11
|
+
label: "Safe",
|
|
12
|
+
description: "Read only",
|
|
13
|
+
value: "1"
|
|
14
14
|
}
|
|
15
15
|
]
|
|
16
16
|
});
|
|
17
17
|
expect(message).toMatchObject({
|
|
18
|
-
channel:
|
|
19
|
-
text:
|
|
18
|
+
channel: "D123",
|
|
19
|
+
text: "Scope: Choose one",
|
|
20
20
|
blocks: [
|
|
21
21
|
{
|
|
22
|
-
type:
|
|
22
|
+
type: "section",
|
|
23
23
|
text: {
|
|
24
|
-
type:
|
|
25
|
-
text:
|
|
24
|
+
type: "mrkdwn",
|
|
25
|
+
text: "*Scope*\nChoose one"
|
|
26
26
|
}
|
|
27
27
|
},
|
|
28
28
|
{
|
|
29
|
-
type:
|
|
29
|
+
type: "actions",
|
|
30
30
|
elements: [
|
|
31
31
|
{
|
|
32
|
-
type:
|
|
33
|
-
action_id:
|
|
32
|
+
type: "button",
|
|
33
|
+
action_id: "ai_devkit_question",
|
|
34
34
|
text: {
|
|
35
|
-
type:
|
|
36
|
-
text:
|
|
35
|
+
type: "plain_text",
|
|
36
|
+
text: "Safe"
|
|
37
37
|
},
|
|
38
|
-
value:
|
|
38
|
+
value: "q1:1"
|
|
39
39
|
},
|
|
40
40
|
{
|
|
41
|
-
type:
|
|
42
|
-
action_id:
|
|
41
|
+
type: "button",
|
|
42
|
+
action_id: "ai_devkit_question",
|
|
43
43
|
text: {
|
|
44
|
-
type:
|
|
45
|
-
text:
|
|
44
|
+
type: "plain_text",
|
|
45
|
+
text: "Skip"
|
|
46
46
|
},
|
|
47
|
-
value:
|
|
47
|
+
value: "q1:skip"
|
|
48
48
|
}
|
|
49
49
|
]
|
|
50
50
|
}
|
|
51
51
|
]
|
|
52
52
|
});
|
|
53
53
|
});
|
|
54
|
-
it(
|
|
55
|
-
const message = buildSlackQuestionMessage(
|
|
56
|
-
id:
|
|
57
|
-
header:
|
|
58
|
-
question:
|
|
54
|
+
it("escapes Slack control syntax in fallback and mrkdwn text", ()=>{
|
|
55
|
+
const message = buildSlackQuestionMessage("D123", {
|
|
56
|
+
id: "q1",
|
|
57
|
+
header: "<!channel>",
|
|
58
|
+
question: "Choose <one> & continue",
|
|
59
59
|
allowSkip: false,
|
|
60
60
|
options: [
|
|
61
61
|
{
|
|
62
|
-
label:
|
|
63
|
-
value:
|
|
62
|
+
label: "Safe",
|
|
63
|
+
value: "1"
|
|
64
64
|
}
|
|
65
65
|
]
|
|
66
66
|
});
|
|
67
|
-
expect(message.text).toBe(
|
|
68
|
-
expect(message.blocks[0].text.text).toBe(
|
|
67
|
+
expect(message.text).toBe("<!channel>: Choose <one> & continue");
|
|
68
|
+
expect(message.blocks[0].text.text).toBe("*<!channel>*\nChoose <one> & continue");
|
|
69
69
|
});
|
|
70
|
-
it(
|
|
71
|
-
const message = buildSlackQuestionMessage(
|
|
72
|
-
id:
|
|
73
|
-
question:
|
|
70
|
+
it("limits button labels and values to Slack bounds", ()=>{
|
|
71
|
+
const message = buildSlackQuestionMessage("D123", {
|
|
72
|
+
id: "q1",
|
|
73
|
+
question: "Choose",
|
|
74
74
|
allowSkip: false,
|
|
75
75
|
options: [
|
|
76
76
|
{
|
|
77
|
-
label:
|
|
78
|
-
value:
|
|
77
|
+
label: "A".repeat(100),
|
|
78
|
+
value: "v".repeat(3000)
|
|
79
79
|
}
|
|
80
80
|
]
|
|
81
81
|
});
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../src/__tests__/adapters/slack/slackQuestion.test.ts"],"sourcesContent":["import { buildSlackQuestionMessage } from
|
|
1
|
+
{"version":3,"sources":["../../../../src/__tests__/adapters/slack/slackQuestion.test.ts"],"sourcesContent":["import { buildSlackQuestionMessage } from \"../../../adapters/slack/slackQuestion.js\";\n\ndescribe(\"buildSlackQuestionMessage\", () => {\n it(\"builds an accessible Block Kit question with optional skip action\", () => {\n const message = buildSlackQuestionMessage(\"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\n expect(message).toMatchObject({\n channel: \"D123\",\n text: \"Scope: Choose one\",\n blocks: [\n {\n type: \"section\",\n text: { type: \"mrkdwn\", text: \"*Scope*\\nChoose one\" },\n },\n {\n type: \"actions\",\n elements: [\n {\n type: \"button\",\n action_id: \"ai_devkit_question\",\n text: { type: \"plain_text\", text: \"Safe\" },\n value: \"q1:1\",\n },\n {\n type: \"button\",\n action_id: \"ai_devkit_question\",\n text: { type: \"plain_text\", text: \"Skip\" },\n value: \"q1:skip\",\n },\n ],\n },\n ],\n });\n });\n\n it(\"escapes Slack control syntax in fallback and mrkdwn text\", () => {\n const message = buildSlackQuestionMessage(\"D123\", {\n id: \"q1\",\n header: \"<!channel>\",\n question: \"Choose <one> & continue\",\n allowSkip: false,\n options: [{ label: \"Safe\", value: \"1\" }],\n });\n\n expect(message.text).toBe(\"<!channel>: Choose <one> & continue\");\n expect(message.blocks[0].text.text).toBe(\n \"*<!channel>*\\nChoose <one> & continue\",\n );\n });\n\n it(\"limits button labels and values to Slack bounds\", () => {\n const message = buildSlackQuestionMessage(\"D123\", {\n id: \"q1\",\n question: \"Choose\",\n allowSkip: false,\n options: [{ label: \"A\".repeat(100), value: \"v\".repeat(3000) }],\n });\n\n const button = message.blocks[1].elements[0];\n expect(button.text.text).toHaveLength(75);\n expect(button.value).toHaveLength(2000);\n });\n});\n"],"names":["buildSlackQuestionMessage","describe","it","message","id","header","question","allowSkip","options","label","description","value","expect","toMatchObject","channel","text","blocks","type","elements","action_id","toBe","repeat","button","toHaveLength"],"mappings":"AAAA,SAASA,yBAAyB,QAAQ,2CAA2C;AAErFC,SAAS,6BAA6B;IACpCC,GAAG,qEAAqE;QACtE,MAAMC,UAAUH,0BAA0B,QAAQ;YAChDI,IAAI;YACJC,QAAQ;YACRC,UAAU;YACVC,WAAW;YACXC,SAAS;gBAAC;oBAAEC,OAAO;oBAAQC,aAAa;oBAAaC,OAAO;gBAAI;aAAE;QACpE;QAEAC,OAAOT,SAASU,aAAa,CAAC;YAC5BC,SAAS;YACTC,MAAM;YACNC,QAAQ;gBACN;oBACEC,MAAM;oBACNF,MAAM;wBAAEE,MAAM;wBAAUF,MAAM;oBAAsB;gBACtD;gBACA;oBACEE,MAAM;oBACNC,UAAU;wBACR;4BACED,MAAM;4BACNE,WAAW;4BACXJ,MAAM;gCAAEE,MAAM;gCAAcF,MAAM;4BAAO;4BACzCJ,OAAO;wBACT;wBACA;4BACEM,MAAM;4BACNE,WAAW;4BACXJ,MAAM;gCAAEE,MAAM;gCAAcF,MAAM;4BAAO;4BACzCJ,OAAO;wBACT;qBACD;gBACH;aACD;QACH;IACF;IAEAT,GAAG,4DAA4D;QAC7D,MAAMC,UAAUH,0BAA0B,QAAQ;YAChDI,IAAI;YACJC,QAAQ;YACRC,UAAU;YACVC,WAAW;YACXC,SAAS;gBAAC;oBAAEC,OAAO;oBAAQE,OAAO;gBAAI;aAAE;QAC1C;QAEAC,OAAOT,QAAQY,IAAI,EAAEK,IAAI,CAAC;QAC1BR,OAAOT,QAAQa,MAAM,CAAC,EAAE,CAACD,IAAI,CAACA,IAAI,EAAEK,IAAI,CACtC;IAEJ;IAEAlB,GAAG,mDAAmD;QACpD,MAAMC,UAAUH,0BAA0B,QAAQ;YAChDI,IAAI;YACJE,UAAU;YACVC,WAAW;YACXC,SAAS;gBAAC;oBAAEC,OAAO,IAAIY,MAAM,CAAC;oBAAMV,OAAO,IAAIU,MAAM,CAAC;gBAAM;aAAE;QAChE;QAEA,MAAMC,SAASnB,QAAQa,MAAM,CAAC,EAAE,CAACE,QAAQ,CAAC,EAAE;QAC5CN,OAAOU,OAAOP,IAAI,CAACA,IAAI,EAAEQ,YAAY,CAAC;QACtCX,OAAOU,OAAOX,KAAK,EAAEY,YAAY,CAAC;IACpC;AACF"}
|