@frontend-monitor/upload-sourcemaps 0.0.3 → 0.2.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.
- package/CHANGELOG.md +74 -0
- package/README.md +69 -2
- package/README.zh-CN.md +72 -2
- package/bin/upload-sourcemaps.js +338 -0
- package/dist/upload-sourcemaps.js +7 -1
- package/dist/upload.js +5 -1
- package/package.json +4 -3
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to `@frontend-monitor/upload-sourcemaps` are documented here.
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## [0.2.0] (Unreleased)
|
|
8
|
+
|
|
9
|
+
### 版本升级说明 / Release Notes
|
|
10
|
+
|
|
11
|
+
#### 新增 / Added
|
|
12
|
+
|
|
13
|
+
- **`init` 子命令**
|
|
14
|
+
在项目根目录执行 `npx @frontend-monitor/upload-sourcemaps init`,自动在 `package.json` 中写入推荐脚本:`build:upload`(构建并上传)、`build:upload:archive`(构建并打包上传)。无需记忆 CLI 参数,配置好凭证后直接使用 `npm run build:upload` 或 `npm run build:upload:archive`。
|
|
15
|
+
- **上传进度提示**
|
|
16
|
+
三种上传方式均会输出实时进度,避免用户误以为卡死:
|
|
17
|
+
- **多文件上传**:按「已上传文件总大小 / 总大小」显示 `上传进度 X.XX/Y.YY MB (百分比%)`,每 3 秒刷新一次。
|
|
18
|
+
- **单次归档上传**:按已发送字节显示 `上传进度 归档 X.XX/Y.YY MB (百分比%)`,最多每 3 秒刷新一次。
|
|
19
|
+
- **分片上传**:按已上传分片字节显示 `上传进度 分片 X.XX/Y.YY MB (百分比%)`,每 3 秒刷新一次。
|
|
20
|
+
进度统一写入 stderr,使用 `\r` 单行刷新。
|
|
21
|
+
|
|
22
|
+
#### 变更 / Changed
|
|
23
|
+
|
|
24
|
+
- **打包脚本性能**
|
|
25
|
+
本包自身构建(`build.js`)改为并行执行两个 esbuild 入口,缩短构建时间。
|
|
26
|
+
- **单次归档上传实现**
|
|
27
|
+
归档上传改为手动构建 multipart body 并以流式发送,便于按字节统计并输出上传进度(百分比基于已发送字节 / 总字节)。
|
|
28
|
+
|
|
29
|
+
#### 文档 / Documentation
|
|
30
|
+
|
|
31
|
+
- **SDK 配套说明**
|
|
32
|
+
`@frontend-monitor/sdk` 的 README(中英文)中「配套包:upload-sourcemaps」一节增加「一键写入推荐脚本」:推荐先执行 `npx @frontend-monitor/upload-sourcemaps init`,再使用 `npm run build:upload` / `build:upload:archive`。
|
|
33
|
+
- **本包 README**
|
|
34
|
+
- 快速开始中增加 Step 1b:执行 `init` 写入推荐脚本。
|
|
35
|
+
- 参数速查中增加「子命令」表格,说明 `init`、`build`、`patch`、`restore`。
|
|
36
|
+
|
|
37
|
+
#### 使用建议
|
|
38
|
+
|
|
39
|
+
- 新项目建议执行一次 `npx @frontend-monitor/upload-sourcemaps init`,之后在 CI 或本地使用 `npm run build:upload` 或 `npm run build:upload:archive`。
|
|
40
|
+
- 上传过程中关注终端中的「上传进度 … MB (…%)」即可判断是否在正常上传。
|
|
41
|
+
|
|
42
|
+
---
|
|
43
|
+
|
|
44
|
+
## [0.1.0] (2025-03-13)
|
|
45
|
+
|
|
46
|
+
### 版本升级说明 / Release Notes
|
|
47
|
+
|
|
48
|
+
#### 行为变更 / Behavior Changes
|
|
49
|
+
|
|
50
|
+
- **移除 `uploadMode: 'all'`**
|
|
51
|
+
仅保留 `mapOnly`(默认)与 `backup`。若配置或参数中使用了 `all`,将自动按 `backup` 处理。
|
|
52
|
+
- **backup 模式与项目配置联动**
|
|
53
|
+
备份 tar 是否包含 .map 由项目打包配置(如 Vite/Webpack 的 sourcemap、是否保留 .map 等)推断的 `keepMapInOutput` 决定:
|
|
54
|
+
- 项目**保留** .map 时:备份包包含 `project-code.tar.gz` + 所有 .map;
|
|
55
|
+
- 项目**不保留** .map 时:备份包仅包含 `project-code.tar.gz`(便于仅做部署包备份)。
|
|
56
|
+
|
|
57
|
+
#### 修复 / Fixes
|
|
58
|
+
|
|
59
|
+
- **backup 时未传入 `keepMapInOutput`**
|
|
60
|
+
修复 `upload.js` 中 `uploadArchiveThenDelete` / `uploadArchiveChunkedThenDelete` 未将 `keepMapInOutput` 传给 `uploadArchive`、`uploadArchiveChunked` 的问题,确保 backup 打包与删除行为与配置一致。
|
|
61
|
+
- **backup 仅上传部署包时的提示**
|
|
62
|
+
当 backup 模式且项目配置不保留 .map 时,服务端会保存 0 个 .map;现区分提示为「Backup uploaded (deploy package only; no .map in tar per project config).」,避免与「构建未产出 .map」混淆。
|
|
63
|
+
|
|
64
|
+
#### 使用建议
|
|
65
|
+
|
|
66
|
+
- 使用 **mapOnly**:只上传 .map,不打包代码(默认)。
|
|
67
|
+
- 使用 **backup**:上传部署包(及按项目配置可选的 .map),便于服务端保留版本与还原。
|
|
68
|
+
- 若此前使用 `--upload-mode all`,可改为 `--upload-mode backup`(或直接省略,脚本会对 `all` 自动按 backup 处理)。
|
|
69
|
+
|
|
70
|
+
---
|
|
71
|
+
|
|
72
|
+
## [0.0.3] and earlier
|
|
73
|
+
|
|
74
|
+
Initial releases: upload single .map files or archive (tar.gz), build subcommand, chunked upload, certificate auth.
|
package/README.md
CHANGED
|
@@ -16,6 +16,16 @@ npm install @frontend-monitor/upload-sourcemaps
|
|
|
16
16
|
|
|
17
17
|
Or run without installing: `npx @frontend-monitor/upload-sourcemaps`
|
|
18
18
|
|
|
19
|
+
### Step 1b: Add scripts to package.json (recommended)
|
|
20
|
+
|
|
21
|
+
From your project root, run:
|
|
22
|
+
|
|
23
|
+
```bash
|
|
24
|
+
npx @frontend-monitor/upload-sourcemaps init
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
This adds `build:upload` and `build:upload:archive` to your `package.json`. After configuring credentials (Step 2), you can run `npm run build:upload` or `npm run build:upload:archive` instead of remembering CLI options.
|
|
28
|
+
|
|
19
29
|
### Step 2: Configure credentials
|
|
20
30
|
|
|
21
31
|
Create `.upload-sourcemaps.json` at your **project root** (do not commit to public repos):
|
|
@@ -158,15 +168,26 @@ Priority: **CLI > env > config**.
|
|
|
158
168
|
| `--secret-key` | `SECRET_KEY` | Project secret | — |
|
|
159
169
|
| `--endpoint` | `ENDPOINT` | API URL | — |
|
|
160
170
|
| `--dir` | `DIR` | Directory with .map files | Current dir |
|
|
171
|
+
| `--concurrency` | `CONCURRENCY` | Max parallel uploads (when not using --archive) | 12 |
|
|
161
172
|
| `--release` | `RELEASE` | Version (must match SDK) | From package.json |
|
|
162
173
|
| `--build-id` | `BUILD_ID` | Build serial number | Auto-generated |
|
|
163
174
|
| `--archive` | — | Pack as tar.gz and upload encrypted (recommended) | — |
|
|
175
|
+
| `--upload-mode` | `UPLOAD_MODE` | `mapOnly` (default) / `backup` (code tar; .map included only if project keeps sourcemap) | `mapOnly` |
|
|
164
176
|
| `--chunk-size` | `UPLOAD_SOURCEMAPS_CHUNK_SIZE` | Chunk size with --archive (e.g. 1M) | 1MB |
|
|
165
177
|
| `--auth-cert` | `MONITOR_AUTH_CERT` | Certificate content (alternative to project-id + secret-key) | — |
|
|
166
178
|
| `-d` / `--delete-after-upload` | — | Delete local .map after upload | Inferred from bundler |
|
|
167
179
|
| `--no-encrypt` | — | With --archive: upload plain tar.gz (older servers) | — |
|
|
168
180
|
| `--config` | `UPLOAD_SOURCEMAPS_CONFIG` | Config file path | — |
|
|
169
181
|
|
|
182
|
+
### Subcommands
|
|
183
|
+
|
|
184
|
+
| Subcommand | Description |
|
|
185
|
+
|------------|-------------|
|
|
186
|
+
| **init** | Add recommended scripts to `package.json` (`build:upload`, `build:upload:archive`). Run from project root. No credentials needed. |
|
|
187
|
+
| **build** | One-step: run build (with Source Maps forced), then upload .map files. Uses `buildCommand` / `buildOutputDir` from config. |
|
|
188
|
+
| **patch** | Temporarily enable Source Map in bundler config; then run your build and `upload-sourcemaps` (no subcommand). |
|
|
189
|
+
| **restore** | Restore bundler config from a previous `patch` (from `.upload-sourcemaps.patch-backup.json`). |
|
|
190
|
+
|
|
170
191
|
---
|
|
171
192
|
|
|
172
193
|
## Advanced
|
|
@@ -175,9 +196,16 @@ Priority: **CLI > env > config**.
|
|
|
175
196
|
|
|
176
197
|
Links errors to the right Source Map when the same version is built multiple times. With the **build** subcommand, buildId is generated and injected automatically; the SDK reads it — no manual setup. For manual builds, inject `MONITOR_BUILD_ID` in your bundler and pass the same `--build-id` when uploading.
|
|
177
198
|
|
|
199
|
+
### Upload mode (--upload-mode)
|
|
200
|
+
|
|
201
|
+
- **mapOnly** (default): Upload only `.map` files for stack resolution.
|
|
202
|
+
- **backup**: Pack **non-.map resources** into `project-code.tar.gz`. Whether the backup tar also includes `.map` files is determined by your **project bundler config** (e.g. Vite/Webpack sourcemap setting): if the project is configured to keep separate .map files, the backup tar contains `project-code.tar.gz` and all `.map` files at the same level; otherwise the backup tar contains only `project-code.tar.gz`. Server extracts and saves .map (if any) and the code package. Use with `--archive`.
|
|
203
|
+
|
|
204
|
+
Config: `"uploadMode": "backup"` or CLI: `--upload-mode backup`.
|
|
205
|
+
|
|
178
206
|
### Archive upload (--archive)
|
|
179
207
|
|
|
180
|
-
Packs the output as `.tar.gz` and uploads it.
|
|
208
|
+
Packs the output as `.tar.gz` and uploads it. Server decrypts and extracts .map (if any) and backup `project-code.tar.gz`. Default is encrypted. With `--upload-mode backup`, the archive is a single tar: `project-code.tar.gz` plus (per project config) `.map` files at the same level.
|
|
181
209
|
|
|
182
210
|
### Chunked upload (--chunk-size)
|
|
183
211
|
|
|
@@ -290,11 +318,32 @@ Recommended: `npx upload-sourcemaps build --archive` and wire up the SDK as in i
|
|
|
290
318
|
|
|
291
319
|
---
|
|
292
320
|
|
|
321
|
+
## Nuxt (2 & 3)
|
|
322
|
+
|
|
323
|
+
- **Output directory**: Client assets and their `.map` files are under **`.output/public/_nuxt/`**. Set `dir` to that path so the CLI finds and uploads them:
|
|
324
|
+
- In `.upload-sourcemaps.json`: `"dir": ".output/public/_nuxt"`
|
|
325
|
+
- Or: `npx upload-sourcemaps --dir .output/public/_nuxt`
|
|
326
|
+
- **Generate .map (SSR-friendly)**:
|
|
327
|
+
- **Nuxt 3**: For SSR you need source maps for both server and browser. In `nuxt.config.ts` set **both** `server` and `client`: `sourcemap: { server: true, client: true }`.
|
|
328
|
+
- Or use **one-step** `npx upload-sourcemaps build --dir .output/public/_nuxt`: the tool will temporarily enable **both** server and client source maps, run `npm run build`, then upload from `.output/public/_nuxt`.
|
|
329
|
+
- **Suggested scripts** (project root):
|
|
330
|
+
|
|
331
|
+
```json
|
|
332
|
+
"scripts": {
|
|
333
|
+
"build": "nuxt build",
|
|
334
|
+
"upload-sourcemaps": "upload-sourcemaps --dir .output/public/_nuxt"
|
|
335
|
+
}
|
|
336
|
+
```
|
|
337
|
+
|
|
338
|
+
Then: `npm run build && npx upload-sourcemaps` or use `npx upload-sourcemaps build --dir .output/public/_nuxt` (with endpoint/projectId/secretKey in config or env).
|
|
339
|
+
|
|
340
|
+
---
|
|
341
|
+
|
|
293
342
|
## FAQ
|
|
294
343
|
|
|
295
344
|
**Stack still shows minified locations?** Ensure SDK `release` matches upload version; stack script names match uploaded .map basenames; correct project is selected in the admin.
|
|
296
345
|
|
|
297
|
-
**No .map after build subcommand?** Vite usually works as-is;
|
|
346
|
+
**No .map after build subcommand?** Vite usually works as-is; for Nuxt set `dir` to `.output/public/_nuxt` and enable `sourcemap: { client: true }` (Nuxt 3) or use the build subcommand. Other bundlers may need to enable Source Maps when `BUILD_SOURCEMAP=1`.
|
|
298
347
|
|
|
299
348
|
**Where to get Project ID / Secret Key?** Admin console → Project settings.
|
|
300
349
|
|
|
@@ -302,6 +351,24 @@ Recommended: `npx upload-sourcemaps build --archive` and wire up the SDK as in i
|
|
|
302
351
|
|
|
303
352
|
---
|
|
304
353
|
|
|
354
|
+
## Changelog
|
|
355
|
+
|
|
356
|
+
### 0.0.3 (2026-03-10)
|
|
357
|
+
|
|
358
|
+
**Current stable release**
|
|
359
|
+
- Support `build` subcommand to auto-inject buildId and upload Source Maps
|
|
360
|
+
- Support `--archive` mode to pack as tar.gz before upload
|
|
361
|
+
- Support certificate authentication (`--auth-cert`)
|
|
362
|
+
- Compatible with Vite / Webpack / Rollup and other bundlers
|
|
363
|
+
|
|
364
|
+
### 0.0.2 (withdrawn)
|
|
365
|
+
|
|
366
|
+
- Added archive mode, certificate authentication support
|
|
367
|
+
|
|
368
|
+
### 0.0.1 / 0.0.1-beta.1 (withdrawn)
|
|
369
|
+
|
|
370
|
+
- Initial version with basic Source Map upload functionality
|
|
371
|
+
|
|
305
372
|
## License
|
|
306
373
|
|
|
307
374
|
MIT
|
package/README.zh-CN.md
CHANGED
|
@@ -16,6 +16,16 @@ npm install @frontend-monitor/upload-sourcemaps
|
|
|
16
16
|
|
|
17
17
|
或直接使用(不安装):`npx @frontend-monitor/upload-sourcemaps`
|
|
18
18
|
|
|
19
|
+
### 第一步补充:写入推荐脚本(推荐)
|
|
20
|
+
|
|
21
|
+
在项目根目录执行:
|
|
22
|
+
|
|
23
|
+
```bash
|
|
24
|
+
npx @frontend-monitor/upload-sourcemaps init
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
会在 `package.json` 中增加 `build:upload` 与 `build:upload:archive` 脚本。配置好凭证(第二步)后,可直接使用 `npm run build:upload` 或 `npm run build:upload:archive`,无需记忆 CLI 参数。
|
|
28
|
+
|
|
19
29
|
### 第二步:配置凭证
|
|
20
30
|
|
|
21
31
|
在**项目根目录**创建 `.upload-sourcemaps.json`(不要提交到公开仓库):
|
|
@@ -135,11 +145,14 @@ npx upload-sourcemaps
|
|
|
135
145
|
"endpoint": "https://monitor.example.com/monitorApi",
|
|
136
146
|
"dir": "./dist",
|
|
137
147
|
"uploadAsArchive": true,
|
|
148
|
+
"uploadMode": "mapOnly",
|
|
138
149
|
"buildCommand": "npm run build",
|
|
139
150
|
"buildOutputDir": "./dist"
|
|
140
151
|
}
|
|
141
152
|
```
|
|
142
153
|
|
|
154
|
+
`uploadMode` 可选 `mapOnly`(默认,仅 .map)或 `backup`(代码包打 tar;是否含 .map 由项目打包配置「是否保留 sourcemap」决定,便于回退)。
|
|
155
|
+
|
|
143
156
|
### 鉴权与证书
|
|
144
157
|
|
|
145
158
|
- **方式一**:Project ID + Secret Key(表单或请求头 `X-Project-Id`、`X-Secret-Key`)。
|
|
@@ -158,15 +171,26 @@ npx upload-sourcemaps
|
|
|
158
171
|
| `--secret-key` | `SECRET_KEY` | 项目密钥 | — |
|
|
159
172
|
| `--endpoint` | `ENDPOINT` | API 地址 | — |
|
|
160
173
|
| `--dir` | `DIR` | 包含 .map 的目录 | 当前目录 |
|
|
174
|
+
| `--concurrency` | `CONCURRENCY` | 最大并发上传数(未使用 --archive 时) | 12 |
|
|
161
175
|
| `--release` | `RELEASE` | 版本号(需与 SDK 一致) | 读 package.json |
|
|
162
176
|
| `--build-id` | `BUILD_ID` | 构建序列号 | 自动生成 |
|
|
163
177
|
| `--archive` | — | 打 tar.gz 并加密上传(推荐) | — |
|
|
178
|
+
| `--upload-mode` | `UPLOAD_MODE` | 上传范围:`mapOnly` 仅 .map(默认)/ `backup` 代码包 tar(.map 依项目是否保留 sourcemap 配置) | `mapOnly` |
|
|
164
179
|
| `--chunk-size` | `UPLOAD_SOURCEMAPS_CHUNK_SIZE` | 分片大小(如 1M),与 --archive 同用 | 1MB |
|
|
165
180
|
| `--auth-cert` | `MONITOR_AUTH_CERT` | 证书内容(与 project-id+secret-key 二选一) | — |
|
|
166
181
|
| `-d` / `--delete-after-upload` | — | 上传后删除本地 .map | 按打包配置推断 |
|
|
167
182
|
| `--no-encrypt` | — | 与 --archive 同用时明文上传(兼容旧服务端) | — |
|
|
168
183
|
| `--config` | `UPLOAD_SOURCEMAPS_CONFIG` | 配置文件路径 | — |
|
|
169
184
|
|
|
185
|
+
### 子命令
|
|
186
|
+
|
|
187
|
+
| 子命令 | 说明 |
|
|
188
|
+
|--------|------|
|
|
189
|
+
| **init** | 在 `package.json` 中写入推荐脚本(`build:upload`、`build:upload:archive`)。在项目根目录执行,无需配置凭证。 |
|
|
190
|
+
| **build** | 一键:执行构建(强制生成 Source Map)并上传 .map,使用配置中的 `buildCommand` / `buildOutputDir`。 |
|
|
191
|
+
| **patch** | 临时开启打包工具的 Source Map 配置;之后自行构建并执行 `upload-sourcemaps`(无子命令)。 |
|
|
192
|
+
| **restore** | 从之前的 `patch` 备份(`.upload-sourcemaps.patch-backup.json`)恢复打包配置。 |
|
|
193
|
+
|
|
170
194
|
---
|
|
171
195
|
|
|
172
196
|
## 进阶
|
|
@@ -175,9 +199,16 @@ npx upload-sourcemaps
|
|
|
175
199
|
|
|
176
200
|
同一版本可能构建多次,`buildId` 用于精确关联错误与 Source Map。使用 **`build` 子命令**时自动生成并注入构建环境,SDK 自动读取,无需手动配置。手动构建时需在打包配置中注入 `MONITOR_BUILD_ID`,上传时传相同 `--build-id`。
|
|
177
201
|
|
|
202
|
+
### 上传模式(--upload-mode)
|
|
203
|
+
|
|
204
|
+
- **mapOnly**(默认):仅上传 `.map` 文件,供监控端堆栈还原使用。
|
|
205
|
+
- **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` 一起使用。
|
|
206
|
+
|
|
207
|
+
配置文件示例:`"uploadMode": "backup"`,或命令行:`--upload-mode backup`。
|
|
208
|
+
|
|
178
209
|
### 压缩包上传(--archive)
|
|
179
210
|
|
|
180
|
-
将产物打成 `.tar.gz`
|
|
211
|
+
将产物打成 `.tar.gz` 上传,服务端解压后保存 .map(若有)及 backup 模式下的 `project-code.tar.gz`。默认加密。当 `--upload-mode backup` 时,压缩包内为 `project-code.tar.gz` 与(依项目配置)各 `.map` 文件同级,再整体打成一个 tar 传输。
|
|
181
212
|
|
|
182
213
|
### 分片上传(--chunk-size)
|
|
183
214
|
|
|
@@ -290,11 +321,32 @@ const { ok, fail } = await uploadAll({
|
|
|
290
321
|
|
|
291
322
|
---
|
|
292
323
|
|
|
324
|
+
## Nuxt(2 与 3)
|
|
325
|
+
|
|
326
|
+
- **输出目录**:客户端资源及 `.map` 在 **`.output/public/_nuxt/`**。需把 `dir` 指向该目录,上传脚本才会扫描并上传:
|
|
327
|
+
- 在 `.upload-sourcemaps.json` 中:`"dir": ".output/public/_nuxt"`
|
|
328
|
+
- 或:`npx upload-sourcemaps --dir .output/public/_nuxt`
|
|
329
|
+
- **生成 .map(兼容 SSR)**:
|
|
330
|
+
- **Nuxt 3**:服务端渲染需同时兼容服务端与浏览器端,请在 `nuxt.config.ts` 中同时开启 **server 与 client**:`sourcemap: { server: true, client: true }`。
|
|
331
|
+
- 或使用**一键** `npx upload-sourcemaps build --dir .output/public/_nuxt`:工具会临时同时开启 **服务端与客户端** source map、执行 `npm run build`,再从 `.output/public/_nuxt` 上传。
|
|
332
|
+
- **推荐脚本**(项目根目录):
|
|
333
|
+
|
|
334
|
+
```json
|
|
335
|
+
"scripts": {
|
|
336
|
+
"build": "nuxt build",
|
|
337
|
+
"upload-sourcemaps": "upload-sourcemaps --dir .output/public/_nuxt"
|
|
338
|
+
}
|
|
339
|
+
```
|
|
340
|
+
|
|
341
|
+
然后执行:`npm run build && npx upload-sourcemaps`,或使用 `npx upload-sourcemaps build --dir .output/public/_nuxt`(endpoint/projectId/secretKey 写在配置或环境变量中)。
|
|
342
|
+
|
|
343
|
+
---
|
|
344
|
+
|
|
293
345
|
## 常见问题
|
|
294
346
|
|
|
295
347
|
**堆栈仍显示压缩后位置?** 确认 SDK 的 `release` 与上传版本一致;堆栈中的脚本名与上传的 .map 文件名对应;管理端选择了正确项目。
|
|
296
348
|
|
|
297
|
-
**build 子命令后没有 .map?** Vite
|
|
349
|
+
**build 子命令后没有 .map?** Vite 一般自动生效;Nuxt 需将 `dir` 设为 `.output/public/_nuxt`,并在 Nuxt 3 中开启 `sourcemap: { client: true }` 或使用 build 子命令。其他打包工具需在配置中根据 `BUILD_SOURCEMAP=1` 开启 Source Map。
|
|
298
350
|
|
|
299
351
|
**Project ID / Secret Key 在哪?** 登录管理端 → 项目设置。
|
|
300
352
|
|
|
@@ -302,6 +354,24 @@ const { ok, fail } = await uploadAll({
|
|
|
302
354
|
|
|
303
355
|
---
|
|
304
356
|
|
|
357
|
+
## 版本记录
|
|
358
|
+
|
|
359
|
+
### 0.0.3(2026-03-10)
|
|
360
|
+
|
|
361
|
+
**当前稳定版本**
|
|
362
|
+
- 支持 `build` 子命令自动注入 buildId 并上传 Source Map
|
|
363
|
+
- 支持 `--archive` 模式打包为 tar.gz 后上传
|
|
364
|
+
- 支持证书认证(`--auth-cert`)
|
|
365
|
+
- 兼容 Vite / Webpack / Rollup 等主流打包工具
|
|
366
|
+
|
|
367
|
+
### 0.0.2(已撤回)
|
|
368
|
+
|
|
369
|
+
- 新增 archive 模式、证书认证支持
|
|
370
|
+
|
|
371
|
+
### 0.0.1 / 0.0.1-beta.1(已撤回)
|
|
372
|
+
|
|
373
|
+
- 初始版本,基础 Source Map 上传功能
|
|
374
|
+
|
|
305
375
|
## License
|
|
306
376
|
|
|
307
377
|
MIT
|
|
@@ -0,0 +1,338 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/** upload-sourcemaps [build] [options] - 上传 Source Map 到监控 API */
|
|
3
|
+
import { readFile, writeFile, access } from 'fs/promises';
|
|
4
|
+
import { resolve, dirname, join } from 'path';
|
|
5
|
+
import { fileURLToPath } from 'url';
|
|
6
|
+
import { randomUUID } from 'crypto';
|
|
7
|
+
import { spawn } from 'child_process';
|
|
8
|
+
import { uploadAll, uploadArchiveThenDelete, uploadArchiveChunkedThenDelete } from '../lib/upload.js';
|
|
9
|
+
import { getBundlerSourceMapConfig, findProjectRoot } from '../lib/detect-sourcemap-config.js';
|
|
10
|
+
import { prepareForceSourcemap, restoreFromBackup, PATCH_BACKUP_FILENAME } from '../lib/force-sourcemap-build.js';
|
|
11
|
+
|
|
12
|
+
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
13
|
+
|
|
14
|
+
function getArg(name, envName) {
|
|
15
|
+
const env = process.env[envName];
|
|
16
|
+
if (env) return env;
|
|
17
|
+
const i = process.argv.indexOf(name);
|
|
18
|
+
if (i !== -1 && process.argv[i + 1]) return process.argv[i + 1];
|
|
19
|
+
return null;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
function hasFlag(name) {
|
|
23
|
+
return process.argv.includes(name);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/** 解析分片大小字符串(如 5M、5MB、1048576)为字节数。@param {string|number} v @returns {number|null} */
|
|
27
|
+
function parseChunkSizeFromValue(v) {
|
|
28
|
+
if (v == null || v === '') return null;
|
|
29
|
+
const s = String(v).trim().toUpperCase();
|
|
30
|
+
const num = parseInt(s.replace(/[^0-9]/g, ''), 10) || 0;
|
|
31
|
+
if (s.endsWith('G')) return num * 1024 * 1024 * 1024;
|
|
32
|
+
if (s.endsWith('MB') || s.endsWith('M')) return num * 1024 * 1024;
|
|
33
|
+
if (s.endsWith('KB') || s.endsWith('K')) return num * 1024;
|
|
34
|
+
if (/^\d+$/.test(String(v).trim())) return parseInt(String(v).trim(), 10) || null;
|
|
35
|
+
return num > 0 ? num * 1024 * 1024 : null;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/** 从 --chunk-size / 环境变量 / config 解析分片大小(字节)。@param {object} [cfg] cfg.chunkSize @returns {number|null} */
|
|
39
|
+
function parseChunkSize(cfg = {}) {
|
|
40
|
+
const v = getArg('--chunk-size', 'UPLOAD_SOURCEMAPS_CHUNK_SIZE') || cfg.chunkSize;
|
|
41
|
+
return parseChunkSizeFromValue(v);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/** 加载上传配置(monitor.config.js + .upload-sourcemaps.json 或 --config 指定文件)。@returns {Promise<object>} */
|
|
45
|
+
async function loadConfig() {
|
|
46
|
+
const { resolveUploadConfig } = await import('../lib/load-monitor-config.js');
|
|
47
|
+
return resolveUploadConfig(process.cwd(), getArg('--config', 'UPLOAD_SOURCEMAPS_CONFIG') || undefined);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/** 从目录向上查找 package.json 取 version。@param {string} dir 起始目录 @returns {Promise<string|null>} */
|
|
51
|
+
async function getVersionFromPackageJson(dir) {
|
|
52
|
+
const candidates = [dir, resolve(dir, '..'), process.cwd()];
|
|
53
|
+
for (const d of candidates) {
|
|
54
|
+
try {
|
|
55
|
+
const raw = await readFile(join(d, 'package.json'), 'utf-8');
|
|
56
|
+
const pkg = JSON.parse(raw);
|
|
57
|
+
if (pkg.version && typeof pkg.version === 'string') {
|
|
58
|
+
return pkg.version.trim();
|
|
59
|
+
}
|
|
60
|
+
} catch {
|
|
61
|
+
continue;
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
return null;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/** 解析 release:显式 > config > package.json version > 'default'。@param {string} [explicitOrConfig] @param {string} [dir] @returns {Promise<string>} */
|
|
68
|
+
async function resolveRelease(explicitOrConfig, dir) {
|
|
69
|
+
if (explicitOrConfig && explicitOrConfig !== 'default') return explicitOrConfig;
|
|
70
|
+
const fromPkg = await getVersionFromPackageJson(dir);
|
|
71
|
+
return fromPkg || 'default';
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
let config = {};
|
|
75
|
+
try {
|
|
76
|
+
config = await loadConfig();
|
|
77
|
+
} catch {
|
|
78
|
+
config = {};
|
|
79
|
+
}
|
|
80
|
+
const isBuild = process.argv[2] === 'build';
|
|
81
|
+
const isPatch = process.argv[2] === 'patch';
|
|
82
|
+
const isRestore = process.argv[2] === 'restore';
|
|
83
|
+
const isInit = process.argv[2] === 'init';
|
|
84
|
+
|
|
85
|
+
function effectiveNoEncrypt(noEncryptFlag, configNoEncrypt) {
|
|
86
|
+
return noEncryptFlag || configNoEncrypt === true;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
async function runBuildThenUpload() {
|
|
90
|
+
const projectId = getArg('--project-id', 'PROJECT_ID') || config.projectId;
|
|
91
|
+
const secretKey = getArg('--secret-key', 'SECRET_KEY') || config.secretKey;
|
|
92
|
+
const authCert = getArg('--auth-cert', 'MONITOR_AUTH_CERT') || config.authCert;
|
|
93
|
+
const endpoint = (getArg('--endpoint', 'ENDPOINT') || config.endpoint || '').replace(/\/$/, '');
|
|
94
|
+
if (!endpoint) {
|
|
95
|
+
throw new Error('build subcommand requires endpoint (config file or env/args).');
|
|
96
|
+
}
|
|
97
|
+
if (!authCert && (!projectId || !secretKey)) {
|
|
98
|
+
throw new Error('build subcommand requires projectId+secretKey or authCert (config file or env/args).');
|
|
99
|
+
}
|
|
100
|
+
const buildCommand = config.buildCommand || process.env.UPLOAD_SOURCEMAPS_BUILD_CMD || 'npm run build';
|
|
101
|
+
const buildOutputDir = config.buildOutputDir || config.dir || getArg('--dir', 'DIR') || process.cwd();
|
|
102
|
+
const useArchive = hasFlag('--archive') || config.uploadAsArchive === true;
|
|
103
|
+
const noEncrypt = effectiveNoEncrypt(hasFlag('--no-encrypt'), config.noEncrypt);
|
|
104
|
+
let uploadModeBuild = getArg('--upload-mode', 'UPLOAD_MODE') || config.uploadMode || 'mapOnly';
|
|
105
|
+
if (uploadModeBuild === 'all') uploadModeBuild = 'backup';
|
|
106
|
+
const bundlerConfig = await getBundlerSourceMapConfig(buildOutputDir);
|
|
107
|
+
const deleteAfter = !bundlerConfig.keepMapInOutput || hasFlag('--delete-after-upload') || hasFlag('-d') || config.deleteAfterUpload === true;
|
|
108
|
+
const buildId = getArg('--build-id', 'BUILD_ID') || config.buildId || randomUUID();
|
|
109
|
+
const forceRestore = await prepareForceSourcemap(buildOutputDir);
|
|
110
|
+
try {
|
|
111
|
+
console.log('Step 1: Running build (forcing source map generation):', buildCommand);
|
|
112
|
+
console.log('Build ID:', buildId);
|
|
113
|
+
const [cmd, ...args] = buildCommand.split(/\s+/);
|
|
114
|
+
await new Promise((res, rej) => {
|
|
115
|
+
const child = spawn(cmd, args, {
|
|
116
|
+
stdio: 'inherit',
|
|
117
|
+
shell: true,
|
|
118
|
+
env: { ...process.env, BUILD_SOURCEMAP: '1', SOURCEMAP: '1', MONITOR_BUILD_ID: buildId, VITE_MONITOR_BUILD_ID: buildId },
|
|
119
|
+
});
|
|
120
|
+
child.on('exit', (code) => (code === 0 ? res() : rej(new Error(`Build exited ${code}`))));
|
|
121
|
+
});
|
|
122
|
+
console.log('Step 2: Uploading source maps...');
|
|
123
|
+
const release = await resolveRelease(getArg('--release', 'RELEASE') || config.release, buildOutputDir);
|
|
124
|
+
if (release !== 'default') console.log('Release (from package.json or config):', release);
|
|
125
|
+
const chunkSizeBytes = parseChunkSize(config);
|
|
126
|
+
if (useArchive) {
|
|
127
|
+
const archiveOpts = { dir: buildOutputDir, endpoint, projectId, secretKey, authCert, release, buildId, deleteEvenOnFailure: false, noEncrypt, uploadMode: uploadModeBuild, keepMapInOutput: bundlerConfig.keepMapInOutput };
|
|
128
|
+
const archiveResult = chunkSizeBytes
|
|
129
|
+
? await uploadArchiveChunkedThenDelete({ ...archiveOpts, chunkSizeBytes }, console)
|
|
130
|
+
: await uploadArchiveThenDelete(archiveOpts, console);
|
|
131
|
+
if (archiveResult.uploaded === 0) {
|
|
132
|
+
console.log('No .map files in output. For Vite we patch config to force source map; check build output dir and README.');
|
|
133
|
+
}
|
|
134
|
+
} else {
|
|
135
|
+
const buildConcurrency = Math.max(1, Math.min(64, parseInt(getArg('--concurrency', 'CONCURRENCY') || config.concurrency || '12', 10) || 12));
|
|
136
|
+
const { ok, fail } = await uploadAll(
|
|
137
|
+
{ endpoint, projectId, secretKey, authCert, release, buildId, dir: buildOutputDir, deleteAfterUpload: deleteAfter, deleteEvenOnFailure: false, concurrency: buildConcurrency },
|
|
138
|
+
console
|
|
139
|
+
);
|
|
140
|
+
if (fail > 0) {
|
|
141
|
+
throw new Error('Upload had failures. Local .map files were NOT deleted. Fix errors and re-run.');
|
|
142
|
+
}
|
|
143
|
+
if (ok === 0) {
|
|
144
|
+
console.log('No .map files in output. For Vite we patch config to force source map; check build output dir and README.');
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
console.log('Done. Output dir:', buildOutputDir);
|
|
148
|
+
} catch (err) {
|
|
149
|
+
console.error('Error:', err.message);
|
|
150
|
+
if (deleteAfter) {
|
|
151
|
+
console.error('If the failure was during upload, local .map files may have been removed. Fix the issue and re-run.');
|
|
152
|
+
} else {
|
|
153
|
+
console.error('Local files were not modified. Fix the issue and re-run.');
|
|
154
|
+
}
|
|
155
|
+
throw err;
|
|
156
|
+
} finally {
|
|
157
|
+
await forceRestore.restore();
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
/** 内置推荐的 npm scripts,减轻使用方配置成本 */
|
|
162
|
+
const RECOMMENDED_SCRIPTS = {
|
|
163
|
+
'build:upload': 'upload-sourcemaps build',
|
|
164
|
+
'build:upload:archive': 'upload-sourcemaps build --archive',
|
|
165
|
+
};
|
|
166
|
+
|
|
167
|
+
async function runInit() {
|
|
168
|
+
const cwd = process.cwd();
|
|
169
|
+
const root = await findProjectRoot(cwd);
|
|
170
|
+
if (!root) {
|
|
171
|
+
console.error('[upload-sourcemaps] Cannot find project root (no package.json from:', cwd, '). Run from project root.');
|
|
172
|
+
process.exitCode = 1;
|
|
173
|
+
return;
|
|
174
|
+
}
|
|
175
|
+
const pkgPath = join(root, 'package.json');
|
|
176
|
+
let pkg;
|
|
177
|
+
try {
|
|
178
|
+
const raw = await readFile(pkgPath, 'utf-8');
|
|
179
|
+
pkg = JSON.parse(raw);
|
|
180
|
+
} catch (e) {
|
|
181
|
+
console.error('[upload-sourcemaps] Failed to read package.json:', e.message);
|
|
182
|
+
process.exitCode = 1;
|
|
183
|
+
return;
|
|
184
|
+
}
|
|
185
|
+
const scripts = pkg.scripts || {};
|
|
186
|
+
let added = 0;
|
|
187
|
+
for (const [name, cmd] of Object.entries(RECOMMENDED_SCRIPTS)) {
|
|
188
|
+
if (!scripts[name]) {
|
|
189
|
+
scripts[name] = cmd;
|
|
190
|
+
added++;
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
pkg.scripts = scripts;
|
|
194
|
+
try {
|
|
195
|
+
await writeFile(pkgPath, JSON.stringify(pkg, null, 2) + '\n', 'utf-8');
|
|
196
|
+
} catch (e) {
|
|
197
|
+
console.error('[upload-sourcemaps] Failed to write package.json:', e.message);
|
|
198
|
+
process.exitCode = 1;
|
|
199
|
+
return;
|
|
200
|
+
}
|
|
201
|
+
let hasJson = false;
|
|
202
|
+
try {
|
|
203
|
+
await access(join(root, '.upload-sourcemaps.json'));
|
|
204
|
+
hasJson = true;
|
|
205
|
+
} catch {
|
|
206
|
+
// no file
|
|
207
|
+
}
|
|
208
|
+
console.log('[upload-sourcemaps] init ok. Added', added, 'script(s) to package.json.');
|
|
209
|
+
if (added > 0) {
|
|
210
|
+
console.log(' Use: npm run build:upload (build + upload .map)');
|
|
211
|
+
console.log(' Or: npm run build:upload:archive (build + pack & upload, recommended)');
|
|
212
|
+
}
|
|
213
|
+
if (!hasJson) {
|
|
214
|
+
console.log(' Config: add monitor.config.js or .upload-sourcemaps.json (see README).');
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
if (isInit) {
|
|
219
|
+
await runInit();
|
|
220
|
+
} else if (isPatch) {
|
|
221
|
+
const patchDir = getArg('--dir', 'DIR') || config.dir || process.cwd();
|
|
222
|
+
const root = await findProjectRoot(patchDir);
|
|
223
|
+
if (!root) {
|
|
224
|
+
console.error('[upload-sourcemaps] Could not find project root (no package.json from dir:', patchDir, '). Fix --dir or run from project root.');
|
|
225
|
+
process.exitCode = 1;
|
|
226
|
+
} else {
|
|
227
|
+
const backupPath = join(root, PATCH_BACKUP_FILENAME);
|
|
228
|
+
const result = await prepareForceSourcemap(patchDir, { writeBackupTo: backupPath });
|
|
229
|
+
if (result.patchedFile) {
|
|
230
|
+
try {
|
|
231
|
+
const { readFile } = await import('fs/promises');
|
|
232
|
+
const patchedContent = await readFile(result.patchedFile, 'utf-8');
|
|
233
|
+
const hasTop = /sourcemap\s*:\s*\{\s*[\s\S]*?server\s*:\s*true[\s\S]*?client\s*:\s*true/.test(patchedContent) || /sourcemap\s*:\s*\{\s*[\s\S]*?client\s*:\s*true[\s\S]*?server\s*:\s*true/.test(patchedContent);
|
|
234
|
+
const hasViteTrue = /sourcemap\s*:\s*true/.test(patchedContent);
|
|
235
|
+
console.log('[upload-sourcemaps] Verified:', hasTop ? 'top-level sourcemap' : '(no top-level)', hasViteTrue ? 'vite.build.sourcemap=true' : '');
|
|
236
|
+
} catch (_) {}
|
|
237
|
+
console.log('Run your build, then run: upload-sourcemaps restore');
|
|
238
|
+
} else {
|
|
239
|
+
console.log('No config needed patching (source map already enabled or unsupported bundler).');
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
} else if (isRestore) {
|
|
243
|
+
const backupPath = getArg('--backup', 'UPLOAD_SOURCEMAPS_PATCH_BACKUP') || join(process.cwd(), PATCH_BACKUP_FILENAME);
|
|
244
|
+
const restored = await restoreFromBackup(backupPath);
|
|
245
|
+
if (!restored) {
|
|
246
|
+
console.error('Restore failed or no backup file found at', backupPath);
|
|
247
|
+
process.exitCode = 1;
|
|
248
|
+
}
|
|
249
|
+
} else if (isBuild) {
|
|
250
|
+
try {
|
|
251
|
+
await runBuildThenUpload();
|
|
252
|
+
} catch (err) {
|
|
253
|
+
process.exitCode = 1;
|
|
254
|
+
}
|
|
255
|
+
} else {
|
|
256
|
+
const projectId = getArg('--project-id', 'PROJECT_ID') || config.projectId;
|
|
257
|
+
const secretKey = getArg('--secret-key', 'SECRET_KEY') || config.secretKey;
|
|
258
|
+
const authCert = getArg('--auth-cert', 'MONITOR_AUTH_CERT') || config.authCert;
|
|
259
|
+
const endpoint = (getArg('--endpoint', 'ENDPOINT') || config.endpoint || '').replace(/\/$/, '');
|
|
260
|
+
const dir = getArg('--dir', 'DIR') || config.dir || process.cwd();
|
|
261
|
+
const release = await resolveRelease(getArg('--release', 'RELEASE') || config.release, dir);
|
|
262
|
+
const buildId = getArg('--build-id', 'BUILD_ID') || config.buildId || randomUUID();
|
|
263
|
+
const useArchive = hasFlag('--archive') || config.uploadAsArchive === true;
|
|
264
|
+
const noEncrypt = effectiveNoEncrypt(hasFlag('--no-encrypt'), config.noEncrypt);
|
|
265
|
+
const chunkSizeBytes = parseChunkSize(config);
|
|
266
|
+
const concurrency = Math.max(1, Math.min(64, parseInt(getArg('--concurrency', 'CONCURRENCY') || config.concurrency || '12', 10) || 12));
|
|
267
|
+
let uploadMode = getArg('--upload-mode', 'UPLOAD_MODE') || config.uploadMode || 'mapOnly';
|
|
268
|
+
if (uploadMode === 'all') uploadMode = 'backup';
|
|
269
|
+
const bundlerConfig = await getBundlerSourceMapConfig(dir);
|
|
270
|
+
const deleteAfter = !bundlerConfig.keepMapInOutput || hasFlag('--delete-after-upload') || hasFlag('-d') || config.deleteAfterUpload === true;
|
|
271
|
+
|
|
272
|
+
if (!endpoint) {
|
|
273
|
+
console.error('Usage: ENDPOINT is required.');
|
|
274
|
+
} else if (!authCert && (!projectId || !secretKey)) {
|
|
275
|
+
console.error('Usage: PROJECT_ID and SECRET_KEY are required, or use AUTH_CERT (certificate auth).');
|
|
276
|
+
console.error('');
|
|
277
|
+
console.error(' Arguments:');
|
|
278
|
+
console.error(' --project-id <id> Project ID');
|
|
279
|
+
console.error(' --secret-key <key> Secret Key');
|
|
280
|
+
console.error(' --endpoint <url> API base URL (e.g. https://api.example.com/monitorApi)');
|
|
281
|
+
console.error(' --auth-cert <cert> Certificate content (or MONITOR_AUTH_CERT); use with monitor.config.js, no secretKey needed');
|
|
282
|
+
console.error(' --release <tag> Release version (default: read from package.json)');
|
|
283
|
+
console.error(' --build-id <id> Build ID (default: auto-generated UUID)');
|
|
284
|
+
console.error(' --dir <path> Directory (default: cwd)');
|
|
285
|
+
console.error(' --concurrency <n> Max parallel uploads when not using --archive (default: 12)');
|
|
286
|
+
console.error(' --delete-after-upload, -d Delete local .map after upload (from deployment output)');
|
|
287
|
+
console.error(' --archive Pack as .tar.gz (encrypted by default) → upload → server decrypts and extracts');
|
|
288
|
+
console.error(' --upload-mode <mode> mapOnly (default) | backup (code tar, .map 依项目配置是否保留)');
|
|
289
|
+
console.error(' --chunk-size <size> With --archive: chunked upload (e.g. 1M, 5M), default 1MB, enables resumable and retry');
|
|
290
|
+
console.error(' --no-encrypt With --archive: upload plain tar.gz (certificate auth implies no-encrypt)');
|
|
291
|
+
console.error(' --config <path> Config file (default: monitor.config.js + .upload-sourcemaps.json)');
|
|
292
|
+
console.error('');
|
|
293
|
+
console.error(' Subcommands:');
|
|
294
|
+
console.error(' init Add recommended scripts to package.json (build:upload, build:upload:archive)');
|
|
295
|
+
console.error(' build One-step: run build then upload source maps');
|
|
296
|
+
console.error(' patch / restore Temporarily enable source map in bundler config, then restore');
|
|
297
|
+
console.error('');
|
|
298
|
+
console.error(' Whether to delete .map after upload: inferred from project bundler config (Vite/Webpack etc.). Use -d to force delete.');
|
|
299
|
+
process.exitCode = 1;
|
|
300
|
+
} else {
|
|
301
|
+
if (release !== 'default') console.log('Release:', release);
|
|
302
|
+
console.log('Build ID:', buildId);
|
|
303
|
+
try {
|
|
304
|
+
if (useArchive) {
|
|
305
|
+
const archiveOpts = { dir, endpoint, projectId, secretKey, authCert, release, buildId, deleteEvenOnFailure: false, noEncrypt, uploadMode, keepMapInOutput: bundlerConfig.keepMapInOutput };
|
|
306
|
+
if (chunkSizeBytes) {
|
|
307
|
+
await uploadArchiveChunkedThenDelete({ ...archiveOpts, chunkSizeBytes }, console);
|
|
308
|
+
} else {
|
|
309
|
+
await uploadArchiveThenDelete(archiveOpts, console);
|
|
310
|
+
}
|
|
311
|
+
} else {
|
|
312
|
+
const { ok, fail, deleted } = await uploadAll(
|
|
313
|
+
{ endpoint, projectId, secretKey, authCert, release, buildId, dir, deleteAfterUpload: deleteAfter, deleteEvenOnFailure: deleteAfter, concurrency },
|
|
314
|
+
console
|
|
315
|
+
);
|
|
316
|
+
if (deleted > 0) {
|
|
317
|
+
console.log('Deleted', deleted, 'local .map file(s) from output.');
|
|
318
|
+
}
|
|
319
|
+
if (fail > 0) {
|
|
320
|
+
if (deleteAfter) {
|
|
321
|
+
console.error('Upload had failures. Local .map files were still deleted (per project config). Fix errors and re-run to re-upload.');
|
|
322
|
+
} else {
|
|
323
|
+
console.error('Upload had failures. Local .map files were NOT deleted. Fix errors and re-run.');
|
|
324
|
+
}
|
|
325
|
+
process.exitCode = 1;
|
|
326
|
+
}
|
|
327
|
+
}
|
|
328
|
+
} catch (err) {
|
|
329
|
+
console.error('Error:', err.message);
|
|
330
|
+
if (deleteAfter) {
|
|
331
|
+
console.error('Local .map files were still deleted. Fix the issue and re-run.');
|
|
332
|
+
} else {
|
|
333
|
+
console.error('Local files were not modified. Fix the issue and re-run.');
|
|
334
|
+
}
|
|
335
|
+
process.exitCode = 1;
|
|
336
|
+
}
|
|
337
|
+
}
|
|
338
|
+
}
|
|
@@ -1,2 +1,8 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
var Je=Object.defineProperty;var N=(t,e)=>()=>(t&&(e=t(t=0)),e);var K=(t,e)=>{for(var o in e)Je(t,o,{get:e[o],enumerable:!0})};var z={};K(z,{extractProjectIdFromCert:()=>He,extractUploadKeyFromCert:()=>Ve});function He(t){if(typeof t!="string")return"";let e=t.trim();if(e.startsWith(Xe+"$"))return e.split("$")[1]||"";if(e.includes(le)){let o=e.match(/Project:\s*(\S+)/);return o?o[1]:""}return""}function Ve(t){if(typeof t!="string"||!t.includes(le))return null;let e=t.match(/Upload-Key:\s*([0-9a-fA-F]{64})/);if(!e)return null;try{return Buffer.from(e[1],"hex")}catch{return null}}var Xe,le,J=N(()=>{Xe="MC1",le="-----BEGIN MONITOR CERT-----"});var B={};K(B,{decryptArchive:()=>et,encryptArchive:()=>Qe,encryptArchiveWithKey:()=>fe});import{createCipheriv as Ge,createDecipheriv as qe,createHash as Ye,randomBytes as Ze}from"crypto";function de(t,e){let o=`${t}:${e}`;return Ye("sha256").update(o,"utf8").digest()}function Qe(t,e,o){let r=de(e,o);return fe(t,r)}function fe(t,e){if(!Buffer.isBuffer(e)||e.length!==32)throw new Error("Invalid key: must be 32-byte Buffer");let o=Ze(X),r=Ge(pe,e,o,{authTagLength:_}),a=Buffer.concat([r.update(t),r.final()]),n=r.getAuthTag();return Buffer.concat([o,a,n])}function et(t,e,o){if(t.length<X+_)throw new Error("Invalid encrypted archive: too short");let r=de(e,o),a=t.subarray(0,X),n=t.subarray(t.length-_),s=t.subarray(X,t.length-_),c=qe(pe,r,a,{authTagLength:_});return c.setAuthTag(n),Buffer.concat([c.update(s),c.final()])}var pe,X,_,L=N(()=>{pe="aes-256-gcm",X=12,_=16});var ee={};K(ee,{DEFAULT_EXCLUDE_EXT:()=>V,collectFiles:()=>Q,createArchive:()=>G,deleteMapFiles:()=>he,uploadArchive:()=>pt});import{createRequire as tt}from"module";import{readdir as me,unlink as rt,readFile as ot,mkdtemp as nt,rm as at}from"fs/promises";import{join as H,relative as st}from"path";import{createWriteStream as ct}from"fs";import{tmpdir as it}from"os";async function Q(t,e=t,o=V){let r=await me(t,{withFileTypes:!0}),a=[];for(let n of r){let s=H(t,n.name),c=st(e,s).replace(/\\/g,"/");if(n.isDirectory()){let p=await Q(s,e,o);a.push(...p)}else{let p=(n.name.includes(".")?"."+n.name.split(".").pop():"").toLowerCase();if(o.has(p))continue;a.push({relativePath:c,fullPath:s})}}return a}async function G(t,e,o=V,r=!0){let a=await Q(t,t,o),n=a.map(s=>s.relativePath);return n.length===0?0:new Promise((s,c)=>{let p=lt.create({gzip:r,cwd:t},n);p.on("error",c),p.pipe(ct(e)).on("close",()=>s(a.length)).on("error",c)})}async function pt(t,e=console){let{dir:o,endpoint:r,projectId:a,secretKey:n,authCert:s,release:c,buildId:p,excludeExt:f,noEncrypt:i}=t,d=!!s,{extractProjectIdFromCert:u,extractUploadKeyFromCert:m}=await Promise.resolve().then(()=>(J(),z)),l=a||(d?u(s):""),h=d?m(s):null,y=await nt(H(it(),"upload-sourcemaps-")),g=H(y,"archive.tar.gz");try{let k=await G(o,g,f||V,!0);if(k===0)return e.log("No files to pack (directory empty or only excluded). Skip upload."),{ok:!0,uploaded:0};let S=await ot(g),j=!i&&(d?h!==null:!!(l&&n));if(j)if(h){let{encryptArchiveWithKey:T}=await Promise.resolve().then(()=>(L(),B));S=T(S,h),e.log("Created tar.gz with",k,"file(s), encrypted with cert Upload-Key, uploading...")}else{let{encryptArchive:T}=await Promise.resolve().then(()=>(L(),B));S=T(S,l,n),e.log("Created tar.gz with",k,"file(s), encrypted, uploading...")}else e.log("Created tar.gz with",k,"file(s), uploading...");let P="/sourcemaps/upload-archive",D=r.replace(/\/$/,""),C=D.endsWith(P)?D:D+P,I=new FormData;I.append("projectId",l),d||I.append("secretKey",n||""),I.append("release",c),p&&I.append("buildId",p),j&&I.append("encrypted","1"),I.append("file",new Blob([S]),j?"archive.tar.gz.enc":"archive.tar.gz");let W={"X-Project-Id":l};d?W["X-Auth-Cert"]=s:W["X-Secret-Key"]=n||"";let E=await fetch(C,{method:"POST",headers:W,body:I}),U=await E.text();if(!E.ok)throw new Error(`${E.status} ${U}`);let Z=JSON.parse(U||"{}");return e.log("Upload archive OK, server extracted and saved",Z.uploaded,".map file(s)"),{ok:!0,uploaded:Z.uploaded||0}}finally{await at(y,{recursive:!0,force:!0})}}async function he(t,e=[]){let o=await me(t,{withFileTypes:!0});for(let r of o){let a=H(t,r.name);r.isDirectory()?await he(a,e):r.name.endsWith(".map")&&(await rt(a),e.push(a))}return e}var ut,lt,V,q=N(()=>{ut=tt(import.meta.url),lt=ut("tar"),V=new Set([".png",".jpg",".jpeg",".gif",".svg",".webp",".ico",".bmp",".avif",".woff",".woff2",".ttf",".eot",".otf",".mp4",".webm",".ogg",".mp3",".wav",".m4a",".aac",".pdf",".doc",".docx",".xls",".xlsx"])});var ye={};K(ye,{uploadArchiveChunked:()=>It});import{readFile as dt,mkdtemp as ft,rm as mt}from"fs/promises";import{join as we}from"path";import{tmpdir as ht}from"os";function gt(t){return t.replace(/\/$/,"")}function Y(t,e){let o=gt(t),r=e.startsWith("/")?e:`/${e}`;return o.endsWith(r)?o:o+r}async function vt(t){return new Promise(e=>setTimeout(e,t))}async function te(t,e,o,r=yt){let a;for(let n=0;n<=r;n++)try{let s=await fetch(t,e),c=await s.text();if(!s.ok)throw new Error(`${s.status} ${c}`);return c?JSON.parse(c):{}}catch(s){if(a=s,n<r){let c=bt*Math.pow(2,n);o.log?.(` Retry ${n+1}/${r} after ${c}ms: ${s.message}`),await vt(c)}}throw a}async function It(t,e=console){let{dir:o,endpoint:r,projectId:a,secretKey:n,authCert:s,release:c,buildId:p,noEncrypt:f,chunkSizeBytes:i=wt,excludeExt:d}=t,u=!!s,{extractProjectIdFromCert:m,extractUploadKeyFromCert:l}=await Promise.resolve().then(()=>(J(),z)),h=a||(u?m(s):""),y=u?l(s):null,g=await ft(we(ht(),"upload-sourcemaps-chunked-")),k=we(g,"archive.tar.gz");try{let S=await G(o,k,d||void 0,!0);if(S===0)return e.log?.("No files to pack. Skip upload."),{ok:!0,uploaded:0};let j=await dt(k),P=!f&&(u?y!==null:!!(h&&n));if(P){if(y){let{encryptArchiveWithKey:b}=await Promise.resolve().then(()=>(L(),B));j=b(j,y)}else{let{encryptArchive:b}=await Promise.resolve().then(()=>(L(),B));j=b(j,h,n)}e.log?.(`Created tar.gz with ${S} file(s), encrypted, uploading in chunks...`)}else e.log?.(`Created tar.gz with ${S} file(s), uploading in chunks...`);let D=j.length,C=Math.ceil(D/i),I={"Content-Type":"application/json"};u?I["X-Auth-Cert"]=s:(I["X-Project-Id"]=h,I["X-Secret-Key"]=n||"");let E=(await te(Y(r,"/sourcemaps/upload-archive-multipart/init"),{method:"POST",headers:{...I,"Content-Type":"application/json"},body:JSON.stringify({projectId:h,secretKey:u?"":n||"",release:c,buildId:p||void 0,totalChunks:C,chunkSizeBytes:i,encrypted:P})},e)).uploadId;if(!E)throw new Error("Init did not return uploadId");let U=new Set;for(let b=0;b<C;b++)U.add(b);try{let b=await fetch(Y(r,"/sourcemaps/upload-archive-multipart/status")+`?uploadId=${encodeURIComponent(E)}`,{method:"GET",headers:I}),F=await b.text();if(b.ok&&F){let R=JSON.parse(F).receivedChunks||[];R.forEach(A=>U.delete(A)),R.length>0&&e.log?.(`Resume: ${R.length}/${C} chunks already uploaded`)}}catch{}for(let b=0;b<C;b++){if(!U.has(b))continue;let F=b*i,ue=Math.min(F+i,D),R=j.subarray(F,ue),A=new FormData;A.append("uploadId",E),A.append("index",String(b)),A.append("totalChunks",String(C)),A.append("chunk",new Blob([R]),`chunk-${b}`);let ze=Y(r,"/sourcemaps/upload-archive-multipart/chunk");await te(ze,{method:"POST",headers:u?{"X-Auth-Cert":s}:{"X-Project-Id":h,"X-Secret-Key":n||""},body:A},e),e.log?.(` Chunk ${b+1}/${C} OK`)}let T=(await te(Y(r,"/sourcemaps/upload-archive-multipart/complete"),{method:"POST",headers:{...I,"Content-Type":"application/json"},body:JSON.stringify({uploadId:E})},e)).uploaded??0;return e.log?.("Chunked upload OK, server extracted and saved",T,".map file(s)"),{ok:!0,uploaded:T}}finally{await mt(g,{recursive:!0,force:!0})}}var wt,yt,bt,be=N(()=>{q();wt=1*1024*1024,yt=3,bt=1e3});var Ke={};K(Ke,{loadMonitorConfig:()=>Re,loadUploadSourcemapsJson:()=>Ne,resolveUploadConfig:()=>Ut});import{readFile as Ue}from"fs/promises";import{join as Te,resolve as Dt}from"path";import{pathToFileURL as Pe}from"url";function Fe(){try{return process.env.UPLOAD_ENV||process.env.NODE_ENV||"production"}catch{return"production"}}async function Re(t){let e=["monitor.config.js","monitor.config.cjs","monitor.config.mjs"];for(let o of e){let r=Te(t,o),a;try{a=await import(Pe(r).href)}catch{continue}let n=a.default;if(!n||typeof n!="object")return null;let s=Fe(),c=n.common||{},p=n[s]||{},f={...c,...p},i=(f.endpoint||"").trim(),d=(f.apiPrefix||"").trim(),u=i+(d.startsWith("/")?d:d?"/"+d:"");return{projectId:f.projectId,secretKey:f.secretKey,authCert:f.authCert,endpoint:u||void 0,release:f.release||n.version,version:n.version}}return null}async function Ne(t){try{let e=await Ue(Te(t,".upload-sourcemaps.json"),"utf-8");return JSON.parse(e)}catch{return{}}}async function Ut(t,e){let o=null,r=null;if(e){let a=Dt(t,e);if(e.endsWith(".json"))try{r=JSON.parse(await Ue(a,"utf-8"))}catch{r={}}else try{let s=(await import(Pe(a).href)).default;if(s&&typeof s=="object"&&(s.common||s.production||s.development)){let c=Fe(),p=s.common||{},f=s[c]||{},i={...p,...f},d=(i.endpoint||"").trim(),u=(i.apiPrefix||"").trim();o={projectId:i.projectId,secretKey:i.secretKey,authCert:i.authCert,endpoint:d+(u.startsWith("/")?u:u?"/"+u:""),release:i.release||s.version}}else r=s||{}}catch{r={}}}else o=await Re(t),r=await Ne(t);return{projectId:r.projectId??o?.projectId,secretKey:r.secretKey??o?.secretKey,authCert:r.authCert??o?.authCert,endpoint:r.endpoint??o?.endpoint,release:r.release??o?.release??o?.version,dir:r.dir,chunkSize:r.chunkSize,uploadAsArchive:r.uploadAsArchive,noEncrypt:r.noEncrypt,buildCommand:r.buildCommand,buildOutputDir:r.buildOutputDir}}var _e=N(()=>{});import{readFile as Tt}from"fs/promises";import{resolve as Pt,dirname as Ft,join as Rt}from"path";import{fileURLToPath as Nt}from"url";import{randomUUID as Be}from"crypto";import{spawn as Kt}from"child_process";import{readdir as kt,readFile as St,unlink as jt}from"fs/promises";import{join as xt,basename as $}from"path";async function ve(t,e=[]){let o;try{o=await kt(t,{withFileTypes:!0})}catch(r){if(r?.code==="ENOENT")return e;throw r}for(let r of o){let a=xt(t,r.name);r.isDirectory()?await ve(a,e):r.name.endsWith(".map")&&e.push(a)}return e}var ge="/sourcemaps/upload";function Ct(t){let e=t.replace(/\/$/,"");return e.endsWith(ge)?e:e+ge}async function Et(t){let{endpoint:e,projectId:o,secretKey:r,authCert:a,release:n,buildId:s,filePath:c}=t,p=!!a,f=o||(p?(await Promise.resolve().then(()=>(J(),z))).extractProjectIdFromCert(a):""),i=$(c,".map"),d=await St(c),u=Ct(e),m=new FormData;m.append("projectId",f),p||m.append("secretKey",r||""),m.append("release",n),s&&m.append("buildId",s),m.append("filename",i),m.append("file",new Blob([d]),$(c));let h=await fetch(u,{method:"POST",headers:p?{"X-Project-Id":f,"X-Auth-Cert":a}:{"X-Project-Id":f,"X-Secret-Key":r||""},body:m}),y=await h.text();if(!h.ok)throw new Error(`${h.status} ${y}`);return JSON.parse(y||"{}")}async function re(t,e=console){let{dir:o,endpoint:r,projectId:a,secretKey:n,authCert:s,release:c,buildId:p,deleteAfterUpload:f=!1,deleteEvenOnFailure:i=!1}=t,d=await ve(o);if(d.length===0)return e.log("No .map files found in",o),{ok:0,fail:0,deleted:0};let u=f&&i;e.log("Uploading",d.length,"source map(s) to",r,"release=",c,f?u?"(will delete local .map even on failure)":"(will delete local .map only after all succeed)":"");let m=0,l=0;for(let g of d)try{await Et({endpoint:r,projectId:a,secretKey:n,authCert:s,release:c,buildId:p,filePath:g}),e.log(" OK",$(g)),m++}catch(k){e.error(" FAIL",$(g),k.message),l++}let h=f&&(l===0||i),y=0;if(h&&d.length>0){for(let g of d)try{await jt(g),y++,e.log(" Deleted",$(g))}catch(k){e.error(" Delete failed",g,k.message)}l>0&&i&&e.log("Deleted .map files despite upload failures. Fix errors and re-run to re-upload.")}else l>0&&!i&&e.log("Upload had failures. Local .map files were NOT deleted. Fix errors and re-run.");return{ok:m,fail:l,deleted:y}}async function oe(t,e=console){let{uploadArchive:o,deleteMapFiles:r}=await Promise.resolve().then(()=>(q(),ee)),{dir:a,endpoint:n,projectId:s,secretKey:c,authCert:p,release:f,buildId:i,deleteEvenOnFailure:d=!1,noEncrypt:u}=t;try{let{uploaded:m}=await o({dir:a,endpoint:n,projectId:s,secretKey:c,authCert:p,release:f,buildId:i,noEncrypt:u},e);if(m===0)return{uploaded:0,deleted:0};let l=await r(a,[]);return e.log("Deleted",l.length,"local .map file(s)."),{uploaded:m,deleted:l.length}}catch(m){if(d)try{let l=await r(a,[]);e.log("Deleted",l.length,"local .map file(s) despite upload failure.")}catch(l){e.error("Delete .map after upload failure failed:",l.message)}else e.error("Upload failed. Local .map files were NOT deleted.",m.message);throw m}}async function ne(t,e=console){let{uploadArchiveChunked:o}=await Promise.resolve().then(()=>(be(),ye)),{deleteMapFiles:r}=await Promise.resolve().then(()=>(q(),ee)),{dir:a,endpoint:n,projectId:s,secretKey:c,authCert:p,release:f,buildId:i,deleteEvenOnFailure:d=!1,noEncrypt:u,chunkSizeBytes:m}=t;try{let{uploaded:l}=await o({dir:a,endpoint:n,projectId:s,secretKey:c,authCert:p,release:f,buildId:i,noEncrypt:u,chunkSizeBytes:m},e);if(l===0)return{uploaded:0,deleted:0};let h=await r(a,[]);return e.log("Deleted",h.length,"local .map file(s)."),{uploaded:l,deleted:h.length}}catch(l){if(d)try{let h=await r(a,[]);e.log("Deleted",h.length,"local .map file(s) despite upload failure.")}catch(h){e.error("Delete .map after upload failure failed:",h.message)}else e.error("Upload failed. Local .map files were NOT deleted.",l.message);throw l}}import{readFile as ae}from"fs/promises";import{join as x,resolve as Ie}from"path";async function se(t){let e=[t,Ie(t,".."),Ie(t,"..",".."),process.cwd()];for(let o of e)try{return await ae(x(o,"package.json"),"utf-8"),o}catch{continue}return null}async function O(t){try{return await ae(t,"utf-8")}catch{return""}}async function ke(t){let e=["vite.config.js","vite.config.mjs","vite.config.cjs","vite.config.ts"];for(let o of e){let r=await O(x(t,o));if(r){if(/\bsourcemap\s*:\s*false\b/.test(r)||/build\s*:\s*\{[^}]*sourcemap\s*:\s*false/.test(r))return{emitsSourceMap:!1,bundler:"vite",keepMapInOutput:!1};if(/\bsourcemap\s*:\s*['"]inline['"]/.test(r)||/build\s*:\s*\{[\s\S]*?sourcemap\s*:\s*['"]inline['"]/.test(r))return{emitsSourceMap:!0,bundler:"vite",keepMapInOutput:!1};if(/\bsourcemap\s*:\s*true\b/.test(r)||/\bsourcemap\s*:\s*['"]hidden['"]/.test(r))return{emitsSourceMap:!0,bundler:"vite",keepMapInOutput:!0};if(/build\s*:\s*\{[\s\S]*?sourcemap\s*:\s*true/.test(r))return{emitsSourceMap:!0,bundler:"vite",keepMapInOutput:!0};if(/build\s*:\s*\{[\s\S]*?sourcemap\s*:\s*['"]hidden['"]/.test(r))return{emitsSourceMap:!0,bundler:"vite",keepMapInOutput:!0};if(/BUILD_SOURCEMAP|SOURCEMAP/.test(r)&&/build\s*:\s*\{[\s\S]*?sourcemap\s*:/.test(r))return{emitsSourceMap:!0,bundler:"vite",keepMapInOutput:!0};if(/build\s*:|\bdefineConfig\s*\(/.test(r))return{emitsSourceMap:!1,bundler:"vite",keepMapInOutput:!1}}}return null}async function Se(t){let e=["webpack.config.js","webpack.config.cjs","webpack.config.mjs","webpack.config.ts","webpack.common.js","webpack.prod.js"];for(let o of e){let r=await O(x(t,o));if(r){if(/\bdevtool\s*:\s*false\b/.test(r))return{emitsSourceMap:!1,bundler:"webpack",keepMapInOutput:!1};if(/\bdevtool\s*:\s*['"]source-map['"]/.test(r)||/\bdevtool\s*:\s*['"]hidden-source-map['"]/.test(r))return{emitsSourceMap:!0,bundler:"webpack",keepMapInOutput:!0};if(/\bdevtool\s*:\s*true\b/.test(r))return{emitsSourceMap:!0,bundler:"webpack",keepMapInOutput:!0}}}return null}async function je(t){let e=await O(x(t,"vue.config.js"))||await O(x(t,"vue.config.cjs"));return e?/\bproductionSourceMap\s*:\s*false\b/.test(e)?{emitsSourceMap:!1,bundler:"vue-cli",keepMapInOutput:!1}:/\bproductionSourceMap\s*:\s*true\b/.test(e)?{emitsSourceMap:!0,bundler:"vue-cli",keepMapInOutput:!0}:/\.devtool\s*\(\s*['"]source-map['"]\s*\)/.test(e)?{emitsSourceMap:!0,bundler:"vue-cli",keepMapInOutput:!0}:null:null}async function xe(t){let e=["rollup.config.js","rollup.config.mjs","rollup.config.cjs"];for(let o of e){let r=await O(x(t,o));if(r){if(/\bsourcemap\s*:\s*false\b/.test(r))return{emitsSourceMap:!1,bundler:"rollup",keepMapInOutput:!1};if(/\bsourcemap\s*:\s*['"]inline['"]/.test(r))return{emitsSourceMap:!0,bundler:"rollup",keepMapInOutput:!1};if(/\bsourcemap\s*:\s*true\b/.test(r))return{emitsSourceMap:!0,bundler:"rollup",keepMapInOutput:!0}}}return null}async function Ce(t){let e=["next.config.js","next.config.mjs","next.config.cjs","next.config.ts"];for(let o of e){let r=await O(x(t,o));if(r){if(/\bproductionBrowserSourceMaps\s*:\s*true\b/.test(r))return{emitsSourceMap:!0,bundler:"next",keepMapInOutput:!0};if(/\bproductionBrowserSourceMaps\s*:\s*false\b/.test(r))return{emitsSourceMap:!1,bundler:"next",keepMapInOutput:!1};if(/module\.exports|next\s*\(/.test(r))return{emitsSourceMap:!1,bundler:"next",keepMapInOutput:!1}}}return null}async function Ee(t){let e=await O(x(t,"nuxt.config.js"))||await O(x(t,"nuxt.config.ts"));return e?/\bsourcemap\s*:\s*false\b/.test(e)?{emitsSourceMap:!1,bundler:"nuxt",keepMapInOutput:!1}:/\bsourcemap\s*:\s*true\b/.test(e)?{emitsSourceMap:!0,bundler:"nuxt",keepMapInOutput:!0}:null:null}function Ot(t){let e={...t.dependencies,...t.devDependencies};return!e||typeof e!="object"?null:e.vite||e.vite?"vite":e.next?"next":e.nuxt||e.nuxt3?"nuxt":e.vue&&(e["@vue/cli-service"]||e["vue-cli-service"])?"vue-cli":e.webpack||e["react-scripts"]||e["@angular/cli"]?"webpack":e.rollup?"rollup":e.parcel?"parcel":null}async function ce(t){let e=await se(t);if(!e)return{emitsSourceMap:!0,keepMapInOutput:!1};let o={};try{let n=await ae(x(e,"package.json"),"utf-8");o=JSON.parse(n)}catch{return{emitsSourceMap:!0,keepMapInOutput:!1}}let r=Ot(o),a=[()=>r==="vite"?ke(e):null,()=>r==="next"?Ce(e):null,()=>r==="nuxt"?Ee(e):null,()=>r==="vue-cli"?je(e):null,()=>r==="webpack"?Se(e):null,()=>r==="rollup"?xe(e):null,()=>ke(e),()=>Ce(e),()=>je(e),()=>Se(e),()=>xe(e),()=>Ee(e)];for(let n of a){let s=await n();if(s)return s}return{emitsSourceMap:!0,keepMapInOutput:!1}}import{readFile as Ae,writeFile as Oe}from"fs/promises";import{join as Me}from"path";async function Mt(t){try{return await Ae(t,"utf-8")}catch{return""}}function At(t){let e={...t?.dependencies||{},...t?.devDependencies||{}};return e.vite||e.vite?"vite":e.next?"next":e.nuxt||e.nuxt3?"nuxt":e.vue&&(e["@vue/cli-service"]||e["vue-cli-service"])?"vue-cli":e.webpack||e["react-scripts"]||e["@angular/cli"]?"webpack":e.rollup?"rollup":null}var ie=[{name:"vite",configNames:["vite.config.js","vite.config.mjs","vite.config.cjs","vite.config.ts"],needPatch:t=>/\bsourcemap\s*:\s*false\b/.test(t),patch:t=>t.replace(/(sourcemap\s*:\s*)false\b/,"$1true")},{name:"webpack",configNames:["webpack.config.js","webpack.config.cjs","webpack.config.mjs","webpack.config.ts","webpack.common.js","webpack.prod.js"],needPatch:t=>/\bdevtool\s*:\s*false\b/.test(t),patch:t=>t.replace(/(devtool\s*:\s*)false\b/,(e,o)=>`${o}'source-map'`)},{name:"vue-cli",configNames:["vue.config.js","vue.config.cjs"],needPatch:t=>/\bproductionSourceMap\s*:\s*false\b/.test(t),patch:t=>t.replace(/(productionSourceMap\s*:\s*)false\b/,"$1true")},{name:"rollup",configNames:["rollup.config.js","rollup.config.mjs","rollup.config.cjs"],needPatch:t=>/\bsourcemap\s*:\s*false\b/.test(t),patch:t=>t.replace(/(sourcemap\s*:\s*)false\b/,"$1true")},{name:"next",configNames:["next.config.js","next.config.mjs","next.config.cjs","next.config.ts"],needPatch:t=>/\bproductionBrowserSourceMaps\s*:\s*false\b/.test(t),patch:t=>t.replace(/(productionBrowserSourceMaps\s*:\s*)false\b/,"$1true")},{name:"nuxt",configNames:["nuxt.config.js","nuxt.config.ts"],needPatch:t=>/\bsourcemap\s*:\s*false\b/.test(t),patch:t=>t.replace(/(sourcemap\s*:\s*)false\b/,"$1true")}];async function De(t){let e=await se(t);if(!e)return{restore:async()=>{}};let o={};try{let n=await Ae(Me(e,"package.json"),"utf-8");o=JSON.parse(n)}catch{return{restore:async()=>{}}}let r=At(o),a=r?[...ie.filter(n=>n.name===r),...ie.filter(n=>n.name!==r)]:ie;for(let n of a)for(let s of n.configNames){let c=Me(e,s),p=await Mt(c);if(!p||!n.needPatch(p))continue;let f=n.patch(p);return await Oe(c,f,"utf-8"),{restore:async()=>{try{await Oe(c,p,"utf-8")}catch(i){console.error(`Restore ${n.name} config failed:`,i?.message||i)}}}}return{restore:async()=>{}}}var Sr=Ft(Nt(import.meta.url));function v(t,e){let o=process.env[e];if(o)return o;let r=process.argv.indexOf(t);return r!==-1&&process.argv[r+1]?process.argv[r+1]:null}function M(t){return process.argv.includes(t)}function _t(t){if(t==null||t==="")return null;let e=String(t).trim().toUpperCase(),o=parseInt(e.replace(/[^0-9]/g,""),10)||0;return e.endsWith("G")?o*1024*1024*1024:e.endsWith("MB")||e.endsWith("M")?o*1024*1024:e.endsWith("KB")||e.endsWith("K")?o*1024:/^\d+$/.test(String(t).trim())?parseInt(String(t).trim(),10)||null:o>0?o*1024*1024:null}function Le(t={}){let e=v("--chunk-size","UPLOAD_SOURCEMAPS_CHUNK_SIZE")||t.chunkSize;return _t(e)}async function Bt(){let{resolveUploadConfig:t}=await Promise.resolve().then(()=>(_e(),Ke));return t(process.cwd(),v("--config","UPLOAD_SOURCEMAPS_CONFIG")||void 0)}async function Lt(t){let e=[t,Pt(t,".."),process.cwd()];for(let o of e)try{let r=await Tt(Rt(o,"package.json"),"utf-8"),a=JSON.parse(r);if(a.version&&typeof a.version=="string")return a.version.trim()}catch{continue}return null}async function $e(t,e){return t&&t!=="default"?t:await Lt(e)||"default"}var w=await Bt(),$t=process.argv[2]==="build";function We(t,e){return t||e===!0}async function Wt(){let t=v("--project-id","PROJECT_ID")||w.projectId,e=v("--secret-key","SECRET_KEY")||w.secretKey,o=v("--auth-cert","MONITOR_AUTH_CERT")||w.authCert,r=(v("--endpoint","ENDPOINT")||w.endpoint||"").replace(/\/$/,"");if(!r)throw new Error("build subcommand requires endpoint (config file or env/args).");if(!o&&(!t||!e))throw new Error("build subcommand requires projectId+secretKey or authCert (config file or env/args).");let a=w.buildCommand||process.env.UPLOAD_SOURCEMAPS_BUILD_CMD||"npm run build",n=w.buildOutputDir||w.dir||v("--dir","DIR")||process.cwd(),s=M("--archive")||w.uploadAsArchive===!0,c=We(M("--no-encrypt"),w.noEncrypt),f=!(await ce(n)).keepMapInOutput||M("--delete-after-upload")||M("-d")||w.deleteAfterUpload===!0,i=v("--build-id","BUILD_ID")||w.buildId||Be(),d=await De(n);try{console.log("Step 1: Running build (forcing source map generation):",a),console.log("Build ID:",i);let[u,...m]=a.split(/\s+/);await new Promise((y,g)=>{Kt(u,m,{stdio:"inherit",shell:!0,env:{...process.env,BUILD_SOURCEMAP:"1",SOURCEMAP:"1",MONITOR_BUILD_ID:i,VITE_MONITOR_BUILD_ID:i}}).on("exit",S=>S===0?y():g(new Error(`Build exited ${S}`)))}),console.log("Step 2: Uploading source maps...");let l=await $e(v("--release","RELEASE")||w.release,n);l!=="default"&&console.log("Release (from package.json or config):",l);let h=Le(w);if(s){let y={dir:n,endpoint:r,projectId:t,secretKey:e,authCert:o,release:l,buildId:i,deleteEvenOnFailure:!1,noEncrypt:c};(h?await ne({...y,chunkSizeBytes:h},console):await oe(y,console)).uploaded===0&&console.log("No .map files in output. For Vite we patch config to force source map; check build output dir and README.")}else{let{ok:y,fail:g}=await re({endpoint:r,projectId:t,secretKey:e,authCert:o,release:l,buildId:i,dir:n,deleteAfterUpload:f,deleteEvenOnFailure:!1},console);if(g>0)throw new Error("Upload had failures. Local .map files were NOT deleted. Fix errors and re-run.");y===0&&console.log("No .map files in output. For Vite we patch config to force source map; check build output dir and README.")}console.log("Done. Output dir:",n)}catch(u){throw console.error("Error:",u.message),console.error(f?"If the failure was during upload, local .map files may have been removed. Fix the issue and re-run.":"Local files were not modified. Fix the issue and re-run."),u}finally{await d.restore()}}if($t)try{await Wt()}catch{process.exitCode=1}else{let t=v("--project-id","PROJECT_ID")||w.projectId,e=v("--secret-key","SECRET_KEY")||w.secretKey,o=v("--auth-cert","MONITOR_AUTH_CERT")||w.authCert,r=(v("--endpoint","ENDPOINT")||w.endpoint||"").replace(/\/$/,""),a=v("--dir","DIR")||w.dir||process.cwd(),n=await $e(v("--release","RELEASE")||w.release,a),s=v("--build-id","BUILD_ID")||w.buildId||Be(),c=M("--archive")||w.uploadAsArchive===!0,p=We(M("--no-encrypt"),w.noEncrypt),f=Le(w),d=!(await ce(a)).keepMapInOutput||M("--delete-after-upload")||M("-d")||w.deleteAfterUpload===!0;if(!r)console.error("Usage: ENDPOINT is required.");else if(!o&&(!t||!e))console.error("Usage: PROJECT_ID and SECRET_KEY are required, or use AUTH_CERT (certificate auth)."),console.error(""),console.error(" Arguments:"),console.error(" --project-id <id> Project ID"),console.error(" --secret-key <key> Secret Key"),console.error(" --endpoint <url> API base URL (e.g. https://api.example.com/monitorApi)"),console.error(" --auth-cert <cert> Certificate content (or MONITOR_AUTH_CERT); use with monitor.config.js, no secretKey needed"),console.error(" --release <tag> Release version (default: read from package.json)"),console.error(" --build-id <id> Build ID (default: auto-generated UUID)"),console.error(" --dir <path> Directory (default: cwd)"),console.error(" --delete-after-upload, -d Delete local .map after upload (from deployment output)"),console.error(" --archive Pack as .tar.gz (encrypted by default) \u2192 upload \u2192 server decrypts and extracts"),console.error(" --chunk-size <size> With --archive: chunked upload (e.g. 1M, 5M), default 1MB, enables resumable and retry"),console.error(" --no-encrypt With --archive: upload plain tar.gz (certificate auth implies no-encrypt)"),console.error(" --config <path> Config file (default: monitor.config.js + .upload-sourcemaps.json)"),console.error(""),console.error(" Whether to delete .map after upload: inferred from project bundler config (Vite/Webpack etc.). Use -d to force delete."),process.exitCode=1;else{n!=="default"&&console.log("Release:",n),console.log("Build ID:",s);try{if(c){let u={dir:a,endpoint:r,projectId:t,secretKey:e,authCert:o,release:n,buildId:s,deleteEvenOnFailure:!1,noEncrypt:p};f?await ne({...u,chunkSizeBytes:f},console):await oe(u,console)}else{let{ok:u,fail:m,deleted:l}=await re({endpoint:r,projectId:t,secretKey:e,authCert:o,release:n,buildId:s,dir:a,deleteAfterUpload:d,deleteEvenOnFailure:d},console);l>0&&console.log("Deleted",l,"local .map file(s) from output."),m>0&&(console.error(d?"Upload had failures. Local .map files were still deleted (per project config). Fix errors and re-run to re-upload.":"Upload had failures. Local .map files were NOT deleted. Fix errors and re-run."),process.exitCode=1)}}catch(u){console.error("Error:",u.message),console.error(d?"Local .map files were still deleted. Fix the issue and re-run.":"Local files were not modified. Fix the issue and re-run."),process.exitCode=1}}}
|
|
2
|
+
var vt=Object.defineProperty;var te=(t,e)=>()=>(t&&(e=t(t=0)),e);var re=(t,e)=>{for(var o in e)vt(t,o,{get:e[o],enumerable:!0})};var de={};re(de,{extractProjectIdFromCert:()=>Mt,extractUploadKeyFromCert:()=>xt});function Mt(t){if(typeof t!="string")return"";let e=t.trim();if(e.startsWith(kt+"$"))return e.split("$")[1]||"";if(e.includes(Ue)){let o=e.match(/Project:\s*(\S+)/);return o?o[1]:""}return""}function xt(t){if(typeof t!="string"||!t.includes(Ue))return null;let e=t.match(/Upload-Key:\s*([0-9a-fA-F]{64})/);if(!e)return null;try{return Buffer.from(e[1],"hex")}catch{return null}}var kt,Ue,fe=te(()=>{kt="MC1",Ue="-----BEGIN MONITOR CERT-----"});var ne={};re(ne,{decryptArchive:()=>jt,encryptArchive:()=>Et,encryptArchiveWithKey:()=>Re});import{createCipheriv as Ct,createDecipheriv as St,createHash as It,randomBytes as Ot}from"crypto";function Fe(t,e){let o=`${t}:${e}`;return It("sha256").update(o,"utf8").digest()}function Et(t,e,o){let r=Fe(e,o);return Re(t,r)}function Re(t,e){if(!Buffer.isBuffer(e)||e.length!==32)throw new Error("Invalid key: must be 32-byte Buffer");let o=Ot(me),r=Ct(Te,e,o,{authTagLength:oe}),s=Buffer.concat([r.update(t),r.final()]),n=r.getAuthTag();return Buffer.concat([o,s,n])}function jt(t,e,o){if(t.length<me+oe)throw new Error("Invalid encrypted archive: too short");let r=Fe(e,o),s=t.subarray(0,me),n=t.subarray(t.length-oe),a=t.subarray(me,t.length-oe),c=St(Te,r,s,{authTagLength:oe});return c.setAuthTag(n),Buffer.concat([c.update(a),c.final()])}var Te,me,oe,se=te(()=>{Te="aes-256-gcm",me=12,oe=16});var ke={};re(ke,{DEFAULT_EXCLUDE_EXT:()=>_,UPLOAD_MODE_BACKUP:()=>he,UPLOAD_MODE_MAP_ONLY:()=>Rt,collectFiles:()=>be,createArchive:()=>H,createBackupArchive:()=>we,deleteMapFiles:()=>Xe,uploadArchive:()=>Nt});import{createRequire as At}from"module";import{readdir as _e,unlink as Be,readFile as Pt,mkdtemp as Le,rm as Ke,mkdir as Dt,copyFile as $e,writeFile as Ut}from"fs/promises";import{join as N,relative as Tt,dirname as Ne}from"path";import{createWriteStream as ze}from"fs";import{tmpdir as We}from"os";async function be(t,e=t,o={}){let{excludeExt:r=_,mapOnly:s=!1,excludeMap:n=!1}=typeof o=="object"&&o!==null?o:{},a=await _e(t,{withFileTypes:!0}),c=[];for(let u of a){let i=N(t,u.name),p=Tt(e,i).replace(/\\/g,"/");if(u.isDirectory()){let d=await be(i,e,o);c.push(...d)}else{if(s&&!u.name.endsWith(".map")||!s&&n&&u.name.endsWith(".map"))continue;if(!s&&r&&r.size>0){let d=(u.name.includes(".")?"."+u.name.split(".").pop():"").toLowerCase();if(r.has(d))continue}c.push({relativePath:p,fullPath:i})}}return c}async function H(t,e,o={},r=!0){let s={};o instanceof Set?(s={excludeExt:o,mapOnly:!1},r=arguments[3]!==void 0?arguments[3]:!0):typeof o=="object"&&o!==null&&(s=o);let n=await be(t,t,s),a=n.map(c=>c.relativePath);return a.length===0?0:new Promise((c,u)=>{let i=Je.create({gzip:r,cwd:t},a);i.on("error",u),i.pipe(ze(e)).on("close",()=>c(n.length)).on("error",u)})}async function we(t,e,o={}){let{excludeExt:r=_,includeMapInOutput:s=!0}=typeof o=="object"&&o!==null?o:{},n=await Le(N(We(),"upload-sourcemaps-backup-")),a=Ne(e),c=N(a,"project-code.tar.gz"),u=N(n,"project-code.tar.gz");try{let i=await H(t,u,{excludeExt:r||_,mapOnly:!1,excludeMap:!0},!0);if(i===0){let f=N(n,".keep");await Ut(f,""),await H(n,u,{excludeExt:new Set,mapOnly:!1},!0),await Be(f).catch(()=>{})}else await $e(u,c);let p=[];if(s){p=await be(t,t,{mapOnly:!0});for(let f of p){let m=N(n,f.relativePath);await Dt(Ne(m),{recursive:!0}),await $e(f.fullPath,m)}}let d=["project-code.tar.gz",...p.map(f=>f.relativePath)];return await $t(n,e,d,!0),{codeFileCount:i,mapFileCount:p.length,codeTarPath:i>0?c:void 0}}finally{await Ke(n,{recursive:!0,force:!0})}}function $t(t,e,o,r=!0){return o.length===0?Promise.resolve(0):new Promise((s,n)=>{let a=Je.create({gzip:r,cwd:t},o);a.on("error",n),a.pipe(ze(e)).on("close",()=>s(o.length)).on("error",n)})}async function Nt(t,e=console){let{dir:o,endpoint:r,projectId:s,secretKey:n,authCert:a,release:c,buildId:u,excludeExt:i,noEncrypt:p,uploadMode:d,keepMapInOutput:f}=t,m=!!a,{extractProjectIdFromCert:k,extractUploadKeyFromCert:v}=await Promise.resolve().then(()=>(fe(),de)),b=s||(m?k(a):""),g=m?v(a):null,A=await Le(N(We(),"upload-sourcemaps-")),j=N(A,"archive.tar.gz"),I=d===he,B=I?null:{excludeExt:i||_,mapOnly:!0};try{let R;if(I){let{codeFileCount:C,mapFileCount:E}=await we(o,j,{excludeExt:i||_,includeMapInOutput:f!==!1});R=Math.max(1,E),f!==!1?e.log("Created backup archive: project-code.tar.gz (",C," files for rollback) + ",E," .map file(s), uploading..."):e.log("Created backup archive: project-code.tar.gz (",C," files for rollback, no .map per project config), uploading...")}else R=await H(o,j,B,!0);if(R===0)return e.log("No files to pack (directory empty or only excluded). Skip upload."),{ok:!0,uploaded:0};let O=await Pt(j),P=!p&&(m?g!==null:!!(b&&n));if(P)if(g){let{encryptArchiveWithKey:C}=await Promise.resolve().then(()=>(se(),ne));O=C(O,g),I||e.log("Created tar.gz with",R,"file(s), encrypted with cert Upload-Key, uploading...")}else{let{encryptArchive:C}=await Promise.resolve().then(()=>(se(),ne));O=C(O,b,n),I||e.log("Created tar.gz with",R,"file(s), encrypted, uploading...")}else I||e.log("Created tar.gz with",R,"file(s), uploading...");let U="/sourcemaps/upload-archive",D=r.replace(/\/$/,""),T=D.endsWith(U)?D:D+U,h="----UploadSourceMaps"+Date.now().toString(36)+Math.random().toString(36).slice(2),l=`\r
|
|
3
|
+
`,F=Buffer.isBuffer(O)?O:Buffer.from(O),W=P?"archive.tar.gz.enc":"archive.tar.gz",J=Buffer.from([`--${h}${l}`,`Content-Disposition: form-data; name="projectId"${l}${l}`,b+l,`--${h}${l}`,`Content-Disposition: form-data; name="secretKey"${l}${l}`,(m?"":n||"")+l,`--${h}${l}`,`Content-Disposition: form-data; name="release"${l}${l}`,c+l,`--${h}${l}`,`Content-Disposition: form-data; name="buildId"${l}${l}`,(u||"")+l,`--${h}${l}`,`Content-Disposition: form-data; name="encrypted"${l}${l}`,(P?"1":"")+l,`--${h}${l}`,`Content-Disposition: form-data; name="uploadMode"${l}${l}`,(d||"")+l,`--${h}${l}`,`Content-Disposition: form-data; name="file"; filename="${W}"${l}`,`Content-Type: application/octet-stream${l}${l}`].join(""),"utf-8"),Y=Buffer.from(l+`--${h}--${l}`,"utf-8"),q=J.length+F.length+Y.length,Z=(q/1024/1024).toFixed(2),L=64*1024,ue=3e3,Q=0,ee=0,le=C=>{if(typeof process<"u"&&process.stderr&&process.stderr.write){let E=(Q/1024/1024).toFixed(2);process.stderr.write(`\r \u4E0A\u4F20\u8FDB\u5EA6 \u5F52\u6863 ${E}/${Z} MB (${C}%) `)}},pe=()=>{let C=q?Math.min(100,Math.round(Q/q*100)):0,E=Date.now();(C>=100||E-ee>=ue)&&(ee=E,le(C))},De=new ReadableStream({start(C){let E=M=>{C.enqueue(M),Q+=M.length,pe()};for(let M=0;M<J.length;M+=L)E(J.subarray(M,Math.min(M+L,J.length)));for(let M=0;M<F.length;M+=L)E(F.subarray(M,Math.min(M+L,F.length)));for(let M=0;M<Y.length;M+=L)E(Y.subarray(M,Math.min(M+L,Y.length)));C.close()}}),X={"X-Project-Id":b,"Content-Type":`multipart/form-data; boundary=${h}`,"Content-Length":String(q)};m?X["X-Auth-Cert"]=a:X["X-Secret-Key"]=n||"";let w=await fetch(T,{method:"POST",headers:X,body:De,duplex:"half"}),S=await w.text();if(!w.ok)throw new Error(`${w.status} ${S}`);typeof process<"u"&&process.stderr&&process.stderr.write&&(le(100),process.stderr.write(`
|
|
4
|
+
`));let V=JSON.parse(S||"{}");return e.log("Upload archive OK, server extracted and saved",V.uploaded,".map file(s)"),{ok:!0,uploaded:V.uploaded||0}}finally{await Ke(A,{recursive:!0,force:!0})}}async function Xe(t,e=[]){let o=await _e(t,{withFileTypes:!0});for(let r of o){let s=N(t,r.name);r.isDirectory()?await Xe(s,e):r.name.endsWith(".map")&&(await Be(s),e.push(s))}return e}var Ft,Je,_,Rt,he,ye=te(()=>{Ft=At(import.meta.url),Je=Ft("tar"),_=new Set([".png",".jpg",".jpeg",".gif",".svg",".webp",".ico",".bmp",".avif",".woff",".woff2",".ttf",".eot",".otf",".mp4",".webm",".ogg",".mp3",".wav",".m4a",".aac",".pdf",".doc",".docx",".xls",".xlsx"]),Rt="mapOnly",he="backup"});var He={};re(He,{uploadArchiveChunked:()=>Ht});import{readFile as _t,mkdtemp as Bt,rm as Lt}from"fs/promises";import{join as Ve}from"path";import{tmpdir as Kt}from"os";function Xt(t){return t.replace(/\/$/,"")}function ge(t,e){let o=Xt(t),r=e.startsWith("/")?e:`/${e}`;return o.endsWith(r)?o:o+r}async function Vt(t){return new Promise(e=>setTimeout(e,t))}async function Me(t,e,o,r=Wt){let s;for(let n=0;n<=r;n++)try{let a=await fetch(t,e),c=await a.text();if(!a.ok)throw new Error(`${a.status} ${c}`);return c?JSON.parse(c):{}}catch(a){if(s=a,n<r){let c=Jt*Math.pow(2,n);o.log?.(` Retry ${n+1}/${r} after ${c}ms: ${a.message}`),await Vt(c)}}throw s}async function Ht(t,e=console){let{dir:o,endpoint:r,projectId:s,secretKey:n,authCert:a,release:c,buildId:u,noEncrypt:i,chunkSizeBytes:p=zt,excludeExt:d,uploadMode:f,keepMapInOutput:m}=t,k=!!a,{extractProjectIdFromCert:v,extractUploadKeyFromCert:b}=await Promise.resolve().then(()=>(fe(),de)),g=s||(k?v(a):""),A=k?b(a):null,j=await Bt(Ve(Kt(),"upload-sourcemaps-chunked-")),I=Ve(j,"archive.tar.gz"),B=f===he,R=B?null:{excludeExt:d||_,mapOnly:!0};try{let O;if(B){let{codeFileCount:w,mapFileCount:S}=await we(o,I,{excludeExt:d||_,includeMapInOutput:m!==!1});O=Math.max(1,S),m!==!1?e.log?.("Created backup archive: project-code.tar.gz (",w," files for rollback) + ",S," .map file(s), uploading in chunks..."):e.log?.("Created backup archive: project-code.tar.gz (",w," files for rollback, no .map per project config), uploading in chunks...")}else O=await H(o,I,R,!0);if(O===0)return e.log?.("No files to pack. Skip upload."),{ok:!0,uploaded:0};let P=await _t(I),U=!i&&(k?A!==null:!!(g&&n));if(U){if(A){let{encryptArchiveWithKey:w}=await Promise.resolve().then(()=>(se(),ne));P=w(P,A)}else{let{encryptArchive:w}=await Promise.resolve().then(()=>(se(),ne));P=w(P,g,n)}B||e.log?.(`Created tar.gz with ${O} file(s), encrypted, uploading in chunks...`)}else B||e.log?.(`Created tar.gz with ${O} file(s), uploading in chunks...`);let D=P.length,T=Math.ceil(D/p),h={"Content-Type":"application/json"};k?h["X-Auth-Cert"]=a:(h["X-Project-Id"]=g,h["X-Secret-Key"]=n||"");let F=(await Me(ge(r,"/sourcemaps/upload-archive-multipart/init"),{method:"POST",headers:{...h,"Content-Type":"application/json"},body:JSON.stringify({projectId:g,secretKey:k?"":n||"",release:c,buildId:u||void 0,totalChunks:T,chunkSizeBytes:p,encrypted:U,uploadMode:f||void 0})},e)).uploadId;if(!F)throw new Error("Init did not return uploadId");let W=new Set;for(let w=0;w<T;w++)W.add(w);try{let w=await fetch(ge(r,"/sourcemaps/upload-archive-multipart/status")+`?uploadId=${encodeURIComponent(F)}`,{method:"GET",headers:h}),S=await w.text();if(w.ok&&S){let C=JSON.parse(S).receivedChunks||[];C.forEach(E=>W.delete(E)),C.length>0&&e.log?.(`Resume: ${C.length}/${T} chunks already uploaded`)}}catch{}let J=3e3,Z=(T-W.size)*p,L=(D/1024/1024).toFixed(2),ue=()=>{let w=D?Math.min(100,Math.round(Z/D*100)):0,S=(Z/1024/1024).toFixed(2);typeof process<"u"&&process.stderr&&process.stderr.write&&process.stderr.write(`\r \u4E0A\u4F20\u8FDB\u5EA6 \u5206\u7247 ${S}/${L} MB (${w}%) `)},Q=setInterval(ue,J),ee=Math.min(8,T),le=async w=>{let S=w*p,V=Math.min(S+p,D),C=V-S,E=P.subarray(S,V),M=new FormData;M.append("uploadId",F),M.append("index",String(w)),M.append("totalChunks",String(T)),M.append("chunk",new Blob([E]),`chunk-${w}`);let gt=ge(r,"/sourcemaps/upload-archive-multipart/chunk");await Me(gt,{method:"POST",headers:k?{"X-Auth-Cert":a}:{"X-Project-Id":g,"X-Secret-Key":n||""},body:M},e),Z+=C},pe=[...W].sort((w,S)=>w-S);for(let w=0;w<pe.length;w+=ee){let S=pe.slice(w,w+ee);await Promise.all(S.map(le))}clearInterval(Q),D>0&&typeof process<"u"&&process.stderr&&process.stderr.write&&(ue(),process.stderr.write(`
|
|
5
|
+
`));let X=(await Me(ge(r,"/sourcemaps/upload-archive-multipart/complete"),{method:"POST",headers:{...h,"Content-Type":"application/json"},body:JSON.stringify({uploadId:F})},e)).uploaded??0;return e.log?.("Chunked upload OK, server extracted and saved",X,".map file(s)"),{ok:!0,uploaded:X}}finally{await Lt(j,{recursive:!0,force:!0})}}var zt,Wt,Jt,Ge=te(()=>{ye();zt=1*1024*1024,Wt=3,Jt=1e3});var dt={};re(dt,{loadMonitorConfig:()=>lt,loadUploadSourcemapsJson:()=>pt,resolveUploadConfig:()=>lr});import{readFile as at}from"fs/promises";import{join as ct,resolve as ur}from"path";import{pathToFileURL as it}from"url";function ut(){try{return process.env.UPLOAD_ENV||process.env.NODE_ENV||"production"}catch{return"production"}}async function lt(t){let e=["monitor.config.js","monitor.config.cjs","monitor.config.mjs"];for(let o of e){let r=ct(t,o),s;try{s=await import(it(r).href)}catch{continue}let n=s.default;if(!n||typeof n!="object")return null;let a=ut(),c=n.common||{},u=n[a]||{},i={...c,...u},p=(i.endpoint||"").trim(),d=(i.apiPrefix||"").trim(),f=p+(d.startsWith("/")?d:d?"/"+d:"");return{projectId:i.projectId,secretKey:i.secretKey,authCert:i.authCert,endpoint:f||void 0,release:i.release||n.version,version:n.version}}return null}async function pt(t){try{let e=await at(ct(t,".upload-sourcemaps.json"),"utf-8");return JSON.parse(e)}catch{return{}}}async function lr(t,e){let o=null,r=null;if(e){let s=ur(t,e);if(e.endsWith(".json"))try{r=JSON.parse(await at(s,"utf-8"))}catch{r={}}else try{let a=(await import(it(s).href)).default;if(a&&typeof a=="object"&&(a.common||a.production||a.development)){let c=ut(),u=a.common||{},i=a[c]||{},p={...u,...i},d=(p.endpoint||"").trim(),f=(p.apiPrefix||"").trim();o={projectId:p.projectId,secretKey:p.secretKey,authCert:p.authCert,endpoint:d+(f.startsWith("/")?f:f?"/"+f:""),release:p.release||a.version}}else r=a||{}}catch{r={}}}else o=await lt(t),r=await pt(t);return{projectId:r.projectId??o?.projectId,secretKey:r.secretKey??o?.secretKey,authCert:r.authCert??o?.authCert,endpoint:r.endpoint??o?.endpoint,release:r.release??o?.release??o?.version,dir:r.dir,chunkSize:r.chunkSize,concurrency:r.concurrency,uploadAsArchive:r.uploadAsArchive,noEncrypt:r.noEncrypt,buildCommand:r.buildCommand,buildOutputDir:r.buildOutputDir,uploadMode:r.uploadMode==="backup"?"backup":"mapOnly"}}var ft=te(()=>{});import{readFile as mt,writeFile as pr,access as dr}from"fs/promises";import{resolve as fr,dirname as mr,join as ie}from"path";import{fileURLToPath as hr}from"url";import{randomUUID as ht}from"crypto";import{spawn as br}from"child_process";import{readdir as Gt,readFile as Yt,unlink as qt,stat as Zt}from"fs/promises";import{join as Qt,basename as ae}from"path";var er=3e3;async function tr(t,e=12){let o=[],r=0;async function s(){for(;r<t.length;){let a=r++;try{o[a]=await t[a]()}catch(c){o[a]=Promise.reject(c)}}}let n=Array.from({length:Math.min(e,t.length)},()=>s());return await Promise.all(n),o}async function qe(t,e=[]){let o;try{o=await Gt(t,{withFileTypes:!0})}catch(r){if(r?.code==="ENOENT")return e;throw r}for(let r of o){let s=Qt(t,r.name);r.isDirectory()?await qe(s,e):r.name.endsWith(".map")&&e.push(s)}return e}var Ye="/sourcemaps/upload";function rr(t){let e=t.replace(/\/$/,"");return e.endsWith(Ye)?e:e+Ye}async function or(t){let{endpoint:e,projectId:o,secretKey:r,authCert:s,release:n,buildId:a,filePath:c}=t,u=!!s,i=o||(u?(await Promise.resolve().then(()=>(fe(),de))).extractProjectIdFromCert(s):""),p=ae(c,".map"),d=await Yt(c),f=rr(e),m=new FormData;m.append("projectId",i),u||m.append("secretKey",r||""),m.append("release",n),a&&m.append("buildId",a),m.append("filename",p),m.append("file",new Blob([d]),ae(c));let v=await fetch(f,{method:"POST",headers:u?{"X-Project-Id":i,"X-Auth-Cert":s}:{"X-Project-Id":i,"X-Secret-Key":r||""},body:m}),b=await v.text();if(!v.ok)throw new Error(`${v.status} ${b}`);return JSON.parse(b||"{}")}async function xe(t,e=console){let{dir:o,endpoint:r,projectId:s,secretKey:n,authCert:a,release:c,buildId:u,deleteAfterUpload:i=!1,deleteEvenOnFailure:p=!1,concurrency:d=12}=t,f=await qe(o);if(f.length===0)return e.log("No .map files found in",o),{ok:0,fail:0,deleted:0};let m=Math.max(1,Math.min(64,Number(d)||12)),k=i&&p,v=f.length,b=await Promise.all(f.map(h=>Zt(h).then(l=>l.size))),g=b.reduce((h,l)=>h+l,0),A=(g/1024/1024).toFixed(2);e.log("Uploading",v,"source map(s) to",r,"release=",c,"concurrency=",m,i?k?"(will delete local .map even on failure)":"(will delete local .map only after all succeed)":"");let j=0,I=()=>{let h=g?Math.min(100,Math.round(j/g*100)):0,l=(j/1024/1024).toFixed(2);typeof process<"u"&&process.stderr&&process.stderr.write&&process.stderr.write(`\r \u4E0A\u4F20\u8FDB\u5EA6 ${l}/${A} MB (${h}%) `)},B=setInterval(I,er),R=f.map((h,l)=>()=>or({endpoint:r,projectId:s,secretKey:n,authCert:a,release:c,buildId:u,filePath:h}).then(()=>(j+=b[l],{ok:!0,name:ae(h)})).catch(F=>(j+=b[l],{ok:!1,name:ae(h),err:F}))),O=await tr(R,m);clearInterval(B),g>0&&process.stderr&&process.stderr.write&&(I(),process.stderr.write(`
|
|
6
|
+
`));let P=0,U=0;for(let h of O)h.ok?(e.log(" OK",h.name),P++):(e.error(" FAIL",h.name,h.err?.message||h.err),U++);let D=i&&(U===0||p),T=0;if(D&&f.length>0){for(let h of f)try{await qt(h),T++,e.log(" Deleted",ae(h))}catch(l){e.error(" Delete failed",h,l.message)}U>0&&p&&e.log("Deleted .map files despite upload failures. Fix errors and re-run to re-upload.")}else U>0&&!p&&e.log("Upload had failures. Local .map files were NOT deleted. Fix errors and re-run.");return{ok:P,fail:U,deleted:T}}async function Ce(t,e=console){let{uploadArchive:o,deleteMapFiles:r}=await Promise.resolve().then(()=>(ye(),ke)),{dir:s,endpoint:n,projectId:a,secretKey:c,authCert:u,release:i,buildId:p,deleteEvenOnFailure:d=!1,noEncrypt:f,uploadMode:m,excludeExt:k}=t;try{let{uploaded:v}=await o({dir:s,endpoint:n,projectId:a,secretKey:c,authCert:u,release:i,buildId:p,noEncrypt:f,uploadMode:m,excludeExt:k},e);if(v===0)return{uploaded:0,deleted:0};let b=await r(s,[]);return e.log("Deleted",b.length,"local .map file(s)."),{uploaded:v,deleted:b.length}}catch(v){if(d)try{let b=await r(s,[]);e.log("Deleted",b.length,"local .map file(s) despite upload failure.")}catch(b){e.error("Delete .map after upload failure failed:",b.message)}else e.error("Upload failed. Local .map files were NOT deleted.",v.message);throw v}}async function Se(t,e=console){let{uploadArchiveChunked:o}=await Promise.resolve().then(()=>(Ge(),He)),{deleteMapFiles:r}=await Promise.resolve().then(()=>(ye(),ke)),{dir:s,endpoint:n,projectId:a,secretKey:c,authCert:u,release:i,buildId:p,deleteEvenOnFailure:d=!1,noEncrypt:f,chunkSizeBytes:m,uploadMode:k,excludeExt:v}=t;try{let{uploaded:b}=await o({dir:s,endpoint:n,projectId:a,secretKey:c,authCert:u,release:i,buildId:p,noEncrypt:f,chunkSizeBytes:m,uploadMode:k,excludeExt:v},e);if(b===0)return{uploaded:0,deleted:0};let g=await r(s,[]);return e.log("Deleted",g.length,"local .map file(s)."),{uploaded:b,deleted:g.length}}catch(b){if(d)try{let g=await r(s,[]);e.log("Deleted",g.length,"local .map file(s) despite upload failure.")}catch(g){e.error("Delete .map after upload failure failed:",g.message)}else e.error("Upload failed. Local .map files were NOT deleted.",b.message);throw b}}import{readFile as Ie}from"fs/promises";import{join as $,resolve as ce}from"path";async function G(t){let e=t?ce(process.cwd(),t):process.cwd(),o=[process.cwd(),e,ce(e,".."),ce(e,"..",".."),ce(e,"..","..","..")],r=new Set;for(let s of o){let n=s&&ce(s);if(!(!n||r.has(n))){r.add(n);try{return await Ie($(n,"package.json"),"utf-8"),n}catch{continue}}}return null}async function K(t){try{return await Ie(t,"utf-8")}catch{return""}}async function Ze(t){let e=["vite.config.js","vite.config.mjs","vite.config.cjs","vite.config.ts"];for(let o of e){let r=await K($(t,o));if(r){if(/\bsourcemap\s*:\s*false\b/.test(r)||/build\s*:\s*\{[^}]*sourcemap\s*:\s*false/.test(r))return{emitsSourceMap:!1,bundler:"vite",keepMapInOutput:!1};if(/\bsourcemap\s*:\s*['"]inline['"]/.test(r)||/build\s*:\s*\{[\s\S]*?sourcemap\s*:\s*['"]inline['"]/.test(r))return{emitsSourceMap:!0,bundler:"vite",keepMapInOutput:!1};if(/\bsourcemap\s*:\s*true\b/.test(r)||/\bsourcemap\s*:\s*['"]hidden['"]/.test(r))return{emitsSourceMap:!0,bundler:"vite",keepMapInOutput:!0};if(/build\s*:\s*\{[\s\S]*?sourcemap\s*:\s*true/.test(r))return{emitsSourceMap:!0,bundler:"vite",keepMapInOutput:!0};if(/build\s*:\s*\{[\s\S]*?sourcemap\s*:\s*['"]hidden['"]/.test(r))return{emitsSourceMap:!0,bundler:"vite",keepMapInOutput:!0};if(/BUILD_SOURCEMAP|SOURCEMAP/.test(r)&&/build\s*:\s*\{[\s\S]*?sourcemap\s*:/.test(r))return{emitsSourceMap:!0,bundler:"vite",keepMapInOutput:!0};if(/build\s*:|\bdefineConfig\s*\(/.test(r))return{emitsSourceMap:!1,bundler:"vite",keepMapInOutput:!1}}}return null}async function Qe(t){let e=["webpack.config.js","webpack.config.cjs","webpack.config.mjs","webpack.config.ts","webpack.common.js","webpack.prod.js"];for(let o of e){let r=await K($(t,o));if(r){if(/\bdevtool\s*:\s*false\b/.test(r))return{emitsSourceMap:!1,bundler:"webpack",keepMapInOutput:!1};if(/\bdevtool\s*:\s*['"]source-map['"]/.test(r)||/\bdevtool\s*:\s*['"]hidden-source-map['"]/.test(r))return{emitsSourceMap:!0,bundler:"webpack",keepMapInOutput:!0};if(/\bdevtool\s*:\s*true\b/.test(r))return{emitsSourceMap:!0,bundler:"webpack",keepMapInOutput:!0}}}return null}async function et(t){let e=await K($(t,"vue.config.js"))||await K($(t,"vue.config.cjs"));return e?/\bproductionSourceMap\s*:\s*false\b/.test(e)?{emitsSourceMap:!1,bundler:"vue-cli",keepMapInOutput:!1}:/\bproductionSourceMap\s*:\s*true\b/.test(e)?{emitsSourceMap:!0,bundler:"vue-cli",keepMapInOutput:!0}:/\.devtool\s*\(\s*['"]source-map['"]\s*\)/.test(e)?{emitsSourceMap:!0,bundler:"vue-cli",keepMapInOutput:!0}:null:null}async function tt(t){let e=["rollup.config.js","rollup.config.mjs","rollup.config.cjs"];for(let o of e){let r=await K($(t,o));if(r){if(/\bsourcemap\s*:\s*false\b/.test(r))return{emitsSourceMap:!1,bundler:"rollup",keepMapInOutput:!1};if(/\bsourcemap\s*:\s*['"]inline['"]/.test(r))return{emitsSourceMap:!0,bundler:"rollup",keepMapInOutput:!1};if(/\bsourcemap\s*:\s*true\b/.test(r))return{emitsSourceMap:!0,bundler:"rollup",keepMapInOutput:!0}}}return null}async function rt(t){let e=["next.config.js","next.config.mjs","next.config.cjs","next.config.ts"];for(let o of e){let r=await K($(t,o));if(r){if(/\bproductionBrowserSourceMaps\s*:\s*true\b/.test(r))return{emitsSourceMap:!0,bundler:"next",keepMapInOutput:!0};if(/\bproductionBrowserSourceMaps\s*:\s*false\b/.test(r))return{emitsSourceMap:!1,bundler:"next",keepMapInOutput:!1};if(/module\.exports|next\s*\(/.test(r))return{emitsSourceMap:!1,bundler:"next",keepMapInOutput:!1}}}return null}async function ot(t){let e=await K($(t,"nuxt.config.js"))||await K($(t,"nuxt.config.ts"));return e?/\bsourcemap\s*:\s*false\b/.test(e)?{emitsSourceMap:!1,bundler:"nuxt",keepMapInOutput:!1}:/\bsourcemap\s*:\s*true\b/.test(e)?{emitsSourceMap:!0,bundler:"nuxt",keepMapInOutput:!0}:/\bsourcemap\s*:\s*\{[\s\S]*?\bclient\s*:\s*false\b/.test(e)||/\bsourcemap\s*:\s*\{[\s\S]*?\bserver\s*:\s*false\b/.test(e)?{emitsSourceMap:!1,bundler:"nuxt",keepMapInOutput:!1}:/\bsourcemap\s*:\s*\{[\s\S]*?\bclient\s*:\s*true\b/.test(e)?{emitsSourceMap:!0,bundler:"nuxt",keepMapInOutput:!0}:/\bsourcemap\s*:\s*\{[\s\S]*?\bserver\s*:\s*true[\s\S]*?\bclient\s*:\s*true\b/.test(e)?{emitsSourceMap:!0,bundler:"nuxt",keepMapInOutput:!0}:null:null}function nr(t){let e={...t.dependencies,...t.devDependencies};return!e||typeof e!="object"?null:e.vite||e.vite?"vite":e.next?"next":e.nuxt||e.nuxt3?"nuxt":e.vue&&(e["@vue/cli-service"]||e["vue-cli-service"])?"vue-cli":e.webpack||e["react-scripts"]||e["@angular/cli"]?"webpack":e.rollup?"rollup":e.parcel?"parcel":null}async function Oe(t){let e=await G(t);if(!e)return{emitsSourceMap:!0,keepMapInOutput:!1};let o={};try{let n=await Ie($(e,"package.json"),"utf-8");o=JSON.parse(n)}catch{return{emitsSourceMap:!0,keepMapInOutput:!1}}let r=nr(o),s=[()=>r==="vite"?Ze(e):null,()=>r==="next"?rt(e):null,()=>r==="nuxt"?ot(e):null,()=>r==="vue-cli"?et(e):null,()=>r==="webpack"?Qe(e):null,()=>r==="rollup"?tt(e):null,()=>Ze(e),()=>rt(e),()=>et(e),()=>Qe(e),()=>tt(e),()=>ot(e)];for(let n of s){let a=await n();if(a)return a}return{emitsSourceMap:!0,keepMapInOutput:!1}}import{readFile as je,writeFile as ve,unlink as sr}from"fs/promises";import{join as nt,resolve as ar}from"path";var Ae=".upload-sourcemaps.patch-backup.json";async function cr(t){try{return await je(t,"utf-8")}catch{return""}}function ir(t){let e={...t?.dependencies||{},...t?.devDependencies||{}};return e.vite||e.vite?"vite":e.next?"next":e.nuxt||e.nuxt3?"nuxt":e.vue&&(e["@vue/cli-service"]||e["vue-cli-service"])?"vue-cli":e.webpack||e["react-scripts"]||e["@angular/cli"]?"webpack":e.rollup?"rollup":null}var Ee=[{name:"vite",configNames:["vite.config.js","vite.config.mjs","vite.config.cjs","vite.config.ts"],needPatch:t=>/\bsourcemap\s*:\s*false\b/.test(t),patch:t=>t.replace(/(sourcemap\s*:\s*)false\b/,"$1true")},{name:"webpack",configNames:["webpack.config.js","webpack.config.cjs","webpack.config.mjs","webpack.config.ts","webpack.common.js","webpack.prod.js"],needPatch:t=>/\bdevtool\s*:\s*false\b/.test(t),patch:t=>t.replace(/(devtool\s*:\s*)false\b/,(e,o)=>`${o}'source-map'`)},{name:"vue-cli",configNames:["vue.config.js","vue.config.cjs"],needPatch:t=>/\bproductionSourceMap\s*:\s*false\b/.test(t),patch:t=>t.replace(/(productionSourceMap\s*:\s*)false\b/,"$1true")},{name:"rollup",configNames:["rollup.config.js","rollup.config.mjs","rollup.config.cjs"],needPatch:t=>/\bsourcemap\s*:\s*false\b/.test(t),patch:t=>t.replace(/(sourcemap\s*:\s*)false\b/,"$1true")},{name:"next",configNames:["next.config.js","next.config.mjs","next.config.cjs","next.config.ts"],needPatch:t=>/\bproductionBrowserSourceMaps\s*:\s*false\b/.test(t),patch:t=>t.replace(/(productionBrowserSourceMaps\s*:\s*)false\b/,"$1true")},{name:"nuxt",configNames:["nuxt.config.js","nuxt.config.ts"],needPatch:t=>!!(!/\bsourcemap\s*:/.test(t)||/\bsourcemap\s*:\s*false\b/.test(t)||/\bsourcemap\s*:\s*\{/.test(t)&&(/\bclient\s*:\s*false\b/.test(t)||/\bserver\s*:\s*false\b/.test(t)||!/\bclient\s*:\s*true\b/.test(t)||!/\bserver\s*:\s*true\b/.test(t))),patch:t=>{let e=t;if(!/\bsourcemap\s*:\s*\{[\s\S]*?\b(server|client)\s*:/.test(e)){let r=e.match(/(defineNuxtConfig\s*\(\s*)\{/);r&&(e=e.replace(/(defineNuxtConfig\s*\(\s*)\{/,`${r[1]}{
|
|
7
|
+
sourcemap: { server: true, client: true },`))}return/\bsourcemap\s*:\s*false\b/.test(e)&&(e=e.replace(/(sourcemap\s*:\s*)false\b/g,"$1true")),/\bclient\s*:\s*false\b/.test(e)&&(e=e.replace(/(\bclient\s*:\s*)false\b/g,"$1true")),/\bserver\s*:\s*false\b/.test(e)&&(e=e.replace(/(\bserver\s*:\s*)false\b/g,"$1true")),/\bsourcemap\s*:\s*\{[\s\S]*?\bserver\s*:\s*true\b/.test(e)&&!/\bclient\s*:/.test(e)&&(e=e.replace(/(\bserver\s*:\s*true)\b/,"$1, client: true")),/\bsourcemap\s*:\s*\{[\s\S]*?\bclient\s*:\s*true\b/.test(e)&&!/\bserver\s*:/.test(e)&&(e=e.replace(/(\bsourcemap\s*:\s*\{)/,"$1 server: true, ")),/\bsourcemap\s*:\s*\{\s*\}/.test(e)&&(e=e.replace(/\bsourcemap\s*:\s*\{\s*\}/,"sourcemap: { server: true, client: true }")),/\bsourcemap\s*:\s*\{/.test(e)&&(!/\bclient\s*:\s*true\b/.test(e)||!/\bserver\s*:\s*true\b/.test(e))&&(e=e.replace(/(\bsourcemap\s*:\s*\{)/,"$1 server: true, client: true, ")),e}}];async function Pe(t,e={}){let o=await G(t);if(!o)return{restore:async()=>{}};let r={};try{let a=await je(nt(o,"package.json"),"utf-8");r=JSON.parse(a)}catch{return{restore:async()=>{}}}let s=ir(r),n=s?[...Ee.filter(a=>a.name===s),...Ee.filter(a=>a.name!==s)]:Ee;for(let a of n)for(let c of a.configNames){let u=nt(o,c),i=await cr(u);if(!i||!a.needPatch(i))continue;let p=a.patch(i);if(await ve(u,p,"utf-8"),e.writeBackupTo){let d={configPath:ar(u),content:i};await ve(e.writeBackupTo,JSON.stringify(d),"utf-8")}return console.log("[upload-sourcemaps] Patched %s to enable source map (%s)",u,a.name),{patchedFile:u,restore:async()=>{try{await ve(u,i,"utf-8")}catch(d){console.error(`Restore ${a.name} config failed:`,d?.message||d)}}}}return{restore:async()=>{}}}async function st(t){try{let e=await je(t,"utf-8"),{configPath:o,content:r}=JSON.parse(e);return!o||r===void 0?!1:(await ve(o,r,"utf-8"),await sr(t),console.log("[upload-sourcemaps] Restored %s",o),!0)}catch{return!1}}var ao=mr(hr(import.meta.url));function x(t,e){let o=process.env[e];if(o)return o;let r=process.argv.indexOf(t);return r!==-1&&process.argv[r+1]?process.argv[r+1]:null}function z(t){return process.argv.includes(t)}function wr(t){if(t==null||t==="")return null;let e=String(t).trim().toUpperCase(),o=parseInt(e.replace(/[^0-9]/g,""),10)||0;return e.endsWith("G")?o*1024*1024*1024:e.endsWith("MB")||e.endsWith("M")?o*1024*1024:e.endsWith("KB")||e.endsWith("K")?o*1024:/^\d+$/.test(String(t).trim())?parseInt(String(t).trim(),10)||null:o>0?o*1024*1024:null}function bt(t={}){let e=x("--chunk-size","UPLOAD_SOURCEMAPS_CHUNK_SIZE")||t.chunkSize;return wr(e)}async function yr(){let{resolveUploadConfig:t}=await Promise.resolve().then(()=>(ft(),dt));return t(process.cwd(),x("--config","UPLOAD_SOURCEMAPS_CONFIG")||void 0)}async function gr(t){let e=[t,fr(t,".."),process.cwd()];for(let o of e)try{let r=await mt(ie(o,"package.json"),"utf-8"),s=JSON.parse(r);if(s.version&&typeof s.version=="string")return s.version.trim()}catch{continue}return null}async function wt(t,e){return t&&t!=="default"?t:await gr(e)||"default"}var y={};try{y=await yr()}catch{y={}}var vr=process.argv[2]==="build",kr=process.argv[2]==="patch",Mr=process.argv[2]==="restore",xr=process.argv[2]==="init";function yt(t,e){return t||e===!0}async function Cr(){let t=x("--project-id","PROJECT_ID")||y.projectId,e=x("--secret-key","SECRET_KEY")||y.secretKey,o=x("--auth-cert","MONITOR_AUTH_CERT")||y.authCert,r=(x("--endpoint","ENDPOINT")||y.endpoint||"").replace(/\/$/,"");if(!r)throw new Error("build subcommand requires endpoint (config file or env/args).");if(!o&&(!t||!e))throw new Error("build subcommand requires projectId+secretKey or authCert (config file or env/args).");let s=y.buildCommand||process.env.UPLOAD_SOURCEMAPS_BUILD_CMD||"npm run build",n=y.buildOutputDir||y.dir||x("--dir","DIR")||process.cwd(),a=z("--archive")||y.uploadAsArchive===!0,c=yt(z("--no-encrypt"),y.noEncrypt),u=x("--upload-mode","UPLOAD_MODE")||y.uploadMode||"mapOnly";u==="all"&&(u="backup");let i=await Oe(n),p=!i.keepMapInOutput||z("--delete-after-upload")||z("-d")||y.deleteAfterUpload===!0,d=x("--build-id","BUILD_ID")||y.buildId||ht(),f=await Pe(n);try{console.log("Step 1: Running build (forcing source map generation):",s),console.log("Build ID:",d);let[m,...k]=s.split(/\s+/);await new Promise((g,A)=>{br(m,k,{stdio:"inherit",shell:!0,env:{...process.env,BUILD_SOURCEMAP:"1",SOURCEMAP:"1",MONITOR_BUILD_ID:d,VITE_MONITOR_BUILD_ID:d}}).on("exit",I=>I===0?g():A(new Error(`Build exited ${I}`)))}),console.log("Step 2: Uploading source maps...");let v=await wt(x("--release","RELEASE")||y.release,n);v!=="default"&&console.log("Release (from package.json or config):",v);let b=bt(y);if(a){let g={dir:n,endpoint:r,projectId:t,secretKey:e,authCert:o,release:v,buildId:d,deleteEvenOnFailure:!1,noEncrypt:c,uploadMode:u,keepMapInOutput:i.keepMapInOutput};(b?await Se({...g,chunkSizeBytes:b},console):await Ce(g,console)).uploaded===0&&console.log("No .map files in output. For Vite we patch config to force source map; check build output dir and README.")}else{let g=Math.max(1,Math.min(64,parseInt(x("--concurrency","CONCURRENCY")||y.concurrency||"12",10)||12)),{ok:A,fail:j}=await xe({endpoint:r,projectId:t,secretKey:e,authCert:o,release:v,buildId:d,dir:n,deleteAfterUpload:p,deleteEvenOnFailure:!1,concurrency:g},console);if(j>0)throw new Error("Upload had failures. Local .map files were NOT deleted. Fix errors and re-run.");A===0&&console.log("No .map files in output. For Vite we patch config to force source map; check build output dir and README.")}console.log("Done. Output dir:",n)}catch(m){throw console.error("Error:",m.message),console.error(p?"If the failure was during upload, local .map files may have been removed. Fix the issue and re-run.":"Local files were not modified. Fix the issue and re-run."),m}finally{await f.restore()}}var Sr={"build:upload":"upload-sourcemaps build","build:upload:archive":"upload-sourcemaps build --archive"};async function Ir(){let t=process.cwd(),e=await G(t);if(!e){console.error("[upload-sourcemaps] Cannot find project root (no package.json from:",t,"). Run from project root."),process.exitCode=1;return}let o=ie(e,"package.json"),r;try{let c=await mt(o,"utf-8");r=JSON.parse(c)}catch(c){console.error("[upload-sourcemaps] Failed to read package.json:",c.message),process.exitCode=1;return}let s=r.scripts||{},n=0;for(let[c,u]of Object.entries(Sr))s[c]||(s[c]=u,n++);r.scripts=s;try{await pr(o,JSON.stringify(r,null,2)+`
|
|
8
|
+
`,"utf-8")}catch(c){console.error("[upload-sourcemaps] Failed to write package.json:",c.message),process.exitCode=1;return}let a=!1;try{await dr(ie(e,".upload-sourcemaps.json")),a=!0}catch{}console.log("[upload-sourcemaps] init ok. Added",n,"script(s) to package.json."),n>0&&(console.log(" Use: npm run build:upload (build + upload .map)"),console.log(" Or: npm run build:upload:archive (build + pack & upload, recommended)")),a||console.log(" Config: add monitor.config.js or .upload-sourcemaps.json (see README).")}if(xr)await Ir();else if(kr){let t=x("--dir","DIR")||y.dir||process.cwd(),e=await G(t);if(!e)console.error("[upload-sourcemaps] Could not find project root (no package.json from dir:",t,"). Fix --dir or run from project root."),process.exitCode=1;else{let o=ie(e,Ae),r=await Pe(t,{writeBackupTo:o});if(r.patchedFile){try{let{readFile:s}=await import("fs/promises"),n=await s(r.patchedFile,"utf-8"),a=/sourcemap\s*:\s*\{\s*[\s\S]*?server\s*:\s*true[\s\S]*?client\s*:\s*true/.test(n)||/sourcemap\s*:\s*\{\s*[\s\S]*?client\s*:\s*true[\s\S]*?server\s*:\s*true/.test(n),c=/sourcemap\s*:\s*true/.test(n);console.log("[upload-sourcemaps] Verified:",a?"top-level sourcemap":"(no top-level)",c?"vite.build.sourcemap=true":"")}catch{}console.log("Run your build, then run: upload-sourcemaps restore")}else console.log("No config needed patching (source map already enabled or unsupported bundler).")}}else if(Mr){let t=x("--backup","UPLOAD_SOURCEMAPS_PATCH_BACKUP")||ie(process.cwd(),Ae);await st(t)||(console.error("Restore failed or no backup file found at",t),process.exitCode=1)}else if(vr)try{await Cr()}catch{process.exitCode=1}else{let t=x("--project-id","PROJECT_ID")||y.projectId,e=x("--secret-key","SECRET_KEY")||y.secretKey,o=x("--auth-cert","MONITOR_AUTH_CERT")||y.authCert,r=(x("--endpoint","ENDPOINT")||y.endpoint||"").replace(/\/$/,""),s=x("--dir","DIR")||y.dir||process.cwd(),n=await wt(x("--release","RELEASE")||y.release,s),a=x("--build-id","BUILD_ID")||y.buildId||ht(),c=z("--archive")||y.uploadAsArchive===!0,u=yt(z("--no-encrypt"),y.noEncrypt),i=bt(y),p=Math.max(1,Math.min(64,parseInt(x("--concurrency","CONCURRENCY")||y.concurrency||"12",10)||12)),d=x("--upload-mode","UPLOAD_MODE")||y.uploadMode||"mapOnly";d==="all"&&(d="backup");let f=await Oe(s),m=!f.keepMapInOutput||z("--delete-after-upload")||z("-d")||y.deleteAfterUpload===!0;if(!r)console.error("Usage: ENDPOINT is required.");else if(!o&&(!t||!e))console.error("Usage: PROJECT_ID and SECRET_KEY are required, or use AUTH_CERT (certificate auth)."),console.error(""),console.error(" Arguments:"),console.error(" --project-id <id> Project ID"),console.error(" --secret-key <key> Secret Key"),console.error(" --endpoint <url> API base URL (e.g. https://api.example.com/monitorApi)"),console.error(" --auth-cert <cert> Certificate content (or MONITOR_AUTH_CERT); use with monitor.config.js, no secretKey needed"),console.error(" --release <tag> Release version (default: read from package.json)"),console.error(" --build-id <id> Build ID (default: auto-generated UUID)"),console.error(" --dir <path> Directory (default: cwd)"),console.error(" --concurrency <n> Max parallel uploads when not using --archive (default: 12)"),console.error(" --delete-after-upload, -d Delete local .map after upload (from deployment output)"),console.error(" --archive Pack as .tar.gz (encrypted by default) \u2192 upload \u2192 server decrypts and extracts"),console.error(" --upload-mode <mode> mapOnly (default) | backup (code tar, .map \u4F9D\u9879\u76EE\u914D\u7F6E\u662F\u5426\u4FDD\u7559)"),console.error(" --chunk-size <size> With --archive: chunked upload (e.g. 1M, 5M), default 1MB, enables resumable and retry"),console.error(" --no-encrypt With --archive: upload plain tar.gz (certificate auth implies no-encrypt)"),console.error(" --config <path> Config file (default: monitor.config.js + .upload-sourcemaps.json)"),console.error(""),console.error(" Subcommands:"),console.error(" init Add recommended scripts to package.json (build:upload, build:upload:archive)"),console.error(" build One-step: run build then upload source maps"),console.error(" patch / restore Temporarily enable source map in bundler config, then restore"),console.error(""),console.error(" Whether to delete .map after upload: inferred from project bundler config (Vite/Webpack etc.). Use -d to force delete."),process.exitCode=1;else{n!=="default"&&console.log("Release:",n),console.log("Build ID:",a);try{if(c){let k={dir:s,endpoint:r,projectId:t,secretKey:e,authCert:o,release:n,buildId:a,deleteEvenOnFailure:!1,noEncrypt:u,uploadMode:d,keepMapInOutput:f.keepMapInOutput};i?await Se({...k,chunkSizeBytes:i},console):await Ce(k,console)}else{let{ok:k,fail:v,deleted:b}=await xe({endpoint:r,projectId:t,secretKey:e,authCert:o,release:n,buildId:a,dir:s,deleteAfterUpload:m,deleteEvenOnFailure:m,concurrency:p},console);b>0&&console.log("Deleted",b,"local .map file(s) from output."),v>0&&(console.error(m?"Upload had failures. Local .map files were still deleted (per project config). Fix errors and re-run to re-upload.":"Upload had failures. Local .map files were NOT deleted. Fix errors and re-run."),process.exitCode=1)}}catch(k){console.error("Error:",k.message),console.error(m?"Local .map files were still deleted. Fix the issue and re-run.":"Local files were not modified. Fix the issue and re-run."),process.exitCode=1}}}
|
package/dist/upload.js
CHANGED
|
@@ -1 +1,5 @@
|
|
|
1
|
-
var de=Object.defineProperty;var N=(t,e)=>()=>(t&&(e=t(t=0)),e);var L=(t,e)=>{for(var a in e)de(t,a,{get:e[a],enumerable:!0})};var X={};L(X,{extractProjectIdFromCert:()=>ue,extractUploadKeyFromCert:()=>fe});function ue(t){if(typeof t!="string")return"";let e=t.trim();if(e.startsWith(pe+"$"))return e.split("$")[1]||"";if(e.includes(Z)){let a=e.match(/Project:\s*(\S+)/);return a?a[1]:""}return""}function fe(t){if(typeof t!="string"||!t.includes(Z))return null;let e=t.match(/Upload-Key:\s*([0-9a-fA-F]{64})/);if(!e)return null;try{return Buffer.from(e[1],"hex")}catch{return null}}var pe,Z,B=N(()=>{pe="MC1",Z="-----BEGIN MONITOR CERT-----"});var O={};L(O,{decryptArchive:()=>xe,encryptArchive:()=>ve,encryptArchiveWithKey:()=>te});import{createCipheriv as he,createDecipheriv as me,createHash as ye,randomBytes as we}from"crypto";function ee(t,e){let a=`${t}:${e}`;return ye("sha256").update(a,"utf8").digest()}function ve(t,e,a){let r=ee(e,a);return te(t,r)}function te(t,e){if(!Buffer.isBuffer(e)||e.length!==32)throw new Error("Invalid key: must be 32-byte Buffer");let a=we(R),r=he(Q,e,a,{authTagLength:S}),o=Buffer.concat([r.update(t),r.final()]),n=r.getAuthTag();return Buffer.concat([a,o,n])}function xe(t,e,a){if(t.length<R+S)throw new Error("Invalid encrypted archive: too short");let r=ee(e,a),o=t.subarray(0,R),n=t.subarray(t.length-S),i=t.subarray(R,t.length-S),c=me(Q,r,o,{authTagLength:S});return c.setAuthTag(n),Buffer.concat([c.update(i),c.final()])}var Q,R,S,$=N(()=>{Q="aes-256-gcm",R=12,S=16});var q={};L(q,{DEFAULT_EXCLUDE_EXT:()=>z,collectFiles:()=>J,createArchive:()=>M,deleteMapFiles:()=>ae,uploadArchive:()=>je});import{createRequire as Ce}from"module";import{readdir as re,unlink as Ie,readFile as ke,mkdtemp as Ee,rm as be}from"fs/promises";import{join as _,relative as Ae}from"path";import{createWriteStream as ge}from"fs";import{tmpdir as Te}from"os";async function J(t,e=t,a=z){let r=await re(t,{withFileTypes:!0}),o=[];for(let n of r){let i=_(t,n.name),c=Ae(e,i).replace(/\\/g,"/");if(n.isDirectory()){let p=await J(i,e,a);o.push(...p)}else{let p=(n.name.includes(".")?"."+n.name.split(".").pop():"").toLowerCase();if(a.has(p))continue;o.push({relativePath:c,fullPath:i})}}return o}async function M(t,e,a=z,r=!0){let o=await J(t,t,a),n=o.map(i=>i.relativePath);return n.length===0?0:new Promise((i,c)=>{let p=Fe.create({gzip:r,cwd:t},n);p.on("error",c),p.pipe(ge(e)).on("close",()=>i(o.length)).on("error",c)})}async function je(t,e=console){let{dir:a,endpoint:r,projectId:o,secretKey:n,authCert:i,release:c,buildId:p,excludeExt:y,noEncrypt:h}=t,u=!!i,{extractProjectIdFromCert:m,extractUploadKeyFromCert:l}=await Promise.resolve().then(()=>(B(),X)),s=o||(u?m(i):""),d=u?l(i):null,x=await Ee(_(Te(),"upload-sourcemaps-")),v=_(x,"archive.tar.gz");try{let C=await M(a,v,y||z,!0);if(C===0)return e.log("No files to pack (directory empty or only excluded). Skip upload."),{ok:!0,uploaded:0};let k=await ke(v),I=!h&&(u?d!==null:!!(s&&n));if(I)if(d){let{encryptArchiveWithKey:D}=await Promise.resolve().then(()=>($(),O));k=D(k,d),e.log("Created tar.gz with",C,"file(s), encrypted with cert Upload-Key, uploading...")}else{let{encryptArchive:D}=await Promise.resolve().then(()=>($(),O));k=D(k,s,n),e.log("Created tar.gz with",C,"file(s), encrypted, uploading...")}else e.log("Created tar.gz with",C,"file(s), uploading...");let F="/sourcemaps/upload-archive",g=r.replace(/\/$/,""),E=g.endsWith(F)?g:g+F,w=new FormData;w.append("projectId",s),u||w.append("secretKey",n||""),w.append("release",c),p&&w.append("buildId",p),I&&w.append("encrypted","1"),w.append("file",new Blob([k]),I?"archive.tar.gz.enc":"archive.tar.gz");let U={"X-Project-Id":s};u?U["X-Auth-Cert"]=i:U["X-Secret-Key"]=n||"";let b=await fetch(E,{method:"POST",headers:U,body:w}),T=await b.text();if(!b.ok)throw new Error(`${b.status} ${T}`);let H=JSON.parse(T||"{}");return e.log("Upload archive OK, server extracted and saved",H.uploaded,".map file(s)"),{ok:!0,uploaded:H.uploaded||0}}finally{await be(x,{recursive:!0,force:!0})}}async function ae(t,e=[]){let a=await re(t,{withFileTypes:!0});for(let r of a){let o=_(t,r.name);r.isDirectory()?await ae(o,e):r.name.endsWith(".map")&&(await Ie(o),e.push(o))}return e}var De,Fe,z,W=N(()=>{De=Ce(import.meta.url),Fe=De("tar"),z=new Set([".png",".jpg",".jpeg",".gif",".svg",".webp",".ico",".bmp",".avif",".woff",".woff2",".ttf",".eot",".otf",".mp4",".webm",".ogg",".mp3",".wav",".m4a",".aac",".pdf",".doc",".docx",".xls",".xlsx"])});var ne={};L(ne,{uploadArchiveChunked:()=>Be});import{readFile as Ke,mkdtemp as Se,rm as Oe}from"fs/promises";import{join as oe}from"path";import{tmpdir as $e}from"os";function Le(t){return t.replace(/\/$/,"")}function G(t,e){let a=Le(t),r=e.startsWith("/")?e:`/${e}`;return a.endsWith(r)?a:a+r}async function Xe(t){return new Promise(e=>setTimeout(e,t))}async function Y(t,e,a,r=Ue){let o;for(let n=0;n<=r;n++)try{let i=await fetch(t,e),c=await i.text();if(!i.ok)throw new Error(`${i.status} ${c}`);return c?JSON.parse(c):{}}catch(i){if(o=i,n<r){let c=Ne*Math.pow(2,n);a.log?.(` Retry ${n+1}/${r} after ${c}ms: ${i.message}`),await Xe(c)}}throw o}async function Be(t,e=console){let{dir:a,endpoint:r,projectId:o,secretKey:n,authCert:i,release:c,buildId:p,noEncrypt:y,chunkSizeBytes:h=Pe,excludeExt:u}=t,m=!!i,{extractProjectIdFromCert:l,extractUploadKeyFromCert:s}=await Promise.resolve().then(()=>(B(),X)),d=o||(m?l(i):""),x=m?s(i):null,v=await Se(oe($e(),"upload-sourcemaps-chunked-")),C=oe(v,"archive.tar.gz");try{let k=await M(a,C,u||void 0,!0);if(k===0)return e.log?.("No files to pack. Skip upload."),{ok:!0,uploaded:0};let I=await Ke(C),F=!y&&(m?x!==null:!!(d&&n));if(F){if(x){let{encryptArchiveWithKey:f}=await Promise.resolve().then(()=>($(),O));I=f(I,x)}else{let{encryptArchive:f}=await Promise.resolve().then(()=>($(),O));I=f(I,d,n)}e.log?.(`Created tar.gz with ${k} file(s), encrypted, uploading in chunks...`)}else e.log?.(`Created tar.gz with ${k} file(s), uploading in chunks...`);let g=I.length,E=Math.ceil(g/h),w={"Content-Type":"application/json"};m?w["X-Auth-Cert"]=i:(w["X-Project-Id"]=d,w["X-Secret-Key"]=n||"");let b=(await Y(G(r,"/sourcemaps/upload-archive-multipart/init"),{method:"POST",headers:{...w,"Content-Type":"application/json"},body:JSON.stringify({projectId:d,secretKey:m?"":n||"",release:c,buildId:p||void 0,totalChunks:E,chunkSizeBytes:h,encrypted:F})},e)).uploadId;if(!b)throw new Error("Init did not return uploadId");let T=new Set;for(let f=0;f<E;f++)T.add(f);try{let f=await fetch(G(r,"/sourcemaps/upload-archive-multipart/status")+`?uploadId=${encodeURIComponent(b)}`,{method:"GET",headers:w}),j=await f.text();if(f.ok&&j){let K=JSON.parse(j).receivedChunks||[];K.forEach(A=>T.delete(A)),K.length>0&&e.log?.(`Resume: ${K.length}/${E} chunks already uploaded`)}}catch{}for(let f=0;f<E;f++){if(!T.has(f))continue;let j=f*h,V=Math.min(j+h,g),K=I.subarray(j,V),A=new FormData;A.append("uploadId",b),A.append("index",String(f)),A.append("totalChunks",String(E)),A.append("chunk",new Blob([K]),`chunk-${f}`);let le=G(r,"/sourcemaps/upload-archive-multipart/chunk");await Y(le,{method:"POST",headers:m?{"X-Auth-Cert":i}:{"X-Project-Id":d,"X-Secret-Key":n||""},body:A},e),e.log?.(` Chunk ${f+1}/${E} OK`)}let D=(await Y(G(r,"/sourcemaps/upload-archive-multipart/complete"),{method:"POST",headers:{...w,"Content-Type":"application/json"},body:JSON.stringify({uploadId:b})},e)).uploaded??0;return e.log?.("Chunked upload OK, server extracted and saved",D,".map file(s)"),{ok:!0,uploaded:D}}finally{await Oe(v,{recursive:!0,force:!0})}}var Pe,Ue,Ne,ie=N(()=>{W();Pe=1*1024*1024,Ue=3,Ne=1e3});import{readdir as Re,readFile as _e,unlink as ze}from"fs/promises";import{join as Me,basename as P}from"path";async function se(t,e=[]){let a;try{a=await Re(t,{withFileTypes:!0})}catch(r){if(r?.code==="ENOENT")return e;throw r}for(let r of a){let o=Me(t,r.name);r.isDirectory()?await se(o,e):r.name.endsWith(".map")&&e.push(o)}return e}var ce="/sourcemaps/upload";function We(t){let e=t.replace(/\/$/,"");return e.endsWith(ce)?e:e+ce}async function Ge(t){let{endpoint:e,projectId:a,secretKey:r,authCert:o,release:n,buildId:i,filePath:c}=t,p=!!o,y=a||(p?(await Promise.resolve().then(()=>(B(),X))).extractProjectIdFromCert(o):""),h=P(c,".map"),u=await _e(c),m=We(e),l=new FormData;l.append("projectId",y),p||l.append("secretKey",r||""),l.append("release",n),i&&l.append("buildId",i),l.append("filename",h),l.append("file",new Blob([u]),P(c));let d=await fetch(m,{method:"POST",headers:p?{"X-Project-Id":y,"X-Auth-Cert":o}:{"X-Project-Id":y,"X-Secret-Key":r||""},body:l}),x=await d.text();if(!d.ok)throw new Error(`${d.status} ${x}`);return JSON.parse(x||"{}")}async function ct(t,e=console){let{dir:a,endpoint:r,projectId:o,secretKey:n,authCert:i,release:c,buildId:p,deleteAfterUpload:y=!1,deleteEvenOnFailure:h=!1}=t,u=await se(a);if(u.length===0)return e.log("No .map files found in",a),{ok:0,fail:0,deleted:0};let m=y&&h;e.log("Uploading",u.length,"source map(s) to",r,"release=",c,y?m?"(will delete local .map even on failure)":"(will delete local .map only after all succeed)":"");let l=0,s=0;for(let v of u)try{await Ge({endpoint:r,projectId:o,secretKey:n,authCert:i,release:c,buildId:p,filePath:v}),e.log(" OK",P(v)),l++}catch(C){e.error(" FAIL",P(v),C.message),s++}let d=y&&(s===0||h),x=0;if(d&&u.length>0){for(let v of u)try{await ze(v),x++,e.log(" Deleted",P(v))}catch(C){e.error(" Delete failed",v,C.message)}s>0&&h&&e.log("Deleted .map files despite upload failures. Fix errors and re-run to re-upload.")}else s>0&&!h&&e.log("Upload had failures. Local .map files were NOT deleted. Fix errors and re-run.");return{ok:l,fail:s,deleted:x}}async function st(t,e=console){let{uploadArchive:a,deleteMapFiles:r}=await Promise.resolve().then(()=>(W(),q)),{dir:o,endpoint:n,projectId:i,secretKey:c,authCert:p,release:y,buildId:h,deleteEvenOnFailure:u=!1,noEncrypt:m}=t;try{let{uploaded:l}=await a({dir:o,endpoint:n,projectId:i,secretKey:c,authCert:p,release:y,buildId:h,noEncrypt:m},e);if(l===0)return{uploaded:0,deleted:0};let s=await r(o,[]);return e.log("Deleted",s.length,"local .map file(s)."),{uploaded:l,deleted:s.length}}catch(l){if(u)try{let s=await r(o,[]);e.log("Deleted",s.length,"local .map file(s) despite upload failure.")}catch(s){e.error("Delete .map after upload failure failed:",s.message)}else e.error("Upload failed. Local .map files were NOT deleted.",l.message);throw l}}async function lt(t,e=console){let{uploadArchiveChunked:a}=await Promise.resolve().then(()=>(ie(),ne)),{deleteMapFiles:r}=await Promise.resolve().then(()=>(W(),q)),{dir:o,endpoint:n,projectId:i,secretKey:c,authCert:p,release:y,buildId:h,deleteEvenOnFailure:u=!1,noEncrypt:m,chunkSizeBytes:l}=t;try{let{uploaded:s}=await a({dir:o,endpoint:n,projectId:i,secretKey:c,authCert:p,release:y,buildId:h,noEncrypt:m,chunkSizeBytes:l},e);if(s===0)return{uploaded:0,deleted:0};let d=await r(o,[]);return e.log("Deleted",d.length,"local .map file(s)."),{uploaded:s,deleted:d.length}}catch(s){if(u)try{let d=await r(o,[]);e.log("Deleted",d.length,"local .map file(s) despite upload failure.")}catch(d){e.error("Delete .map after upload failure failed:",d.message)}else e.error("Upload failed. Local .map files were NOT deleted.",s.message);throw s}}export{se as findMapFiles,ct as uploadAll,lt as uploadArchiveChunkedThenDelete,st as uploadArchiveThenDelete,Ge as uploadOne};
|
|
1
|
+
var je=Object.defineProperty;var re=(t,e)=>()=>(t&&(e=t(t=0)),e);var oe=(t,e)=>{for(var r in e)je(t,r,{get:e[r],enumerable:!0})};var ae={};oe(ae,{extractProjectIdFromCert:()=>Ue,extractUploadKeyFromCert:()=>Le});function Ue(t){if(typeof t!="string")return"";let e=t.trim();if(e.startsWith(Be+"$"))return e.split("$")[1]||"";if(e.includes(me)){let r=e.match(/Project:\s*(\S+)/);return r?r[1]:""}return""}function Le(t){if(typeof t!="string"||!t.includes(me))return null;let e=t.match(/Upload-Key:\s*([0-9a-fA-F]{64})/);if(!e)return null;try{return Buffer.from(e[1],"hex")}catch{return null}}var Be,me,ne=re(()=>{Be="MC1",me="-----BEGIN MONITOR CERT-----"});var q={};oe(q,{decryptArchive:()=>Xe,encryptArchive:()=>Ne,encryptArchiveWithKey:()=>xe});import{createCipheriv as Ke,createDecipheriv as _e,createHash as ze,randomBytes as Re}from"crypto";function we(t,e){let r=`${t}:${e}`;return ze("sha256").update(r,"utf8").digest()}function Ne(t,e,r){let o=we(e,r);return xe(t,o)}function xe(t,e){if(!Buffer.isBuffer(e)||e.length!==32)throw new Error("Invalid key: must be 32-byte Buffer");let r=Re(se),o=Ke(ye,e,r,{authTagLength:V}),a=Buffer.concat([o.update(t),o.final()]),n=o.getAuthTag();return Buffer.concat([r,a,n])}function Xe(t,e,r){if(t.length<se+V)throw new Error("Invalid encrypted archive: too short");let o=we(e,r),a=t.subarray(0,se),n=t.subarray(t.length-V),s=t.subarray(se,t.length-V),i=_e(ye,o,a,{authTagLength:V});return i.setAuthTag(n),Buffer.concat([i.update(s),i.final()])}var ye,se,V,Y=re(()=>{ye="aes-256-gcm",se=12,V=16});var pe={};oe(pe,{DEFAULT_EXCLUDE_EXT:()=>j,UPLOAD_MODE_BACKUP:()=>ce,UPLOAD_MODE_MAP_ONLY:()=>Ye,collectFiles:()=>ie,createArchive:()=>N,createBackupArchive:()=>le,deleteMapFiles:()=>Oe,uploadArchive:()=>Qe});import{createRequire as We}from"module";import{readdir as Ce,unlink as ke,readFile as Ge,mkdtemp as $e,rm as be,mkdir as He,copyFile as ve,writeFile as Je}from"fs/promises";import{join as T,relative as Ve,dirname as ge}from"path";import{createWriteStream as Ee}from"fs";import{tmpdir as Me}from"os";async function ie(t,e=t,r={}){let{excludeExt:o=j,mapOnly:a=!1,excludeMap:n=!1}=typeof r=="object"&&r!==null?r:{},s=await Ce(t,{withFileTypes:!0}),i=[];for(let u of s){let p=T(t,u.name),y=Ve(e,p).replace(/\\/g,"/");if(u.isDirectory()){let g=await ie(p,e,r);i.push(...g)}else{if(a&&!u.name.endsWith(".map")||!a&&n&&u.name.endsWith(".map"))continue;if(!a&&o&&o.size>0){let g=(u.name.includes(".")?"."+u.name.split(".").pop():"").toLowerCase();if(o.has(g))continue}i.push({relativePath:y,fullPath:p})}}return i}async function N(t,e,r={},o=!0){let a={};r instanceof Set?(a={excludeExt:r,mapOnly:!1},o=arguments[3]!==void 0?arguments[3]:!0):typeof r=="object"&&r!==null&&(a=r);let n=await ie(t,t,a),s=n.map(i=>i.relativePath);return s.length===0?0:new Promise((i,u)=>{let p=Ie.create({gzip:o,cwd:t},s);p.on("error",u),p.pipe(Ee(e)).on("close",()=>i(n.length)).on("error",u)})}async function le(t,e,r={}){let{excludeExt:o=j,includeMapInOutput:a=!0}=typeof r=="object"&&r!==null?r:{},n=await $e(T(Me(),"upload-sourcemaps-backup-")),s=ge(e),i=T(s,"project-code.tar.gz"),u=T(n,"project-code.tar.gz");try{let p=await N(t,u,{excludeExt:o||j,mapOnly:!1,excludeMap:!0},!0);if(p===0){let f=T(n,".keep");await Je(f,""),await N(n,u,{excludeExt:new Set,mapOnly:!1},!0),await ke(f).catch(()=>{})}else await ve(u,i);let y=[];if(a){y=await ie(t,t,{mapOnly:!0});for(let f of y){let h=T(n,f.relativePath);await He(ge(h),{recursive:!0}),await ve(f.fullPath,h)}}let g=["project-code.tar.gz",...y.map(f=>f.relativePath)];return await Ze(n,e,g,!0),{codeFileCount:p,mapFileCount:y.length,codeTarPath:p>0?i:void 0}}finally{await be(n,{recursive:!0,force:!0})}}function Ze(t,e,r,o=!0){return r.length===0?Promise.resolve(0):new Promise((a,n)=>{let s=Ie.create({gzip:o,cwd:t},r);s.on("error",n),s.pipe(Ee(e)).on("close",()=>a(r.length)).on("error",n)})}async function Qe(t,e=console){let{dir:r,endpoint:o,projectId:a,secretKey:n,authCert:s,release:i,buildId:u,excludeExt:p,noEncrypt:y,uploadMode:g,keepMapInOutput:f}=t,h=!!s,{extractProjectIdFromCert:$,extractUploadKeyFromCert:k}=await Promise.resolve().then(()=>(ne(),ae)),m=a||(h?$(s):""),x=h?k(s):null,B=await $e(T(Me(),"upload-sourcemaps-")),S=T(B,"archive.tar.gz"),I=g===ce,U=I?null:{excludeExt:p||j,mapOnly:!0};try{let F;if(I){let{codeFileCount:v,mapFileCount:E}=await le(r,S,{excludeExt:p||j,includeMapInOutput:f!==!1});F=Math.max(1,E),f!==!1?e.log("Created backup archive: project-code.tar.gz (",v," files for rollback) + ",E," .map file(s), uploading..."):e.log("Created backup archive: project-code.tar.gz (",v," files for rollback, no .map per project config), uploading...")}else F=await N(r,S,U,!0);if(F===0)return e.log("No files to pack (directory empty or only excluded). Skip upload."),{ok:!0,uploaded:0};let b=await Ge(S),M=!y&&(h?x!==null:!!(m&&n));if(M)if(x){let{encryptArchiveWithKey:v}=await Promise.resolve().then(()=>(Y(),q));b=v(b,x),I||e.log("Created tar.gz with",F,"file(s), encrypted with cert Upload-Key, uploading...")}else{let{encryptArchive:v}=await Promise.resolve().then(()=>(Y(),q));b=v(b,m,n),I||e.log("Created tar.gz with",F,"file(s), encrypted, uploading...")}else I||e.log("Created tar.gz with",F,"file(s), uploading...");let D="/sourcemaps/upload-archive",O=o.replace(/\/$/,""),P=O.endsWith(D)?O:O+D,l="----UploadSourceMaps"+Date.now().toString(36)+Math.random().toString(36).slice(2),c=`\r
|
|
2
|
+
`,A=Buffer.isBuffer(b)?b:Buffer.from(b),K=M?"archive.tar.gz.enc":"archive.tar.gz",_=Buffer.from([`--${l}${c}`,`Content-Disposition: form-data; name="projectId"${c}${c}`,m+c,`--${l}${c}`,`Content-Disposition: form-data; name="secretKey"${c}${c}`,(h?"":n||"")+c,`--${l}${c}`,`Content-Disposition: form-data; name="release"${c}${c}`,i+c,`--${l}${c}`,`Content-Disposition: form-data; name="buildId"${c}${c}`,(u||"")+c,`--${l}${c}`,`Content-Disposition: form-data; name="encrypted"${c}${c}`,(M?"1":"")+c,`--${l}${c}`,`Content-Disposition: form-data; name="uploadMode"${c}${c}`,(g||"")+c,`--${l}${c}`,`Content-Disposition: form-data; name="file"; filename="${K}"${c}`,`Content-Type: application/octet-stream${c}${c}`].join(""),"utf-8"),X=Buffer.from(c+`--${l}--${c}`,"utf-8"),W=_.length+A.length+X.length,G=(W/1024/1024).toFixed(2),L=64*1024,Q=3e3,H=0,J=0,ee=v=>{if(typeof process<"u"&&process.stderr&&process.stderr.write){let E=(H/1024/1024).toFixed(2);process.stderr.write(`\r \u4E0A\u4F20\u8FDB\u5EA6 \u5F52\u6863 ${E}/${G} MB (${v}%) `)}},te=()=>{let v=W?Math.min(100,Math.round(H/W*100)):0,E=Date.now();(v>=100||E-J>=Q)&&(J=E,ee(v))},he=new ReadableStream({start(v){let E=w=>{v.enqueue(w),H+=w.length,te()};for(let w=0;w<_.length;w+=L)E(_.subarray(w,Math.min(w+L,_.length)));for(let w=0;w<A.length;w+=L)E(A.subarray(w,Math.min(w+L,A.length)));for(let w=0;w<X.length;w+=L)E(X.subarray(w,Math.min(w+L,X.length)));v.close()}}),z={"X-Project-Id":m,"Content-Type":`multipart/form-data; boundary=${l}`,"Content-Length":String(W)};h?z["X-Auth-Cert"]=s:z["X-Secret-Key"]=n||"";let d=await fetch(P,{method:"POST",headers:z,body:he,duplex:"half"}),C=await d.text();if(!d.ok)throw new Error(`${d.status} ${C}`);typeof process<"u"&&process.stderr&&process.stderr.write&&(ee(100),process.stderr.write(`
|
|
3
|
+
`));let R=JSON.parse(C||"{}");return e.log("Upload archive OK, server extracted and saved",R.uploaded,".map file(s)"),{ok:!0,uploaded:R.uploaded||0}}finally{await be(B,{recursive:!0,force:!0})}}async function Oe(t,e=[]){let r=await Ce(t,{withFileTypes:!0});for(let o of r){let a=T(t,o.name);o.isDirectory()?await Oe(a,e):o.name.endsWith(".map")&&(await ke(a),e.push(a))}return e}var qe,Ie,j,Ye,ce,de=re(()=>{qe=We(import.meta.url),Ie=qe("tar"),j=new Set([".png",".jpg",".jpeg",".gif",".svg",".webp",".ico",".bmp",".avif",".woff",".woff2",".ttf",".eot",".otf",".mp4",".webm",".ogg",".mp3",".wav",".m4a",".aac",".pdf",".doc",".docx",".xls",".xlsx"]),Ye="mapOnly",ce="backup"});var De={};oe(De,{uploadArchiveChunked:()=>lt});import{readFile as et,mkdtemp as tt,rm as rt}from"fs/promises";import{join as Se}from"path";import{tmpdir as ot}from"os";function ct(t){return t.replace(/\/$/,"")}function ue(t,e){let r=ct(t),o=e.startsWith("/")?e:`/${e}`;return r.endsWith(o)?r:r+o}async function it(t){return new Promise(e=>setTimeout(e,t))}async function fe(t,e,r,o=nt){let a;for(let n=0;n<=o;n++)try{let s=await fetch(t,e),i=await s.text();if(!s.ok)throw new Error(`${s.status} ${i}`);return i?JSON.parse(i):{}}catch(s){if(a=s,n<o){let i=st*Math.pow(2,n);r.log?.(` Retry ${n+1}/${o} after ${i}ms: ${s.message}`),await it(i)}}throw a}async function lt(t,e=console){let{dir:r,endpoint:o,projectId:a,secretKey:n,authCert:s,release:i,buildId:u,noEncrypt:p,chunkSizeBytes:y=at,excludeExt:g,uploadMode:f,keepMapInOutput:h}=t,$=!!s,{extractProjectIdFromCert:k,extractUploadKeyFromCert:m}=await Promise.resolve().then(()=>(ne(),ae)),x=a||($?k(s):""),B=$?m(s):null,S=await tt(Se(ot(),"upload-sourcemaps-chunked-")),I=Se(S,"archive.tar.gz"),U=f===ce,F=U?null:{excludeExt:g||j,mapOnly:!0};try{let b;if(U){let{codeFileCount:d,mapFileCount:C}=await le(r,I,{excludeExt:g||j,includeMapInOutput:h!==!1});b=Math.max(1,C),h!==!1?e.log?.("Created backup archive: project-code.tar.gz (",d," files for rollback) + ",C," .map file(s), uploading in chunks..."):e.log?.("Created backup archive: project-code.tar.gz (",d," files for rollback, no .map per project config), uploading in chunks...")}else b=await N(r,I,F,!0);if(b===0)return e.log?.("No files to pack. Skip upload."),{ok:!0,uploaded:0};let M=await et(I),D=!p&&($?B!==null:!!(x&&n));if(D){if(B){let{encryptArchiveWithKey:d}=await Promise.resolve().then(()=>(Y(),q));M=d(M,B)}else{let{encryptArchive:d}=await Promise.resolve().then(()=>(Y(),q));M=d(M,x,n)}U||e.log?.(`Created tar.gz with ${b} file(s), encrypted, uploading in chunks...`)}else U||e.log?.(`Created tar.gz with ${b} file(s), uploading in chunks...`);let O=M.length,P=Math.ceil(O/y),l={"Content-Type":"application/json"};$?l["X-Auth-Cert"]=s:(l["X-Project-Id"]=x,l["X-Secret-Key"]=n||"");let A=(await fe(ue(o,"/sourcemaps/upload-archive-multipart/init"),{method:"POST",headers:{...l,"Content-Type":"application/json"},body:JSON.stringify({projectId:x,secretKey:$?"":n||"",release:i,buildId:u||void 0,totalChunks:P,chunkSizeBytes:y,encrypted:D,uploadMode:f||void 0})},e)).uploadId;if(!A)throw new Error("Init did not return uploadId");let K=new Set;for(let d=0;d<P;d++)K.add(d);try{let d=await fetch(ue(o,"/sourcemaps/upload-archive-multipart/status")+`?uploadId=${encodeURIComponent(A)}`,{method:"GET",headers:l}),C=await d.text();if(d.ok&&C){let v=JSON.parse(C).receivedChunks||[];v.forEach(E=>K.delete(E)),v.length>0&&e.log?.(`Resume: ${v.length}/${P} chunks already uploaded`)}}catch{}let _=3e3,G=(P-K.size)*y,L=(O/1024/1024).toFixed(2),Q=()=>{let d=O?Math.min(100,Math.round(G/O*100)):0,C=(G/1024/1024).toFixed(2);typeof process<"u"&&process.stderr&&process.stderr.write&&process.stderr.write(`\r \u4E0A\u4F20\u8FDB\u5EA6 \u5206\u7247 ${C}/${L} MB (${d}%) `)},H=setInterval(Q,_),J=Math.min(8,P),ee=async d=>{let C=d*y,R=Math.min(C+y,O),v=R-C,E=M.subarray(C,R),w=new FormData;w.append("uploadId",A),w.append("index",String(d)),w.append("totalChunks",String(P)),w.append("chunk",new Blob([E]),`chunk-${d}`);let Te=ue(o,"/sourcemaps/upload-archive-multipart/chunk");await fe(Te,{method:"POST",headers:$?{"X-Auth-Cert":s}:{"X-Project-Id":x,"X-Secret-Key":n||""},body:w},e),G+=v},te=[...K].sort((d,C)=>d-C);for(let d=0;d<te.length;d+=J){let C=te.slice(d,d+J);await Promise.all(C.map(ee))}clearInterval(H),O>0&&typeof process<"u"&&process.stderr&&process.stderr.write&&(Q(),process.stderr.write(`
|
|
4
|
+
`));let z=(await fe(ue(o,"/sourcemaps/upload-archive-multipart/complete"),{method:"POST",headers:{...l,"Content-Type":"application/json"},body:JSON.stringify({uploadId:A})},e)).uploaded??0;return e.log?.("Chunked upload OK, server extracted and saved",z,".map file(s)"),{ok:!0,uploaded:z}}finally{await rt(S,{recursive:!0,force:!0})}}var at,nt,st,Pe=re(()=>{de();at=1*1024*1024,nt=3,st=1e3});import{readdir as dt,readFile as ut,unlink as pt,stat as ft}from"fs/promises";import{join as ht,basename as Z}from"path";var mt=3e3;async function yt(t,e=12){let r=[],o=0;async function a(){for(;o<t.length;){let s=o++;try{r[s]=await t[s]()}catch(i){r[s]=Promise.reject(i)}}}let n=Array.from({length:Math.min(e,t.length)},()=>a());return await Promise.all(n),r}async function Fe(t,e=[]){let r;try{r=await dt(t,{withFileTypes:!0})}catch(o){if(o?.code==="ENOENT")return e;throw o}for(let o of r){let a=ht(t,o.name);o.isDirectory()?await Fe(a,e):o.name.endsWith(".map")&&e.push(a)}return e}var Ae="/sourcemaps/upload";function wt(t){let e=t.replace(/\/$/,"");return e.endsWith(Ae)?e:e+Ae}async function xt(t){let{endpoint:e,projectId:r,secretKey:o,authCert:a,release:n,buildId:s,filePath:i}=t,u=!!a,p=r||(u?(await Promise.resolve().then(()=>(ne(),ae))).extractProjectIdFromCert(a):""),y=Z(i,".map"),g=await ut(i),f=wt(e),h=new FormData;h.append("projectId",p),u||h.append("secretKey",o||""),h.append("release",n),s&&h.append("buildId",s),h.append("filename",y),h.append("file",new Blob([g]),Z(i));let k=await fetch(f,{method:"POST",headers:u?{"X-Project-Id":p,"X-Auth-Cert":a}:{"X-Project-Id":p,"X-Secret-Key":o||""},body:h}),m=await k.text();if(!k.ok)throw new Error(`${k.status} ${m}`);return JSON.parse(m||"{}")}async function Ft(t,e=console){let{dir:r,endpoint:o,projectId:a,secretKey:n,authCert:s,release:i,buildId:u,deleteAfterUpload:p=!1,deleteEvenOnFailure:y=!1,concurrency:g=12}=t,f=await Fe(r);if(f.length===0)return e.log("No .map files found in",r),{ok:0,fail:0,deleted:0};let h=Math.max(1,Math.min(64,Number(g)||12)),$=p&&y,k=f.length,m=await Promise.all(f.map(l=>ft(l).then(c=>c.size))),x=m.reduce((l,c)=>l+c,0),B=(x/1024/1024).toFixed(2);e.log("Uploading",k,"source map(s) to",o,"release=",i,"concurrency=",h,p?$?"(will delete local .map even on failure)":"(will delete local .map only after all succeed)":"");let S=0,I=()=>{let l=x?Math.min(100,Math.round(S/x*100)):0,c=(S/1024/1024).toFixed(2);typeof process<"u"&&process.stderr&&process.stderr.write&&process.stderr.write(`\r \u4E0A\u4F20\u8FDB\u5EA6 ${c}/${B} MB (${l}%) `)},U=setInterval(I,mt),F=f.map((l,c)=>()=>xt({endpoint:o,projectId:a,secretKey:n,authCert:s,release:i,buildId:u,filePath:l}).then(()=>(S+=m[c],{ok:!0,name:Z(l)})).catch(A=>(S+=m[c],{ok:!1,name:Z(l),err:A}))),b=await yt(F,h);clearInterval(U),x>0&&process.stderr&&process.stderr.write&&(I(),process.stderr.write(`
|
|
5
|
+
`));let M=0,D=0;for(let l of b)l.ok?(e.log(" OK",l.name),M++):(e.error(" FAIL",l.name,l.err?.message||l.err),D++);let O=p&&(D===0||y),P=0;if(O&&f.length>0){for(let l of f)try{await pt(l),P++,e.log(" Deleted",Z(l))}catch(c){e.error(" Delete failed",l,c.message)}D>0&&y&&e.log("Deleted .map files despite upload failures. Fix errors and re-run to re-upload.")}else D>0&&!y&&e.log("Upload had failures. Local .map files were NOT deleted. Fix errors and re-run.");return{ok:M,fail:D,deleted:P}}async function Tt(t,e=console){let{uploadArchive:r,deleteMapFiles:o}=await Promise.resolve().then(()=>(de(),pe)),{dir:a,endpoint:n,projectId:s,secretKey:i,authCert:u,release:p,buildId:y,deleteEvenOnFailure:g=!1,noEncrypt:f,uploadMode:h,excludeExt:$}=t;try{let{uploaded:k}=await r({dir:a,endpoint:n,projectId:s,secretKey:i,authCert:u,release:p,buildId:y,noEncrypt:f,uploadMode:h,excludeExt:$},e);if(k===0)return{uploaded:0,deleted:0};let m=await o(a,[]);return e.log("Deleted",m.length,"local .map file(s)."),{uploaded:k,deleted:m.length}}catch(k){if(g)try{let m=await o(a,[]);e.log("Deleted",m.length,"local .map file(s) despite upload failure.")}catch(m){e.error("Delete .map after upload failure failed:",m.message)}else e.error("Upload failed. Local .map files were NOT deleted.",k.message);throw k}}async function jt(t,e=console){let{uploadArchiveChunked:r}=await Promise.resolve().then(()=>(Pe(),De)),{deleteMapFiles:o}=await Promise.resolve().then(()=>(de(),pe)),{dir:a,endpoint:n,projectId:s,secretKey:i,authCert:u,release:p,buildId:y,deleteEvenOnFailure:g=!1,noEncrypt:f,chunkSizeBytes:h,uploadMode:$,excludeExt:k}=t;try{let{uploaded:m}=await r({dir:a,endpoint:n,projectId:s,secretKey:i,authCert:u,release:p,buildId:y,noEncrypt:f,chunkSizeBytes:h,uploadMode:$,excludeExt:k},e);if(m===0)return{uploaded:0,deleted:0};let x=await o(a,[]);return e.log("Deleted",x.length,"local .map file(s)."),{uploaded:m,deleted:x.length}}catch(m){if(g)try{let x=await o(a,[]);e.log("Deleted",x.length,"local .map file(s) despite upload failure.")}catch(x){e.error("Delete .map after upload failure failed:",x.message)}else e.error("Upload failed. Local .map files were NOT deleted.",m.message);throw m}}export{Fe as findMapFiles,Ft as uploadAll,jt as uploadArchiveChunkedThenDelete,Tt as uploadArchiveThenDelete,xt as uploadOne};
|
package/package.json
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@frontend-monitor/upload-sourcemaps",
|
|
3
|
-
"version": "0.0
|
|
3
|
+
"version": "0.2.0",
|
|
4
4
|
"description": "CLI to upload Source Map files to frontend-monitor API (for CI/CD)",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/upload.js",
|
|
7
7
|
"bin": {
|
|
8
|
-
"upload-sourcemaps": "./
|
|
8
|
+
"upload-sourcemaps": "./bin/upload-sourcemaps.js"
|
|
9
9
|
},
|
|
10
10
|
"scripts": {
|
|
11
11
|
"build": "node build.js",
|
|
@@ -16,7 +16,8 @@
|
|
|
16
16
|
"files": [
|
|
17
17
|
"dist",
|
|
18
18
|
"README.md",
|
|
19
|
-
"README.zh-CN.md"
|
|
19
|
+
"README.zh-CN.md",
|
|
20
|
+
"CHANGELOG.md"
|
|
20
21
|
],
|
|
21
22
|
"devDependencies": {
|
|
22
23
|
"esbuild": "^0.24.2"
|