refactor(core): migrate to Bun and converge acquisition policies and Agent boundaries

This commit is contained in:
root committed 2026-10-13 14:37:03 +08:00
1 parent 3f1c6d02f6
commit 27c0f27434
268 files changed
+7468 -7852

No files matched your search

+4 -2
View File
@@ -1,6 +1,6 @@
# @umt/mapping
domain ↔ contracts 的集中映射(W1 审查 B5)。只依赖 `@umt/domain` 与 `@umt/contracts`,不依赖 db / adapters。server 各模块需要在两套取值之间转换时一律走这里,不要在模块里各写一份。
domain ↔ contracts 的语义映射。只依赖 `@umt/domain` 与 `@umt/contracts`,不依赖 db / adapters。server 各模块需要在两套取值之间转换时一律走这里,不要在模块里各写一份。
db 行(snake_case)↔ contracts(camelCase)的转换不在本包;adapters 的 `SiteRelease` 以结构类型 `SiteReleaseLike` 接入(兼容性由 `apps/server/test/mapping-compat.test.ts` 在类型层保证)。
@@ -10,12 +10,14 @@ db 行(snake_case)↔ contracts(camelCase)的转换不在本包;adapters 的 `Si
| `evidence.ts` | `toContractEvidenceLevel` / `toDomainConfidence` / `toDomainMediaType` / `toContractMediaKind` / `toDomainEntityType` / `toContractEntityType` | `needs_confirm ↔ needs_review`,`manual → rule_confirmed`,`rejected → null`;`tv ↔ series/variety` |
| `identity.ts` | `toContractResolveStatus` / `toReleaseResolutionStatus` / `rankedToIdentityCreate` / `localIdentityCandidate` | `auto/ambiguous/no_match → resolved|proposed/ambiguous/unidentifiable → source_release.resolution_status`;排名候选 → `ExternalIdentityCreate`;本地作品 → 本地优先候选 |
| `slot.ts` | `toContractSlotStatus` / `toDomainSlotStatus` / `slotSpecFromRow` / `slotSpecToColumns` / `toContractSlotEvaluation` | Slot 状态;规则 JSON 列 ↔ `SlotSpec`;评估结果 → `{ decision, acceptable, rejected[] }` |
| `release.ts` | `toParsedColumns` / `fromParsedColumns` / `toContractContentType` / `toDomainContentType` / `toContractSegment` / `toDomainSegment` | `ParsedRelease ↔ source_release.parsed + parser_version`(版本不符返回 null,重解析) |
| `release.ts` | `toParsedColumns` / `fromParsedColumns` / `toContractSegment` / `toDomainSegment` | `ParsedRelease ↔ source_release.parsed + parser_version`(版本不符返回 null,重解析) |
| `site-release.ts` | `siteReleaseToSourceRelease` / `sourceReleaseToSiteRelease` / `SiteReleaseLike` | 站点资源 ↔ `SourceReleaseCreate` / `SourceRelease`;downloadUrl 不入库 |
| `path-binding.ts` | `toDomainPathBinding` / `toPathBindingCreate` / `parseTemplateVersion` | `entityId/scope/season/templateVersion:number/active ↔ mediaEntityId/bindingKind/seasonNumber/templateVersion:string/status` |
| `organize.ts` | `organizePlanToRecord` / `organizePlanToFileResults` / `organizePlanToCapabilityOutput` / `organizationStatusFromFiles` / `toOrganizationAction` / `toOrganizeOperation` / `toFileRole` | `OrganizePlan →` organization_record / organization_file_result 草稿 / organize.plan 输出 |
## 规则
- 同一语义的类型直接共用,不维护同值枚举的双向转换表。只在取值或含义存在实际差异时添加映射。
- 映射表一律写成 `Record<一侧的联合类型, …>`:domain 增删取值时 typecheck 失败;contracts 侧在 `test/enums.test.ts` 按 `z.enum(...).options` 逐个断言,新增取值时测试失败。
- 新增映射先写测试(往返、schema 合法)。输出给 contracts 的对象都用对应 zod schema 在测试里 `safeParse` 一遍。
-9
View File
@@ -1,9 +0,0 @@
{
"name": "@umt/mapping",
"version": "0.3.44",
"exports": { ".": "./src/index.ts" },
"tasks": {
"test": "deno run -A npm:vitest run",
"typecheck": "deno run -A npm:typescript/tsc --noEmit -p tsconfig.json"
}
}
+9
View File
@@ -4,5 +4,14 @@
"type": "module",
"exports": {
".": "./src/index.ts"
},
"version": "0.3.44",
"scripts": {
"test": "bun x --bun vitest run",
"typecheck": "bun x --bun tsc --noEmit -p tsconfig.json"
},
"dependencies": {
"@umt/contracts": "workspace:*",
"@umt/domain": "workspace:*"
}
}
+2 -30
View File
@@ -1,10 +1,5 @@
import type { ContentType as ContractContentType, Segment as ContractSegment, JsonObject } from "@umt/contracts";
import {
type ContentType as DomainContentType,
type Segment as DomainSegment,
PARSER_VERSION,
type ParsedRelease,
} from "@umt/domain";
import type { Segment as ContractSegment, JsonObject } from "@umt/contracts";
import { type Segment as DomainSegment, PARSER_VERSION, type ParsedRelease } from "@umt/domain";
// 标题解析结果 ↔ source_release.parsed / parser_version 列;综艺内容类型与分段 ↔ content_unit 列。
@@ -28,29 +23,6 @@ export function fromParsedColumns(cols: { parsed: JsonObject; parserVersion: str
return p as unknown as ParsedRelease;
}
const CONTENT_TYPE: Record<DomainContentType, ContractContentType> = {
unknown: "unknown",
main: "main",
extra: "extra",
behind_the_scenes: "behind_the_scenes",
performance_only: "performance_only",
preview: "preview",
reaction: "reaction",
};
const CONTENT_TYPE_BACK: Record<ContractContentType, DomainContentType> = {
main: "main",
extra: "extra",
behind_the_scenes: "behind_the_scenes",
performance_only: "performance_only",
preview: "preview",
reaction: "reaction",
unknown: "unknown",
};
export const DOMAIN_CONTENT_TYPES = Object.keys(CONTENT_TYPE) as DomainContentType[];
export const toContractContentType = (t: DomainContentType): ContractContentType => CONTENT_TYPE[t];
export const toDomainContentType = (t: ContractContentType): DomainContentType => CONTENT_TYPE_BACK[t];
const SEGMENT: Record<DomainSegment, ContractSegment> = {
full: "full",
upper: "upper",
+2 -4
View File
@@ -117,15 +117,13 @@ describe("身份解析状态", () => {
});
});
describe("Slot / 内容类型 / 分段 / 整理动作", () => {
describe("Slot / 分段 / 整理动作", () => {
it("Slot 状态两侧一一对应", () => {
expect(sorted(M.DOMAIN_SLOT_STATUSES)).toEqual(sorted(C.SlotStatus.options));
for (const s of C.SlotStatus.options) expect(M.toContractSlotStatus(M.toDomainSlotStatus(s))).toBe(s);
});
it("综艺内容类型两侧一一对应;分段 unknown ↔ 缺失", () => {
expect(sorted(M.DOMAIN_CONTENT_TYPES)).toEqual(sorted(C.ContentType.options));
for (const t of C.ContentType.options) expect(M.toContractContentType(M.toDomainContentType(t))).toBe(t);
it("分段 unknown ↔ 缺失", () => {
expect(sorted([...M.DOMAIN_SEGMENTS, "unknown"])).toEqual(sorted(C.Segment.options));
for (const s of C.Segment.options) expect(M.toContractSegment(M.toDomainSegment(s))).toBe(s);
});