# cms / AGENTS.md

> 给 LLM / Agent 看的操作手册。修改 CMS 数据时**必须**遵守本文件的数据契约。
>
> - 通用信息（目录结构、部署命令、访问 URL）见 [`README.md`](./README.md)，本文件不重复。
> - 从外部数据（业务方模型 + xlsx/csv）批量导入新 entry 时，可参考 [`IMPORT-SOP.md`](./IMPORT-SOP.md)（可选辅助，非强约束）；无论走不走 SOP，最终产物都必须满足本文件的契约。

## 0. 执行前置：输入不足先问，禁止脑补

收到转换/生成请求后，若缺少必要输入（数据格式 / 字段模型、数据记录、枚举代码表等）或关键决策点不明，**先列出缺失项并停下等用户补齐，不得用占位或猜测数据继续**（枚举值尤其禁止凭 xlsx 中文标签脑补，详见 [`IMPORT-SOP.md`](./IMPORT-SOP.md)）。

## 1. 快速开始

生成任何 entry 前**先看参考模板** `cms/data/_example/`（`schema.ts` + `index.ts`），它是最精简的完整样例，照抄结构即可。

新增一个 entry 三步：

1. 在 `data/<entry>/` 建 `schema.ts`：`export default` 一个 zod schema（如 `z.array(row)`）+ 源数据（`zh.json` / `en.json`）。
2. 在 `data/<entry>/` 建 `index.ts`：`export default defineEntry(import.meta.url, schema, locales, opts?)`。
3. 在 `data/index.ts` 中 `import` 该 entry 并加入 `registry`（**未注册不会进 dist**）。

## 2. 数据契约（zod）

### 2.1 schema.ts

- 每个 entry 的数据结构由 zod schema 在 `schema.ts` 中定义，**自包含、不互相 import**（允许重复定义，便于各 entry 字段独立分化）。
- schema 既是数据契约文档（供人工参考），也在构建期由 `zod-to-json-schema` 反向生成 `schema.json` 供前端消费。
- 生成的 `schema.json` 是**纯 JSON Schema**，不注入任何自定义扩展字段。
- 构建期**严格校验**：类型不匹配 / 多余字段 / 缺失必填字段均阻断构建。

**数据形态由 schema 的顶层 zod 类型决定：**

| 顶层类型 | 数据形态 | 说明 |
|---|---|---|
| `z.array(row)` | 行集合（collection） | 每行是 zod object 且 `.strict()`（禁止多余字段）；主键推荐 `_id`（非强制，可用 `identifier` 等其他字段名） |
| `z.object(...)` | 单例配置（single / global config） | 字段直接写在 zod object 中；`zh.json` / `en.json` 也是裸对象，不要包成数组 |

### 2.2 zh.json / en.json

- schema 为 `z.array(...)` 时各自是数组；`z.object(...)` 时各自是对象。
- 构建期用 zod schema 严格校验每行 / 整体结构。
- **行顺序**：构建期**不做任何排序**，产物行顺序 = 源文件行顺序（逐字节透传）。如需按某字段（如 `order`）展示，请在写入前就排好；`order` 只是普通业务数据，构建系统不会读它排序。
- **改数据时原地替换值，保持原有 key 顺序不变，新增字段追加到末尾。** 否则该行在 `git diff` 中会整行红绿，看不出真正改动。新建 entry 时可自由排列 key，但入库后不再调整顺序。

### 2.3 资源引用

引用图片 / zip 等资源时，先把文件放到 `cms/` 内（推荐 `cms/assets/`，可建子目录），再在 json 中用相对路径或 `@/` 别名引用。**无需在 schema 中显式标注**，由相对地址驱动改写：

- `@/` 开头、或路径中含 `/assets` 的字符串 → 视为相对资源引用：
  - `@/` 相对 `cms/` 根，其他相对 `cms/data/<entry>/`；
  - 解析后落在 `cms/` 根内且文件真实存在 → 改写为 CDN URL（带 `?v=<hash>` 缓存失效）；
  - 资源可放在 `cms/` 任意子目录（不限 `assets/`），`assets/` 之外的被引用文件会自动拷贝到 dist。
- 以 URI scheme（`http:` / `https:` / `data:` / `cloud:` / `mailto:` 等）或 `//` 开头 → 可访问地址，原样保留。
- 其他普通字符串（无上述前缀）→ 不动。
- 相对引用解析后越出 `cms/` 目录、或目标文件不存在 → 构建报错。

### 2.4 注册到 registry（defineEntry）

只有在 `data/index.ts` 的 `registry` 中注册的 entry 才会被构建到 dist。`defineEntry` 的 `locales` 支持两种数据来源（由 `opts.localesMode` 区分）：

| 模式 | `locales` 值 | 调用示例 |
|---|---|---|
| `path`（默认） | 相对 entry 目录的源文件名 | `defineEntry(import.meta.url, schema, { zh:'zh.json', en:'en.json' })` |
| `inline` | 已解析的数据对象（`import` 自 `zh.ts` / `en.ts`） | `defineEntry(import.meta.url, schema, { zh, en }, { localesMode:'inline' })` |

- 两种模式下产物文件名均取 locale key（`zh` → `zh.json`），与源文件名解耦。
- `inline` 模式下数据会被**深拷贝**后使用，避免污染 import 的模块对象。

## 3. 站点维度（targetPlatform）

平台已支持国际化多站点发布，**所有新 entry 必须在 zod schema 中加入 `targetPlatform` 字段**（`z.array(targetPlatformEnum).min(1)`，非空数组），可选值：

- `default`：国内站
- `intl`：国外站
- `private`：私有化部署

源数据**只有一份**，构建产物不区分站点；前端加载后根据当前站点自行 filter。

## 4. 提交清单（每次改动数据）

- [ ] zod schema / zh / en 已对齐，跑 `npm run build` 通过；
- [ ] 新增 / 删除资源已在 `cms/assets/` 下放好，无未引用的孤儿资源；
- [ ] 不要提交 `cms/dist/`、`cms/.env`、`cms/node_modules/`、`cms/temp/`（均已 gitignore，无需单独处理）。

## 5. 从外部数据导入新 entry

需要把业务方给的数据模型 + xlsx/csv/导出 JSON 落成新 entry 时，参考 **[`IMPORT-SOP.md`](./IMPORT-SOP.md)**（一次性转换脚本骨架、i18n 漏翻译审计、报告模板、典型陷阱等）。该流程为可选辅助，最终产物仍以本文件的数据契约为准。
