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