@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/dist/webpack.cjs CHANGED
@@ -25,40 +25,11 @@ __export(webpack_exports, {
25
25
  default: () => webpack_default
26
26
  });
27
27
  module.exports = __toCommonJS(webpack_exports);
28
- var import_node_path2 = require("path");
29
-
30
- // src/inject.ts
31
- var import_promises2 = require("fs/promises");
28
+ var import_node_path3 = require("path");
32
29
 
33
- // src/debug-id.ts
34
- var import_node_crypto = require("crypto");
35
- var DEBUG_ID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/;
36
- function normalizeDebugId(value) {
37
- if (typeof value !== "string") return null;
38
- const id = value.trim().toLowerCase();
39
- return DEBUG_ID_RE.test(id) ? id : null;
40
- }
41
- function debugIdFor(bytes) {
42
- const hash = (0, import_node_crypto.createHash)("sha256").update(bytes).digest();
43
- hash[6] = hash[6] & 15 | 64;
44
- hash[8] = hash[8] & 63 | 128;
45
- const hex = hash.subarray(0, 16).toString("hex");
46
- return `${hex.slice(0, 8)}-${hex.slice(8, 12)}-${hex.slice(12, 16)}-${hex.slice(16, 20)}-${hex.slice(20, 32)}`;
47
- }
48
- var DEBUG_ID_COMMENT_RE = /^\/\/# debugId=([0-9a-fA-F-]{36})[ \t]*\r?$/gm;
49
- function readDebugIdComment(code) {
50
- let found = null;
51
- for (const match of code.matchAll(DEBUG_ID_COMMENT_RE)) found = normalizeDebugId(match[1]) ?? found;
52
- return found;
53
- }
54
- var DEBUG_ID_REGISTRY = "_autterDebugIds";
55
- function debugIdSnippet(id) {
56
- return `;!function(){try{var g="undefined"!=typeof globalThis?globalThis:"undefined"!=typeof window?window:"undefined"!=typeof self?self:{},s=(new g.Error).stack;s&&((g.${DEBUG_ID_REGISTRY}=g.${DEBUG_ID_REGISTRY}||{})[s]="${id}")}catch(e){}}();`;
57
- }
58
- var SNIPPET_RE = /\(g\._autterDebugIds=g\._autterDebugIds\|\|\{\}\)\[s\]="[0-9a-f]{8}-/;
59
- function hasDebugIdSnippet(code) {
60
- return SNIPPET_RE.test(code);
61
- }
30
+ // src/build.ts
31
+ var import_node_fs2 = require("fs");
32
+ var import_node_path2 = require("path");
62
33
 
63
34
  // src/files.ts
64
35
  var import_node_fs = require("fs");
@@ -98,7 +69,7 @@ function mapPathFor(js, code) {
98
69
  const sibling = `${js}.map`;
99
70
  return (0, import_node_fs.existsSync)(sibling) ? sibling : null;
100
71
  }
101
- async function walk(dir, out) {
72
+ async function walk(dir, out, pattern = BUNDLE_RE) {
102
73
  let entries;
103
74
  try {
104
75
  entries = await (0, import_promises.readdir)(dir, { withFileTypes: true });
@@ -108,9 +79,46 @@ async function walk(dir, out) {
108
79
  for (const entry of entries) {
109
80
  if (entry.name === "node_modules") continue;
110
81
  const full = (0, import_node_path.join)(dir, entry.name);
111
- if (entry.isDirectory()) await walk(full, out);
112
- else if (entry.isFile() && BUNDLE_RE.test(entry.name)) out.push(full);
82
+ if (entry.isDirectory()) await walk(full, out, pattern);
83
+ else if (entry.isFile() && pattern.test(entry.name)) out.push(full);
84
+ }
85
+ }
86
+ var CSS_SOURCE_MAPPING_URL_RE = /\/\*[#@] sourceMappingURL=([^\s*]+)\s*\*\/\s*$/;
87
+ function pointsAt(file, url, map) {
88
+ if (!url || /^[a-z][\w+.-]*:/i.test(url) || url.startsWith("/")) return false;
89
+ let path = url.split(/[?#]/)[0];
90
+ try {
91
+ path = decodeURIComponent(path);
92
+ } catch {
113
93
  }
94
+ return (0, import_node_path.resolve)((0, import_node_path.dirname)(file), path) === map;
95
+ }
96
+ async function stripReference(file, map) {
97
+ let code;
98
+ try {
99
+ code = await (0, import_promises.readFile)(file, "utf8");
100
+ } catch {
101
+ return;
102
+ }
103
+ let stripped = code;
104
+ if (BUNDLE_RE.test(file)) {
105
+ if (pointsAt(file, readSourceMappingUrl(code), map)) stripped = stripSourceMappingUrl(code);
106
+ } else if (file.endsWith(".css")) {
107
+ const match = CSS_SOURCE_MAPPING_URL_RE.exec(code);
108
+ if (match && pointsAt(file, match[1], map)) stripped = code.slice(0, match.index).replace(/\n?$/, "\n");
109
+ }
110
+ if (stripped !== code) await (0, import_promises.writeFile)(file, stripped);
111
+ }
112
+ async function removeSourceMaps(maps, owners = /* @__PURE__ */ new Map()) {
113
+ const removed = [];
114
+ for (const map of [...new Set(maps)].sort()) {
115
+ if (!(0, import_node_fs.existsSync)(map)) continue;
116
+ await (0, import_promises.rm)(map, { force: true });
117
+ removed.push(map);
118
+ const owner = owners.get(map) ?? map.replace(/\.map$/, "");
119
+ await stripReference(owner, map);
120
+ }
121
+ return removed;
114
122
  }
115
123
  async function findBundles(dir, files) {
116
124
  const root = (0, import_node_path.resolve)(dir);
@@ -134,6 +142,39 @@ async function findBundles(dir, files) {
134
142
  return bundles;
135
143
  }
136
144
 
145
+ // src/inject.ts
146
+ var import_promises2 = require("fs/promises");
147
+
148
+ // src/debug-id.ts
149
+ var import_node_crypto = require("crypto");
150
+ var DEBUG_ID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/;
151
+ function normalizeDebugId(value) {
152
+ if (typeof value !== "string") return null;
153
+ const id = value.trim().toLowerCase();
154
+ return DEBUG_ID_RE.test(id) ? id : null;
155
+ }
156
+ function debugIdFor(bytes) {
157
+ const hash = (0, import_node_crypto.createHash)("sha256").update(bytes).digest();
158
+ hash[6] = hash[6] & 15 | 64;
159
+ hash[8] = hash[8] & 63 | 128;
160
+ const hex = hash.subarray(0, 16).toString("hex");
161
+ return `${hex.slice(0, 8)}-${hex.slice(8, 12)}-${hex.slice(12, 16)}-${hex.slice(16, 20)}-${hex.slice(20, 32)}`;
162
+ }
163
+ var DEBUG_ID_COMMENT_RE = /^\/\/# debugId=([0-9a-fA-F-]{36})[ \t]*\r?$/gm;
164
+ function readDebugIdComment(code) {
165
+ let found = null;
166
+ for (const match of code.matchAll(DEBUG_ID_COMMENT_RE)) found = normalizeDebugId(match[1]) ?? found;
167
+ return found;
168
+ }
169
+ var DEBUG_ID_REGISTRY = "_autterDebugIds";
170
+ function debugIdSnippet(id) {
171
+ return `;!function(){try{var g="undefined"!=typeof globalThis?globalThis:"undefined"!=typeof window?window:"undefined"!=typeof self?self:{},s=(new g.Error).stack;s&&((g.${DEBUG_ID_REGISTRY}=g.${DEBUG_ID_REGISTRY}||{})[s]="${id}")}catch(e){}}();`;
172
+ }
173
+ var SNIPPET_RE = /\(g\._autterDebugIds=g\._autterDebugIds\|\|\{\}\)\[s\]="[0-9a-f]{8}-/;
174
+ function hasDebugIdSnippet(code) {
175
+ return SNIPPET_RE.test(code);
176
+ }
177
+
137
178
  // src/inject.ts
138
179
  var SNIPPET_LINE_KEY = "x_autter_snippet_line";
139
180
  function snippetLine(code) {
@@ -236,6 +277,8 @@ async function injectDir(dir, options = {}) {
236
277
  // src/upload.ts
237
278
  var import_node_crypto2 = require("crypto");
238
279
  var import_promises3 = require("fs/promises");
280
+ var import_node_util = require("util");
281
+ var import_node_zlib = require("zlib");
239
282
 
240
283
  // src/release.ts
241
284
  var import_node_child_process = require("child_process");
@@ -281,9 +324,385 @@ function resolveRelease(explicit, env = process.env, cwd = process.cwd()) {
281
324
  );
282
325
  }
283
326
 
327
+ // src/scopes.ts
328
+ var import_acorn = require("acorn");
329
+ var SCOPES_KEY = "x_autter_scopes";
330
+ var SCOPES_VERSION = 1;
331
+ var BASE_OPTIONS = {
332
+ ecmaVersion: "latest",
333
+ allowHashBang: true,
334
+ allowReturnOutsideFunction: true,
335
+ allowReserved: true
336
+ };
337
+ function parseBundle(code) {
338
+ try {
339
+ return (0, import_acorn.parse)(code, {
340
+ ...BASE_OPTIONS,
341
+ sourceType: "module",
342
+ allowAwaitOutsideFunction: true,
343
+ allowImportExportEverywhere: true
344
+ });
345
+ } catch (moduleError) {
346
+ try {
347
+ return (0, import_acorn.parse)(code, { ...BASE_OPTIONS, sourceType: "script" });
348
+ } catch (scriptError) {
349
+ const pos = (err) => typeof err.pos === "number" ? err.pos : -1;
350
+ throw pos(scriptError) > pos(moduleError) ? scriptError : moduleError;
351
+ }
352
+ }
353
+ }
354
+ function isNode(value) {
355
+ return typeof value === "object" && value !== null && typeof value.type === "string";
356
+ }
357
+ var IDENTIFIER_NAME_RE = /^[$_\p{ID_Start}][$\u200c\u200d\p{ID_Continue}]*$/u;
358
+ function identifierToken(node) {
359
+ if (!isNode(node)) return null;
360
+ if (node.type === "Identifier") return { offset: node.start, name: node.name, literal: false };
361
+ if (node.type === "PrivateIdentifier") return { offset: node.start, name: `#${node.name}`, literal: false };
362
+ return null;
363
+ }
364
+ function literalToken(node) {
365
+ if (!isNode(node) || node.type !== "Literal" || typeof node.value !== "string") return null;
366
+ return IDENTIFIER_NAME_RE.test(node.value) ? { offset: node.start, name: node.value, literal: true } : null;
367
+ }
368
+ function keyToken(property) {
369
+ if (!property.computed) {
370
+ const id = identifierToken(property.key);
371
+ if (id) return id;
372
+ }
373
+ return literalToken(property.key);
374
+ }
375
+ function bindingToken(target) {
376
+ if (!isNode(target)) return null;
377
+ if (target.type === "Identifier") return identifierToken(target);
378
+ if (target.type === "MemberExpression") {
379
+ return target.computed ? literalToken(target.property) : identifierToken(target.property);
380
+ }
381
+ return null;
382
+ }
383
+ function contextTokens(node, parent) {
384
+ let token = null;
385
+ switch (parent?.type) {
386
+ case "VariableDeclarator":
387
+ if (parent.init === node) token = bindingToken(parent.id);
388
+ break;
389
+ case "AssignmentExpression":
390
+ case "AssignmentPattern":
391
+ if (parent.right === node) token = bindingToken(parent.left);
392
+ break;
393
+ case "Property":
394
+ case "PropertyDefinition":
395
+ case "MethodDefinition":
396
+ if (parent.value === node) token = keyToken(parent);
397
+ break;
398
+ }
399
+ return token ? [token] : [];
400
+ }
401
+ function classTokens(path) {
402
+ if (!path || path.node.type !== "ClassDeclaration" && path.node.type !== "ClassExpression") return [];
403
+ const own = identifierToken(path.node.id);
404
+ return [...own ? [own] : [], ...contextTokens(path.node, path.parent?.node)];
405
+ }
406
+ function describe(path) {
407
+ const fn = path.node;
408
+ const parent = path.parent?.node;
409
+ const own = identifierToken(fn.id);
410
+ const isMethod = parent?.type === "MethodDefinition" || parent?.type === "Property" && parent.value === fn && (parent.method === true || parent.kind !== "init");
411
+ let context;
412
+ if (parent?.type === "MethodDefinition" && parent.kind === "constructor") {
413
+ context = classTokens(path.parent?.parent?.parent ?? null);
414
+ } else {
415
+ context = contextTokens(fn, parent);
416
+ }
417
+ return {
418
+ start: fn.start,
419
+ end: fn.end,
420
+ own,
421
+ recoverAt: !own && fn.type !== "ArrowFunctionExpression" && !isMethod ? fn.start : null,
422
+ context
423
+ };
424
+ }
425
+ var FUNCTION_TYPES = /* @__PURE__ */ new Set(["FunctionDeclaration", "FunctionExpression", "ArrowFunctionExpression"]);
426
+ function classInitializer(path) {
427
+ const node = path.node;
428
+ let range = null;
429
+ if (node.type === "StaticBlock") range = node;
430
+ else if (node.type === "PropertyDefinition" && isNode(node.value) && !FUNCTION_TYPES.has(node.value.type)) range = node.value;
431
+ if (!range) return null;
432
+ return { start: range.start, end: range.end, own: null, recoverAt: null, context: classTokens(path.parent?.parent ?? null) };
433
+ }
434
+ function collectFunctions(program) {
435
+ const found = [];
436
+ const stack = [{ node: program, parent: null }];
437
+ for (let path = stack.pop(); path; path = stack.pop()) {
438
+ const node = path.node;
439
+ if (FUNCTION_TYPES.has(node.type)) found.push(describe(path));
440
+ else {
441
+ const initializer = classInitializer(path);
442
+ if (initializer) found.push(initializer);
443
+ }
444
+ for (const key in node) {
445
+ const value = node[key];
446
+ if (Array.isArray(value)) {
447
+ for (let i = value.length - 1; i >= 0; i--) if (isNode(value[i])) stack.push({ node: value[i], parent: path });
448
+ } else if (isNode(value)) {
449
+ stack.push({ node: value, parent: path });
450
+ }
451
+ }
452
+ }
453
+ return found;
454
+ }
455
+ function lineStarts(code) {
456
+ const starts = [0];
457
+ for (let i = 0; i < code.length; i++) {
458
+ const c = code.charCodeAt(i);
459
+ if (c === 10 || c === 8232 || c === 8233) starts.push(i + 1);
460
+ else if (c === 13) {
461
+ if (code.charCodeAt(i + 1) === 10) i++;
462
+ starts.push(i + 1);
463
+ }
464
+ }
465
+ return starts;
466
+ }
467
+ function positionOf(starts, offset) {
468
+ let lo = 0;
469
+ let hi = starts.length - 1;
470
+ while (lo < hi) {
471
+ const mid = lo + hi + 1 >> 1;
472
+ if (starts[mid] <= offset) lo = mid;
473
+ else hi = mid - 1;
474
+ }
475
+ return [lo, offset - starts[lo]];
476
+ }
477
+ var BASE64 = new Int8Array(128).fill(-1);
478
+ for (const [i, ch] of [..."ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"].entries()) BASE64[ch.charCodeAt(0)] = i;
479
+ function findSegments(mappings, wanted) {
480
+ let line = 0;
481
+ let column = 0;
482
+ let source = 0;
483
+ let originalLine = 0;
484
+ let originalColumn = 0;
485
+ let name = 0;
486
+ let want = wanted.get(0);
487
+ const fields = [0, 0, 0, 0, 0];
488
+ let i = 0;
489
+ while (i < mappings.length) {
490
+ const ch = mappings.charCodeAt(i);
491
+ if (ch === 59) {
492
+ line++;
493
+ column = 0;
494
+ want = wanted.get(line);
495
+ i++;
496
+ continue;
497
+ }
498
+ if (ch === 44) {
499
+ i++;
500
+ continue;
501
+ }
502
+ let count = 0;
503
+ while (i < mappings.length) {
504
+ const next = mappings.charCodeAt(i);
505
+ if (next === 44 || next === 59) break;
506
+ let value = 0;
507
+ let shift = 0;
508
+ let digit;
509
+ do {
510
+ const code = mappings.charCodeAt(i++);
511
+ digit = code < 128 ? BASE64[code] : -1;
512
+ if (digit < 0 || shift > 30) throw new Error("invalid mappings");
513
+ value |= (digit & 31) << shift;
514
+ shift += 5;
515
+ } while (digit & 32);
516
+ const decoded = value & 1 ? -(value >>> 1) : value >>> 1;
517
+ if (count < 5) fields[count] = decoded;
518
+ count++;
519
+ }
520
+ column += fields[0];
521
+ if (count >= 4) {
522
+ source += fields[1];
523
+ originalLine += fields[2];
524
+ originalColumn += fields[3];
525
+ }
526
+ if (count >= 5) name += fields[4];
527
+ if (want && want.get(column) === null) {
528
+ want.set(column, {
529
+ source: count >= 4 ? source : -1,
530
+ line: originalLine,
531
+ column: originalColumn,
532
+ name: count >= 5 ? name : -1
533
+ });
534
+ }
535
+ }
536
+ }
537
+ function looksMangled(name) {
538
+ return name.replace(/^#/, "").length <= 2;
539
+ }
540
+ var IDENTIFIER_AT_RE = /[$_\p{ID_Start}][$\u200c\u200d\p{ID_Continue}]*/uy;
541
+ var OWN_NAME_AT_RE = /(?:async\s+)?function\b\s*\*?\s*([$_\p{ID_Start}][$\u200c\u200d\p{ID_Continue}]*)/uy;
542
+ var NOT_A_NAME = new Set(
543
+ "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(" ")
544
+ );
545
+ var NameResolver = class {
546
+ constructor(map, segmentAt) {
547
+ this.segmentAt = segmentAt;
548
+ this.names = Array.isArray(map.names) ? map.names : [];
549
+ this.sourcesContent = Array.isArray(map.sourcesContent) ? map.sourcesContent : [];
550
+ }
551
+ segmentAt;
552
+ names;
553
+ sourcesContent;
554
+ sourceLines = /* @__PURE__ */ new Map();
555
+ originalLine(segment) {
556
+ if (segment.source < 0) return null;
557
+ let lines = this.sourceLines.get(segment.source);
558
+ if (lines === void 0) {
559
+ const content = this.sourcesContent[segment.source];
560
+ lines = typeof content === "string" ? content.split(/\r\n|[\n\r\u2028\u2029]/) : null;
561
+ this.sourceLines.set(segment.source, lines);
562
+ }
563
+ return lines?.[segment.line] ?? null;
564
+ }
565
+ /** The original identifier at a segment's source position. */
566
+ identifierAt(segment, re, group = 0) {
567
+ const text = this.originalLine(segment);
568
+ if (text === null) return null;
569
+ re.lastIndex = segment.column;
570
+ const found = re.exec(text)?.[group];
571
+ return found && !NOT_A_NAME.has(found) ? found : null;
572
+ }
573
+ /** Original name of a token: the map's `names` at its exact position;
574
+ * else the generated name when it isn't mangled; else the identifier
575
+ * at the mapped position in `sourcesContent`. Null when unknown. */
576
+ resolve(token) {
577
+ if (token.literal) return token.name;
578
+ const segment = this.segmentAt(token.offset);
579
+ if (segment && segment.name >= 0) {
580
+ const name = this.names[segment.name];
581
+ if (typeof name === "string" && name) return name;
582
+ }
583
+ if (!looksMangled(token.name)) return token.name;
584
+ return segment ? this.identifierAt(segment, IDENTIFIER_AT_RE) : null;
585
+ }
586
+ /** The name a minifier removed from `function name() {}`, read from the
587
+ * original source at the function's mapped start. */
588
+ recover(offset) {
589
+ const segment = this.segmentAt(offset);
590
+ return segment ? this.identifierAt(segment, OWN_NAME_AT_RE, 1) : null;
591
+ }
592
+ };
593
+ var BASE64_DIGITS = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
594
+ function vlq(value) {
595
+ let rest = value < 0 ? -value << 1 | 1 : value << 1;
596
+ let out = "";
597
+ do {
598
+ let digit = rest & 31;
599
+ rest >>>= 5;
600
+ if (rest > 0) digit |= 32;
601
+ out += BASE64_DIGITS[digit];
602
+ } while (rest > 0);
603
+ return out;
604
+ }
605
+ function encodeScopes(entries) {
606
+ const parts = [];
607
+ let line = 0;
608
+ let previousStart = 0;
609
+ let previousName = 0;
610
+ let firstOnLine = true;
611
+ for (const [startLine, startColumn, endLine, endColumn, nameIndex] of entries) {
612
+ if (startLine > line) {
613
+ parts.push(";".repeat(startLine - line));
614
+ line = startLine;
615
+ previousStart = 0;
616
+ firstOnLine = true;
617
+ }
618
+ if (!firstOnLine) parts.push(",");
619
+ firstOnLine = false;
620
+ let segment = vlq(startColumn - previousStart) + vlq(endLine - startLine) + vlq(endLine === startLine ? endColumn - startColumn : endColumn);
621
+ previousStart = startColumn;
622
+ if (nameIndex >= 0) {
623
+ segment += vlq(nameIndex - previousName);
624
+ previousName = nameIndex;
625
+ }
626
+ parts.push(segment);
627
+ }
628
+ return parts.join("");
629
+ }
630
+ function computeScopes(code, map) {
631
+ try {
632
+ return scopesOf(code, map);
633
+ } catch (err) {
634
+ return { error: err instanceof Error ? err.message : String(err) };
635
+ }
636
+ }
637
+ function scopesOf(code, map) {
638
+ if (typeof map.mappings !== "string") return { error: "the map has no mappings string" };
639
+ let functions;
640
+ try {
641
+ functions = collectFunctions(parseBundle(code));
642
+ } catch (err) {
643
+ return { error: `could not parse the bundle: ${err instanceof Error ? err.message : String(err)}` };
644
+ }
645
+ const starts = lineStarts(code);
646
+ const wanted = /* @__PURE__ */ new Map();
647
+ const want = (offset) => {
648
+ const [line, column] = positionOf(starts, offset);
649
+ let columns = wanted.get(line);
650
+ if (!columns) wanted.set(line, columns = /* @__PURE__ */ new Map());
651
+ columns.set(column, null);
652
+ };
653
+ for (const fn of functions) {
654
+ if (fn.own && !fn.own.literal) want(fn.own.offset);
655
+ if (fn.recoverAt !== null) want(fn.recoverAt);
656
+ for (const token of fn.context) if (!token.literal) want(token.offset);
657
+ }
658
+ try {
659
+ findSegments(map.mappings, wanted);
660
+ } catch (err) {
661
+ return { error: `could not read the map: ${err instanceof Error ? err.message : String(err)}` };
662
+ }
663
+ const resolver = new NameResolver(map, (offset) => {
664
+ const [line, column] = positionOf(starts, offset);
665
+ return wanted.get(line)?.get(column) ?? null;
666
+ });
667
+ functions.sort((a, b) => a.start - b.start || b.end - a.end);
668
+ const mapNames = Array.isArray(map.names) ? map.names : [];
669
+ const nameIndex = /* @__PURE__ */ new Map();
670
+ mapNames.forEach((name, index) => {
671
+ if (typeof name === "string" && !nameIndex.has(name)) nameIndex.set(name, index);
672
+ });
673
+ const extraNames = [];
674
+ const indexOf = (name) => {
675
+ let index = nameIndex.get(name);
676
+ if (index === void 0) {
677
+ index = mapNames.length + extraNames.push(name) - 1;
678
+ nameIndex.set(name, index);
679
+ }
680
+ return index;
681
+ };
682
+ const entries = functions.map((fn) => {
683
+ let name = fn.own ? resolver.resolve(fn.own) : null;
684
+ if (name === null && fn.recoverAt !== null) name = resolver.recover(fn.recoverAt);
685
+ for (const token of fn.context) {
686
+ if (name !== null) break;
687
+ name = resolver.resolve(token);
688
+ }
689
+ const index = name !== null ? indexOf(name) : fn.own || fn.context.length > 0 ? indexOf(null) : -1;
690
+ const [startLine, startColumn] = positionOf(starts, fn.start);
691
+ const [endLine, endColumn] = positionOf(starts, fn.end);
692
+ return [startLine, startColumn, endLine, endColumn, index];
693
+ });
694
+ return { scopes: { version: SCOPES_VERSION, names: extraNames, scopes: encodeScopes(entries) } };
695
+ }
696
+
284
697
  // src/upload.ts
285
698
  var DEFAULT_ENDPOINT = "https://otlp.autter.dev";
286
- var MAX_UPLOAD_BYTES = 5 * 1024 * 1024;
699
+ var MAX_MAP_CHARS = 32 * 1024 * 1024;
700
+ var MAX_UPLOAD_BYTES = MAX_MAP_CHARS + 1024 * 1024;
701
+ var LEGACY_MAX_BYTES = 5 * 1024 * 1024;
702
+ var UPLOAD_KEY_PREFIX = "autter_rtu_";
703
+ var CLIENT_KEY_PREFIX = "autter_rtc_";
704
+ 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)";
705
+ var gzip = (0, import_node_util.promisify)(import_node_zlib.gzip);
287
706
  var CHECK_BATCH = 500;
288
707
  var MAX_ATTEMPTS = 3;
289
708
  function resolveEndpoint(flag, env = process.env) {
@@ -300,7 +719,16 @@ function storedMapJson(map) {
300
719
  const { sourcesContent: _omitted, ...safe } = map;
301
720
  return JSON.stringify(safe);
302
721
  }
303
- async function prepare(bundle, urlPrefix, release) {
722
+ function bytesOf(buffer) {
723
+ return new Uint8Array(buffer.buffer, buffer.byteOffset, buffer.byteLength);
724
+ }
725
+ function fits(json, body) {
726
+ return json.length <= MAX_MAP_CHARS && Buffer.byteLength(body) <= MAX_UPLOAD_BYTES;
727
+ }
728
+ function uploadBody(release, filename, debugId, json) {
729
+ return `{"release":${JSON.stringify(release)},"filename":${JSON.stringify(filename)},${debugId ? `"debugId":${JSON.stringify(debugId)},` : ""}"map":${json}}`;
730
+ }
731
+ async function prepare(bundle, urlPrefix, release, log, compress) {
304
732
  let map;
305
733
  try {
306
734
  map = JSON.parse(await (0, import_promises3.readFile)(bundle.map, "utf8"));
@@ -312,17 +740,31 @@ async function prepare(bundle, urlPrefix, release) {
312
740
  }
313
741
  const code = await (0, import_promises3.readFile)(bundle.js, "utf8");
314
742
  const debugId = readDebugIdComment(code) ?? normalizeDebugId(map.debugId);
315
- const json = storedMapJson(map);
316
743
  const filename = servedFilename(urlPrefix, bundle.path);
317
- const body = `{"release":${JSON.stringify(release)},"filename":${JSON.stringify(filename)},${debugId ? `"debugId":${JSON.stringify(debugId)},` : ""}"map":${json}}`;
744
+ const { [SCOPES_KEY]: _stale, ...plain } = map;
745
+ const scopes = computeScopes(code, map);
746
+ let json = storedMapJson(plain);
747
+ if ("scopes" in scopes) {
748
+ const scoped = storedMapJson({ ...plain, [SCOPES_KEY]: scopes.scopes });
749
+ if (fits(scoped, uploadBody(release, filename, debugId, scoped))) json = scoped;
750
+ else if (fits(json, uploadBody(release, filename, debugId, json))) {
751
+ log(`warning: ${bundle.path}: its map is over 32 MB with function scopes, so it is uploaded without them (frames keep call-site names)`);
752
+ }
753
+ } else {
754
+ log(`warning: ${bundle.path}: no function scopes (${scopes.error}); its map is uploaded without them (frames keep call-site names)`);
755
+ }
756
+ const body = uploadBody(release, filename, debugId, json);
318
757
  return {
319
758
  bundle,
320
- body,
759
+ // Maps are text that compresses several times over; the ingester
760
+ // inflates the body (Content-Encoding: gzip).
761
+ body: compress && fits(json, body) ? bytesOf(await gzip(body)) : new Uint8Array(0),
321
762
  path: bundle.path,
322
763
  filename,
323
764
  debugId,
324
765
  hash: (0, import_node_crypto2.createHash)("sha256").update(json).digest("hex"),
325
- bytes: Buffer.byteLength(body)
766
+ bytes: Buffer.byteLength(body),
767
+ mapChars: json.length
326
768
  };
327
769
  }
328
770
  var FatalUploadError = class extends Error {
@@ -350,14 +792,14 @@ async function uploadDir(dir, options = {}) {
350
792
  };
351
793
  const items = [];
352
794
  for (const bundle of await findBundles(dir, options.files)) {
353
- const prepared = await prepare(bundle, options.urlPrefix, release);
795
+ const prepared = await prepare(bundle, options.urlPrefix, release, log, !options.dryRun);
354
796
  if ("error" in prepared) {
355
797
  summary.failed.push({ path: bundle.path, filename: servedFilename(options.urlPrefix, bundle.path), error: prepared.error });
356
- } else if (prepared.bytes > MAX_UPLOAD_BYTES) {
798
+ } else if (prepared.mapChars > MAX_MAP_CHARS || prepared.bytes > MAX_UPLOAD_BYTES) {
357
799
  summary.failed.push({
358
800
  path: prepared.path,
359
801
  filename: prepared.filename,
360
- error: `map is ${(prepared.bytes / 1048576).toFixed(1)} MB without sourcesContent; the ingester accepts up to 5 MB`
802
+ error: `map is ${(prepared.mapChars / 1048576).toFixed(1)} MB without sourcesContent; the ingester accepts up to 32 MB`
361
803
  });
362
804
  } else {
363
805
  items.push(prepared);
@@ -368,11 +810,15 @@ async function uploadDir(dir, options = {}) {
368
810
  return summary;
369
811
  }
370
812
  const key = (options.key ?? env.AUTTER_RUNTIME_KEY ?? "").trim();
371
- if (!key) throw new FatalUploadError("no ingest key: set AUTTER_RUNTIME_KEY to a server key (autter_rt_\u2026)");
372
- if (key.startsWith("autter_rtc_")) {
373
- throw new FatalUploadError("AUTTER_RUNTIME_KEY is a publishable client key (autter_rtc_\u2026); uploads need a server key (autter_rt_\u2026)");
813
+ if (!key) throw new FatalUploadError(`no ingest key: ${KEY_ADVICE}`);
814
+ if (key.startsWith(CLIENT_KEY_PREFIX)) {
815
+ throw new FatalUploadError(`AUTTER_RUNTIME_KEY is a publishable client key (autter_rtc_\u2026), which cannot upload source maps: ${KEY_ADVICE}`);
374
816
  }
817
+ const rejected = (status, detail) => new FatalUploadError(
818
+ `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)" : "")
819
+ );
375
820
  const headers = { "content-type": "application/json", authorization: `Bearer ${key}` };
821
+ const uploadHeaders = { ...headers, "content-encoding": "gzip" };
376
822
  const skipped = /* @__PURE__ */ new Set();
377
823
  for (let start = 0; start < items.length && !summary.legacyIngester; start += CHECK_BATCH) {
378
824
  const batch = items.slice(start, start + CHECK_BATCH);
@@ -392,9 +838,7 @@ async function uploadDir(dir, options = {}) {
392
838
  );
393
839
  break;
394
840
  }
395
- if (res.status === 401 || res.status === 403) {
396
- throw new FatalUploadError(`the ingester rejected the key (${res.status}): ${(await res.text()).slice(0, 300)}`);
397
- }
841
+ if (res.status === 401 || res.status === 403) throw rejected(res.status, (await res.text()).slice(0, 300));
398
842
  if (!res.ok) throw new Error(`HTTP ${res.status}`);
399
843
  const body = await res.json();
400
844
  const present = new Set(Array.isArray(body.present) ? body.present : []);
@@ -414,7 +858,7 @@ async function uploadDir(dir, options = {}) {
414
858
  let status = 0;
415
859
  let detail = "";
416
860
  try {
417
- const res = await doFetch(`${endpoint}/v1/sourcemaps`, { method: "POST", headers, body: item.body });
861
+ const res = await doFetch(`${endpoint}/v1/sourcemaps`, { method: "POST", headers: uploadHeaders, body: item.body });
418
862
  status = res.status;
419
863
  if (res.ok) {
420
864
  uploaded.push(item);
@@ -425,12 +869,16 @@ async function uploadDir(dir, options = {}) {
425
869
  detail = err.message;
426
870
  }
427
871
  if (status === 401 || status === 403) {
428
- fatal = new FatalUploadError(`the ingester rejected the key (${status}): ${detail}`);
872
+ fatal = rejected(status, detail);
429
873
  return;
430
874
  }
431
875
  const retryable = status === 0 || status === 429 || status >= 500;
432
876
  if (!retryable || attempt >= MAX_ATTEMPTS) {
433
- summary.failed.push({ path: item.path, filename: item.filename, error: status ? `HTTP ${status}: ${detail}` : detail });
877
+ let error = status ? `HTTP ${status}: ${detail}` : detail;
878
+ if (status === 413 && item.bytes > LEGACY_MAX_BYTES) {
879
+ 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)`;
880
+ }
881
+ summary.failed.push({ path: item.path, filename: item.filename, error });
434
882
  return;
435
883
  }
436
884
  await sleep(500 * 2 ** (attempt - 1));
@@ -463,6 +911,18 @@ function strip(item) {
463
911
  }
464
912
 
465
913
  // src/build.ts
914
+ function buildKey(options) {
915
+ const key = (options.key ?? process.env.AUTTER_RUNTIME_KEY ?? "").trim();
916
+ return key || null;
917
+ }
918
+ function shouldGenerateMaps(options) {
919
+ return options.upload === false || Boolean(options.dryRun) || buildKey(options) !== null;
920
+ }
921
+ function logMapsNotGenerated(options) {
922
+ if (!options.silent) {
923
+ console.log("[autter-runtime] AUTTER_RUNTIME_KEY is not set: no source maps generated, nothing uploaded");
924
+ }
925
+ }
466
926
  function formatUploadSummary(summary) {
467
927
  const lines = [];
468
928
  const verb = summary.dryRun ? "would upload" : "uploaded";
@@ -481,70 +941,130 @@ async function processBuildOutput(input) {
481
941
  const log = (line) => {
482
942
  if (!options.silent) console.log(`[autter-runtime] ${line}`);
483
943
  };
484
- const result = { injected: [], upload: null };
944
+ const result = { injected: [], upload: null, removedMaps: [] };
945
+ let outcome = "they were uploaded";
946
+ const owners = /* @__PURE__ */ new Map();
947
+ if (input.ownMaps) {
948
+ for (const bundle of await findBundles(input.dir, input.files)) owners.set(bundle.map, bundle.js);
949
+ }
485
950
  try {
486
- result.injected = await injectDir(input.dir, {
487
- files: input.files,
488
- dryRun: options.dryRun,
489
- stripSourceMappingUrl: input.stripSourceMappingUrl
490
- });
491
- const changed = result.injected.filter((r) => r.status === "injected").length;
492
- if (result.injected.length) {
493
- log(`debug ids: ${changed} bundle(s) injected, ${result.injected.length - changed} already had one`);
951
+ try {
952
+ result.injected = await injectDir(input.dir, {
953
+ files: input.files,
954
+ dryRun: options.dryRun,
955
+ stripSourceMappingUrl: input.stripSourceMappingUrl
956
+ });
957
+ const changed = result.injected.filter((r) => r.status === "injected").length;
958
+ if (result.injected.length) {
959
+ log(`debug ids: ${changed} bundle(s) injected, ${result.injected.length - changed} already had one`);
960
+ }
961
+ for (const bad of result.injected.filter((r) => r.status === "invalid-map")) log(`skipped ${bad.path}: its map is not valid JSON`);
962
+ if (options.upload === false) return result;
963
+ const key = buildKey(options);
964
+ if (!key && !options.dryRun) {
965
+ outcome = "AUTTER_RUNTIME_KEY is not set";
966
+ if (result.injected.length && !input.ownMaps) log("AUTTER_RUNTIME_KEY is not set: source maps were not uploaded");
967
+ return result;
968
+ }
969
+ if (!result.injected.some((r) => r.status !== "invalid-map")) {
970
+ outcome = "no bundle had a usable map to upload";
971
+ return result;
972
+ }
973
+ result.upload = await uploadDir(input.dir, {
974
+ files: input.files,
975
+ release: options.release,
976
+ urlPrefix: options.urlPrefix ?? input.urlPrefix,
977
+ endpoint: options.endpoint,
978
+ key: key ?? void 0,
979
+ dryRun: options.dryRun,
980
+ concurrency: options.concurrency,
981
+ deleteAfterUpload: options.deleteAfterUpload ?? input.deleteAfterUpload,
982
+ log
983
+ });
984
+ for (const line of formatUploadSummary(result.upload)) log(line);
985
+ const { failed, uploaded, skipped, dryRun } = result.upload;
986
+ if (dryRun) outcome = "dry run, nothing was uploaded";
987
+ else if (failed.length) {
988
+ outcome = `${failed.length} of ${failed.length + uploaded.length + skipped.length} could not be uploaded (${failed[0].error})`;
989
+ }
990
+ if (failed.length && options.failOnError) {
991
+ throw new Error(`${failed.length} source map upload(s) failed`);
992
+ }
993
+ } catch (err) {
994
+ const message = err instanceof Error ? err.message : String(err);
995
+ if (outcome === "they were uploaded") outcome = `the upload failed (${message})`;
996
+ if (options.failOnError) throw new Error(`[autter-runtime] source maps: ${message}`);
997
+ if (!options.silent) console.warn(`[autter-runtime] source maps: ${message}`);
494
998
  }
495
- for (const bad of result.injected.filter((r) => r.status === "invalid-map")) log(`skipped ${bad.path}: its map is not valid JSON`);
496
- if (options.upload === false) return result;
497
- const key = options.key ?? process.env.AUTTER_RUNTIME_KEY;
498
- if (!key && !options.dryRun) {
499
- if (result.injected.length) log("AUTTER_RUNTIME_KEY is not set: source maps were not uploaded");
500
- return result;
999
+ } finally {
1000
+ await cleanUp();
1001
+ }
1002
+ return result;
1003
+ async function cleanUp() {
1004
+ if (!input.ownMaps) return;
1005
+ const maps = /* @__PURE__ */ new Set([...input.maps ?? [], ...owners.keys()]);
1006
+ const rel = (path) => (0, import_node_path2.relative)(input.dir, path).split(import_node_path2.sep).join("/");
1007
+ if (options.upload === false || options.deleteAfterUpload === false) {
1008
+ const kept = [...maps].filter((map) => (0, import_node_fs2.existsSync)(map));
1009
+ if (kept.length) {
1010
+ log(
1011
+ `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`
1012
+ );
1013
+ }
1014
+ return;
501
1015
  }
502
- if (!result.injected.some((r) => r.status !== "invalid-map")) return result;
503
- result.upload = await uploadDir(input.dir, {
504
- files: input.files,
505
- release: options.release,
506
- urlPrefix: options.urlPrefix ?? input.urlPrefix,
507
- endpoint: options.endpoint,
508
- key,
509
- dryRun: options.dryRun,
510
- concurrency: options.concurrency,
511
- deleteAfterUpload: options.deleteAfterUpload ?? input.deleteAfterUpload,
512
- log
513
- });
514
- for (const line of formatUploadSummary(result.upload)) log(line);
515
- if (result.upload.failed.length && options.failOnError) {
516
- throw new Error(`${result.upload.failed.length} source map upload(s) failed`);
1016
+ try {
1017
+ await removeSourceMaps(maps, owners);
1018
+ } catch (err) {
1019
+ const message = `could not delete the source maps this plugin generated (${err instanceof Error ? err.message : String(err)}): keep them out of the deploy`;
1020
+ if (options.failOnError) throw new Error(`[autter-runtime] ${message}`);
1021
+ if (!options.silent) console.warn(`[autter-runtime] ${message}`);
1022
+ }
1023
+ result.removedMaps = [...maps].filter((map) => !(0, import_node_fs2.existsSync)(map)).map(rel).sort();
1024
+ if (result.removedMaps.length) {
1025
+ log(`removed ${result.removedMaps.length} source map(s) this plugin generated, so they are not deployed: ${outcome}`);
517
1026
  }
518
- } catch (err) {
519
- const message = `source maps: ${err instanceof Error ? err.message : String(err)}`;
520
- if (options.failOnError) throw new Error(`[autter-runtime] ${message}`);
521
- if (!options.silent) console.warn(`[autter-runtime] ${message}`);
522
1027
  }
523
- return result;
524
1028
  }
525
1029
 
526
1030
  // src/webpack.ts
527
1031
  var PLUGIN_NAME = "AutterSourceMapsWebpackPlugin";
528
1032
  var AutterSourceMapsWebpackPlugin = class {
529
- constructor(options = {}) {
1033
+ constructor(options = {}, internal = {}) {
530
1034
  this.options = options;
1035
+ this.internal = internal;
531
1036
  }
532
1037
  options;
1038
+ internal;
533
1039
  apply(compiler) {
534
1040
  const mode = compiler.options.mode ?? (process.env.NODE_ENV === "development" ? "development" : void 0);
535
1041
  if (mode === "development") return;
536
- let enabledMaps = false;
1042
+ let enabledMaps = this.internal.ownMaps ?? false;
537
1043
  if (!compiler.options.devtool) {
538
- compiler.options.devtool = "hidden-source-map";
539
- enabledMaps = true;
1044
+ if (shouldGenerateMaps(this.options)) {
1045
+ compiler.options.devtool = "hidden-source-map";
1046
+ enabledMaps = true;
1047
+ } else {
1048
+ logMapsNotGenerated(this.options);
1049
+ }
540
1050
  }
541
1051
  compiler.hooks.afterEmit.tapPromise(PLUGIN_NAME, async (compilation) => {
542
1052
  const dir = compilation.outputOptions.path ?? compiler.outputPath;
543
1053
  if (!dir) return;
544
1054
  const publicPath = compilation.outputOptions.publicPath;
545
1055
  const urlPrefix = typeof publicPath === "string" && publicPath !== "auto" && publicPath !== "" ? publicPath : "/";
546
- const files = compilation.getAssets().map((asset) => asset.name).filter((name) => BUNDLE_RE.test(name)).map((name) => (0, import_node_path2.resolve)(dir, name));
547
- await processBuildOutput({ dir, files, urlPrefix, options: this.options, deleteAfterUpload: enabledMaps });
1056
+ const assets = compilation.getAssets().map((asset) => asset.name);
1057
+ const files = assets.filter((name) => BUNDLE_RE.test(name)).map((name) => (0, import_node_path3.resolve)(dir, name));
1058
+ const maps = assets.filter((name) => name.endsWith(".map")).map((name) => (0, import_node_path3.resolve)(dir, name));
1059
+ await processBuildOutput({
1060
+ dir,
1061
+ files,
1062
+ maps,
1063
+ urlPrefix,
1064
+ options: this.options,
1065
+ deleteAfterUpload: enabledMaps,
1066
+ ownMaps: enabledMaps
1067
+ });
548
1068
  });
549
1069
  }
550
1070
  };