@xihe-lab/tapd-cli 2.0.0-rc.5 → 2.0.0-rc.6
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/LICENSE +16 -4
- package/README.md +13 -1
- package/package.json +7 -4
- package/dist/advisor/domain-axis.test.d.ts +0 -2
- package/dist/advisor/domain-axis.test.d.ts.map +0 -1
- package/dist/advisor/domain-axis.test.js +0 -198
- package/dist/advisor/domain-axis.test.js.map +0 -1
- package/dist/flags.test.d.ts +0 -2
- package/dist/flags.test.d.ts.map +0 -1
- package/dist/flags.test.js +0 -56
- package/dist/flags.test.js.map +0 -1
- package/dist/output/formatter.test.d.ts +0 -2
- package/dist/output/formatter.test.d.ts.map +0 -1
- package/dist/output/formatter.test.js +0 -170
- package/dist/output/formatter.test.js.map +0 -1
- package/dist/pipeline/engine.test.d.ts +0 -2
- package/dist/pipeline/engine.test.d.ts.map +0 -1
- package/dist/pipeline/engine.test.js +0 -419
- package/dist/pipeline/engine.test.js.map +0 -1
- package/dist/pipeline/expression.test.d.ts +0 -2
- package/dist/pipeline/expression.test.d.ts.map +0 -1
- package/dist/pipeline/expression.test.js +0 -111
- package/dist/pipeline/expression.test.js.map +0 -1
- package/dist/pipeline/record.test.d.ts +0 -2
- package/dist/pipeline/record.test.d.ts.map +0 -1
- package/dist/pipeline/record.test.js +0 -109
- package/dist/pipeline/record.test.js.map +0 -1
- package/dist/pipeline/template.test.d.ts +0 -2
- package/dist/pipeline/template.test.d.ts.map +0 -1
- package/dist/pipeline/template.test.js +0 -94
- package/dist/pipeline/template.test.js.map +0 -1
- package/dist/pipeline/unwrap.test.d.ts +0 -2
- package/dist/pipeline/unwrap.test.d.ts.map +0 -1
- package/dist/pipeline/unwrap.test.js +0 -36
- package/dist/pipeline/unwrap.test.js.map +0 -1
- package/dist/pipeline/validate.test.d.ts +0 -2
- package/dist/pipeline/validate.test.d.ts.map +0 -1
- package/dist/pipeline/validate.test.js +0 -166
- package/dist/pipeline/validate.test.js.map +0 -1
- package/dist/pm/discovery.test.d.ts +0 -2
- package/dist/pm/discovery.test.d.ts.map +0 -1
- package/dist/pm/discovery.test.js +0 -147
- package/dist/pm/discovery.test.js.map +0 -1
- package/dist/pm/domains.test.d.ts +0 -2
- package/dist/pm/domains.test.d.ts.map +0 -1
- package/dist/pm/domains.test.js +0 -141
- package/dist/pm/domains.test.js.map +0 -1
- package/dist/pm/iteration.test.d.ts +0 -2
- package/dist/pm/iteration.test.d.ts.map +0 -1
- package/dist/pm/iteration.test.js +0 -125
- package/dist/pm/iteration.test.js.map +0 -1
- package/dist/pm/pm-command.test.d.ts +0 -2
- package/dist/pm/pm-command.test.d.ts.map +0 -1
- package/dist/pm/pm-command.test.js +0 -321
- package/dist/pm/pm-command.test.js.map +0 -1
- package/dist/pm/tpl-communication-stakeholder.test.d.ts +0 -2
- package/dist/pm/tpl-communication-stakeholder.test.d.ts.map +0 -1
- package/dist/pm/tpl-communication-stakeholder.test.js +0 -392
- package/dist/pm/tpl-communication-stakeholder.test.js.map +0 -1
- package/dist/pm/tpl-integration-scope.test.d.ts +0 -2
- package/dist/pm/tpl-integration-scope.test.d.ts.map +0 -1
- package/dist/pm/tpl-integration-scope.test.js +0 -387
- package/dist/pm/tpl-integration-scope.test.js.map +0 -1
- package/dist/pm/tpl-quality-risk.test.d.ts +0 -2
- package/dist/pm/tpl-quality-risk.test.d.ts.map +0 -1
- package/dist/pm/tpl-quality-risk.test.js +0 -414
- package/dist/pm/tpl-quality-risk.test.js.map +0 -1
- package/dist/pm/tpl-schedule-cost.test.d.ts +0 -2
- package/dist/pm/tpl-schedule-cost.test.d.ts.map +0 -1
- package/dist/pm/tpl-schedule-cost.test.js +0 -376
- package/dist/pm/tpl-schedule-cost.test.js.map +0 -1
|
@@ -1,109 +0,0 @@
|
|
|
1
|
-
import assert from 'node:assert/strict';
|
|
2
|
-
import { mkdtempSync, rmSync, writeFileSync } from 'node:fs';
|
|
3
|
-
import { tmpdir } from 'node:os';
|
|
4
|
-
import path from 'node:path';
|
|
5
|
-
import { allTools, ToolRegistry, resolveWrite } from '@xihe-lab/tapd-core';
|
|
6
|
-
import YAML from 'yaml';
|
|
7
|
-
import { historyPath, readHistory, recordCommandHistory } from './history.js';
|
|
8
|
-
import { buildTemplateFromHistory, renderTemplateYaml } from './record.js';
|
|
9
|
-
import { parseTemplate, resolveTemplatePath } from './template.js';
|
|
10
|
-
import { validateTemplate } from './validate.js';
|
|
11
|
-
const sandboxDir = mkdtempSync(path.join(tmpdir(), 'tapd-history-test-'));
|
|
12
|
-
const sandboxFile = path.join(sandboxDir, 'history.jsonl');
|
|
13
|
-
const originalPath = process.env.TAPD_HISTORY_PATH;
|
|
14
|
-
process.env.TAPD_HISTORY_PATH = sandboxFile;
|
|
15
|
-
const registry = new ToolRegistry();
|
|
16
|
-
registry.register(allTools);
|
|
17
|
-
const audit = (tool, ok = true) => ({
|
|
18
|
-
ts: new Date().toISOString(),
|
|
19
|
-
entry: 'cli',
|
|
20
|
-
tool,
|
|
21
|
-
args: { id: '1001' },
|
|
22
|
-
ok,
|
|
23
|
-
durationMs: 5,
|
|
24
|
-
});
|
|
25
|
-
const checks = [
|
|
26
|
-
['recordCommandHistory: 成功命令追加 JSONL,失败命令不记录', () => {
|
|
27
|
-
recordCommandHistory(audit('tapd_get_stories'));
|
|
28
|
-
recordCommandHistory(audit('tapd_update_story'));
|
|
29
|
-
recordCommandHistory(audit('tapd_get_bugs', false));
|
|
30
|
-
const entries = readHistory(10);
|
|
31
|
-
assert.deepEqual(entries.map(e => e.tool), ['tapd_get_stories', 'tapd_update_story']);
|
|
32
|
-
assert.equal(entries[0].args.id, '1001');
|
|
33
|
-
assert.ok(historyPath().startsWith(sandboxDir));
|
|
34
|
-
}],
|
|
35
|
-
['readHistory: --last 截取最近 N 条', () => {
|
|
36
|
-
const entries = readHistory(1);
|
|
37
|
-
assert.equal(entries.length, 1);
|
|
38
|
-
assert.equal(entries[0].tool, 'tapd_update_story');
|
|
39
|
-
}],
|
|
40
|
-
['readHistory: 损坏行被跳过', () => {
|
|
41
|
-
writeFileSync(sandboxFile, readHistory(10).map(e => JSON.stringify(e)).concat('{broken').join('\n') + '\n');
|
|
42
|
-
assert.equal(readHistory(10).length, 2);
|
|
43
|
-
}],
|
|
44
|
-
['buildTemplateFromHistory: 写命令经 write-policy 判定 → 模板自动 write: true', () => {
|
|
45
|
-
const entries = readHistory(10);
|
|
46
|
-
const isWrite = (tool) => {
|
|
47
|
-
const def = registry.get(tool);
|
|
48
|
-
return def ? resolveWrite(def) : false;
|
|
49
|
-
};
|
|
50
|
-
const template = buildTemplateFromHistory(entries, { name: 'recorded', title: '录制' }, isWrite);
|
|
51
|
-
assert.equal(template.write, true, 'tapd_update_story 命中写策略');
|
|
52
|
-
assert.equal(template.name, 'recorded');
|
|
53
|
-
assert.equal(template.steps.length, 2);
|
|
54
|
-
assert.equal(template.steps[1].uses, 'tapd_update_story');
|
|
55
|
-
}],
|
|
56
|
-
['buildTemplateFromHistory: 全只读历史 → 模板不带 write 标记', () => {
|
|
57
|
-
const isWrite = (tool) => {
|
|
58
|
-
const def = registry.get(tool);
|
|
59
|
-
return def ? resolveWrite(def) : false;
|
|
60
|
-
};
|
|
61
|
-
const template = buildTemplateFromHistory([{ ts: 't', tool: 'tapd_get_bugs', args: {}, durationMs: 1 }], { name: 'read-only-rec' }, isWrite);
|
|
62
|
-
assert.equal(template.write, undefined);
|
|
63
|
-
}],
|
|
64
|
-
['录制产物闭环: record → yaml → parse → validate 全绿', () => {
|
|
65
|
-
const isWrite = (tool) => {
|
|
66
|
-
const def = registry.get(tool);
|
|
67
|
-
return def ? resolveWrite(def) : false;
|
|
68
|
-
};
|
|
69
|
-
const template = buildTemplateFromHistory(readHistory(10), { name: 'recorded-roundtrip' }, isWrite);
|
|
70
|
-
const yaml = renderTemplateYaml(template);
|
|
71
|
-
const reparsed = parseTemplate(yaml, 'roundtrip');
|
|
72
|
-
assert.equal(reparsed.name, 'recorded-roundtrip');
|
|
73
|
-
const result = validateTemplate(reparsed, registry);
|
|
74
|
-
assert.equal(result.ok, true, JSON.stringify(result.errors));
|
|
75
|
-
const roundtrip = YAML.parse(yaml);
|
|
76
|
-
assert.equal(roundtrip.steps[0].args.id, '1001');
|
|
77
|
-
}],
|
|
78
|
-
['resolveTemplatePath: 录制产物写入内置目录后可用裸名解析(临时目录模拟)', () => {
|
|
79
|
-
// 不真的写入包目录(避免污染仓库),只验证解析逻辑对扩展名回退的行为
|
|
80
|
-
assert.throws(() => resolveTemplatePath('definitely-not-a-template'), Error);
|
|
81
|
-
}],
|
|
82
|
-
];
|
|
83
|
-
let failed = 0;
|
|
84
|
-
try {
|
|
85
|
-
for (const [name, fn] of checks) {
|
|
86
|
-
try {
|
|
87
|
-
await fn();
|
|
88
|
-
console.log(`ok - ${name}`);
|
|
89
|
-
}
|
|
90
|
-
catch (error) {
|
|
91
|
-
failed++;
|
|
92
|
-
console.error(`FAIL - ${name}`);
|
|
93
|
-
console.error(error);
|
|
94
|
-
}
|
|
95
|
-
}
|
|
96
|
-
}
|
|
97
|
-
finally {
|
|
98
|
-
if (originalPath === undefined)
|
|
99
|
-
delete process.env.TAPD_HISTORY_PATH;
|
|
100
|
-
else
|
|
101
|
-
process.env.TAPD_HISTORY_PATH = originalPath;
|
|
102
|
-
rmSync(sandboxDir, { recursive: true, force: true });
|
|
103
|
-
}
|
|
104
|
-
if (failed > 0) {
|
|
105
|
-
console.error(`\n${failed}/${checks.length} failed`);
|
|
106
|
-
process.exit(1);
|
|
107
|
-
}
|
|
108
|
-
console.log(`\n${checks.length}/${checks.length} passed`);
|
|
109
|
-
//# sourceMappingURL=record.test.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"record.test.js","sourceRoot":"","sources":["../../src/pipeline/record.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"template.test.d.ts","sourceRoot":"","sources":["../../src/pipeline/template.test.ts"],"names":[],"mappings":""}
|
|
@@ -1,94 +0,0 @@
|
|
|
1
|
-
import assert from 'node:assert/strict';
|
|
2
|
-
import { listBuiltinTemplates, parseTemplate, resolveTemplatePath } from './template.js';
|
|
3
|
-
import { PipelineError } from './types.js';
|
|
4
|
-
const checks = [
|
|
5
|
-
['parseTemplate: 合法模板完整解析(name/title/write/vars/steps/report)', () => {
|
|
6
|
-
const tpl = parseTemplate(`
|
|
7
|
-
name: demo
|
|
8
|
-
title: 演示模板
|
|
9
|
-
write: true
|
|
10
|
-
vars:
|
|
11
|
-
iteration_id: "1001"
|
|
12
|
-
steps:
|
|
13
|
-
- id: a
|
|
14
|
-
uses: tapd_get_stories
|
|
15
|
-
args: { iteration_id: "${'$'}{vars.iteration_id}" }
|
|
16
|
-
filter: "!item.name.startsWith('zzz')"
|
|
17
|
-
- id: b
|
|
18
|
-
uses: tapd_update_story
|
|
19
|
-
args: { id: "1", status: developing }
|
|
20
|
-
depends_on: [a]
|
|
21
|
-
report:
|
|
22
|
-
format: markdown
|
|
23
|
-
template: |
|
|
24
|
-
# 报告 ${'$'}{steps.a.data | length}
|
|
25
|
-
`, 'inline');
|
|
26
|
-
assert.equal(tpl.name, 'demo');
|
|
27
|
-
assert.equal(tpl.write, true);
|
|
28
|
-
assert.equal(tpl.vars?.iteration_id, '1001');
|
|
29
|
-
assert.equal(tpl.steps.length, 2);
|
|
30
|
-
assert.equal(tpl.steps[1].depends_on?.[0], 'a');
|
|
31
|
-
assert.equal(tpl.report?.format, 'markdown');
|
|
32
|
-
}],
|
|
33
|
-
['parseTemplate: json 源同样可解析(YAML 是 JSON 超集)', () => {
|
|
34
|
-
const tpl = parseTemplate(JSON.stringify({ name: 'j', steps: [{ id: 's', uses: 'tapd_get_bugs' }] }), 'inline');
|
|
35
|
-
assert.equal(tpl.name, 'j');
|
|
36
|
-
assert.equal(tpl.steps[0].uses, 'tapd_get_bugs');
|
|
37
|
-
}],
|
|
38
|
-
['parseTemplate: yaml 语法错误 → TEMPLATE_INVALID', () => {
|
|
39
|
-
assert.throws(() => parseTemplate('name: [unclosed', 'inline'), (error) => error instanceof PipelineError && error.code === 'TEMPLATE_INVALID');
|
|
40
|
-
}],
|
|
41
|
-
['parseTemplate: 缺 name / 空 steps → TEMPLATE_INVALID', () => {
|
|
42
|
-
assert.throws(() => parseTemplate('steps: [{ id: a, uses: x }]', 'inline'), (error) => error instanceof PipelineError && error.code === 'TEMPLATE_INVALID');
|
|
43
|
-
assert.throws(() => parseTemplate('name: demo, steps: []'.replace(',', '\n'), 'inline'), (error) => error instanceof PipelineError && error.code === 'TEMPLATE_INVALID');
|
|
44
|
-
}],
|
|
45
|
-
['parseTemplate: 步骤缺 uses/expr 或两者同存 → TEMPLATE_INVALID', () => {
|
|
46
|
-
assert.throws(() => parseTemplate('name: d\nsteps:\n - id: a\n args: {}', 'inline'), (error) => error instanceof PipelineError && error.code === 'TEMPLATE_INVALID');
|
|
47
|
-
assert.throws(() => parseTemplate('name: d\nsteps:\n - id: a\n uses: tapd_get_bugs\n expr: "1+1"', 'inline'), (error) => error instanceof PipelineError && error.code === 'TEMPLATE_INVALID');
|
|
48
|
-
}],
|
|
49
|
-
['parseTemplate: 步骤 id 重复 → TEMPLATE_INVALID', () => {
|
|
50
|
-
assert.throws(() => parseTemplate('name: d\nsteps:\n - id: a\n uses: tapd_get_bugs\n - id: a\n uses: tapd_get_bugs', 'inline'), (error) => error instanceof PipelineError && error.code === 'TEMPLATE_INVALID');
|
|
51
|
-
}],
|
|
52
|
-
['parseTemplate: depends_on 指向缺失步骤 → TEMPLATE_INVALID', () => {
|
|
53
|
-
assert.throws(() => parseTemplate('name: d\nsteps:\n - id: a\n uses: tapd_get_bugs\n depends_on: [ghost]', 'inline'), (error) => error instanceof PipelineError && error.code === 'TEMPLATE_INVALID');
|
|
54
|
-
}],
|
|
55
|
-
['parseTemplate: 未知顶层字段 → TEMPLATE_INVALID(strict)', () => {
|
|
56
|
-
assert.throws(() => parseTemplate('name: d\nsteps:\n - id: a\n uses: tapd_get_bugs\nbogus: 1', 'inline'), (error) => error instanceof PipelineError && error.code === 'TEMPLATE_INVALID');
|
|
57
|
-
}],
|
|
58
|
-
['resolveTemplatePath: 内置模板裸名与 templates/ 相对名都可解析', () => {
|
|
59
|
-
const bare = resolveTemplatePath('iteration-review');
|
|
60
|
-
const rel = resolveTemplatePath('templates/iteration-review.yaml');
|
|
61
|
-
assert.ok(bare.endsWith('templates/iteration-review.yaml'), bare);
|
|
62
|
-
assert.equal(bare, rel);
|
|
63
|
-
}],
|
|
64
|
-
['resolveTemplatePath: 找不到 → IO 错误并列出尝试路径', () => {
|
|
65
|
-
assert.throws(() => resolveTemplatePath('no-such-template'), (error) => error instanceof PipelineError && error.code === 'IO' && error.message.includes('no-such-template'));
|
|
66
|
-
}],
|
|
67
|
-
['listBuiltinTemplates: 三个内置模板齐全且声明正确的 write 标记', () => {
|
|
68
|
-
const list = listBuiltinTemplates();
|
|
69
|
-
const byName = Object.fromEntries(list.map(t => [t.name, t]));
|
|
70
|
-
assert.deepEqual(Object.keys(byName).sort(), ['deliver-story', 'iteration-review', 'triage-bugs']);
|
|
71
|
-
assert.equal(byName['iteration-review'].write, false);
|
|
72
|
-
assert.equal(byName['deliver-story'].write, true);
|
|
73
|
-
assert.equal(byName['triage-bugs'].write, true);
|
|
74
|
-
assert.ok(byName['iteration-review'].title.length > 0);
|
|
75
|
-
}],
|
|
76
|
-
];
|
|
77
|
-
let failed = 0;
|
|
78
|
-
for (const [name, fn] of checks) {
|
|
79
|
-
try {
|
|
80
|
-
await fn();
|
|
81
|
-
console.log(`ok - ${name}`);
|
|
82
|
-
}
|
|
83
|
-
catch (error) {
|
|
84
|
-
failed++;
|
|
85
|
-
console.error(`FAIL - ${name}`);
|
|
86
|
-
console.error(error);
|
|
87
|
-
}
|
|
88
|
-
}
|
|
89
|
-
if (failed > 0) {
|
|
90
|
-
console.error(`\n${failed}/${checks.length} failed`);
|
|
91
|
-
process.exit(1);
|
|
92
|
-
}
|
|
93
|
-
console.log(`\n${checks.length}/${checks.length} passed`);
|
|
94
|
-
//# sourceMappingURL=template.test.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"template.test.js","sourceRoot":"","sources":["../../src/pipeline/template.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"unwrap.test.d.ts","sourceRoot":"","sources":["../../src/pipeline/unwrap.test.ts"],"names":[],"mappings":""}
|
|
@@ -1,36 +0,0 @@
|
|
|
1
|
-
// unwrapEntities 单元测试:TAPD 包裹形态拍平(真机 L2 冒烟暴露的形态差异,2026-09-11)
|
|
2
|
-
import { test } from 'node:test';
|
|
3
|
-
import assert from 'node:assert/strict';
|
|
4
|
-
import { unwrapEntities } from './unwrap.js';
|
|
5
|
-
void test('数组逐项解包并附 __entity', () => {
|
|
6
|
-
const data = [
|
|
7
|
-
{ Story: { id: '1', name: 'a' } },
|
|
8
|
-
{ Story: { id: '2', name: 'b' } },
|
|
9
|
-
];
|
|
10
|
-
assert.deepEqual(unwrapEntities(data), [
|
|
11
|
-
{ id: '1', name: 'a', __entity: 'Story' },
|
|
12
|
-
{ id: '2', name: 'b', __entity: 'Story' },
|
|
13
|
-
]);
|
|
14
|
-
});
|
|
15
|
-
void test('单对象解包(按 id 查询返回)', () => {
|
|
16
|
-
assert.deepEqual(unwrapEntities({ Iteration: { id: '9', name: 'it' } }), { id: '9', name: 'it', __entity: 'Iteration' });
|
|
17
|
-
});
|
|
18
|
-
void test('小写单键对象不动(count 等非实体形态)', () => {
|
|
19
|
-
assert.deepEqual(unwrapEntities({ count: 3 }), { count: 3 });
|
|
20
|
-
assert.equal(unwrapEntities(42), 42);
|
|
21
|
-
assert.equal(unwrapEntities('x'), 'x');
|
|
22
|
-
assert.equal(unwrapEntities(null), null);
|
|
23
|
-
});
|
|
24
|
-
void test('多键对象不动(已扁平/混合形态)', () => {
|
|
25
|
-
const flat = { id: '1', name: 'a', status: 'planning' };
|
|
26
|
-
assert.deepEqual(unwrapEntities(flat), flat);
|
|
27
|
-
});
|
|
28
|
-
void test('包裹值为标量不动(单键值为原始类型的陷阱形态)', () => {
|
|
29
|
-
assert.deepEqual(unwrapEntities({ Code: 'x' }), { Code: 'x' });
|
|
30
|
-
});
|
|
31
|
-
void test('混合数组:能解的解,不能解的保原样', () => {
|
|
32
|
-
const out = unwrapEntities([{ Bug: { id: '1' } }, { note: 'plain' }]);
|
|
33
|
-
assert.deepEqual(out[0], { id: '1', __entity: 'Bug' });
|
|
34
|
-
assert.deepEqual(out[1], { note: 'plain' });
|
|
35
|
-
});
|
|
36
|
-
//# sourceMappingURL=unwrap.test.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"unwrap.test.js","sourceRoot":"","sources":["../../src/pipeline/unwrap.test.ts"],"names":[],"mappings":"AAAA,8DAA8D;AAC9D,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,MAAM,MAAM,oBAAoB,CAAC;AACxC,OAAO,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAE7C,KAAK,IAAI,CAAC,mBAAmB,EAAE,GAAG,EAAE;IAClC,MAAM,IAAI,GAAG;QACX,EAAE,KAAK,EAAE,EAAE,EAAE,EAAE,GAAG,EAAE,IAAI,EAAE,GAAG,EAAE,EAAE;QACjC,EAAE,KAAK,EAAE,EAAE,EAAE,EAAE,GAAG,EAAE,IAAI,EAAE,GAAG,EAAE,EAAE;KAClC,CAAC;IACF,MAAM,CAAC,SAAS,CAAC,cAAc,CAAC,IAAI,CAAC,EAAE;QACrC,EAAE,EAAE,EAAE,GAAG,EAAE,IAAI,EAAE,GAAG,EAAE,QAAQ,EAAE,OAAO,EAAE;QACzC,EAAE,EAAE,EAAE,GAAG,EAAE,IAAI,EAAE,GAAG,EAAE,QAAQ,EAAE,OAAO,EAAE;KAC1C,CAAC,CAAC;AACL,CAAC,CAAC,CAAC;AAEH,KAAK,IAAI,CAAC,kBAAkB,EAAE,GAAG,EAAE;IACjC,MAAM,CAAC,SAAS,CAAC,cAAc,CAAC,EAAE,SAAS,EAAE,EAAE,EAAE,EAAE,GAAG,EAAE,IAAI,EAAE,IAAI,EAAE,EAAE,CAAC,EAAE,EAAE,EAAE,EAAE,GAAG,EAAE,IAAI,EAAE,IAAI,EAAE,QAAQ,EAAE,WAAW,EAAE,CAAC,CAAC;AAC3H,CAAC,CAAC,CAAC;AAEH,KAAK,IAAI,CAAC,wBAAwB,EAAE,GAAG,EAAE;IACvC,MAAM,CAAC,SAAS,CAAC,cAAc,CAAC,EAAE,KAAK,EAAE,CAAC,EAAE,CAAC,EAAE,EAAE,KAAK,EAAE,CAAC,EAAE,CAAC,CAAC;IAC7D,MAAM,CAAC,KAAK,CAAC,cAAc,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC;IACrC,MAAM,CAAC,KAAK,CAAC,cAAc,CAAC,GAAG,CAAC,EAAE,GAAG,CAAC,CAAC;IACvC,MAAM,CAAC,KAAK,CAAC,cAAc,CAAC,IAAI,CAAC,EAAE,IAAI,CAAC,CAAC;AAC3C,CAAC,CAAC,CAAC;AAEH,KAAK,IAAI,CAAC,kBAAkB,EAAE,GAAG,EAAE;IACjC,MAAM,IAAI,GAAG,EAAE,EAAE,EAAE,GAAG,EAAE,IAAI,EAAE,GAAG,EAAE,MAAM,EAAE,UAAU,EAAE,CAAC;IACxD,MAAM,CAAC,SAAS,CAAC,cAAc,CAAC,IAAI,CAAC,EAAE,IAAI,CAAC,CAAC;AAC/C,CAAC,CAAC,CAAC;AAEH,KAAK,IAAI,CAAC,yBAAyB,EAAE,GAAG,EAAE;IACxC,MAAM,CAAC,SAAS,CAAC,cAAc,CAAC,EAAE,IAAI,EAAE,GAAG,EAAE,CAAC,EAAE,EAAE,IAAI,EAAE,GAAG,EAAE,CAAC,CAAC;AACjE,CAAC,CAAC,CAAC;AAEH,KAAK,IAAI,CAAC,mBAAmB,EAAE,GAAG,EAAE;IAClC,MAAM,GAAG,GAAG,cAAc,CAAC,CAAC,EAAE,GAAG,EAAE,EAAE,EAAE,EAAE,GAAG,EAAE,EAAE,EAAE,EAAE,IAAI,EAAE,OAAO,EAAE,CAAC,CAA8B,CAAC;IACnG,MAAM,CAAC,SAAS,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,EAAE,EAAE,GAAG,EAAE,QAAQ,EAAE,KAAK,EAAE,CAAC,CAAC;IACvD,MAAM,CAAC,SAAS,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,IAAI,EAAE,OAAO,EAAE,CAAC,CAAC;AAC9C,CAAC,CAAC,CAAC"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"validate.test.d.ts","sourceRoot":"","sources":["../../src/pipeline/validate.test.ts"],"names":[],"mappings":""}
|
|
@@ -1,166 +0,0 @@
|
|
|
1
|
-
import assert from 'node:assert/strict';
|
|
2
|
-
import { readFileSync } from 'node:fs';
|
|
3
|
-
import { allTools, ToolRegistry } from '@xihe-lab/tapd-core';
|
|
4
|
-
import { validateTemplate } from './validate.js';
|
|
5
|
-
import { parseTemplate } from './template.js';
|
|
6
|
-
const registry = new ToolRegistry();
|
|
7
|
-
registry.register(allTools);
|
|
8
|
-
function validate(yaml) {
|
|
9
|
-
return validateTemplate(parseTemplate(yaml, 'inline'), registry);
|
|
10
|
-
}
|
|
11
|
-
const checks = [
|
|
12
|
-
['validate: 合法只读模板通过且 writeSteps 为空', () => {
|
|
13
|
-
const result = validate(`
|
|
14
|
-
name: ok-read
|
|
15
|
-
write: false
|
|
16
|
-
steps:
|
|
17
|
-
- id: stories
|
|
18
|
-
uses: tapd_get_stories
|
|
19
|
-
args: { iteration_id: "1001", limit: 5 }
|
|
20
|
-
`);
|
|
21
|
-
assert.equal(result.ok, true, JSON.stringify(result.errors));
|
|
22
|
-
assert.deepEqual(result.writeSteps, []);
|
|
23
|
-
}],
|
|
24
|
-
['validate: 未知命令 → error 且 target 指向 uses', () => {
|
|
25
|
-
const result = validate(`
|
|
26
|
-
name: bad-tool
|
|
27
|
-
write: false
|
|
28
|
-
steps:
|
|
29
|
-
- id: a
|
|
30
|
-
uses: tapd_totally_missing
|
|
31
|
-
`);
|
|
32
|
-
assert.equal(result.ok, false);
|
|
33
|
-
assert.ok(result.errors.some(e => e.target === 'steps.a.uses' && e.message.includes('tapd_totally_missing')));
|
|
34
|
-
}],
|
|
35
|
-
['validate: 参数不符 zod schema → error(limit 传字符串)', () => {
|
|
36
|
-
const result = validate(`
|
|
37
|
-
name: bad-args
|
|
38
|
-
write: false
|
|
39
|
-
steps:
|
|
40
|
-
- id: a
|
|
41
|
-
uses: tapd_get_stories
|
|
42
|
-
args: { limit: "not-a-number" }
|
|
43
|
-
`);
|
|
44
|
-
assert.equal(result.ok, false);
|
|
45
|
-
const issue = result.errors.find(e => e.target === 'steps.a.args.limit');
|
|
46
|
-
assert.ok(issue, JSON.stringify(result.errors));
|
|
47
|
-
assert.ok(issue.message.includes('参数不符 schema'));
|
|
48
|
-
}],
|
|
49
|
-
['validate: 未知参数名 → error(schema 不包含该字段)', () => {
|
|
50
|
-
const result = validate(`
|
|
51
|
-
name: unknown-key
|
|
52
|
-
write: false
|
|
53
|
-
steps:
|
|
54
|
-
- id: a
|
|
55
|
-
uses: tapd_get_stories
|
|
56
|
-
args: { bogus_param: 1 }
|
|
57
|
-
`);
|
|
58
|
-
assert.equal(result.ok, false);
|
|
59
|
-
assert.ok(result.errors.some(e => e.target === 'steps.a.args.bogus_param'));
|
|
60
|
-
}],
|
|
61
|
-
['validate: 必填参数缺失 → error(tapd_update_story 缺 id)', () => {
|
|
62
|
-
const result = validate(`
|
|
63
|
-
name: missing-required
|
|
64
|
-
write: true
|
|
65
|
-
steps:
|
|
66
|
-
- id: a
|
|
67
|
-
uses: tapd_update_story
|
|
68
|
-
args: { status: developing }
|
|
69
|
-
`);
|
|
70
|
-
assert.equal(result.ok, false);
|
|
71
|
-
const issue = result.errors.find(e => e.target === 'steps.a.args.id');
|
|
72
|
-
assert.ok(issue, JSON.stringify(result.errors));
|
|
73
|
-
}],
|
|
74
|
-
['validate: 必填参数为步骤间延迟引用 → 跳过静态检查不误报', () => {
|
|
75
|
-
const result = validate(`
|
|
76
|
-
name: deferred-ok
|
|
77
|
-
write: true
|
|
78
|
-
steps:
|
|
79
|
-
- id: fetch
|
|
80
|
-
uses: tapd_get_stories
|
|
81
|
-
args: { iteration_id: "1" }
|
|
82
|
-
- id: patch
|
|
83
|
-
uses: tapd_update_story
|
|
84
|
-
args: { id: "${'$'}{steps.fetch.data.0.id}", status: developing }
|
|
85
|
-
depends_on: [fetch]
|
|
86
|
-
`);
|
|
87
|
-
assert.deepEqual(result.writeSteps, ['patch']);
|
|
88
|
-
assert.equal(result.ok, true, JSON.stringify(result.errors));
|
|
89
|
-
}],
|
|
90
|
-
['validate: 含写步骤未声明 write: true → WRITE_MARKER_MISSING 语义错误', () => {
|
|
91
|
-
const result = validate(`
|
|
92
|
-
name: unmarked-write
|
|
93
|
-
steps:
|
|
94
|
-
- id: a
|
|
95
|
-
uses: tapd_update_story
|
|
96
|
-
args: { id: "1", status: developing }
|
|
97
|
-
`);
|
|
98
|
-
assert.equal(result.ok, false);
|
|
99
|
-
const issue = result.errors.find(e => e.target === 'write');
|
|
100
|
-
assert.ok(issue, JSON.stringify(result.errors));
|
|
101
|
-
assert.ok(issue.message.includes('write: true') && issue.message.includes('a'));
|
|
102
|
-
}],
|
|
103
|
-
['validate: 声明 write: true 但全只读 → warning 不阻塞', () => {
|
|
104
|
-
const result = validate(`
|
|
105
|
-
name: overmarked
|
|
106
|
-
write: true
|
|
107
|
-
steps:
|
|
108
|
-
- id: a
|
|
109
|
-
uses: tapd_get_stories
|
|
110
|
-
args: { iteration_id: "1" }
|
|
111
|
-
`);
|
|
112
|
-
assert.equal(result.ok, true);
|
|
113
|
-
assert.ok(result.warnings.some(w => w.target === 'write'));
|
|
114
|
-
}],
|
|
115
|
-
['validate: 三个内置模板全部通过(写模板正确声明 write)', () => {
|
|
116
|
-
for (const name of ['deliver-story', 'triage-bugs', 'iteration-review']) {
|
|
117
|
-
// 经文件系统加载,覆盖 builtinTemplatesDir 相对路径推导(src/pipeline → ../../templates)
|
|
118
|
-
const source = readFileSync(new URL(`../../templates/${name}.yaml`, import.meta.url), 'utf8');
|
|
119
|
-
const template = parseTemplate(source, name);
|
|
120
|
-
const result = validateTemplate(template, registry);
|
|
121
|
-
assert.equal(result.ok, true, `${name}: ${JSON.stringify(result.errors)}`);
|
|
122
|
-
}
|
|
123
|
-
}],
|
|
124
|
-
['validate: 引用不存在的步骤 → REF_UNKNOWN 语义错误', () => {
|
|
125
|
-
const result = validate(`
|
|
126
|
-
name: bad-ref
|
|
127
|
-
write: false
|
|
128
|
-
steps:
|
|
129
|
-
- id: a
|
|
130
|
-
uses: tapd_get_stories
|
|
131
|
-
args: { id: "${'$'}{steps.ghost.data.0.id}" }
|
|
132
|
-
`);
|
|
133
|
-
assert.equal(result.ok, false);
|
|
134
|
-
assert.ok(result.errors.some(e => e.message.includes('ghost')));
|
|
135
|
-
}],
|
|
136
|
-
['validate: where 表达式语法错误 → EXPR_ERROR 语义错误', () => {
|
|
137
|
-
const result = validate(`
|
|
138
|
-
name: bad-expr
|
|
139
|
-
write: false
|
|
140
|
-
steps:
|
|
141
|
-
- id: a
|
|
142
|
-
uses: tapd_get_stories
|
|
143
|
-
where: "steps.a.data.length >"
|
|
144
|
-
`);
|
|
145
|
-
assert.equal(result.ok, false);
|
|
146
|
-
assert.ok(result.errors.some(e => e.message.includes('表达式语法错误')));
|
|
147
|
-
}],
|
|
148
|
-
];
|
|
149
|
-
let failed = 0;
|
|
150
|
-
for (const [name, fn] of checks) {
|
|
151
|
-
try {
|
|
152
|
-
await fn();
|
|
153
|
-
console.log(`ok - ${name}`);
|
|
154
|
-
}
|
|
155
|
-
catch (error) {
|
|
156
|
-
failed++;
|
|
157
|
-
console.error(`FAIL - ${name}`);
|
|
158
|
-
console.error(error);
|
|
159
|
-
}
|
|
160
|
-
}
|
|
161
|
-
if (failed > 0) {
|
|
162
|
-
console.error(`\n${failed}/${checks.length} failed`);
|
|
163
|
-
process.exit(1);
|
|
164
|
-
}
|
|
165
|
-
console.log(`\n${checks.length}/${checks.length} passed`);
|
|
166
|
-
//# sourceMappingURL=validate.test.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"validate.test.js","sourceRoot":"","sources":["../../src/pipeline/validate.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"discovery.test.d.ts","sourceRoot":"","sources":["../../src/pm/discovery.test.ts"],"names":[],"mappings":""}
|
|
@@ -1,147 +0,0 @@
|
|
|
1
|
-
// pm 模板发现 / 用户覆盖 / 别名解析 / 元数据剥离测试(S2)
|
|
2
|
-
// fixture 模板写在测试临时目录(TAPD_USER_TEMPLATES_DIR 注入),不污染 templates/pm/。
|
|
3
|
-
import assert from 'node:assert/strict';
|
|
4
|
-
import { mkdtempSync, mkdirSync, rmSync, writeFileSync } from 'node:fs';
|
|
5
|
-
import { tmpdir } from 'node:os';
|
|
6
|
-
import path from 'node:path';
|
|
7
|
-
import { PipelineError } from '../pipeline/types.js';
|
|
8
|
-
import { CliError } from '../errors.js';
|
|
9
|
-
import { discoverPmTemplates, loadPmTemplate, mergePmEntries, resolvePmEntry, userPmTemplatesDir, } from './discovery.js';
|
|
10
|
-
import { builtinPmTemplatesDir } from './discovery.js';
|
|
11
|
-
const RISK_SCAN = `# 多源风险扫描(fixture)
|
|
12
|
-
name: risk-scan
|
|
13
|
-
domain: risk
|
|
14
|
-
title: 风险扫描(fixture)
|
|
15
|
-
write: false
|
|
16
|
-
vars:
|
|
17
|
-
iteration_id: ""
|
|
18
|
-
steps:
|
|
19
|
-
- id: iterations
|
|
20
|
-
uses: tapd_get_iterations
|
|
21
|
-
args: { status: open, fields: "id,name,startdate" }
|
|
22
|
-
filter: "!item.name.startsWith('zzz-delete-me')"
|
|
23
|
-
- id: overdue
|
|
24
|
-
uses: tapd_get_stories
|
|
25
|
-
args: { iteration_id: "${'$'}{vars.iteration_id}" }
|
|
26
|
-
`;
|
|
27
|
-
const prevEnv = process.env.TAPD_USER_TEMPLATES_DIR;
|
|
28
|
-
function makeEntry(partial) {
|
|
29
|
-
return {
|
|
30
|
-
domain: 'risk',
|
|
31
|
-
scenario: 'scan',
|
|
32
|
-
name: 'risk-scan',
|
|
33
|
-
title: '',
|
|
34
|
-
write: false,
|
|
35
|
-
file: '/tmp/x.yaml',
|
|
36
|
-
source: 'builtin',
|
|
37
|
-
domainKnown: true,
|
|
38
|
-
...partial,
|
|
39
|
-
};
|
|
40
|
-
}
|
|
41
|
-
const checks = [
|
|
42
|
-
['内置目录 = builtinTemplatesDir()/pm,用户目录默认 ~/.tapd/templates/pm', () => {
|
|
43
|
-
assert.ok(builtinPmTemplatesDir().endsWith(path.join('templates', 'pm')));
|
|
44
|
-
assert.ok(userPmTemplatesDir().endsWith(path.join('pm')));
|
|
45
|
-
}],
|
|
46
|
-
['发现:扫描用户目录,frontmatter domain 命中域表;坏文件降级为 warning 不炸', () => {
|
|
47
|
-
process.env.TAPD_USER_TEMPLATES_DIR = root;
|
|
48
|
-
try {
|
|
49
|
-
const catalog = discoverPmTemplates();
|
|
50
|
-
const scan = catalog.entries.find(e => e.scenario === 'scan');
|
|
51
|
-
assert.ok(scan, 'risk-scan 应被收录');
|
|
52
|
-
assert.equal(scan.domain, 'risk');
|
|
53
|
-
assert.equal(scan.domainKnown, true);
|
|
54
|
-
assert.equal(scan.source, 'user');
|
|
55
|
-
assert.equal(scan.write, false);
|
|
56
|
-
assert.equal(scan.name, 'risk-scan');
|
|
57
|
-
assert.equal(scan.title, '风险扫描(fixture)');
|
|
58
|
-
// broken.yaml(非法 yaml)与 no-steps.yaml(缺 steps)只产 warning
|
|
59
|
-
assert.equal(catalog.warnings.length, 2);
|
|
60
|
-
assert.ok(catalog.warnings.some(w => w.includes('broken.yaml')));
|
|
61
|
-
assert.ok(catalog.warnings.some(w => w.includes('no-steps.yaml')));
|
|
62
|
-
// 自定义域 procurement:domainKnown=false,仍被收录(可执行,help 归「其他」)
|
|
63
|
-
const audit = catalog.entries.find(e => e.scenario === 'audit');
|
|
64
|
-
assert.ok(audit);
|
|
65
|
-
assert.equal(audit.domain, 'procurement');
|
|
66
|
-
assert.equal(audit.domainKnown, false);
|
|
67
|
-
}
|
|
68
|
-
finally {
|
|
69
|
-
if (prevEnv === undefined)
|
|
70
|
-
delete process.env.TAPD_USER_TEMPLATES_DIR;
|
|
71
|
-
else
|
|
72
|
-
process.env.TAPD_USER_TEMPLATES_DIR = prevEnv;
|
|
73
|
-
}
|
|
74
|
-
}],
|
|
75
|
-
['用户同名覆盖内置(mergePmEntries 同别名 user 胜出)', () => {
|
|
76
|
-
const builtin = [
|
|
77
|
-
makeEntry({ source: 'builtin', file: '/builtin/risk-scan.yaml' }),
|
|
78
|
-
makeEntry({ domain: 'scope', scenario: 'drift-check', name: 'scope-drift-check', file: '/builtin/scope-drift-check.yaml' }),
|
|
79
|
-
];
|
|
80
|
-
const user = [makeEntry({ source: 'user', file: '/user/risk-scan.yaml', title: '用户版' })];
|
|
81
|
-
const merged = mergePmEntries(builtin, user);
|
|
82
|
-
assert.equal(merged.length, 2, '同别名只保留一份');
|
|
83
|
-
const scan = merged.find(e => e.scenario === 'scan');
|
|
84
|
-
assert.equal(scan.source, 'user');
|
|
85
|
-
assert.equal(scan.title, '用户版');
|
|
86
|
-
assert.equal(merged.find(e => e.scenario === 'drift-check').source, 'builtin');
|
|
87
|
-
// 排序:域表序在前
|
|
88
|
-
assert.ok(merged.findIndex(e => e.domain === 'scope') < merged.findIndex(e => e.domain === 'risk'));
|
|
89
|
-
}],
|
|
90
|
-
['别名解析:命中 / 未知域 / 已知域缺场景(错误信息给可操作指引)', () => {
|
|
91
|
-
const catalog = {
|
|
92
|
-
entries: [
|
|
93
|
-
makeEntry({ domain: 'risk', scenario: 'scan' }),
|
|
94
|
-
makeEntry({ domain: 'risk', scenario: 'register', name: 'risk-register', write: true }),
|
|
95
|
-
],
|
|
96
|
-
warnings: [],
|
|
97
|
-
};
|
|
98
|
-
const hit = resolvePmEntry(catalog, 'risk', 'scan');
|
|
99
|
-
assert.equal(hit.file, '/tmp/x.yaml');
|
|
100
|
-
// name 兜底匹配 <domain>-<scenario>
|
|
101
|
-
assert.equal(resolvePmEntry(catalog, 'risk', 'register').write, true);
|
|
102
|
-
assert.throws(() => resolvePmEntry(catalog, 'procurement', 'audit'), (error) => error instanceof CliError && error.code === 'INVALID_ARGS' && error.message.includes('未知域 "procurement"') && error.message.includes('integration, scope'));
|
|
103
|
-
assert.throws(() => resolvePmEntry(catalog, 'risk', 'nope'), (error) => error instanceof CliError && error.message.includes('域 risk 下没有场景 "nope"') && error.message.includes('scan, register'));
|
|
104
|
-
// 空目录的已知域:指引放模板即收录
|
|
105
|
-
assert.throws(() => resolvePmEntry({ entries: [], warnings: [] }, 'risk', 'scan'), (error) => error instanceof CliError && error.message.includes('放置 templates/pm'));
|
|
106
|
-
}],
|
|
107
|
-
['loadPmTemplate:剥离 frontmatter domain 后交 S1 严格校验;坏结构抛 TEMPLATE_INVALID', () => {
|
|
108
|
-
const ok = loadPmTemplate({ ...makeEntry({}), file: path.join(root, 'pm', 'risk-scan.yaml') });
|
|
109
|
-
assert.equal(ok.template.name, 'risk-scan');
|
|
110
|
-
assert.equal(ok.template.write, false);
|
|
111
|
-
assert.equal(ok.template.steps.length, 2);
|
|
112
|
-
assert.equal(ok.template.domain, undefined, 'domain 字段必须被剥离');
|
|
113
|
-
assert.equal(ok.path, path.join(root, 'pm', 'risk-scan.yaml'));
|
|
114
|
-
assert.throws(() => loadPmTemplate({ ...makeEntry({}), file: path.join(root, 'pm', 'broken.yaml') }), (error) => error instanceof PipelineError && error.code === 'TEMPLATE_INVALID');
|
|
115
|
-
assert.throws(() => loadPmTemplate({ ...makeEntry({}), file: '/nonexistent/nope.yaml' }), (error) => error instanceof PipelineError && error.code === 'IO');
|
|
116
|
-
}],
|
|
117
|
-
];
|
|
118
|
-
// ---- fixture 准备 ----
|
|
119
|
-
const root = mkdtempSync(path.join(tmpdir(), 'tapd-pm-discovery-'));
|
|
120
|
-
mkdirSync(path.join(root, 'pm'), { recursive: true });
|
|
121
|
-
writeFileSync(path.join(root, 'pm', 'risk-scan.yaml'), RISK_SCAN);
|
|
122
|
-
writeFileSync(path.join(root, 'pm', 'procurement-audit.yaml'), 'name: procurement-audit\ndomain: procurement\ntitle: 外部依赖台账\nsteps:\n - id: a\n uses: tapd_get_stories\n');
|
|
123
|
-
writeFileSync(path.join(root, 'pm', 'broken.yaml'), 'name: [broken\n steps: {{{');
|
|
124
|
-
writeFileSync(path.join(root, 'pm', 'no-steps.yaml'), 'name: no-steps\ntitle: 没有 steps\n');
|
|
125
|
-
let failed = 0;
|
|
126
|
-
try {
|
|
127
|
-
for (const [name, fn] of checks) {
|
|
128
|
-
try {
|
|
129
|
-
await fn();
|
|
130
|
-
console.log(`ok - ${name}`);
|
|
131
|
-
}
|
|
132
|
-
catch (error) {
|
|
133
|
-
failed++;
|
|
134
|
-
console.error(`FAIL - ${name}`);
|
|
135
|
-
console.error(error);
|
|
136
|
-
}
|
|
137
|
-
}
|
|
138
|
-
}
|
|
139
|
-
finally {
|
|
140
|
-
rmSync(root, { recursive: true, force: true });
|
|
141
|
-
}
|
|
142
|
-
if (failed > 0) {
|
|
143
|
-
console.error(`\n${failed}/${checks.length} failed`);
|
|
144
|
-
process.exit(1);
|
|
145
|
-
}
|
|
146
|
-
console.log(`\n${checks.length}/${checks.length} passed`);
|
|
147
|
-
//# sourceMappingURL=discovery.test.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"discovery.test.js","sourceRoot":"","sources":["../../src/pm/discovery.test.ts"],"names":[],"mappings":"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"}
|