@vinktarhq/cli 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +61 -0
- package/LICENSE +21 -0
- package/README.md +378 -0
- package/dist/bin.d.ts +2 -0
- package/dist/bin.js +28 -0
- package/dist/bin.js.map +1 -0
- package/dist/bundler/core.d.ts +140 -0
- package/dist/bundler/core.js +320 -0
- package/dist/bundler/core.js.map +1 -0
- package/dist/bundler/esbuild.d.ts +44 -0
- package/dist/bundler/esbuild.js +85 -0
- package/dist/bundler/esbuild.js.map +1 -0
- package/dist/bundler/rollup.d.ts +84 -0
- package/dist/bundler/rollup.js +221 -0
- package/dist/bundler/rollup.js.map +1 -0
- package/dist/bundler/rspack.d.ts +8 -0
- package/dist/bundler/rspack.js +8 -0
- package/dist/bundler/rspack.js.map +1 -0
- package/dist/bundler/webpack.d.ts +83 -0
- package/dist/bundler/webpack.js +104 -0
- package/dist/bundler/webpack.js.map +1 -0
- package/dist/cjs/bundler/core.d.ts +140 -0
- package/dist/cjs/bundler/core.js +331 -0
- package/dist/cjs/bundler/esbuild.d.ts +44 -0
- package/dist/cjs/bundler/esbuild.js +87 -0
- package/dist/cjs/bundler/rollup.d.ts +84 -0
- package/dist/cjs/bundler/rollup.js +226 -0
- package/dist/cjs/bundler/rspack.d.ts +8 -0
- package/dist/cjs/bundler/rspack.js +12 -0
- package/dist/cjs/bundler/webpack.d.ts +83 -0
- package/dist/cjs/bundler/webpack.js +107 -0
- package/dist/cjs/cli.d.ts +17 -0
- package/dist/cjs/cli.js +358 -0
- package/dist/cjs/commands/doctor.d.ts +15 -0
- package/dist/cjs/commands/doctor.js +134 -0
- package/dist/cjs/commands/inject.d.ts +70 -0
- package/dist/cjs/commands/inject.js +123 -0
- package/dist/cjs/commands/resolve.d.ts +32 -0
- package/dist/cjs/commands/resolve.js +130 -0
- package/dist/cjs/commands/upload.d.ts +74 -0
- package/dist/cjs/commands/upload.js +254 -0
- package/dist/cjs/config.d.ts +51 -0
- package/dist/cjs/config.js +152 -0
- package/dist/cjs/debug-id.d.ts +176 -0
- package/dist/cjs/debug-id.js +323 -0
- package/dist/cjs/discover.d.ts +50 -0
- package/dist/cjs/discover.js +235 -0
- package/dist/cjs/env.d.ts +22 -0
- package/dist/cjs/env.js +52 -0
- package/dist/cjs/glob.d.ts +34 -0
- package/dist/cjs/glob.js +112 -0
- package/dist/cjs/http.d.ts +68 -0
- package/dist/cjs/http.js +211 -0
- package/dist/cjs/index.d.ts +17 -0
- package/dist/cjs/index.js +53 -0
- package/dist/cjs/limits.d.ts +35 -0
- package/dist/cjs/limits.js +38 -0
- package/dist/cjs/multipart.d.ts +28 -0
- package/dist/cjs/multipart.js +44 -0
- package/dist/cjs/package.json +3 -0
- package/dist/cjs/upload.d.ts +178 -0
- package/dist/cjs/upload.js +394 -0
- package/dist/cjs/url.d.ts +25 -0
- package/dist/cjs/url.js +43 -0
- package/dist/cjs/version.d.ts +10 -0
- package/dist/cjs/version.js +13 -0
- package/dist/cli.d.ts +17 -0
- package/dist/cli.js +354 -0
- package/dist/cli.js.map +1 -0
- package/dist/commands/doctor.d.ts +15 -0
- package/dist/commands/doctor.js +132 -0
- package/dist/commands/doctor.js.map +1 -0
- package/dist/commands/inject.d.ts +70 -0
- package/dist/commands/inject.js +120 -0
- package/dist/commands/inject.js.map +1 -0
- package/dist/commands/resolve.d.ts +32 -0
- package/dist/commands/resolve.js +128 -0
- package/dist/commands/resolve.js.map +1 -0
- package/dist/commands/upload.d.ts +74 -0
- package/dist/commands/upload.js +252 -0
- package/dist/commands/upload.js.map +1 -0
- package/dist/config.d.ts +51 -0
- package/dist/config.js +150 -0
- package/dist/config.js.map +1 -0
- package/dist/debug-id.d.ts +176 -0
- package/dist/debug-id.js +307 -0
- package/dist/debug-id.js.map +1 -0
- package/dist/discover.d.ts +50 -0
- package/dist/discover.js +229 -0
- package/dist/discover.js.map +1 -0
- package/dist/env.d.ts +22 -0
- package/dist/env.js +50 -0
- package/dist/env.js.map +1 -0
- package/dist/glob.d.ts +34 -0
- package/dist/glob.js +109 -0
- package/dist/glob.js.map +1 -0
- package/dist/http.d.ts +68 -0
- package/dist/http.js +203 -0
- package/dist/http.js.map +1 -0
- package/dist/index.d.ts +17 -0
- package/dist/index.js +18 -0
- package/dist/index.js.map +1 -0
- package/dist/limits.d.ts +35 -0
- package/dist/limits.js +36 -0
- package/dist/limits.js.map +1 -0
- package/dist/multipart.d.ts +28 -0
- package/dist/multipart.js +41 -0
- package/dist/multipart.js.map +1 -0
- package/dist/upload.d.ts +178 -0
- package/dist/upload.js +383 -0
- package/dist/upload.js.map +1 -0
- package/dist/url.d.ts +25 -0
- package/dist/url.js +40 -0
- package/dist/url.js.map +1 -0
- package/dist/version.d.ts +10 -0
- package/dist/version.js +11 -0
- package/dist/version.js.map +1 -0
- package/dist/vite.d.ts +74 -0
- package/dist/vite.js +166 -0
- package/dist/vite.js.map +1 -0
- package/package.json +141 -0
package/dist/cli.js
ADDED
|
@@ -0,0 +1,354 @@
|
|
|
1
|
+
import { detectRelease } from './bundler/core.js';
|
|
2
|
+
import { doctor } from './commands/doctor.js';
|
|
3
|
+
import { inject } from './commands/inject.js';
|
|
4
|
+
import { resolvePosition } from './commands/resolve.js';
|
|
5
|
+
import { upload } from './commands/upload.js';
|
|
6
|
+
import { resolve } from './config.js';
|
|
7
|
+
import { loadDotEnv } from './env.js';
|
|
8
|
+
import { DEFAULT_HOST } from './limits.js';
|
|
9
|
+
import { UploadError } from './upload.js';
|
|
10
|
+
import { VERSION } from './version.js';
|
|
11
|
+
/**
|
|
12
|
+
* Argument parsing, hand-rolled.
|
|
13
|
+
*
|
|
14
|
+
* A CLI whose only job is uploading files should not drag a dependency tree into someone's build
|
|
15
|
+
* — and `npx` downloads whatever is in the manifest before it runs a single line, so every
|
|
16
|
+
* dependency is latency on every CI run.
|
|
17
|
+
*/
|
|
18
|
+
const USAGE = `
|
|
19
|
+
vinktar — upload source maps so stack traces resolve to your original code
|
|
20
|
+
|
|
21
|
+
Usage
|
|
22
|
+
npx @vinktarhq/cli sourcemaps upload <dir> --release <release> [options]
|
|
23
|
+
npx @vinktarhq/cli sourcemaps inject <dir>
|
|
24
|
+
npx @vinktarhq/cli sourcemaps resolve <map> --line <n> --column <n>
|
|
25
|
+
npx @vinktarhq/cli doctor [--dir <dir>] [options]
|
|
26
|
+
|
|
27
|
+
Commands
|
|
28
|
+
sourcemaps upload Stamp each chunk with a debug id, then send the maps. Run
|
|
29
|
+
after your bundler, on every deploy.
|
|
30
|
+
sourcemaps inject Only the stamping. For pipelines that build on one machine
|
|
31
|
+
and upload from another; upload does this itself.
|
|
32
|
+
sourcemaps resolve Decode one position through a map, locally. Answers "would
|
|
33
|
+
this frame resolve, and to what" without a real error.
|
|
34
|
+
doctor Check the key, its scope, and the host. With --dir, also
|
|
35
|
+
whether the build's chunks and maps carry debug ids.
|
|
36
|
+
|
|
37
|
+
Identity
|
|
38
|
+
--key <key> A key with the "cli" scope (they start vnk_sk_). A public
|
|
39
|
+
write key is NOT enough. Defaults to $VINKTAR_CLI_KEY,
|
|
40
|
+
then $VINKTAR_KEY.
|
|
41
|
+
--release <name> The release these maps belong to. Must match what your SDK
|
|
42
|
+
reports. Defaults to $VINKTAR_RELEASE, then the CI commit,
|
|
43
|
+
then the full git SHA.
|
|
44
|
+
--dist <name> Build discriminator, if you ship several per release.
|
|
45
|
+
--host <url> Ingest host. Defaults to $VINKTAR_HOST or ${DEFAULT_HOST}.
|
|
46
|
+
--url-prefix <p> What the files are served under: /assets/, https://cdn.x/,
|
|
47
|
+
or ~/ (the default) for "wherever this is served from".
|
|
48
|
+
|
|
49
|
+
Selection
|
|
50
|
+
--ignore <glob> Skip matching chunks entirely. Repeatable. A skipped chunk
|
|
51
|
+
is neither stamped nor uploaded, so a rule cannot orphan
|
|
52
|
+
an id by removing a map its chunk still names.
|
|
53
|
+
--ext <list> Extensions to treat as chunks. Default js,cjs,mjs.
|
|
54
|
+
|
|
55
|
+
Behaviour
|
|
56
|
+
--no-inject Upload without stamping. Chunks with no debug id only
|
|
57
|
+
match frames by release + url, and you will be warned.
|
|
58
|
+
--no-rewrite-sources Send the map's "sources" exactly as the bundler wrote
|
|
59
|
+
them, including webpack:/// prefixes and absolute build
|
|
60
|
+
paths. By default those are tidied in the uploaded copy
|
|
61
|
+
only; the file on disk is never touched.
|
|
62
|
+
--dry-run Print what would be uploaded, including the URL each map
|
|
63
|
+
will be stored under. Sends nothing, and needs no key.
|
|
64
|
+
--strict Treat any warning as a failure (exit 2).
|
|
65
|
+
--allow-failure Never exit non-zero because the upload failed.
|
|
66
|
+
--concurrency <n> Requests in flight. Default 4, or what the server asks.
|
|
67
|
+
--timeout <s> Per request, before the size allowance. Default 30.
|
|
68
|
+
--retries <n> Attempts per request, including the first. Default 3.
|
|
69
|
+
--header <h> "Name: value", for a gateway in front of ingest.
|
|
70
|
+
Repeatable. Cannot displace the key header.
|
|
71
|
+
--dotenv-file <p> Read VINKTAR_* from this file. Ranks below the real
|
|
72
|
+
environment. (Not --env-file: Node owns that one and
|
|
73
|
+
would swallow it before this parser saw it.)
|
|
74
|
+
|
|
75
|
+
Output
|
|
76
|
+
--quiet Only errors and the summary.
|
|
77
|
+
--debug Everything, with the key redacted.
|
|
78
|
+
-h, --help This.
|
|
79
|
+
-v, --version Print the version.
|
|
80
|
+
|
|
81
|
+
Typical use, in a deploy script:
|
|
82
|
+
|
|
83
|
+
npx @vinktarhq/cli sourcemaps upload ./dist --release "$GIT_SHA"
|
|
84
|
+
`;
|
|
85
|
+
/** Flags that may be given more than once, where the last value must not win. */
|
|
86
|
+
const REPEATABLE = new Set(['ignore', 'header']);
|
|
87
|
+
/** Flags that are switches. Everything else takes the next token as its value. */
|
|
88
|
+
const BOOLEAN_FLAGS = new Set([
|
|
89
|
+
'dry-run',
|
|
90
|
+
'no-inject',
|
|
91
|
+
'no-rewrite-sources',
|
|
92
|
+
'strict',
|
|
93
|
+
'allow-failure',
|
|
94
|
+
'quiet',
|
|
95
|
+
'debug',
|
|
96
|
+
'help',
|
|
97
|
+
'h',
|
|
98
|
+
'version',
|
|
99
|
+
'v',
|
|
100
|
+
]);
|
|
101
|
+
export function parse(argv) {
|
|
102
|
+
const positional = [];
|
|
103
|
+
const flags = new Map();
|
|
104
|
+
const repeated = new Map();
|
|
105
|
+
const record = (name, value) => {
|
|
106
|
+
flags.set(name, value);
|
|
107
|
+
if (!REPEATABLE.has(name))
|
|
108
|
+
return;
|
|
109
|
+
const list = repeated.get(name) ?? [];
|
|
110
|
+
list.push(value);
|
|
111
|
+
repeated.set(name, list);
|
|
112
|
+
};
|
|
113
|
+
for (let i = 0; i < argv.length; i += 1) {
|
|
114
|
+
const token = argv[i];
|
|
115
|
+
if (token === '--') {
|
|
116
|
+
positional.push(...argv.slice(i + 1));
|
|
117
|
+
break;
|
|
118
|
+
}
|
|
119
|
+
if (!token.startsWith('-')) {
|
|
120
|
+
positional.push(token);
|
|
121
|
+
continue;
|
|
122
|
+
}
|
|
123
|
+
const [name, inline] = splitFlag(token);
|
|
124
|
+
if (inline !== null) {
|
|
125
|
+
record(name, inline);
|
|
126
|
+
continue;
|
|
127
|
+
}
|
|
128
|
+
// A switch never takes a value, so `--dry-run ./dist` leaves `./dist` as the directory. The
|
|
129
|
+
// previous parser swallowed it as the switch's value and then reported a missing directory.
|
|
130
|
+
if (BOOLEAN_FLAGS.has(name)) {
|
|
131
|
+
flags.set(name, true);
|
|
132
|
+
continue;
|
|
133
|
+
}
|
|
134
|
+
const next = argv[i + 1];
|
|
135
|
+
// A bare flag followed by another flag is a boolean, not a flag with a flag for a value.
|
|
136
|
+
if (next === undefined || next.startsWith('-')) {
|
|
137
|
+
flags.set(name, true);
|
|
138
|
+
continue;
|
|
139
|
+
}
|
|
140
|
+
record(name, next);
|
|
141
|
+
i += 1;
|
|
142
|
+
}
|
|
143
|
+
return { positional, flags, repeated };
|
|
144
|
+
}
|
|
145
|
+
function splitFlag(token) {
|
|
146
|
+
const body = token.replace(/^--?/, '');
|
|
147
|
+
const equals = body.indexOf('=');
|
|
148
|
+
return equals === -1 ? [body, null] : [body.slice(0, equals), body.slice(equals + 1)];
|
|
149
|
+
}
|
|
150
|
+
function text(flags, name, fallback = '') {
|
|
151
|
+
const value = flags.get(name);
|
|
152
|
+
return typeof value === 'string' ? value : fallback;
|
|
153
|
+
}
|
|
154
|
+
/**
|
|
155
|
+
* Exit codes: 0 fine, 1 the thing did not work, 2 it worked and something is wrong anyway.
|
|
156
|
+
*
|
|
157
|
+
* Two rather than one for `--strict`, so a pipeline can tell "the upload failed" from "the upload
|
|
158
|
+
* succeeded and half your chunks have no maps" — which are different problems with different
|
|
159
|
+
* owners, and merging them means neither gets fixed.
|
|
160
|
+
*/
|
|
161
|
+
export async function run(argv, log = console.log, fail = console.error) {
|
|
162
|
+
const { positional, flags, repeated } = parse(argv);
|
|
163
|
+
// Before the usage branch: `vinktar --version` has no positional, and used to print usage and
|
|
164
|
+
// exit 1, which made the version unreachable from the command line.
|
|
165
|
+
if (flags.has('version') || flags.has('v')) {
|
|
166
|
+
log(VERSION);
|
|
167
|
+
return 0;
|
|
168
|
+
}
|
|
169
|
+
if (flags.has('help') || flags.has('h') || positional.length === 0) {
|
|
170
|
+
log(USAGE.trim());
|
|
171
|
+
return positional.length === 0 ? 1 : 0;
|
|
172
|
+
}
|
|
173
|
+
const dotenvPath = text(flags, 'dotenv-file');
|
|
174
|
+
const dotenv = dotenvPath === '' ? null : await loadDotEnv(dotenvPath);
|
|
175
|
+
if (dotenv !== null && !dotenv.found) {
|
|
176
|
+
fail(`No such file: ${dotenvPath}`);
|
|
177
|
+
return 1;
|
|
178
|
+
}
|
|
179
|
+
for (const warning of dotenv?.warnings ?? [])
|
|
180
|
+
fail(`WARNING: ${warning}`);
|
|
181
|
+
const config = resolve({
|
|
182
|
+
flags,
|
|
183
|
+
repeated,
|
|
184
|
+
env: process.env,
|
|
185
|
+
...(dotenv === null ? {} : { dotenv: dotenv.values }),
|
|
186
|
+
});
|
|
187
|
+
for (const error of config.errors)
|
|
188
|
+
fail(error);
|
|
189
|
+
if (config.errors.length > 0)
|
|
190
|
+
return 1;
|
|
191
|
+
for (const warning of config.warnings)
|
|
192
|
+
fail(`WARNING: ${warning}`);
|
|
193
|
+
const say = config.quiet ? () => { } : log;
|
|
194
|
+
const debug = config.debug ? (line) => fail(`[debug] ${redact(line, config.key)}`) : () => { };
|
|
195
|
+
try {
|
|
196
|
+
if (positional[0] === 'doctor')
|
|
197
|
+
return await runDoctor(config, flags, say, fail);
|
|
198
|
+
if (positional[0] !== 'sourcemaps') {
|
|
199
|
+
fail(`Unknown command "${positional[0]}". Try --help.`);
|
|
200
|
+
return 1;
|
|
201
|
+
}
|
|
202
|
+
const action = positional[1];
|
|
203
|
+
if (action === 'resolve')
|
|
204
|
+
return await runResolve(positional[2], flags, log, fail);
|
|
205
|
+
const directory = positional[2];
|
|
206
|
+
if (directory === undefined)
|
|
207
|
+
return missing('a directory', 'e.g. ./dist', fail);
|
|
208
|
+
if (action === 'inject')
|
|
209
|
+
return await runInject(directory, config, say, fail);
|
|
210
|
+
if (action === 'upload')
|
|
211
|
+
return await runUpload(directory, config, say, fail, debug);
|
|
212
|
+
fail(`Unknown sourcemaps action "${action ?? ''}". Try inject, upload or resolve.`);
|
|
213
|
+
return 1;
|
|
214
|
+
}
|
|
215
|
+
catch (error) {
|
|
216
|
+
if (error instanceof UploadError) {
|
|
217
|
+
fail('');
|
|
218
|
+
fail(error.message);
|
|
219
|
+
if (error.hint !== undefined)
|
|
220
|
+
fail(error.hint);
|
|
221
|
+
if (!config.debug)
|
|
222
|
+
fail('Re-run with --debug for the full exchange.');
|
|
223
|
+
return config.allowFailure ? 0 : 1;
|
|
224
|
+
}
|
|
225
|
+
fail(error instanceof Error ? error.message : String(error));
|
|
226
|
+
if (error instanceof Error && error.cause !== undefined) {
|
|
227
|
+
fail(`caused by: ${error.cause instanceof Error ? error.cause.message : String(error.cause)}`);
|
|
228
|
+
}
|
|
229
|
+
return 1;
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
async function runDoctor(config, flags, log, fail) {
|
|
233
|
+
if (config.key === '')
|
|
234
|
+
return missing('a key', '--key, $VINKTAR_CLI_KEY or $VINKTAR_KEY', fail);
|
|
235
|
+
const dir = text(flags, 'dir');
|
|
236
|
+
const ok = await doctor(config.host, config.key, log, {
|
|
237
|
+
...(dir === '' ? {} : { dir }),
|
|
238
|
+
ignore: config.ignore,
|
|
239
|
+
...(config.extensions === undefined ? {} : { extensions: config.extensions }),
|
|
240
|
+
});
|
|
241
|
+
return ok ? 0 : 1;
|
|
242
|
+
}
|
|
243
|
+
async function runInject(directory, config, log, fail) {
|
|
244
|
+
log(`Injecting debug ids into ${directory}…`);
|
|
245
|
+
const result = await inject(directory, log, {
|
|
246
|
+
ignore: config.ignore,
|
|
247
|
+
...(config.extensions === undefined ? {} : { extensions: config.extensions }),
|
|
248
|
+
});
|
|
249
|
+
log('');
|
|
250
|
+
for (const line of result.warnings)
|
|
251
|
+
fail(`WARNING: ${line}`);
|
|
252
|
+
log(`Injected ${result.injected}, already stamped ${result.skipped}` +
|
|
253
|
+
`${result.adopted > 0 ? `, ${result.adopted} adopted from the bundler` : ''}` +
|
|
254
|
+
`${result.repaired > 0 ? `, ${result.repaired} map(s) repaired` : ''}` +
|
|
255
|
+
`${result.empty > 0 ? `, ${result.empty} empty map(s) left alone` : ''}.`);
|
|
256
|
+
if (result.withoutMaps > 0) {
|
|
257
|
+
log(`${result.withoutMaps} file(s) had no .map beside them and were left alone.`);
|
|
258
|
+
log('If that is unexpected, your bundler is not emitting source maps for them.');
|
|
259
|
+
}
|
|
260
|
+
return config.strict && result.warnings.length > 0 ? 2 : 0;
|
|
261
|
+
}
|
|
262
|
+
async function runUpload(directory, config, log, fail, debug) {
|
|
263
|
+
// Detected here rather than demanded, exactly as the plugins do: a deploy script that already
|
|
264
|
+
// knows its commit should not have to say so twice. Announced when it was detected, because a
|
|
265
|
+
// release nobody chose is a release nobody checks, and the SDK has to report the same one.
|
|
266
|
+
const release = config.release === '' ? detectRelease(process.env) : config.release;
|
|
267
|
+
if (config.release === '' && release !== '')
|
|
268
|
+
log(`Release not given; using ${release}.`);
|
|
269
|
+
// A dry run opens no socket, so it needs no key: "what would this upload" is the first thing
|
|
270
|
+
// anyone asks, and needing a production secret to answer it is why nobody asked.
|
|
271
|
+
if (!config.dryRun && config.key === '') {
|
|
272
|
+
return missing('a key', '--key, $VINKTAR_CLI_KEY or $VINKTAR_KEY', fail);
|
|
273
|
+
}
|
|
274
|
+
if (!config.dryRun && release === '') {
|
|
275
|
+
return missing('a release', '--release or $VINKTAR_RELEASE, matching what your SDK reports', fail);
|
|
276
|
+
}
|
|
277
|
+
debug(`host ${config.host}, release ${release}, key ${config.key}`);
|
|
278
|
+
log(`${config.dryRun ? 'Checking' : 'Uploading'} source maps in ${directory}…`);
|
|
279
|
+
const summary = await upload(directory, {
|
|
280
|
+
host: config.host,
|
|
281
|
+
key: config.key,
|
|
282
|
+
release,
|
|
283
|
+
...(config.dist === '' ? {} : { dist: config.dist }),
|
|
284
|
+
urlPrefix: config.urlPrefix,
|
|
285
|
+
dryRun: config.dryRun,
|
|
286
|
+
inject: config.inject,
|
|
287
|
+
rewriteSources: config.rewriteSources,
|
|
288
|
+
root: directory,
|
|
289
|
+
ignore: config.ignore,
|
|
290
|
+
...(config.extensions === undefined ? {} : { extensions: config.extensions }),
|
|
291
|
+
...(config.concurrency === undefined ? {} : { concurrency: config.concurrency }),
|
|
292
|
+
...(config.timeoutMs === undefined ? {} : { timeoutMs: config.timeoutMs }),
|
|
293
|
+
...(config.maxRetries === undefined ? {} : { maxRetries: config.maxRetries }),
|
|
294
|
+
...(Object.keys(config.headers).length === 0 ? {} : { headers: config.headers }),
|
|
295
|
+
}, log,
|
|
296
|
+
// Warnings go to stderr so a CI log filter cannot miss them; the exit code stays 0 unless
|
|
297
|
+
// --strict says otherwise, because the maps are still useful, just less so.
|
|
298
|
+
fail);
|
|
299
|
+
// Only a build with no maps at all is a failure. "Everything was already stored" is the
|
|
300
|
+
// successful shape of a redeploy, and it is reachable: the upload asks the server what it holds
|
|
301
|
+
// before sending, so an unchanged build sends nothing and says so.
|
|
302
|
+
const found = summary.uploaded + summary.alreadyStored + summary.oversized;
|
|
303
|
+
if (found === 0 && !config.dryRun) {
|
|
304
|
+
log('');
|
|
305
|
+
log('No source maps found. Run your bundler with source maps enabled first.');
|
|
306
|
+
return config.allowFailure ? 0 : 1;
|
|
307
|
+
}
|
|
308
|
+
if (!config.dryRun) {
|
|
309
|
+
log(summary.uploaded === 0
|
|
310
|
+
? `Every map for release "${release}" was already stored.`
|
|
311
|
+
: `Release "${release}".`);
|
|
312
|
+
}
|
|
313
|
+
const problems = summary.warnings.length + summary.oversized + summary.uninjected + summary.skipped + summary.duplicates;
|
|
314
|
+
return config.strict && problems > 0 ? 2 : 0;
|
|
315
|
+
}
|
|
316
|
+
async function runResolve(mapPath, flags, log, fail) {
|
|
317
|
+
if (mapPath === undefined)
|
|
318
|
+
return missing('a map', 'e.g. ./dist/app.js.map', fail);
|
|
319
|
+
const line = Number(text(flags, 'line', '0'));
|
|
320
|
+
const column = Number(text(flags, 'column', '1'));
|
|
321
|
+
if (!Number.isInteger(line) || line < 1) {
|
|
322
|
+
return missing('a line', '--line <n>, 1-based, as a stack frame reports it', fail);
|
|
323
|
+
}
|
|
324
|
+
if (!Number.isInteger(column) || column < 1) {
|
|
325
|
+
return missing('a column', '--column <n>, 1-based', fail);
|
|
326
|
+
}
|
|
327
|
+
const result = await resolvePosition(mapPath, line, column);
|
|
328
|
+
for (const warning of result.warnings)
|
|
329
|
+
fail(`WARNING: ${warning}`);
|
|
330
|
+
if (result.debugId !== null)
|
|
331
|
+
log(`debug id ${result.debugId}`);
|
|
332
|
+
if (result.position === null) {
|
|
333
|
+
log(`Nothing resolves at ${line}:${column}.`);
|
|
334
|
+
return 1;
|
|
335
|
+
}
|
|
336
|
+
log(`${result.position.source}:${result.position.line}:${result.position.column}${result.position.name === null ? '' : ` (${result.position.name})`}`);
|
|
337
|
+
if (result.context.length > 0) {
|
|
338
|
+
log('');
|
|
339
|
+
for (const entry of result.context) {
|
|
340
|
+
log(`${entry.here ? '>' : ' '} ${String(entry.line).padStart(5)} | ${entry.text}`);
|
|
341
|
+
}
|
|
342
|
+
}
|
|
343
|
+
return 0;
|
|
344
|
+
}
|
|
345
|
+
/** Never print a key, even under --debug: CI logs are archived and shared far more than intended. */
|
|
346
|
+
function redact(line, key) {
|
|
347
|
+
return key.length > 8 ? line.split(key).join(`${key.slice(0, 7)}…redacted`) : line;
|
|
348
|
+
}
|
|
349
|
+
function missing(what, how, fail) {
|
|
350
|
+
fail(`Missing ${what}. Pass ${how}.`);
|
|
351
|
+
return 1;
|
|
352
|
+
}
|
|
353
|
+
export { VERSION };
|
|
354
|
+
//# sourceMappingURL=cli.js.map
|
package/dist/cli.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"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","sourcesContent":["import { detectRelease } from './bundler/core.js';\nimport { doctor } from './commands/doctor.js';\nimport { inject } from './commands/inject.js';\nimport { resolvePosition } from './commands/resolve.js';\nimport { upload } from './commands/upload.js';\nimport { resolve, type Resolved } from './config.js';\nimport { loadDotEnv } from './env.js';\nimport { DEFAULT_HOST } from './limits.js';\nimport { UploadError } from './upload.js';\nimport { VERSION } from './version.js';\n\n/**\n * Argument parsing, hand-rolled.\n *\n * A CLI whose only job is uploading files should not drag a dependency tree into someone's build\n * — and `npx` downloads whatever is in the manifest before it runs a single line, so every\n * dependency is latency on every CI run.\n */\n\nconst USAGE = `\nvinktar — upload source maps so stack traces resolve to your original code\n\nUsage\n npx @vinktarhq/cli sourcemaps upload <dir> --release <release> [options]\n npx @vinktarhq/cli sourcemaps inject <dir>\n npx @vinktarhq/cli sourcemaps resolve <map> --line <n> --column <n>\n npx @vinktarhq/cli doctor [--dir <dir>] [options]\n\nCommands\n sourcemaps upload Stamp each chunk with a debug id, then send the maps. Run\n after your bundler, on every deploy.\n sourcemaps inject Only the stamping. For pipelines that build on one machine\n and upload from another; upload does this itself.\n sourcemaps resolve Decode one position through a map, locally. Answers \"would\n this frame resolve, and to what\" without a real error.\n doctor Check the key, its scope, and the host. With --dir, also\n whether the build's chunks and maps carry debug ids.\n\nIdentity\n --key <key> A key with the \"cli\" scope (they start vnk_sk_). A public\n write key is NOT enough. Defaults to $VINKTAR_CLI_KEY,\n then $VINKTAR_KEY.\n --release <name> The release these maps belong to. Must match what your SDK\n reports. Defaults to $VINKTAR_RELEASE, then the CI commit,\n then the full git SHA.\n --dist <name> Build discriminator, if you ship several per release.\n --host <url> Ingest host. Defaults to $VINKTAR_HOST or ${DEFAULT_HOST}.\n --url-prefix <p> What the files are served under: /assets/, https://cdn.x/,\n or ~/ (the default) for \"wherever this is served from\".\n\nSelection\n --ignore <glob> Skip matching chunks entirely. Repeatable. A skipped chunk\n is neither stamped nor uploaded, so a rule cannot orphan\n an id by removing a map its chunk still names.\n --ext <list> Extensions to treat as chunks. Default js,cjs,mjs.\n\nBehaviour\n --no-inject Upload without stamping. Chunks with no debug id only\n match frames by release + url, and you will be warned.\n --no-rewrite-sources Send the map's \"sources\" exactly as the bundler wrote\n them, including webpack:/// prefixes and absolute build\n paths. By default those are tidied in the uploaded copy\n only; the file on disk is never touched.\n --dry-run Print what would be uploaded, including the URL each map\n will be stored under. Sends nothing, and needs no key.\n --strict Treat any warning as a failure (exit 2).\n --allow-failure Never exit non-zero because the upload failed.\n --concurrency <n> Requests in flight. Default 4, or what the server asks.\n --timeout <s> Per request, before the size allowance. Default 30.\n --retries <n> Attempts per request, including the first. Default 3.\n --header <h> \"Name: value\", for a gateway in front of ingest.\n Repeatable. Cannot displace the key header.\n --dotenv-file <p> Read VINKTAR_* from this file. Ranks below the real\n environment. (Not --env-file: Node owns that one and\n would swallow it before this parser saw it.)\n\nOutput\n --quiet Only errors and the summary.\n --debug Everything, with the key redacted.\n -h, --help This.\n -v, --version Print the version.\n\nTypical use, in a deploy script:\n\n npx @vinktarhq/cli sourcemaps upload ./dist --release \"$GIT_SHA\"\n`;\n\nexport interface Args {\n readonly positional: string[];\n readonly flags: Map<string, string | boolean>;\n /** Values of repeatable flags, in order. `--ignore` and `--header` are the two. */\n readonly repeated: Map<string, string[]>;\n}\n\n/** Flags that may be given more than once, where the last value must not win. */\nconst REPEATABLE = new Set(['ignore', 'header']);\n\n/** Flags that are switches. Everything else takes the next token as its value. */\nconst BOOLEAN_FLAGS = new Set([\n 'dry-run',\n 'no-inject',\n 'no-rewrite-sources',\n 'strict',\n 'allow-failure',\n 'quiet',\n 'debug',\n 'help',\n 'h',\n 'version',\n 'v',\n]);\n\nexport function parse(argv: readonly string[]): Args {\n const positional: string[] = [];\n const flags = new Map<string, string | boolean>();\n const repeated = new Map<string, string[]>();\n\n const record = (name: string, value: string): void => {\n flags.set(name, value);\n if (!REPEATABLE.has(name)) return;\n\n const list = repeated.get(name) ?? [];\n list.push(value);\n repeated.set(name, list);\n };\n\n for (let i = 0; i < argv.length; i += 1) {\n const token = argv[i]!;\n\n if (token === '--') {\n positional.push(...argv.slice(i + 1));\n break;\n }\n\n if (!token.startsWith('-')) {\n positional.push(token);\n continue;\n }\n\n const [name, inline] = splitFlag(token);\n\n if (inline !== null) {\n record(name, inline);\n continue;\n }\n\n // A switch never takes a value, so `--dry-run ./dist` leaves `./dist` as the directory. The\n // previous parser swallowed it as the switch's value and then reported a missing directory.\n if (BOOLEAN_FLAGS.has(name)) {\n flags.set(name, true);\n continue;\n }\n\n const next = argv[i + 1];\n // A bare flag followed by another flag is a boolean, not a flag with a flag for a value.\n if (next === undefined || next.startsWith('-')) {\n flags.set(name, true);\n continue;\n }\n\n record(name, next);\n i += 1;\n }\n\n return { positional, flags, repeated };\n}\n\nfunction splitFlag(token: string): [string, string | null] {\n const body = token.replace(/^--?/, '');\n const equals = body.indexOf('=');\n\n return equals === -1 ? [body, null] : [body.slice(0, equals), body.slice(equals + 1)];\n}\n\nfunction text(flags: Args['flags'], name: string, fallback = ''): string {\n const value = flags.get(name);\n\n return typeof value === 'string' ? value : fallback;\n}\n\n/**\n * Exit codes: 0 fine, 1 the thing did not work, 2 it worked and something is wrong anyway.\n *\n * Two rather than one for `--strict`, so a pipeline can tell \"the upload failed\" from \"the upload\n * succeeded and half your chunks have no maps\" — which are different problems with different\n * owners, and merging them means neither gets fixed.\n */\nexport async function run(argv: readonly string[], log = console.log, fail = console.error): Promise<number> {\n const { positional, flags, repeated } = parse(argv);\n\n // Before the usage branch: `vinktar --version` has no positional, and used to print usage and\n // exit 1, which made the version unreachable from the command line.\n if (flags.has('version') || flags.has('v')) {\n log(VERSION);\n\n return 0;\n }\n\n if (flags.has('help') || flags.has('h') || positional.length === 0) {\n log(USAGE.trim());\n\n return positional.length === 0 ? 1 : 0;\n }\n\n const dotenvPath = text(flags, 'dotenv-file');\n const dotenv = dotenvPath === '' ? null : await loadDotEnv(dotenvPath);\n if (dotenv !== null && !dotenv.found) {\n fail(`No such file: ${dotenvPath}`);\n\n return 1;\n }\n for (const warning of dotenv?.warnings ?? []) fail(`WARNING: ${warning}`);\n\n const config = resolve({\n flags,\n repeated,\n env: process.env,\n ...(dotenv === null ? {} : { dotenv: dotenv.values }),\n });\n\n for (const error of config.errors) fail(error);\n if (config.errors.length > 0) return 1;\n for (const warning of config.warnings) fail(`WARNING: ${warning}`);\n\n const say = config.quiet ? (): void => {} : log;\n const debug = config.debug ? (line: string): void => fail(`[debug] ${redact(line, config.key)}`) : (): void => {};\n\n try {\n if (positional[0] === 'doctor') return await runDoctor(config, flags, say, fail);\n\n if (positional[0] !== 'sourcemaps') {\n fail(`Unknown command \"${positional[0]}\". Try --help.`);\n\n return 1;\n }\n\n const action = positional[1];\n\n if (action === 'resolve') return await runResolve(positional[2], flags, log, fail);\n\n const directory = positional[2];\n if (directory === undefined) return missing('a directory', 'e.g. ./dist', fail);\n\n if (action === 'inject') return await runInject(directory, config, say, fail);\n if (action === 'upload') return await runUpload(directory, config, say, fail, debug);\n\n fail(`Unknown sourcemaps action \"${action ?? ''}\". Try inject, upload or resolve.`);\n\n return 1;\n } catch (error) {\n if (error instanceof UploadError) {\n fail('');\n fail(error.message);\n if (error.hint !== undefined) fail(error.hint);\n if (!config.debug) fail('Re-run with --debug for the full exchange.');\n\n return config.allowFailure ? 0 : 1;\n }\n\n fail(error instanceof Error ? error.message : String(error));\n if (error instanceof Error && error.cause !== undefined) {\n fail(`caused by: ${error.cause instanceof Error ? error.cause.message : String(error.cause)}`);\n }\n\n return 1;\n }\n}\n\nasync function runDoctor(\n config: Resolved,\n flags: Args['flags'],\n log: (line: string) => void,\n fail: (line: string) => void,\n): Promise<number> {\n if (config.key === '') return missing('a key', '--key, $VINKTAR_CLI_KEY or $VINKTAR_KEY', fail);\n\n const dir = text(flags, 'dir');\n const ok = await doctor(config.host, config.key, log, {\n ...(dir === '' ? {} : { dir }),\n ignore: config.ignore,\n ...(config.extensions === undefined ? {} : { extensions: config.extensions }),\n });\n\n return ok ? 0 : 1;\n}\n\nasync function runInject(\n directory: string,\n config: Resolved,\n log: (line: string) => void,\n fail: (line: string) => void,\n): Promise<number> {\n log(`Injecting debug ids into ${directory}…`);\n const result = await inject(directory, log, {\n ignore: config.ignore,\n ...(config.extensions === undefined ? {} : { extensions: config.extensions }),\n });\n\n log('');\n for (const line of result.warnings) fail(`WARNING: ${line}`);\n log(\n `Injected ${result.injected}, already stamped ${result.skipped}` +\n `${result.adopted > 0 ? `, ${result.adopted} adopted from the bundler` : ''}` +\n `${result.repaired > 0 ? `, ${result.repaired} map(s) repaired` : ''}` +\n `${result.empty > 0 ? `, ${result.empty} empty map(s) left alone` : ''}.`,\n );\n if (result.withoutMaps > 0) {\n log(`${result.withoutMaps} file(s) had no .map beside them and were left alone.`);\n log('If that is unexpected, your bundler is not emitting source maps for them.');\n }\n\n return config.strict && result.warnings.length > 0 ? 2 : 0;\n}\n\nasync function runUpload(\n directory: string,\n config: Resolved,\n log: (line: string) => void,\n fail: (line: string) => void,\n debug: (line: string) => void,\n): Promise<number> {\n // Detected here rather than demanded, exactly as the plugins do: a deploy script that already\n // knows its commit should not have to say so twice. Announced when it was detected, because a\n // release nobody chose is a release nobody checks, and the SDK has to report the same one.\n const release = config.release === '' ? detectRelease(process.env) : config.release;\n if (config.release === '' && release !== '') log(`Release not given; using ${release}.`);\n\n // A dry run opens no socket, so it needs no key: \"what would this upload\" is the first thing\n // anyone asks, and needing a production secret to answer it is why nobody asked.\n if (!config.dryRun && config.key === '') {\n return missing('a key', '--key, $VINKTAR_CLI_KEY or $VINKTAR_KEY', fail);\n }\n if (!config.dryRun && release === '') {\n return missing('a release', '--release or $VINKTAR_RELEASE, matching what your SDK reports', fail);\n }\n\n debug(`host ${config.host}, release ${release}, key ${config.key}`);\n log(`${config.dryRun ? 'Checking' : 'Uploading'} source maps in ${directory}…`);\n\n const summary = await upload(\n directory,\n {\n host: config.host,\n key: config.key,\n release,\n ...(config.dist === '' ? {} : { dist: config.dist }),\n urlPrefix: config.urlPrefix,\n dryRun: config.dryRun,\n inject: config.inject,\n rewriteSources: config.rewriteSources,\n root: directory,\n ignore: config.ignore,\n ...(config.extensions === undefined ? {} : { extensions: config.extensions }),\n ...(config.concurrency === undefined ? {} : { concurrency: config.concurrency }),\n ...(config.timeoutMs === undefined ? {} : { timeoutMs: config.timeoutMs }),\n ...(config.maxRetries === undefined ? {} : { maxRetries: config.maxRetries }),\n ...(Object.keys(config.headers).length === 0 ? {} : { headers: config.headers }),\n },\n log,\n // Warnings go to stderr so a CI log filter cannot miss them; the exit code stays 0 unless\n // --strict says otherwise, because the maps are still useful, just less so.\n fail,\n );\n\n // Only a build with no maps at all is a failure. \"Everything was already stored\" is the\n // successful shape of a redeploy, and it is reachable: the upload asks the server what it holds\n // before sending, so an unchanged build sends nothing and says so.\n const found = summary.uploaded + summary.alreadyStored + summary.oversized;\n if (found === 0 && !config.dryRun) {\n log('');\n log('No source maps found. Run your bundler with source maps enabled first.');\n\n return config.allowFailure ? 0 : 1;\n }\n\n if (!config.dryRun) {\n log(\n summary.uploaded === 0\n ? `Every map for release \"${release}\" was already stored.`\n : `Release \"${release}\".`,\n );\n }\n\n const problems =\n summary.warnings.length + summary.oversized + summary.uninjected + summary.skipped + summary.duplicates;\n\n return config.strict && problems > 0 ? 2 : 0;\n}\n\nasync function runResolve(\n mapPath: string | undefined,\n flags: Args['flags'],\n log: (line: string) => void,\n fail: (line: string) => void,\n): Promise<number> {\n if (mapPath === undefined) return missing('a map', 'e.g. ./dist/app.js.map', fail);\n\n const line = Number(text(flags, 'line', '0'));\n const column = Number(text(flags, 'column', '1'));\n\n if (!Number.isInteger(line) || line < 1) {\n return missing('a line', '--line <n>, 1-based, as a stack frame reports it', fail);\n }\n if (!Number.isInteger(column) || column < 1) {\n return missing('a column', '--column <n>, 1-based', fail);\n }\n\n const result = await resolvePosition(mapPath, line, column);\n for (const warning of result.warnings) fail(`WARNING: ${warning}`);\n\n if (result.debugId !== null) log(`debug id ${result.debugId}`);\n\n if (result.position === null) {\n log(`Nothing resolves at ${line}:${column}.`);\n\n return 1;\n }\n\n log(`${result.position.source}:${result.position.line}:${result.position.column}${result.position.name === null ? '' : ` (${result.position.name})`}`);\n\n if (result.context.length > 0) {\n log('');\n for (const entry of result.context) {\n log(`${entry.here ? '>' : ' '} ${String(entry.line).padStart(5)} | ${entry.text}`);\n }\n }\n\n return 0;\n}\n\n/** Never print a key, even under --debug: CI logs are archived and shared far more than intended. */\nfunction redact(line: string, key: string): string {\n return key.length > 8 ? line.split(key).join(`${key.slice(0, 7)}…redacted`) : line;\n}\n\nfunction missing(what: string, how: string, fail: (line: string) => void): number {\n fail(`Missing ${what}. Pass ${how}.`);\n\n return 1;\n}\n\nexport { VERSION };\n"]}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
export interface DoctorOptions {
|
|
2
|
+
/** A build directory to audit for debug ids, on top of the key and host checks. */
|
|
3
|
+
readonly dir?: string;
|
|
4
|
+
readonly ignore?: readonly string[];
|
|
5
|
+
readonly extensions?: readonly string[];
|
|
6
|
+
}
|
|
7
|
+
/**
|
|
8
|
+
* Answers "is this key going to work, and for what", and with `--dir`, "is this build stamped".
|
|
9
|
+
*
|
|
10
|
+
* Every failure mode of a source-map setup looks identical from the outside (stack traces stay
|
|
11
|
+
* minified), so this exists to distinguish "wrong key", "right key, wrong scope", "wrong host",
|
|
12
|
+
* "the build was never injected" and "fine, your maps just have not uploaded yet" before anyone
|
|
13
|
+
* spends an afternoon on it.
|
|
14
|
+
*/
|
|
15
|
+
export declare function doctor(host: string, key: string, log: (line: string) => void, options?: DoctorOptions): Promise<boolean>;
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
import { DEFAULT_HOST, REQUIRED_SCOPE } from '../limits.js';
|
|
2
|
+
import { audit } from './inject.js';
|
|
3
|
+
/**
|
|
4
|
+
* Answers "is this key going to work, and for what", and with `--dir`, "is this build stamped".
|
|
5
|
+
*
|
|
6
|
+
* Every failure mode of a source-map setup looks identical from the outside (stack traces stay
|
|
7
|
+
* minified), so this exists to distinguish "wrong key", "right key, wrong scope", "wrong host",
|
|
8
|
+
* "the build was never injected" and "fine, your maps just have not uploaded yet" before anyone
|
|
9
|
+
* spends an afternoon on it.
|
|
10
|
+
*/
|
|
11
|
+
export async function doctor(host, key, log, options = {}) {
|
|
12
|
+
log(`host ${host}${host === DEFAULT_HOST ? '' : ' (custom)'}`);
|
|
13
|
+
log(`key ${key.slice(0, 11)}…${key.slice(-4)}`);
|
|
14
|
+
log('');
|
|
15
|
+
// Local, so it runs before the network: an unreachable host should not hide a broken build.
|
|
16
|
+
let buildOk = true;
|
|
17
|
+
if (options.dir !== undefined) {
|
|
18
|
+
buildOk = await checkBuild(options.dir, log, options);
|
|
19
|
+
log('');
|
|
20
|
+
}
|
|
21
|
+
const health = await probe(`${host}/v1/health`);
|
|
22
|
+
log(health.ok ? 'reachable yes' : `reachable NO — ${health.detail}`);
|
|
23
|
+
if (!health.ok) {
|
|
24
|
+
log('');
|
|
25
|
+
log('Nothing else can be checked until the host responds. Check --host and your network.');
|
|
26
|
+
return false;
|
|
27
|
+
}
|
|
28
|
+
// An empty upload is the cheapest way to ask "would you accept this key". It never stores
|
|
29
|
+
// anything: the server rejects it for a missing release long before it looks at any file.
|
|
30
|
+
const form = new FormData();
|
|
31
|
+
const response = await fetch(`${host}/v1/sourcemaps`, {
|
|
32
|
+
method: 'POST',
|
|
33
|
+
headers: { 'X-Vinktar-Key': key },
|
|
34
|
+
body: form,
|
|
35
|
+
});
|
|
36
|
+
const payload = (await response.json().catch(() => ({})));
|
|
37
|
+
const code = String(payload['error'] ?? '');
|
|
38
|
+
if (response.status === 401) {
|
|
39
|
+
log('key accepted NO — the key is unknown or revoked');
|
|
40
|
+
return false;
|
|
41
|
+
}
|
|
42
|
+
if (code === 'cli_scope_required') {
|
|
43
|
+
log('key accepted yes');
|
|
44
|
+
log(`upload scope NO — this key lacks the "${REQUIRED_SCOPE}" scope`);
|
|
45
|
+
log('');
|
|
46
|
+
log(`Source-map upload needs a key with the "${REQUIRED_SCOPE}" scope; a write key is not enough.`);
|
|
47
|
+
log('Create one in project settings and pass it with --key.');
|
|
48
|
+
return false;
|
|
49
|
+
}
|
|
50
|
+
// Reaching the release check means auth and scope both passed.
|
|
51
|
+
log('key accepted yes');
|
|
52
|
+
log('upload scope yes');
|
|
53
|
+
log('');
|
|
54
|
+
log(buildOk ? 'Ready to upload.' : 'Fix the build above, then upload.');
|
|
55
|
+
return buildOk;
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* Two counts and a verdict. "Not injected" is a warning (release + url still matches); a map
|
|
59
|
+
* whose id differs from its chunk's is a failure, because that map can never be found.
|
|
60
|
+
*/
|
|
61
|
+
async function checkBuild(dir, log, options) {
|
|
62
|
+
let found;
|
|
63
|
+
try {
|
|
64
|
+
found = await audit(dir, options);
|
|
65
|
+
}
|
|
66
|
+
catch (error) {
|
|
67
|
+
log(`build NO — cannot read ${dir}: ${error instanceof Error ? error.message : String(error)}`);
|
|
68
|
+
return false;
|
|
69
|
+
}
|
|
70
|
+
const total = found.injected + found.notInjected;
|
|
71
|
+
log(`build ${dir}`);
|
|
72
|
+
for (const line of found.warnings)
|
|
73
|
+
log(` note: ${line}`);
|
|
74
|
+
if (total === 0) {
|
|
75
|
+
log('chunks with maps none');
|
|
76
|
+
log(found.withoutMaps > 0
|
|
77
|
+
? `${found.withoutMaps} chunk(s) have no .map beside them. Build with source maps enabled.`
|
|
78
|
+
: 'No JavaScript found. Is that the build output directory?');
|
|
79
|
+
return false;
|
|
80
|
+
}
|
|
81
|
+
log(`chunks injected ${found.injected} of ${total}${found.notInjected > 0 ? ` (${found.notInjected} without a debug id)` : ''}`);
|
|
82
|
+
if (found.withoutMaps > 0)
|
|
83
|
+
log(`without a map ${found.withoutMaps}, left alone`);
|
|
84
|
+
// A chunk naming an id that nothing registers at runtime is the quietest failure here: the map
|
|
85
|
+
// is filed correctly and no frame ever asks for it, because the snippet was stripped or was
|
|
86
|
+
// never there. Rollup's own `output.sourcemapDebugIds` produces exactly this on its own.
|
|
87
|
+
if (found.unregistered > 0) {
|
|
88
|
+
log(`registered at run NO — ${found.unregistered} chunk(s) name a debug id but do not register it`);
|
|
89
|
+
log('Those ids come from the bundler, not from this plugin. Frames from them match by release + url only.');
|
|
90
|
+
}
|
|
91
|
+
const blank = found.entries.filter((entry) => entry.empty).length;
|
|
92
|
+
if (blank > 0)
|
|
93
|
+
log(`empty maps ${blank}, nothing to resolve (usually an HTML entry facade)`);
|
|
94
|
+
const bare = found.entries.filter((entry) => !entry.empty && !entry.hasSourcesContent);
|
|
95
|
+
if (bare.length > 0) {
|
|
96
|
+
log(`sourcesContent ${bare.length} map(s) carry none`);
|
|
97
|
+
log('Frames from those resolve to a file and a line, with no source text to show. Turn on');
|
|
98
|
+
log('`sourcesContent` in your bundler, or upload the sources alongside.');
|
|
99
|
+
}
|
|
100
|
+
// Two snippets in one chunk means it was stamped twice: the output directory held the previous
|
|
101
|
+
// build when this one ran, so the id is right and the bytes are a build older than they look.
|
|
102
|
+
const twice = found.entries.filter((entry) => entry.snippets > 1).length;
|
|
103
|
+
if (twice > 0) {
|
|
104
|
+
log(`stamped twice ${twice} chunk(s)`);
|
|
105
|
+
log('The output directory was not cleaned before the build. Delete it and rebuild.');
|
|
106
|
+
}
|
|
107
|
+
if (found.mismatched > 0) {
|
|
108
|
+
log(`maps match NO — ${found.mismatched} map(s) do not carry their chunk's debug id`);
|
|
109
|
+
for (const entry of found.entries) {
|
|
110
|
+
if (entry.chunkId !== null && entry.mapId !== entry.chunkId) {
|
|
111
|
+
log(` ${entry.relative}: chunk ${entry.chunkId}, map ${entry.mapId ?? 'none'}`);
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
log('The map was rebuilt after the chunk was stamped. Rebuild, then upload (which injects again).');
|
|
115
|
+
return false;
|
|
116
|
+
}
|
|
117
|
+
log(`maps match yes${found.notInjected > 0 ? ' (for the injected chunks)' : ''}`);
|
|
118
|
+
if (found.notInjected > 0) {
|
|
119
|
+
log(`${found.notInjected} chunk(s) have no debug id; frames from them will only match by release + url. "sourcemaps upload" injects by default.`);
|
|
120
|
+
}
|
|
121
|
+
return true;
|
|
122
|
+
}
|
|
123
|
+
async function probe(url) {
|
|
124
|
+
try {
|
|
125
|
+
const response = await fetch(url, { signal: AbortSignal.timeout(10_000) });
|
|
126
|
+
return { ok: response.ok, detail: `HTTP ${response.status}` };
|
|
127
|
+
}
|
|
128
|
+
catch (error) {
|
|
129
|
+
return { ok: false, detail: error instanceof Error ? error.message : 'unreachable' };
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
//# sourceMappingURL=doctor.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"doctor.js","sourceRoot":"","sources":["../../src/commands/doctor.ts"],"names":[],"mappings":"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","sourcesContent":["import { DEFAULT_HOST, REQUIRED_SCOPE } from '../limits.js';\nimport { audit } from './inject.js';\n\nexport interface DoctorOptions {\n /** A build directory to audit for debug ids, on top of the key and host checks. */\n readonly dir?: string;\n readonly ignore?: readonly string[];\n readonly extensions?: readonly string[];\n}\n\n/**\n * Answers \"is this key going to work, and for what\", and with `--dir`, \"is this build stamped\".\n *\n * Every failure mode of a source-map setup looks identical from the outside (stack traces stay\n * minified), so this exists to distinguish \"wrong key\", \"right key, wrong scope\", \"wrong host\",\n * \"the build was never injected\" and \"fine, your maps just have not uploaded yet\" before anyone\n * spends an afternoon on it.\n */\nexport async function doctor(\n host: string,\n key: string,\n log: (line: string) => void,\n options: DoctorOptions = {},\n): Promise<boolean> {\n log(`host ${host}${host === DEFAULT_HOST ? '' : ' (custom)'}`);\n log(`key ${key.slice(0, 11)}…${key.slice(-4)}`);\n log('');\n\n // Local, so it runs before the network: an unreachable host should not hide a broken build.\n let buildOk = true;\n if (options.dir !== undefined) {\n buildOk = await checkBuild(options.dir, log, options);\n log('');\n }\n\n const health = await probe(`${host}/v1/health`);\n log(health.ok ? 'reachable yes' : `reachable NO — ${health.detail}`);\n if (!health.ok) {\n log('');\n log('Nothing else can be checked until the host responds. Check --host and your network.');\n\n return false;\n }\n\n // An empty upload is the cheapest way to ask \"would you accept this key\". It never stores\n // anything: the server rejects it for a missing release long before it looks at any file.\n const form = new FormData();\n const response = await fetch(`${host}/v1/sourcemaps`, {\n method: 'POST',\n headers: { 'X-Vinktar-Key': key },\n body: form,\n });\n\n const payload = (await response.json().catch(() => ({}))) as Record<string, unknown>;\n const code = String(payload['error'] ?? '');\n\n if (response.status === 401) {\n log('key accepted NO — the key is unknown or revoked');\n\n return false;\n }\n\n if (code === 'cli_scope_required') {\n log('key accepted yes');\n log(`upload scope NO — this key lacks the \"${REQUIRED_SCOPE}\" scope`);\n log('');\n log(`Source-map upload needs a key with the \"${REQUIRED_SCOPE}\" scope; a write key is not enough.`);\n log('Create one in project settings and pass it with --key.');\n\n return false;\n }\n\n // Reaching the release check means auth and scope both passed.\n log('key accepted yes');\n log('upload scope yes');\n log('');\n log(buildOk ? 'Ready to upload.' : 'Fix the build above, then upload.');\n\n return buildOk;\n}\n\n/**\n * Two counts and a verdict. \"Not injected\" is a warning (release + url still matches); a map\n * whose id differs from its chunk's is a failure, because that map can never be found.\n */\nasync function checkBuild(\n dir: string,\n log: (line: string) => void,\n options: DoctorOptions,\n): Promise<boolean> {\n let found: Awaited<ReturnType<typeof audit>>;\n try {\n found = await audit(dir, options);\n } catch (error) {\n log(`build NO — cannot read ${dir}: ${error instanceof Error ? error.message : String(error)}`);\n\n return false;\n }\n\n const total = found.injected + found.notInjected;\n log(`build ${dir}`);\n\n for (const line of found.warnings) log(` note: ${line}`);\n\n if (total === 0) {\n log('chunks with maps none');\n log(found.withoutMaps > 0\n ? `${found.withoutMaps} chunk(s) have no .map beside them. Build with source maps enabled.`\n : 'No JavaScript found. Is that the build output directory?');\n\n return false;\n }\n\n log(`chunks injected ${found.injected} of ${total}${found.notInjected > 0 ? ` (${found.notInjected} without a debug id)` : ''}`);\n if (found.withoutMaps > 0) log(`without a map ${found.withoutMaps}, left alone`);\n\n // A chunk naming an id that nothing registers at runtime is the quietest failure here: the map\n // is filed correctly and no frame ever asks for it, because the snippet was stripped or was\n // never there. Rollup's own `output.sourcemapDebugIds` produces exactly this on its own.\n if (found.unregistered > 0) {\n log(`registered at run NO — ${found.unregistered} chunk(s) name a debug id but do not register it`);\n log('Those ids come from the bundler, not from this plugin. Frames from them match by release + url only.');\n }\n\n const blank = found.entries.filter((entry) => entry.empty).length;\n if (blank > 0) log(`empty maps ${blank}, nothing to resolve (usually an HTML entry facade)`);\n\n const bare = found.entries.filter((entry) => !entry.empty && !entry.hasSourcesContent);\n if (bare.length > 0) {\n log(`sourcesContent ${bare.length} map(s) carry none`);\n log('Frames from those resolve to a file and a line, with no source text to show. Turn on');\n log('`sourcesContent` in your bundler, or upload the sources alongside.');\n }\n\n // Two snippets in one chunk means it was stamped twice: the output directory held the previous\n // build when this one ran, so the id is right and the bytes are a build older than they look.\n const twice = found.entries.filter((entry) => entry.snippets > 1).length;\n if (twice > 0) {\n log(`stamped twice ${twice} chunk(s)`);\n log('The output directory was not cleaned before the build. Delete it and rebuild.');\n }\n\n if (found.mismatched > 0) {\n log(`maps match NO — ${found.mismatched} map(s) do not carry their chunk's debug id`);\n for (const entry of found.entries) {\n if (entry.chunkId !== null && entry.mapId !== entry.chunkId) {\n log(` ${entry.relative}: chunk ${entry.chunkId}, map ${entry.mapId ?? 'none'}`);\n }\n }\n log('The map was rebuilt after the chunk was stamped. Rebuild, then upload (which injects again).');\n\n return false;\n }\n\n log(`maps match yes${found.notInjected > 0 ? ' (for the injected chunks)' : ''}`);\n if (found.notInjected > 0) {\n log(`${found.notInjected} chunk(s) have no debug id; frames from them will only match by release + url. \"sourcemaps upload\" injects by default.`);\n }\n\n return true;\n}\n\nasync function probe(url: string): Promise<{ ok: boolean; detail: string }> {\n try {\n const response = await fetch(url, { signal: AbortSignal.timeout(10_000) });\n\n return { ok: response.ok, detail: `HTTP ${response.status}` };\n } catch (error) {\n return { ok: false, detail: error instanceof Error ? error.message : 'unreachable' };\n }\n}\n"]}
|