Files
ittoview/scripts/generate-api.mjs
2026-05-09 17:04:22 +01:00

300 lines
8.5 KiB
JavaScript

import fs from 'node:fs'
import path from 'node:path'
import vm from 'node:vm'
import { fileURLToPath } from 'node:url'
const __dirname = path.dirname(fileURLToPath(import.meta.url))
const rootDir = path.resolve(__dirname, '..')
const dataDir = path.join(rootDir, 'src', 'data')
const apiDir = path.join(rootDir, 'public', 'api')
const apiDocPath = path.join(rootDir, 'public', 'apidoc')
function readJson(relativePath) {
return JSON.parse(fs.readFileSync(path.join(rootDir, relativePath), 'utf8'))
}
function ensureDir(dir) {
fs.mkdirSync(dir, { recursive: true })
}
function writeJson(relativePath, data) {
const target = path.join(apiDir, relativePath)
ensureDir(path.dirname(target))
fs.writeFileSync(target, `${JSON.stringify(data, null, 2)}\n`, 'utf8')
}
function cleanApiDir() {
fs.rmSync(apiDir, { recursive: true, force: true })
fs.rmSync(apiDocPath, { force: true })
ensureDir(apiDir)
}
function writeTextFile(target, content) {
ensureDir(path.dirname(target))
fs.writeFileSync(target, content, 'utf8')
}
function copyApiDoc() {
const source = path.join(rootDir, 'docs', '知识库API接口说明.md')
const content = fs.readFileSync(source, 'utf8')
writeTextFile(apiDocPath, content)
}
function extractConstArrayFromTs(filePath, constName) {
const text = fs.readFileSync(filePath, 'utf8')
const marker = `export const ${constName}`
const markerIndex = text.indexOf(marker)
if (markerIndex === -1) {
throw new Error(`未找到 ${constName}`)
}
const assignmentIndex = text.indexOf('=', markerIndex)
if (assignmentIndex === -1) {
throw new Error(`未找到 ${constName} 赋值符号`)
}
const arrayStart = text.indexOf('[', assignmentIndex)
if (arrayStart === -1) {
throw new Error(`未找到 ${constName} 数组起点`)
}
let depth = 0
let quote = null
let escaped = false
let lineComment = false
let blockComment = false
for (let index = arrayStart; index < text.length; index += 1) {
const char = text[index]
const next = text[index + 1]
if (lineComment) {
if (char === '\n') lineComment = false
continue
}
if (blockComment) {
if (char === '*' && next === '/') {
blockComment = false
index += 1
}
continue
}
if (quote) {
if (escaped) {
escaped = false
} else if (char === '\\') {
escaped = true
} else if (char === quote) {
quote = null
}
continue
}
if (char === '/' && next === '/') {
lineComment = true
index += 1
continue
}
if (char === '/' && next === '*') {
blockComment = true
index += 1
continue
}
if (char === '\'' || char === '"' || char === '`') {
quote = char
continue
}
if (char === '[') depth += 1
if (char === ']') {
depth -= 1
if (depth === 0) {
const source = text.slice(arrayStart, index + 1)
return vm.runInNewContext(`(${source})`, {}, { timeout: 1000 })
}
}
}
throw new Error(`未能完整解析 ${constName}`)
}
const knowledgeAreas = readJson('src/data/knowledge-areas.json').knowledgeAreas
const processGroups = readJson('src/data/process-groups.json').processGroups
const processes = readJson('src/data/processes.json').processes
const artifacts = readJson('src/data/artifacts.json').artifacts
const tools = readJson('src/data/tools.json').tools
const performanceDomains = extractConstArrayFromTs(
path.join(dataDir, 'performance-domains.ts'),
'performanceDomains'
)
const knowledgeAreaMap = new Map(knowledgeAreas.map((item) => [item.id, item]))
const processGroupMap = new Map(processGroups.map((item) => [item.id, item]))
const artifactMap = new Map(artifacts.map((item) => [item.id, item]))
const toolMap = new Map(tools.map((item) => [item.id, item]))
function normalizeRef(ref) {
if (typeof ref === 'string') return { id: ref, details: [], note: undefined }
return {
id: ref.id,
details: Array.isArray(ref.detail) ? ref.detail.map((item) => ({ label: item.label })) : [],
note: ref.note,
}
}
function processSummary(process) {
const knowledgeArea = knowledgeAreaMap.get(process.knowledgeAreaId)
const processGroup = processGroupMap.get(process.processGroupId)
return {
id: process.id,
name: process.name,
knowledgeAreaId: process.knowledgeAreaId,
knowledgeAreaName: knowledgeArea?.name ?? '',
processGroupId: process.processGroupId,
processGroupName: processGroup?.name ?? '',
purpose: process.purpose ?? '',
}
}
function attachRefMeta(ref, entityMap) {
const normalized = normalizeRef(ref)
const entity = entityMap.get(normalized.id)
return {
id: normalized.id,
name: entity?.name ?? normalized.id,
details: normalized.details,
...(normalized.note ? { note: normalized.note } : {}),
}
}
function processItto(process) {
return {
id: process.id,
name: process.name,
inputs: process.inputs.map((ref) => attachRefMeta(ref, artifactMap)),
tools: process.tools.map((ref) => attachRefMeta(ref, toolMap)),
outputs: process.outputs.map((ref) => attachRefMeta(ref, artifactMap)),
}
}
function includesRef(refs, targetId) {
return refs.some((ref) => normalizeRef(ref).id === targetId)
}
function artifactUsage(artifact) {
return {
id: artifact.id,
name: artifact.name,
asInput: processes.filter((process) => includesRef(process.inputs, artifact.id)).map(processSummary),
asOutput: processes.filter((process) => includesRef(process.outputs, artifact.id)).map(processSummary),
}
}
function toolUsage(tool) {
return {
id: tool.id,
name: tool.name,
usedIn: processes.filter((process) => includesRef(process.tools, tool.id)).map(processSummary),
}
}
cleanApiDir()
writeJson('process-groups.json', processGroups.map((item) => ({ id: item.id, name: item.name })))
writeJson(
'knowledge-areas.json',
knowledgeAreas.map((item) => ({
id: item.id,
name: item.name,
tailoringFactors: (item.tailoringFactors ?? []).map((factor) => ({
title: factor.title,
description: factor.description,
})),
}))
)
for (const knowledgeArea of knowledgeAreas) {
writeJson(
`knowledge-areas/${knowledgeArea.id}/tailoring-factors.json`,
(knowledgeArea.tailoringFactors ?? []).map((factor) => ({
title: factor.title,
description: factor.description,
}))
)
writeJson(
`knowledge-areas/${knowledgeArea.id}/processes.json`,
processes
.filter((process) => process.knowledgeAreaId === knowledgeArea.id)
.map((process) => {
const summary = processSummary(process)
return {
id: summary.id,
name: summary.name,
processGroupId: summary.processGroupId,
processGroupName: summary.processGroupName,
purpose: summary.purpose,
}
})
)
}
for (const processGroup of processGroups) {
writeJson(
`process-groups/${processGroup.id}/processes.json`,
processes
.filter((process) => process.processGroupId === processGroup.id)
.map((process) => {
const summary = processSummary(process)
return {
id: summary.id,
name: summary.name,
knowledgeAreaId: summary.knowledgeAreaId,
knowledgeAreaName: summary.knowledgeAreaName,
purpose: summary.purpose,
}
})
)
}
writeJson('processes.json', processes.map(processSummary))
for (const process of processes) {
writeJson(`processes/${process.id}.json`, processSummary(process))
writeJson(`processes/${process.id}/itto.json`, processItto(process))
}
writeJson('performance-domains.json', performanceDomains.map((item) => ({ id: item.id, name: item.name })))
for (const domain of performanceDomains) {
writeJson(`performance-domains/${domain.id}.json`, {
id: domain.id,
name: domain.name,
expectedGoals: domain.detail?.expectedGoals ?? [],
keyPoints: domain.detail?.keyPoints ?? [],
interactions: domain.detail?.interactions ?? [],
checks: domain.detail?.checks ?? [],
})
}
writeJson('artifacts.json', artifacts.map((item) => ({ id: item.id, name: item.name })))
writeJson('tools.json', tools.map((item) => ({ id: item.id, name: item.name })))
for (const artifact of artifacts) {
writeJson(`artifacts/${artifact.id}/usage.json`, artifactUsage(artifact))
}
for (const tool of tools) {
writeJson(`tools/${tool.id}/usage.json`, toolUsage(tool))
}
copyApiDoc()
console.log(`已生成静态 API 文件:${path.relative(rootDir, apiDir)}`)
console.log(`已生成 Markdown 接口文档:${path.relative(rootDir, apiDocPath)}`)