@frontend-monitor/upload-sourcemaps 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of @frontend-monitor/upload-sourcemaps might be problematic. Click here for more details.

package/README.md ADDED
@@ -0,0 +1,122 @@
1
+ # @frontend-monitor/upload-sourcemaps
2
+
3
+ 将构建产物中的 Source Map 文件批量上传到前端监控平台 API,便于错误堆栈还原。适用于 CI/CD 流水线。
4
+
5
+ ## 要求
6
+
7
+ - Node.js >= 18(使用原生 `fetch`、`FormData`)
8
+ - 监控平台 API 已部署,并已创建项目(获得 Project ID 与 Secret Key)
9
+
10
+ ## 安装
11
+
12
+ ```bash
13
+ # 安装为项目依赖(推荐在 CI 中与 npx 二选一)
14
+ npm install @frontend-monitor/upload-sourcemaps
15
+
16
+ # 全局安装(可选,便于直接使用命令)
17
+ npm install -g @frontend-monitor/upload-sourcemaps
18
+
19
+ # 或仅用 npx 运行,无需安装
20
+ npx @frontend-monitor/upload-sourcemaps --project-id xxx --secret-key yyy --endpoint https://api.example.com
21
+ ```
22
+
23
+ ## 用法
24
+
25
+ ### 参数(环境变量或命令行)
26
+
27
+ | 参数 | 环境变量 | 说明 | 必填 |
28
+ |------|----------|------|------|
29
+ | `--project-id` | `PROJECT_ID` | 项目 ID | 是 |
30
+ | `--secret-key` | `SECRET_KEY` | 项目密钥 | 是 |
31
+ | `--endpoint` | `ENDPOINT` | API 根地址,如 `https://api.example.com` | 是 |
32
+ | `--release` | `RELEASE` | 版本号,需与前端 SDK `init({ release })` 一致 | 否,默认 `default` |
33
+ | `--dir` | `DIR` | 包含 `.map` 文件的目录 | 否,默认当前目录 |
34
+
35
+ ### 示例
36
+
37
+ ```bash
38
+ # 使用命令行参数
39
+ npx @frontend-monitor/upload-sourcemaps \
40
+ --project-id p_xxx \
41
+ --secret-key your_secret_key \
42
+ --endpoint https://api.example.com \
43
+ --release 1.0.0 \
44
+ --dir ./dist
45
+
46
+ # 使用环境变量(适合 CI)
47
+ export PROJECT_ID=p_xxx
48
+ export SECRET_KEY=your_secret_key
49
+ export ENDPOINT=https://api.example.com
50
+ export RELEASE=1.0.0
51
+ export DIR=./dist
52
+ npx @frontend-monitor/upload-sourcemaps
53
+ ```
54
+
55
+ ### 在 CI 中使用
56
+
57
+ **GitHub Actions 示例:**
58
+
59
+ ```yaml
60
+ - name: Upload Source Maps
61
+ env:
62
+ PROJECT_ID: ${{ secrets.MONITOR_PROJECT_ID }}
63
+ SECRET_KEY: ${{ secrets.MONITOR_SECRET_KEY }}
64
+ ENDPOINT: https://api.your-company.com
65
+ RELEASE: ${{ github.ref_name }}
66
+ DIR: ./dist
67
+ run: npx @frontend-monitor/upload-sourcemaps
68
+ ```
69
+
70
+ **在代码中调用(Node.js):**
71
+
72
+ ```js
73
+ import { uploadAll } from '@frontend-monitor/upload-sourcemaps';
74
+
75
+ const { ok, fail } = await uploadAll({
76
+ endpoint: 'https://api.example.com',
77
+ projectId: 'p_xxx',
78
+ secretKey: 'your_secret_key',
79
+ release: '1.0.0',
80
+ dir: './dist',
81
+ });
82
+ console.log(`Uploaded ${ok} files, ${fail} failed`);
83
+ ```
84
+
85
+ ## 行为说明
86
+
87
+ - 递归扫描 `--dir` 下所有 `.map` 文件并逐个上传。
88
+ - 请求地址为 `{ENDPOINT}/sourcemaps/upload`,鉴权方式与监控平台 ingest 一致(Project ID + Secret Key)。
89
+ - 若有文件上传失败,会打印错误并以退出码 `1` 结束,便于 CI 失败感知。
90
+
91
+ ## 发布到 npm
92
+
93
+ 本包为 **scoped 包**(`@frontend-monitor/upload-sourcemaps`),首次发布需带 `--access public`。
94
+
95
+ ### 发布脚本说明
96
+
97
+ | 脚本 | 说明 |
98
+ |------|------|
99
+ | `prepublishOnly` | 发布前自动执行:检查 `bin`、`lib` 是否存在,避免漏文件 |
100
+ | `npm run publish:dry` | 仅做发布预检(不实际上传):先跑 prepublishOnly,再 `npm publish --dry-run` |
101
+ | `npm run publish:public` | 执行发布并设为公开:prepublishOnly + `npm publish --access public` |
102
+
103
+ ### 发布步骤
104
+
105
+ ```bash
106
+ cd packages/upload-sourcemaps
107
+
108
+ # 1. 确认将要发布的文件列表(推荐先执行)
109
+ npm run publish:dry
110
+
111
+ # 2. 登录 npm(未登录时)
112
+ npm login
113
+
114
+ # 3. 发布(scoped 包需加 --access public)
115
+ npm run publish:public
116
+ ```
117
+
118
+ 若已配置 `npm config set access public`,也可直接执行 `npm publish`。
119
+
120
+ ## License
121
+
122
+ MIT
@@ -0,0 +1,57 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * frontend-monitor-upload-sourcemaps CLI
4
+ *
5
+ * 环境变量或参数:
6
+ * PROJECT_ID / --project-id 项目 ID(必填)
7
+ * SECRET_KEY / --secret-key 项目密钥(必填)
8
+ * ENDPOINT / --endpoint API 地址(必填)
9
+ * RELEASE / --release release 版本(默认 default)
10
+ * DIR / --dir 要上传的目录(默认当前目录)
11
+ *
12
+ * 示例:
13
+ * npx frontend-monitor-upload-sourcemaps --project-id xxx --secret-key yyy --endpoint https://api.com
14
+ * PROJECT_ID=xxx SECRET_KEY=yyy ENDPOINT=https://api.com RELEASE=1.0.0 DIR=./dist upload-sourcemaps
15
+ */
16
+ import { uploadAll } from '../lib/upload.js';
17
+
18
+ function getArg(name, envName) {
19
+ const env = process.env[envName];
20
+ if (env) return env;
21
+ const i = process.argv.indexOf(name);
22
+ if (i !== -1 && process.argv[i + 1]) return process.argv[i + 1];
23
+ return null;
24
+ }
25
+
26
+ const projectId = getArg('--project-id', 'PROJECT_ID');
27
+ const secretKey = getArg('--secret-key', 'SECRET_KEY');
28
+ const endpoint = getArg('--endpoint', 'ENDPOINT') || '';
29
+ const release = getArg('--release', 'RELEASE') || 'default';
30
+ const dir = getArg('--dir', 'DIR') || process.cwd();
31
+
32
+ if (!projectId || !secretKey || !endpoint) {
33
+ console.error('Usage: PROJECT_ID, SECRET_KEY and ENDPOINT are required.');
34
+ console.error('');
35
+ console.error(' Environment:');
36
+ console.error(' PROJECT_ID=xxx SECRET_KEY=yyy ENDPOINT=https://api.com [RELEASE=1.0.0] [DIR=./dist]');
37
+ console.error('');
38
+ console.error(' Arguments:');
39
+ console.error(' --project-id <id> Project ID');
40
+ console.error(' --secret-key <key> Secret Key');
41
+ console.error(' --endpoint <url> API base URL');
42
+ console.error(' --release <tag> Release version (default: default)');
43
+ console.error(' --dir <path> Directory containing .map files (default: cwd)');
44
+ console.error('');
45
+ console.error(' Example:');
46
+ console.error(' npx @frontend-monitor/upload-sourcemaps --project-id p_xxx --secret-key sk_yyy --endpoint https://api.example.com --release 1.0.0 --dir ./dist');
47
+ process.exit(1);
48
+ }
49
+
50
+ const { ok, fail } = await uploadAll(
51
+ { endpoint, projectId, secretKey, release, dir },
52
+ console
53
+ );
54
+
55
+ if (fail > 0) {
56
+ process.exit(1);
57
+ }
package/lib/upload.js ADDED
@@ -0,0 +1,79 @@
1
+ import { readdir, readFile } from 'fs/promises';
2
+ import { join, basename } from 'path';
3
+
4
+ /**
5
+ * 递归收集目录下所有 .map 文件
6
+ */
7
+ export async function findMapFiles(dirPath, list = []) {
8
+ const entries = await readdir(dirPath, { withFileTypes: true });
9
+ for (const e of entries) {
10
+ const full = join(dirPath, e.name);
11
+ if (e.isDirectory()) {
12
+ await findMapFiles(full, list);
13
+ } else if (e.name.endsWith('.map')) {
14
+ list.push(full);
15
+ }
16
+ }
17
+ return list;
18
+ }
19
+
20
+ /**
21
+ * 上传单个 .map 文件到监控 API
22
+ * @param {object} opts - { endpoint, projectId, secretKey, release, filePath }
23
+ * @returns {Promise<object>} API 返回结果
24
+ */
25
+ export async function uploadOne(opts) {
26
+ const { endpoint, projectId, secretKey, release, filePath } = opts;
27
+ const base = basename(filePath, '.map');
28
+ const buf = await readFile(filePath);
29
+ const url = endpoint.replace(/\/$/, '') + '/sourcemaps/upload';
30
+ const form = new FormData();
31
+ form.append('projectId', projectId);
32
+ form.append('secretKey', secretKey);
33
+ form.append('release', release);
34
+ form.append('filename', base);
35
+ form.append('file', new Blob([buf]), basename(filePath));
36
+
37
+ const res = await fetch(url, {
38
+ method: 'POST',
39
+ headers: {
40
+ 'X-Project-Id': projectId,
41
+ 'X-Secret-Key': secretKey,
42
+ },
43
+ body: form,
44
+ });
45
+ const text = await res.text();
46
+ if (!res.ok) {
47
+ throw new Error(`${res.status} ${text}`);
48
+ }
49
+ return JSON.parse(text || '{}');
50
+ }
51
+
52
+ /**
53
+ * 批量上传目录下所有 .map 文件
54
+ * @param {object} opts - { endpoint, projectId, secretKey, release, dir }
55
+ * @param {object} [logger] - { log, error } 默认 console
56
+ * @returns {Promise<{ ok: number, fail: number }>}
57
+ */
58
+ export async function uploadAll(opts, logger = console) {
59
+ const { dir, endpoint, projectId, secretKey, release } = opts;
60
+ const files = await findMapFiles(dir);
61
+ if (files.length === 0) {
62
+ logger.log('No .map files found in', dir);
63
+ return { ok: 0, fail: 0 };
64
+ }
65
+ logger.log('Uploading', files.length, 'source map(s) to', endpoint, 'release=', release);
66
+ let ok = 0;
67
+ let fail = 0;
68
+ for (const f of files) {
69
+ try {
70
+ await uploadOne({ endpoint, projectId, secretKey, release, filePath: f });
71
+ logger.log(' OK', basename(f));
72
+ ok++;
73
+ } catch (err) {
74
+ logger.error(' FAIL', f, err.message);
75
+ fail++;
76
+ }
77
+ }
78
+ return { ok, fail };
79
+ }
package/package.json ADDED
@@ -0,0 +1,38 @@
1
+ {
2
+ "name": "@frontend-monitor/upload-sourcemaps",
3
+ "version": "1.0.0",
4
+ "description": "CLI to upload Source Map files to frontend-monitor API (for CI/CD)",
5
+ "type": "module",
6
+ "main": "lib/upload.js",
7
+ "bin": {
8
+ "upload-sourcemaps": "./bin/upload-sourcemaps.js"
9
+ },
10
+ "scripts": {
11
+ "prepublishOnly": "node -e \"require('fs').accessSync('bin/upload-sourcemaps.js'); require('fs').accessSync('lib/upload.js'); console.log('publish check ok');\"",
12
+ "publish:dry": "npm run prepublishOnly && npm publish --dry-run",
13
+ "publish:public": "npm run prepublishOnly && npm publish --access public"
14
+ },
15
+ "files": [
16
+ "bin",
17
+ "lib",
18
+ "README.md"
19
+ ],
20
+ "keywords": [
21
+ "sourcemap",
22
+ "source-map",
23
+ "frontend",
24
+ "monitoring",
25
+ "cli",
26
+ "upload",
27
+ "ci"
28
+ ],
29
+ "author": "mishuxing",
30
+ "license": "MIT",
31
+ "repository": {
32
+ "type": "git",
33
+ "url": ""
34
+ },
35
+ "engines": {
36
+ "node": ">=18"
37
+ }
38
+ }