# 项目拓扑 (Project)

Project 是仓库的地图：描述有哪些前端应用、哪些后端 API，以及每个 API 服务哪些前端。

```ts
import { defineProject, FrontAppSchema, ProjectApiSchema } from '@pylonts/dsl';

// Export-symbol rule: the export name equals the schema name kebab-camel
// (name 'admin' exports 'admin', 'mini-user' exports 'miniUser').
export const admin: FrontAppSchema = { name: 'admin', type: 'admin', dir: 'admin/', description: '管理后台' };
export const miniUser: FrontAppSchema = { name: 'mini-user', type: 'wxmini', dir: 'mini-user/', description: 'C端小程序' };
export const miniVerify: FrontAppSchema = { name: 'mini-verify', type: 'wxmini', dir: 'mini-verify/', description: '核销小程序' };

// The first api must be named 'api'; a second api may use a prefixed name
// like 'xx-api' with dir 'xx-api/'.
export const api: ProjectApiSchema = { name: 'api', description: '商城主后端', dir: 'api/', contextPath: '/mall', apps: [admin, miniUser, miniVerify] };

export const mall = defineProject('mall', {
  description: '合作商户权益兑换商城',
  apps: [admin, miniUser, miniVerify],
  apis: [api],
});
```

- `FrontAppSchema`：`name` / `description` / `type`（admin | wxmini）/ `dir`（相对仓库根目录的源码目录）。
- `ProjectApiSchema`：`name` / `description` / `dir` / `apps`（直接引用共享的 FrontAppSchema 实例——一个 app 被多个 API 服务就定义一次、引用多次）/ `contextPath`（API 基础 URL 前缀，如 `/mall`，空串表示无前缀）。
- **直接对象引用优先**：`api.apps` 与 `project.apps` 指向同一实例，不写字符串。
- **contextPath 解析**：前端 app 的 API 前缀由服务它的 api 决定——`api.apps` 必须恰好包含该 app（零个或多个都报错），app 本身不声明 contextPath。
- **命名约定（defineProject 运行时强制，违反即抛错）**：
  - 每个 app / api / thirdApi 的 `dir` 必须等于 `name`（尾斜杠可有可无，`'api/'` == `'api'`）。
  - `apis` 的第一个 api 必须命名为 `api`（导出符号即 `api`）；带前缀的名字（如 `xx-api`）只允许从第二个 api 起。
  - 实例导出符号 = 名字的 kebab-camel（`mini-user` → `miniUser`），loader 强制。