@xihe-lab/tapd-core 2.0.0-rc.1 → 2.0.0-rc.3

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 (158) hide show
  1. package/README.md +23 -0
  2. package/dist/advisor/index.d.ts.map +1 -1
  3. package/dist/advisor/index.js +2 -0
  4. package/dist/advisor/index.js.map +1 -1
  5. package/dist/cache/read-cache.d.ts +136 -0
  6. package/dist/cache/read-cache.d.ts.map +1 -0
  7. package/dist/cache/read-cache.js +322 -0
  8. package/dist/cache/read-cache.js.map +1 -0
  9. package/dist/cache/read-cache.test.d.ts +2 -0
  10. package/dist/cache/read-cache.test.d.ts.map +1 -0
  11. package/dist/cache/read-cache.test.js +429 -0
  12. package/dist/cache/read-cache.test.js.map +1 -0
  13. package/dist/errors/index.d.ts +120 -0
  14. package/dist/errors/index.d.ts.map +1 -0
  15. package/dist/errors/index.js +429 -0
  16. package/dist/errors/index.js.map +1 -0
  17. package/dist/index.d.ts +3 -0
  18. package/dist/index.d.ts.map +1 -1
  19. package/dist/index.js +2 -0
  20. package/dist/index.js.map +1 -1
  21. package/dist/registry/registry.d.ts +7 -0
  22. package/dist/registry/registry.d.ts.map +1 -1
  23. package/dist/registry/registry.js +9 -3
  24. package/dist/registry/registry.js.map +1 -1
  25. package/dist/registry/registry.test.js +293 -2
  26. package/dist/registry/registry.test.js.map +1 -1
  27. package/dist/registry/richtext-manifests.generated.d.ts +1 -1
  28. package/dist/registry/richtext-manifests.generated.d.ts.map +1 -1
  29. package/dist/registry/richtext-manifests.generated.js +1 -1
  30. package/dist/registry/richtext-manifests.generated.js.map +1 -1
  31. package/dist/registry/richtext.d.ts +9 -0
  32. package/dist/registry/richtext.d.ts.map +1 -1
  33. package/dist/registry/richtext.js +33 -15
  34. package/dist/registry/richtext.js.map +1 -1
  35. package/dist/registry/richtext.test.js +70 -7
  36. package/dist/registry/richtext.test.js.map +1 -1
  37. package/dist/richtext/attachment-anchor.d.ts +58 -0
  38. package/dist/richtext/attachment-anchor.d.ts.map +1 -0
  39. package/dist/richtext/attachment-anchor.js +221 -0
  40. package/dist/richtext/attachment-anchor.js.map +1 -0
  41. package/dist/richtext/attachment-anchor.test.d.ts +2 -0
  42. package/dist/richtext/attachment-anchor.test.d.ts.map +1 -0
  43. package/dist/richtext/attachment-anchor.test.js +149 -0
  44. package/dist/richtext/attachment-anchor.test.js.map +1 -0
  45. package/dist/richtext/escape.d.ts +7 -0
  46. package/dist/richtext/escape.d.ts.map +1 -0
  47. package/dist/richtext/escape.js +60 -0
  48. package/dist/richtext/escape.js.map +1 -0
  49. package/dist/richtext/index.d.ts +15 -0
  50. package/dist/richtext/index.d.ts.map +1 -0
  51. package/dist/richtext/index.js +35 -0
  52. package/dist/richtext/index.js.map +1 -0
  53. package/dist/richtext/mention.d.ts +49 -0
  54. package/dist/richtext/mention.d.ts.map +1 -0
  55. package/dist/richtext/mention.js +179 -0
  56. package/dist/richtext/mention.js.map +1 -0
  57. package/dist/richtext/mention.test.d.ts +2 -0
  58. package/dist/richtext/mention.test.d.ts.map +1 -0
  59. package/dist/richtext/mention.test.js +123 -0
  60. package/dist/richtext/mention.test.js.map +1 -0
  61. package/dist/richtext/pipeline.d.ts +5 -0
  62. package/dist/richtext/pipeline.d.ts.map +1 -0
  63. package/dist/richtext/pipeline.js +199 -0
  64. package/dist/richtext/pipeline.js.map +1 -0
  65. package/dist/richtext/round-trip.test.d.ts +2 -0
  66. package/dist/richtext/round-trip.test.d.ts.map +1 -0
  67. package/dist/richtext/round-trip.test.js +105 -0
  68. package/dist/richtext/round-trip.test.js.map +1 -0
  69. package/dist/sdk-routes.d.ts.map +1 -1
  70. package/dist/sdk-routes.js +4 -0
  71. package/dist/sdk-routes.js.map +1 -1
  72. package/dist/tapd-client.d.ts +79 -2
  73. package/dist/tapd-client.d.ts.map +1 -1
  74. package/dist/tapd-client.js +177 -24
  75. package/dist/tapd-client.js.map +1 -1
  76. package/dist/tools/attachment.d.ts +47 -0
  77. package/dist/tools/attachment.d.ts.map +1 -1
  78. package/dist/tools/attachment.js +278 -32
  79. package/dist/tools/attachment.js.map +1 -1
  80. package/dist/tools/attachment.test.d.ts +2 -0
  81. package/dist/tools/attachment.test.d.ts.map +1 -0
  82. package/dist/tools/attachment.test.js +432 -0
  83. package/dist/tools/attachment.test.js.map +1 -0
  84. package/dist/tools/board.js +22 -22
  85. package/dist/tools/board.js.map +1 -1
  86. package/dist/tools/bug.d.ts.map +1 -1
  87. package/dist/tools/bug.js +262 -259
  88. package/dist/tools/bug.js.map +1 -1
  89. package/dist/tools/changes.js +20 -20
  90. package/dist/tools/changes.js.map +1 -1
  91. package/dist/tools/comment.d.ts.map +1 -1
  92. package/dist/tools/comment.js +26 -23
  93. package/dist/tools/comment.js.map +1 -1
  94. package/dist/tools/custom-fields.js +16 -16
  95. package/dist/tools/custom-fields.js.map +1 -1
  96. package/dist/tools/image.js +2 -2
  97. package/dist/tools/image.js.map +1 -1
  98. package/dist/tools/index.d.ts.map +1 -1
  99. package/dist/tools/index.js +2 -0
  100. package/dist/tools/index.js.map +1 -1
  101. package/dist/tools/iteration.js +50 -50
  102. package/dist/tools/iteration.js.map +1 -1
  103. package/dist/tools/media.d.ts +9 -0
  104. package/dist/tools/media.d.ts.map +1 -0
  105. package/dist/tools/media.js +45 -0
  106. package/dist/tools/media.js.map +1 -0
  107. package/dist/tools/mini-comment.js +27 -27
  108. package/dist/tools/mini-comment.js.map +1 -1
  109. package/dist/tools/mini-item.js +83 -83
  110. package/dist/tools/mini-item.js.map +1 -1
  111. package/dist/tools/mini-workspace.js +5 -5
  112. package/dist/tools/mini-workspace.js.map +1 -1
  113. package/dist/tools/program.js +7 -7
  114. package/dist/tools/program.js.map +1 -1
  115. package/dist/tools/relations.js +14 -14
  116. package/dist/tools/relations.js.map +1 -1
  117. package/dist/tools/release.js +53 -53
  118. package/dist/tools/release.js.map +1 -1
  119. package/dist/tools/report.js +8 -8
  120. package/dist/tools/report.js.map +1 -1
  121. package/dist/tools/richtext-wiring.test.d.ts +2 -0
  122. package/dist/tools/richtext-wiring.test.d.ts.map +1 -0
  123. package/dist/tools/richtext-wiring.test.js +302 -0
  124. package/dist/tools/richtext-wiring.test.js.map +1 -0
  125. package/dist/tools/settings.js +77 -77
  126. package/dist/tools/settings.js.map +1 -1
  127. package/dist/tools/source.js +14 -14
  128. package/dist/tools/source.js.map +1 -1
  129. package/dist/tools/story.d.ts.map +1 -1
  130. package/dist/tools/story.js +123 -120
  131. package/dist/tools/story.js.map +1 -1
  132. package/dist/tools/task.d.ts.map +1 -1
  133. package/dist/tools/task.js +126 -123
  134. package/dist/tools/task.js.map +1 -1
  135. package/dist/tools/test.js +117 -117
  136. package/dist/tools/test.js.map +1 -1
  137. package/dist/tools/timesheet.js +23 -23
  138. package/dist/tools/timesheet.js.map +1 -1
  139. package/dist/tools/user.js +8 -8
  140. package/dist/tools/user.js.map +1 -1
  141. package/dist/tools/utility.d.ts +17 -0
  142. package/dist/tools/utility.d.ts.map +1 -1
  143. package/dist/tools/utility.js +204 -88
  144. package/dist/tools/utility.js.map +1 -1
  145. package/dist/tools/wiki.d.ts.map +1 -1
  146. package/dist/tools/wiki.js +71 -56
  147. package/dist/tools/wiki.js.map +1 -1
  148. package/dist/tools/workflow.js +26 -26
  149. package/dist/tools/workflow.js.map +1 -1
  150. package/dist/tools/workspace.js +16 -16
  151. package/dist/tools/workspace.js.map +1 -1
  152. package/dist/types.d.ts +5 -0
  153. package/dist/types.d.ts.map +1 -1
  154. package/package.json +15 -11
  155. package/dist/tools/zz-acceptance-echo.d.ts +0 -3
  156. package/dist/tools/zz-acceptance-echo.d.ts.map +0 -1
  157. package/dist/tools/zz-acceptance-echo.js +0 -9
  158. package/dist/tools/zz-acceptance-echo.js.map +0 -1
@@ -0,0 +1,302 @@
1
+ /**
2
+ * rc.2 P3 接线测试(需求 1139814312001001548 P3 + 1139814312001001547 P2 md 闭环)。
3
+ *
4
+ * 覆盖(FSD §3.4-3.6 / §3.7 / §8):
5
+ * 1. tapd_md_to_html upload_images:本地图上传替换(mock 上传)、失败清单、远端路径不动、去重单次上传
6
+ * 2. tapd_md_to_html workspace_id:@昵称成员校验 warnings + /users 不可用降级跳过
7
+ * 3. @ 语义 token 级转换:code_span / 链接 URL / 裸 URL / 邮箱 / 工单号内 @ 不误伤
8
+ * 4. raw_html 透传对比:写工具 md 管道 vs raw_html 直发(registry 实链路 + D8 双写不破坏)
9
+ * 5. turndown 规则顺序:保真规则(at-who/附件锚点)先于 GFM/内建规则匹配
10
+ *
11
+ * Run directly: npx tsx src/tools/richtext-wiring.test.ts (from packages/core)
12
+ * Mock client pattern follows attachment.test.ts — no real credentials.
13
+ */
14
+ import assert from 'node:assert/strict';
15
+ import { mkdtemp, rm, writeFile } from 'node:fs/promises';
16
+ import { tmpdir } from 'node:os';
17
+ import path from 'node:path';
18
+ import { ReadStream } from 'node:fs';
19
+ import { ToolRegistry } from '../registry/registry.js';
20
+ import { transformWriteArgs } from '../registry/richtext.js';
21
+ import { utilityTools } from './utility.js';
22
+ import { allTools } from './index.js';
23
+ // ---------------------------------------------------------------------------
24
+ // Recording mock client
25
+ // ---------------------------------------------------------------------------
26
+ class RecordingClient {
27
+ postFileCalls = [];
28
+ getCalls = [];
29
+ postCalls = [];
30
+ postFileResponse = { image_src: '/tfl/pictures/202609/uploaded.png' };
31
+ getResponse = [];
32
+ postResponse = { Comment: { id: '9001' } };
33
+ postFile(p, params) {
34
+ this.postFileCalls.push({ path: p, params });
35
+ return Promise.resolve(this.postFileResponse);
36
+ }
37
+ get(p, params) {
38
+ this.getCalls.push({ path: p, params });
39
+ return Promise.resolve(this.getResponse);
40
+ }
41
+ post(p, params) {
42
+ this.postCalls.push({ path: p, params });
43
+ return Promise.resolve(this.postResponse);
44
+ }
45
+ }
46
+ const WS = 39814312;
47
+ const registry = new ToolRegistry();
48
+ registry.register(allTools);
49
+ const mdToHtmlTool = utilityTools.find(t => t.name === 'tapd_md_to_html');
50
+ const htmlToMdTool = utilityTools.find(t => t.name === 'tapd_html_to_md');
51
+ const createCommentTool = registry.get('tapd_create_comment');
52
+ async function mdRender(params, client = new RecordingClient()) {
53
+ return mdToHtmlTool.handler(client, params);
54
+ }
55
+ async function mdFromHtml(html) {
56
+ const result = await htmlToMdTool.handler({}, { html });
57
+ return result.markdown;
58
+ }
59
+ const AT = (name) => `<b class="at-who" contenteditable="false" data-userid="${name}" data-type="user">@${name}</b>`;
60
+ const tmpRoot = await mkdtemp(path.join(tmpdir(), 'tapd-rc2-wiring-'));
61
+ async function tempFile(name, content) {
62
+ const p = path.join(tmpRoot, name);
63
+ await writeFile(p, content);
64
+ return p;
65
+ }
66
+ const checks = [
67
+ // -------------------------------------------------- 1. upload_images (P2 闭环)
68
+ ['upload_images: 本地图上传并替换为 /tfl/(返回 uploaded_images 清单 + ReadStream 进 multipart)', async () => {
69
+ const img = await tempFile('shot.png', Buffer.from('89504e47', 'hex'));
70
+ const dotted = `${tmpRoot}/./shot.png`; // 同一文件的另一种写法(resolve 后同路径)
71
+ const rec = new RecordingClient();
72
+ const result = await mdRender({ content: `看截图 ![首页](${dotted}) 与 ![首页](${img})`, upload_images: true, workspace_id: WS }, rec);
73
+ // 同一文件去重:单次上传(绝对路径归一)
74
+ assert.equal(rec.postFileCalls.length, 1);
75
+ assert.equal(rec.postFileCalls[0].path, '/files/upload_image');
76
+ assert.ok(rec.postFileCalls[0].params.image instanceof ReadStream);
77
+ const html = result.html;
78
+ assert.ok(html.includes('<img src="/tfl/pictures/202609/uploaded.png" alt="首页">'), html);
79
+ assert.ok(!html.includes('shot.png'));
80
+ // 两种写法都替换为同一 /tfl/ 路径;上传清单只记真实发生的 1 次上传
81
+ assert.equal((html.match(/\/tfl\/pictures\/202609\/uploaded\.png/g) ?? []).length, 2);
82
+ assert.deepEqual(result.uploaded_images, [
83
+ { local_path: dotted, image_src: '/tfl/pictures/202609/uploaded.png' },
84
+ ]);
85
+ assert.equal(result.failed_images, undefined);
86
+ }],
87
+ ['upload_images: 缺失文件列入 failed_images 不中断;远端 /tfl/ 与 http(s) 路径原样保留', async () => {
88
+ const rec = new RecordingClient();
89
+ const result = await mdRender({
90
+ content: '![远端](/tfl/a.png) ![网站](https://cdn.example/x.png) ![丢失](./nope-missing.png)',
91
+ upload_images: true,
92
+ workspace_id: WS,
93
+ }, rec);
94
+ assert.equal(rec.postFileCalls.length, 0, '远端路径绝不触发上传');
95
+ const html = result.html;
96
+ assert.ok(html.includes('src="/tfl/a.png"'));
97
+ assert.ok(html.includes('src="https://cdn.example/x.png"'));
98
+ assert.ok(html.includes('src="./nope-missing.png"'));
99
+ assert.deepEqual(result.failed_images, [{ local_path: './nope-missing.png', reason: 'file not found: ./nope-missing.png (resolved: ' + path.resolve('./nope-missing.png') + ')' }]);
100
+ }],
101
+ ['upload_images: 无 workspace 且无默认配置 → 明确报错;默认 false 零副作用', async () => {
102
+ const rec = new RecordingClient();
103
+ await assert.rejects(mdToHtmlTool.handler(rec, { content: '![a](./x.png)', upload_images: true }), /workspace_id is required/);
104
+ const result = await mdRender({ content: '![a](./x.png)' }, rec);
105
+ assert.equal(rec.postFileCalls.length, 0, '默认关闭时不触达上传通道');
106
+ assert.ok(result.html.includes('src="./x.png"'));
107
+ }],
108
+ ['upload_images 语义:md_to_html 保持读闸门外(write=false),side_effect=upload 供审计(FSD §3.4)', () => {
109
+ const def = registry.get('tapd_md_to_html');
110
+ assert.equal(def.write ?? false, false, '不计入实体写闸门');
111
+ assert.equal(def.side_effect, 'upload');
112
+ }],
113
+ // -------------------------------------------------- 2. @昵称成员校验 (§3.6)
114
+ ['workspace_id 提供时:未命中昵称进 warnings,命中不提示', async () => {
115
+ const rec = new RecordingClient();
116
+ rec.getResponse = [{ User: { user: '徐昭' } }, { User: { user: 'bob' } }];
117
+ const result = await mdRender({ content: '请 @徐昭 与 @ghost 与 @bob 处理', workspace_id: WS }, rec);
118
+ assert.equal(rec.getCalls[0].path, '/users');
119
+ assert.equal((rec.getCalls[0].params).workspace_id, WS);
120
+ assert.ok(result.html.includes(AT('徐昭')));
121
+ assert.deepEqual(result.warnings, ['@ghost 未命中项目成员昵称(请核对昵称拼写;关键通知场景建议同时填 cc)']);
122
+ }],
123
+ ['workspace_id 校验降级:/users 403/失败 → 跳过校验并提示,转换不受影响', async () => {
124
+ const rec = new RecordingClient();
125
+ rec.get = () => Promise.reject(new Error('403 Forbidden: users::_get required'));
126
+ const result = await mdRender({ content: '请 @徐昭 处理', workspace_id: WS }, rec);
127
+ assert.ok(result.html.includes(AT('徐昭')));
128
+ const warnings = result.warnings;
129
+ assert.equal(warnings.length, 1);
130
+ assert.match(warnings[0], /成员校验已跳过.*403/);
131
+ assert.match(warnings[0], /服务端智能识别/);
132
+ }],
133
+ ['未提供 workspace_id:零额外请求(校验不触发),纯转换', async () => {
134
+ const rec = new RecordingClient();
135
+ const result = await mdRender({ content: '请 @徐昭 处理' }, rec);
136
+ assert.equal(rec.getCalls.length, 0);
137
+ assert.equal(result.warnings, undefined);
138
+ assert.ok(result.html.includes(AT('徐昭')));
139
+ }],
140
+ // -------------------------------------------------- 3. @ token 级不误伤 (§8)
141
+ ['@ token 级:code_span / 链接 URL / 裸 URL / 邮箱 / 工单号 内 @ 不误伤', async () => {
142
+ const result = await mdRender({ content: '联系 user@example.com;工单 T-100@QA;代码 `@code_x`;链接 [标题](https://e.com/@li);裸地址 https://e.com/@wang;真提及 @徐昭' });
143
+ const html = result.html;
144
+ assert.ok(html.includes('user@example.com'));
145
+ assert.ok(html.includes('T-100@QA'));
146
+ assert.ok(html.includes('<code>@code_x</code>'));
147
+ assert.ok(html.includes('href="https://e.com/@li"'));
148
+ assert.ok(html.includes('https://e.com/@wang'));
149
+ assert.equal(html.match(/at-who/g)?.length, 1, '仅真提及生成一个 at-who');
150
+ assert.ok(html.includes(AT('徐昭')));
151
+ }],
152
+ ['@ 转义 @@ 与列表/引用上下文:@@ → 字面 @,块内提及照常', async () => {
153
+ const result = await mdRender({ content: '- 字面 @@support\n> 引用里 @李四' });
154
+ const html = result.html;
155
+ assert.ok(html.includes('字面 @support'));
156
+ assert.ok(!html.includes('@@'));
157
+ assert.ok(html.includes(AT('李四')));
158
+ }],
159
+ ['attach: 链接 token 级转换:→ R6 五件套锚点;链接文本/URL 内 @ 不二次处理', async () => {
160
+ const result = await mdRender({ content: `材料 [📎 报表@v2.txt](attach:${WS}/1139814312001000001),普通 [链接](https://e.com)。` });
161
+ const html = result.html;
162
+ assert.ok(html.includes('data-is-tapd-attachment="true"'));
163
+ assert.ok(html.includes('data-file-type="text"'));
164
+ assert.ok(html.includes('data-name="报表@v2.txt"'));
165
+ assert.ok(html.includes(`href="/${WS}/attachments/preview_attachments/1139814312001000001/story_description_attachment"`));
166
+ assert.ok(html.includes('href="https://e.com"'));
167
+ assert.ok(!html.includes('attach:'), 'attach: scheme 已被锚点替换');
168
+ }],
169
+ // -------------------------------------------------- 4. raw_html 透传对比 (§3.5)
170
+ ['raw_html=false(默认):写管道 md→html + @/附件语义自动转换', async () => {
171
+ const rec = new RecordingClient();
172
+ await registry.exec('tapd_create_comment', { workspace_id: WS, entry_type: 'stories', entry_id: '1139814312001001533', description: '变更通知:@徐昭 请确认,材料 [📎 说明.txt](attach:39814312/1)' }, { entry: 'cli' }, () => rec);
173
+ const params = rec.postCalls[0].params;
174
+ assert.equal(params.description, `<p>变更通知:${AT('徐昭')} 请确认,材料 <a data-is-tapd-attachment="true" data-can-preview="true" data-file-type="text" data-name="说明.txt" target="_blank" rel="noopener" href="/39814312/attachments/preview_attachments/1/story_description_attachment">说明.txt</a></p>\n`);
175
+ assert.equal(params.raw_html, undefined, 'raw_html 参数消费后不透传 TAPD API');
176
+ }],
177
+ ['raw_html=true:description 原样透传(绕过 md→html),参数摘除', async () => {
178
+ const rec = new RecordingClient();
179
+ const raw = '<p>原文 <b>HTML</b>:@徐昭 不转换、[📎 a.txt](attach:1/2) 不转换</p>';
180
+ await registry.exec('tapd_create_comment', { workspace_id: WS, entry_type: 'stories', entry_id: '1139814312001001533', description: raw, raw_html: true }, { entry: 'cli' }, () => rec);
181
+ const params = rec.postCalls[0].params;
182
+ assert.equal(params.description, raw, '一字不改直发(R7 服务端保真)');
183
+ assert.equal(params.raw_html, undefined);
184
+ }],
185
+ ['raw_html 对比 registry.exec:md 管道 vs raw_html 同文异果', async () => {
186
+ const mdPath = await registry.exec('tapd_create_comment', { workspace_id: WS, entry_type: 'stories', entry_id: '1', description: 'hello @徐昭 **加粗**' }, { entry: 'cli' }, () => ({ post: () => Promise.resolve({}) }));
187
+ assert.equal(mdPath.ok, true);
188
+ const rawRec = new RecordingClient();
189
+ await registry.exec('tapd_create_comment', { workspace_id: WS, entry_type: 'stories', entry_id: '1', description: 'hello @徐昭 **加粗**', raw_html: true }, { entry: 'cli' }, () => rawRec);
190
+ assert.equal((rawRec.postCalls[0].params).description, 'hello @徐昭 **加粗**');
191
+ }],
192
+ ['raw_html=true:D8 双写不破坏(wiki markdown_description 维持既有策略——永不自动改写、无 md 源时不回填)', () => {
193
+ const raw = '<p>HTML 正文 @徐昭</p>';
194
+ // wiki:raw_html 时 description 透传,markdown_description 不被自动回填为 HTML
195
+ const wikiRaw = transformWriteArgs('tapd_create_wiki', { description: raw, raw_html: true });
196
+ assert.equal(wikiRaw.description, raw);
197
+ assert.equal(wikiRaw.markdown_description, undefined);
198
+ assert.equal(wikiRaw.raw_html, undefined);
199
+ // 对照组(raw_html=false):双写生效——description 渲染、markdown_description 保留 md 源
200
+ const wikiMd = transformWriteArgs('tapd_create_wiki', { description: '# 标题 @徐昭' });
201
+ assert.ok(wikiMd.description.includes('<h1>标题 '));
202
+ assert.ok(wikiMd.description.includes(AT('徐昭')));
203
+ assert.equal(wikiMd.markdown_description, '# 标题 @徐昭');
204
+ // 用户显式提供的 markdown_description 始终原样(raw_html 与否一致)
205
+ const wikiExplicit = transformWriteArgs('tapd_create_wiki', { description: raw, markdown_description: '自有 md', raw_html: true });
206
+ assert.equal(wikiExplicit.markdown_description, '自有 md');
207
+ // story(非双写):raw_html 透传;对照走管道
208
+ const storyRaw = transformWriteArgs('tapd_create_story', { description: raw, raw_html: true });
209
+ assert.equal(storyRaw.description, raw);
210
+ const storyMd = transformWriteArgs('tapd_create_story', { description: '正文 @徐昭' });
211
+ assert.ok(storyMd.description.includes(AT('徐昭')));
212
+ // raw_html 仅是语义开关:TAPD_RICHTEXT_AUTO=0 时同样摘除、全原样
213
+ const prev = process.env.TAPD_RICHTEXT_AUTO;
214
+ process.env.TAPD_RICHTEXT_AUTO = '0';
215
+ try {
216
+ const off = transformWriteArgs('tapd_create_comment', { description: '正文 @徐昭', raw_html: true });
217
+ assert.equal(off.description, '正文 @徐昭');
218
+ assert.equal(off.raw_html, undefined);
219
+ }
220
+ finally {
221
+ if (prev === undefined)
222
+ delete process.env.TAPD_RICHTEXT_AUTO;
223
+ else
224
+ process.env.TAPD_RICHTEXT_AUTO = prev;
225
+ }
226
+ }],
227
+ // -------------------------------------------------- 5. turndown 规则顺序 (§8)
228
+ ['turndown 保真规则先于 GFM/内建规则:at-who 不降级 **粗体**,附件锚点不被当普通链接', async () => {
229
+ const md = await mdFromHtml(`<p>负责人 ${AT('徐昭')},材料 <a data-is-tapd-attachment="true" data-can-preview="true" data-file-type="pdf" data-name="设计.pdf" data-size="9" target="_blank" rel="noopener" href="/39814312/attachments/preview_attachments/2/story_description_attachment">设计.pdf</a></p>`);
230
+ assert.ok(md.includes('@徐昭'));
231
+ assert.ok(!md.includes('**'), `at-who 禁止降级为粗体: ${md}`);
232
+ assert.ok(md.includes('[📎 设计.pdf](attach:39814312/2)'));
233
+ assert.ok(!md.includes('href='), '附件锚点不得残留为普通链接');
234
+ }],
235
+ ['turndown 规则优先级:保真规则 unshift 后先于 GFM 表格/删除线与内建 strong/inlineLink 匹配', async () => {
236
+ // 表格单元格 + 删除线 + 粗体 + 链接混排:保真语义优先,GFM 其余能力不受影响
237
+ const html = '<table><thead><tr><th>负责人</th><th>材料</th></tr></thead><tbody><tr><td>' + AT('徐昭') + '</td><td>' +
238
+ '<a data-is-tapd-attachment="true" data-can-preview="true" data-file-type="other" data-name="a.bin" href="/39814312/attachments/preview_attachments/3/story_description_attachment">a.bin</a>' +
239
+ '</td></tr></tbody></table><p><b>普通粗体</b> <a href="https://e.com">普通链接</a></p>';
240
+ const md = await mdFromHtml(html);
241
+ assert.ok(md.includes('@徐昭'));
242
+ assert.ok(md.includes('[📎 a.bin](attach:39814312/3)'));
243
+ assert.ok(md.includes('**普通粗体**'));
244
+ assert.ok(md.includes('[普通链接](https://e.com)'));
245
+ assert.match(md, /\|\s*负责人\s*\|\s*材料\s*\|/);
246
+ }],
247
+ ['html→md:字面 @ 加倍保 round-trip,CODE 内 @ 不加倍', async () => {
248
+ const md = await mdFromHtml('<p>找 @徐昭 或字面 @support,代码 <code>@code_x</code></p>');
249
+ assert.ok(md.includes('@@徐昭'), 'html→md 方向 mention 加倍为 @@(round-trip 稳定)');
250
+ assert.ok(md.includes('@@support'));
251
+ assert.ok(md.includes('`@code_x`'), 'code_span 内 @ 原样');
252
+ // round-trip 稳定
253
+ const once = await mdRender({ content: md });
254
+ const again = await mdFromHtml(once.html);
255
+ assert.equal(again, md);
256
+ }],
257
+ ['registry 全量注册:10 个富文本写工具均带 raw_html 通道(comment/story/task/bug/wiki create+update)', () => {
258
+ const expected = [
259
+ 'tapd_create_comment', 'tapd_update_comment',
260
+ 'tapd_create_story', 'tapd_update_story',
261
+ 'tapd_create_task', 'tapd_update_task',
262
+ 'tapd_create_bug', 'tapd_update_bug',
263
+ 'tapd_create_wiki', 'tapd_update_wiki',
264
+ ];
265
+ for (const name of expected) {
266
+ const tool = registry.get(name);
267
+ // 从 zod schema shape 中确认 raw_html 字段存在且默认 false
268
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
269
+ const shape = tool.inputSchema.shape;
270
+ assert.ok(shape.raw_html, `${name} 缺 raw_html 参数`);
271
+ }
272
+ // 基线不回归:工具总数保持 216 + tapd_md_to_html/html_to_md 增参不新增工具
273
+ assert.equal(registry.list().length, 216);
274
+ }],
275
+ ['cli meta:md-to-html 增参不影响派生命令;tapd_html_to_md 纯读不受写闸门影响', async () => {
276
+ assert.equal(createCommentTool.name, 'tapd_create_comment');
277
+ const back = await registry.exec('tapd_html_to_md', { html: `<p>${AT('徐昭')}</p>` }, { entry: 'cli', readOnly: true }, () => ({}));
278
+ assert.equal(back.ok, true);
279
+ assert.equal(back.data.markdown, '@徐昭');
280
+ }],
281
+ ];
282
+ let failed = 0;
283
+ for (const [name, check] of checks) {
284
+ try {
285
+ await check();
286
+ console.log(`PASS ${name}`);
287
+ }
288
+ catch (error) {
289
+ failed++;
290
+ console.error(`FAIL ${name}`);
291
+ console.error(` ${error instanceof Error ? error.stack ?? error.message : String(error)}`);
292
+ }
293
+ }
294
+ await rm(tmpRoot, { recursive: true, force: true });
295
+ if (failed > 0) {
296
+ console.error(`\n${failed}/${checks.length} failed`);
297
+ process.exitCode = 1;
298
+ }
299
+ else {
300
+ console.log(`\n${checks.length}/${checks.length} passed`);
301
+ }
302
+ //# sourceMappingURL=richtext-wiring.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"richtext-wiring.test.js","sourceRoot":"","sources":["../../src/tools/richtext-wiring.test.ts"],"names":[],"mappings":"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"}