@autter/runtime-cli 1.0.0 → 1.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +97 -19
- package/dist/{build-BXFh_jbP.d.cts → build-BqjqqeLg.d.cts} +35 -9
- package/dist/{build-BXFh_jbP.d.ts → build-BqjqqeLg.d.ts} +35 -9
- package/dist/chunk-KIQRQY3X.js +1044 -0
- package/dist/{chunk-GGHHEUPQ.js → chunk-SIL3WZZ3.js} +26 -8
- package/dist/cli.js +2394 -154
- package/dist/index.cjs +817 -60
- package/dist/index.d.cts +111 -2
- package/dist/index.d.ts +111 -2
- package/dist/index.js +261 -2
- package/dist/next.cjs +645 -104
- package/dist/next.d.cts +3 -2
- package/dist/next.d.ts +3 -2
- package/dist/next.js +27 -8
- package/dist/vite.cjs +622 -95
- package/dist/vite.d.cts +3 -2
- package/dist/vite.d.ts +3 -2
- package/dist/vite.js +32 -6
- package/dist/webpack.cjs +615 -95
- package/dist/webpack.d.cts +11 -4
- package/dist/webpack.d.ts +11 -4
- package/dist/webpack.js +2 -2
- package/package.json +6 -1
- package/dist/chunk-SIRFO2QU.js +0 -524
package/dist/index.cjs
CHANGED
|
@@ -21,19 +21,29 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
|
|
|
21
21
|
var src_exports = {};
|
|
22
22
|
__export(src_exports, {
|
|
23
23
|
BUNDLE_RE: () => BUNDLE_RE,
|
|
24
|
+
CLIENT_KEY_PREFIX: () => CLIENT_KEY_PREFIX,
|
|
24
25
|
DEBUG_ID_RE: () => DEBUG_ID_RE,
|
|
25
26
|
DEBUG_ID_REGISTRY: () => DEBUG_ID_REGISTRY,
|
|
26
27
|
DEFAULT_ENDPOINT: () => DEFAULT_ENDPOINT,
|
|
27
28
|
FatalUploadError: () => FatalUploadError,
|
|
29
|
+
KEY_ADVICE: () => KEY_ADVICE,
|
|
30
|
+
MAX_MAP_CHARS: () => MAX_MAP_CHARS,
|
|
28
31
|
MAX_UPLOAD_BYTES: () => MAX_UPLOAD_BYTES,
|
|
29
32
|
RELEASE_ENV_VARS: () => RELEASE_ENV_VARS,
|
|
30
33
|
RELEASE_RE: () => RELEASE_RE,
|
|
34
|
+
SCOPES_KEY: () => SCOPES_KEY,
|
|
35
|
+
SCOPES_VERSION: () => SCOPES_VERSION,
|
|
36
|
+
SERVER_KEY_PREFIX: () => SERVER_KEY_PREFIX,
|
|
31
37
|
SNIPPET_LINE_KEY: () => SNIPPET_LINE_KEY,
|
|
38
|
+
UPLOAD_KEY_PREFIX: () => UPLOAD_KEY_PREFIX,
|
|
39
|
+
computeScopes: () => computeScopes,
|
|
32
40
|
debugIdFor: () => debugIdFor,
|
|
33
41
|
debugIdSnippet: () => debugIdSnippet,
|
|
34
42
|
findBundles: () => findBundles,
|
|
35
43
|
formatUploadSummary: () => formatUploadSummary,
|
|
44
|
+
formatVerify: () => formatVerify,
|
|
36
45
|
gitHead: () => gitHead,
|
|
46
|
+
globToRegExp: () => globToRegExp,
|
|
37
47
|
hasDebugIdSnippet: () => hasDebugIdSnippet,
|
|
38
48
|
injectBundle: () => injectBundle,
|
|
39
49
|
injectDir: () => injectDir,
|
|
@@ -49,7 +59,8 @@ __export(src_exports, {
|
|
|
49
59
|
snippetLine: () => snippetLine,
|
|
50
60
|
storedMapJson: () => storedMapJson,
|
|
51
61
|
stripSourceMappingUrl: () => stripSourceMappingUrl,
|
|
52
|
-
uploadDir: () => uploadDir
|
|
62
|
+
uploadDir: () => uploadDir,
|
|
63
|
+
verifyDir: () => verifyDir
|
|
53
64
|
});
|
|
54
65
|
module.exports = __toCommonJS(src_exports);
|
|
55
66
|
|
|
@@ -121,7 +132,7 @@ function mapPathFor(js, code) {
|
|
|
121
132
|
const sibling = `${js}.map`;
|
|
122
133
|
return (0, import_node_fs.existsSync)(sibling) ? sibling : null;
|
|
123
134
|
}
|
|
124
|
-
async function walk(dir, out) {
|
|
135
|
+
async function walk(dir, out, pattern = BUNDLE_RE) {
|
|
125
136
|
let entries;
|
|
126
137
|
try {
|
|
127
138
|
entries = await (0, import_promises.readdir)(dir, { withFileTypes: true });
|
|
@@ -131,10 +142,47 @@ async function walk(dir, out) {
|
|
|
131
142
|
for (const entry of entries) {
|
|
132
143
|
if (entry.name === "node_modules") continue;
|
|
133
144
|
const full = (0, import_node_path.join)(dir, entry.name);
|
|
134
|
-
if (entry.isDirectory()) await walk(full, out);
|
|
135
|
-
else if (entry.isFile() &&
|
|
145
|
+
if (entry.isDirectory()) await walk(full, out, pattern);
|
|
146
|
+
else if (entry.isFile() && pattern.test(entry.name)) out.push(full);
|
|
136
147
|
}
|
|
137
148
|
}
|
|
149
|
+
var CSS_SOURCE_MAPPING_URL_RE = /\/\*[#@] sourceMappingURL=([^\s*]+)\s*\*\/\s*$/;
|
|
150
|
+
function pointsAt(file, url, map) {
|
|
151
|
+
if (!url || /^[a-z][\w+.-]*:/i.test(url) || url.startsWith("/")) return false;
|
|
152
|
+
let path = url.split(/[?#]/)[0];
|
|
153
|
+
try {
|
|
154
|
+
path = decodeURIComponent(path);
|
|
155
|
+
} catch {
|
|
156
|
+
}
|
|
157
|
+
return (0, import_node_path.resolve)((0, import_node_path.dirname)(file), path) === map;
|
|
158
|
+
}
|
|
159
|
+
async function stripReference(file, map) {
|
|
160
|
+
let code;
|
|
161
|
+
try {
|
|
162
|
+
code = await (0, import_promises.readFile)(file, "utf8");
|
|
163
|
+
} catch {
|
|
164
|
+
return;
|
|
165
|
+
}
|
|
166
|
+
let stripped = code;
|
|
167
|
+
if (BUNDLE_RE.test(file)) {
|
|
168
|
+
if (pointsAt(file, readSourceMappingUrl(code), map)) stripped = stripSourceMappingUrl(code);
|
|
169
|
+
} else if (file.endsWith(".css")) {
|
|
170
|
+
const match = CSS_SOURCE_MAPPING_URL_RE.exec(code);
|
|
171
|
+
if (match && pointsAt(file, match[1], map)) stripped = code.slice(0, match.index).replace(/\n?$/, "\n");
|
|
172
|
+
}
|
|
173
|
+
if (stripped !== code) await (0, import_promises.writeFile)(file, stripped);
|
|
174
|
+
}
|
|
175
|
+
async function removeSourceMaps(maps, owners = /* @__PURE__ */ new Map()) {
|
|
176
|
+
const removed = [];
|
|
177
|
+
for (const map of [...new Set(maps)].sort()) {
|
|
178
|
+
if (!(0, import_node_fs.existsSync)(map)) continue;
|
|
179
|
+
await (0, import_promises.rm)(map, { force: true });
|
|
180
|
+
removed.push(map);
|
|
181
|
+
const owner = owners.get(map) ?? map.replace(/\.map$/, "");
|
|
182
|
+
await stripReference(owner, map);
|
|
183
|
+
}
|
|
184
|
+
return removed;
|
|
185
|
+
}
|
|
138
186
|
async function findBundles(dir, files) {
|
|
139
187
|
const root = (0, import_node_path.resolve)(dir);
|
|
140
188
|
const candidates = [];
|
|
@@ -260,6 +308,8 @@ async function injectDir(dir, options = {}) {
|
|
|
260
308
|
// src/upload.ts
|
|
261
309
|
var import_node_crypto2 = require("crypto");
|
|
262
310
|
var import_promises3 = require("fs/promises");
|
|
311
|
+
var import_node_util = require("util");
|
|
312
|
+
var import_node_zlib = require("zlib");
|
|
263
313
|
|
|
264
314
|
// src/release.ts
|
|
265
315
|
var import_node_child_process = require("child_process");
|
|
@@ -305,9 +355,386 @@ function resolveRelease(explicit, env = process.env, cwd = process.cwd()) {
|
|
|
305
355
|
);
|
|
306
356
|
}
|
|
307
357
|
|
|
358
|
+
// src/scopes.ts
|
|
359
|
+
var import_acorn = require("acorn");
|
|
360
|
+
var SCOPES_KEY = "x_autter_scopes";
|
|
361
|
+
var SCOPES_VERSION = 1;
|
|
362
|
+
var BASE_OPTIONS = {
|
|
363
|
+
ecmaVersion: "latest",
|
|
364
|
+
allowHashBang: true,
|
|
365
|
+
allowReturnOutsideFunction: true,
|
|
366
|
+
allowReserved: true
|
|
367
|
+
};
|
|
368
|
+
function parseBundle(code) {
|
|
369
|
+
try {
|
|
370
|
+
return (0, import_acorn.parse)(code, {
|
|
371
|
+
...BASE_OPTIONS,
|
|
372
|
+
sourceType: "module",
|
|
373
|
+
allowAwaitOutsideFunction: true,
|
|
374
|
+
allowImportExportEverywhere: true
|
|
375
|
+
});
|
|
376
|
+
} catch (moduleError) {
|
|
377
|
+
try {
|
|
378
|
+
return (0, import_acorn.parse)(code, { ...BASE_OPTIONS, sourceType: "script" });
|
|
379
|
+
} catch (scriptError) {
|
|
380
|
+
const pos = (err) => typeof err.pos === "number" ? err.pos : -1;
|
|
381
|
+
throw pos(scriptError) > pos(moduleError) ? scriptError : moduleError;
|
|
382
|
+
}
|
|
383
|
+
}
|
|
384
|
+
}
|
|
385
|
+
function isNode(value) {
|
|
386
|
+
return typeof value === "object" && value !== null && typeof value.type === "string";
|
|
387
|
+
}
|
|
388
|
+
var IDENTIFIER_NAME_RE = /^[$_\p{ID_Start}][$\u200c\u200d\p{ID_Continue}]*$/u;
|
|
389
|
+
function identifierToken(node) {
|
|
390
|
+
if (!isNode(node)) return null;
|
|
391
|
+
if (node.type === "Identifier") return { offset: node.start, name: node.name, literal: false };
|
|
392
|
+
if (node.type === "PrivateIdentifier") return { offset: node.start, name: `#${node.name}`, literal: false };
|
|
393
|
+
return null;
|
|
394
|
+
}
|
|
395
|
+
function literalToken(node) {
|
|
396
|
+
if (!isNode(node) || node.type !== "Literal" || typeof node.value !== "string") return null;
|
|
397
|
+
return IDENTIFIER_NAME_RE.test(node.value) ? { offset: node.start, name: node.value, literal: true } : null;
|
|
398
|
+
}
|
|
399
|
+
function keyToken(property) {
|
|
400
|
+
if (!property.computed) {
|
|
401
|
+
const id = identifierToken(property.key);
|
|
402
|
+
if (id) return id;
|
|
403
|
+
}
|
|
404
|
+
return literalToken(property.key);
|
|
405
|
+
}
|
|
406
|
+
function bindingToken(target) {
|
|
407
|
+
if (!isNode(target)) return null;
|
|
408
|
+
if (target.type === "Identifier") return identifierToken(target);
|
|
409
|
+
if (target.type === "MemberExpression") {
|
|
410
|
+
return target.computed ? literalToken(target.property) : identifierToken(target.property);
|
|
411
|
+
}
|
|
412
|
+
return null;
|
|
413
|
+
}
|
|
414
|
+
function contextTokens(node, parent) {
|
|
415
|
+
let token = null;
|
|
416
|
+
switch (parent?.type) {
|
|
417
|
+
case "VariableDeclarator":
|
|
418
|
+
if (parent.init === node) token = bindingToken(parent.id);
|
|
419
|
+
break;
|
|
420
|
+
case "AssignmentExpression":
|
|
421
|
+
case "AssignmentPattern":
|
|
422
|
+
if (parent.right === node) token = bindingToken(parent.left);
|
|
423
|
+
break;
|
|
424
|
+
case "Property":
|
|
425
|
+
case "PropertyDefinition":
|
|
426
|
+
case "MethodDefinition":
|
|
427
|
+
if (parent.value === node) token = keyToken(parent);
|
|
428
|
+
break;
|
|
429
|
+
}
|
|
430
|
+
return token ? [token] : [];
|
|
431
|
+
}
|
|
432
|
+
function classTokens(path) {
|
|
433
|
+
if (!path || path.node.type !== "ClassDeclaration" && path.node.type !== "ClassExpression") return [];
|
|
434
|
+
const own = identifierToken(path.node.id);
|
|
435
|
+
return [...own ? [own] : [], ...contextTokens(path.node, path.parent?.node)];
|
|
436
|
+
}
|
|
437
|
+
function describe(path) {
|
|
438
|
+
const fn = path.node;
|
|
439
|
+
const parent = path.parent?.node;
|
|
440
|
+
const own = identifierToken(fn.id);
|
|
441
|
+
const isMethod = parent?.type === "MethodDefinition" || parent?.type === "Property" && parent.value === fn && (parent.method === true || parent.kind !== "init");
|
|
442
|
+
let context;
|
|
443
|
+
if (parent?.type === "MethodDefinition" && parent.kind === "constructor") {
|
|
444
|
+
context = classTokens(path.parent?.parent?.parent ?? null);
|
|
445
|
+
} else {
|
|
446
|
+
context = contextTokens(fn, parent);
|
|
447
|
+
}
|
|
448
|
+
return {
|
|
449
|
+
start: fn.start,
|
|
450
|
+
end: fn.end,
|
|
451
|
+
own,
|
|
452
|
+
recoverAt: !own && fn.type !== "ArrowFunctionExpression" && !isMethod ? fn.start : null,
|
|
453
|
+
context
|
|
454
|
+
};
|
|
455
|
+
}
|
|
456
|
+
var FUNCTION_TYPES = /* @__PURE__ */ new Set(["FunctionDeclaration", "FunctionExpression", "ArrowFunctionExpression"]);
|
|
457
|
+
function classInitializer(path) {
|
|
458
|
+
const node = path.node;
|
|
459
|
+
let range = null;
|
|
460
|
+
if (node.type === "StaticBlock") range = node;
|
|
461
|
+
else if (node.type === "PropertyDefinition" && isNode(node.value) && !FUNCTION_TYPES.has(node.value.type)) range = node.value;
|
|
462
|
+
if (!range) return null;
|
|
463
|
+
return { start: range.start, end: range.end, own: null, recoverAt: null, context: classTokens(path.parent?.parent ?? null) };
|
|
464
|
+
}
|
|
465
|
+
function collectFunctions(program) {
|
|
466
|
+
const found = [];
|
|
467
|
+
const stack = [{ node: program, parent: null }];
|
|
468
|
+
for (let path = stack.pop(); path; path = stack.pop()) {
|
|
469
|
+
const node = path.node;
|
|
470
|
+
if (FUNCTION_TYPES.has(node.type)) found.push(describe(path));
|
|
471
|
+
else {
|
|
472
|
+
const initializer = classInitializer(path);
|
|
473
|
+
if (initializer) found.push(initializer);
|
|
474
|
+
}
|
|
475
|
+
for (const key in node) {
|
|
476
|
+
const value = node[key];
|
|
477
|
+
if (Array.isArray(value)) {
|
|
478
|
+
for (let i = value.length - 1; i >= 0; i--) if (isNode(value[i])) stack.push({ node: value[i], parent: path });
|
|
479
|
+
} else if (isNode(value)) {
|
|
480
|
+
stack.push({ node: value, parent: path });
|
|
481
|
+
}
|
|
482
|
+
}
|
|
483
|
+
}
|
|
484
|
+
return found;
|
|
485
|
+
}
|
|
486
|
+
function lineStarts(code) {
|
|
487
|
+
const starts = [0];
|
|
488
|
+
for (let i = 0; i < code.length; i++) {
|
|
489
|
+
const c = code.charCodeAt(i);
|
|
490
|
+
if (c === 10 || c === 8232 || c === 8233) starts.push(i + 1);
|
|
491
|
+
else if (c === 13) {
|
|
492
|
+
if (code.charCodeAt(i + 1) === 10) i++;
|
|
493
|
+
starts.push(i + 1);
|
|
494
|
+
}
|
|
495
|
+
}
|
|
496
|
+
return starts;
|
|
497
|
+
}
|
|
498
|
+
function positionOf(starts, offset) {
|
|
499
|
+
let lo = 0;
|
|
500
|
+
let hi = starts.length - 1;
|
|
501
|
+
while (lo < hi) {
|
|
502
|
+
const mid = lo + hi + 1 >> 1;
|
|
503
|
+
if (starts[mid] <= offset) lo = mid;
|
|
504
|
+
else hi = mid - 1;
|
|
505
|
+
}
|
|
506
|
+
return [lo, offset - starts[lo]];
|
|
507
|
+
}
|
|
508
|
+
var BASE64 = new Int8Array(128).fill(-1);
|
|
509
|
+
for (const [i, ch] of [..."ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"].entries()) BASE64[ch.charCodeAt(0)] = i;
|
|
510
|
+
function findSegments(mappings, wanted) {
|
|
511
|
+
let line = 0;
|
|
512
|
+
let column = 0;
|
|
513
|
+
let source = 0;
|
|
514
|
+
let originalLine = 0;
|
|
515
|
+
let originalColumn = 0;
|
|
516
|
+
let name = 0;
|
|
517
|
+
let want = wanted.get(0);
|
|
518
|
+
const fields = [0, 0, 0, 0, 0];
|
|
519
|
+
let i = 0;
|
|
520
|
+
while (i < mappings.length) {
|
|
521
|
+
const ch = mappings.charCodeAt(i);
|
|
522
|
+
if (ch === 59) {
|
|
523
|
+
line++;
|
|
524
|
+
column = 0;
|
|
525
|
+
want = wanted.get(line);
|
|
526
|
+
i++;
|
|
527
|
+
continue;
|
|
528
|
+
}
|
|
529
|
+
if (ch === 44) {
|
|
530
|
+
i++;
|
|
531
|
+
continue;
|
|
532
|
+
}
|
|
533
|
+
let count = 0;
|
|
534
|
+
while (i < mappings.length) {
|
|
535
|
+
const next = mappings.charCodeAt(i);
|
|
536
|
+
if (next === 44 || next === 59) break;
|
|
537
|
+
let value = 0;
|
|
538
|
+
let shift = 0;
|
|
539
|
+
let digit;
|
|
540
|
+
do {
|
|
541
|
+
const code = mappings.charCodeAt(i++);
|
|
542
|
+
digit = code < 128 ? BASE64[code] : -1;
|
|
543
|
+
if (digit < 0 || shift > 30) throw new Error("invalid mappings");
|
|
544
|
+
value |= (digit & 31) << shift;
|
|
545
|
+
shift += 5;
|
|
546
|
+
} while (digit & 32);
|
|
547
|
+
const decoded = value & 1 ? -(value >>> 1) : value >>> 1;
|
|
548
|
+
if (count < 5) fields[count] = decoded;
|
|
549
|
+
count++;
|
|
550
|
+
}
|
|
551
|
+
column += fields[0];
|
|
552
|
+
if (count >= 4) {
|
|
553
|
+
source += fields[1];
|
|
554
|
+
originalLine += fields[2];
|
|
555
|
+
originalColumn += fields[3];
|
|
556
|
+
}
|
|
557
|
+
if (count >= 5) name += fields[4];
|
|
558
|
+
if (want && want.get(column) === null) {
|
|
559
|
+
want.set(column, {
|
|
560
|
+
source: count >= 4 ? source : -1,
|
|
561
|
+
line: originalLine,
|
|
562
|
+
column: originalColumn,
|
|
563
|
+
name: count >= 5 ? name : -1
|
|
564
|
+
});
|
|
565
|
+
}
|
|
566
|
+
}
|
|
567
|
+
}
|
|
568
|
+
function looksMangled(name) {
|
|
569
|
+
return name.replace(/^#/, "").length <= 2;
|
|
570
|
+
}
|
|
571
|
+
var IDENTIFIER_AT_RE = /[$_\p{ID_Start}][$\u200c\u200d\p{ID_Continue}]*/uy;
|
|
572
|
+
var OWN_NAME_AT_RE = /(?:async\s+)?function\b\s*\*?\s*([$_\p{ID_Start}][$\u200c\u200d\p{ID_Continue}]*)/uy;
|
|
573
|
+
var NOT_A_NAME = new Set(
|
|
574
|
+
"abstract async await break case catch class const continue debugger declare default delete do else enum export extends false finally for function get if implements import in instanceof interface let new null of override package private protected public readonly return set static super switch this throw true try typeof var void while with yield".split(" ")
|
|
575
|
+
);
|
|
576
|
+
var NameResolver = class {
|
|
577
|
+
constructor(map, segmentAt) {
|
|
578
|
+
this.segmentAt = segmentAt;
|
|
579
|
+
this.names = Array.isArray(map.names) ? map.names : [];
|
|
580
|
+
this.sourcesContent = Array.isArray(map.sourcesContent) ? map.sourcesContent : [];
|
|
581
|
+
}
|
|
582
|
+
segmentAt;
|
|
583
|
+
names;
|
|
584
|
+
sourcesContent;
|
|
585
|
+
sourceLines = /* @__PURE__ */ new Map();
|
|
586
|
+
originalLine(segment) {
|
|
587
|
+
if (segment.source < 0) return null;
|
|
588
|
+
let lines = this.sourceLines.get(segment.source);
|
|
589
|
+
if (lines === void 0) {
|
|
590
|
+
const content = this.sourcesContent[segment.source];
|
|
591
|
+
lines = typeof content === "string" ? content.split(/\r\n|[\n\r\u2028\u2029]/) : null;
|
|
592
|
+
this.sourceLines.set(segment.source, lines);
|
|
593
|
+
}
|
|
594
|
+
return lines?.[segment.line] ?? null;
|
|
595
|
+
}
|
|
596
|
+
/** The original identifier at a segment's source position. */
|
|
597
|
+
identifierAt(segment, re, group = 0) {
|
|
598
|
+
const text = this.originalLine(segment);
|
|
599
|
+
if (text === null) return null;
|
|
600
|
+
re.lastIndex = segment.column;
|
|
601
|
+
const found = re.exec(text)?.[group];
|
|
602
|
+
return found && !NOT_A_NAME.has(found) ? found : null;
|
|
603
|
+
}
|
|
604
|
+
/** Original name of a token: the map's `names` at its exact position;
|
|
605
|
+
* else the generated name when it isn't mangled; else the identifier
|
|
606
|
+
* at the mapped position in `sourcesContent`. Null when unknown. */
|
|
607
|
+
resolve(token) {
|
|
608
|
+
if (token.literal) return token.name;
|
|
609
|
+
const segment = this.segmentAt(token.offset);
|
|
610
|
+
if (segment && segment.name >= 0) {
|
|
611
|
+
const name = this.names[segment.name];
|
|
612
|
+
if (typeof name === "string" && name) return name;
|
|
613
|
+
}
|
|
614
|
+
if (!looksMangled(token.name)) return token.name;
|
|
615
|
+
return segment ? this.identifierAt(segment, IDENTIFIER_AT_RE) : null;
|
|
616
|
+
}
|
|
617
|
+
/** The name a minifier removed from `function name() {}`, read from the
|
|
618
|
+
* original source at the function's mapped start. */
|
|
619
|
+
recover(offset) {
|
|
620
|
+
const segment = this.segmentAt(offset);
|
|
621
|
+
return segment ? this.identifierAt(segment, OWN_NAME_AT_RE, 1) : null;
|
|
622
|
+
}
|
|
623
|
+
};
|
|
624
|
+
var BASE64_DIGITS = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
|
|
625
|
+
function vlq(value) {
|
|
626
|
+
let rest = value < 0 ? -value << 1 | 1 : value << 1;
|
|
627
|
+
let out = "";
|
|
628
|
+
do {
|
|
629
|
+
let digit = rest & 31;
|
|
630
|
+
rest >>>= 5;
|
|
631
|
+
if (rest > 0) digit |= 32;
|
|
632
|
+
out += BASE64_DIGITS[digit];
|
|
633
|
+
} while (rest > 0);
|
|
634
|
+
return out;
|
|
635
|
+
}
|
|
636
|
+
function encodeScopes(entries) {
|
|
637
|
+
const parts = [];
|
|
638
|
+
let line = 0;
|
|
639
|
+
let previousStart = 0;
|
|
640
|
+
let previousName = 0;
|
|
641
|
+
let firstOnLine = true;
|
|
642
|
+
for (const [startLine, startColumn, endLine, endColumn, nameIndex] of entries) {
|
|
643
|
+
if (startLine > line) {
|
|
644
|
+
parts.push(";".repeat(startLine - line));
|
|
645
|
+
line = startLine;
|
|
646
|
+
previousStart = 0;
|
|
647
|
+
firstOnLine = true;
|
|
648
|
+
}
|
|
649
|
+
if (!firstOnLine) parts.push(",");
|
|
650
|
+
firstOnLine = false;
|
|
651
|
+
let segment = vlq(startColumn - previousStart) + vlq(endLine - startLine) + vlq(endLine === startLine ? endColumn - startColumn : endColumn);
|
|
652
|
+
previousStart = startColumn;
|
|
653
|
+
if (nameIndex >= 0) {
|
|
654
|
+
segment += vlq(nameIndex - previousName);
|
|
655
|
+
previousName = nameIndex;
|
|
656
|
+
}
|
|
657
|
+
parts.push(segment);
|
|
658
|
+
}
|
|
659
|
+
return parts.join("");
|
|
660
|
+
}
|
|
661
|
+
function computeScopes(code, map) {
|
|
662
|
+
try {
|
|
663
|
+
return scopesOf(code, map);
|
|
664
|
+
} catch (err) {
|
|
665
|
+
return { error: err instanceof Error ? err.message : String(err) };
|
|
666
|
+
}
|
|
667
|
+
}
|
|
668
|
+
function scopesOf(code, map) {
|
|
669
|
+
if (typeof map.mappings !== "string") return { error: "the map has no mappings string" };
|
|
670
|
+
let functions;
|
|
671
|
+
try {
|
|
672
|
+
functions = collectFunctions(parseBundle(code));
|
|
673
|
+
} catch (err) {
|
|
674
|
+
return { error: `could not parse the bundle: ${err instanceof Error ? err.message : String(err)}` };
|
|
675
|
+
}
|
|
676
|
+
const starts = lineStarts(code);
|
|
677
|
+
const wanted = /* @__PURE__ */ new Map();
|
|
678
|
+
const want = (offset) => {
|
|
679
|
+
const [line, column] = positionOf(starts, offset);
|
|
680
|
+
let columns = wanted.get(line);
|
|
681
|
+
if (!columns) wanted.set(line, columns = /* @__PURE__ */ new Map());
|
|
682
|
+
columns.set(column, null);
|
|
683
|
+
};
|
|
684
|
+
for (const fn of functions) {
|
|
685
|
+
if (fn.own && !fn.own.literal) want(fn.own.offset);
|
|
686
|
+
if (fn.recoverAt !== null) want(fn.recoverAt);
|
|
687
|
+
for (const token of fn.context) if (!token.literal) want(token.offset);
|
|
688
|
+
}
|
|
689
|
+
try {
|
|
690
|
+
findSegments(map.mappings, wanted);
|
|
691
|
+
} catch (err) {
|
|
692
|
+
return { error: `could not read the map: ${err instanceof Error ? err.message : String(err)}` };
|
|
693
|
+
}
|
|
694
|
+
const resolver = new NameResolver(map, (offset) => {
|
|
695
|
+
const [line, column] = positionOf(starts, offset);
|
|
696
|
+
return wanted.get(line)?.get(column) ?? null;
|
|
697
|
+
});
|
|
698
|
+
functions.sort((a, b) => a.start - b.start || b.end - a.end);
|
|
699
|
+
const mapNames = Array.isArray(map.names) ? map.names : [];
|
|
700
|
+
const nameIndex = /* @__PURE__ */ new Map();
|
|
701
|
+
mapNames.forEach((name, index) => {
|
|
702
|
+
if (typeof name === "string" && !nameIndex.has(name)) nameIndex.set(name, index);
|
|
703
|
+
});
|
|
704
|
+
const extraNames = [];
|
|
705
|
+
const indexOf = (name) => {
|
|
706
|
+
let index = nameIndex.get(name);
|
|
707
|
+
if (index === void 0) {
|
|
708
|
+
index = mapNames.length + extraNames.push(name) - 1;
|
|
709
|
+
nameIndex.set(name, index);
|
|
710
|
+
}
|
|
711
|
+
return index;
|
|
712
|
+
};
|
|
713
|
+
const entries = functions.map((fn) => {
|
|
714
|
+
let name = fn.own ? resolver.resolve(fn.own) : null;
|
|
715
|
+
if (name === null && fn.recoverAt !== null) name = resolver.recover(fn.recoverAt);
|
|
716
|
+
for (const token of fn.context) {
|
|
717
|
+
if (name !== null) break;
|
|
718
|
+
name = resolver.resolve(token);
|
|
719
|
+
}
|
|
720
|
+
const index = name !== null ? indexOf(name) : fn.own || fn.context.length > 0 ? indexOf(null) : -1;
|
|
721
|
+
const [startLine, startColumn] = positionOf(starts, fn.start);
|
|
722
|
+
const [endLine, endColumn] = positionOf(starts, fn.end);
|
|
723
|
+
return [startLine, startColumn, endLine, endColumn, index];
|
|
724
|
+
});
|
|
725
|
+
return { scopes: { version: SCOPES_VERSION, names: extraNames, scopes: encodeScopes(entries) } };
|
|
726
|
+
}
|
|
727
|
+
|
|
308
728
|
// src/upload.ts
|
|
309
729
|
var DEFAULT_ENDPOINT = "https://otlp.autter.dev";
|
|
310
|
-
var
|
|
730
|
+
var MAX_MAP_CHARS = 32 * 1024 * 1024;
|
|
731
|
+
var MAX_UPLOAD_BYTES = MAX_MAP_CHARS + 1024 * 1024;
|
|
732
|
+
var LEGACY_MAX_BYTES = 5 * 1024 * 1024;
|
|
733
|
+
var UPLOAD_KEY_PREFIX = "autter_rtu_";
|
|
734
|
+
var SERVER_KEY_PREFIX = "autter_rt_";
|
|
735
|
+
var CLIENT_KEY_PREFIX = "autter_rtc_";
|
|
736
|
+
var KEY_ADVICE = "set AUTTER_RUNTIME_KEY to an upload-only key (autter_rtu_\u2026, recommended for CI: it can only upload source maps) or the full server key (autter_rt_\u2026, also works)";
|
|
737
|
+
var gzip = (0, import_node_util.promisify)(import_node_zlib.gzip);
|
|
311
738
|
var CHECK_BATCH = 500;
|
|
312
739
|
var MAX_ATTEMPTS = 3;
|
|
313
740
|
function resolveEndpoint(flag, env = process.env) {
|
|
@@ -324,7 +751,16 @@ function storedMapJson(map) {
|
|
|
324
751
|
const { sourcesContent: _omitted, ...safe } = map;
|
|
325
752
|
return JSON.stringify(safe);
|
|
326
753
|
}
|
|
327
|
-
|
|
754
|
+
function bytesOf(buffer) {
|
|
755
|
+
return new Uint8Array(buffer.buffer, buffer.byteOffset, buffer.byteLength);
|
|
756
|
+
}
|
|
757
|
+
function fits(json, body) {
|
|
758
|
+
return json.length <= MAX_MAP_CHARS && Buffer.byteLength(body) <= MAX_UPLOAD_BYTES;
|
|
759
|
+
}
|
|
760
|
+
function uploadBody(release, filename, debugId, json) {
|
|
761
|
+
return `{"release":${JSON.stringify(release)},"filename":${JSON.stringify(filename)},${debugId ? `"debugId":${JSON.stringify(debugId)},` : ""}"map":${json}}`;
|
|
762
|
+
}
|
|
763
|
+
async function prepare(bundle, urlPrefix, release, log, compress) {
|
|
328
764
|
let map;
|
|
329
765
|
try {
|
|
330
766
|
map = JSON.parse(await (0, import_promises3.readFile)(bundle.map, "utf8"));
|
|
@@ -336,23 +772,37 @@ async function prepare(bundle, urlPrefix, release) {
|
|
|
336
772
|
}
|
|
337
773
|
const code = await (0, import_promises3.readFile)(bundle.js, "utf8");
|
|
338
774
|
const debugId = readDebugIdComment(code) ?? normalizeDebugId(map.debugId);
|
|
339
|
-
const json = storedMapJson(map);
|
|
340
775
|
const filename = servedFilename(urlPrefix, bundle.path);
|
|
341
|
-
const
|
|
776
|
+
const { [SCOPES_KEY]: _stale, ...plain } = map;
|
|
777
|
+
const scopes = computeScopes(code, map);
|
|
778
|
+
let json = storedMapJson(plain);
|
|
779
|
+
if ("scopes" in scopes) {
|
|
780
|
+
const scoped = storedMapJson({ ...plain, [SCOPES_KEY]: scopes.scopes });
|
|
781
|
+
if (fits(scoped, uploadBody(release, filename, debugId, scoped))) json = scoped;
|
|
782
|
+
else if (fits(json, uploadBody(release, filename, debugId, json))) {
|
|
783
|
+
log(`warning: ${bundle.path}: its map is over 32 MB with function scopes, so it is uploaded without them (frames keep call-site names)`);
|
|
784
|
+
}
|
|
785
|
+
} else {
|
|
786
|
+
log(`warning: ${bundle.path}: no function scopes (${scopes.error}); its map is uploaded without them (frames keep call-site names)`);
|
|
787
|
+
}
|
|
788
|
+
const body = uploadBody(release, filename, debugId, json);
|
|
342
789
|
return {
|
|
343
790
|
bundle,
|
|
344
|
-
|
|
791
|
+
// Maps are text that compresses several times over; the ingester
|
|
792
|
+
// inflates the body (Content-Encoding: gzip).
|
|
793
|
+
body: compress && fits(json, body) ? bytesOf(await gzip(body)) : new Uint8Array(0),
|
|
345
794
|
path: bundle.path,
|
|
346
795
|
filename,
|
|
347
796
|
debugId,
|
|
348
797
|
hash: (0, import_node_crypto2.createHash)("sha256").update(json).digest("hex"),
|
|
349
|
-
bytes: Buffer.byteLength(body)
|
|
798
|
+
bytes: Buffer.byteLength(body),
|
|
799
|
+
mapChars: json.length
|
|
350
800
|
};
|
|
351
801
|
}
|
|
352
802
|
var FatalUploadError = class extends Error {
|
|
353
803
|
};
|
|
354
804
|
function sleep(ms) {
|
|
355
|
-
return new Promise((
|
|
805
|
+
return new Promise((resolve3) => setTimeout(resolve3, ms));
|
|
356
806
|
}
|
|
357
807
|
async function uploadDir(dir, options = {}) {
|
|
358
808
|
const env = options.env ?? process.env;
|
|
@@ -374,14 +824,14 @@ async function uploadDir(dir, options = {}) {
|
|
|
374
824
|
};
|
|
375
825
|
const items = [];
|
|
376
826
|
for (const bundle of await findBundles(dir, options.files)) {
|
|
377
|
-
const prepared = await prepare(bundle, options.urlPrefix, release);
|
|
827
|
+
const prepared = await prepare(bundle, options.urlPrefix, release, log, !options.dryRun);
|
|
378
828
|
if ("error" in prepared) {
|
|
379
829
|
summary.failed.push({ path: bundle.path, filename: servedFilename(options.urlPrefix, bundle.path), error: prepared.error });
|
|
380
|
-
} else if (prepared.bytes > MAX_UPLOAD_BYTES) {
|
|
830
|
+
} else if (prepared.mapChars > MAX_MAP_CHARS || prepared.bytes > MAX_UPLOAD_BYTES) {
|
|
381
831
|
summary.failed.push({
|
|
382
832
|
path: prepared.path,
|
|
383
833
|
filename: prepared.filename,
|
|
384
|
-
error: `map is ${(prepared.
|
|
834
|
+
error: `map is ${(prepared.mapChars / 1048576).toFixed(1)} MB without sourcesContent; the ingester accepts up to 32 MB`
|
|
385
835
|
});
|
|
386
836
|
} else {
|
|
387
837
|
items.push(prepared);
|
|
@@ -392,11 +842,15 @@ async function uploadDir(dir, options = {}) {
|
|
|
392
842
|
return summary;
|
|
393
843
|
}
|
|
394
844
|
const key = (options.key ?? env.AUTTER_RUNTIME_KEY ?? "").trim();
|
|
395
|
-
if (!key) throw new FatalUploadError(
|
|
396
|
-
if (key.startsWith(
|
|
397
|
-
throw new FatalUploadError(
|
|
845
|
+
if (!key) throw new FatalUploadError(`no ingest key: ${KEY_ADVICE}`);
|
|
846
|
+
if (key.startsWith(CLIENT_KEY_PREFIX)) {
|
|
847
|
+
throw new FatalUploadError(`AUTTER_RUNTIME_KEY is a publishable client key (autter_rtc_\u2026), which cannot upload source maps: ${KEY_ADVICE}`);
|
|
398
848
|
}
|
|
849
|
+
const rejected = (status, detail) => new FatalUploadError(
|
|
850
|
+
`the ingester rejected the key (${status}): ${detail}` + (key.startsWith(UPLOAD_KEY_PREFIX) ? " (upload keys, autter_rtu_\u2026, need ingester 1.7.0 or later; the server key works with any)" : "")
|
|
851
|
+
);
|
|
399
852
|
const headers = { "content-type": "application/json", authorization: `Bearer ${key}` };
|
|
853
|
+
const uploadHeaders = { ...headers, "content-encoding": "gzip" };
|
|
400
854
|
const skipped = /* @__PURE__ */ new Set();
|
|
401
855
|
for (let start = 0; start < items.length && !summary.legacyIngester; start += CHECK_BATCH) {
|
|
402
856
|
const batch = items.slice(start, start + CHECK_BATCH);
|
|
@@ -416,9 +870,7 @@ async function uploadDir(dir, options = {}) {
|
|
|
416
870
|
);
|
|
417
871
|
break;
|
|
418
872
|
}
|
|
419
|
-
if (res.status === 401 || res.status === 403)
|
|
420
|
-
throw new FatalUploadError(`the ingester rejected the key (${res.status}): ${(await res.text()).slice(0, 300)}`);
|
|
421
|
-
}
|
|
873
|
+
if (res.status === 401 || res.status === 403) throw rejected(res.status, (await res.text()).slice(0, 300));
|
|
422
874
|
if (!res.ok) throw new Error(`HTTP ${res.status}`);
|
|
423
875
|
const body = await res.json();
|
|
424
876
|
const present = new Set(Array.isArray(body.present) ? body.present : []);
|
|
@@ -438,7 +890,7 @@ async function uploadDir(dir, options = {}) {
|
|
|
438
890
|
let status = 0;
|
|
439
891
|
let detail = "";
|
|
440
892
|
try {
|
|
441
|
-
const res = await doFetch(`${endpoint}/v1/sourcemaps`, { method: "POST", headers, body: item.body });
|
|
893
|
+
const res = await doFetch(`${endpoint}/v1/sourcemaps`, { method: "POST", headers: uploadHeaders, body: item.body });
|
|
442
894
|
status = res.status;
|
|
443
895
|
if (res.ok) {
|
|
444
896
|
uploaded.push(item);
|
|
@@ -449,12 +901,16 @@ async function uploadDir(dir, options = {}) {
|
|
|
449
901
|
detail = err.message;
|
|
450
902
|
}
|
|
451
903
|
if (status === 401 || status === 403) {
|
|
452
|
-
fatal =
|
|
904
|
+
fatal = rejected(status, detail);
|
|
453
905
|
return;
|
|
454
906
|
}
|
|
455
907
|
const retryable = status === 0 || status === 429 || status >= 500;
|
|
456
908
|
if (!retryable || attempt >= MAX_ATTEMPTS) {
|
|
457
|
-
|
|
909
|
+
let error = status ? `HTTP ${status}: ${detail}` : detail;
|
|
910
|
+
if (status === 413 && item.bytes > LEGACY_MAX_BYTES) {
|
|
911
|
+
error += ` (this map is ${(item.bytes / 1048576).toFixed(1)} MB; ingester 1.6.0 and older accept up to 5 MB, newer ones 32 MB)`;
|
|
912
|
+
}
|
|
913
|
+
summary.failed.push({ path: item.path, filename: item.filename, error });
|
|
458
914
|
return;
|
|
459
915
|
}
|
|
460
916
|
await sleep(500 * 2 ** (attempt - 1));
|
|
@@ -486,7 +942,253 @@ function strip(item) {
|
|
|
486
942
|
return { path, filename, debugId, hash, bytes };
|
|
487
943
|
}
|
|
488
944
|
|
|
945
|
+
// src/verify.ts
|
|
946
|
+
var import_node_fs2 = require("fs");
|
|
947
|
+
var import_promises4 = require("fs/promises");
|
|
948
|
+
var import_node_path2 = require("path");
|
|
949
|
+
var CHECK_BATCH2 = 500;
|
|
950
|
+
var SNIPPET_ID_RE = /\(g\._autterDebugIds=g\._autterDebugIds\|\|\{\}\)\[s\]="([0-9a-f-]{36})"/;
|
|
951
|
+
var NOT_BUNDLES = [
|
|
952
|
+
[/(^|\/)_(build|ssg|middleware|clientMiddleware)Manifest\.js$/, "Next.js build manifest"],
|
|
953
|
+
[/(^|\/)chunks\/polyfills-[\w-]+\.js$/, "Next.js polyfills, copied prebuilt without a source map"],
|
|
954
|
+
[/(^|\/)(sw|service-worker|serviceWorker|firebase-messaging-sw|mockServiceWorker)\.js$/, "service worker (the browser SDK doesn't run there)"],
|
|
955
|
+
[/(^|\/)workbox-[\w-]+\.js$/, "service worker runtime (the browser SDK doesn't run there)"]
|
|
956
|
+
];
|
|
957
|
+
function globToRegExp(glob) {
|
|
958
|
+
let re = "";
|
|
959
|
+
for (let i = 0; i < glob.length; i++) {
|
|
960
|
+
const ch = glob[i];
|
|
961
|
+
if (ch === "*" && glob[i + 1] === "*") {
|
|
962
|
+
re += glob[i + 2] === "/" ? "(?:.*/)?" : ".*";
|
|
963
|
+
i += glob[i + 2] === "/" ? 2 : 1;
|
|
964
|
+
} else if (ch === "*") re += "[^/]*";
|
|
965
|
+
else if (ch === "?") re += "[^/]";
|
|
966
|
+
else re += ch.replace(/[.+^${}()|[\]\\]/g, "\\$&");
|
|
967
|
+
}
|
|
968
|
+
return new RegExp(glob.includes("/") ? `^${re.replace(/^\.\//, "")}$` : `(^|/)${re}$`);
|
|
969
|
+
}
|
|
970
|
+
async function walk2(dir, out) {
|
|
971
|
+
let entries;
|
|
972
|
+
try {
|
|
973
|
+
entries = await (0, import_promises4.readdir)(dir, { withFileTypes: true });
|
|
974
|
+
} catch {
|
|
975
|
+
return;
|
|
976
|
+
}
|
|
977
|
+
for (const entry of entries) {
|
|
978
|
+
if (entry.name === "node_modules") continue;
|
|
979
|
+
const full = (0, import_node_path2.join)(dir, entry.name);
|
|
980
|
+
if (entry.isDirectory()) await walk2(full, out);
|
|
981
|
+
else if (entry.isFile()) out.push(full);
|
|
982
|
+
}
|
|
983
|
+
}
|
|
984
|
+
function staticRoots(dir) {
|
|
985
|
+
for (let current = (0, import_node_path2.dirname)(dir); (0, import_node_path2.dirname)(current) !== current; current = (0, import_node_path2.dirname)(current)) {
|
|
986
|
+
if ((0, import_node_fs2.existsSync)((0, import_node_path2.join)(current, "package.json"))) return ["public", "static", "src"].map((name) => (0, import_node_path2.join)(current, name));
|
|
987
|
+
}
|
|
988
|
+
return [];
|
|
989
|
+
}
|
|
990
|
+
async function verifyDir(dir, options = {}) {
|
|
991
|
+
const root = (0, import_node_path2.resolve)(dir);
|
|
992
|
+
const env = options.env ?? process.env;
|
|
993
|
+
const files = [];
|
|
994
|
+
await walk2(root, files);
|
|
995
|
+
files.sort();
|
|
996
|
+
const relPath = (path) => (0, import_node_path2.relative)(root, path).split(import_node_path2.sep).join("/");
|
|
997
|
+
const ignore = (options.ignore ?? []).map(globToRegExp);
|
|
998
|
+
const roots = staticRoots(root);
|
|
999
|
+
const result = {
|
|
1000
|
+
dir: root,
|
|
1001
|
+
ok: true,
|
|
1002
|
+
bundles: [],
|
|
1003
|
+
maps: [],
|
|
1004
|
+
upload: { status: "unverified", endpoint: null, reason: null, checked: 0, missing: 0 },
|
|
1005
|
+
failures: []
|
|
1006
|
+
};
|
|
1007
|
+
const fail = (path, reason) => result.failures.push({ path, reason });
|
|
1008
|
+
for (const file of files) {
|
|
1009
|
+
const path = relPath(file);
|
|
1010
|
+
if (file.endsWith(".map")) {
|
|
1011
|
+
result.maps.push(path);
|
|
1012
|
+
if (!options.allowMaps) {
|
|
1013
|
+
fail(path, "source map in the deploy output: it exposes your original source. Delete it after upload (upload --delete-after-upload, or the bundler plugin) or keep it out of the deploy");
|
|
1014
|
+
}
|
|
1015
|
+
continue;
|
|
1016
|
+
}
|
|
1017
|
+
if (!BUNDLE_RE.test(file)) continue;
|
|
1018
|
+
const bytes = await (0, import_promises4.readFile)(file);
|
|
1019
|
+
const bundle = { path, status: "ok", debugId: null, reasons: [], uploaded: null };
|
|
1020
|
+
result.bundles.push(bundle);
|
|
1021
|
+
const skip = (reason) => {
|
|
1022
|
+
bundle.status = "skipped";
|
|
1023
|
+
bundle.reasons.push(reason);
|
|
1024
|
+
};
|
|
1025
|
+
if (ignore.some((re) => re.test(path))) {
|
|
1026
|
+
skip("matches --ignore");
|
|
1027
|
+
continue;
|
|
1028
|
+
}
|
|
1029
|
+
const known = NOT_BUNDLES.find(([re]) => re.test(path));
|
|
1030
|
+
if (known) {
|
|
1031
|
+
skip(known[1]);
|
|
1032
|
+
continue;
|
|
1033
|
+
}
|
|
1034
|
+
let copied = null;
|
|
1035
|
+
for (const staticRoot of roots) {
|
|
1036
|
+
const candidate = (0, import_node_path2.join)(staticRoot, path);
|
|
1037
|
+
if ((0, import_node_fs2.existsSync)(candidate) && (await (0, import_promises4.readFile)(candidate)).equals(bytes)) copied = (0, import_node_path2.relative)((0, import_node_path2.dirname)(staticRoot), candidate).split(import_node_path2.sep).join("/");
|
|
1038
|
+
}
|
|
1039
|
+
if (copied) {
|
|
1040
|
+
skip(`copied unchanged from ${copied}, not built`);
|
|
1041
|
+
continue;
|
|
1042
|
+
}
|
|
1043
|
+
const code = bytes.toString("utf8");
|
|
1044
|
+
const commentId = readDebugIdComment(code);
|
|
1045
|
+
const snippetId = SNIPPET_ID_RE.exec(code)?.[1] ?? null;
|
|
1046
|
+
bundle.debugId = commentId;
|
|
1047
|
+
if (!commentId && !hasDebugIdSnippet(code)) {
|
|
1048
|
+
bundle.reasons.push(
|
|
1049
|
+
"no debug id: its errors can't be matched to a source map. Run `autter-runtime sourcemaps inject` on the build output (or use the bundler plugin with AUTTER_RUNTIME_KEY set) before deploying"
|
|
1050
|
+
);
|
|
1051
|
+
} else if (!commentId) {
|
|
1052
|
+
bundle.reasons.push("has the debug id snippet but no //# debugId= comment: run `autter-runtime sourcemaps inject` on it again");
|
|
1053
|
+
} else if (!hasDebugIdSnippet(code)) {
|
|
1054
|
+
bundle.reasons.push(
|
|
1055
|
+
"has a //# debugId= comment but no registration snippet, so the browser SDK can't report its id: run `autter-runtime sourcemaps inject` on it"
|
|
1056
|
+
);
|
|
1057
|
+
} else if (snippetId && snippetId !== commentId) {
|
|
1058
|
+
bundle.reasons.push(`its snippet registers debug id ${snippetId} but its comment says ${commentId}: run inject on the original build output`);
|
|
1059
|
+
}
|
|
1060
|
+
const url = readSourceMappingUrl(code);
|
|
1061
|
+
if (url?.startsWith("data:")) {
|
|
1062
|
+
bundle.reasons.push("carries an inline source map (data: URL): your original source ships with the bundle. Use hidden or external maps");
|
|
1063
|
+
} else if (url && !/^[a-z][\w+.-]*:/i.test(url) && !url.startsWith("/")) {
|
|
1064
|
+
let target = url.split(/[?#]/)[0];
|
|
1065
|
+
try {
|
|
1066
|
+
target = decodeURIComponent(target);
|
|
1067
|
+
} catch {
|
|
1068
|
+
}
|
|
1069
|
+
if (!(0, import_node_fs2.existsSync)((0, import_node_path2.resolve)((0, import_node_path2.dirname)(file), target))) {
|
|
1070
|
+
bundle.reasons.push(
|
|
1071
|
+
`its sourceMappingURL points at ${url}, which is not in the output (browsers' devtools request it and get a 404). Remove the comment: upload --delete-after-upload does`
|
|
1072
|
+
);
|
|
1073
|
+
}
|
|
1074
|
+
}
|
|
1075
|
+
if (bundle.reasons.length) {
|
|
1076
|
+
bundle.status = "failed";
|
|
1077
|
+
for (const reason of bundle.reasons) fail(path, reason);
|
|
1078
|
+
}
|
|
1079
|
+
}
|
|
1080
|
+
const counted = result.bundles.filter((b) => b.status !== "skipped");
|
|
1081
|
+
if (!counted.length) fail(".", `no .js/.mjs/.cjs bundles under ${dir}: is this the build output?`);
|
|
1082
|
+
await checkUploads(result, counted, options, env);
|
|
1083
|
+
result.ok = result.failures.length === 0;
|
|
1084
|
+
return result;
|
|
1085
|
+
}
|
|
1086
|
+
async function checkUploads(result, bundles, options, env) {
|
|
1087
|
+
const upload = result.upload;
|
|
1088
|
+
const key = (options.key ?? env.AUTTER_RUNTIME_KEY ?? "").trim();
|
|
1089
|
+
if (!key) {
|
|
1090
|
+
upload.reason = "AUTTER_RUNTIME_KEY is not set: only the local checks ran";
|
|
1091
|
+
return;
|
|
1092
|
+
}
|
|
1093
|
+
upload.endpoint = resolveEndpoint(options.endpoint, env);
|
|
1094
|
+
if (key.startsWith(CLIENT_KEY_PREFIX)) {
|
|
1095
|
+
upload.status = "error";
|
|
1096
|
+
upload.reason = "AUTTER_RUNTIME_KEY is a publishable client key (autter_rtc_\u2026), which can't check source maps: use an upload key (autter_rtu_\u2026) or the server key (autter_rt_\u2026)";
|
|
1097
|
+
result.failures.push({ path: ".", reason: upload.reason });
|
|
1098
|
+
return;
|
|
1099
|
+
}
|
|
1100
|
+
const withIds = bundles.filter((b) => b.debugId);
|
|
1101
|
+
if (!withIds.length) {
|
|
1102
|
+
upload.reason = "no bundle has a debug id to check";
|
|
1103
|
+
return;
|
|
1104
|
+
}
|
|
1105
|
+
let release = "verify";
|
|
1106
|
+
try {
|
|
1107
|
+
release = resolveRelease(options.release, env, options.cwd).release;
|
|
1108
|
+
} catch {
|
|
1109
|
+
}
|
|
1110
|
+
const doFetch = options.fetch ?? fetch;
|
|
1111
|
+
const missing = [];
|
|
1112
|
+
for (let start = 0; start < withIds.length; start += CHECK_BATCH2) {
|
|
1113
|
+
const batch = withIds.slice(start, start + CHECK_BATCH2);
|
|
1114
|
+
let res;
|
|
1115
|
+
try {
|
|
1116
|
+
res = await doFetch(`${upload.endpoint}/v1/sourcemaps/check`, {
|
|
1117
|
+
method: "POST",
|
|
1118
|
+
headers: { "content-type": "application/json", authorization: `Bearer ${key}` },
|
|
1119
|
+
body: JSON.stringify({ release, files: batch.map((b) => ({ filename: servedFilename(options.urlPrefix, b.path), debugId: b.debugId })) })
|
|
1120
|
+
});
|
|
1121
|
+
} catch (err) {
|
|
1122
|
+
upload.status = "error";
|
|
1123
|
+
upload.reason = `could not reach the ingester: ${err.message}`;
|
|
1124
|
+
result.failures.push({ path: ".", reason: upload.reason });
|
|
1125
|
+
return;
|
|
1126
|
+
}
|
|
1127
|
+
if (res.status === 404 || res.status === 400) {
|
|
1128
|
+
upload.status = "unverified";
|
|
1129
|
+
upload.reason = res.status === 404 ? "the ingester predates 1.6.0 (no /v1/sourcemaps/check), so uploads can't be checked: upgrade it" : "the ingester predates 1.7.0, which checks maps by debug id alone, so uploads can't be checked after the maps were deleted: upgrade it";
|
|
1130
|
+
return;
|
|
1131
|
+
}
|
|
1132
|
+
if (res.status === 401 || res.status === 403) {
|
|
1133
|
+
upload.status = "error";
|
|
1134
|
+
upload.reason = `the ingester rejected the key (${res.status}): ${(await res.text()).slice(0, 300)}` + (key.startsWith(UPLOAD_KEY_PREFIX) ? " (upload keys, autter_rtu_\u2026, need ingester 1.7.0 or later)" : "");
|
|
1135
|
+
result.failures.push({ path: ".", reason: upload.reason });
|
|
1136
|
+
return;
|
|
1137
|
+
}
|
|
1138
|
+
if (!res.ok) {
|
|
1139
|
+
upload.status = "error";
|
|
1140
|
+
upload.reason = `the source map check failed: HTTP ${res.status}`;
|
|
1141
|
+
result.failures.push({ path: ".", reason: upload.reason });
|
|
1142
|
+
return;
|
|
1143
|
+
}
|
|
1144
|
+
const body = await res.json();
|
|
1145
|
+
const present = new Set(Array.isArray(body.present) ? body.present : []);
|
|
1146
|
+
batch.forEach((bundle, index) => {
|
|
1147
|
+
bundle.uploaded = present.has(index);
|
|
1148
|
+
if (!bundle.uploaded) missing.push(bundle);
|
|
1149
|
+
});
|
|
1150
|
+
}
|
|
1151
|
+
upload.checked = withIds.length;
|
|
1152
|
+
upload.missing = missing.length;
|
|
1153
|
+
upload.status = missing.length ? "missing" : "verified";
|
|
1154
|
+
upload.reason = missing.length ? `${missing.length} of ${withIds.length} bundle(s) have no map on the ingester` : null;
|
|
1155
|
+
for (const bundle of missing) {
|
|
1156
|
+
const reason = `no map with debug id ${bundle.debugId} on the ingester for this key's repository: upload this build's maps (autter-runtime sourcemaps upload, or the bundler plugin with AUTTER_RUNTIME_KEY set)`;
|
|
1157
|
+
bundle.status = "failed";
|
|
1158
|
+
bundle.reasons.push(reason);
|
|
1159
|
+
result.failures.push({ path: bundle.path, reason });
|
|
1160
|
+
}
|
|
1161
|
+
}
|
|
1162
|
+
function formatVerify(result) {
|
|
1163
|
+
const lines = [];
|
|
1164
|
+
for (const bundle of result.bundles) {
|
|
1165
|
+
if (bundle.status === "ok") lines.push(`ok ${bundle.path} (debug id ${bundle.debugId}${bundle.uploaded ? ", map uploaded" : ""})`);
|
|
1166
|
+
else if (bundle.status === "skipped") lines.push(`skipped ${bundle.path}: ${bundle.reasons.join("; ")}`);
|
|
1167
|
+
else for (const reason of bundle.reasons) lines.push(`FAILED ${bundle.path}: ${reason}`);
|
|
1168
|
+
}
|
|
1169
|
+
for (const map of result.maps) {
|
|
1170
|
+
const failure = result.failures.find((f) => f.path === map);
|
|
1171
|
+
lines.push(failure ? `FAILED ${map}: ${failure.reason}` : `warning ${map}: source map in the output (allowed by --allow-maps; remove it before deploying)`);
|
|
1172
|
+
}
|
|
1173
|
+
for (const failure of result.failures.filter((f) => f.path === ".")) lines.push(`FAILED ${failure.reason}`);
|
|
1174
|
+
const u = result.upload;
|
|
1175
|
+
lines.push(
|
|
1176
|
+
u.status === "verified" ? `upload: verified, ${u.checked} bundle map(s) on the ingester (${u.endpoint})` : u.status === "missing" ? `upload: ${u.reason} (${u.endpoint})` : `upload: ${u.status}${u.reason ? `, ${u.reason}` : ""}`
|
|
1177
|
+
);
|
|
1178
|
+
const counted = result.bundles.filter((b) => b.status !== "skipped");
|
|
1179
|
+
lines.push(
|
|
1180
|
+
result.ok ? `ok: ${counted.length} bundle(s) with debug ids, ${result.maps.length ? `${result.maps.length} source map(s) left (--allow-maps)` : "no source maps left"}${u.status === "unverified" ? ", upload not verified" : ""}` : `${result.failures.length} problem(s) in ${result.dir}`
|
|
1181
|
+
);
|
|
1182
|
+
return lines;
|
|
1183
|
+
}
|
|
1184
|
+
|
|
489
1185
|
// src/build.ts
|
|
1186
|
+
var import_node_fs3 = require("fs");
|
|
1187
|
+
var import_node_path3 = require("path");
|
|
1188
|
+
function buildKey(options) {
|
|
1189
|
+
const key = (options.key ?? process.env.AUTTER_RUNTIME_KEY ?? "").trim();
|
|
1190
|
+
return key || null;
|
|
1191
|
+
}
|
|
490
1192
|
function formatUploadSummary(summary) {
|
|
491
1193
|
const lines = [];
|
|
492
1194
|
const verb = summary.dryRun ? "would upload" : "uploaded";
|
|
@@ -505,63 +1207,117 @@ async function processBuildOutput(input) {
|
|
|
505
1207
|
const log = (line) => {
|
|
506
1208
|
if (!options.silent) console.log(`[autter-runtime] ${line}`);
|
|
507
1209
|
};
|
|
508
|
-
const result = { injected: [], upload: null };
|
|
1210
|
+
const result = { injected: [], upload: null, removedMaps: [] };
|
|
1211
|
+
let outcome = "they were uploaded";
|
|
1212
|
+
const owners = /* @__PURE__ */ new Map();
|
|
1213
|
+
if (input.ownMaps) {
|
|
1214
|
+
for (const bundle of await findBundles(input.dir, input.files)) owners.set(bundle.map, bundle.js);
|
|
1215
|
+
}
|
|
509
1216
|
try {
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
if (
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
1217
|
+
try {
|
|
1218
|
+
result.injected = await injectDir(input.dir, {
|
|
1219
|
+
files: input.files,
|
|
1220
|
+
dryRun: options.dryRun,
|
|
1221
|
+
stripSourceMappingUrl: input.stripSourceMappingUrl
|
|
1222
|
+
});
|
|
1223
|
+
const changed = result.injected.filter((r) => r.status === "injected").length;
|
|
1224
|
+
if (result.injected.length) {
|
|
1225
|
+
log(`debug ids: ${changed} bundle(s) injected, ${result.injected.length - changed} already had one`);
|
|
1226
|
+
}
|
|
1227
|
+
for (const bad of result.injected.filter((r) => r.status === "invalid-map")) log(`skipped ${bad.path}: its map is not valid JSON`);
|
|
1228
|
+
if (options.upload === false) return result;
|
|
1229
|
+
const key = buildKey(options);
|
|
1230
|
+
if (!key && !options.dryRun) {
|
|
1231
|
+
outcome = "AUTTER_RUNTIME_KEY is not set";
|
|
1232
|
+
if (result.injected.length && !input.ownMaps) log("AUTTER_RUNTIME_KEY is not set: source maps were not uploaded");
|
|
1233
|
+
return result;
|
|
1234
|
+
}
|
|
1235
|
+
if (!result.injected.some((r) => r.status !== "invalid-map")) {
|
|
1236
|
+
outcome = "no bundle had a usable map to upload";
|
|
1237
|
+
return result;
|
|
1238
|
+
}
|
|
1239
|
+
result.upload = await uploadDir(input.dir, {
|
|
1240
|
+
files: input.files,
|
|
1241
|
+
release: options.release,
|
|
1242
|
+
urlPrefix: options.urlPrefix ?? input.urlPrefix,
|
|
1243
|
+
endpoint: options.endpoint,
|
|
1244
|
+
key: key ?? void 0,
|
|
1245
|
+
dryRun: options.dryRun,
|
|
1246
|
+
concurrency: options.concurrency,
|
|
1247
|
+
deleteAfterUpload: options.deleteAfterUpload ?? input.deleteAfterUpload,
|
|
1248
|
+
log
|
|
1249
|
+
});
|
|
1250
|
+
for (const line of formatUploadSummary(result.upload)) log(line);
|
|
1251
|
+
const { failed, uploaded, skipped, dryRun } = result.upload;
|
|
1252
|
+
if (dryRun) outcome = "dry run, nothing was uploaded";
|
|
1253
|
+
else if (failed.length) {
|
|
1254
|
+
outcome = `${failed.length} of ${failed.length + uploaded.length + skipped.length} could not be uploaded (${failed[0].error})`;
|
|
1255
|
+
}
|
|
1256
|
+
if (failed.length && options.failOnError) {
|
|
1257
|
+
throw new Error(`${failed.length} source map upload(s) failed`);
|
|
1258
|
+
}
|
|
1259
|
+
} catch (err) {
|
|
1260
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
1261
|
+
if (outcome === "they were uploaded") outcome = `the upload failed (${message})`;
|
|
1262
|
+
if (options.failOnError) throw new Error(`[autter-runtime] source maps: ${message}`);
|
|
1263
|
+
if (!options.silent) console.warn(`[autter-runtime] source maps: ${message}`);
|
|
541
1264
|
}
|
|
542
|
-
}
|
|
543
|
-
|
|
544
|
-
if (options.failOnError) throw new Error(`[autter-runtime] ${message}`);
|
|
545
|
-
if (!options.silent) console.warn(`[autter-runtime] ${message}`);
|
|
1265
|
+
} finally {
|
|
1266
|
+
await cleanUp();
|
|
546
1267
|
}
|
|
547
1268
|
return result;
|
|
1269
|
+
async function cleanUp() {
|
|
1270
|
+
if (!input.ownMaps) return;
|
|
1271
|
+
const maps = /* @__PURE__ */ new Set([...input.maps ?? [], ...owners.keys()]);
|
|
1272
|
+
const rel = (path) => (0, import_node_path3.relative)(input.dir, path).split(import_node_path3.sep).join("/");
|
|
1273
|
+
if (options.upload === false || options.deleteAfterUpload === false) {
|
|
1274
|
+
const kept = [...maps].filter((map) => (0, import_node_fs3.existsSync)(map));
|
|
1275
|
+
if (kept.length) {
|
|
1276
|
+
log(
|
|
1277
|
+
`kept ${kept.length} source map(s) this plugin generated (${options.upload === false ? "upload: false" : "deleteAfterUpload: false"}): they expose your source, so upload them with \`autter-runtime sourcemaps upload --delete-after-upload\` or keep them out of the deploy`
|
|
1278
|
+
);
|
|
1279
|
+
}
|
|
1280
|
+
return;
|
|
1281
|
+
}
|
|
1282
|
+
try {
|
|
1283
|
+
await removeSourceMaps(maps, owners);
|
|
1284
|
+
} catch (err) {
|
|
1285
|
+
const message = `could not delete the source maps this plugin generated (${err instanceof Error ? err.message : String(err)}): keep them out of the deploy`;
|
|
1286
|
+
if (options.failOnError) throw new Error(`[autter-runtime] ${message}`);
|
|
1287
|
+
if (!options.silent) console.warn(`[autter-runtime] ${message}`);
|
|
1288
|
+
}
|
|
1289
|
+
result.removedMaps = [...maps].filter((map) => !(0, import_node_fs3.existsSync)(map)).map(rel).sort();
|
|
1290
|
+
if (result.removedMaps.length) {
|
|
1291
|
+
log(`removed ${result.removedMaps.length} source map(s) this plugin generated, so they are not deployed: ${outcome}`);
|
|
1292
|
+
}
|
|
1293
|
+
}
|
|
548
1294
|
}
|
|
549
1295
|
// Annotate the CommonJS export names for ESM import in node:
|
|
550
1296
|
0 && (module.exports = {
|
|
551
1297
|
BUNDLE_RE,
|
|
1298
|
+
CLIENT_KEY_PREFIX,
|
|
552
1299
|
DEBUG_ID_RE,
|
|
553
1300
|
DEBUG_ID_REGISTRY,
|
|
554
1301
|
DEFAULT_ENDPOINT,
|
|
555
1302
|
FatalUploadError,
|
|
1303
|
+
KEY_ADVICE,
|
|
1304
|
+
MAX_MAP_CHARS,
|
|
556
1305
|
MAX_UPLOAD_BYTES,
|
|
557
1306
|
RELEASE_ENV_VARS,
|
|
558
1307
|
RELEASE_RE,
|
|
1308
|
+
SCOPES_KEY,
|
|
1309
|
+
SCOPES_VERSION,
|
|
1310
|
+
SERVER_KEY_PREFIX,
|
|
559
1311
|
SNIPPET_LINE_KEY,
|
|
1312
|
+
UPLOAD_KEY_PREFIX,
|
|
1313
|
+
computeScopes,
|
|
560
1314
|
debugIdFor,
|
|
561
1315
|
debugIdSnippet,
|
|
562
1316
|
findBundles,
|
|
563
1317
|
formatUploadSummary,
|
|
1318
|
+
formatVerify,
|
|
564
1319
|
gitHead,
|
|
1320
|
+
globToRegExp,
|
|
565
1321
|
hasDebugIdSnippet,
|
|
566
1322
|
injectBundle,
|
|
567
1323
|
injectDir,
|
|
@@ -577,5 +1333,6 @@ async function processBuildOutput(input) {
|
|
|
577
1333
|
snippetLine,
|
|
578
1334
|
storedMapJson,
|
|
579
1335
|
stripSourceMappingUrl,
|
|
580
|
-
uploadDir
|
|
1336
|
+
uploadDir,
|
|
1337
|
+
verifyDir
|
|
581
1338
|
});
|