@guidelytech/sourcemaps 0.0.0-stage → 0.4.1
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 +84 -2
- package/dist/cli.js +14 -0
- package/dist/cli.js.map +1 -0
- package/dist/index.d.ts +120 -0
- package/dist/index.js +312 -0
- package/dist/index.js.map +1 -0
- package/package.json +59 -3
package/README.md
CHANGED
|
@@ -1,3 +1,85 @@
|
|
|
1
|
-
#
|
|
1
|
+
# @guidelytech/sourcemaps
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Uploads your build's JavaScript source maps to Guidely so exceptions on the Errors page show your
|
|
4
|
+
original files, functions and lines instead of minified code. Zero runtime dependencies; Node ≥ 18.
|
|
5
|
+
|
|
6
|
+
```sh
|
|
7
|
+
npx @guidelytech/sourcemaps upload --dir dist --release "$GIT_SHA" --delete-after
|
|
8
|
+
```
|
|
9
|
+
|
|
10
|
+
1. Create a secret API key with the **sourcemaps:write** scope in Guidely (Settings → API keys) and
|
|
11
|
+
store it as a CI secret `GUIDELY_SOURCEMAP_KEY`. Never ship it to the browser.
|
|
12
|
+
2. Build with source maps enabled.
|
|
13
|
+
3. Upload them with the same **release** the SDK reports, then deploy (without the `.map` files).
|
|
14
|
+
4. Set the same value as the SDK's `release` option: `createGuidely({ …, release })`,
|
|
15
|
+
`NEXT_PUBLIC_GUIDELY_RELEASE`, `VITE_GUIDELY_RELEASE` / `PUBLIC_GUIDELY_RELEASE`, or
|
|
16
|
+
`<script data-release="…">` for the script tag. Exceptions are matched to maps by release + script URL path.
|
|
17
|
+
|
|
18
|
+
## Commands
|
|
19
|
+
|
|
20
|
+
```
|
|
21
|
+
guidely-sourcemaps upload --dir <dir> [--url-prefix /] [--release <r>] [--api-url <url>] [--key <key>]
|
|
22
|
+
[--include .js.map] [--delete-after] [--dry-run]
|
|
23
|
+
guidely-sourcemaps verify [--key <key>] [--api-url <url>]
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
| Option | Env | Default | |
|
|
27
|
+
|---|---|---|---|
|
|
28
|
+
| `--dir` | — | (required) | Build output searched recursively for `*.js.map` files whose minified file (same name without `.map`) sits next to them. |
|
|
29
|
+
| `--url-prefix` | — | `/` | URL path the directory is served under. The minified file's URL path is `url-prefix` + its path relative to `--dir`. |
|
|
30
|
+
| `--release` | `GUIDELY_RELEASE` | — | App version / commit SHA (≤ 100 chars). |
|
|
31
|
+
| `--api-url` | `GUIDELY_API_URL` | `https://api.guidely.app` | Your Guidely API. |
|
|
32
|
+
| `--key` | `GUIDELY_SOURCEMAP_KEY` | — | Secret key with `sourcemaps:write`. Never printed. |
|
|
33
|
+
| `--include` | — | `.js.map` | File-name suffix of the maps to upload. |
|
|
34
|
+
| `--delete-after` | — | off | Delete each local `.map` once it uploaded successfully (otherwise deploying the build publishes your original source). |
|
|
35
|
+
| `--dry-run` | — | off | List what would be uploaded; no key needed. |
|
|
36
|
+
|
|
37
|
+
Maps must be JSON with `version: 3` (others are skipped with a warning) and ≤ 25 MB. Uploads are
|
|
38
|
+
batched (≤ 50 files / 40 MB per request) and retried 3× on network errors, 5xx and 429. The command
|
|
39
|
+
prints one line per map and a summary, and exits with code 1 if any map failed, the key was rejected
|
|
40
|
+
(401/403: invalid or revoked key, or missing `sourcemaps:write` scope) or no maps were found.
|
|
41
|
+
|
|
42
|
+
`verify` calls `GET /api/v1/sourcemaps/whoami` and prints the project and key prefix.
|
|
43
|
+
|
|
44
|
+
## Frameworks
|
|
45
|
+
|
|
46
|
+
| Build | Enable maps | Upload |
|
|
47
|
+
|---|---|---|
|
|
48
|
+
| Vite (React, Vue, SvelteKit client) | `build: { sourcemap: true }` (`"hidden"` keeps the `//# sourceMappingURL` comment out of the files) | `--dir dist --url-prefix /` (SvelteKit: `--dir .svelte-kit/output/client --url-prefix /`) |
|
|
49
|
+
| Next.js | `productionBrowserSourceMaps: true` in `next.config.js` | `--dir .next/static --url-prefix /_next/static/` |
|
|
50
|
+
| Served from a CDN path | — | `--url-prefix /assets/` (or the full URL, e.g. `https://cdn.example.com/app/`, matching the script URLs in stack traces) |
|
|
51
|
+
|
|
52
|
+
## GitHub Actions
|
|
53
|
+
|
|
54
|
+
```yaml
|
|
55
|
+
- run: pnpm build
|
|
56
|
+
env:
|
|
57
|
+
VITE_GUIDELY_RELEASE: ${{ github.sha }} # same release the SDK reports
|
|
58
|
+
- run: npx @guidelytech/sourcemaps upload --dir dist --release ${{ github.sha }} --delete-after
|
|
59
|
+
env:
|
|
60
|
+
GUIDELY_SOURCEMAP_KEY: ${{ secrets.GUIDELY_SOURCEMAP_KEY }}
|
|
61
|
+
GUIDELY_API_URL: https://guidely.example.com
|
|
62
|
+
- run: ./deploy.sh # deploys dist without the .map files
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
Next.js:
|
|
66
|
+
|
|
67
|
+
```yaml
|
|
68
|
+
- run: pnpm next build
|
|
69
|
+
env:
|
|
70
|
+
NEXT_PUBLIC_GUIDELY_RELEASE: ${{ github.sha }}
|
|
71
|
+
- run: npx @guidelytech/sourcemaps upload --dir .next/static --url-prefix /_next/static/ --release ${{ github.sha }} --delete-after
|
|
72
|
+
env:
|
|
73
|
+
GUIDELY_SOURCEMAP_KEY: ${{ secrets.GUIDELY_SOURCEMAP_KEY }}
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
## Programmatic use
|
|
77
|
+
|
|
78
|
+
```ts
|
|
79
|
+
import { uploadSourceMaps } from "@guidelytech/sourcemaps";
|
|
80
|
+
|
|
81
|
+
const result = await uploadSourceMaps({ dir: "dist", urlPrefix: "/", release: process.env.GIT_SHA, key: process.env.GUIDELY_SOURCEMAP_KEY, deleteAfter: true });
|
|
82
|
+
// { release, files, uploaded: [{ path, sizeBytes, sources, hasContent, replaced }], errors: [{ path, message }], skipped, deleted, dryRun }
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
Also exported: `findSourceMaps`, `urlPathFor`, `batchFiles`, `verifyKey`, `runCli`, `GuidelyAuthError`.
|
package/dist/cli.js
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
// src/cli.ts
|
|
4
|
+
import { runCli } from "./index.js";
|
|
5
|
+
runCli(process.argv.slice(2)).then(
|
|
6
|
+
(code) => {
|
|
7
|
+
process.exitCode = code;
|
|
8
|
+
},
|
|
9
|
+
(err) => {
|
|
10
|
+
console.error(`error: ${err instanceof Error ? err.message : String(err)}`);
|
|
11
|
+
process.exitCode = 1;
|
|
12
|
+
}
|
|
13
|
+
);
|
|
14
|
+
//# sourceMappingURL=cli.js.map
|
package/dist/cli.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/cli.ts"],"sourcesContent":["import { runCli } from \"./index.js\";\n\nrunCli(process.argv.slice(2)).then(\n (code) => {\n process.exitCode = code;\n },\n (err: unknown) => {\n console.error(`error: ${err instanceof Error ? err.message : String(err)}`);\n process.exitCode = 1;\n },\n);\n"],"mappings":";;;AAAA,SAAS,cAAc;AAEvB,OAAO,QAAQ,KAAK,MAAM,CAAC,CAAC,EAAE;AAAA,EAC5B,CAAC,SAAS;AACR,YAAQ,WAAW;AAAA,EACrB;AAAA,EACA,CAAC,QAAiB;AAChB,YAAQ,MAAM,UAAU,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG,CAAC,EAAE;AAC1E,YAAQ,WAAW;AAAA,EACrB;AACF;","names":[]}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
/** Default Guidely API base URL (same as the browser SDK). Its own module so `run.ts` can import it without an import cycle. */
|
|
2
|
+
declare const DEFAULT_API_URL = "https://api.guidely.app";
|
|
3
|
+
|
|
4
|
+
/** Where the CLI writes and what it reads (injectable for tests). */
|
|
5
|
+
interface CliIO {
|
|
6
|
+
env?: Record<string, string | undefined>;
|
|
7
|
+
out?: (line: string) => void;
|
|
8
|
+
err?: (line: string) => void;
|
|
9
|
+
fetch?: typeof fetch;
|
|
10
|
+
retryDelayMs?: number;
|
|
11
|
+
}
|
|
12
|
+
/** Runs the CLI; resolves with the process exit code. Never prints the API key. */
|
|
13
|
+
declare function runCli(argv: string[], io?: CliIO): Promise<number>;
|
|
14
|
+
|
|
15
|
+
/** Per-request limits and the per-file cap enforced by the upload API. */
|
|
16
|
+
declare const MAX_FILES_PER_REQUEST = 50;
|
|
17
|
+
declare const MAX_BYTES_PER_REQUEST: number;
|
|
18
|
+
declare const MAX_FILE_BYTES: number;
|
|
19
|
+
interface Logger {
|
|
20
|
+
info(message: string): void;
|
|
21
|
+
warn(message: string): void;
|
|
22
|
+
}
|
|
23
|
+
/** A source map found on disk next to its minified file. */
|
|
24
|
+
interface SourceMapFile {
|
|
25
|
+
/** Absolute path of the `.map` file. */
|
|
26
|
+
mapPath: string;
|
|
27
|
+
/** Absolute path of the minified file the map belongs to. */
|
|
28
|
+
filePath: string;
|
|
29
|
+
/** URL path the minified file is served at (`urlPrefix` + posix relative path). */
|
|
30
|
+
urlPath: string;
|
|
31
|
+
sizeBytes: number;
|
|
32
|
+
}
|
|
33
|
+
interface SkippedFile {
|
|
34
|
+
path: string;
|
|
35
|
+
reason: string;
|
|
36
|
+
}
|
|
37
|
+
/** One map stored by the server. */
|
|
38
|
+
interface UploadedMap {
|
|
39
|
+
path: string;
|
|
40
|
+
sizeBytes: number;
|
|
41
|
+
sources: number;
|
|
42
|
+
hasContent: boolean;
|
|
43
|
+
replaced: boolean;
|
|
44
|
+
}
|
|
45
|
+
interface UploadOptions {
|
|
46
|
+
/** Build output directory searched recursively (e.g. `dist`, `.next/static`). */
|
|
47
|
+
dir: string;
|
|
48
|
+
/** URL path the directory is served under (default `/`; `/_next/static/` for Next.js). */
|
|
49
|
+
urlPrefix?: string;
|
|
50
|
+
/** App version / commit SHA; must equal the SDK's `release` option. */
|
|
51
|
+
release?: string;
|
|
52
|
+
/** Guidely API base URL (default `https://api.guidely.app`). */
|
|
53
|
+
apiUrl?: string;
|
|
54
|
+
/** Secret project API key with the `sourcemaps:write` scope. */
|
|
55
|
+
key?: string;
|
|
56
|
+
/** File-name suffix of the maps to upload (default `.js.map`). */
|
|
57
|
+
include?: string;
|
|
58
|
+
/** Delete local `.map` files that were uploaded successfully. */
|
|
59
|
+
deleteAfter?: boolean;
|
|
60
|
+
/** Only list what would be uploaded. */
|
|
61
|
+
dryRun?: boolean;
|
|
62
|
+
logger?: Logger;
|
|
63
|
+
fetch?: typeof fetch;
|
|
64
|
+
/** Retries for network errors, 5xx and 429 (default 3). */
|
|
65
|
+
retries?: number;
|
|
66
|
+
/** Base backoff delay (default 1000ms, doubled per attempt). */
|
|
67
|
+
retryDelayMs?: number;
|
|
68
|
+
maxFilesPerRequest?: number;
|
|
69
|
+
maxBytesPerRequest?: number;
|
|
70
|
+
maxFileBytes?: number;
|
|
71
|
+
}
|
|
72
|
+
interface UploadResult {
|
|
73
|
+
release: string | null;
|
|
74
|
+
/** Maps found (and, unless skipped, uploaded or listed in a dry run). */
|
|
75
|
+
files: SourceMapFile[];
|
|
76
|
+
uploaded: UploadedMap[];
|
|
77
|
+
errors: Array<{
|
|
78
|
+
path: string;
|
|
79
|
+
message: string;
|
|
80
|
+
}>;
|
|
81
|
+
skipped: SkippedFile[];
|
|
82
|
+
/** Local `.map` files deleted with `deleteAfter`. */
|
|
83
|
+
deleted: string[];
|
|
84
|
+
dryRun: boolean;
|
|
85
|
+
}
|
|
86
|
+
/** Invalid or revoked key, or a key without the `sourcemaps:write` scope. */
|
|
87
|
+
declare class GuidelyAuthError extends Error {
|
|
88
|
+
constructor(status: number);
|
|
89
|
+
}
|
|
90
|
+
/** URL path of a built file: `prefix` (default `/`) + its posix path relative to the build directory. */
|
|
91
|
+
declare function urlPathFor(urlPrefix: string | undefined, relPath: string): string;
|
|
92
|
+
/** Recursively finds `*<include>` maps whose minified sibling (same name without `.map`) exists. */
|
|
93
|
+
declare function findSourceMaps(dir: string, options?: {
|
|
94
|
+
urlPrefix?: string;
|
|
95
|
+
include?: string;
|
|
96
|
+
logger?: Logger;
|
|
97
|
+
}): {
|
|
98
|
+
files: SourceMapFile[];
|
|
99
|
+
skipped: SkippedFile[];
|
|
100
|
+
};
|
|
101
|
+
/** Groups files into requests of at most `maxFiles` files and `maxBytes` bytes (a larger single file gets its own request). */
|
|
102
|
+
declare function batchFiles<T extends {
|
|
103
|
+
sizeBytes: number;
|
|
104
|
+
}>(files: T[], maxFiles?: number, maxBytes?: number): T[][];
|
|
105
|
+
/** Returns why a map file cannot be uploaded, or null when it is a valid v3 source map. */
|
|
106
|
+
declare function invalidMapReason(content: string): string | null;
|
|
107
|
+
/** Finds the maps under `dir` and uploads them in batches. Throws {@link GuidelyAuthError} for a rejected key. */
|
|
108
|
+
declare function uploadSourceMaps(options: UploadOptions): Promise<UploadResult>;
|
|
109
|
+
/** What `GET /api/v1/sourcemaps/whoami` reports about a key (fields are read leniently). */
|
|
110
|
+
interface WhoAmI {
|
|
111
|
+
projectName: string | null;
|
|
112
|
+
projectId: string | null;
|
|
113
|
+
keyName: string | null;
|
|
114
|
+
keyPrefix: string | null;
|
|
115
|
+
scopes: string[];
|
|
116
|
+
}
|
|
117
|
+
/** Checks a key against the API (`guidely-sourcemaps verify`). */
|
|
118
|
+
declare function verifyKey(options: Pick<UploadOptions, "apiUrl" | "key" | "fetch" | "retries" | "retryDelayMs">): Promise<WhoAmI>;
|
|
119
|
+
|
|
120
|
+
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 };
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,312 @@
|
|
|
1
|
+
// src/index.ts
|
|
2
|
+
import { readdirSync, readFileSync, statSync, unlinkSync, existsSync } from "fs";
|
|
3
|
+
import { basename, join, relative, sep } from "path";
|
|
4
|
+
|
|
5
|
+
// src/constants.ts
|
|
6
|
+
var DEFAULT_API_URL = "https://api.guidely.app";
|
|
7
|
+
|
|
8
|
+
// src/run.ts
|
|
9
|
+
var HELP = `Usage:
|
|
10
|
+
guidely-sourcemaps upload --dir <dir> [options] Upload source maps from a build directory
|
|
11
|
+
guidely-sourcemaps verify [--key <key>] Check the API key
|
|
12
|
+
|
|
13
|
+
Options:
|
|
14
|
+
--dir <dir> Build output directory, searched recursively (e.g. dist, .next/static)
|
|
15
|
+
--url-prefix <path> URL path the directory is served under (default "/"; "/_next/static/" for Next.js)
|
|
16
|
+
--release <release> App version or commit SHA; set the same value as the SDK's \`release\` (env GUIDELY_RELEASE)
|
|
17
|
+
--api-url <url> Guidely API URL (env GUIDELY_API_URL, default ${DEFAULT_API_URL})
|
|
18
|
+
--key <key> Secret API key with the sourcemaps:write scope (env GUIDELY_SOURCEMAP_KEY)
|
|
19
|
+
--include <suffix> File-name suffix of the maps to upload (default .js.map)
|
|
20
|
+
--delete-after Delete local .map files once uploaded (so they are not deployed publicly)
|
|
21
|
+
--dry-run List what would be uploaded without uploading
|
|
22
|
+
-h, --help Show this help`;
|
|
23
|
+
var BOOLEAN = /* @__PURE__ */ new Set(["delete-after", "dry-run", "help"]);
|
|
24
|
+
function parseArgs(argv) {
|
|
25
|
+
const flags = {};
|
|
26
|
+
let command;
|
|
27
|
+
for (let i = 0; i < argv.length; i++) {
|
|
28
|
+
const arg = argv[i];
|
|
29
|
+
if (arg === "-h") flags.help = true;
|
|
30
|
+
else if (arg.startsWith("--")) {
|
|
31
|
+
const [name, inline] = arg.slice(2).split(/=(.*)/s, 2);
|
|
32
|
+
if (BOOLEAN.has(name)) flags[name] = inline === void 0 ? true : inline !== "false";
|
|
33
|
+
else if (inline !== void 0) flags[name] = inline;
|
|
34
|
+
else {
|
|
35
|
+
const next = argv[i + 1];
|
|
36
|
+
if (next === void 0 || next.startsWith("--")) throw new Error(`--${name} needs a value`);
|
|
37
|
+
flags[name] = next;
|
|
38
|
+
i++;
|
|
39
|
+
}
|
|
40
|
+
} else if (!command) command = arg;
|
|
41
|
+
else throw new Error(`unexpected argument: ${arg}`);
|
|
42
|
+
}
|
|
43
|
+
return { command, flags };
|
|
44
|
+
}
|
|
45
|
+
var kb = (n) => `${Math.max(1, Math.round(n / 1024))} KB`;
|
|
46
|
+
async function runCli(argv, io = {}) {
|
|
47
|
+
const env = io.env ?? process.env;
|
|
48
|
+
const out = io.out ?? ((l) => console.log(l));
|
|
49
|
+
const err = io.err ?? ((l) => console.error(l));
|
|
50
|
+
const logger = { info: out, warn: (m) => err(`warning: ${m}`) };
|
|
51
|
+
let parsed;
|
|
52
|
+
try {
|
|
53
|
+
parsed = parseArgs(argv);
|
|
54
|
+
} catch (e) {
|
|
55
|
+
err(`error: ${e.message}
|
|
56
|
+
|
|
57
|
+
${HELP}`);
|
|
58
|
+
return 1;
|
|
59
|
+
}
|
|
60
|
+
const { command, flags } = parsed;
|
|
61
|
+
if (flags.help || !command) {
|
|
62
|
+
(command || flags.help ? out : err)(HELP);
|
|
63
|
+
return flags.help ? 0 : 1;
|
|
64
|
+
}
|
|
65
|
+
const str = (name) => typeof flags[name] === "string" ? flags[name] : void 0;
|
|
66
|
+
const apiUrl = str("api-url") || env.GUIDELY_API_URL || DEFAULT_API_URL;
|
|
67
|
+
const key = str("key") || env.GUIDELY_SOURCEMAP_KEY;
|
|
68
|
+
try {
|
|
69
|
+
if (command === "verify") {
|
|
70
|
+
const who = await verifyKey({ apiUrl, key, fetch: io.fetch, retryDelayMs: io.retryDelayMs });
|
|
71
|
+
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(", ")}` : ""}`);
|
|
72
|
+
return 0;
|
|
73
|
+
}
|
|
74
|
+
if (command !== "upload") {
|
|
75
|
+
err(`error: unknown command "${command}"
|
|
76
|
+
|
|
77
|
+
${HELP}`);
|
|
78
|
+
return 1;
|
|
79
|
+
}
|
|
80
|
+
const dryRun = flags["dry-run"] === true;
|
|
81
|
+
const release = str("release") || env.GUIDELY_RELEASE || void 0;
|
|
82
|
+
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 });
|
|
83
|
+
const total = result.files.length;
|
|
84
|
+
if (!total) {
|
|
85
|
+
err(`error: no source maps to upload in ${str("dir")} (looked for *${str("include") || ".js.map"} files next to their minified file)`);
|
|
86
|
+
return 1;
|
|
87
|
+
}
|
|
88
|
+
if (dryRun) {
|
|
89
|
+
out(`Dry run: ${total} source map${total === 1 ? "" : "s"} would be uploaded${release ? ` for release ${release}` : ""} to ${apiUrl}.`);
|
|
90
|
+
return 0;
|
|
91
|
+
}
|
|
92
|
+
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" : ""})`);
|
|
93
|
+
for (const e of result.errors) err(`failed ${e.path}: ${e.message}`);
|
|
94
|
+
out(`
|
|
95
|
+
${result.uploaded.length} uploaded, ${result.errors.length} failed, ${result.skipped.length} skipped${result.release ? ` (release ${result.release})` : ""}.`);
|
|
96
|
+
if (result.deleted.length) out(`Deleted ${result.deleted.length} local .map file${result.deleted.length === 1 ? "" : "s"}.`);
|
|
97
|
+
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).");
|
|
98
|
+
return result.errors.length ? 1 : 0;
|
|
99
|
+
} catch (e) {
|
|
100
|
+
err(`error: ${e instanceof GuidelyAuthError ? e.message : e.message}`);
|
|
101
|
+
return 1;
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
// src/index.ts
|
|
106
|
+
var MAX_FILES_PER_REQUEST = 50;
|
|
107
|
+
var MAX_BYTES_PER_REQUEST = 40 * 1024 * 1024;
|
|
108
|
+
var MAX_FILE_BYTES = 25 * 1024 * 1024;
|
|
109
|
+
var GuidelyAuthError = class extends Error {
|
|
110
|
+
constructor(status) {
|
|
111
|
+
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 \u2192 API keys.`);
|
|
112
|
+
this.name = "GuidelyAuthError";
|
|
113
|
+
}
|
|
114
|
+
};
|
|
115
|
+
var silent = { info: () => {
|
|
116
|
+
}, warn: () => {
|
|
117
|
+
} };
|
|
118
|
+
function urlPathFor(urlPrefix, relPath) {
|
|
119
|
+
let prefix = (urlPrefix || "/").replace(/\\/g, "/");
|
|
120
|
+
if (!prefix.includes("://") && !prefix.startsWith("/")) prefix = `/${prefix}`;
|
|
121
|
+
if (!prefix.endsWith("/")) prefix += "/";
|
|
122
|
+
const rel = relPath.replace(/\\/g, "/").replace(/^(\.\/)+/, "").replace(/^\/+/, "");
|
|
123
|
+
return prefix + rel;
|
|
124
|
+
}
|
|
125
|
+
function findSourceMaps(dir, options = {}) {
|
|
126
|
+
const include = options.include || ".js.map";
|
|
127
|
+
const log = options.logger ?? silent;
|
|
128
|
+
const files = [];
|
|
129
|
+
const skipped = [];
|
|
130
|
+
const walk = (current) => {
|
|
131
|
+
for (const entry of readdirSync(current, { withFileTypes: true }).sort((a, b) => a.name.localeCompare(b.name))) {
|
|
132
|
+
const full = join(current, entry.name);
|
|
133
|
+
if (entry.isDirectory()) {
|
|
134
|
+
if (entry.name !== "node_modules") walk(full);
|
|
135
|
+
continue;
|
|
136
|
+
}
|
|
137
|
+
if (!entry.isFile() || !entry.name.endsWith(include) || !entry.name.endsWith(".map")) continue;
|
|
138
|
+
const filePath = full.slice(0, -".map".length);
|
|
139
|
+
if (!existsSync(filePath)) {
|
|
140
|
+
skipped.push({ path: full, reason: `no minified file next to it (${basename(filePath)})` });
|
|
141
|
+
log.warn(`skipping ${relative(dir, full)}: no ${basename(filePath)} next to it`);
|
|
142
|
+
continue;
|
|
143
|
+
}
|
|
144
|
+
files.push({ mapPath: full, filePath, urlPath: urlPathFor(options.urlPrefix, relative(dir, filePath).split(sep).join("/")), sizeBytes: statSync(full).size });
|
|
145
|
+
}
|
|
146
|
+
};
|
|
147
|
+
walk(dir);
|
|
148
|
+
return { files, skipped };
|
|
149
|
+
}
|
|
150
|
+
function batchFiles(files, maxFiles = MAX_FILES_PER_REQUEST, maxBytes = MAX_BYTES_PER_REQUEST) {
|
|
151
|
+
const batches = [];
|
|
152
|
+
let current = [];
|
|
153
|
+
let bytes = 0;
|
|
154
|
+
for (const f of files) {
|
|
155
|
+
if (current.length && (current.length >= maxFiles || bytes + f.sizeBytes > maxBytes)) {
|
|
156
|
+
batches.push(current);
|
|
157
|
+
current = [];
|
|
158
|
+
bytes = 0;
|
|
159
|
+
}
|
|
160
|
+
current.push(f);
|
|
161
|
+
bytes += f.sizeBytes;
|
|
162
|
+
}
|
|
163
|
+
if (current.length) batches.push(current);
|
|
164
|
+
return batches;
|
|
165
|
+
}
|
|
166
|
+
function invalidMapReason(content) {
|
|
167
|
+
try {
|
|
168
|
+
const map = JSON.parse(content);
|
|
169
|
+
return map && typeof map === "object" && map.version === 3 ? null : "not a version 3 source map";
|
|
170
|
+
} catch {
|
|
171
|
+
return "not valid JSON";
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
var sleep = (ms) => new Promise((r) => setTimeout(r, ms));
|
|
175
|
+
var kb2 = (n) => `${Math.max(1, Math.round(n / 1024))} KB`;
|
|
176
|
+
function endpoint(apiUrl, path) {
|
|
177
|
+
return `${(apiUrl || DEFAULT_API_URL).replace(/\/+$/, "")}/api/v1/sourcemaps${path}`;
|
|
178
|
+
}
|
|
179
|
+
async function errorMessage(res) {
|
|
180
|
+
const text = await res.text().catch(() => "");
|
|
181
|
+
try {
|
|
182
|
+
const body = JSON.parse(text);
|
|
183
|
+
const msg = typeof body.error === "string" ? body.error : body.error?.message ?? body.message;
|
|
184
|
+
if (msg) return `${msg} (HTTP ${res.status})`;
|
|
185
|
+
} catch {
|
|
186
|
+
}
|
|
187
|
+
return `HTTP ${res.status}${text ? `: ${text.slice(0, 200)}` : ""}`;
|
|
188
|
+
}
|
|
189
|
+
async function request(doFetch, url, init, retries, delayMs) {
|
|
190
|
+
for (let attempt = 0; ; attempt++) {
|
|
191
|
+
let res = null;
|
|
192
|
+
let failure = null;
|
|
193
|
+
try {
|
|
194
|
+
res = await doFetch(url, init());
|
|
195
|
+
} catch (err) {
|
|
196
|
+
failure = err;
|
|
197
|
+
}
|
|
198
|
+
if (res && (res.status === 401 || res.status === 403)) throw new GuidelyAuthError(res.status);
|
|
199
|
+
const retryable = !res || res.status >= 500 || res.status === 429;
|
|
200
|
+
if (!retryable || attempt >= retries) {
|
|
201
|
+
if (res) return res;
|
|
202
|
+
throw new Error(`network error: ${failure instanceof Error ? failure.message : String(failure)}`);
|
|
203
|
+
}
|
|
204
|
+
await sleep(delayMs * 2 ** attempt);
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
async function uploadSourceMaps(options) {
|
|
208
|
+
const log = options.logger ?? silent;
|
|
209
|
+
if (!options.dir || !existsSync(options.dir) || !statSync(options.dir).isDirectory()) throw new Error(`directory not found: ${options.dir || "(missing --dir)"}`);
|
|
210
|
+
if (!options.dryRun && !options.key) throw new Error("missing API key: pass --key or set GUIDELY_SOURCEMAP_KEY");
|
|
211
|
+
const release = options.release?.trim() || null;
|
|
212
|
+
if (release && release.length > 100) throw new Error("release must be at most 100 characters");
|
|
213
|
+
const maxFileBytes = options.maxFileBytes ?? MAX_FILE_BYTES;
|
|
214
|
+
const found = findSourceMaps(options.dir, { urlPrefix: options.urlPrefix, include: options.include, logger: log });
|
|
215
|
+
const result = { release, files: [], uploaded: [], errors: [], skipped: found.skipped, deleted: [], dryRun: !!options.dryRun };
|
|
216
|
+
for (const f of found.files) {
|
|
217
|
+
if (f.sizeBytes > maxFileBytes) {
|
|
218
|
+
result.skipped.push({ path: f.mapPath, reason: `larger than ${kb2(maxFileBytes)}` });
|
|
219
|
+
log.warn(`skipping ${f.urlPath}.map: ${kb2(f.sizeBytes)} is over the ${kb2(maxFileBytes)} limit`);
|
|
220
|
+
} else result.files.push(f);
|
|
221
|
+
}
|
|
222
|
+
const doFetch = options.fetch ?? globalThis.fetch;
|
|
223
|
+
const valid = [];
|
|
224
|
+
const succeeded = /* @__PURE__ */ new Set();
|
|
225
|
+
for (const batch of batchFiles(result.files, options.maxFilesPerRequest, options.maxBytesPerRequest ?? MAX_BYTES_PER_REQUEST)) {
|
|
226
|
+
const items = [];
|
|
227
|
+
for (const f of batch) {
|
|
228
|
+
const content = readFileSync(f.mapPath, "utf8");
|
|
229
|
+
const reason = invalidMapReason(content);
|
|
230
|
+
if (reason) {
|
|
231
|
+
result.skipped.push({ path: f.mapPath, reason });
|
|
232
|
+
log.warn(`skipping ${f.urlPath}.map: ${reason}`);
|
|
233
|
+
} else items.push({ ...f, content });
|
|
234
|
+
}
|
|
235
|
+
valid.push(...items.map(({ content: _content, ...f }) => f));
|
|
236
|
+
if (options.dryRun) {
|
|
237
|
+
for (const f of items) log.info(`would upload ${f.urlPath} (${kb2(f.sizeBytes)})`);
|
|
238
|
+
continue;
|
|
239
|
+
}
|
|
240
|
+
if (!items.length) continue;
|
|
241
|
+
const res = await request(
|
|
242
|
+
doFetch,
|
|
243
|
+
endpoint(options.apiUrl, "/upload"),
|
|
244
|
+
() => {
|
|
245
|
+
const form = new FormData();
|
|
246
|
+
if (release) form.set("release", release);
|
|
247
|
+
form.set("manifest", JSON.stringify(items.map((f, i) => ({ field: `f${i}`, path: f.urlPath }))));
|
|
248
|
+
items.forEach((f, i) => form.set(`f${i}`, new Blob([f.content], { type: "application/json" }), basename(f.mapPath)));
|
|
249
|
+
return { method: "POST", headers: { Authorization: `Bearer ${options.key}` }, body: form };
|
|
250
|
+
},
|
|
251
|
+
options.retries ?? 3,
|
|
252
|
+
options.retryDelayMs ?? 1e3
|
|
253
|
+
).catch((err) => {
|
|
254
|
+
if (err instanceof GuidelyAuthError) throw err;
|
|
255
|
+
return err instanceof Error ? err : new Error(String(err));
|
|
256
|
+
});
|
|
257
|
+
if (res instanceof Error || !res.ok) {
|
|
258
|
+
const message = res instanceof Error ? res.message : await errorMessage(res);
|
|
259
|
+
for (const f of items) result.errors.push({ path: f.urlPath, message });
|
|
260
|
+
continue;
|
|
261
|
+
}
|
|
262
|
+
const body = await res.json().catch(() => ({}));
|
|
263
|
+
if (body.release) result.release = body.release;
|
|
264
|
+
for (const u of body.uploaded ?? []) {
|
|
265
|
+
result.uploaded.push(u);
|
|
266
|
+
succeeded.add(u.path);
|
|
267
|
+
}
|
|
268
|
+
result.errors.push(...body.errors ?? []);
|
|
269
|
+
}
|
|
270
|
+
result.files = valid;
|
|
271
|
+
if (options.deleteAfter && !options.dryRun) {
|
|
272
|
+
for (const f of result.files) {
|
|
273
|
+
if (!succeeded.has(f.urlPath)) continue;
|
|
274
|
+
try {
|
|
275
|
+
unlinkSync(f.mapPath);
|
|
276
|
+
result.deleted.push(f.mapPath);
|
|
277
|
+
} catch (err) {
|
|
278
|
+
log.warn(`could not delete ${f.mapPath}: ${err instanceof Error ? err.message : String(err)}`);
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
return result;
|
|
283
|
+
}
|
|
284
|
+
async function verifyKey(options) {
|
|
285
|
+
if (!options.key) throw new Error("missing API key: pass --key or set GUIDELY_SOURCEMAP_KEY");
|
|
286
|
+
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 ?? 1e3);
|
|
287
|
+
if (!res.ok) throw new Error(await errorMessage(res));
|
|
288
|
+
const b = await res.json().catch(() => ({}));
|
|
289
|
+
const str = (v) => typeof v === "string" && v ? v : null;
|
|
290
|
+
return {
|
|
291
|
+
projectName: str(b.project?.name) ?? str(b.projectName),
|
|
292
|
+
projectId: str(b.project?.id) ?? str(b.projectId),
|
|
293
|
+
keyName: str(b.key?.name) ?? str(b.keyName),
|
|
294
|
+
keyPrefix: str(b.key?.prefix) ?? str(b.keyPrefix) ?? str(b.prefix),
|
|
295
|
+
scopes: Array.isArray(b.key?.scopes) ? b.key.scopes : Array.isArray(b.scopes) ? b.scopes : []
|
|
296
|
+
};
|
|
297
|
+
}
|
|
298
|
+
export {
|
|
299
|
+
DEFAULT_API_URL,
|
|
300
|
+
GuidelyAuthError,
|
|
301
|
+
MAX_BYTES_PER_REQUEST,
|
|
302
|
+
MAX_FILES_PER_REQUEST,
|
|
303
|
+
MAX_FILE_BYTES,
|
|
304
|
+
batchFiles,
|
|
305
|
+
findSourceMaps,
|
|
306
|
+
invalidMapReason,
|
|
307
|
+
runCli,
|
|
308
|
+
uploadSourceMaps,
|
|
309
|
+
urlPathFor,
|
|
310
|
+
verifyKey
|
|
311
|
+
};
|
|
312
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +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 { basename, join, relative, sep } from \"node:path\";\n\nimport { DEFAULT_API_URL } from \"./constants\";\n\nexport { DEFAULT_API_URL };\n/** Per-request limits and the per-file cap enforced by the upload API. */\nexport const MAX_FILES_PER_REQUEST = 50;\nexport const MAX_BYTES_PER_REQUEST = 40 * 1024 * 1024;\nexport const MAX_FILE_BYTES = 25 * 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\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 for (const f of found.files) {\n if (f.sizeBytes > maxFileBytes) {\n result.skipped.push({ path: f.mapPath, reason: `larger than ${kb(maxFileBytes)}` });\n log.warn(`skipping ${f.urlPath}.map: ${kb(f.sizeBytes)} is over the ${kb(maxFileBytes)} limit`);\n } else result.files.push(f);\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 }> = [];\n for (const f of batch) {\n const content = 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 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([f.content], { type: \"application/json\" }), basename(f.mapPath)));\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":"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","names":["kb"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,62 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@guidelytech/sourcemaps",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"
|
|
5
|
-
"
|
|
3
|
+
"version": "0.4.1",
|
|
4
|
+
"description": "Upload JavaScript source maps to Guidely so error stack traces show your original code",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"type": "module",
|
|
7
|
+
"sideEffects": false,
|
|
8
|
+
"main": "./dist/index.js",
|
|
9
|
+
"types": "./dist/index.d.ts",
|
|
10
|
+
"exports": {
|
|
11
|
+
".": {
|
|
12
|
+
"types": "./dist/index.d.ts",
|
|
13
|
+
"import": "./dist/index.js"
|
|
14
|
+
}
|
|
15
|
+
},
|
|
16
|
+
"bin": {
|
|
17
|
+
"guidely-sourcemaps": "./dist/cli.js"
|
|
18
|
+
},
|
|
19
|
+
"files": [
|
|
20
|
+
"dist",
|
|
21
|
+
"README.md"
|
|
22
|
+
],
|
|
23
|
+
"engines": {
|
|
24
|
+
"node": ">=18"
|
|
25
|
+
},
|
|
26
|
+
"devDependencies": {
|
|
27
|
+
"@eslint/js": "^9.39.5",
|
|
28
|
+
"@types/node": "^22.20.5",
|
|
29
|
+
"eslint": "^9.39.5",
|
|
30
|
+
"tsup": "^8.5.1",
|
|
31
|
+
"typescript": "^5.9.3",
|
|
32
|
+
"typescript-eslint": "^8.46.0",
|
|
33
|
+
"vitest": "^3.2.7"
|
|
34
|
+
},
|
|
35
|
+
"publishConfig": {
|
|
36
|
+
"access": "public"
|
|
37
|
+
},
|
|
38
|
+
"repository": {
|
|
39
|
+
"type": "git",
|
|
40
|
+
"url": "git+https://github.com/parmarg/Guidely.git",
|
|
41
|
+
"directory": "packages/sourcemaps"
|
|
42
|
+
},
|
|
43
|
+
"homepage": "https://github.com/parmarg/Guidely/tree/main/packages/sourcemaps#readme",
|
|
44
|
+
"bugs": {
|
|
45
|
+
"url": "https://github.com/parmarg/Guidely/issues"
|
|
46
|
+
},
|
|
47
|
+
"keywords": [
|
|
48
|
+
"guidely",
|
|
49
|
+
"sourcemaps",
|
|
50
|
+
"source-maps",
|
|
51
|
+
"error-tracking",
|
|
52
|
+
"cli"
|
|
53
|
+
],
|
|
54
|
+
"scripts": {
|
|
55
|
+
"build": "rm -rf dist && tsup",
|
|
56
|
+
"dev": "tsup --watch",
|
|
57
|
+
"typecheck": "tsc --noEmit",
|
|
58
|
+
"test": "vitest run",
|
|
59
|
+
"lint": "eslint src",
|
|
60
|
+
"clean": "rm -rf dist"
|
|
61
|
+
}
|
|
6
62
|
}
|