# 铁铺 (TiePM) 设计笔记

> **版本**:v0.1.0 | **状态**:早期开发阶段
> 本文档由ClaudeFable5生成。

---

## 设计目标

铁铺是玄铁语言的包管理器,核心目标:

1. **安装**:从远程索引获取包,下载、验证哈希、解压到本地
2. **搜索**:按关键词搜索可用铁器
3. **发布**:将本地项目打包并登记到远程索引
4. **轻量**:不依赖第三方包管理器运行时(Node.js/Python 等),核心逻辑用玄铁自举实现

---

## 架构总览

```
┌─────────────────┐     HTTP      ┌──────────────────┐
│   铁铺 CLI       │ ◄──────────► │  TiePM 官网       │
│  (铁铺.xt)       │   JSON API   │  (tiepm-site/)    │
│                  │              │  Go + 静态 HTML    │
├─────────────────┤              ├──────────────────┤
│ • 安装/搜索/发布 │              │  GET /api/index   │
│ • SHA256 验证    │              │  POST /api/publish │
│ • TOML 清单解析  │              │  GET /api/search  │
│ • 本地缓存管理   │              │  GET /download/*  │
└─────────────────┘              └────────┬─────────┘
                                         │ 读写
                                  ┌──────▼──────────┐
                                  │  data/index.json│
                                  │  (包索引文件)    │
                                  └─────────────────┘
```

**关键设计决策**:

- **索引与源码分离**:官网只托管包索引 JSON,包源码由 GitHub Releases 承载。TiePM 不存储任何包文件。
- **无状态服务**:Go 服务器以本地 JSON 文件作为持久化存储,无需数据库。
- **embed 静态文件**:HTML/CSS 通过 Go 1.16+ `embed.FS` 编译进二进制,单文件部署。
- **裸包名导入**:编译器自动识别裸包名(无路径前缀的 `引 "包名"`),解析到 `~/.tiepm/已安装/<包名>/` 下查找入口文件。安装后使用体验与 npm/pip 一致——无需手写路径。

---

## 包索引格式

索引文件为单个 JSON 对象,结构如下:

```json
{
  "数组工具": {
    "1.0.0": {
      "版本": "1.0.0",
      "哈希": "a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6...",
      "仓库": "https://github.com/username/array-utils",
      "作者": "张三",
      "描述": "提供数组去重、过滤、映射等工具函数"
    }
  }
}
```

字段说明:

| 字段 | 类型 | 必填 | 说明 |
|------|------|------|------|
| 版本 | 字 | 是 | 语义版本号(如 `1.0.0`) |
| 哈希 | 字 | 是 | 打包文件 SHA256 十六进制字符串 |
| 仓库 | 字 | 是 | GitHub 仓库地址,用于构造下载 URL |
| 作者 | 字 | 否 | 包作者/维护者名称 |
| 描述 | 字 | 否 | 包功能简述 |

索引顶层 key 为包名,包名下可包含多个版本。`/api/search` 返回每个包的最新版本。

---

## API 设计

### GET /api/index

返回完整索引用 JSON。

### GET /api/search?q=<关键词>

按关键词搜索包名和描述,返回数组:

```json
[
  {
    "名称": "数组工具",
    "最新版本": "1.0.0",
    "作者": "张三",
    "描述": "提供数组去重、过滤、映射等工具函数"
  }
]
```

### POST /api/publish

登记新包或新版本。请求体:

```json
{
  "名称": "数组工具",
  "版本": "1.0.0",
  "哈希": "a1b2c3...",
  "仓库": "https://github.com/username/array-utils",
  "作者": "张三",
  "描述": "数组操作工具"
}
```

成功返回 `{"状态":"成功","消息":"..."}`。缺少必填字段返回 400。

### GET /download/:name/:version

302 重定向到 `{仓库}/archive/v{版本}.zip`(GitHub 自动打包)。

---

## 编译期裸包名解析

铁铺最核心的设计创新在于**编译器直接参与包解析**。当编译器遇到不带路径前缀的 `引` 语句(如 `引 "数组工具"`),自动在铁铺安装目录下查找:

### 解析逻辑

```
引 "数组工具"  (裸包名)
  │
  ├─ isBarePackageName() → 真(无 \、/、: 且不以 . 开头)
  │
  ├─ getTiePMInstallDir() → ~/.tiepm/已安装
  │   ├─ 优先使用 $TIEPM_HOME 环境变量
  │   └─ 否则使用 %USERPROFILE%\.tiepm\已安装
  │
  ├─ resolveTiePMPackagePath()
  │   ├─ 方案1: <安装目录>\数组工具\数组工具.xt
  │   ├─ 方案2: <安装目录>\数组工具\<内层目录>\数组工具.xt
  │   │         (GitHub archive 格式: <名>-<版本>/)
  │   └─ 均失败 → 空字符串
  │
  └─ 成功 → 使用解析后的绝对路径编译
     失败 → 报错"请先执行 '铁铺 安装 数组工具'"
```

### 实现位置

| 编译器 | 文件 | 关键函数 |
|--------|------|----------|
| 自举编译器 | `编译.xt` | `是裸包名()`, `获取铁铺安装目录()`, `解析铁铺包路径()`, `预扫描引入()` |
| Go 种子 | `compiler/compiler.go` | `isBarePackageName()`, `getTiePMInstallDir()`, `resolveTiePMPackagePath()`, `importExpressionCode()` |
| Go 解释器 | `evaluator/evaluator.go` | 同名函数(Eval 后缀), `evalImportExpression()` |

---

## 安装流程

```
铁铺 安装 <包名>
  │
  ├─ 1. GET /api/index 获取远程索引
  ├─ 2. 查找包名,获取最新版本的哈希、仓库
  ├─ 3. 构造 URL: {仓库}/archive/v{版本}.zip
  ├─ 4. 下载 ZIP 到 ~/.tiepm/缓存/
  ├─ 5. SHA256 验证(certutil -hashfile)
  ├─ 6. tar -xf 解压到 ~/.tiepm/已安装/<包名>/
  └─ 7. 清理下载缓存
```

### 本地目录结构

```
~/.tiepm/
├── 缓存/              # 下载的 ZIP 缓存
└── 已安装/            # 已安装的包
    ├── 数组工具/       # 每个包独立目录
    └── ...
```

---

## 发布流程

```
项目根目录/
├── tiepm.toml        # 包清单
├── src/              # 源代码
└── ...
```

### tiepm.toml 清单

```toml
[包]
名称 = "数组工具"
版本 = "1.0.0"
仓库 = "https://github.com/username/array-utils"

[作者]
姓名 = "开发者名"
邮箱 = "dev@example.com"

[描述]
简介 = "提供数组去重、过滤、映射等工具函数"
许可 = "MIT"
```

### 发布步骤

1. 编写 `tiepm.toml`
2. 执行 `铁铺 发布` — 打包为 `.zip` 并计算 SHA256
3. `git tag v1.0.0 && git push --tags`
4. 创建 GitHub Release,上传 `.zip`
5. 执行 `curl -X POST /api/publish` 向索引登记

---

## 安全设计

### 哈希验证

安装时必须验证下载文件的 SHA256 哈希与索引中记录的一致。当前通过 Windows `certutil -hashfile` 实现,后续计划用纯玄铁实现 SHA256。

### 线程安全

Go 服务端使用 `sync.RWMutex` 保护索引读写,支持并发查询但写入独占。

### 输入验证

- 发布 API 校验必填字段(名称、版本、哈希),缺少则返回 400
- 搜索 API 对关键词做大小写不敏感处理
- 哈希日志预览安全截断(<=16 字符直接显示,超过则截取并加 `...`)

---

## 已知局限与后续规划

| 局限 | 说明 | 计划 |
|------|------|------|
| 依赖解析递归深度限制 | 最大 10 层嵌套依赖 | 可配置 |
| 版本冲突无自动解决 | 冲突需手动统一版本 | v0.3+ 自动选择策略 |
| 无权限控制 | 发布无认证机制 | v0.3+ |
| Windows 限定 | 哈希/解压依赖 cmd 命令 | 跨平台支持 |
| 单索引源 | 仅支持一个远端索引 | 多注册表支持 |

---

## 项目文件结构

```
tiepm/                     # 铁铺核心库(玄铁源码)
├── 核心.xt                # 包管理核心逻辑
├── 核心_test.xt           # 测试
└── 示例tiepm.toml          # 清单模板

tiepm-site/                # 铁铺官网(独立 Go 项目)
├── .gitignore             # 排除于玄铁主仓库
├── main.go                # Go HTTP 服务端
├── static/
│   ├── index.html         # 前端页面
│   └── style.css          # 样式(深色主题)
└── data/
    └── index.json         # 包索引数据(运行时生成)

铁铺.xt                    # 独立 CLI 入口(可直接编译执行)
```

---

## 技术栈

| 组件 | 技术 | 原因 |
|------|------|------|
| CLI | 玄铁自举 | 展示语言能力,零外部依赖 |
| 官网后端 | Go + net/http | 单文件部署,无框架依赖 |
| 官网前端 | 原生 HTML/CSS/JS | 零构建步骤,embed 到二进制 |
| 源码托管 | GitHub Releases | 利用现有 CDN,无存储成本 |
| 索引存储 | 本地 JSON 文件 | 无数据库运维,Git 可追踪 |