cc-lark 0.1.1
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/.github/workflows/ci.yml +47 -0
- package/.github/workflows/release.yml +47 -0
- package/.github/workflows/sync-upstream.yml +127 -0
- package/.prettierrc.json +7 -0
- package/README.md +214 -0
- package/dist/core/api-error.d.ts +193 -0
- package/dist/core/api-error.d.ts.map +1 -0
- package/dist/core/api-error.js +263 -0
- package/dist/core/api-error.js.map +1 -0
- package/dist/core/auth-errors.d.ts +13 -0
- package/dist/core/auth-errors.d.ts.map +1 -0
- package/dist/core/auth-errors.js +14 -0
- package/dist/core/auth-errors.js.map +1 -0
- package/dist/core/config.d.ts +60 -0
- package/dist/core/config.d.ts.map +1 -0
- package/dist/core/config.js +115 -0
- package/dist/core/config.js.map +1 -0
- package/dist/core/device-flow.d.ts +80 -0
- package/dist/core/device-flow.d.ts.map +1 -0
- package/dist/core/device-flow.js +231 -0
- package/dist/core/device-flow.js.map +1 -0
- package/dist/core/index.d.ts +16 -0
- package/dist/core/index.d.ts.map +1 -0
- package/dist/core/index.js +16 -0
- package/dist/core/index.js.map +1 -0
- package/dist/core/lark-client.d.ts +136 -0
- package/dist/core/lark-client.d.ts.map +1 -0
- package/dist/core/lark-client.js +315 -0
- package/dist/core/lark-client.js.map +1 -0
- package/dist/core/token-store.d.ts +67 -0
- package/dist/core/token-store.d.ts.map +1 -0
- package/dist/core/token-store.js +215 -0
- package/dist/core/token-store.js.map +1 -0
- package/dist/core/types.d.ts +286 -0
- package/dist/core/types.d.ts.map +1 -0
- package/dist/core/types.js +11 -0
- package/dist/core/types.js.map +1 -0
- package/dist/core/uat-client.d.ts +64 -0
- package/dist/core/uat-client.d.ts.map +1 -0
- package/dist/core/uat-client.js +227 -0
- package/dist/core/uat-client.js.map +1 -0
- package/dist/core/version.d.ts +26 -0
- package/dist/core/version.d.ts.map +1 -0
- package/dist/core/version.js +50 -0
- package/dist/core/version.js.map +1 -0
- package/dist/index.d.ts +12 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +116 -0
- package/dist/index.js.map +1 -0
- package/dist/tools/bitable/app.d.ts +20 -0
- package/dist/tools/bitable/app.d.ts.map +1 -0
- package/dist/tools/bitable/app.js +301 -0
- package/dist/tools/bitable/app.js.map +1 -0
- package/dist/tools/bitable/field.d.ts +19 -0
- package/dist/tools/bitable/field.d.ts.map +1 -0
- package/dist/tools/bitable/field.js +315 -0
- package/dist/tools/bitable/field.js.map +1 -0
- package/dist/tools/bitable/index.d.ts +21 -0
- package/dist/tools/bitable/index.d.ts.map +1 -0
- package/dist/tools/bitable/index.js +39 -0
- package/dist/tools/bitable/index.js.map +1 -0
- package/dist/tools/bitable/record.d.ts +22 -0
- package/dist/tools/bitable/record.d.ts.map +1 -0
- package/dist/tools/bitable/record.js +434 -0
- package/dist/tools/bitable/record.js.map +1 -0
- package/dist/tools/bitable/table.d.ts +21 -0
- package/dist/tools/bitable/table.d.ts.map +1 -0
- package/dist/tools/bitable/table.js +361 -0
- package/dist/tools/bitable/table.js.map +1 -0
- package/dist/tools/calendar/calendar.d.ts +18 -0
- package/dist/tools/calendar/calendar.d.ts.map +1 -0
- package/dist/tools/calendar/calendar.js +192 -0
- package/dist/tools/calendar/calendar.js.map +1 -0
- package/dist/tools/calendar/event.d.ts +20 -0
- package/dist/tools/calendar/event.d.ts.map +1 -0
- package/dist/tools/calendar/event.js +465 -0
- package/dist/tools/calendar/event.js.map +1 -0
- package/dist/tools/calendar/index.d.ts +19 -0
- package/dist/tools/calendar/index.d.ts.map +1 -0
- package/dist/tools/calendar/index.js +37 -0
- package/dist/tools/calendar/index.js.map +1 -0
- package/dist/tools/chat/chat.d.ts +11 -0
- package/dist/tools/chat/chat.d.ts.map +1 -0
- package/dist/tools/chat/chat.js +106 -0
- package/dist/tools/chat/chat.js.map +1 -0
- package/dist/tools/chat/index.d.ts +11 -0
- package/dist/tools/chat/index.d.ts.map +1 -0
- package/dist/tools/chat/index.js +20 -0
- package/dist/tools/chat/index.js.map +1 -0
- package/dist/tools/chat/members.d.ts +9 -0
- package/dist/tools/chat/members.d.ts.map +1 -0
- package/dist/tools/chat/members.js +80 -0
- package/dist/tools/chat/members.js.map +1 -0
- package/dist/tools/common/get-user.d.ts +11 -0
- package/dist/tools/common/get-user.d.ts.map +1 -0
- package/dist/tools/common/get-user.js +112 -0
- package/dist/tools/common/get-user.js.map +1 -0
- package/dist/tools/common/index.d.ts +11 -0
- package/dist/tools/common/index.d.ts.map +1 -0
- package/dist/tools/common/index.js +20 -0
- package/dist/tools/common/index.js.map +1 -0
- package/dist/tools/common/search-user.d.ts +9 -0
- package/dist/tools/common/search-user.d.ts.map +1 -0
- package/dist/tools/common/search-user.js +88 -0
- package/dist/tools/common/search-user.js.map +1 -0
- package/dist/tools/doc/create.d.ts +17 -0
- package/dist/tools/doc/create.d.ts.map +1 -0
- package/dist/tools/doc/create.js +159 -0
- package/dist/tools/doc/create.js.map +1 -0
- package/dist/tools/doc/fetch.d.ts +17 -0
- package/dist/tools/doc/fetch.d.ts.map +1 -0
- package/dist/tools/doc/fetch.js +123 -0
- package/dist/tools/doc/fetch.js.map +1 -0
- package/dist/tools/doc/index.d.ts +21 -0
- package/dist/tools/doc/index.d.ts.map +1 -0
- package/dist/tools/doc/index.js +33 -0
- package/dist/tools/doc/index.js.map +1 -0
- package/dist/tools/doc/shared.d.ts +69 -0
- package/dist/tools/doc/shared.d.ts.map +1 -0
- package/dist/tools/doc/shared.js +172 -0
- package/dist/tools/doc/shared.js.map +1 -0
- package/dist/tools/doc/update.d.ts +25 -0
- package/dist/tools/doc/update.d.ts.map +1 -0
- package/dist/tools/doc/update.js +208 -0
- package/dist/tools/doc/update.js.map +1 -0
- package/dist/tools/drive/file.d.ts +13 -0
- package/dist/tools/drive/file.d.ts.map +1 -0
- package/dist/tools/drive/file.js +212 -0
- package/dist/tools/drive/file.js.map +1 -0
- package/dist/tools/drive/index.d.ts +12 -0
- package/dist/tools/drive/index.d.ts.map +1 -0
- package/dist/tools/drive/index.js +25 -0
- package/dist/tools/drive/index.js.map +1 -0
- package/dist/tools/im/format-messages.d.ts +99 -0
- package/dist/tools/im/format-messages.d.ts.map +1 -0
- package/dist/tools/im/format-messages.js +277 -0
- package/dist/tools/im/format-messages.js.map +1 -0
- package/dist/tools/im/helpers.d.ts +53 -0
- package/dist/tools/im/helpers.d.ts.map +1 -0
- package/dist/tools/im/helpers.js +85 -0
- package/dist/tools/im/helpers.js.map +1 -0
- package/dist/tools/im/index.d.ts +25 -0
- package/dist/tools/im/index.d.ts.map +1 -0
- package/dist/tools/im/index.js +44 -0
- package/dist/tools/im/index.js.map +1 -0
- package/dist/tools/im/message-read.d.ts +19 -0
- package/dist/tools/im/message-read.d.ts.map +1 -0
- package/dist/tools/im/message-read.js +526 -0
- package/dist/tools/im/message-read.js.map +1 -0
- package/dist/tools/im/message.d.ts +22 -0
- package/dist/tools/im/message.d.ts.map +1 -0
- package/dist/tools/im/message.js +233 -0
- package/dist/tools/im/message.js.map +1 -0
- package/dist/tools/im/resource.d.ts +19 -0
- package/dist/tools/im/resource.d.ts.map +1 -0
- package/dist/tools/im/resource.js +185 -0
- package/dist/tools/im/resource.js.map +1 -0
- package/dist/tools/im/time-utils.d.ts +70 -0
- package/dist/tools/im/time-utils.d.ts.map +1 -0
- package/dist/tools/im/time-utils.js +277 -0
- package/dist/tools/im/time-utils.js.map +1 -0
- package/dist/tools/index.d.ts +85 -0
- package/dist/tools/index.d.ts.map +1 -0
- package/dist/tools/index.js +135 -0
- package/dist/tools/index.js.map +1 -0
- package/dist/tools/oauth.d.ts +15 -0
- package/dist/tools/oauth.d.ts.map +1 -0
- package/dist/tools/oauth.js +379 -0
- package/dist/tools/oauth.js.map +1 -0
- package/dist/tools/search/doc-search.d.ts +9 -0
- package/dist/tools/search/doc-search.d.ts.map +1 -0
- package/dist/tools/search/doc-search.js +219 -0
- package/dist/tools/search/doc-search.js.map +1 -0
- package/dist/tools/search/index.d.ts +11 -0
- package/dist/tools/search/index.d.ts.map +1 -0
- package/dist/tools/search/index.js +18 -0
- package/dist/tools/search/index.js.map +1 -0
- package/dist/tools/sheets/index.d.ts +11 -0
- package/dist/tools/sheets/index.d.ts.map +1 -0
- package/dist/tools/sheets/index.js +18 -0
- package/dist/tools/sheets/index.js.map +1 -0
- package/dist/tools/sheets/sheet.d.ts +11 -0
- package/dist/tools/sheets/sheet.d.ts.map +1 -0
- package/dist/tools/sheets/sheet.js +332 -0
- package/dist/tools/sheets/sheet.js.map +1 -0
- package/dist/tools/task/index.d.ts +12 -0
- package/dist/tools/task/index.d.ts.map +1 -0
- package/dist/tools/task/index.js +30 -0
- package/dist/tools/task/index.js.map +1 -0
- package/dist/tools/task/task.d.ts +13 -0
- package/dist/tools/task/task.d.ts.map +1 -0
- package/dist/tools/task/task.js +225 -0
- package/dist/tools/task/task.js.map +1 -0
- package/dist/tools/task/tasklist.d.ts +13 -0
- package/dist/tools/task/tasklist.d.ts.map +1 -0
- package/dist/tools/task/tasklist.js +206 -0
- package/dist/tools/task/tasklist.js.map +1 -0
- package/dist/tools/wiki/index.d.ts +11 -0
- package/dist/tools/wiki/index.d.ts.map +1 -0
- package/dist/tools/wiki/index.js +20 -0
- package/dist/tools/wiki/index.js.map +1 -0
- package/dist/tools/wiki/node.d.ts +11 -0
- package/dist/tools/wiki/node.d.ts.map +1 -0
- package/dist/tools/wiki/node.js +112 -0
- package/dist/tools/wiki/node.js.map +1 -0
- package/dist/tools/wiki/space.d.ts +11 -0
- package/dist/tools/wiki/space.d.ts.map +1 -0
- package/dist/tools/wiki/space.js +125 -0
- package/dist/tools/wiki/space.js.map +1 -0
- package/dist/utils/index.d.ts +8 -0
- package/dist/utils/index.d.ts.map +1 -0
- package/dist/utils/index.js +8 -0
- package/dist/utils/index.js.map +1 -0
- package/dist/utils/logger.d.ts +36 -0
- package/dist/utils/logger.d.ts.map +1 -0
- package/dist/utils/logger.js +101 -0
- package/dist/utils/logger.js.map +1 -0
- package/eslint.config.js +13 -0
- package/package.json +54 -0
- package/skills/feishu-bitable/SKILL.md +248 -0
- package/skills/feishu-bitable/references/examples.md +813 -0
- package/skills/feishu-bitable/references/field-properties.md +763 -0
- package/skills/feishu-bitable/references/record-values.md +911 -0
- package/skills/feishu-calendar/SKILL.md +244 -0
- package/skills/feishu-channel-rules/SKILL.md +18 -0
- package/skills/feishu-channel-rules/references/markdown-syntax.md +138 -0
- package/skills/feishu-create-doc/SKILL.md +719 -0
- package/skills/feishu-fetch-doc/SKILL.md +93 -0
- package/skills/feishu-im-read/SKILL.md +163 -0
- package/skills/feishu-task/SKILL.md +293 -0
- package/skills/feishu-troubleshoot/SKILL.md +70 -0
- package/skills/feishu-update-doc/SKILL.md +285 -0
- package/src/core/api-error.ts +342 -0
- package/src/core/auth-errors.ts +27 -0
- package/src/core/config.ts +134 -0
- package/src/core/device-flow.ts +314 -0
- package/src/core/index.ts +16 -0
- package/src/core/lark-client.ts +391 -0
- package/src/core/token-store.ts +249 -0
- package/src/core/types.ts +302 -0
- package/src/core/uat-client.ts +298 -0
- package/src/core/version.ts +53 -0
- package/src/index.ts +138 -0
- package/src/tools/bitable/app.ts +390 -0
- package/src/tools/bitable/field.ts +406 -0
- package/src/tools/bitable/index.ts +43 -0
- package/src/tools/bitable/record.ts +559 -0
- package/src/tools/bitable/table.ts +472 -0
- package/src/tools/calendar/calendar.ts +254 -0
- package/src/tools/calendar/event.ts +606 -0
- package/src/tools/calendar/index.ts +41 -0
- package/src/tools/chat/chat.ts +127 -0
- package/src/tools/chat/index.ts +24 -0
- package/src/tools/chat/members.ts +93 -0
- package/src/tools/common/get-user.ts +127 -0
- package/src/tools/common/index.ts +24 -0
- package/src/tools/common/search-user.ts +99 -0
- package/src/tools/doc/create.ts +184 -0
- package/src/tools/doc/fetch.ts +149 -0
- package/src/tools/doc/index.ts +38 -0
- package/src/tools/doc/shared.ts +228 -0
- package/src/tools/doc/update.ts +240 -0
- package/src/tools/drive/file.ts +265 -0
- package/src/tools/drive/index.ts +29 -0
- package/src/tools/im/format-messages.ts +391 -0
- package/src/tools/im/helpers.ts +109 -0
- package/src/tools/im/index.ts +49 -0
- package/src/tools/im/message-read.ts +676 -0
- package/src/tools/im/message.ts +303 -0
- package/src/tools/im/resource.ts +225 -0
- package/src/tools/im/time-utils.ts +347 -0
- package/src/tools/index.ts +205 -0
- package/src/tools/oauth.ts +460 -0
- package/src/tools/search/doc-search.ts +250 -0
- package/src/tools/search/index.ts +22 -0
- package/src/tools/sheets/index.ts +22 -0
- package/src/tools/sheets/sheet.ts +382 -0
- package/src/tools/task/index.ts +34 -0
- package/src/tools/task/task.ts +265 -0
- package/src/tools/task/tasklist.ts +262 -0
- package/src/tools/wiki/index.ts +24 -0
- package/src/tools/wiki/node.ts +131 -0
- package/src/tools/wiki/space.ts +152 -0
- package/src/utils/index.ts +8 -0
- package/src/utils/logger.ts +132 -0
- package/tests/core/config.test.ts +238 -0
- package/tests/core/device-flow.test.ts +490 -0
- package/tests/core/lark-client.test.ts +378 -0
- package/tests/core/token-store.test.ts +438 -0
- package/tests/index.test.ts +360 -0
- package/tests/tools/doc/create.test.ts +224 -0
- package/tests/tools/doc/fetch.test.ts +182 -0
- package/tests/tools/doc/shared.test.ts +183 -0
- package/tests/tools/doc/update.test.ts +330 -0
- package/tests/tools/im/format-messages.test.ts +184 -0
- package/tests/tools/im/time-utils.test.ts +178 -0
- package/tests/utils/logger.test.ts +140 -0
- package/tsconfig.json +20 -0
|
@@ -0,0 +1,184 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (c) 2026 ByteDance Ltd. and/or its affiliates
|
|
3
|
+
* SPDX-License-Identifier: MIT
|
|
4
|
+
*
|
|
5
|
+
* Tests for IM tools message formatting.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import { describe, it, expect } from 'vitest';
|
|
9
|
+
import {
|
|
10
|
+
extractMentionOpenId,
|
|
11
|
+
convertMessageContent,
|
|
12
|
+
buildConvertContextFromItem,
|
|
13
|
+
type ApiMessageItem,
|
|
14
|
+
} from '../../../src/tools/im/format-messages.js';
|
|
15
|
+
|
|
16
|
+
describe('format-messages', () => {
|
|
17
|
+
describe('extractMentionOpenId', () => {
|
|
18
|
+
it('should extract open_id from string', () => {
|
|
19
|
+
expect(extractMentionOpenId('ou_abc123')).toBe('ou_abc123');
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
it('should extract open_id from object', () => {
|
|
23
|
+
expect(extractMentionOpenId({ open_id: 'ou_abc123' })).toBe('ou_abc123');
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
it('should return empty string for null', () => {
|
|
27
|
+
expect(extractMentionOpenId(null)).toBe('');
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
it('should return empty string for undefined', () => {
|
|
31
|
+
expect(extractMentionOpenId(undefined)).toBe('');
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
it('should return empty string for object without open_id', () => {
|
|
35
|
+
expect(extractMentionOpenId({ user_id: 'abc' })).toBe('');
|
|
36
|
+
});
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
describe('convertMessageContent', () => {
|
|
40
|
+
const ctx = {
|
|
41
|
+
messageId: 'test-msg',
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
it('should convert text message', () => {
|
|
45
|
+
const content = JSON.stringify({ text: 'Hello world' });
|
|
46
|
+
const result = convertMessageContent(content, 'text', ctx);
|
|
47
|
+
expect(result).toBe('Hello world');
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
it('should convert image message', () => {
|
|
51
|
+
const content = JSON.stringify({ image_key: 'img_abc123' });
|
|
52
|
+
const result = convertMessageContent(content, 'image', ctx);
|
|
53
|
+
expect(result).toBe('[Image: img_abc123]');
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
it('should convert file message', () => {
|
|
57
|
+
const content = JSON.stringify({ file_key: 'file_abc123', file_name: 'document.pdf' });
|
|
58
|
+
const result = convertMessageContent(content, 'file', ctx);
|
|
59
|
+
expect(result).toBe('[File: document.pdf]');
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
it('should convert audio message', () => {
|
|
63
|
+
const content = JSON.stringify({ file_key: 'file_abc123', file_name: 'recording.mp3' });
|
|
64
|
+
const result = convertMessageContent(content, 'audio', ctx);
|
|
65
|
+
expect(result).toBe('[Audio: recording.mp3]');
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
it('should convert media message', () => {
|
|
69
|
+
const content = JSON.stringify({ media_key: 'media_abc123', file_name: 'video.mp4' });
|
|
70
|
+
const result = convertMessageContent(content, 'media', ctx);
|
|
71
|
+
expect(result).toBe('[Media: video.mp4]');
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
it('should convert sticker message', () => {
|
|
75
|
+
const content = JSON.stringify({ file_key: 'sticker_abc123' });
|
|
76
|
+
const result = convertMessageContent(content, 'sticker', ctx);
|
|
77
|
+
expect(result).toBe('[Sticker: sticker_abc123]');
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
it('should convert interactive card message', () => {
|
|
81
|
+
const content = JSON.stringify({});
|
|
82
|
+
const result = convertMessageContent(content, 'interactive', ctx);
|
|
83
|
+
expect(result).toBe('[Interactive Card]');
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
it('should convert share_chat message', () => {
|
|
87
|
+
const content = JSON.stringify({ chat_id: 'oc_abc123' });
|
|
88
|
+
const result = convertMessageContent(content, 'share_chat', ctx);
|
|
89
|
+
expect(result).toBe('[Share Chat: oc_abc123]');
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
it('should convert share_user message', () => {
|
|
93
|
+
const content = JSON.stringify({ user_id: 'ou_abc123' });
|
|
94
|
+
const result = convertMessageContent(content, 'share_user', ctx);
|
|
95
|
+
expect(result).toBe('[Share User: ou_abc123]');
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
it('should convert merge_forward message', () => {
|
|
99
|
+
const content = JSON.stringify({});
|
|
100
|
+
const result = convertMessageContent(content, 'merge_forward', ctx);
|
|
101
|
+
expect(result).toBe('[Merged Forwarded Messages]');
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
it('should convert post message with title and content', () => {
|
|
105
|
+
const content = JSON.stringify({
|
|
106
|
+
zh_cn: {
|
|
107
|
+
title: 'Title',
|
|
108
|
+
content: [
|
|
109
|
+
[{ tag: 'text', text: 'Line 1' }],
|
|
110
|
+
[{ tag: 'text', text: 'Line 2' }],
|
|
111
|
+
],
|
|
112
|
+
},
|
|
113
|
+
});
|
|
114
|
+
const result = convertMessageContent(content, 'post', ctx);
|
|
115
|
+
expect(result).toContain('Title');
|
|
116
|
+
expect(result).toContain('Line 1');
|
|
117
|
+
expect(result).toContain('Line 2');
|
|
118
|
+
});
|
|
119
|
+
|
|
120
|
+
it('should return raw content for unknown message type', () => {
|
|
121
|
+
const content = '{"custom": "data"}';
|
|
122
|
+
const result = convertMessageContent(content, 'unknown_type', ctx);
|
|
123
|
+
expect(result).toBe('{"custom": "data"}');
|
|
124
|
+
});
|
|
125
|
+
|
|
126
|
+
it('should return raw content for invalid JSON', () => {
|
|
127
|
+
const content = 'not valid json';
|
|
128
|
+
const result = convertMessageContent(content, 'text', ctx);
|
|
129
|
+
expect(result).toBe('not valid json');
|
|
130
|
+
});
|
|
131
|
+
|
|
132
|
+
it('should handle empty content', () => {
|
|
133
|
+
const result = convertMessageContent('', 'text', ctx);
|
|
134
|
+
expect(result).toBe('');
|
|
135
|
+
});
|
|
136
|
+
});
|
|
137
|
+
|
|
138
|
+
describe('buildConvertContextFromItem', () => {
|
|
139
|
+
it('should build context from API message item', () => {
|
|
140
|
+
const item: ApiMessageItem = {
|
|
141
|
+
message_id: 'om_test123',
|
|
142
|
+
msg_type: 'text',
|
|
143
|
+
body: { content: '{"text":"hello"}' },
|
|
144
|
+
sender: { id: 'ou_sender', sender_type: 'user' },
|
|
145
|
+
mentions: [
|
|
146
|
+
{ key: '@_user_1', id: 'ou_mentioned', name: 'John' },
|
|
147
|
+
],
|
|
148
|
+
};
|
|
149
|
+
|
|
150
|
+
const ctx = buildConvertContextFromItem(item, 'fallback-id', 'account-1');
|
|
151
|
+
|
|
152
|
+
expect(ctx.messageId).toBe('om_test123');
|
|
153
|
+
expect(ctx.accountId).toBe('account-1');
|
|
154
|
+
// mentions and mentionsByOpenId are internal Maps, not exposed on ConvertContext
|
|
155
|
+
});
|
|
156
|
+
|
|
157
|
+
it('should use fallback message ID if not present', () => {
|
|
158
|
+
const item: ApiMessageItem = {};
|
|
159
|
+
const ctx = buildConvertContextFromItem(item, 'fallback-id');
|
|
160
|
+
|
|
161
|
+
expect(ctx.messageId).toBe('fallback-id');
|
|
162
|
+
});
|
|
163
|
+
|
|
164
|
+
it('should handle empty mentions', () => {
|
|
165
|
+
const item: ApiMessageItem = {
|
|
166
|
+
message_id: 'om_test',
|
|
167
|
+
mentions: [],
|
|
168
|
+
};
|
|
169
|
+
|
|
170
|
+
const ctx = buildConvertContextFromItem(item, 'fallback');
|
|
171
|
+
// mentions and mentionsByOpenId are internal Maps, not exposed on ConvertContext
|
|
172
|
+
expect(ctx.messageId).toBe('om_test');
|
|
173
|
+
});
|
|
174
|
+
|
|
175
|
+
it('should handle missing mentions', () => {
|
|
176
|
+
const item: ApiMessageItem = {
|
|
177
|
+
message_id: 'om_test',
|
|
178
|
+
};
|
|
179
|
+
|
|
180
|
+
const ctx = buildConvertContextFromItem(item, 'fallback');
|
|
181
|
+
expect(ctx.messageId).toBe('om_test');
|
|
182
|
+
});
|
|
183
|
+
});
|
|
184
|
+
});
|
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (c) 2026 ByteDance Ltd. and/or its affiliates
|
|
3
|
+
* SPDX-License-Identifier: MIT
|
|
4
|
+
*
|
|
5
|
+
* Tests for IM tools time utilities.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import { describe, it, expect } from 'vitest';
|
|
9
|
+
import {
|
|
10
|
+
secondsToDateTime,
|
|
11
|
+
secondsStringToDateTime,
|
|
12
|
+
millisToDateTime,
|
|
13
|
+
millisStringToDateTime,
|
|
14
|
+
dateTimeToSeconds,
|
|
15
|
+
dateTimeToSecondsString,
|
|
16
|
+
dateTimeToMillis,
|
|
17
|
+
parseTimeRange,
|
|
18
|
+
parseTimeRangeToSeconds,
|
|
19
|
+
parseTimeToTimestamp,
|
|
20
|
+
unixTimestampToISO8601,
|
|
21
|
+
} from '../../../src/tools/im/time-utils.js';
|
|
22
|
+
|
|
23
|
+
// 2026-03-14 20:00:00+08:00 = 2026-03-14 12:00:00 UTC
|
|
24
|
+
const TEST_TIMESTAMP_SECONDS = 1773489600;
|
|
25
|
+
const TEST_TIMESTAMP_MILLIS = 1773489600000;
|
|
26
|
+
const TEST_ISO = '2026-03-14T20:00:00+08:00';
|
|
27
|
+
|
|
28
|
+
describe('time-utils', () => {
|
|
29
|
+
describe('secondsToDateTime', () => {
|
|
30
|
+
it('should convert Unix seconds to ISO 8601 Beijing time', () => {
|
|
31
|
+
const result = secondsToDateTime(TEST_TIMESTAMP_SECONDS);
|
|
32
|
+
expect(result).toBe(TEST_ISO);
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
it('should handle epoch', () => {
|
|
36
|
+
const result = secondsToDateTime(0);
|
|
37
|
+
expect(result).toBe('1970-01-01T08:00:00+08:00');
|
|
38
|
+
});
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
describe('secondsStringToDateTime', () => {
|
|
42
|
+
it('should convert Unix seconds string to ISO 8601', () => {
|
|
43
|
+
const result = secondsStringToDateTime(String(TEST_TIMESTAMP_SECONDS));
|
|
44
|
+
expect(result).toBe(TEST_ISO);
|
|
45
|
+
});
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
describe('millisToDateTime', () => {
|
|
49
|
+
it('should convert Unix milliseconds to ISO 8601', () => {
|
|
50
|
+
const result = millisToDateTime(TEST_TIMESTAMP_MILLIS);
|
|
51
|
+
expect(result).toBe(TEST_ISO);
|
|
52
|
+
});
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
describe('millisStringToDateTime', () => {
|
|
56
|
+
it('should convert Unix milliseconds string to ISO 8601', () => {
|
|
57
|
+
const result = millisStringToDateTime(String(TEST_TIMESTAMP_MILLIS));
|
|
58
|
+
expect(result).toBe(TEST_ISO);
|
|
59
|
+
});
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
describe('dateTimeToSeconds', () => {
|
|
63
|
+
it('should convert ISO 8601 to Unix seconds', () => {
|
|
64
|
+
const result = dateTimeToSeconds(TEST_ISO);
|
|
65
|
+
expect(result).toBe(TEST_TIMESTAMP_SECONDS);
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
it('should throw for invalid date format', () => {
|
|
69
|
+
expect(() => dateTimeToSeconds('invalid')).toThrow();
|
|
70
|
+
});
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
describe('dateTimeToSecondsString', () => {
|
|
74
|
+
it('should convert ISO 8601 to Unix seconds string', () => {
|
|
75
|
+
const result = dateTimeToSecondsString(TEST_ISO);
|
|
76
|
+
expect(result).toBe(String(TEST_TIMESTAMP_SECONDS));
|
|
77
|
+
});
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
describe('dateTimeToMillis', () => {
|
|
81
|
+
it('should convert ISO 8601 to Unix milliseconds', () => {
|
|
82
|
+
const result = dateTimeToMillis(TEST_ISO);
|
|
83
|
+
expect(result).toBe(TEST_TIMESTAMP_MILLIS);
|
|
84
|
+
});
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
describe('parseTimeRange', () => {
|
|
88
|
+
it('should parse "today"', () => {
|
|
89
|
+
const result = parseTimeRange('today');
|
|
90
|
+
expect(result.start).toMatch(/^\d{4}-\d{2}-\d{2}T00:00:00\+08:00$/);
|
|
91
|
+
expect(result.end).toMatch(/^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\+08:00$/);
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
it('should parse "yesterday"', () => {
|
|
95
|
+
const result = parseTimeRange('yesterday');
|
|
96
|
+
expect(result.start).toMatch(/^\d{4}-\d{2}-\d{2}T00:00:00\+08:00$/);
|
|
97
|
+
expect(result.end).toMatch(/^\d{4}-\d{2}-\d{2}T23:59:59\+08:00$/);
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
it('should parse "this_week"', () => {
|
|
101
|
+
const result = parseTimeRange('this_week');
|
|
102
|
+
expect(result.start).toMatch(/^\d{4}-\d{2}-\d{2}T00:00:00\+08:00$/);
|
|
103
|
+
expect(result.end).toMatch(/^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\+08:00$/);
|
|
104
|
+
});
|
|
105
|
+
|
|
106
|
+
it('should parse "this_month"', () => {
|
|
107
|
+
const result = parseTimeRange('this_month');
|
|
108
|
+
expect(result.start).toMatch(/^\d{4}-\d{2}-01T00:00:00\+08:00$/);
|
|
109
|
+
expect(result.end).toMatch(/^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\+08:00$/);
|
|
110
|
+
});
|
|
111
|
+
|
|
112
|
+
it('should parse "last_3_days"', () => {
|
|
113
|
+
const result = parseTimeRange('last_3_days');
|
|
114
|
+
expect(result.start).toMatch(/^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\+08:00$/);
|
|
115
|
+
expect(result.end).toMatch(/^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\+08:00$/);
|
|
116
|
+
});
|
|
117
|
+
|
|
118
|
+
it('should parse "last_60_minutes"', () => {
|
|
119
|
+
const result = parseTimeRange('last_60_minutes');
|
|
120
|
+
expect(result.start).toMatch(/^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\+08:00$/);
|
|
121
|
+
expect(result.end).toMatch(/^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\+08:00$/);
|
|
122
|
+
});
|
|
123
|
+
|
|
124
|
+
it('should throw for unsupported format', () => {
|
|
125
|
+
expect(() => parseTimeRange('invalid')).toThrow();
|
|
126
|
+
});
|
|
127
|
+
});
|
|
128
|
+
|
|
129
|
+
describe('parseTimeRangeToSeconds', () => {
|
|
130
|
+
it('should return Unix seconds for time range', () => {
|
|
131
|
+
const result = parseTimeRangeToSeconds('today');
|
|
132
|
+
expect(result.start).toMatch(/^\d+$/);
|
|
133
|
+
expect(result.end).toMatch(/^\d+$/);
|
|
134
|
+
});
|
|
135
|
+
});
|
|
136
|
+
|
|
137
|
+
describe('parseTimeToTimestamp', () => {
|
|
138
|
+
it('should parse ISO 8601 with timezone', () => {
|
|
139
|
+
const result = parseTimeToTimestamp(TEST_ISO);
|
|
140
|
+
expect(result).toBe(String(TEST_TIMESTAMP_SECONDS));
|
|
141
|
+
});
|
|
142
|
+
|
|
143
|
+
it('should parse ISO 8601 without timezone (default to Beijing)', () => {
|
|
144
|
+
// 2026-03-14 20:00 Beijing = same as above
|
|
145
|
+
const result = parseTimeToTimestamp('2026-03-14 20:00');
|
|
146
|
+
expect(result).toBe(String(TEST_TIMESTAMP_SECONDS));
|
|
147
|
+
});
|
|
148
|
+
|
|
149
|
+
it('should parse ISO 8601 with T separator without timezone', () => {
|
|
150
|
+
const result = parseTimeToTimestamp('2026-03-14T20:00:00');
|
|
151
|
+
expect(result).toBe(String(TEST_TIMESTAMP_SECONDS));
|
|
152
|
+
});
|
|
153
|
+
|
|
154
|
+
it('should return null for invalid format', () => {
|
|
155
|
+
expect(parseTimeToTimestamp('invalid')).toBeNull();
|
|
156
|
+
});
|
|
157
|
+
});
|
|
158
|
+
|
|
159
|
+
describe('unixTimestampToISO8601', () => {
|
|
160
|
+
it('should convert Unix seconds to ISO 8601', () => {
|
|
161
|
+
const result = unixTimestampToISO8601(TEST_TIMESTAMP_SECONDS);
|
|
162
|
+
expect(result).toBe(TEST_ISO);
|
|
163
|
+
});
|
|
164
|
+
|
|
165
|
+
it('should convert Unix milliseconds to ISO 8601', () => {
|
|
166
|
+
const result = unixTimestampToISO8601(TEST_TIMESTAMP_MILLIS);
|
|
167
|
+
expect(result).toBe(TEST_ISO);
|
|
168
|
+
});
|
|
169
|
+
|
|
170
|
+
it('should return null for undefined', () => {
|
|
171
|
+
expect(unixTimestampToISO8601(undefined)).toBeNull();
|
|
172
|
+
});
|
|
173
|
+
|
|
174
|
+
it('should return null for invalid input', () => {
|
|
175
|
+
expect(unixTimestampToISO8601('invalid')).toBeNull();
|
|
176
|
+
});
|
|
177
|
+
});
|
|
178
|
+
});
|
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (c) 2026 ByteDance Ltd. and/or its affiliates
|
|
3
|
+
* SPDX-License-Identifier: MIT
|
|
4
|
+
*
|
|
5
|
+
* Tests for the logger utility.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';
|
|
9
|
+
import { logger, defaultLogger } from '../../src/utils/logger.js';
|
|
10
|
+
|
|
11
|
+
describe('logger', () => {
|
|
12
|
+
// Mock console methods
|
|
13
|
+
const originalConsole = {
|
|
14
|
+
log: console.log,
|
|
15
|
+
debug: console.debug,
|
|
16
|
+
warn: console.warn,
|
|
17
|
+
error: console.error,
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
beforeEach(() => {
|
|
21
|
+
console.log = vi.fn();
|
|
22
|
+
console.debug = vi.fn();
|
|
23
|
+
console.warn = vi.fn();
|
|
24
|
+
console.error = vi.fn();
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
afterEach(() => {
|
|
28
|
+
console.log = originalConsole.log;
|
|
29
|
+
console.debug = originalConsole.debug;
|
|
30
|
+
console.warn = originalConsole.warn;
|
|
31
|
+
console.error = originalConsole.error;
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
describe('logger factory', () => {
|
|
35
|
+
it('should create a logger with the correct subsystem', () => {
|
|
36
|
+
const log = logger('test');
|
|
37
|
+
expect(log.subsystem).toBe('test');
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
it('should create child loggers with nested subsystems', () => {
|
|
41
|
+
const log = logger('parent');
|
|
42
|
+
const child = log.child('child');
|
|
43
|
+
expect(child.subsystem).toBe('parent/child');
|
|
44
|
+
|
|
45
|
+
const grandchild = child.child('grandchild');
|
|
46
|
+
expect(grandchild.subsystem).toBe('parent/child/grandchild');
|
|
47
|
+
});
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
describe('logging methods', () => {
|
|
51
|
+
it('should call console.debug for debug level', () => {
|
|
52
|
+
const log = logger('test');
|
|
53
|
+
log.debug('test message');
|
|
54
|
+
|
|
55
|
+
expect(console.debug).toHaveBeenCalled();
|
|
56
|
+
const call = (console.debug as ReturnType<typeof vi.fn>).mock.calls[0];
|
|
57
|
+
expect(call[1]).toContain('cc-lark/test');
|
|
58
|
+
expect(call[1]).toContain('test message');
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
it('should call console.log for info level', () => {
|
|
62
|
+
const log = logger('test');
|
|
63
|
+
log.info('test message');
|
|
64
|
+
|
|
65
|
+
expect(console.log).toHaveBeenCalled();
|
|
66
|
+
const call = (console.log as ReturnType<typeof vi.fn>).mock.calls[0];
|
|
67
|
+
expect(call[1]).toContain('cc-lark/test');
|
|
68
|
+
expect(call[1]).toContain('test message');
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
it('should call console.warn for warn level', () => {
|
|
72
|
+
const log = logger('test');
|
|
73
|
+
log.warn('test message');
|
|
74
|
+
|
|
75
|
+
expect(console.warn).toHaveBeenCalled();
|
|
76
|
+
const call = (console.warn as ReturnType<typeof vi.fn>).mock.calls[0];
|
|
77
|
+
expect(call[1]).toContain('cc-lark/test');
|
|
78
|
+
expect(call[1]).toContain('test message');
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
it('should call console.error for error level', () => {
|
|
82
|
+
const log = logger('test');
|
|
83
|
+
log.error('test message');
|
|
84
|
+
|
|
85
|
+
expect(console.error).toHaveBeenCalled();
|
|
86
|
+
const call = (console.error as ReturnType<typeof vi.fn>).mock.calls[0];
|
|
87
|
+
expect(call[1]).toContain('cc-lark/test');
|
|
88
|
+
expect(call[1]).toContain('test message');
|
|
89
|
+
});
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
describe('metadata handling', () => {
|
|
93
|
+
it('should format metadata in message', () => {
|
|
94
|
+
const log = logger('test');
|
|
95
|
+
log.info('test message', { key: 'value', count: 42 });
|
|
96
|
+
|
|
97
|
+
const call = (console.log as ReturnType<typeof vi.fn>).mock.calls[0];
|
|
98
|
+
expect(call[1]).toContain('key=value');
|
|
99
|
+
expect(call[1]).toContain('count=42');
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
it('should handle empty metadata', () => {
|
|
103
|
+
const log = logger('test');
|
|
104
|
+
log.info('test message', {});
|
|
105
|
+
|
|
106
|
+
const call = (console.log as ReturnType<typeof vi.fn>).mock.calls[0];
|
|
107
|
+
expect(call[1]).toContain('test message');
|
|
108
|
+
});
|
|
109
|
+
|
|
110
|
+
it('should handle null and undefined metadata values', () => {
|
|
111
|
+
const log = logger('test');
|
|
112
|
+
log.info('test message', { key: null, other: undefined });
|
|
113
|
+
|
|
114
|
+
const call = (console.log as ReturnType<typeof vi.fn>).mock.calls[0];
|
|
115
|
+
expect(call[1]).toContain('test message');
|
|
116
|
+
});
|
|
117
|
+
|
|
118
|
+
it('should stringify object values in metadata', () => {
|
|
119
|
+
const log = logger('test');
|
|
120
|
+
log.info('test message', { obj: { nested: 'value' } });
|
|
121
|
+
|
|
122
|
+
const call = (console.log as ReturnType<typeof vi.fn>).mock.calls[0];
|
|
123
|
+
expect(call[1]).toContain('obj={"nested":"value"}');
|
|
124
|
+
});
|
|
125
|
+
});
|
|
126
|
+
|
|
127
|
+
describe('defaultLogger', () => {
|
|
128
|
+
it('should have subsystem "core"', () => {
|
|
129
|
+
expect(defaultLogger.subsystem).toBe('core');
|
|
130
|
+
});
|
|
131
|
+
|
|
132
|
+
it('should be a valid logger instance', () => {
|
|
133
|
+
expect(typeof defaultLogger.info).toBe('function');
|
|
134
|
+
expect(typeof defaultLogger.debug).toBe('function');
|
|
135
|
+
expect(typeof defaultLogger.warn).toBe('function');
|
|
136
|
+
expect(typeof defaultLogger.error).toBe('function');
|
|
137
|
+
expect(typeof defaultLogger.child).toBe('function');
|
|
138
|
+
});
|
|
139
|
+
});
|
|
140
|
+
});
|
package/tsconfig.json
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
"target": "ES2022",
|
|
4
|
+
"module": "NodeNext",
|
|
5
|
+
"moduleResolution": "NodeNext",
|
|
6
|
+
"lib": ["ES2022"],
|
|
7
|
+
"outDir": "./dist",
|
|
8
|
+
"rootDir": "./src",
|
|
9
|
+
"strict": true,
|
|
10
|
+
"esModuleInterop": true,
|
|
11
|
+
"skipLibCheck": true,
|
|
12
|
+
"forceConsistentCasingInFileNames": true,
|
|
13
|
+
"declaration": true,
|
|
14
|
+
"declarationMap": true,
|
|
15
|
+
"sourceMap": true,
|
|
16
|
+
"resolveJsonModule": true
|
|
17
|
+
},
|
|
18
|
+
"include": ["src/**/*"],
|
|
19
|
+
"exclude": ["node_modules", "dist", "tests"]
|
|
20
|
+
}
|