@guidelytech/sourcemaps 0.15.0 → 0.17.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/README.md +43 -0
- package/dist/index.d.ts +98 -1
- package/dist/index.js +314 -21
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -20,6 +20,7 @@ npx @guidelytech/sourcemaps upload --dir dist --release "$GIT_SHA" --delete-afte
|
|
|
20
20
|
```
|
|
21
21
|
guidely-sourcemaps upload --dir <dir> [--url-prefix /] [--release <r>] [--api-url <url>] [--key <key>]
|
|
22
22
|
[--include .js.map] [--delete-after] [--dry-run]
|
|
23
|
+
guidely-sourcemaps inject --dir <dir> [--include .js.map] [--dry-run]
|
|
23
24
|
guidely-sourcemaps verify [--key <key>] [--api-url <url>]
|
|
24
25
|
```
|
|
25
26
|
|
|
@@ -43,6 +44,48 @@ prints one line per map and a summary, and exits with code 1 if any map failed,
|
|
|
43
44
|
|
|
44
45
|
`verify` calls `GET /api/v1/sourcemaps/whoami` and prints the project and key prefix.
|
|
45
46
|
|
|
47
|
+
## Debug ids (Sentry SDKs)
|
|
48
|
+
|
|
49
|
+
A debug id ties a minified file to its map whatever URL or release it is served under. Sentry SDKs
|
|
50
|
+
read it from the file and send it with every error, and Guidely matches maps by debug id before
|
|
51
|
+
release + path.
|
|
52
|
+
|
|
53
|
+
```sh
|
|
54
|
+
npx @guidelytech/sourcemaps inject --dir dist # after the build, before upload and deploy
|
|
55
|
+
npx @guidelytech/sourcemaps upload --dir dist --delete-after
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
`inject` does what `sentry-cli sourcemaps inject` does: it adds a one-line snippet at the top of each
|
|
59
|
+
minified file that has a map (recording the id in `globalThis._sentryDebugIds`), a `//# debugId=…`
|
|
60
|
+
comment at its end, and `debug_id` / `debugId` to the map (moving its mappings down one line). The id
|
|
61
|
+
is derived from the file's content, so rebuilding the same code gives the same id; files that already
|
|
62
|
+
have one are left alone. `upload` sends each map's debug id (from `debug_id` / `debugId`), so maps
|
|
63
|
+
injected by `sentry-cli` or the Sentry bundler plugins work too. Guidely's own browser SDK matches by
|
|
64
|
+
release + path as before.
|
|
65
|
+
## Native debug files (iOS, Android, Flutter)
|
|
66
|
+
|
|
67
|
+
Crashes from native code carry addresses and obfuscated names until Guidely has the matching debug
|
|
68
|
+
files. Use a key with the **debug-files:write** scope (same `GUIDELY_SOURCEMAP_KEY` / `--key`):
|
|
69
|
+
|
|
70
|
+
```sh
|
|
71
|
+
# iOS dSYMs (Xcode archive, or the dSYM folder of a build), Mach-O and ELF files, Flutter symbols
|
|
72
|
+
npx @guidelytech/sourcemaps upload-dif build/MyApp.xcarchive/dSYMs
|
|
73
|
+
npx @guidelytech/sourcemaps upload-dif build/app/outputs/symbols # flutter build --split-debug-info=build/app/outputs/symbols
|
|
74
|
+
# Android ProGuard / R8 mapping
|
|
75
|
+
npx @guidelytech/sourcemaps upload-proguard app/build/outputs/mapping/release/mapping.txt --app-version 2.4.1
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
| Option | | |
|
|
79
|
+
|---|---|---|
|
|
80
|
+
| `--app-version` | `upload-dif`, `upload-proguard` | The app version the files belong to. A crash that names no mapping UUID is retraced with the newest mapping for its app version. |
|
|
81
|
+
| `--uuid` | `upload-proguard` | The mapping UUID the build embedded (default: derived from the file, like sentry-cli). |
|
|
82
|
+
| `--write-properties <path>` | `upload-proguard` | Also writes `io.guidely.ProguardUuids=<uuid>`, e.g. to `app/src/main/assets/guidely-debug-meta.properties`, so the Android SDK reports the exact mapping. |
|
|
83
|
+
| `--dry-run` | both | List what would be uploaded. |
|
|
84
|
+
|
|
85
|
+
Files are uploaded in SHA-1 addressed, gzipped chunks (only the chunks the server doesn't have yet)
|
|
86
|
+
and assembled server-side, the protocol sentry-cli uses, so `sentry-cli debug-files upload` and
|
|
87
|
+
`sentry-cli upload-proguard` work against Guidely too (docs/error-monitoring/sdks.md). Up to 256 MB per file.
|
|
88
|
+
|
|
46
89
|
## Frameworks
|
|
47
90
|
|
|
48
91
|
| Build | Enable maps | Upload |
|
package/dist/index.d.ts
CHANGED
|
@@ -12,6 +12,69 @@ interface CliIO {
|
|
|
12
12
|
/** Runs the CLI; resolves with the process exit code. Never prints the API key. */
|
|
13
13
|
declare function runCli(argv: string[], io?: CliIO): Promise<number>;
|
|
14
14
|
|
|
15
|
+
interface DebugFileLogger {
|
|
16
|
+
info(message: string): void;
|
|
17
|
+
warn(message: string): void;
|
|
18
|
+
}
|
|
19
|
+
interface DebugFileUploadOptions {
|
|
20
|
+
apiUrl?: string;
|
|
21
|
+
/** Secret project API key with the `debug-files:write` scope. */
|
|
22
|
+
key?: string;
|
|
23
|
+
/** App version the files belong to (ProGuard mappings fall back to it when an event names no mapping UUID). */
|
|
24
|
+
appVersion?: string;
|
|
25
|
+
dryRun?: boolean;
|
|
26
|
+
logger?: DebugFileLogger;
|
|
27
|
+
fetch?: typeof fetch;
|
|
28
|
+
retries?: number;
|
|
29
|
+
retryDelayMs?: number;
|
|
30
|
+
/** Assemble polls before giving up (default 30, one per second). */
|
|
31
|
+
maxPolls?: number;
|
|
32
|
+
}
|
|
33
|
+
interface DebugFileCandidate {
|
|
34
|
+
path: string;
|
|
35
|
+
/** Name sent to the server (path relative to the searched directory, `MyApp.app.dSYM/Contents/Resources/DWARF/MyApp`). */
|
|
36
|
+
name: string;
|
|
37
|
+
kind: "macho" | "elf" | "proguard";
|
|
38
|
+
sizeBytes: number;
|
|
39
|
+
}
|
|
40
|
+
interface DebugFileUploadResult {
|
|
41
|
+
files: DebugFileCandidate[];
|
|
42
|
+
uploaded: Array<{
|
|
43
|
+
name: string;
|
|
44
|
+
debugId: string;
|
|
45
|
+
kind: string;
|
|
46
|
+
arch: string;
|
|
47
|
+
}>;
|
|
48
|
+
errors: Array<{
|
|
49
|
+
name: string;
|
|
50
|
+
message: string;
|
|
51
|
+
}>;
|
|
52
|
+
}
|
|
53
|
+
declare const MAX_DEBUG_FILE_BYTES: number;
|
|
54
|
+
/** Mach-O, universal (fat) Mach-O or ELF, from the first bytes of a file. */
|
|
55
|
+
declare function nativeKind(head: Buffer): "macho" | "elf" | null;
|
|
56
|
+
/** Mach-O and ELF files under the given paths (dSYM bundles, .xcarchive/dSYMs, symbols directories), recursively. */
|
|
57
|
+
declare function findDebugFiles(paths: string[], logger?: DebugFileLogger): DebugFileCandidate[];
|
|
58
|
+
/** UUID v5 of a mapping's contents (namespace uuid5(DNS, "guardsquare.com")), as sentry-cli and the server derive it. */
|
|
59
|
+
declare function proguardUuid(content: Buffer): string;
|
|
60
|
+
/** Writes `io.guidely.ProguardUuids=<uuid>` for the Android SDK to read from its assets. */
|
|
61
|
+
declare function writeProguardProperties(path: string, uuid: string): void;
|
|
62
|
+
/** Finds and uploads native debug files (dSYM DWARF files, Mach-O, ELF). */
|
|
63
|
+
declare function uploadDebugFiles(paths: string[], o?: DebugFileUploadOptions): Promise<DebugFileUploadResult>;
|
|
64
|
+
/**
|
|
65
|
+
* Uploads a ProGuard / R8 `mapping.txt`. Its UUID is `uuid` (when the build embedded one, e.g. with
|
|
66
|
+
* `--write-properties` before the build) or derived from the contents; the app version lets events
|
|
67
|
+
* without a UUID find it too.
|
|
68
|
+
*/
|
|
69
|
+
declare function uploadProguardMapping(path: string, o?: DebugFileUploadOptions & {
|
|
70
|
+
uuid?: string;
|
|
71
|
+
writeProperties?: string;
|
|
72
|
+
}): Promise<{
|
|
73
|
+
uuid: string;
|
|
74
|
+
uploaded: boolean;
|
|
75
|
+
error?: string;
|
|
76
|
+
}>;
|
|
77
|
+
|
|
15
78
|
/** Per-request limits (uncompressed) and the per-file cap enforced by the upload API. Maps are sent gzipped. */
|
|
16
79
|
declare const MAX_FILES_PER_REQUEST = 50;
|
|
17
80
|
declare const MAX_BYTES_PER_REQUEST: number;
|
|
@@ -104,6 +167,8 @@ declare function batchFiles<T extends {
|
|
|
104
167
|
}>(files: T[], maxFiles?: number, maxBytes?: number): T[][];
|
|
105
168
|
/** Returns why a map file cannot be uploaded, or null when it is a valid v3 source map. */
|
|
106
169
|
declare function invalidMapReason(content: string): string | null;
|
|
170
|
+
/** A map's debug id (`debug_id` or `debugId`, as sentry-cli and the bundler plugins write it), lowercased, or null. */
|
|
171
|
+
declare function mapDebugId(content: string): string | null;
|
|
107
172
|
/** The map re-serialised without `sourcesContent`, or null when it has none (or is not JSON). */
|
|
108
173
|
declare function withoutSourcesContent(content: string): string | null;
|
|
109
174
|
/** Finds the maps under `dir` and uploads them in batches. Throws {@link GuidelyAuthError} for a rejected key. */
|
|
@@ -118,5 +183,37 @@ interface WhoAmI {
|
|
|
118
183
|
}
|
|
119
184
|
/** Checks a key against the API (`guidely-sourcemaps verify`). */
|
|
120
185
|
declare function verifyKey(options: Pick<UploadOptions, "apiUrl" | "key" | "fetch" | "retries" | "retryDelayMs">): Promise<WhoAmI>;
|
|
186
|
+
/**
|
|
187
|
+
* The snippet `sentry-cli sourcemaps inject` writes: it records the file's debug id under its own
|
|
188
|
+
* stack trace in `_sentryDebugIds`, where Sentry SDKs find it and send it with errors.
|
|
189
|
+
*/
|
|
190
|
+
declare const debugIdSnippet: (id: string) => string;
|
|
191
|
+
/** A deterministic debug id for a file's contents (a UUID), so rebuilding the same code gives the same id. */
|
|
192
|
+
declare function debugIdFor(content: string): string;
|
|
193
|
+
interface InjectResult {
|
|
194
|
+
injected: Array<{
|
|
195
|
+
filePath: string;
|
|
196
|
+
mapPath: string;
|
|
197
|
+
debugId: string;
|
|
198
|
+
}>;
|
|
199
|
+
/** Files that already had a debug id (kept; their map gets it when missing). */
|
|
200
|
+
existing: Array<{
|
|
201
|
+
filePath: string;
|
|
202
|
+
debugId: string;
|
|
203
|
+
}>;
|
|
204
|
+
skipped: SkippedFile[];
|
|
205
|
+
}
|
|
206
|
+
/**
|
|
207
|
+
* Gives every minified file that has a map a debug id, like `sentry-cli sourcemaps inject`: the
|
|
208
|
+
* snippet as the file's first line, a `//# debugId=` comment at its end, and `debug_id` / `debugId`
|
|
209
|
+
* in the map (whose mappings move down one line for the snippet). Run it on the build output before
|
|
210
|
+
* uploading and deploying; uploads then carry the debug id and errors find their map by it.
|
|
211
|
+
*/
|
|
212
|
+
declare function injectDebugIds(options: {
|
|
213
|
+
dir: string;
|
|
214
|
+
include?: string;
|
|
215
|
+
dryRun?: boolean;
|
|
216
|
+
logger?: Logger;
|
|
217
|
+
}): InjectResult;
|
|
121
218
|
|
|
122
|
-
export { type CliIO, DEFAULT_API_URL, GuidelyAuthError, type Logger, MAX_BYTES_PER_REQUEST, MAX_FILES_PER_REQUEST, MAX_FILE_BYTES, type SkippedFile, type SourceMapFile, type UploadOptions, type UploadResult, type UploadedMap, type WhoAmI, batchFiles, findSourceMaps, invalidMapReason, runCli, uploadSourceMaps, urlPathFor, verifyKey, withoutSourcesContent };
|
|
219
|
+
export { type CliIO, DEFAULT_API_URL, type DebugFileCandidate, type DebugFileUploadOptions, type DebugFileUploadResult, GuidelyAuthError, type InjectResult, type Logger, MAX_BYTES_PER_REQUEST, MAX_DEBUG_FILE_BYTES, MAX_FILES_PER_REQUEST, MAX_FILE_BYTES, type SkippedFile, type SourceMapFile, type UploadOptions, type UploadResult, type UploadedMap, type WhoAmI, batchFiles, debugIdFor, debugIdSnippet, findDebugFiles, findSourceMaps, injectDebugIds, invalidMapReason, mapDebugId, nativeKind, proguardUuid, runCli, uploadDebugFiles, uploadProguardMapping, uploadSourceMaps, urlPathFor, verifyKey, withoutSourcesContent, writeProguardProperties };
|
package/dist/index.js
CHANGED
|
@@ -1,14 +1,187 @@
|
|
|
1
1
|
// src/index.ts
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
2
|
+
import { createHash as createHash2 } from "crypto";
|
|
3
|
+
import { readdirSync as readdirSync2, readFileSync as readFileSync2, statSync as statSync2, unlinkSync, existsSync as existsSync2, writeFileSync as writeFileSync2 } from "fs";
|
|
4
|
+
import { gzipSync as gzipSync2 } from "zlib";
|
|
5
|
+
import { basename as basename2, join as join2, relative as relative2, sep as sep2 } from "path";
|
|
5
6
|
|
|
6
7
|
// src/constants.ts
|
|
7
8
|
var DEFAULT_API_URL = "https://api.guidely.app";
|
|
8
9
|
|
|
10
|
+
// src/debug-files.ts
|
|
11
|
+
import { createHash } from "crypto";
|
|
12
|
+
import { closeSync, existsSync, mkdirSync, openSync, readFileSync, readSync, readdirSync, statSync, writeFileSync } from "fs";
|
|
13
|
+
import { basename, dirname, join, relative, sep } from "path";
|
|
14
|
+
import { gzipSync } from "zlib";
|
|
15
|
+
var MAX_DEBUG_FILE_BYTES = 256 * 1024 * 1024;
|
|
16
|
+
var sha1 = (b) => createHash("sha1").update(b).digest("hex");
|
|
17
|
+
var sleep = (ms) => new Promise((r) => setTimeout(r, ms));
|
|
18
|
+
function nativeKind(head) {
|
|
19
|
+
if (head.length < 8) return null;
|
|
20
|
+
const le = head.readUInt32LE(0);
|
|
21
|
+
if (le === 4277009102 || le === 4277009103) return "macho";
|
|
22
|
+
const be = head.readUInt32BE(0);
|
|
23
|
+
if ((be === 3405691582 || be === 3405691583) && head.readUInt32BE(4) > 0 && head.readUInt32BE(4) <= 32) return "macho";
|
|
24
|
+
if (be === 2135247942) return "elf";
|
|
25
|
+
return null;
|
|
26
|
+
}
|
|
27
|
+
function readHead(path) {
|
|
28
|
+
const fd = openSync(path, "r");
|
|
29
|
+
try {
|
|
30
|
+
const b = Buffer.alloc(8);
|
|
31
|
+
const n = readSync(fd, b, 0, 8, 0);
|
|
32
|
+
return b.subarray(0, n);
|
|
33
|
+
} finally {
|
|
34
|
+
closeSync(fd);
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
function findDebugFiles(paths, logger) {
|
|
38
|
+
const out = [];
|
|
39
|
+
const visit = (path, root) => {
|
|
40
|
+
const st = statSync(path);
|
|
41
|
+
if (st.isDirectory()) {
|
|
42
|
+
for (const entry of readdirSync(path).sort()) if (entry !== "node_modules" && !entry.startsWith(".")) visit(join(path, entry), root);
|
|
43
|
+
return;
|
|
44
|
+
}
|
|
45
|
+
if (!st.isFile() || st.size < 64) return;
|
|
46
|
+
const kind = nativeKind(readHead(path));
|
|
47
|
+
if (!kind) return;
|
|
48
|
+
const name = (relative(root, path) || basename(path)).split(sep).join("/");
|
|
49
|
+
if (st.size > MAX_DEBUG_FILE_BYTES) {
|
|
50
|
+
logger?.warn(`skipping ${name}: larger than ${MAX_DEBUG_FILE_BYTES / 1024 / 1024} MB`);
|
|
51
|
+
return;
|
|
52
|
+
}
|
|
53
|
+
out.push({ path, name, kind, sizeBytes: st.size });
|
|
54
|
+
};
|
|
55
|
+
for (const p of paths) {
|
|
56
|
+
if (!existsSync(p)) throw new Error(`not found: ${p}`);
|
|
57
|
+
visit(p, statSync(p).isDirectory() ? p : dirname(p));
|
|
58
|
+
}
|
|
59
|
+
return out;
|
|
60
|
+
}
|
|
61
|
+
function proguardUuid(content) {
|
|
62
|
+
const v5 = (ns, name) => {
|
|
63
|
+
const h = createHash("sha1").update(ns).update(name).digest().subarray(0, 16);
|
|
64
|
+
h[6] = h[6] & 15 | 80;
|
|
65
|
+
h[8] = h[8] & 63 | 128;
|
|
66
|
+
return Buffer.from(h);
|
|
67
|
+
};
|
|
68
|
+
const id = v5(v5(Buffer.from("6ba7b8109dad11d180b400c04fd430c8", "hex"), Buffer.from("guardsquare.com")), content).toString("hex");
|
|
69
|
+
return `${id.slice(0, 8)}-${id.slice(8, 12)}-${id.slice(12, 16)}-${id.slice(16, 20)}-${id.slice(20)}`;
|
|
70
|
+
}
|
|
71
|
+
function writeProguardProperties(path, uuid) {
|
|
72
|
+
mkdirSync(dirname(path), { recursive: true });
|
|
73
|
+
writeFileSync(path, `io.guidely.ProguardUuids=${uuid}
|
|
74
|
+
`);
|
|
75
|
+
}
|
|
76
|
+
var Api = class {
|
|
77
|
+
constructor(o) {
|
|
78
|
+
this.o = o;
|
|
79
|
+
if (!o.key) throw new Error("missing API key: pass --key or set GUIDELY_SOURCEMAP_KEY (a secret key with the debug-files:write scope)");
|
|
80
|
+
}
|
|
81
|
+
o;
|
|
82
|
+
url(path) {
|
|
83
|
+
return `${(this.o.apiUrl || DEFAULT_API_URL).replace(/\/+$/, "")}${path}`;
|
|
84
|
+
}
|
|
85
|
+
async call(url, init) {
|
|
86
|
+
const doFetch = this.o.fetch ?? globalThis.fetch;
|
|
87
|
+
const retries = this.o.retries ?? 3;
|
|
88
|
+
for (let attempt = 0; ; attempt++) {
|
|
89
|
+
let res = null;
|
|
90
|
+
let failure = null;
|
|
91
|
+
try {
|
|
92
|
+
const i = init();
|
|
93
|
+
res = await doFetch(url, { ...i, headers: { Authorization: `Bearer ${this.o.key}`, Accept: "application/json", ...i.headers } });
|
|
94
|
+
} catch (err) {
|
|
95
|
+
failure = err;
|
|
96
|
+
}
|
|
97
|
+
if (res && (res.status === 401 || res.status === 403)) throw new Error(`the API key was rejected (HTTP ${res.status}); it needs the debug-files:write scope`);
|
|
98
|
+
const retryable = !res || res.status >= 500 || res.status === 429;
|
|
99
|
+
if (!retryable || attempt >= retries) {
|
|
100
|
+
if (res && !res.ok) throw new Error(`HTTP ${res.status}: ${(await res.text().catch(() => "")).slice(0, 300)}`);
|
|
101
|
+
if (res) return res;
|
|
102
|
+
throw new Error(`network error: ${failure instanceof Error ? failure.message : String(failure)}`);
|
|
103
|
+
}
|
|
104
|
+
await sleep((this.o.retryDelayMs ?? 1e3) * 2 ** attempt);
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
};
|
|
108
|
+
async function uploadOne(api, opts, file, body, maxPolls, retryDelayMs) {
|
|
109
|
+
const checksum = sha1(file);
|
|
110
|
+
const chunks = [];
|
|
111
|
+
for (let i = 0; i < file.length; i += opts.chunkSize) chunks.push(file.subarray(i, i + opts.chunkSize));
|
|
112
|
+
if (!chunks.length) chunks.push(Buffer.alloc(0));
|
|
113
|
+
const bySum = new Map(chunks.map((c) => [sha1(c), c]));
|
|
114
|
+
const assemble = async () => {
|
|
115
|
+
const res = await api.call(api.url("/api/v1/debug-files/assemble"), () => ({ method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ [checksum]: { ...body, chunks: chunks.map(sha1) } }) }));
|
|
116
|
+
const answer2 = (await res.json())[checksum];
|
|
117
|
+
if (!answer2) throw new Error("the server did not answer for this file");
|
|
118
|
+
return answer2;
|
|
119
|
+
};
|
|
120
|
+
let answer = await assemble();
|
|
121
|
+
if (answer.state === "not_found" && answer.missingChunks.length) {
|
|
122
|
+
const missing = answer.missingChunks.map((s) => bySum.get(s)).filter((c) => !!c);
|
|
123
|
+
const gzip = opts.compression?.includes("gzip") ?? false;
|
|
124
|
+
for (let i = 0; i < missing.length; ) {
|
|
125
|
+
const form = new FormData();
|
|
126
|
+
let size = 0;
|
|
127
|
+
let n = 0;
|
|
128
|
+
while (i < missing.length && n < opts.chunksPerRequest && (n === 0 || size + missing[i].length <= opts.maxRequestSize)) {
|
|
129
|
+
const c = missing[i];
|
|
130
|
+
const data = gzip ? gzipSync(c) : c;
|
|
131
|
+
form.append(gzip ? "file_gzip" : "file", new Blob([new Uint8Array(data)]), sha1(c));
|
|
132
|
+
size += data.length;
|
|
133
|
+
n++;
|
|
134
|
+
i++;
|
|
135
|
+
}
|
|
136
|
+
await api.call(opts.url, () => ({ method: "POST", body: form }));
|
|
137
|
+
}
|
|
138
|
+
answer = await assemble();
|
|
139
|
+
}
|
|
140
|
+
for (let polls = 0; (answer.state === "created" || answer.state === "assembling") && polls < maxPolls; polls++) {
|
|
141
|
+
await sleep(retryDelayMs);
|
|
142
|
+
answer = await assemble();
|
|
143
|
+
}
|
|
144
|
+
return answer;
|
|
145
|
+
}
|
|
146
|
+
async function chunkOptions(api) {
|
|
147
|
+
const res = await api.call(api.url("/api/v1/debug-files/chunk-upload"), () => ({}));
|
|
148
|
+
return await res.json();
|
|
149
|
+
}
|
|
150
|
+
async function uploadDebugFiles(paths, o = {}) {
|
|
151
|
+
const files = findDebugFiles(paths, o.logger);
|
|
152
|
+
const result = { files, uploaded: [], errors: [] };
|
|
153
|
+
if (o.dryRun || !files.length) return result;
|
|
154
|
+
const api = new Api(o);
|
|
155
|
+
const opts = await chunkOptions(api);
|
|
156
|
+
for (const f of files) {
|
|
157
|
+
try {
|
|
158
|
+
const answer = await uploadOne(api, opts, readFileSync(f.path), { name: f.name, ...o.appVersion ? { app_version: o.appVersion } : {} }, o.maxPolls ?? 30, o.retryDelayMs ?? 1e3);
|
|
159
|
+
if (answer.state === "ok" && answer.dif) result.uploaded.push({ name: f.name, debugId: answer.dif.debugId, kind: answer.dif.symbolType, arch: answer.dif.cpuName });
|
|
160
|
+
else result.errors.push({ name: f.name, message: answer.detail ?? `assembly ${answer.state}` });
|
|
161
|
+
} catch (err) {
|
|
162
|
+
result.errors.push({ name: f.name, message: err.message });
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
return result;
|
|
166
|
+
}
|
|
167
|
+
async function uploadProguardMapping(path, o = {}) {
|
|
168
|
+
if (!existsSync(path) || !statSync(path).isFile()) throw new Error(`mapping file not found: ${path}`);
|
|
169
|
+
const content = readFileSync(path);
|
|
170
|
+
if (!/ -> [^\s]+:\s*$/m.test(content.subarray(0, 64 * 1024).toString("utf8"))) throw new Error(`${path} doesn't look like a ProGuard / R8 mapping file`);
|
|
171
|
+
const uuid = (o.uuid || proguardUuid(content)).toLowerCase();
|
|
172
|
+
if (o.writeProperties) writeProguardProperties(o.writeProperties, uuid);
|
|
173
|
+
if (o.dryRun) return { uuid, uploaded: false };
|
|
174
|
+
const api = new Api(o);
|
|
175
|
+
const answer = await uploadOne(api, await chunkOptions(api), content, { name: `proguard/${uuid}.txt`, debug_id: uuid, ...o.appVersion ? { app_version: o.appVersion } : {} }, o.maxPolls ?? 30, o.retryDelayMs ?? 1e3);
|
|
176
|
+
return answer.state === "ok" ? { uuid, uploaded: true } : { uuid, uploaded: false, error: answer.detail ?? `assembly ${answer.state}` };
|
|
177
|
+
}
|
|
178
|
+
|
|
9
179
|
// src/run.ts
|
|
10
180
|
var HELP = `Usage:
|
|
11
181
|
guidely-sourcemaps upload --dir <dir> [options] Upload source maps from a build directory
|
|
182
|
+
guidely-sourcemaps inject --dir <dir> Add debug ids to minified files and their maps (before upload)
|
|
183
|
+
guidely-sourcemaps upload-dif <path>... [options] Upload native debug files: dSYMs, Mach-O, ELF (NDK, Flutter --split-debug-info)
|
|
184
|
+
guidely-sourcemaps upload-proguard <mapping.txt> Upload a ProGuard / R8 mapping
|
|
12
185
|
guidely-sourcemaps verify [--key <key>] Check the API key
|
|
13
186
|
|
|
14
187
|
Options:
|
|
@@ -20,10 +193,18 @@ Options:
|
|
|
20
193
|
--include <suffix> File-name suffix of the maps to upload (default .js.map)
|
|
21
194
|
--delete-after Delete local .map files once uploaded (so they are not deployed publicly)
|
|
22
195
|
--dry-run List what would be uploaded without uploading
|
|
23
|
-
-h, --help Show this help
|
|
196
|
+
-h, --help Show this help
|
|
197
|
+
|
|
198
|
+
Debug files (key with the debug-files:write scope):
|
|
199
|
+
--app-version <v> App version the files belong to (events without a mapping UUID use the newest mapping for it)
|
|
200
|
+
--uuid <uuid> upload-proguard: the mapping UUID your build embedded (default: derived from the file)
|
|
201
|
+
--write-properties <path>
|
|
202
|
+
upload-proguard: also write io.guidely.ProguardUuids=<uuid> there
|
|
203
|
+
(e.g. app/src/main/assets/guidely-debug-meta.properties)`;
|
|
24
204
|
var BOOLEAN = /* @__PURE__ */ new Set(["delete-after", "dry-run", "help"]);
|
|
25
205
|
function parseArgs(argv) {
|
|
26
206
|
const flags = {};
|
|
207
|
+
const args = [];
|
|
27
208
|
let command;
|
|
28
209
|
for (let i = 0; i < argv.length; i++) {
|
|
29
210
|
const arg = argv[i];
|
|
@@ -39,9 +220,10 @@ function parseArgs(argv) {
|
|
|
39
220
|
i++;
|
|
40
221
|
}
|
|
41
222
|
} else if (!command) command = arg;
|
|
223
|
+
else if (command === "upload-dif" || command === "upload-proguard" && !args.length) args.push(arg);
|
|
42
224
|
else throw new Error(`unexpected argument: ${arg}`);
|
|
43
225
|
}
|
|
44
|
-
return { command, flags };
|
|
226
|
+
return { command, flags, args };
|
|
45
227
|
}
|
|
46
228
|
var kb = (n) => `${Math.max(1, Math.round(n / 1024))} KB`;
|
|
47
229
|
async function runCli(argv, io = {}) {
|
|
@@ -58,7 +240,7 @@ async function runCli(argv, io = {}) {
|
|
|
58
240
|
${HELP}`);
|
|
59
241
|
return 1;
|
|
60
242
|
}
|
|
61
|
-
const { command, flags } = parsed;
|
|
243
|
+
const { command, flags, args } = parsed;
|
|
62
244
|
if (flags.help || !command) {
|
|
63
245
|
(command || flags.help ? out : err)(HELP);
|
|
64
246
|
return flags.help ? 0 : 1;
|
|
@@ -72,6 +254,17 @@ ${HELP}`);
|
|
|
72
254
|
out(`Key OK for project ${who.projectName ?? who.projectId ?? "(unknown)"}${who.keyPrefix ? ` (key gly_sk_${who.keyPrefix}\u2026)` : ""}${who.scopes.length ? `, scopes: ${who.scopes.join(", ")}` : ""}`);
|
|
73
255
|
return 0;
|
|
74
256
|
}
|
|
257
|
+
if (command === "inject") {
|
|
258
|
+
const result2 = injectDebugIds({ dir: str("dir") ?? "", include: str("include"), dryRun: flags["dry-run"] === true, logger });
|
|
259
|
+
if (!result2.injected.length && !result2.existing.length) {
|
|
260
|
+
err(`error: no minified files with source maps in ${str("dir")} (looked for *${str("include") || ".js.map"} files next to their minified file)`);
|
|
261
|
+
return 1;
|
|
262
|
+
}
|
|
263
|
+
out(`
|
|
264
|
+
${result2.injected.length} ${flags["dry-run"] === true ? "would get" : "got"} a debug id, ${result2.existing.length} already had one, ${result2.skipped.length} skipped.`);
|
|
265
|
+
return 0;
|
|
266
|
+
}
|
|
267
|
+
if (command === "upload-dif" || command === "upload-proguard") return await uploadNative(command, args, { apiUrl, key, str, dryRun: flags["dry-run"] === true, io, out, err, logger });
|
|
75
268
|
if (command !== "upload") {
|
|
76
269
|
err(`error: unknown command "${command}"
|
|
77
270
|
|
|
@@ -102,6 +295,37 @@ ${result.uploaded.length} uploaded, ${result.errors.length} failed, ${result.ski
|
|
|
102
295
|
return 1;
|
|
103
296
|
}
|
|
104
297
|
}
|
|
298
|
+
async function uploadNative(command, args, c) {
|
|
299
|
+
const common = { apiUrl: c.apiUrl, key: c.key, appVersion: c.str("app-version"), dryRun: c.dryRun, fetch: c.io.fetch, retryDelayMs: c.io.retryDelayMs, logger: c.logger };
|
|
300
|
+
if (!args.length) {
|
|
301
|
+
c.err(`error: ${command} needs ${command === "upload-dif" ? "one or more paths" : "the mapping.txt path"}
|
|
302
|
+
|
|
303
|
+
${HELP}`);
|
|
304
|
+
return 1;
|
|
305
|
+
}
|
|
306
|
+
if (command === "upload-proguard") {
|
|
307
|
+
const r = await uploadProguardMapping(args[0], { ...common, uuid: c.str("uuid"), writeProperties: c.str("write-properties") });
|
|
308
|
+
if (c.str("write-properties")) c.out(`wrote io.guidely.ProguardUuids=${r.uuid} to ${c.str("write-properties")}`);
|
|
309
|
+
if (c.dryRun) c.out(`Dry run: ${args[0]} would be uploaded as ProGuard mapping ${r.uuid}.`);
|
|
310
|
+
else if (r.uploaded) c.out(`uploaded ProGuard mapping ${r.uuid}${common.appVersion ? ` (app version ${common.appVersion})` : ""}`);
|
|
311
|
+
else c.err(`failed ${args[0]}: ${r.error}`);
|
|
312
|
+
return r.uploaded || c.dryRun ? 0 : 1;
|
|
313
|
+
}
|
|
314
|
+
const result = await uploadDebugFiles(args, common);
|
|
315
|
+
if (!result.files.length) {
|
|
316
|
+
c.err(`error: no debug files (Mach-O, dSYM, ELF) found in ${args.join(", ")}`);
|
|
317
|
+
return 1;
|
|
318
|
+
}
|
|
319
|
+
if (c.dryRun) {
|
|
320
|
+
for (const f of result.files) c.out(`would upload ${f.name} (${f.kind}, ${kb(f.sizeBytes)})`);
|
|
321
|
+
return 0;
|
|
322
|
+
}
|
|
323
|
+
for (const u of result.uploaded) c.out(`uploaded ${u.name} (${u.kind} ${u.arch}, ${u.debugId})`);
|
|
324
|
+
for (const e of result.errors) c.err(`failed ${e.name}: ${e.message}`);
|
|
325
|
+
c.out(`
|
|
326
|
+
${result.uploaded.length} uploaded, ${result.errors.length} failed.`);
|
|
327
|
+
return result.errors.length ? 1 : 0;
|
|
328
|
+
}
|
|
105
329
|
|
|
106
330
|
// src/index.ts
|
|
107
331
|
var MAX_FILES_PER_REQUEST = 50;
|
|
@@ -129,20 +353,20 @@ function findSourceMaps(dir, options = {}) {
|
|
|
129
353
|
const files = [];
|
|
130
354
|
const skipped = [];
|
|
131
355
|
const walk = (current) => {
|
|
132
|
-
for (const entry of
|
|
133
|
-
const full =
|
|
356
|
+
for (const entry of readdirSync2(current, { withFileTypes: true }).sort((a, b) => a.name.localeCompare(b.name))) {
|
|
357
|
+
const full = join2(current, entry.name);
|
|
134
358
|
if (entry.isDirectory()) {
|
|
135
359
|
if (entry.name !== "node_modules") walk(full);
|
|
136
360
|
continue;
|
|
137
361
|
}
|
|
138
362
|
if (!entry.isFile() || !entry.name.endsWith(include) || !entry.name.endsWith(".map")) continue;
|
|
139
363
|
const filePath = full.slice(0, -".map".length);
|
|
140
|
-
if (!
|
|
141
|
-
skipped.push({ path: full, reason: `no minified file next to it (${
|
|
142
|
-
log.warn(`skipping ${
|
|
364
|
+
if (!existsSync2(filePath)) {
|
|
365
|
+
skipped.push({ path: full, reason: `no minified file next to it (${basename2(filePath)})` });
|
|
366
|
+
log.warn(`skipping ${relative2(dir, full)}: no ${basename2(filePath)} next to it`);
|
|
143
367
|
continue;
|
|
144
368
|
}
|
|
145
|
-
files.push({ mapPath: full, filePath, urlPath: urlPathFor(options.urlPrefix,
|
|
369
|
+
files.push({ mapPath: full, filePath, urlPath: urlPathFor(options.urlPrefix, relative2(dir, filePath).split(sep2).join("/")), sizeBytes: statSync2(full).size });
|
|
146
370
|
}
|
|
147
371
|
};
|
|
148
372
|
walk(dir);
|
|
@@ -172,6 +396,15 @@ function invalidMapReason(content) {
|
|
|
172
396
|
return "not valid JSON";
|
|
173
397
|
}
|
|
174
398
|
}
|
|
399
|
+
function mapDebugId(content) {
|
|
400
|
+
try {
|
|
401
|
+
const map = JSON.parse(content);
|
|
402
|
+
const id = map?.debug_id ?? map?.debugId;
|
|
403
|
+
return typeof id === "string" && /^[0-9a-f]{8}-?[0-9a-f]{4}-?[0-9a-f]{4}-?[0-9a-f]{4}-?[0-9a-f]{12}$/i.test(id) ? id.toLowerCase() : null;
|
|
404
|
+
} catch {
|
|
405
|
+
return null;
|
|
406
|
+
}
|
|
407
|
+
}
|
|
175
408
|
function withoutSourcesContent(content) {
|
|
176
409
|
try {
|
|
177
410
|
const map = JSON.parse(content);
|
|
@@ -182,7 +415,7 @@ function withoutSourcesContent(content) {
|
|
|
182
415
|
return null;
|
|
183
416
|
}
|
|
184
417
|
}
|
|
185
|
-
var
|
|
418
|
+
var sleep2 = (ms) => new Promise((r) => setTimeout(r, ms));
|
|
186
419
|
var kb2 = (n) => `${Math.max(1, Math.round(n / 1024))} KB`;
|
|
187
420
|
function endpoint(apiUrl, path) {
|
|
188
421
|
return `${(apiUrl || DEFAULT_API_URL).replace(/\/+$/, "")}/api/v1/sourcemaps${path}`;
|
|
@@ -212,12 +445,12 @@ async function request(doFetch, url, init, retries, delayMs) {
|
|
|
212
445
|
if (res) return res;
|
|
213
446
|
throw new Error(`network error: ${failure instanceof Error ? failure.message : String(failure)}`);
|
|
214
447
|
}
|
|
215
|
-
await
|
|
448
|
+
await sleep2(delayMs * 2 ** attempt);
|
|
216
449
|
}
|
|
217
450
|
}
|
|
218
451
|
async function uploadSourceMaps(options) {
|
|
219
452
|
const log = options.logger ?? silent;
|
|
220
|
-
if (!options.dir || !
|
|
453
|
+
if (!options.dir || !existsSync2(options.dir) || !statSync2(options.dir).isDirectory()) throw new Error(`directory not found: ${options.dir || "(missing --dir)"}`);
|
|
221
454
|
if (!options.dryRun && !options.key) throw new Error("missing API key: pass --key or set GUIDELY_SOURCEMAP_KEY");
|
|
222
455
|
const release = options.release?.trim() || null;
|
|
223
456
|
if (release && release.length > 100) throw new Error("release must be at most 100 characters");
|
|
@@ -230,7 +463,7 @@ async function uploadSourceMaps(options) {
|
|
|
230
463
|
result.files.push(f);
|
|
231
464
|
continue;
|
|
232
465
|
}
|
|
233
|
-
const smaller = withoutSourcesContent(
|
|
466
|
+
const smaller = withoutSourcesContent(readFileSync2(f.mapPath, "utf8"));
|
|
234
467
|
const smallerBytes = smaller === null ? 0 : Buffer.byteLength(smaller, "utf8");
|
|
235
468
|
if (smaller !== null && smallerBytes <= maxFileBytes) {
|
|
236
469
|
shrunk.set(f.mapPath, smaller);
|
|
@@ -247,7 +480,7 @@ async function uploadSourceMaps(options) {
|
|
|
247
480
|
for (const batch of batchFiles(result.files, options.maxFilesPerRequest, options.maxBytesPerRequest ?? MAX_BYTES_PER_REQUEST)) {
|
|
248
481
|
const items = [];
|
|
249
482
|
for (const f of batch) {
|
|
250
|
-
const content = shrunk.get(f.mapPath) ??
|
|
483
|
+
const content = shrunk.get(f.mapPath) ?? readFileSync2(f.mapPath, "utf8");
|
|
251
484
|
const reason = invalidMapReason(content);
|
|
252
485
|
if (reason) {
|
|
253
486
|
result.skipped.push({ path: f.mapPath, reason });
|
|
@@ -260,15 +493,23 @@ async function uploadSourceMaps(options) {
|
|
|
260
493
|
continue;
|
|
261
494
|
}
|
|
262
495
|
if (!items.length) continue;
|
|
263
|
-
for (const f of items) f.gzipped =
|
|
496
|
+
for (const f of items) f.gzipped = gzipSync2(f.content);
|
|
264
497
|
const res = await request(
|
|
265
498
|
doFetch,
|
|
266
499
|
endpoint(options.apiUrl, "/upload"),
|
|
267
500
|
() => {
|
|
268
501
|
const form = new FormData();
|
|
269
502
|
if (release) form.set("release", release);
|
|
270
|
-
form.set(
|
|
271
|
-
|
|
503
|
+
form.set(
|
|
504
|
+
"manifest",
|
|
505
|
+
JSON.stringify(
|
|
506
|
+
items.map((f, i) => {
|
|
507
|
+
const debugId = mapDebugId(f.content);
|
|
508
|
+
return { field: `f${i}`, path: f.urlPath, ...debugId ? { debugId } : {} };
|
|
509
|
+
})
|
|
510
|
+
)
|
|
511
|
+
);
|
|
512
|
+
items.forEach((f, i) => form.set(`f${i}`, new Blob([new Uint8Array(f.gzipped)], { type: "application/gzip" }), `${basename2(f.mapPath)}.gz`));
|
|
272
513
|
return { method: "POST", headers: { Authorization: `Bearer ${options.key}` }, body: form };
|
|
273
514
|
},
|
|
274
515
|
options.retries ?? 3,
|
|
@@ -318,19 +559,71 @@ async function verifyKey(options) {
|
|
|
318
559
|
scopes: Array.isArray(b.key?.scopes) ? b.key.scopes : Array.isArray(b.scopes) ? b.scopes : []
|
|
319
560
|
};
|
|
320
561
|
}
|
|
562
|
+
var debugIdSnippet = (id) => `!function(){try{var e="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self?self:{},n=(new e.Error).stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="${id}")}catch(e){}}();`;
|
|
563
|
+
function debugIdFor(content) {
|
|
564
|
+
const h = createHash2("sha256").update(content).digest("hex");
|
|
565
|
+
const variant = (parseInt(h[16], 16) & 3 | 8).toString(16);
|
|
566
|
+
return `${h.slice(0, 8)}-${h.slice(8, 12)}-4${h.slice(13, 16)}-${variant}${h.slice(17, 20)}-${h.slice(20, 32)}`;
|
|
567
|
+
}
|
|
568
|
+
var DEBUG_ID_COMMENT = /\/\/# debugId=([0-9a-fA-F-]{32,36})\s*$/m;
|
|
569
|
+
function injectDebugIds(options) {
|
|
570
|
+
const log = options.logger ?? silent;
|
|
571
|
+
if (!options.dir || !existsSync2(options.dir) || !statSync2(options.dir).isDirectory()) throw new Error(`directory not found: ${options.dir || "(missing --dir)"}`);
|
|
572
|
+
const found = findSourceMaps(options.dir, { include: options.include, logger: log });
|
|
573
|
+
const result = { injected: [], existing: [], skipped: found.skipped };
|
|
574
|
+
for (const f of found.files) {
|
|
575
|
+
const js = readFileSync2(f.filePath, "utf8");
|
|
576
|
+
const mapText = readFileSync2(f.mapPath, "utf8");
|
|
577
|
+
const reason = invalidMapReason(mapText);
|
|
578
|
+
if (reason) {
|
|
579
|
+
result.skipped.push({ path: f.mapPath, reason });
|
|
580
|
+
log.warn(`skipping ${relative2(options.dir, f.mapPath)}: ${reason}`);
|
|
581
|
+
continue;
|
|
582
|
+
}
|
|
583
|
+
const map = JSON.parse(mapText);
|
|
584
|
+
const had = DEBUG_ID_COMMENT.exec(js)?.[1]?.toLowerCase();
|
|
585
|
+
if (had) {
|
|
586
|
+
if (!mapDebugId(mapText) && !options.dryRun) writeFileSync2(f.mapPath, JSON.stringify({ ...map, debug_id: had, debugId: had }));
|
|
587
|
+
result.existing.push({ filePath: f.filePath, debugId: had });
|
|
588
|
+
continue;
|
|
589
|
+
}
|
|
590
|
+
const id = debugIdFor(js);
|
|
591
|
+
if (!options.dryRun) {
|
|
592
|
+
writeFileSync2(f.filePath, `${debugIdSnippet(id)}
|
|
593
|
+
${js.replace(/\s*$/, "")}
|
|
594
|
+
//# debugId=${id}
|
|
595
|
+
`);
|
|
596
|
+
writeFileSync2(f.mapPath, JSON.stringify({ ...map, ...typeof map.mappings === "string" ? { mappings: `;${map.mappings}` } : {}, debug_id: id, debugId: id }));
|
|
597
|
+
}
|
|
598
|
+
result.injected.push({ filePath: f.filePath, mapPath: f.mapPath, debugId: id });
|
|
599
|
+
log.info(`${options.dryRun ? "would inject" : "injected"} ${id} into ${relative2(options.dir, f.filePath)}`);
|
|
600
|
+
}
|
|
601
|
+
return result;
|
|
602
|
+
}
|
|
321
603
|
export {
|
|
322
604
|
DEFAULT_API_URL,
|
|
323
605
|
GuidelyAuthError,
|
|
324
606
|
MAX_BYTES_PER_REQUEST,
|
|
607
|
+
MAX_DEBUG_FILE_BYTES,
|
|
325
608
|
MAX_FILES_PER_REQUEST,
|
|
326
609
|
MAX_FILE_BYTES,
|
|
327
610
|
batchFiles,
|
|
611
|
+
debugIdFor,
|
|
612
|
+
debugIdSnippet,
|
|
613
|
+
findDebugFiles,
|
|
328
614
|
findSourceMaps,
|
|
615
|
+
injectDebugIds,
|
|
329
616
|
invalidMapReason,
|
|
617
|
+
mapDebugId,
|
|
618
|
+
nativeKind,
|
|
619
|
+
proguardUuid,
|
|
330
620
|
runCli,
|
|
621
|
+
uploadDebugFiles,
|
|
622
|
+
uploadProguardMapping,
|
|
331
623
|
uploadSourceMaps,
|
|
332
624
|
urlPathFor,
|
|
333
625
|
verifyKey,
|
|
334
|
-
withoutSourcesContent
|
|
626
|
+
withoutSourcesContent,
|
|
627
|
+
writeProguardProperties
|
|
335
628
|
};
|
|
336
629
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts","../src/constants.ts","../src/run.ts"],"sourcesContent":["/**\n * @guidelytech/sourcemaps — uploads JavaScript source maps to Guidely so exception stack traces are\n * shown against your original source. Zero runtime dependencies (Node ≥18: fetch, FormData, Blob).\n */\nimport { readdirSync, readFileSync, statSync, unlinkSync, existsSync } from \"node:fs\";\nimport { gzipSync } from \"node:zlib\";\nimport { basename, join, relative, sep } from \"node:path\";\n\nimport { DEFAULT_API_URL } from \"./constants\";\n\nexport { DEFAULT_API_URL };\n/** Per-request limits (uncompressed) and the per-file cap enforced by the upload API. Maps are sent gzipped. */\nexport const MAX_FILES_PER_REQUEST = 50;\nexport const MAX_BYTES_PER_REQUEST = 40 * 1024 * 1024;\nexport const MAX_FILE_BYTES = 100 * 1024 * 1024;\n\nexport interface Logger {\n info(message: string): void;\n warn(message: string): void;\n}\n\n/** A source map found on disk next to its minified file. */\nexport interface SourceMapFile {\n /** Absolute path of the `.map` file. */\n mapPath: string;\n /** Absolute path of the minified file the map belongs to. */\n filePath: string;\n /** URL path the minified file is served at (`urlPrefix` + posix relative path). */\n urlPath: string;\n sizeBytes: number;\n}\n\nexport interface SkippedFile {\n path: string;\n reason: string;\n}\n\n/** One map stored by the server. */\nexport interface UploadedMap {\n path: string;\n sizeBytes: number;\n sources: number;\n hasContent: boolean;\n replaced: boolean;\n}\n\nexport interface UploadOptions {\n /** Build output directory searched recursively (e.g. `dist`, `.next/static`). */\n dir: string;\n /** URL path the directory is served under (default `/`; `/_next/static/` for Next.js). */\n urlPrefix?: string;\n /** App version / commit SHA; must equal the SDK's `release` option. */\n release?: string;\n /** Guidely API base URL (default `https://api.guidely.app`). */\n apiUrl?: string;\n /** Secret project API key with the `sourcemaps:write` scope. */\n key?: string;\n /** File-name suffix of the maps to upload (default `.js.map`). */\n include?: string;\n /** Delete local `.map` files that were uploaded successfully. */\n deleteAfter?: boolean;\n /** Only list what would be uploaded. */\n dryRun?: boolean;\n logger?: Logger;\n fetch?: typeof fetch;\n /** Retries for network errors, 5xx and 429 (default 3). */\n retries?: number;\n /** Base backoff delay (default 1000ms, doubled per attempt). */\n retryDelayMs?: number;\n maxFilesPerRequest?: number;\n maxBytesPerRequest?: number;\n maxFileBytes?: number;\n}\n\nexport interface UploadResult {\n release: string | null;\n /** Maps found (and, unless skipped, uploaded or listed in a dry run). */\n files: SourceMapFile[];\n uploaded: UploadedMap[];\n errors: Array<{ path: string; message: string }>;\n skipped: SkippedFile[];\n /** Local `.map` files deleted with `deleteAfter`. */\n deleted: string[];\n dryRun: boolean;\n}\n\n/** Invalid or revoked key, or a key without the `sourcemaps:write` scope. */\nexport class GuidelyAuthError extends Error {\n constructor(status: number) {\n super(`Guidely rejected the API key (${status}): it is invalid or revoked, or it is missing the sourcemaps:write scope. Create a key under Settings → API keys.`);\n this.name = \"GuidelyAuthError\";\n }\n}\n\nconst silent: Logger = { info: () => {}, warn: () => {} };\n\n/** URL path of a built file: `prefix` (default `/`) + its posix path relative to the build directory. */\nexport function urlPathFor(urlPrefix: string | undefined, relPath: string): string {\n let prefix = (urlPrefix || \"/\").replace(/\\\\/g, \"/\");\n if (!prefix.includes(\"://\") && !prefix.startsWith(\"/\")) prefix = `/${prefix}`;\n if (!prefix.endsWith(\"/\")) prefix += \"/\";\n const rel = relPath.replace(/\\\\/g, \"/\").replace(/^(\\.\\/)+/, \"\").replace(/^\\/+/, \"\");\n return prefix + rel;\n}\n\n/** Recursively finds `*<include>` maps whose minified sibling (same name without `.map`) exists. */\nexport function findSourceMaps(dir: string, options: { urlPrefix?: string; include?: string; logger?: Logger } = {}): { files: SourceMapFile[]; skipped: SkippedFile[] } {\n const include = options.include || \".js.map\";\n const log = options.logger ?? silent;\n const files: SourceMapFile[] = [];\n const skipped: SkippedFile[] = [];\n const walk = (current: string) => {\n for (const entry of readdirSync(current, { withFileTypes: true }).sort((a, b) => a.name.localeCompare(b.name))) {\n const full = join(current, entry.name);\n if (entry.isDirectory()) {\n if (entry.name !== \"node_modules\") walk(full);\n continue;\n }\n if (!entry.isFile() || !entry.name.endsWith(include) || !entry.name.endsWith(\".map\")) continue;\n const filePath = full.slice(0, -\".map\".length);\n if (!existsSync(filePath)) {\n skipped.push({ path: full, reason: `no minified file next to it (${basename(filePath)})` });\n log.warn(`skipping ${relative(dir, full)}: no ${basename(filePath)} next to it`);\n continue;\n }\n files.push({ mapPath: full, filePath, urlPath: urlPathFor(options.urlPrefix, relative(dir, filePath).split(sep).join(\"/\")), sizeBytes: statSync(full).size });\n }\n };\n walk(dir);\n return { files, skipped };\n}\n\n/** Groups files into requests of at most `maxFiles` files and `maxBytes` bytes (a larger single file gets its own request). */\nexport function batchFiles<T extends { sizeBytes: number }>(files: T[], maxFiles = MAX_FILES_PER_REQUEST, maxBytes = MAX_BYTES_PER_REQUEST): T[][] {\n const batches: T[][] = [];\n let current: T[] = [];\n let bytes = 0;\n for (const f of files) {\n if (current.length && (current.length >= maxFiles || bytes + f.sizeBytes > maxBytes)) {\n batches.push(current);\n current = [];\n bytes = 0;\n }\n current.push(f);\n bytes += f.sizeBytes;\n }\n if (current.length) batches.push(current);\n return batches;\n}\n\n/** Returns why a map file cannot be uploaded, or null when it is a valid v3 source map. */\nexport function invalidMapReason(content: string): string | null {\n try {\n const map = JSON.parse(content) as { version?: unknown };\n return map && typeof map === \"object\" && map.version === 3 ? null : \"not a version 3 source map\";\n } catch {\n return \"not valid JSON\";\n }\n}\n\n/** The map re-serialised without `sourcesContent`, or null when it has none (or is not JSON). */\nexport function withoutSourcesContent(content: string): string | null {\n try {\n const map = JSON.parse(content) as Record<string, unknown> | null;\n if (!map || typeof map !== \"object\" || !(\"sourcesContent\" in map)) return null;\n delete map.sourcesContent;\n return JSON.stringify(map);\n } catch {\n return null;\n }\n}\n\nconst sleep = (ms: number) => new Promise<void>((r) => setTimeout(r, ms));\nconst kb = (n: number) => `${Math.max(1, Math.round(n / 1024))} KB`;\n\nfunction endpoint(apiUrl: string | undefined, path: string): string {\n return `${(apiUrl || DEFAULT_API_URL).replace(/\\/+$/, \"\")}/api/v1/sourcemaps${path}`;\n}\n\nasync function errorMessage(res: Response): Promise<string> {\n const text = await res.text().catch(() => \"\");\n try {\n const body = JSON.parse(text) as { error?: { message?: string } | string; message?: string };\n const msg = typeof body.error === \"string\" ? body.error : body.error?.message ?? body.message;\n if (msg) return `${msg} (HTTP ${res.status})`;\n } catch {\n /* not JSON */\n }\n return `HTTP ${res.status}${text ? `: ${text.slice(0, 200)}` : \"\"}`;\n}\n\n/** fetch with retries on network errors, 5xx and 429; 401/403 throw {@link GuidelyAuthError}. */\nasync function request(doFetch: typeof fetch, url: string, init: () => RequestInit, retries: number, delayMs: number): Promise<Response> {\n for (let attempt = 0; ; attempt++) {\n let res: Response | null = null;\n let failure: unknown = null;\n try {\n res = await doFetch(url, init());\n } catch (err) {\n failure = err;\n }\n if (res && (res.status === 401 || res.status === 403)) throw new GuidelyAuthError(res.status);\n const retryable = !res || res.status >= 500 || res.status === 429;\n if (!retryable || attempt >= retries) {\n if (res) return res;\n throw new Error(`network error: ${failure instanceof Error ? failure.message : String(failure)}`);\n }\n await sleep(delayMs * 2 ** attempt);\n }\n}\n\n/** Finds the maps under `dir` and uploads them in batches. Throws {@link GuidelyAuthError} for a rejected key. */\nexport async function uploadSourceMaps(options: UploadOptions): Promise<UploadResult> {\n const log = options.logger ?? silent;\n if (!options.dir || !existsSync(options.dir) || !statSync(options.dir).isDirectory()) throw new Error(`directory not found: ${options.dir || \"(missing --dir)\"}`);\n if (!options.dryRun && !options.key) throw new Error(\"missing API key: pass --key or set GUIDELY_SOURCEMAP_KEY\");\n const release = options.release?.trim() || null;\n if (release && release.length > 100) throw new Error(\"release must be at most 100 characters\");\n const maxFileBytes = options.maxFileBytes ?? MAX_FILE_BYTES;\n const found = findSourceMaps(options.dir, { urlPrefix: options.urlPrefix, include: options.include, logger: log });\n const result: UploadResult = { release, files: [], uploaded: [], errors: [], skipped: found.skipped, deleted: [], dryRun: !!options.dryRun };\n // Maps over the limit are uploaded without their embedded sources when that brings them under it\n // (frames still map to original files and lines, without code context). A map that is still too\n // large fails the run: it is usually the main bundle, the one most errors point at.\n const shrunk = new Map<string, string>();\n for (const f of found.files) {\n if (f.sizeBytes <= maxFileBytes) {\n result.files.push(f);\n continue;\n }\n const smaller = withoutSourcesContent(readFileSync(f.mapPath, \"utf8\"));\n const smallerBytes = smaller === null ? 0 : Buffer.byteLength(smaller, \"utf8\");\n if (smaller !== null && smallerBytes <= maxFileBytes) {\n shrunk.set(f.mapPath, smaller);\n result.files.push({ ...f, sizeBytes: smallerBytes });\n log.warn(`${f.urlPath}.map is ${kb(f.sizeBytes)}, over the ${kb(maxFileBytes)} limit: uploading it without sourcesContent (${kb(smallerBytes)}), so traces map to your files and lines but show no code context`);\n } else {\n const message = `map is ${kb(smaller === null ? f.sizeBytes : smallerBytes)}${smaller === null ? \"\" : \" without sourcesContent\"}, over the ${kb(maxFileBytes)} limit; errors in this file stay minified (split the bundle into smaller chunks)`;\n result.errors.push({ path: f.urlPath, message });\n }\n }\n const doFetch = options.fetch ?? globalThis.fetch;\n const valid: SourceMapFile[] = [];\n const succeeded = new Set<string>();\n for (const batch of batchFiles(result.files, options.maxFilesPerRequest, options.maxBytesPerRequest ?? MAX_BYTES_PER_REQUEST)) {\n // Maps are read one batch at a time so memory stays bounded by the request size.\n const items: Array<SourceMapFile & { content: string; gzipped?: Buffer }> = [];\n for (const f of batch) {\n const content = shrunk.get(f.mapPath) ?? readFileSync(f.mapPath, \"utf8\");\n const reason = invalidMapReason(content);\n if (reason) {\n result.skipped.push({ path: f.mapPath, reason });\n log.warn(`skipping ${f.urlPath}.map: ${reason}`);\n } else items.push({ ...f, content });\n }\n valid.push(...items.map(({ content: _content, ...f }) => f));\n if (options.dryRun) {\n for (const f of items) log.info(`would upload ${f.urlPath} (${kb(f.sizeBytes)})`);\n continue;\n }\n if (!items.length) continue;\n for (const f of items) f.gzipped = gzipSync(f.content);\n const res = await request(\n doFetch,\n endpoint(options.apiUrl, \"/upload\"),\n () => {\n const form = new FormData();\n if (release) form.set(\"release\", release);\n form.set(\"manifest\", JSON.stringify(items.map((f, i) => ({ field: `f${i}`, path: f.urlPath }))));\n items.forEach((f, i) => form.set(`f${i}`, new Blob([new Uint8Array(f.gzipped!)], { type: \"application/gzip\" }), `${basename(f.mapPath)}.gz`));\n return { method: \"POST\", headers: { Authorization: `Bearer ${options.key}` }, body: form };\n },\n options.retries ?? 3,\n options.retryDelayMs ?? 1000,\n ).catch((err: unknown) => {\n if (err instanceof GuidelyAuthError) throw err;\n return err instanceof Error ? err : new Error(String(err));\n });\n if (res instanceof Error || !res.ok) {\n const message = res instanceof Error ? res.message : await errorMessage(res);\n for (const f of items) result.errors.push({ path: f.urlPath, message });\n continue;\n }\n const body = (await res.json().catch(() => ({}))) as { release?: string; uploaded?: UploadedMap[]; errors?: Array<{ path: string; message: string }> };\n if (body.release) result.release = body.release;\n for (const u of body.uploaded ?? []) {\n result.uploaded.push(u);\n succeeded.add(u.path);\n }\n result.errors.push(...(body.errors ?? []));\n }\n result.files = valid;\n if (options.deleteAfter && !options.dryRun) {\n for (const f of result.files) {\n if (!succeeded.has(f.urlPath)) continue;\n try {\n unlinkSync(f.mapPath);\n result.deleted.push(f.mapPath);\n } catch (err) {\n log.warn(`could not delete ${f.mapPath}: ${err instanceof Error ? err.message : String(err)}`);\n }\n }\n }\n return result;\n}\n\n/** What `GET /api/v1/sourcemaps/whoami` reports about a key (fields are read leniently). */\nexport interface WhoAmI {\n projectName: string | null;\n projectId: string | null;\n keyName: string | null;\n keyPrefix: string | null;\n scopes: string[];\n}\n\n/** Checks a key against the API (`guidely-sourcemaps verify`). */\nexport async function verifyKey(options: Pick<UploadOptions, \"apiUrl\" | \"key\" | \"fetch\" | \"retries\" | \"retryDelayMs\">): Promise<WhoAmI> {\n if (!options.key) throw new Error(\"missing API key: pass --key or set GUIDELY_SOURCEMAP_KEY\");\n const res = await request(options.fetch ?? globalThis.fetch, endpoint(options.apiUrl, \"/whoami\"), () => ({ headers: { Authorization: `Bearer ${options.key}`, Accept: \"application/json\" } }), options.retries ?? 3, options.retryDelayMs ?? 1000);\n if (!res.ok) throw new Error(await errorMessage(res));\n const b = (await res.json().catch(() => ({}))) as Record<string, any>;\n const str = (v: unknown) => (typeof v === \"string\" && v ? v : null);\n return {\n projectName: str(b.project?.name) ?? str(b.projectName),\n projectId: str(b.project?.id) ?? str(b.projectId),\n keyName: str(b.key?.name) ?? str(b.keyName),\n keyPrefix: str(b.key?.prefix) ?? str(b.keyPrefix) ?? str(b.prefix),\n scopes: Array.isArray(b.key?.scopes) ? b.key.scopes : Array.isArray(b.scopes) ? b.scopes : [],\n };\n}\n\nexport { runCli } from \"./run\";\nexport type { CliIO } from \"./run\";\n","/** Default Guidely API base URL (same as the browser SDK). Its own module so `run.ts` can import it without an import cycle. */\nexport const DEFAULT_API_URL = \"https://api.guidely.app\";\n","import { DEFAULT_API_URL } from \"./constants\";\nimport { GuidelyAuthError, uploadSourceMaps, verifyKey, type Logger } from \"./index\";\n\n/** Where the CLI writes and what it reads (injectable for tests). */\nexport interface CliIO {\n env?: Record<string, string | undefined>;\n out?: (line: string) => void;\n err?: (line: string) => void;\n fetch?: typeof fetch;\n retryDelayMs?: number;\n}\n\nconst HELP = `Usage:\n guidely-sourcemaps upload --dir <dir> [options] Upload source maps from a build directory\n guidely-sourcemaps verify [--key <key>] Check the API key\n\nOptions:\n --dir <dir> Build output directory, searched recursively (e.g. dist, .next/static)\n --url-prefix <path> URL path the directory is served under (default \"/\"; \"/_next/static/\" for Next.js)\n --release <release> App version or commit SHA; set the same value as the SDK's \\`release\\` (env GUIDELY_RELEASE)\n --api-url <url> Guidely API URL (env GUIDELY_API_URL, default ${DEFAULT_API_URL})\n --key <key> Secret API key with the sourcemaps:write scope (env GUIDELY_SOURCEMAP_KEY)\n --include <suffix> File-name suffix of the maps to upload (default .js.map)\n --delete-after Delete local .map files once uploaded (so they are not deployed publicly)\n --dry-run List what would be uploaded without uploading\n -h, --help Show this help`;\n\nconst BOOLEAN = new Set([\"delete-after\", \"dry-run\", \"help\"]);\n\n/** Parses `--name value`, `--name=value` and boolean flags. */\nexport function parseArgs(argv: string[]): { command: string | undefined; flags: Record<string, string | boolean> } {\n const flags: Record<string, string | boolean> = {};\n let command: string | undefined;\n for (let i = 0; i < argv.length; i++) {\n const arg = argv[i] as string;\n if (arg === \"-h\") flags.help = true;\n else if (arg.startsWith(\"--\")) {\n const [name, inline] = arg.slice(2).split(/=(.*)/s, 2) as [string, string | undefined];\n if (BOOLEAN.has(name)) flags[name] = inline === undefined ? true : inline !== \"false\";\n else if (inline !== undefined) flags[name] = inline;\n else {\n const next = argv[i + 1];\n if (next === undefined || next.startsWith(\"--\")) throw new Error(`--${name} needs a value`);\n flags[name] = next;\n i++;\n }\n } else if (!command) command = arg;\n else throw new Error(`unexpected argument: ${arg}`);\n }\n return { command, flags };\n}\n\nconst kb = (n: number) => `${Math.max(1, Math.round(n / 1024))} KB`;\n\n/** Runs the CLI; resolves with the process exit code. Never prints the API key. */\nexport async function runCli(argv: string[], io: CliIO = {}): Promise<number> {\n const env = io.env ?? process.env;\n const out = io.out ?? ((l: string) => console.log(l));\n const err = io.err ?? ((l: string) => console.error(l));\n const logger: Logger = { info: out, warn: (m) => err(`warning: ${m}`) };\n let parsed: ReturnType<typeof parseArgs>;\n try {\n parsed = parseArgs(argv);\n } catch (e) {\n err(`error: ${(e as Error).message}\\n\\n${HELP}`);\n return 1;\n }\n const { command, flags } = parsed;\n if (flags.help || !command) {\n (command || flags.help ? out : err)(HELP);\n return flags.help ? 0 : 1;\n }\n const str = (name: string) => (typeof flags[name] === \"string\" ? (flags[name] as string) : undefined);\n const apiUrl = str(\"api-url\") || env.GUIDELY_API_URL || DEFAULT_API_URL;\n const key = str(\"key\") || env.GUIDELY_SOURCEMAP_KEY;\n try {\n if (command === \"verify\") {\n const who = await verifyKey({ apiUrl, key, fetch: io.fetch, retryDelayMs: io.retryDelayMs });\n out(`Key OK for project ${who.projectName ?? who.projectId ?? \"(unknown)\"}${who.keyPrefix ? ` (key gly_sk_${who.keyPrefix}…)` : \"\"}${who.scopes.length ? `, scopes: ${who.scopes.join(\", \")}` : \"\"}`);\n return 0;\n }\n if (command !== \"upload\") {\n err(`error: unknown command \"${command}\"\\n\\n${HELP}`);\n return 1;\n }\n const dryRun = flags[\"dry-run\"] === true;\n const release = str(\"release\") || env.GUIDELY_RELEASE || undefined;\n const result = await uploadSourceMaps({ dir: str(\"dir\") ?? \"\", urlPrefix: str(\"url-prefix\"), release, apiUrl, key, include: str(\"include\"), deleteAfter: flags[\"delete-after\"] === true, dryRun, fetch: io.fetch, retryDelayMs: io.retryDelayMs, logger });\n const total = result.files.length;\n if (!total) {\n err(`error: no source maps to upload in ${str(\"dir\")} (looked for *${str(\"include\") || \".js.map\"} files next to their minified file)`);\n return 1;\n }\n if (dryRun) {\n out(`Dry run: ${total} source map${total === 1 ? \"\" : \"s\"} would be uploaded${release ? ` for release ${release}` : \"\"} to ${apiUrl}.`);\n return 0;\n }\n for (const u of result.uploaded) out(`uploaded ${u.path} (${kb(u.sizeBytes)}, ${u.sources} source${u.sources === 1 ? \"\" : \"s\"}${u.hasContent ? \"\" : \", no sourcesContent\"}${u.replaced ? \", replaced\" : \"\"})`);\n for (const e of result.errors) err(`failed ${e.path}: ${e.message}`);\n out(`\\n${result.uploaded.length} uploaded, ${result.errors.length} failed, ${result.skipped.length} skipped${result.release ? ` (release ${result.release})` : \"\"}.`);\n if (result.deleted.length) out(`Deleted ${result.deleted.length} local .map file${result.deleted.length === 1 ? \"\" : \"s\"}.`);\n else if (result.uploaded.length) logger.warn(\"the .map files are still in your build output: deploying them publishes your original source. Pass --delete-after (or exclude *.map from the deploy).\");\n return result.errors.length ? 1 : 0;\n } catch (e) {\n err(`error: ${e instanceof GuidelyAuthError ? e.message : (e as Error).message}`);\n return 1;\n }\n}\n"],"mappings":";AAIA,SAAS,aAAa,cAAc,UAAU,YAAY,kBAAkB;AAC5E,SAAS,gBAAgB;AACzB,SAAS,UAAU,MAAM,UAAU,WAAW;;;ACLvC,IAAM,kBAAkB;;;ACW/B,IAAM,OAAO;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,uEAQ0D,eAAe;AAAA;AAAA;AAAA;AAAA;AAAA;AAOtF,IAAM,UAAU,oBAAI,IAAI,CAAC,gBAAgB,WAAW,MAAM,CAAC;AAGpD,SAAS,UAAU,MAA0F;AAClH,QAAM,QAA0C,CAAC;AACjD,MAAI;AACJ,WAAS,IAAI,GAAG,IAAI,KAAK,QAAQ,KAAK;AACpC,UAAM,MAAM,KAAK,CAAC;AAClB,QAAI,QAAQ,KAAM,OAAM,OAAO;AAAA,aACtB,IAAI,WAAW,IAAI,GAAG;AAC7B,YAAM,CAAC,MAAM,MAAM,IAAI,IAAI,MAAM,CAAC,EAAE,MAAM,UAAU,CAAC;AACrD,UAAI,QAAQ,IAAI,IAAI,EAAG,OAAM,IAAI,IAAI,WAAW,SAAY,OAAO,WAAW;AAAA,eACrE,WAAW,OAAW,OAAM,IAAI,IAAI;AAAA,WACxC;AACH,cAAM,OAAO,KAAK,IAAI,CAAC;AACvB,YAAI,SAAS,UAAa,KAAK,WAAW,IAAI,EAAG,OAAM,IAAI,MAAM,KAAK,IAAI,gBAAgB;AAC1F,cAAM,IAAI,IAAI;AACd;AAAA,MACF;AAAA,IACF,WAAW,CAAC,QAAS,WAAU;AAAA,QAC1B,OAAM,IAAI,MAAM,wBAAwB,GAAG,EAAE;AAAA,EACpD;AACA,SAAO,EAAE,SAAS,MAAM;AAC1B;AAEA,IAAM,KAAK,CAAC,MAAc,GAAG,KAAK,IAAI,GAAG,KAAK,MAAM,IAAI,IAAI,CAAC,CAAC;AAG9D,eAAsB,OAAO,MAAgB,KAAY,CAAC,GAAoB;AAC5E,QAAM,MAAM,GAAG,OAAO,QAAQ;AAC9B,QAAM,MAAM,GAAG,QAAQ,CAAC,MAAc,QAAQ,IAAI,CAAC;AACnD,QAAM,MAAM,GAAG,QAAQ,CAAC,MAAc,QAAQ,MAAM,CAAC;AACrD,QAAM,SAAiB,EAAE,MAAM,KAAK,MAAM,CAAC,MAAM,IAAI,YAAY,CAAC,EAAE,EAAE;AACtE,MAAI;AACJ,MAAI;AACF,aAAS,UAAU,IAAI;AAAA,EACzB,SAAS,GAAG;AACV,QAAI,UAAW,EAAY,OAAO;AAAA;AAAA,EAAO,IAAI,EAAE;AAC/C,WAAO;AAAA,EACT;AACA,QAAM,EAAE,SAAS,MAAM,IAAI;AAC3B,MAAI,MAAM,QAAQ,CAAC,SAAS;AAC1B,KAAC,WAAW,MAAM,OAAO,MAAM,KAAK,IAAI;AACxC,WAAO,MAAM,OAAO,IAAI;AAAA,EAC1B;AACA,QAAM,MAAM,CAAC,SAAkB,OAAO,MAAM,IAAI,MAAM,WAAY,MAAM,IAAI,IAAe;AAC3F,QAAM,SAAS,IAAI,SAAS,KAAK,IAAI,mBAAmB;AACxD,QAAM,MAAM,IAAI,KAAK,KAAK,IAAI;AAC9B,MAAI;AACF,QAAI,YAAY,UAAU;AACxB,YAAM,MAAM,MAAM,UAAU,EAAE,QAAQ,KAAK,OAAO,GAAG,OAAO,cAAc,GAAG,aAAa,CAAC;AAC3F,UAAI,sBAAsB,IAAI,eAAe,IAAI,aAAa,WAAW,GAAG,IAAI,YAAY,gBAAgB,IAAI,SAAS,YAAO,EAAE,GAAG,IAAI,OAAO,SAAS,aAAa,IAAI,OAAO,KAAK,IAAI,CAAC,KAAK,EAAE,EAAE;AACpM,aAAO;AAAA,IACT;AACA,QAAI,YAAY,UAAU;AACxB,UAAI,2BAA2B,OAAO;AAAA;AAAA,EAAQ,IAAI,EAAE;AACpD,aAAO;AAAA,IACT;AACA,UAAM,SAAS,MAAM,SAAS,MAAM;AACpC,UAAM,UAAU,IAAI,SAAS,KAAK,IAAI,mBAAmB;AACzD,UAAM,SAAS,MAAM,iBAAiB,EAAE,KAAK,IAAI,KAAK,KAAK,IAAI,WAAW,IAAI,YAAY,GAAG,SAAS,QAAQ,KAAK,SAAS,IAAI,SAAS,GAAG,aAAa,MAAM,cAAc,MAAM,MAAM,QAAQ,OAAO,GAAG,OAAO,cAAc,GAAG,cAAc,OAAO,CAAC;AACzP,UAAM,QAAQ,OAAO,MAAM;AAC3B,QAAI,CAAC,OAAO;AACV,UAAI,sCAAsC,IAAI,KAAK,CAAC,iBAAiB,IAAI,SAAS,KAAK,SAAS,qCAAqC;AACrI,aAAO;AAAA,IACT;AACA,QAAI,QAAQ;AACV,UAAI,YAAY,KAAK,cAAc,UAAU,IAAI,KAAK,GAAG,qBAAqB,UAAU,gBAAgB,OAAO,KAAK,EAAE,OAAO,MAAM,GAAG;AACtI,aAAO;AAAA,IACT;AACA,eAAW,KAAK,OAAO,SAAU,KAAI,YAAY,EAAE,IAAI,KAAK,GAAG,EAAE,SAAS,CAAC,KAAK,EAAE,OAAO,UAAU,EAAE,YAAY,IAAI,KAAK,GAAG,GAAG,EAAE,aAAa,KAAK,qBAAqB,GAAG,EAAE,WAAW,eAAe,EAAE,GAAG;AAC7M,eAAW,KAAK,OAAO,OAAQ,KAAI,YAAY,EAAE,IAAI,KAAK,EAAE,OAAO,EAAE;AACrE,QAAI;AAAA,EAAK,OAAO,SAAS,MAAM,cAAc,OAAO,OAAO,MAAM,YAAY,OAAO,QAAQ,MAAM,WAAW,OAAO,UAAU,aAAa,OAAO,OAAO,MAAM,EAAE,GAAG;AACpK,QAAI,OAAO,QAAQ,OAAQ,KAAI,WAAW,OAAO,QAAQ,MAAM,mBAAmB,OAAO,QAAQ,WAAW,IAAI,KAAK,GAAG,GAAG;AAAA,aAClH,OAAO,SAAS,OAAQ,QAAO,KAAK,uJAAuJ;AACpM,WAAO,OAAO,OAAO,SAAS,IAAI;AAAA,EACpC,SAAS,GAAG;AACV,QAAI,UAAU,aAAa,mBAAmB,EAAE,UAAW,EAAY,OAAO,EAAE;AAChF,WAAO;AAAA,EACT;AACF;;;AF/FO,IAAM,wBAAwB;AAC9B,IAAM,wBAAwB,KAAK,OAAO;AAC1C,IAAM,iBAAiB,MAAM,OAAO;AAyEpC,IAAM,mBAAN,cAA+B,MAAM;AAAA,EAC1C,YAAY,QAAgB;AAC1B,UAAM,iCAAiC,MAAM,wHAAmH;AAChK,SAAK,OAAO;AAAA,EACd;AACF;AAEA,IAAM,SAAiB,EAAE,MAAM,MAAM;AAAC,GAAG,MAAM,MAAM;AAAC,EAAE;AAGjD,SAAS,WAAW,WAA+B,SAAyB;AACjF,MAAI,UAAU,aAAa,KAAK,QAAQ,OAAO,GAAG;AAClD,MAAI,CAAC,OAAO,SAAS,KAAK,KAAK,CAAC,OAAO,WAAW,GAAG,EAAG,UAAS,IAAI,MAAM;AAC3E,MAAI,CAAC,OAAO,SAAS,GAAG,EAAG,WAAU;AACrC,QAAM,MAAM,QAAQ,QAAQ,OAAO,GAAG,EAAE,QAAQ,YAAY,EAAE,EAAE,QAAQ,QAAQ,EAAE;AAClF,SAAO,SAAS;AAClB;AAGO,SAAS,eAAe,KAAa,UAAqE,CAAC,GAAuD;AACvK,QAAM,UAAU,QAAQ,WAAW;AACnC,QAAM,MAAM,QAAQ,UAAU;AAC9B,QAAM,QAAyB,CAAC;AAChC,QAAM,UAAyB,CAAC;AAChC,QAAM,OAAO,CAAC,YAAoB;AAChC,eAAW,SAAS,YAAY,SAAS,EAAE,eAAe,KAAK,CAAC,EAAE,KAAK,CAAC,GAAG,MAAM,EAAE,KAAK,cAAc,EAAE,IAAI,CAAC,GAAG;AAC9G,YAAM,OAAO,KAAK,SAAS,MAAM,IAAI;AACrC,UAAI,MAAM,YAAY,GAAG;AACvB,YAAI,MAAM,SAAS,eAAgB,MAAK,IAAI;AAC5C;AAAA,MACF;AACA,UAAI,CAAC,MAAM,OAAO,KAAK,CAAC,MAAM,KAAK,SAAS,OAAO,KAAK,CAAC,MAAM,KAAK,SAAS,MAAM,EAAG;AACtF,YAAM,WAAW,KAAK,MAAM,GAAG,CAAC,OAAO,MAAM;AAC7C,UAAI,CAAC,WAAW,QAAQ,GAAG;AACzB,gBAAQ,KAAK,EAAE,MAAM,MAAM,QAAQ,gCAAgC,SAAS,QAAQ,CAAC,IAAI,CAAC;AAC1F,YAAI,KAAK,YAAY,SAAS,KAAK,IAAI,CAAC,QAAQ,SAAS,QAAQ,CAAC,aAAa;AAC/E;AAAA,MACF;AACA,YAAM,KAAK,EAAE,SAAS,MAAM,UAAU,SAAS,WAAW,QAAQ,WAAW,SAAS,KAAK,QAAQ,EAAE,MAAM,GAAG,EAAE,KAAK,GAAG,CAAC,GAAG,WAAW,SAAS,IAAI,EAAE,KAAK,CAAC;AAAA,IAC9J;AAAA,EACF;AACA,OAAK,GAAG;AACR,SAAO,EAAE,OAAO,QAAQ;AAC1B;AAGO,SAAS,WAA4C,OAAY,WAAW,uBAAuB,WAAW,uBAA8B;AACjJ,QAAM,UAAiB,CAAC;AACxB,MAAI,UAAe,CAAC;AACpB,MAAI,QAAQ;AACZ,aAAW,KAAK,OAAO;AACrB,QAAI,QAAQ,WAAW,QAAQ,UAAU,YAAY,QAAQ,EAAE,YAAY,WAAW;AACpF,cAAQ,KAAK,OAAO;AACpB,gBAAU,CAAC;AACX,cAAQ;AAAA,IACV;AACA,YAAQ,KAAK,CAAC;AACd,aAAS,EAAE;AAAA,EACb;AACA,MAAI,QAAQ,OAAQ,SAAQ,KAAK,OAAO;AACxC,SAAO;AACT;AAGO,SAAS,iBAAiB,SAAgC;AAC/D,MAAI;AACF,UAAM,MAAM,KAAK,MAAM,OAAO;AAC9B,WAAO,OAAO,OAAO,QAAQ,YAAY,IAAI,YAAY,IAAI,OAAO;AAAA,EACtE,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAGO,SAAS,sBAAsB,SAAgC;AACpE,MAAI;AACF,UAAM,MAAM,KAAK,MAAM,OAAO;AAC9B,QAAI,CAAC,OAAO,OAAO,QAAQ,YAAY,EAAE,oBAAoB,KAAM,QAAO;AAC1E,WAAO,IAAI;AACX,WAAO,KAAK,UAAU,GAAG;AAAA,EAC3B,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAEA,IAAM,QAAQ,CAAC,OAAe,IAAI,QAAc,CAAC,MAAM,WAAW,GAAG,EAAE,CAAC;AACxE,IAAMA,MAAK,CAAC,MAAc,GAAG,KAAK,IAAI,GAAG,KAAK,MAAM,IAAI,IAAI,CAAC,CAAC;AAE9D,SAAS,SAAS,QAA4B,MAAsB;AAClE,SAAO,IAAI,UAAU,iBAAiB,QAAQ,QAAQ,EAAE,CAAC,qBAAqB,IAAI;AACpF;AAEA,eAAe,aAAa,KAAgC;AAC1D,QAAM,OAAO,MAAM,IAAI,KAAK,EAAE,MAAM,MAAM,EAAE;AAC5C,MAAI;AACF,UAAM,OAAO,KAAK,MAAM,IAAI;AAC5B,UAAM,MAAM,OAAO,KAAK,UAAU,WAAW,KAAK,QAAQ,KAAK,OAAO,WAAW,KAAK;AACtF,QAAI,IAAK,QAAO,GAAG,GAAG,UAAU,IAAI,MAAM;AAAA,EAC5C,QAAQ;AAAA,EAER;AACA,SAAO,QAAQ,IAAI,MAAM,GAAG,OAAO,KAAK,KAAK,MAAM,GAAG,GAAG,CAAC,KAAK,EAAE;AACnE;AAGA,eAAe,QAAQ,SAAuB,KAAa,MAAyB,SAAiB,SAAoC;AACvI,WAAS,UAAU,KAAK,WAAW;AACjC,QAAI,MAAuB;AAC3B,QAAI,UAAmB;AACvB,QAAI;AACF,YAAM,MAAM,QAAQ,KAAK,KAAK,CAAC;AAAA,IACjC,SAAS,KAAK;AACZ,gBAAU;AAAA,IACZ;AACA,QAAI,QAAQ,IAAI,WAAW,OAAO,IAAI,WAAW,KAAM,OAAM,IAAI,iBAAiB,IAAI,MAAM;AAC5F,UAAM,YAAY,CAAC,OAAO,IAAI,UAAU,OAAO,IAAI,WAAW;AAC9D,QAAI,CAAC,aAAa,WAAW,SAAS;AACpC,UAAI,IAAK,QAAO;AAChB,YAAM,IAAI,MAAM,kBAAkB,mBAAmB,QAAQ,QAAQ,UAAU,OAAO,OAAO,CAAC,EAAE;AAAA,IAClG;AACA,UAAM,MAAM,UAAU,KAAK,OAAO;AAAA,EACpC;AACF;AAGA,eAAsB,iBAAiB,SAA+C;AACpF,QAAM,MAAM,QAAQ,UAAU;AAC9B,MAAI,CAAC,QAAQ,OAAO,CAAC,WAAW,QAAQ,GAAG,KAAK,CAAC,SAAS,QAAQ,GAAG,EAAE,YAAY,EAAG,OAAM,IAAI,MAAM,wBAAwB,QAAQ,OAAO,iBAAiB,EAAE;AAChK,MAAI,CAAC,QAAQ,UAAU,CAAC,QAAQ,IAAK,OAAM,IAAI,MAAM,0DAA0D;AAC/G,QAAM,UAAU,QAAQ,SAAS,KAAK,KAAK;AAC3C,MAAI,WAAW,QAAQ,SAAS,IAAK,OAAM,IAAI,MAAM,wCAAwC;AAC7F,QAAM,eAAe,QAAQ,gBAAgB;AAC7C,QAAM,QAAQ,eAAe,QAAQ,KAAK,EAAE,WAAW,QAAQ,WAAW,SAAS,QAAQ,SAAS,QAAQ,IAAI,CAAC;AACjH,QAAM,SAAuB,EAAE,SAAS,OAAO,CAAC,GAAG,UAAU,CAAC,GAAG,QAAQ,CAAC,GAAG,SAAS,MAAM,SAAS,SAAS,CAAC,GAAG,QAAQ,CAAC,CAAC,QAAQ,OAAO;AAI3I,QAAM,SAAS,oBAAI,IAAoB;AACvC,aAAW,KAAK,MAAM,OAAO;AAC3B,QAAI,EAAE,aAAa,cAAc;AAC/B,aAAO,MAAM,KAAK,CAAC;AACnB;AAAA,IACF;AACA,UAAM,UAAU,sBAAsB,aAAa,EAAE,SAAS,MAAM,CAAC;AACrE,UAAM,eAAe,YAAY,OAAO,IAAI,OAAO,WAAW,SAAS,MAAM;AAC7E,QAAI,YAAY,QAAQ,gBAAgB,cAAc;AACpD,aAAO,IAAI,EAAE,SAAS,OAAO;AAC7B,aAAO,MAAM,KAAK,EAAE,GAAG,GAAG,WAAW,aAAa,CAAC;AACnD,UAAI,KAAK,GAAG,EAAE,OAAO,WAAWA,IAAG,EAAE,SAAS,CAAC,cAAcA,IAAG,YAAY,CAAC,gDAAgDA,IAAG,YAAY,CAAC,mEAAmE;AAAA,IAClN,OAAO;AACL,YAAM,UAAU,UAAUA,IAAG,YAAY,OAAO,EAAE,YAAY,YAAY,CAAC,GAAG,YAAY,OAAO,KAAK,yBAAyB,cAAcA,IAAG,YAAY,CAAC;AAC7J,aAAO,OAAO,KAAK,EAAE,MAAM,EAAE,SAAS,QAAQ,CAAC;AAAA,IACjD;AAAA,EACF;AACA,QAAM,UAAU,QAAQ,SAAS,WAAW;AAC5C,QAAM,QAAyB,CAAC;AAChC,QAAM,YAAY,oBAAI,IAAY;AAClC,aAAW,SAAS,WAAW,OAAO,OAAO,QAAQ,oBAAoB,QAAQ,sBAAsB,qBAAqB,GAAG;AAE7H,UAAM,QAAsE,CAAC;AAC7E,eAAW,KAAK,OAAO;AACrB,YAAM,UAAU,OAAO,IAAI,EAAE,OAAO,KAAK,aAAa,EAAE,SAAS,MAAM;AACvE,YAAM,SAAS,iBAAiB,OAAO;AACvC,UAAI,QAAQ;AACV,eAAO,QAAQ,KAAK,EAAE,MAAM,EAAE,SAAS,OAAO,CAAC;AAC/C,YAAI,KAAK,YAAY,EAAE,OAAO,SAAS,MAAM,EAAE;AAAA,MACjD,MAAO,OAAM,KAAK,EAAE,GAAG,GAAG,QAAQ,CAAC;AAAA,IACrC;AACA,UAAM,KAAK,GAAG,MAAM,IAAI,CAAC,EAAE,SAAS,UAAU,GAAG,EAAE,MAAM,CAAC,CAAC;AAC3D,QAAI,QAAQ,QAAQ;AAClB,iBAAW,KAAK,MAAO,KAAI,KAAK,gBAAgB,EAAE,OAAO,KAAKA,IAAG,EAAE,SAAS,CAAC,GAAG;AAChF;AAAA,IACF;AACA,QAAI,CAAC,MAAM,OAAQ;AACnB,eAAW,KAAK,MAAO,GAAE,UAAU,SAAS,EAAE,OAAO;AACrD,UAAM,MAAM,MAAM;AAAA,MAChB;AAAA,MACA,SAAS,QAAQ,QAAQ,SAAS;AAAA,MAClC,MAAM;AACJ,cAAM,OAAO,IAAI,SAAS;AAC1B,YAAI,QAAS,MAAK,IAAI,WAAW,OAAO;AACxC,aAAK,IAAI,YAAY,KAAK,UAAU,MAAM,IAAI,CAAC,GAAG,OAAO,EAAE,OAAO,IAAI,CAAC,IAAI,MAAM,EAAE,QAAQ,EAAE,CAAC,CAAC;AAC/F,cAAM,QAAQ,CAAC,GAAG,MAAM,KAAK,IAAI,IAAI,CAAC,IAAI,IAAI,KAAK,CAAC,IAAI,WAAW,EAAE,OAAQ,CAAC,GAAG,EAAE,MAAM,mBAAmB,CAAC,GAAG,GAAG,SAAS,EAAE,OAAO,CAAC,KAAK,CAAC;AAC5I,eAAO,EAAE,QAAQ,QAAQ,SAAS,EAAE,eAAe,UAAU,QAAQ,GAAG,GAAG,GAAG,MAAM,KAAK;AAAA,MAC3F;AAAA,MACA,QAAQ,WAAW;AAAA,MACnB,QAAQ,gBAAgB;AAAA,IAC1B,EAAE,MAAM,CAAC,QAAiB;AACxB,UAAI,eAAe,iBAAkB,OAAM;AAC3C,aAAO,eAAe,QAAQ,MAAM,IAAI,MAAM,OAAO,GAAG,CAAC;AAAA,IAC3D,CAAC;AACD,QAAI,eAAe,SAAS,CAAC,IAAI,IAAI;AACnC,YAAM,UAAU,eAAe,QAAQ,IAAI,UAAU,MAAM,aAAa,GAAG;AAC3E,iBAAW,KAAK,MAAO,QAAO,OAAO,KAAK,EAAE,MAAM,EAAE,SAAS,QAAQ,CAAC;AACtE;AAAA,IACF;AACA,UAAM,OAAQ,MAAM,IAAI,KAAK,EAAE,MAAM,OAAO,CAAC,EAAE;AAC/C,QAAI,KAAK,QAAS,QAAO,UAAU,KAAK;AACxC,eAAW,KAAK,KAAK,YAAY,CAAC,GAAG;AACnC,aAAO,SAAS,KAAK,CAAC;AACtB,gBAAU,IAAI,EAAE,IAAI;AAAA,IACtB;AACA,WAAO,OAAO,KAAK,GAAI,KAAK,UAAU,CAAC,CAAE;AAAA,EAC3C;AACA,SAAO,QAAQ;AACf,MAAI,QAAQ,eAAe,CAAC,QAAQ,QAAQ;AAC1C,eAAW,KAAK,OAAO,OAAO;AAC5B,UAAI,CAAC,UAAU,IAAI,EAAE,OAAO,EAAG;AAC/B,UAAI;AACF,mBAAW,EAAE,OAAO;AACpB,eAAO,QAAQ,KAAK,EAAE,OAAO;AAAA,MAC/B,SAAS,KAAK;AACZ,YAAI,KAAK,oBAAoB,EAAE,OAAO,KAAK,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG,CAAC,EAAE;AAAA,MAC/F;AAAA,IACF;AAAA,EACF;AACA,SAAO;AACT;AAYA,eAAsB,UAAU,SAAwG;AACtI,MAAI,CAAC,QAAQ,IAAK,OAAM,IAAI,MAAM,0DAA0D;AAC5F,QAAM,MAAM,MAAM,QAAQ,QAAQ,SAAS,WAAW,OAAO,SAAS,QAAQ,QAAQ,SAAS,GAAG,OAAO,EAAE,SAAS,EAAE,eAAe,UAAU,QAAQ,GAAG,IAAI,QAAQ,mBAAmB,EAAE,IAAI,QAAQ,WAAW,GAAG,QAAQ,gBAAgB,GAAI;AACjP,MAAI,CAAC,IAAI,GAAI,OAAM,IAAI,MAAM,MAAM,aAAa,GAAG,CAAC;AACpD,QAAM,IAAK,MAAM,IAAI,KAAK,EAAE,MAAM,OAAO,CAAC,EAAE;AAC5C,QAAM,MAAM,CAAC,MAAgB,OAAO,MAAM,YAAY,IAAI,IAAI;AAC9D,SAAO;AAAA,IACL,aAAa,IAAI,EAAE,SAAS,IAAI,KAAK,IAAI,EAAE,WAAW;AAAA,IACtD,WAAW,IAAI,EAAE,SAAS,EAAE,KAAK,IAAI,EAAE,SAAS;AAAA,IAChD,SAAS,IAAI,EAAE,KAAK,IAAI,KAAK,IAAI,EAAE,OAAO;AAAA,IAC1C,WAAW,IAAI,EAAE,KAAK,MAAM,KAAK,IAAI,EAAE,SAAS,KAAK,IAAI,EAAE,MAAM;AAAA,IACjE,QAAQ,MAAM,QAAQ,EAAE,KAAK,MAAM,IAAI,EAAE,IAAI,SAAS,MAAM,QAAQ,EAAE,MAAM,IAAI,EAAE,SAAS,CAAC;AAAA,EAC9F;AACF;","names":["kb"]}
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/constants.ts","../src/debug-files.ts","../src/run.ts"],"sourcesContent":["/**\n * @guidelytech/sourcemaps — uploads JavaScript source maps to Guidely so exception stack traces are\n * shown against your original source. Zero runtime dependencies (Node ≥18: fetch, FormData, Blob).\n */\nimport { createHash } from \"node:crypto\";\nimport { readdirSync, readFileSync, statSync, unlinkSync, existsSync, writeFileSync } from \"node:fs\";\nimport { gzipSync } from \"node:zlib\";\nimport { basename, join, relative, sep } from \"node:path\";\n\nimport { DEFAULT_API_URL } from \"./constants\";\n\nexport { DEFAULT_API_URL };\n/** Per-request limits (uncompressed) and the per-file cap enforced by the upload API. Maps are sent gzipped. */\nexport const MAX_FILES_PER_REQUEST = 50;\nexport const MAX_BYTES_PER_REQUEST = 40 * 1024 * 1024;\nexport const MAX_FILE_BYTES = 100 * 1024 * 1024;\n\nexport interface Logger {\n info(message: string): void;\n warn(message: string): void;\n}\n\n/** A source map found on disk next to its minified file. */\nexport interface SourceMapFile {\n /** Absolute path of the `.map` file. */\n mapPath: string;\n /** Absolute path of the minified file the map belongs to. */\n filePath: string;\n /** URL path the minified file is served at (`urlPrefix` + posix relative path). */\n urlPath: string;\n sizeBytes: number;\n}\n\nexport interface SkippedFile {\n path: string;\n reason: string;\n}\n\n/** One map stored by the server. */\nexport interface UploadedMap {\n path: string;\n sizeBytes: number;\n sources: number;\n hasContent: boolean;\n replaced: boolean;\n}\n\nexport interface UploadOptions {\n /** Build output directory searched recursively (e.g. `dist`, `.next/static`). */\n dir: string;\n /** URL path the directory is served under (default `/`; `/_next/static/` for Next.js). */\n urlPrefix?: string;\n /** App version / commit SHA; must equal the SDK's `release` option. */\n release?: string;\n /** Guidely API base URL (default `https://api.guidely.app`). */\n apiUrl?: string;\n /** Secret project API key with the `sourcemaps:write` scope. */\n key?: string;\n /** File-name suffix of the maps to upload (default `.js.map`). */\n include?: string;\n /** Delete local `.map` files that were uploaded successfully. */\n deleteAfter?: boolean;\n /** Only list what would be uploaded. */\n dryRun?: boolean;\n logger?: Logger;\n fetch?: typeof fetch;\n /** Retries for network errors, 5xx and 429 (default 3). */\n retries?: number;\n /** Base backoff delay (default 1000ms, doubled per attempt). */\n retryDelayMs?: number;\n maxFilesPerRequest?: number;\n maxBytesPerRequest?: number;\n maxFileBytes?: number;\n}\n\nexport interface UploadResult {\n release: string | null;\n /** Maps found (and, unless skipped, uploaded or listed in a dry run). */\n files: SourceMapFile[];\n uploaded: UploadedMap[];\n errors: Array<{ path: string; message: string }>;\n skipped: SkippedFile[];\n /** Local `.map` files deleted with `deleteAfter`. */\n deleted: string[];\n dryRun: boolean;\n}\n\n/** Invalid or revoked key, or a key without the `sourcemaps:write` scope. */\nexport class GuidelyAuthError extends Error {\n constructor(status: number) {\n super(`Guidely rejected the API key (${status}): it is invalid or revoked, or it is missing the sourcemaps:write scope. Create a key under Settings → API keys.`);\n this.name = \"GuidelyAuthError\";\n }\n}\n\nconst silent: Logger = { info: () => {}, warn: () => {} };\n\n/** URL path of a built file: `prefix` (default `/`) + its posix path relative to the build directory. */\nexport function urlPathFor(urlPrefix: string | undefined, relPath: string): string {\n let prefix = (urlPrefix || \"/\").replace(/\\\\/g, \"/\");\n if (!prefix.includes(\"://\") && !prefix.startsWith(\"/\")) prefix = `/${prefix}`;\n if (!prefix.endsWith(\"/\")) prefix += \"/\";\n const rel = relPath.replace(/\\\\/g, \"/\").replace(/^(\\.\\/)+/, \"\").replace(/^\\/+/, \"\");\n return prefix + rel;\n}\n\n/** Recursively finds `*<include>` maps whose minified sibling (same name without `.map`) exists. */\nexport function findSourceMaps(dir: string, options: { urlPrefix?: string; include?: string; logger?: Logger } = {}): { files: SourceMapFile[]; skipped: SkippedFile[] } {\n const include = options.include || \".js.map\";\n const log = options.logger ?? silent;\n const files: SourceMapFile[] = [];\n const skipped: SkippedFile[] = [];\n const walk = (current: string) => {\n for (const entry of readdirSync(current, { withFileTypes: true }).sort((a, b) => a.name.localeCompare(b.name))) {\n const full = join(current, entry.name);\n if (entry.isDirectory()) {\n if (entry.name !== \"node_modules\") walk(full);\n continue;\n }\n if (!entry.isFile() || !entry.name.endsWith(include) || !entry.name.endsWith(\".map\")) continue;\n const filePath = full.slice(0, -\".map\".length);\n if (!existsSync(filePath)) {\n skipped.push({ path: full, reason: `no minified file next to it (${basename(filePath)})` });\n log.warn(`skipping ${relative(dir, full)}: no ${basename(filePath)} next to it`);\n continue;\n }\n files.push({ mapPath: full, filePath, urlPath: urlPathFor(options.urlPrefix, relative(dir, filePath).split(sep).join(\"/\")), sizeBytes: statSync(full).size });\n }\n };\n walk(dir);\n return { files, skipped };\n}\n\n/** Groups files into requests of at most `maxFiles` files and `maxBytes` bytes (a larger single file gets its own request). */\nexport function batchFiles<T extends { sizeBytes: number }>(files: T[], maxFiles = MAX_FILES_PER_REQUEST, maxBytes = MAX_BYTES_PER_REQUEST): T[][] {\n const batches: T[][] = [];\n let current: T[] = [];\n let bytes = 0;\n for (const f of files) {\n if (current.length && (current.length >= maxFiles || bytes + f.sizeBytes > maxBytes)) {\n batches.push(current);\n current = [];\n bytes = 0;\n }\n current.push(f);\n bytes += f.sizeBytes;\n }\n if (current.length) batches.push(current);\n return batches;\n}\n\n/** Returns why a map file cannot be uploaded, or null when it is a valid v3 source map. */\nexport function invalidMapReason(content: string): string | null {\n try {\n const map = JSON.parse(content) as { version?: unknown };\n return map && typeof map === \"object\" && map.version === 3 ? null : \"not a version 3 source map\";\n } catch {\n return \"not valid JSON\";\n }\n}\n\n/** A map's debug id (`debug_id` or `debugId`, as sentry-cli and the bundler plugins write it), lowercased, or null. */\nexport function mapDebugId(content: string): string | null {\n try {\n const map = JSON.parse(content) as { debug_id?: unknown; debugId?: unknown } | null;\n const id = map?.debug_id ?? map?.debugId;\n return typeof id === \"string\" && /^[0-9a-f]{8}-?[0-9a-f]{4}-?[0-9a-f]{4}-?[0-9a-f]{4}-?[0-9a-f]{12}$/i.test(id) ? id.toLowerCase() : null;\n } catch {\n return null;\n }\n}\n\n/** The map re-serialised without `sourcesContent`, or null when it has none (or is not JSON). */\nexport function withoutSourcesContent(content: string): string | null {\n try {\n const map = JSON.parse(content) as Record<string, unknown> | null;\n if (!map || typeof map !== \"object\" || !(\"sourcesContent\" in map)) return null;\n delete map.sourcesContent;\n return JSON.stringify(map);\n } catch {\n return null;\n }\n}\n\nconst sleep = (ms: number) => new Promise<void>((r) => setTimeout(r, ms));\nconst kb = (n: number) => `${Math.max(1, Math.round(n / 1024))} KB`;\n\nfunction endpoint(apiUrl: string | undefined, path: string): string {\n return `${(apiUrl || DEFAULT_API_URL).replace(/\\/+$/, \"\")}/api/v1/sourcemaps${path}`;\n}\n\nasync function errorMessage(res: Response): Promise<string> {\n const text = await res.text().catch(() => \"\");\n try {\n const body = JSON.parse(text) as { error?: { message?: string } | string; message?: string };\n const msg = typeof body.error === \"string\" ? body.error : body.error?.message ?? body.message;\n if (msg) return `${msg} (HTTP ${res.status})`;\n } catch {\n /* not JSON */\n }\n return `HTTP ${res.status}${text ? `: ${text.slice(0, 200)}` : \"\"}`;\n}\n\n/** fetch with retries on network errors, 5xx and 429; 401/403 throw {@link GuidelyAuthError}. */\nasync function request(doFetch: typeof fetch, url: string, init: () => RequestInit, retries: number, delayMs: number): Promise<Response> {\n for (let attempt = 0; ; attempt++) {\n let res: Response | null = null;\n let failure: unknown = null;\n try {\n res = await doFetch(url, init());\n } catch (err) {\n failure = err;\n }\n if (res && (res.status === 401 || res.status === 403)) throw new GuidelyAuthError(res.status);\n const retryable = !res || res.status >= 500 || res.status === 429;\n if (!retryable || attempt >= retries) {\n if (res) return res;\n throw new Error(`network error: ${failure instanceof Error ? failure.message : String(failure)}`);\n }\n await sleep(delayMs * 2 ** attempt);\n }\n}\n\n/** Finds the maps under `dir` and uploads them in batches. Throws {@link GuidelyAuthError} for a rejected key. */\nexport async function uploadSourceMaps(options: UploadOptions): Promise<UploadResult> {\n const log = options.logger ?? silent;\n if (!options.dir || !existsSync(options.dir) || !statSync(options.dir).isDirectory()) throw new Error(`directory not found: ${options.dir || \"(missing --dir)\"}`);\n if (!options.dryRun && !options.key) throw new Error(\"missing API key: pass --key or set GUIDELY_SOURCEMAP_KEY\");\n const release = options.release?.trim() || null;\n if (release && release.length > 100) throw new Error(\"release must be at most 100 characters\");\n const maxFileBytes = options.maxFileBytes ?? MAX_FILE_BYTES;\n const found = findSourceMaps(options.dir, { urlPrefix: options.urlPrefix, include: options.include, logger: log });\n const result: UploadResult = { release, files: [], uploaded: [], errors: [], skipped: found.skipped, deleted: [], dryRun: !!options.dryRun };\n // Maps over the limit are uploaded without their embedded sources when that brings them under it\n // (frames still map to original files and lines, without code context). A map that is still too\n // large fails the run: it is usually the main bundle, the one most errors point at.\n const shrunk = new Map<string, string>();\n for (const f of found.files) {\n if (f.sizeBytes <= maxFileBytes) {\n result.files.push(f);\n continue;\n }\n const smaller = withoutSourcesContent(readFileSync(f.mapPath, \"utf8\"));\n const smallerBytes = smaller === null ? 0 : Buffer.byteLength(smaller, \"utf8\");\n if (smaller !== null && smallerBytes <= maxFileBytes) {\n shrunk.set(f.mapPath, smaller);\n result.files.push({ ...f, sizeBytes: smallerBytes });\n log.warn(`${f.urlPath}.map is ${kb(f.sizeBytes)}, over the ${kb(maxFileBytes)} limit: uploading it without sourcesContent (${kb(smallerBytes)}), so traces map to your files and lines but show no code context`);\n } else {\n const message = `map is ${kb(smaller === null ? f.sizeBytes : smallerBytes)}${smaller === null ? \"\" : \" without sourcesContent\"}, over the ${kb(maxFileBytes)} limit; errors in this file stay minified (split the bundle into smaller chunks)`;\n result.errors.push({ path: f.urlPath, message });\n }\n }\n const doFetch = options.fetch ?? globalThis.fetch;\n const valid: SourceMapFile[] = [];\n const succeeded = new Set<string>();\n for (const batch of batchFiles(result.files, options.maxFilesPerRequest, options.maxBytesPerRequest ?? MAX_BYTES_PER_REQUEST)) {\n // Maps are read one batch at a time so memory stays bounded by the request size.\n const items: Array<SourceMapFile & { content: string; gzipped?: Buffer }> = [];\n for (const f of batch) {\n const content = shrunk.get(f.mapPath) ?? readFileSync(f.mapPath, \"utf8\");\n const reason = invalidMapReason(content);\n if (reason) {\n result.skipped.push({ path: f.mapPath, reason });\n log.warn(`skipping ${f.urlPath}.map: ${reason}`);\n } else items.push({ ...f, content });\n }\n valid.push(...items.map(({ content: _content, ...f }) => f));\n if (options.dryRun) {\n for (const f of items) log.info(`would upload ${f.urlPath} (${kb(f.sizeBytes)})`);\n continue;\n }\n if (!items.length) continue;\n for (const f of items) f.gzipped = gzipSync(f.content);\n const res = await request(\n doFetch,\n endpoint(options.apiUrl, \"/upload\"),\n () => {\n const form = new FormData();\n if (release) form.set(\"release\", release);\n // A debug id lets the server find the map even when the script is served from another path or release.\n form.set(\n \"manifest\",\n JSON.stringify(\n items.map((f, i) => {\n const debugId = mapDebugId(f.content);\n return { field: `f${i}`, path: f.urlPath, ...(debugId ? { debugId } : {}) };\n }),\n ),\n );\n items.forEach((f, i) => form.set(`f${i}`, new Blob([new Uint8Array(f.gzipped!)], { type: \"application/gzip\" }), `${basename(f.mapPath)}.gz`));\n return { method: \"POST\", headers: { Authorization: `Bearer ${options.key}` }, body: form };\n },\n options.retries ?? 3,\n options.retryDelayMs ?? 1000,\n ).catch((err: unknown) => {\n if (err instanceof GuidelyAuthError) throw err;\n return err instanceof Error ? err : new Error(String(err));\n });\n if (res instanceof Error || !res.ok) {\n const message = res instanceof Error ? res.message : await errorMessage(res);\n for (const f of items) result.errors.push({ path: f.urlPath, message });\n continue;\n }\n const body = (await res.json().catch(() => ({}))) as { release?: string; uploaded?: UploadedMap[]; errors?: Array<{ path: string; message: string }> };\n if (body.release) result.release = body.release;\n for (const u of body.uploaded ?? []) {\n result.uploaded.push(u);\n succeeded.add(u.path);\n }\n result.errors.push(...(body.errors ?? []));\n }\n result.files = valid;\n if (options.deleteAfter && !options.dryRun) {\n for (const f of result.files) {\n if (!succeeded.has(f.urlPath)) continue;\n try {\n unlinkSync(f.mapPath);\n result.deleted.push(f.mapPath);\n } catch (err) {\n log.warn(`could not delete ${f.mapPath}: ${err instanceof Error ? err.message : String(err)}`);\n }\n }\n }\n return result;\n}\n\n/** What `GET /api/v1/sourcemaps/whoami` reports about a key (fields are read leniently). */\nexport interface WhoAmI {\n projectName: string | null;\n projectId: string | null;\n keyName: string | null;\n keyPrefix: string | null;\n scopes: string[];\n}\n\n/** Checks a key against the API (`guidely-sourcemaps verify`). */\nexport async function verifyKey(options: Pick<UploadOptions, \"apiUrl\" | \"key\" | \"fetch\" | \"retries\" | \"retryDelayMs\">): Promise<WhoAmI> {\n if (!options.key) throw new Error(\"missing API key: pass --key or set GUIDELY_SOURCEMAP_KEY\");\n const res = await request(options.fetch ?? globalThis.fetch, endpoint(options.apiUrl, \"/whoami\"), () => ({ headers: { Authorization: `Bearer ${options.key}`, Accept: \"application/json\" } }), options.retries ?? 3, options.retryDelayMs ?? 1000);\n if (!res.ok) throw new Error(await errorMessage(res));\n const b = (await res.json().catch(() => ({}))) as Record<string, any>;\n const str = (v: unknown) => (typeof v === \"string\" && v ? v : null);\n return {\n projectName: str(b.project?.name) ?? str(b.projectName),\n projectId: str(b.project?.id) ?? str(b.projectId),\n keyName: str(b.key?.name) ?? str(b.keyName),\n keyPrefix: str(b.key?.prefix) ?? str(b.keyPrefix) ?? str(b.prefix),\n scopes: Array.isArray(b.key?.scopes) ? b.key.scopes : Array.isArray(b.scopes) ? b.scopes : [],\n };\n}\n\n// ---------------------------------------------------------------------------\n// Debug ids (`guidely-sourcemaps inject`)\n// ---------------------------------------------------------------------------\n\n/**\n * The snippet `sentry-cli sourcemaps inject` writes: it records the file's debug id under its own\n * stack trace in `_sentryDebugIds`, where Sentry SDKs find it and send it with errors.\n */\nexport const debugIdSnippet = (id: string) =>\n `!function(){try{var e=\"undefined\"!=typeof window?window:\"undefined\"!=typeof global?global:\"undefined\"!=typeof globalThis?globalThis:\"undefined\"!=typeof self?self:{},n=(new e.Error).stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]=\"${id}\")}catch(e){}}();`;\n\n/** A deterministic debug id for a file's contents (a UUID), so rebuilding the same code gives the same id. */\nexport function debugIdFor(content: string): string {\n const h = createHash(\"sha256\").update(content).digest(\"hex\");\n const variant = ((parseInt(h[16]!, 16) & 0x3) | 0x8).toString(16);\n return `${h.slice(0, 8)}-${h.slice(8, 12)}-4${h.slice(13, 16)}-${variant}${h.slice(17, 20)}-${h.slice(20, 32)}`;\n}\n\nexport interface InjectResult {\n injected: Array<{ filePath: string; mapPath: string; debugId: string }>;\n /** Files that already had a debug id (kept; their map gets it when missing). */\n existing: Array<{ filePath: string; debugId: string }>;\n skipped: SkippedFile[];\n}\n\nconst DEBUG_ID_COMMENT = /\\/\\/# debugId=([0-9a-fA-F-]{32,36})\\s*$/m;\n\n/**\n * Gives every minified file that has a map a debug id, like `sentry-cli sourcemaps inject`: the\n * snippet as the file's first line, a `//# debugId=` comment at its end, and `debug_id` / `debugId`\n * in the map (whose mappings move down one line for the snippet). Run it on the build output before\n * uploading and deploying; uploads then carry the debug id and errors find their map by it.\n */\nexport function injectDebugIds(options: { dir: string; include?: string; dryRun?: boolean; logger?: Logger }): InjectResult {\n const log = options.logger ?? silent;\n if (!options.dir || !existsSync(options.dir) || !statSync(options.dir).isDirectory()) throw new Error(`directory not found: ${options.dir || \"(missing --dir)\"}`);\n const found = findSourceMaps(options.dir, { include: options.include, logger: log });\n const result: InjectResult = { injected: [], existing: [], skipped: found.skipped };\n for (const f of found.files) {\n const js = readFileSync(f.filePath, \"utf8\");\n const mapText = readFileSync(f.mapPath, \"utf8\");\n const reason = invalidMapReason(mapText);\n if (reason) {\n result.skipped.push({ path: f.mapPath, reason });\n log.warn(`skipping ${relative(options.dir, f.mapPath)}: ${reason}`);\n continue;\n }\n const map = JSON.parse(mapText) as Record<string, unknown>;\n const had = DEBUG_ID_COMMENT.exec(js)?.[1]?.toLowerCase();\n if (had) {\n if (!mapDebugId(mapText) && !options.dryRun) writeFileSync(f.mapPath, JSON.stringify({ ...map, debug_id: had, debugId: had }));\n result.existing.push({ filePath: f.filePath, debugId: had });\n continue;\n }\n const id = debugIdFor(js);\n if (!options.dryRun) {\n writeFileSync(f.filePath, `${debugIdSnippet(id)}\\n${js.replace(/\\s*$/, \"\")}\\n//# debugId=${id}\\n`);\n writeFileSync(f.mapPath, JSON.stringify({ ...map, ...(typeof map.mappings === \"string\" ? { mappings: `;${map.mappings}` } : {}), debug_id: id, debugId: id }));\n }\n result.injected.push({ filePath: f.filePath, mapPath: f.mapPath, debugId: id });\n log.info(`${options.dryRun ? \"would inject\" : \"injected\"} ${id} into ${relative(options.dir, f.filePath)}`);\n }\n return result;\n}\n\nexport { runCli } from \"./run\";\nexport type { CliIO } from \"./run\";\nexport { findDebugFiles, nativeKind, proguardUuid, uploadDebugFiles, uploadProguardMapping, writeProguardProperties, MAX_DEBUG_FILE_BYTES } from \"./debug-files\";\nexport type { DebugFileCandidate, DebugFileUploadOptions, DebugFileUploadResult } from \"./debug-files\";\n","/** Default Guidely API base URL (same as the browser SDK). Its own module so `run.ts` can import it without an import cycle. */\nexport const DEFAULT_API_URL = \"https://api.guidely.app\";\n","/**\n * Native debug file uploads (`upload-dif`, `upload-proguard`): iOS dSYMs / Mach-O, ELF symbols\n * (Android NDK, Flutter `--split-debug-info`) and ProGuard / R8 mappings. Files go up in SHA-1\n * addressed, gzipped chunks and are then assembled by the server, the same protocol sentry-cli\n * uses, so large dSYMs upload in parallel-sized pieces and an interrupted upload resumes.\n */\nimport { createHash } from \"node:crypto\";\nimport { closeSync, existsSync, mkdirSync, openSync, readFileSync, readSync, readdirSync, statSync, writeFileSync } from \"node:fs\";\nimport { basename, dirname, join, relative, sep } from \"node:path\";\nimport { gzipSync } from \"node:zlib\";\nimport { DEFAULT_API_URL } from \"./constants\";\n\nexport interface DebugFileLogger {\n info(message: string): void;\n warn(message: string): void;\n}\n\nexport interface DebugFileUploadOptions {\n apiUrl?: string;\n /** Secret project API key with the `debug-files:write` scope. */\n key?: string;\n /** App version the files belong to (ProGuard mappings fall back to it when an event names no mapping UUID). */\n appVersion?: string;\n dryRun?: boolean;\n logger?: DebugFileLogger;\n fetch?: typeof fetch;\n retries?: number;\n retryDelayMs?: number;\n /** Assemble polls before giving up (default 30, one per second). */\n maxPolls?: number;\n}\n\nexport interface DebugFileCandidate {\n path: string;\n /** Name sent to the server (path relative to the searched directory, `MyApp.app.dSYM/Contents/Resources/DWARF/MyApp`). */\n name: string;\n kind: \"macho\" | \"elf\" | \"proguard\";\n sizeBytes: number;\n}\n\nexport interface DebugFileUploadResult {\n files: DebugFileCandidate[];\n uploaded: Array<{ name: string; debugId: string; kind: string; arch: string }>;\n errors: Array<{ name: string; message: string }>;\n}\n\ninterface ChunkOptions {\n url: string;\n chunkSize: number;\n chunksPerRequest: number;\n maxRequestSize: number;\n maxFileSize: number;\n concurrency?: number;\n compression?: string[];\n}\n\ninterface AssembleAnswer {\n state: \"ok\" | \"not_found\" | \"created\" | \"assembling\" | \"error\";\n missingChunks: string[];\n detail: string | null;\n dif: { debugId: string; cpuName: string; symbolType: string } | null;\n}\n\nexport const MAX_DEBUG_FILE_BYTES = 256 * 1024 * 1024;\n\nconst sha1 = (b: Buffer) => createHash(\"sha1\").update(b).digest(\"hex\");\nconst sleep = (ms: number) => new Promise<void>((r) => setTimeout(r, ms));\n\n/** Mach-O, universal (fat) Mach-O or ELF, from the first bytes of a file. */\nexport function nativeKind(head: Buffer): \"macho\" | \"elf\" | null {\n if (head.length < 8) return null;\n const le = head.readUInt32LE(0);\n if (le === 0xfeedface || le === 0xfeedfacf) return \"macho\";\n const be = head.readUInt32BE(0);\n // Java class files share 0xcafebabe; their second word is a version number, not a small arch count.\n if ((be === 0xcafebabe || be === 0xcafebabf) && head.readUInt32BE(4) > 0 && head.readUInt32BE(4) <= 32) return \"macho\";\n if (be === 0x7f454c46) return \"elf\";\n return null;\n}\n\nfunction readHead(path: string): Buffer {\n const fd = openSync(path, \"r\");\n try {\n const b = Buffer.alloc(8);\n const n = readSync(fd, b, 0, 8, 0);\n return b.subarray(0, n);\n } finally {\n closeSync(fd);\n }\n}\n\n/** Mach-O and ELF files under the given paths (dSYM bundles, .xcarchive/dSYMs, symbols directories), recursively. */\nexport function findDebugFiles(paths: string[], logger?: DebugFileLogger): DebugFileCandidate[] {\n const out: DebugFileCandidate[] = [];\n const visit = (path: string, root: string) => {\n const st = statSync(path);\n if (st.isDirectory()) {\n for (const entry of readdirSync(path).sort()) if (entry !== \"node_modules\" && !entry.startsWith(\".\")) visit(join(path, entry), root);\n return;\n }\n if (!st.isFile() || st.size < 64) return;\n const kind = nativeKind(readHead(path));\n if (!kind) return;\n const name = (relative(root, path) || basename(path)).split(sep).join(\"/\");\n if (st.size > MAX_DEBUG_FILE_BYTES) {\n logger?.warn(`skipping ${name}: larger than ${MAX_DEBUG_FILE_BYTES / 1024 / 1024} MB`);\n return;\n }\n out.push({ path, name, kind, sizeBytes: st.size });\n };\n for (const p of paths) {\n if (!existsSync(p)) throw new Error(`not found: ${p}`);\n visit(p, statSync(p).isDirectory() ? p : dirname(p));\n }\n return out;\n}\n\n/** UUID v5 of a mapping's contents (namespace uuid5(DNS, \"guardsquare.com\")), as sentry-cli and the server derive it. */\nexport function proguardUuid(content: Buffer): string {\n const v5 = (ns: Buffer, name: Buffer) => {\n const h = createHash(\"sha1\").update(ns).update(name).digest().subarray(0, 16);\n h[6] = (h[6]! & 0x0f) | 0x50;\n h[8] = (h[8]! & 0x3f) | 0x80;\n return Buffer.from(h);\n };\n const id = v5(v5(Buffer.from(\"6ba7b8109dad11d180b400c04fd430c8\", \"hex\"), Buffer.from(\"guardsquare.com\")), content).toString(\"hex\");\n return `${id.slice(0, 8)}-${id.slice(8, 12)}-${id.slice(12, 16)}-${id.slice(16, 20)}-${id.slice(20)}`;\n}\n\n/** Writes `io.guidely.ProguardUuids=<uuid>` for the Android SDK to read from its assets. */\nexport function writeProguardProperties(path: string, uuid: string) {\n mkdirSync(dirname(path), { recursive: true });\n writeFileSync(path, `io.guidely.ProguardUuids=${uuid}\\n`);\n}\n\nclass Api {\n constructor(private readonly o: DebugFileUploadOptions) {\n if (!o.key) throw new Error(\"missing API key: pass --key or set GUIDELY_SOURCEMAP_KEY (a secret key with the debug-files:write scope)\");\n }\n\n url(path: string) {\n return `${(this.o.apiUrl || DEFAULT_API_URL).replace(/\\/+$/, \"\")}${path}`;\n }\n\n async call(url: string, init: () => RequestInit): Promise<Response> {\n const doFetch = this.o.fetch ?? globalThis.fetch;\n const retries = this.o.retries ?? 3;\n for (let attempt = 0; ; attempt++) {\n let res: Response | null = null;\n let failure: unknown = null;\n try {\n const i = init();\n res = await doFetch(url, { ...i, headers: { Authorization: `Bearer ${this.o.key}`, Accept: \"application/json\", ...(i.headers as Record<string, string>) } });\n } catch (err) {\n failure = err;\n }\n if (res && (res.status === 401 || res.status === 403)) throw new Error(`the API key was rejected (HTTP ${res.status}); it needs the debug-files:write scope`);\n const retryable = !res || res.status >= 500 || res.status === 429;\n if (!retryable || attempt >= retries) {\n if (res && !res.ok) throw new Error(`HTTP ${res.status}: ${(await res.text().catch(() => \"\")).slice(0, 300)}`);\n if (res) return res;\n throw new Error(`network error: ${failure instanceof Error ? failure.message : String(failure)}`);\n }\n await sleep((this.o.retryDelayMs ?? 1000) * 2 ** attempt);\n }\n }\n}\n\n/** Uploads one file's missing chunks, then assembles it (polling while the server works). */\nasync function uploadOne(api: Api, opts: ChunkOptions, file: Buffer, body: { name: string; debug_id?: string; app_version?: string }, maxPolls: number, retryDelayMs: number): Promise<AssembleAnswer> {\n const checksum = sha1(file);\n const chunks: Buffer[] = [];\n for (let i = 0; i < file.length; i += opts.chunkSize) chunks.push(file.subarray(i, i + opts.chunkSize));\n if (!chunks.length) chunks.push(Buffer.alloc(0));\n const bySum = new Map(chunks.map((c) => [sha1(c), c]));\n const assemble = async (): Promise<AssembleAnswer> => {\n const res = await api.call(api.url(\"/api/v1/debug-files/assemble\"), () => ({ method: \"POST\", headers: { \"Content-Type\": \"application/json\" }, body: JSON.stringify({ [checksum]: { ...body, chunks: chunks.map(sha1) } }) }));\n const answer = ((await res.json()) as Record<string, AssembleAnswer>)[checksum];\n if (!answer) throw new Error(\"the server did not answer for this file\");\n return answer;\n };\n let answer = await assemble();\n if (answer.state === \"not_found\" && answer.missingChunks.length) {\n const missing = answer.missingChunks.map((s) => bySum.get(s)).filter((c): c is Buffer => !!c);\n const gzip = opts.compression?.includes(\"gzip\") ?? false;\n for (let i = 0; i < missing.length; ) {\n const form = new FormData();\n let size = 0;\n let n = 0;\n while (i < missing.length && n < opts.chunksPerRequest && (n === 0 || size + missing[i]!.length <= opts.maxRequestSize)) {\n const c = missing[i]!;\n const data = gzip ? gzipSync(c) : c;\n form.append(gzip ? \"file_gzip\" : \"file\", new Blob([new Uint8Array(data)]), sha1(c));\n size += data.length;\n n++;\n i++;\n }\n await api.call(opts.url, () => ({ method: \"POST\", body: form }));\n }\n answer = await assemble();\n }\n for (let polls = 0; (answer.state === \"created\" || answer.state === \"assembling\") && polls < maxPolls; polls++) {\n await sleep(retryDelayMs);\n answer = await assemble();\n }\n return answer;\n}\n\nasync function chunkOptions(api: Api): Promise<ChunkOptions> {\n const res = await api.call(api.url(\"/api/v1/debug-files/chunk-upload\"), () => ({}));\n return (await res.json()) as ChunkOptions;\n}\n\n/** Finds and uploads native debug files (dSYM DWARF files, Mach-O, ELF). */\nexport async function uploadDebugFiles(paths: string[], o: DebugFileUploadOptions = {}): Promise<DebugFileUploadResult> {\n const files = findDebugFiles(paths, o.logger);\n const result: DebugFileUploadResult = { files, uploaded: [], errors: [] };\n if (o.dryRun || !files.length) return result;\n const api = new Api(o);\n const opts = await chunkOptions(api);\n for (const f of files) {\n try {\n const answer = await uploadOne(api, opts, readFileSync(f.path), { name: f.name, ...(o.appVersion ? { app_version: o.appVersion } : {}) }, o.maxPolls ?? 30, o.retryDelayMs ?? 1000);\n if (answer.state === \"ok\" && answer.dif) result.uploaded.push({ name: f.name, debugId: answer.dif.debugId, kind: answer.dif.symbolType, arch: answer.dif.cpuName });\n else result.errors.push({ name: f.name, message: answer.detail ?? `assembly ${answer.state}` });\n } catch (err) {\n result.errors.push({ name: f.name, message: (err as Error).message });\n }\n }\n return result;\n}\n\n/**\n * Uploads a ProGuard / R8 `mapping.txt`. Its UUID is `uuid` (when the build embedded one, e.g. with\n * `--write-properties` before the build) or derived from the contents; the app version lets events\n * without a UUID find it too.\n */\nexport async function uploadProguardMapping(path: string, o: DebugFileUploadOptions & { uuid?: string; writeProperties?: string } = {}): Promise<{ uuid: string; uploaded: boolean; error?: string }> {\n if (!existsSync(path) || !statSync(path).isFile()) throw new Error(`mapping file not found: ${path}`);\n const content = readFileSync(path);\n if (!/ -> [^\\s]+:\\s*$/m.test(content.subarray(0, 64 * 1024).toString(\"utf8\"))) throw new Error(`${path} doesn't look like a ProGuard / R8 mapping file`);\n const uuid = (o.uuid || proguardUuid(content)).toLowerCase();\n if (o.writeProperties) writeProguardProperties(o.writeProperties, uuid);\n if (o.dryRun) return { uuid, uploaded: false };\n const api = new Api(o);\n const answer = await uploadOne(api, await chunkOptions(api), content, { name: `proguard/${uuid}.txt`, debug_id: uuid, ...(o.appVersion ? { app_version: o.appVersion } : {}) }, o.maxPolls ?? 30, o.retryDelayMs ?? 1000);\n return answer.state === \"ok\" ? { uuid, uploaded: true } : { uuid, uploaded: false, error: answer.detail ?? `assembly ${answer.state}` };\n}\n","import { DEFAULT_API_URL } from \"./constants\";\nimport { GuidelyAuthError, injectDebugIds, uploadSourceMaps, verifyKey, type Logger } from \"./index\";\nimport { uploadDebugFiles, uploadProguardMapping } from \"./debug-files\";\n\n/** Where the CLI writes and what it reads (injectable for tests). */\nexport interface CliIO {\n env?: Record<string, string | undefined>;\n out?: (line: string) => void;\n err?: (line: string) => void;\n fetch?: typeof fetch;\n retryDelayMs?: number;\n}\n\nconst HELP = `Usage:\n guidely-sourcemaps upload --dir <dir> [options] Upload source maps from a build directory\n guidely-sourcemaps inject --dir <dir> Add debug ids to minified files and their maps (before upload)\n guidely-sourcemaps upload-dif <path>... [options] Upload native debug files: dSYMs, Mach-O, ELF (NDK, Flutter --split-debug-info)\n guidely-sourcemaps upload-proguard <mapping.txt> Upload a ProGuard / R8 mapping\n guidely-sourcemaps verify [--key <key>] Check the API key\n\nOptions:\n --dir <dir> Build output directory, searched recursively (e.g. dist, .next/static)\n --url-prefix <path> URL path the directory is served under (default \"/\"; \"/_next/static/\" for Next.js)\n --release <release> App version or commit SHA; set the same value as the SDK's \\`release\\` (env GUIDELY_RELEASE)\n --api-url <url> Guidely API URL (env GUIDELY_API_URL, default ${DEFAULT_API_URL})\n --key <key> Secret API key with the sourcemaps:write scope (env GUIDELY_SOURCEMAP_KEY)\n --include <suffix> File-name suffix of the maps to upload (default .js.map)\n --delete-after Delete local .map files once uploaded (so they are not deployed publicly)\n --dry-run List what would be uploaded without uploading\n -h, --help Show this help\n\nDebug files (key with the debug-files:write scope):\n --app-version <v> App version the files belong to (events without a mapping UUID use the newest mapping for it)\n --uuid <uuid> upload-proguard: the mapping UUID your build embedded (default: derived from the file)\n --write-properties <path>\n upload-proguard: also write io.guidely.ProguardUuids=<uuid> there\n (e.g. app/src/main/assets/guidely-debug-meta.properties)`;\n\nconst BOOLEAN = new Set([\"delete-after\", \"dry-run\", \"help\"]);\n\n/** Parses `--name value`, `--name=value` and boolean flags. */\nexport function parseArgs(argv: string[]): { command: string | undefined; flags: Record<string, string | boolean>; args: string[] } {\n const flags: Record<string, string | boolean> = {};\n const args: string[] = [];\n let command: string | undefined;\n for (let i = 0; i < argv.length; i++) {\n const arg = argv[i] as string;\n if (arg === \"-h\") flags.help = true;\n else if (arg.startsWith(\"--\")) {\n const [name, inline] = arg.slice(2).split(/=(.*)/s, 2) as [string, string | undefined];\n if (BOOLEAN.has(name)) flags[name] = inline === undefined ? true : inline !== \"false\";\n else if (inline !== undefined) flags[name] = inline;\n else {\n const next = argv[i + 1];\n if (next === undefined || next.startsWith(\"--\")) throw new Error(`--${name} needs a value`);\n flags[name] = next;\n i++;\n }\n } else if (!command) command = arg;\n // Only the debug-file commands take paths.\n else if (command === \"upload-dif\" || (command === \"upload-proguard\" && !args.length)) args.push(arg);\n else throw new Error(`unexpected argument: ${arg}`);\n }\n return { command, flags, args };\n}\n\nconst kb = (n: number) => `${Math.max(1, Math.round(n / 1024))} KB`;\n\n/** Runs the CLI; resolves with the process exit code. Never prints the API key. */\nexport async function runCli(argv: string[], io: CliIO = {}): Promise<number> {\n const env = io.env ?? process.env;\n const out = io.out ?? ((l: string) => console.log(l));\n const err = io.err ?? ((l: string) => console.error(l));\n const logger: Logger = { info: out, warn: (m) => err(`warning: ${m}`) };\n let parsed: ReturnType<typeof parseArgs>;\n try {\n parsed = parseArgs(argv);\n } catch (e) {\n err(`error: ${(e as Error).message}\\n\\n${HELP}`);\n return 1;\n }\n const { command, flags, args } = parsed;\n if (flags.help || !command) {\n (command || flags.help ? out : err)(HELP);\n return flags.help ? 0 : 1;\n }\n const str = (name: string) => (typeof flags[name] === \"string\" ? (flags[name] as string) : undefined);\n const apiUrl = str(\"api-url\") || env.GUIDELY_API_URL || DEFAULT_API_URL;\n const key = str(\"key\") || env.GUIDELY_SOURCEMAP_KEY;\n try {\n if (command === \"verify\") {\n const who = await verifyKey({ apiUrl, key, fetch: io.fetch, retryDelayMs: io.retryDelayMs });\n out(`Key OK for project ${who.projectName ?? who.projectId ?? \"(unknown)\"}${who.keyPrefix ? ` (key gly_sk_${who.keyPrefix}…)` : \"\"}${who.scopes.length ? `, scopes: ${who.scopes.join(\", \")}` : \"\"}`);\n return 0;\n }\n if (command === \"inject\") {\n const result = injectDebugIds({ dir: str(\"dir\") ?? \"\", include: str(\"include\"), dryRun: flags[\"dry-run\"] === true, logger });\n if (!result.injected.length && !result.existing.length) {\n err(`error: no minified files with source maps in ${str(\"dir\")} (looked for *${str(\"include\") || \".js.map\"} files next to their minified file)`);\n return 1;\n }\n out(`\\n${result.injected.length} ${flags[\"dry-run\"] === true ? \"would get\" : \"got\"} a debug id, ${result.existing.length} already had one, ${result.skipped.length} skipped.`);\n return 0;\n }\n if (command === \"upload-dif\" || command === \"upload-proguard\") return await uploadNative(command, args, { apiUrl, key, str, dryRun: flags[\"dry-run\"] === true, io, out, err, logger });\n if (command !== \"upload\") {\n err(`error: unknown command \"${command}\"\\n\\n${HELP}`);\n return 1;\n }\n const dryRun = flags[\"dry-run\"] === true;\n const release = str(\"release\") || env.GUIDELY_RELEASE || undefined;\n const result = await uploadSourceMaps({ dir: str(\"dir\") ?? \"\", urlPrefix: str(\"url-prefix\"), release, apiUrl, key, include: str(\"include\"), deleteAfter: flags[\"delete-after\"] === true, dryRun, fetch: io.fetch, retryDelayMs: io.retryDelayMs, logger });\n const total = result.files.length;\n if (!total) {\n err(`error: no source maps to upload in ${str(\"dir\")} (looked for *${str(\"include\") || \".js.map\"} files next to their minified file)`);\n return 1;\n }\n if (dryRun) {\n out(`Dry run: ${total} source map${total === 1 ? \"\" : \"s\"} would be uploaded${release ? ` for release ${release}` : \"\"} to ${apiUrl}.`);\n return 0;\n }\n for (const u of result.uploaded) out(`uploaded ${u.path} (${kb(u.sizeBytes)}, ${u.sources} source${u.sources === 1 ? \"\" : \"s\"}${u.hasContent ? \"\" : \", no sourcesContent\"}${u.replaced ? \", replaced\" : \"\"})`);\n for (const e of result.errors) err(`failed ${e.path}: ${e.message}`);\n out(`\\n${result.uploaded.length} uploaded, ${result.errors.length} failed, ${result.skipped.length} skipped${result.release ? ` (release ${result.release})` : \"\"}.`);\n if (result.deleted.length) out(`Deleted ${result.deleted.length} local .map file${result.deleted.length === 1 ? \"\" : \"s\"}.`);\n else if (result.uploaded.length) logger.warn(\"the .map files are still in your build output: deploying them publishes your original source. Pass --delete-after (or exclude *.map from the deploy).\");\n return result.errors.length ? 1 : 0;\n } catch (e) {\n err(`error: ${e instanceof GuidelyAuthError ? e.message : (e as Error).message}`);\n return 1;\n }\n}\n\n/** `upload-dif` / `upload-proguard`. */\nasync function uploadNative(command: \"upload-dif\" | \"upload-proguard\", args: string[], c: { apiUrl: string; key: string | undefined; str: (n: string) => string | undefined; dryRun: boolean; io: CliIO; out: (l: string) => void; err: (l: string) => void; logger: Logger }): Promise<number> {\n const common = { apiUrl: c.apiUrl, key: c.key, appVersion: c.str(\"app-version\"), dryRun: c.dryRun, fetch: c.io.fetch, retryDelayMs: c.io.retryDelayMs, logger: c.logger };\n if (!args.length) {\n c.err(`error: ${command} needs ${command === \"upload-dif\" ? \"one or more paths\" : \"the mapping.txt path\"}\\n\\n${HELP}`);\n return 1;\n }\n if (command === \"upload-proguard\") {\n const r = await uploadProguardMapping(args[0]!, { ...common, uuid: c.str(\"uuid\"), writeProperties: c.str(\"write-properties\") });\n if (c.str(\"write-properties\")) c.out(`wrote io.guidely.ProguardUuids=${r.uuid} to ${c.str(\"write-properties\")}`);\n if (c.dryRun) c.out(`Dry run: ${args[0]} would be uploaded as ProGuard mapping ${r.uuid}.`);\n else if (r.uploaded) c.out(`uploaded ProGuard mapping ${r.uuid}${common.appVersion ? ` (app version ${common.appVersion})` : \"\"}`);\n else c.err(`failed ${args[0]}: ${r.error}`);\n return r.uploaded || c.dryRun ? 0 : 1;\n }\n const result = await uploadDebugFiles(args, common);\n if (!result.files.length) {\n c.err(`error: no debug files (Mach-O, dSYM, ELF) found in ${args.join(\", \")}`);\n return 1;\n }\n if (c.dryRun) {\n for (const f of result.files) c.out(`would upload ${f.name} (${f.kind}, ${kb(f.sizeBytes)})`);\n return 0;\n }\n for (const u of result.uploaded) c.out(`uploaded ${u.name} (${u.kind} ${u.arch}, ${u.debugId})`);\n for (const e of result.errors) c.err(`failed ${e.name}: ${e.message}`);\n c.out(`\\n${result.uploaded.length} uploaded, ${result.errors.length} failed.`);\n return result.errors.length ? 1 : 0;\n}\n"],"mappings":"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","names":["createHash","readdirSync","readFileSync","statSync","existsSync","writeFileSync","gzipSync","basename","join","relative","sep","answer","result","readdirSync","join","existsSync","basename","relative","sep","statSync","sleep","kb","readFileSync","gzipSync","createHash","writeFileSync"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@guidelytech/sourcemaps",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.17.0",
|
|
4
4
|
"description": "Upload JavaScript source maps to Guidely so error stack traces show your original code",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
@@ -30,7 +30,7 @@
|
|
|
30
30
|
"tsup": "^8.5.1",
|
|
31
31
|
"typescript": "^5.9.3",
|
|
32
32
|
"typescript-eslint": "^8.46.0",
|
|
33
|
-
"vitest": "^
|
|
33
|
+
"vitest": "^4.1.12"
|
|
34
34
|
},
|
|
35
35
|
"publishConfig": {
|
|
36
36
|
"access": "public"
|