import { existsSync, readFileSync } from 'node:fs'; import { join } from 'node:path'; /** * 编译进代码的契约版本常量。 * 与 `contracts/VERSION` 必须一致;`npm run contracts:check` 在构建前校验。 * 之所以编译进代码:这是运行时读不到 contracts/VERSION 时的兜底值 * (原生部署会通过 CONTRACTS_DIR 指向安装目录下的 contracts/, * 未设置该变量时回落到这里的常量)。 */ export const CONTRACT_VERSIONS: Readonly> = Object.freeze({ 'rest-api': '0.5.0', 'websocket-keypoints': '0.2.0', 'data-models': '0.7.0', 'skeleton-topology': '0.2.0', 'model-formats': '0.5.0', database: '0.4.0', }); /** 解析 `contracts/VERSION`(`key: value` 每行一条,`#` 起注释) */ export function parseVersionFile(content: string): Record { const out: Record = {}; for (const rawLine of content.split(/\r?\n/)) { const line = rawLine.trim(); if (!line || line.startsWith('#')) continue; const idx = line.indexOf(':'); if (idx <= 0) continue; const key = line.slice(0, idx).trim(); const value = line.slice(idx + 1).trim(); if (key && value) out[key] = value; } return out; } /** * 运行期契约版本。设置 `CONTRACTS_DIR` 时读取实时文件(部署可挂载 contracts/), * 否则回落到编译常量。 */ export function resolveContractVersions(contractsDir?: string): Record { if (!contractsDir) return { ...CONTRACT_VERSIONS }; const file = join(contractsDir, 'VERSION'); if (!existsSync(file)) return { ...CONTRACT_VERSIONS }; try { const parsed = parseVersionFile(readFileSync(file, 'utf8')); return Object.keys(parsed).length > 0 ? parsed : { ...CONTRACT_VERSIONS }; } catch { return { ...CONTRACT_VERSIONS }; } }