npm package: @frontend-monitor/upload-sourcemaps
Description: CLI to upload Source Map files to frontend-monitor API (for CI/CD)
Author: mishuxing
Latest version: 0.2.3

# @frontend-monitor/upload-sourcemaps

[English](README.md) | **中文**

将构建产物中的 Source Map 上传到监控 API，便于在管理端将错误堆栈还原为源码位置。支持构建后上传或一键构建+上传。

---

## 三步上手

### 第一步：安装

```bash
npm install @frontend-monitor/upload-sourcemaps
```

或直接使用（不安装）：`npx @frontend-monitor/upload-sourcemaps`

### 第一步补充：写入推荐脚本（推荐）

在项目根目录执行：

```bash
npx @frontend-monitor/upload-sourcemaps init
```

会在 `package.json` 中增加 `build:upload` 与 `build:upload:archive` 脚本。配置好凭证（第二步）后，可直接使用 `npm run build:upload` 或 `npm run build:upload:archive`，无需记忆 CLI 参数。

### 第二步：配置凭证

在**项目根目录**创建 `.upload-sourcemaps.json`（不要提交到公开仓库）：

```json
{
  "projectId": "你的项目ID",
  "secretKey": "你的密钥",
  "endpoint": "https://你的监控API地址/monitorApi",
  "dir": "./dist"
}
```

也可用环境变量：`PROJECT_ID`、`SECRET_KEY`、`ENDPOINT`、`DIR`（适合 CI）。

### 第三步：执行上传

**方式 A：先构建，再上传**

```bash
npm run build
npx upload-sourcemaps
```

**方式 B：一键构建并上传**（自动开启 Source Map、注入 buildId）

```bash
npx upload-sourcemaps build
```

**推荐**：加上 `--archive` 打压缩包并加密上传，减少请求次数且更安全：

```bash
npx upload-sourcemaps --archive
# 或
npx upload-sourcemaps build --archive
```

---

## 文档导航

| 章节 | 内容 |
|------|------|
| [快速开始](#快速开始) | 构建后上传 / build 子命令 / 环境变量示例 |
| [配置](#配置) | 配置文件、与 SDK 共用 monitor.config.js、鉴权与证书 |
| [参数速查](#参数速查) | 命令行参数与环境变量一览 |
| [进阶](#进阶) | buildId、压缩包与分片、上传后删除 .map、build 子命令说明 |
| [CI/CD](#cicd-集成) | GitHub Actions、GitLab CI 示例 |
| [部署与 Nginx](#部署与-nginx) | 直连 API 与 Nginx 配置片段 |
| [与 SDK 配合](#与-sdk-配合) | release / buildId 联动说明 |
| [常见问题](#常见问题) | 堆栈未还原、无 .map、413 等 |

---

## 简介

- **作用**：将构建产物中的 `.map` 文件上传到监控 API，管理端据此将错误堆栈还原为**源码文件名、行号、函数名**。
- **不修改部署产物**：仅扫描 → 上传 → 可选删除本地 `.map`。
- **推荐用法**：使用 `--archive` 打压缩包并加密上传；单文件上传为明文，仅在不便打压缩包时使用。

---

## 快速开始

### 构建后上传

```bash
npm run build
npx upload-sourcemaps \
  --project-id your-project-id \
  --secret-key your-secret-key \
  --endpoint https://monitor.example.com/monitorApi \
  --dir ./dist
```

未指定 `--release` 时自动读取 `package.json` 的 `version`；未指定 `--build-id` 时自动生成。

### 一键构建并上传（build 子命令）

工具会：临时开启 Source Map → 执行构建 → 注入 buildId → 上传 .map。

```bash
npx upload-sourcemaps build \
  --project-id your-project-id \
  --secret-key your-secret-key \
  --endpoint https://monitor.example.com/monitorApi
```

构建命令默认为 `npm run build`，可在配置中通过 `buildCommand` 修改。

### 环境变量（适合 CI）

```bash
export PROJECT_ID=your-project-id
export SECRET_KEY=your-secret-key
export ENDPOINT=https://monitor.example.com/monitorApi
export DIR=./dist
npx upload-sourcemaps
```

---

## 配置

### 配置文件

- **未指定 `--config` 时**：先读 **monitor.config.js**（与 SDK 同格式，可共用 `projectId`、`secretKey`、`authCert`、`endpoint`），再合并 **.upload-sourcemaps.json**（可覆盖 `dir`、`chunkSize`、`buildCommand` 等）。
- **指定 `--config`**：只读该文件（如 `--config .upload-sourcemaps.json`）。

`.upload-sourcemaps.json` 示例：

```json
{
  "projectId": "your-project-id",
  "secretKey": "your-secret-key",
  "endpoint": "https://monitor.example.com/monitorApi",
  "dir": "./dist",
  "uploadAsArchive": true,
  "uploadMode": "mapOnly",
  "buildCommand": "npm run build",
  "buildOutputDir": "./dist"
}
```

`uploadMode` 可选 `mapOnly`（默认，仅 .map）或 `backup`（代码包打 tar；是否含 .map 由项目打包配置「是否保留 sourcemap」决定，便于回退）。

### 鉴权与证书

- **方式一**：Project ID + Secret Key（表单或请求头 `X-Project-Id`、`X-Secret-Key`）。
- **方式二**：证书认证。在管理端「项目管理 → 证书管理」颁发 `.mcert`，将**文件完整内容**配置为 `authCert`（或环境变量 `MONITOR_AUTH_CERT`）。新证书支持加密上传；旧证书上传为明文，需加密时请重新颁发。
- 证书与 SDK 共用，详见 [SDK 文档 - 证书认证](../sdk/README.zh-CN.md#证书认证替代-secret-key)。

---

## 参数速查

优先级：**命令行 > 环境变量 > 配置文件**。

| 参数 | 环境变量 | 说明 | 默认 |
|------|----------|------|------|
| `--project-id` | `PROJECT_ID` | 项目 ID | — |
| `--secret-key` | `SECRET_KEY` | 项目密钥 | — |
| `--endpoint` | `ENDPOINT` | API 地址 | — |
| `--dir` | `DIR` | 包含 .map 的目录 | 当前目录 |
| `--concurrency` | `CONCURRENCY` | 最大并发上传数（未使用 --archive 时） | 12 |
| `--release` | `RELEASE` | 版本号（需与 SDK 一致） | 读 package.json |
| `--build-id` | `BUILD_ID` | 构建序列号 | 自动生成 |
| `--archive` | — | 打 tar.gz 并加密上传（推荐） | — |
| `--upload-mode` | `UPLOAD_MODE` | 上传范围：`mapOnly` 仅 .map（默认）/ `backup` 代码包 tar（.map 依项目是否保留 sourcemap 配置） | `mapOnly` |
| `--chunk-size` | `UPLOAD_SOURCEMAPS_CHUNK_SIZE` | 分片大小（如 1M），与 --archive 同用 | 1MB |
| `--auth-cert` | `MONITOR_AUTH_CERT` | 证书内容（与 project-id+secret-key 二选一） | — |
| `-d` / `--delete-after-upload` | — | 上传后删除本地 .map | 按打包配置推断 |
| `--no-encrypt` | — | 与 --archive 同用时明文上传（兼容旧服务端） | — |
| `--config` | `UPLOAD_SOURCEMAPS_CONFIG` | 配置文件路径 | — |

### 子命令

| 子命令 | 说明 |
|--------|------|
| **init** | 在 `package.json` 中写入推荐脚本（`build:upload`、`build:upload:archive`）。在项目根目录执行，无需配置凭证。 |
| **build** | 一键：执行构建（强制生成 Source Map）并上传 .map，使用配置中的 `buildCommand` / `buildOutputDir`。 |
| **patch** | 临时开启打包工具的 Source Map 配置；之后自行构建并执行 `upload-sourcemaps`（无子命令）。 |
| **restore** | 从之前的 `patch` 备份（`.upload-sourcemaps.patch-backup.json`）恢复打包配置。 |

---

## 进阶

### 构建序列号（buildId）

同一版本可能构建多次，`buildId` 用于精确关联错误与 Source Map。使用 **`build` 子命令**时自动生成并注入构建环境，SDK 自动读取，无需手动配置。手动构建时需在打包配置中注入 `MONITOR_BUILD_ID`，上传时传相同 `--build-id`。

### 上传模式（--upload-mode）

- **mapOnly**（默认）：仅上传 `.map` 文件，供监控端堆栈还原使用。
- **backup**：将**除 .map 外的资源**打成 `project-code.tar.gz`，再根据**项目打包配置是否保留 sourcemap**（Vite/Webpack 等的 sourcemap 配置）决定备份 tar 是否包含 .map：若配置为保留独立 .map，则备份 tar 内为 `project-code.tar.gz` 与各 `.map` 同级；若不保留，则备份 tar 内仅 `project-code.tar.gz`。服务端解压后保存 .map（若有）与代码包。需与 `--archive` 一起使用。

配置文件示例：`"uploadMode": "backup"`，或命令行：`--upload-mode backup`。

### 压缩包上传（--archive）

将产物打成 `.tar.gz` 上传，服务端解压后保存 .map（若有）及 backup 模式下的 `project-code.tar.gz`。默认加密。当 `--upload-mode backup` 时，压缩包内为 `project-code.tar.gz` 与（依项目配置）各 `.map` 文件同级，再整体打成一个 tar 传输。

### 分片上传（--chunk-size）

与 `--archive` 同用时，可加 `--chunk-size 1M` 启用分片上传、断点续传与重试，适合 Nginx 限制 body 或大压缩包。init、每个分片、complete 请求均带自动重试（指数退避），同次运行内会先查已上传分片再补传，减少失败重跑成本。

### build 子命令：强制生成 Source Map

会临时修改打包配置（如 Vite 的 `sourcemap: false` → `true`），构建后自动恢复。支持 Vite、Webpack、Vue CLI、Rollup、Next.js、Nuxt。

### 上传后是否删除本地 .map

按项目打包配置自动推断：配置为输出独立 .map 则保留，否则删除。用 `-d` 可强制删除。

---

## 集成到 package.json

```json
{
  "scripts": {
    "build": "vite build",
    "upload-sourcemaps": "upload-sourcemaps --dir ./dist",
    "build:upload": "upload-sourcemaps build",
    "build:upload:archive": "upload-sourcemaps build --archive"
  }
}
```

```bash
npm run build && npm run upload-sourcemaps   # 构建后上传
npm run build:upload                         # 一键构建并上传
```

---

## CI/CD 集成

### GitHub Actions（构建后上传）

```yaml
- name: Build
  run: npm run build
- name: Upload Source Maps
  env:
    PROJECT_ID: ${{ secrets.MONITOR_PROJECT_ID }}
    SECRET_KEY: ${{ secrets.MONITOR_SECRET_KEY }}
    ENDPOINT: https://monitor.example.com/monitorApi
    DIR: ./dist
  run: npx @frontend-monitor/upload-sourcemaps
```

### GitHub Actions（一键构建并上传）

```yaml
- name: Build & Upload Source Maps
  env:
    PROJECT_ID: ${{ secrets.MONITOR_PROJECT_ID }}
    SECRET_KEY: ${{ secrets.MONITOR_SECRET_KEY }}
    ENDPOINT: https://monitor.example.com/monitorApi
  run: npx @frontend-monitor/upload-sourcemaps build
```

上传失败时退出码为 1，CI 会报错。

---

## 部署与 Nginx

- **推荐**：上传请求直连 API（CI 或内网），不经过 Nginx。例如 `ENDPOINT=http://127.0.0.1:3000` 或内网 IP，无需改 Nginx。
- **若必须经 Nginx**：对 `/monitorApi/sourcemaps/upload` 和 `upload-archive` 单独配置 `client_max_body_size`、`proxy_read_timeout` 等。详见仓库 [Nginx 部署配置说明](../../docs/Nginx部署配置说明.md)。

---

## 编程接口（Node.js）

```js
import { uploadAll, findMapFiles, uploadOne, uploadArchiveThenDelete } from '@frontend-monitor/upload-sourcemaps';

const { ok, fail } = await uploadAll({
  endpoint: 'https://monitor.example.com/monitorApi',
  projectId: 'your-project-id',
  secretKey: 'your-secret-key',
  release: '1.0.0',
  dir: './dist',
  deleteAfterUpload: true,
});
```

| 方法 | 说明 |
|------|------|
| `uploadAll(opts, logger?)` | 批量上传目录下 .map，返回 `{ ok, fail, deleted }` |
| `uploadOne(opts)` | 上传单个文件 |
| `findMapFiles(dir)` | 递归查找 .map 路径 |
| `uploadArchiveThenDelete(opts, logger?)` | 打 tar 包上传并删除本地 .map |
| `uploadArchiveChunkedThenDelete(opts, logger?)` | 分片上传压缩包并删除本地 .map |

---

## 与 SDK 配合

本工具负责**上传 Source Map**，[`@frontend-monitor/sdk`](https://www.npmjs.com/package/@frontend-monitor/sdk) 负责**采集并上报错误**。两者配合才能在管理端看到还原后的堆栈。

| 数据 | 本工具 | SDK | 说明 |
|------|--------|-----|------|
| `release` | `--release` 或 package.json version | `init({ release })` | **必须一致** |
| `buildId` | `build` 子命令自动注入 | 自动读 `MONITOR_BUILD_ID` | 精确区分同版本多次构建 |
| `projectId` / `endpoint` | 上传目标 | 上报目标 | 需同一项目、同一 API |

推荐：`npx upload-sourcemaps build --archive`，SDK 按文档接入即可，无需手动对齐参数。

---

## Nuxt（2 与 3）

- **输出目录**：客户端资源及 `.map` 在 **`.output/public/_nuxt/`**。需把 `dir` 指向该目录，上传脚本才会扫描并上传：
  - 在 `.upload-sourcemaps.json` 中：`"dir": ".output/public/_nuxt"`
  - 或：`npx upload-sourcemaps --dir .output/public/_nuxt`
- **生成 .map（兼容 SSR）**：  
  - **Nuxt 3**：服务端渲染需同时兼容服务端与浏览器端，请在 `nuxt.config.ts` 中同时开启 **server 与 client**：`sourcemap: { server: true, client: true }`。  
  - 或使用**一键** `npx upload-sourcemaps build --dir .output/public/_nuxt`：工具会临时同时开启 **服务端与客户端** source map、执行 `npm run build`，再从 `.output/public/_nuxt` 上传。
- **推荐脚本**（项目根目录）：

```json
"scripts": {
  "build": "nuxt build",
  "upload-sourcemaps": "upload-sourcemaps --dir .output/public/_nuxt"
}
```

然后执行：`npm run build && npx upload-sourcemaps`，或使用 `npx upload-sourcemaps build --dir .output/public/_nuxt`（endpoint/projectId/secretKey 写在配置或环境变量中）。

---

## 常见问题

**堆栈仍显示压缩后位置？** 确认 SDK 的 `release` 与上传版本一致；堆栈中的脚本名与上传的 .map 文件名对应；管理端选择了正确项目。

**build 子命令后没有 .map？** Vite 一般自动生效；Nuxt 需将 `dir` 设为 `.output/public/_nuxt`，并在 Nuxt 3 中开启 `sourcemap: { client: true }` 或使用 build 子命令。其他打包工具需在配置中根据 `BUILD_SOURCEMAP=1` 开启 Source Map。

**Project ID / Secret Key 在哪？** 登录管理端 → 项目设置。

**413 或超时？** 上传经 Nginx 时需放宽 body 与超时，或改用直连 API（见 [部署与 Nginx](#部署与-nginx)）。

---

## 版本记录

### 0.0.3（2026-03-10）

**当前稳定版本**
- 支持 `build` 子命令自动注入 buildId 并上传 Source Map
- 支持 `--archive` 模式打包为 tar.gz 后上传
- 支持证书认证（`--auth-cert`）
- 兼容 Vite / Webpack / Rollup 等主流打包工具

### 0.0.2（已撤回）

- 新增 archive 模式、证书认证支持

### 0.0.1 / 0.0.1-beta.1（已撤回）

- 初始版本，基础 Source Map 上传功能

## License

MIT