next-leak 0.12.1 → 0.12.2

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.
@@ -15,6 +15,26 @@ export type BuildAttribution = {
15
15
  baselineFile: string;
16
16
  afterFile: string;
17
17
  };
18
+ /**
19
+ * Why a run that asked for attribution came back without one.
20
+ *
21
+ * An absent section and a failed diff read the same in a report, and they are
22
+ * opposite findings: one says nobody asked what the worker retained, the other
23
+ * says the question was asked and the evidence could not be read. The build
24
+ * path fails this way hardest on the builds worth measuring — a snapshot past
25
+ * roughly a gigabyte of worker rss cannot be parsed back, so the bigger the
26
+ * build grew, the likelier the report says nothing about what grew.
27
+ */
28
+ export type BuildAttributionGap = {
29
+ /**
30
+ * `capture-missed`: the run was told to capture and never got a usable pair.
31
+ * `snapshot-unreadable`: the pair exists and the diff refused it.
32
+ */
33
+ reason: "capture-missed" | "snapshot-unreadable";
34
+ detail: string;
35
+ };
36
+ /** Narrows the outcome of `attributeBuildCapture`. */
37
+ export declare function isAttributionGap(outcome: BuildAttribution | BuildAttributionGap | null): outcome is BuildAttributionGap;
18
38
  export type BuildAttributionDeps = {
19
39
  diff: typeof diffSnapshotFiles;
20
40
  registry: typeof extractModuleRegistry;
@@ -22,12 +42,14 @@ export type BuildAttributionDeps = {
22
42
  /**
23
43
  * Names what a build's worker retained, from the pair the run captured.
24
44
  *
25
- * Returns null rather than throwing on every failure path. A build measurement
26
- * stands on its curve and its verdict; this is an addition to the report, and
27
- * an addition that can fail must not be able to take the report with it.
45
+ * Never throws on a failure path. A build measurement stands on its curve and
46
+ * its verdict; this is an addition to the report, and an addition that can
47
+ * fail must not be able to take the report with it. It returns a gap instead,
48
+ * so the report can tell a question nobody asked from one it could not answer.
49
+ * Null is reserved for the run that never asked.
28
50
  *
29
51
  * The registry is read from `.next/server` *after* the build, never during it:
30
52
  * a half-written chunk can resolve a module id to the wrong source, and naming
31
53
  * the wrong owner confidently is worse than naming none.
32
54
  */
33
- export declare function attributeBuildCapture(result: BuildRunResult, appDir: string, onProgress?: (message: string) => void, deps?: BuildAttributionDeps): Promise<BuildAttribution | null>;
55
+ export declare function attributeBuildCapture(result: BuildRunResult, appDir: string, onProgress?: (message: string) => void, deps?: BuildAttributionDeps): Promise<BuildAttribution | BuildAttributionGap | null>;
@@ -1,4 +1,4 @@
1
- import type { BuildAttribution } from "./build-attribution.js";
1
+ import { type BuildAttribution, type BuildAttributionGap } from "./build-attribution.js";
2
2
  import type { BuildRunResult } from "./build-run.js";
3
3
  /**
4
4
  * Renders the build report.
@@ -9,4 +9,4 @@ import type { BuildRunResult } from "./build-run.js";
9
9
  * still the honest axis here — it is what a CI runner's limit is enforced
10
10
  * against and what the OOM killer reads.
11
11
  */
12
- export declare function formatBuildReport(result: BuildRunResult, attribution?: BuildAttribution | null): string;
12
+ export declare function formatBuildReport(result: BuildRunResult, attribution?: BuildAttribution | BuildAttributionGap | null): string;
@@ -51,6 +51,15 @@ export type BuildRunResult = {
51
51
  * produced whether or not capture worked.
52
52
  */
53
53
  capture: BuildCapture | null;
54
+ /**
55
+ * Whether this run was asked to capture at all. Without it a null `capture`
56
+ * is ambiguous — nobody asked, or it was asked and missed — and the report
57
+ * has to choose between staying silent on a lost measurement and nagging
58
+ * every run that never wanted one.
59
+ */
60
+ captureRequested: boolean;
61
+ /** How far capture got before giving up, when it was requested and missed. */
62
+ captureFailure: string | null;
54
63
  strippedCapWarning: string | null;
55
64
  exitCode: number | null;
56
65
  output: string;
@@ -8,7 +8,7 @@ import {
8
8
  resolveCycles,
9
9
  warrantsIssueDraft,
10
10
  withdrawnByDisagreement
11
- } from "./chunk-UU4RXOZO.js";
11
+ } from "./chunk-QUFL2FXU.js";
12
12
  import {
13
13
  assessPeakPressure,
14
14
  assessPressureVerdict,
@@ -93182,15 +93182,15 @@ async function jsFilesUnder(dir) {
93182
93182
  async function extractModuleRegistry(nextServerDir) {
93183
93183
  const registry = /* @__PURE__ */ new Map();
93184
93184
  for (const file of await jsFilesUnder(nextServerDir)) {
93185
- let code;
93186
- let map;
93185
+ let modules;
93187
93186
  try {
93188
- code = await readFile(file, "utf8");
93189
- map = JSON.parse(await readFile(`${file}.map`, "utf8"));
93187
+ const code = await readFile(file, "utf8");
93188
+ const map = JSON.parse(await readFile(`${file}.map`, "utf8"));
93189
+ modules = extractChunkModules(code, map);
93190
93190
  } catch {
93191
93191
  continue;
93192
93192
  }
93193
- for (const [id, source] of extractChunkModules(code, map)) {
93193
+ for (const [id, source] of modules) {
93194
93194
  if (!registry.has(id)) {
93195
93195
  registry.set(id, source);
93196
93196
  }
@@ -94812,7 +94812,7 @@ async function runSelfCheck(options, deps = defaultDeps2) {
94812
94812
  passed,
94813
94813
  verdict: result.trend.verdict,
94814
94814
  growthPer1000Requests,
94815
- summary: passed ? `harness verified: the planted leak was detected at ${rate}` : `harness NOT verified: a leak of ${PLANTED_LEAK_BYTES_PER_REQUEST} bytes per request came back ${result.trend.verdict} at ${rate}. Verdicts measured in this environment cannot be trusted \u2014 a flat curve here means the measurement is not working, not that nothing leaks`
94815
+ summary: passed ? `harness detected the planted leak at ${rate} \u2014 it responds to retention in this environment; the routes' own figures are not checked by this` : `harness NOT verified: a leak of ${PLANTED_LEAK_BYTES_PER_REQUEST} bytes per request came back ${result.trend.verdict} at ${rate}. Verdicts measured in this environment cannot be trusted \u2014 a flat curve here means the measurement is not working, not that nothing leaks`
94816
94816
  };
94817
94817
  } finally {
94818
94818
  await rm(dir, { recursive: true, force: true });
@@ -95187,7 +95187,9 @@ function harnessLine(report) {
95187
95187
  const measured = report.routes.filter((route) => route.status === "measured");
95188
95188
  if (report.harness.verified) {
95189
95189
  const rate = formatGrowth(report.harness.growthPer1000Requests);
95190
- return [`harness verified this session: a planted leak measured ${rate}`];
95190
+ return [
95191
+ `harness detected a planted leak this session at ${rate} \u2014 it can see retention here; that is not a check on the figures above`
95192
+ ];
95191
95193
  }
95192
95194
  const allStable = measured.length > 0 && measured.every((route) => effectiveVerdict(route) === "stable");
95193
95195
  if (!allStable) {
@@ -95622,8 +95624,8 @@ async function measureRoute(context, route, requestPath, index, pass = void 0) {
95622
95624
  };
95623
95625
  }
95624
95626
  async function writeEvidenceBundle(report, workDir) {
95625
- const { renderHtmlReport } = await import("./html-report-DLKFB3IU.js");
95626
- const { renderIssueMarkdown } = await import("./issue-report-OALZZLVR.js");
95627
+ const { renderHtmlReport } = await import("./html-report-MSEC3ZI4.js");
95628
+ const { renderIssueMarkdown } = await import("./issue-report-JXVLP7CV.js");
95627
95629
  for (const route of report.routes) {
95628
95630
  if (route.status === "measured" && warrantsIssueDraft(route)) {
95629
95631
  const file = path9.join(workDir, `ISSUE-${routeSlug(route.route)}.md`);
@@ -40,7 +40,7 @@ function isSaturating(deltas, minGrowth) {
40
40
  if (first === void 0 || last === void 0 || first < minGrowth) {
41
41
  return false;
42
42
  }
43
- if (!deltas.every((delta) => delta > 0)) {
43
+ if (!hasArrivedWithoutResuming(deltas, minGrowth)) {
44
44
  return false;
45
45
  }
46
46
  if (last > first * SATURATION_MAX_FINAL_RATIO) {
@@ -48,6 +48,17 @@ function isSaturating(deltas, minGrowth) {
48
48
  }
49
49
  return isHeadingDown(deltas);
50
50
  }
51
+ function hasArrivedWithoutResuming(deltas, minGrowth) {
52
+ const stop = deltas.findIndex((delta) => delta <= 0);
53
+ if (stop === -1) {
54
+ return true;
55
+ }
56
+ const approach = deltas[stop - 1];
57
+ if (approach === void 0 || approach >= minGrowth) {
58
+ return false;
59
+ }
60
+ return deltas.slice(stop).every((delta) => delta < minGrowth);
61
+ }
51
62
  function isHeadingDown(deltas) {
52
63
  const midpoint = Math.floor(deltas.length / 2);
53
64
  const head = deltas.slice(0, midpoint);
@@ -8,27 +8,46 @@ import {
8
8
  // src/issue-report.ts
9
9
  import path from "path";
10
10
  var MB = 1024 * 1024;
11
+ function codeSpan(text) {
12
+ const flat = text.replaceAll(/[\r\n]+/g, " ");
13
+ const longestRun = Math.max(0, ...[...flat.matchAll(/`+/g)].map((run) => run[0].length));
14
+ const fence = "`".repeat(longestRun + 1);
15
+ const padding = flat.startsWith("`") || flat.endsWith("`") ? " " : "";
16
+ return `${fence}${padding}${flat}${padding}${fence}`;
17
+ }
18
+ function publicWorkDir(run) {
19
+ const relative = path.relative(run.appDir, run.workDir);
20
+ return relative.startsWith("..") || path.isAbsolute(relative) ? path.basename(run.workDir) : relative;
21
+ }
11
22
  function ownerLabel(attribution) {
12
23
  if (attribution === void 0 || attribution.owner === "unattributed") {
13
24
  return "unattributed";
14
25
  }
15
- const source = attribution.source ? ` \u2014 \`${attribution.source}\`` : "";
26
+ const source = attribution.source ? ` \u2014 ${codeSpan(attribution.source)}` : "";
16
27
  const packageName = attribution.packageName ? ` \u2014 ${attribution.packageName}` : "";
17
28
  return `${attribution.owner}${source}${packageName}`;
18
29
  }
19
30
  function blamedParty(owner, culprit) {
20
31
  if (owner === "app") {
21
- return `**your own code** (\`${culprit?.source ?? "app code"}\`)`;
32
+ return `**your own code** (${codeSpan(culprit?.source ?? "app code")})`;
22
33
  }
23
34
  return `the dependency **${culprit?.packageName ?? "a dependency"}**`;
24
35
  }
36
+ function missingEvidence(route) {
37
+ if (route.attributionGap === void 0) {
38
+ return "- (no findings above thresholds)";
39
+ }
40
+ const cause = route.attributionGap.reason === "snapshot-unavailable" ? "the final heap snapshot was never taken" : "the heap snapshots could not be read";
41
+ return `- **Attribution unavailable** \u2014 ${cause}, so this run cannot name what retains the memory. This is not a finding that nothing grew: the diff was never computed. The growth figure above is unaffected.
42
+ - ${route.attributionGap.detail}`;
43
+ }
25
44
  function evidenceRows(route) {
26
45
  const findings = [...route.diff?.grownNodes ?? [], ...route.diff?.newNodes ?? []];
27
46
  return findings.slice(0, 6).map((finding, index) => {
28
47
  const attribution = route.attribution?.findings[index];
29
48
  const owner = ownerLabel(attribution);
30
- return `- **${finding.kind}** \`[${finding.nodeType}] ${finding.name}\` ${(finding.retainedBytes / MB).toFixed(2)} MB retained (${owner})
31
- - retainers: \`${finding.retainerChain || "(none)"}\``;
49
+ return `- **${finding.kind}** ${codeSpan(`[${finding.nodeType}] ${finding.name}`)} ${(finding.retainedBytes / MB).toFixed(2)} MB retained (${owner})
50
+ - retainers: ${codeSpan(finding.retainerChain || "(none)")}`;
32
51
  }).join("\n");
33
52
  }
34
53
  function renderIssueMarkdown(route, run) {
@@ -71,7 +90,7 @@ Sampled during the load phases, without forcing collection: ${describePeakPressu
71
90
  const signatures = route.signatures.map(
72
91
  (signature) => `- ${signature.historical ? "(historical) " : ""}${signature.title} \u2014 ${signature.issue}`
73
92
  ).join("\n");
74
- return `${preamble}${cacheDisclosure}# Memory leak on route \`${route.route}\`
93
+ return `${preamble}${cacheDisclosure}# Memory leak on route ${codeSpan(route.route)}
75
94
 
76
95
  Report generated by \`next-leak\` \u2014 every number below is reproducible from
77
96
  the raw heap snapshots referenced at the bottom.
@@ -93,7 +112,7 @@ Deployment: output "standalone", node --expose-gc --max-old-space-size=${paramet
93
112
  \`\`\`
94
113
  npx next-leak <app-dir>
95
114
  \`\`\`
96
- 2. next-leak boots \`.next/standalone/server.js\` in a fresh process per route and runs, against \`${route.requestPath}\`:
115
+ 2. next-leak boots \`.next/standalone/server.js\` in a fresh process per route and runs, against ${codeSpan(route.requestPath)}:
97
116
  warm-up ${parameters.warmupRequests} requests \u2192 forced GC \u2192 baseline heap snapshot \u2192
98
117
  ${parameters.cycles} \xD7 [${parameters.loadRequests} requests at ${parameters.connections} connections \u2192 ${(parameters.idleMs / 1e3).toFixed(0)}s idle \u2192 forced GC \u2192 post-GC sample] \u2192 final snapshot.
99
118
  3. A cycle counts as growing when it retains at least ${(parameters.minGrowthPerCycle / 1024).toFixed(0)} KiB
@@ -109,7 +128,7 @@ The first cycle is excluded from the verdict (engine warm-up).
109
128
 
110
129
  ### Heap evidence
111
130
 
112
- ${evidenceRows(route) || "- (no findings above thresholds)"}
131
+ ${evidenceRows(route) || missingEvidence(route)}
113
132
  ${signatures === "" ? "" : `
114
133
  **Matched known causes:**
115
134
  ${signatures}
@@ -118,7 +137,7 @@ ${signatures}
118
137
 
119
138
  Raw snapshots (Chrome DevTools \u2192 Memory \u2192 Load, compare baseline vs after):
120
139
 
121
- - \`${path.basename(route.baselineSnapshot)}\` / \`${path.basename(route.afterSnapshot)}\` in \`${run.workDir}\`
140
+ - ${codeSpan(path.basename(route.baselineSnapshot))} / ${codeSpan(path.basename(route.afterSnapshot))} in ${codeSpan(publicWorkDir(run))}
122
141
  - machine-readable data: \`run.json\`, curves: \`report.html\`
123
142
  `;
124
143
  }
@@ -2,7 +2,7 @@ import { createRequire as __nextLeakCreateRequire } from 'node:module';import {
2
2
  import {
3
3
  effectiveVerdict,
4
4
  withdrawnByDisagreement
5
- } from "./chunk-UU4RXOZO.js";
5
+ } from "./chunk-QUFL2FXU.js";
6
6
  import {
7
7
  assessPeakPressure,
8
8
  describePeakPressure,
@@ -69,6 +69,13 @@ function peakBlock(route, parameters) {
69
69
  const note = pressure === null ? "" : `<p class="warn">Peak pressure: ${escapeHtml(describePeakPressure(pressure))}</p>`;
70
70
  return note + `<table><tr><th>peak during</th><th>heap</th><th>external</th><th>arrayBuffers</th><th>rss</th></tr>${peakRows}</table>`;
71
71
  }
72
+ function attributionGapBlock(route) {
73
+ if (route.status !== "measured" || route.attributionGap === void 0) {
74
+ return "";
75
+ }
76
+ const cause = route.attributionGap.reason === "snapshot-unavailable" ? "the final heap snapshot was never taken" : "the heap snapshots could not be read";
77
+ return `<p class="warn">No attribution: ${cause}, so this run cannot name what retains the memory. The curve above is unaffected. ${escapeHtml(route.attributionGap.detail)}</p>`;
78
+ }
72
79
  function measuredSection(route, parameters) {
73
80
  if (route.status !== "measured") {
74
81
  return "";
@@ -84,7 +91,7 @@ function measuredSection(route, parameters) {
84
91
  const owner = ownerCell(attribution);
85
92
  return `<tr><td>${finding.kind}</td><td>${escapeHtml(finding.nodeType)}</td><td>${escapeHtml(finding.name)}</td><td>${(finding.retainedBytes / MB).toFixed(2)} MB</td><td>${owner}</td></tr>`;
86
93
  }).join("");
87
- return `<section><h2><span class="badge" style="background:${color}">${verdict}</span> <code>${escapeHtml(route.route)}</code></h2>` + heapCurveSvg(route.samples, color) + `<p class="curve">heap ${curve} MB \xB7 ${(route.growthPer1000Requests / MB).toFixed(2)} MB/1000 req</p>` + withdrawn + warnings + peakBlock(route, parameters) + (findingRows === "" ? "" : `<table><tr><th>kind</th><th>type</th><th>node</th><th>retained</th><th>owner</th></tr>${findingRows}</table>`) + `</section>`;
94
+ return `<section><h2><span class="badge" style="background:${color}">${verdict}</span> <code>${escapeHtml(route.route)}</code></h2>` + heapCurveSvg(route.samples, color) + `<p class="curve">heap ${curve} MB \xB7 ${(route.growthPer1000Requests / MB).toFixed(2)} MB/1000 req</p>` + withdrawn + warnings + peakBlock(route, parameters) + (findingRows === "" ? attributionGapBlock(route) : `<table><tr><th>kind</th><th>type</th><th>node</th><th>retained</th><th>owner</th></tr>${findingRows}</table>`) + `</section>`;
88
95
  }
89
96
  function renderHtmlReport(run) {
90
97
  const measured = run.routes.filter((route) => route.status === "measured");
package/dist/cli.js CHANGED
@@ -22,10 +22,10 @@ import {
22
22
  runSelfCheck,
23
23
  unregisterChild,
24
24
  validateTarget
25
- } from "./chunk-L5KWU2RU.js";
25
+ } from "./chunk-HDCOHSBY.js";
26
26
  import {
27
27
  classifyTrend
28
- } from "./chunk-UU4RXOZO.js";
28
+ } from "./chunk-QUFL2FXU.js";
29
29
  import "./chunk-E464SO7G.js";
30
30
  import "./chunk-6XYFBOL2.js";
31
31
 
@@ -151,6 +151,9 @@ function discardPendingSnapshotsSync() {
151
151
  }
152
152
 
153
153
  // src/build-attribution.ts
154
+ function isAttributionGap(outcome) {
155
+ return outcome !== null && "reason" in outcome;
156
+ }
154
157
  var defaultDeps = {
155
158
  diff: diffSnapshotFiles,
156
159
  registry: extractModuleRegistry
@@ -159,17 +162,19 @@ async function attributeBuildCapture(result, appDir, onProgress = () => {
159
162
  }, deps = defaultDeps) {
160
163
  const capture = result.capture;
161
164
  if (capture === null) {
162
- return null;
165
+ return result.captureRequested ? {
166
+ reason: "capture-missed",
167
+ detail: result.captureFailure ?? "the run never captured a usable snapshot pair from the worker"
168
+ } : null;
163
169
  }
164
170
  onProgress(`diffing what worker ${capture.pid} retained`);
165
171
  let diff;
166
172
  try {
167
173
  diff = await deps.diff(capture.files.baselineFile, capture.files.afterFile);
168
174
  } catch (cause) {
169
- onProgress(
170
- cause instanceof SnapshotError ? `no attribution: ${cause.message}` : `no attribution: the snapshot pair could not be diffed`
171
- );
172
- return null;
175
+ const detail = cause instanceof SnapshotError ? cause.message : "the snapshot pair could not be diffed";
176
+ onProgress(`no attribution: ${detail}`);
177
+ return { reason: "snapshot-unreadable", detail };
173
178
  }
174
179
  const registry = await deps.registry(path2.join(appDir, ".next", "server"));
175
180
  return {
@@ -212,6 +217,13 @@ function attributionLines(attribution) {
212
217
  if (attribution === null) {
213
218
  return [];
214
219
  }
220
+ if (isAttributionGap(attribution)) {
221
+ return [
222
+ "",
223
+ ` no attribution: ${attribution.detail}.`,
224
+ ` This does not say nothing grew \u2014 the comparison was never made.`
225
+ ];
226
+ }
215
227
  const { diff, attributed, bracketed } = attribution;
216
228
  const findings = [...diff.grownNodes, ...diff.newNodes];
217
229
  if (findings.length === 0) {
@@ -566,6 +578,24 @@ function recordSample(rows, rootPid, atMs, workers, parentSamples) {
566
578
  workers.set(row.pid, series);
567
579
  }
568
580
  }
581
+ function describeCaptureFailure(stage, pid, mismatchedWorker) {
582
+ if (mismatchedWorker) {
583
+ return "the snapshot pair belongs to a worker other than the one judged";
584
+ }
585
+ if (pid === null) {
586
+ return "no static-generation worker was seen to capture from";
587
+ }
588
+ switch (stage) {
589
+ case "waiting":
590
+ return `worker ${pid} never grew enough to snapshot`;
591
+ case "baseline-taken":
592
+ return `worker ${pid} was snapshotted once and never reached a second point`;
593
+ case "missed":
594
+ return `capture gave up on worker ${pid} before it had a pair`;
595
+ case "pair-taken":
596
+ return `the pair captured from worker ${pid} could not be collected from disk`;
597
+ }
598
+ }
569
599
  async function collectCapture(options) {
570
600
  const { appDir, workDir, pid, stage, baselineRssBytes, afterRssBytes } = options;
571
601
  if (workDir === void 0 || pid === null) {
@@ -716,6 +746,8 @@ async function runBuildMeasurement(options, deps = defaultDeps2) {
716
746
  retentionPerPageBytes: null,
717
747
  heapExhausted,
718
748
  capture: workerCapture,
749
+ captureRequested: capturing,
750
+ captureFailure: workerCapture === null ? describeCaptureFailure(captureStage, capturePid, false) : null,
719
751
  strippedCapWarning,
720
752
  exitCode,
721
753
  output
@@ -747,6 +779,12 @@ async function runBuildMeasurement(options, deps = defaultDeps2) {
747
779
  // A pair belonging to a worker other than the one judged supports no claim
748
780
  // about the verdict above, so it is dropped rather than reported next to it.
749
781
  capture: workerCapture !== null && workerCapture.pid === worst?.pid ? workerCapture : null,
782
+ captureRequested: capturing,
783
+ captureFailure: workerCapture !== null && workerCapture.pid === worst?.pid ? null : describeCaptureFailure(
784
+ captureStage,
785
+ capturePid,
786
+ workerCapture !== null && workerCapture.pid !== worst?.pid
787
+ ),
750
788
  strippedCapWarning,
751
789
  exitCode,
752
790
  output
@@ -1,8 +1,8 @@
1
1
  import { createRequire as __nextLeakCreateRequire } from 'node:module';import { fileURLToPath as __nextLeakFileURLToPath } from 'node:url';import { dirname as __nextLeakDirname } from 'node:path';const require = __nextLeakCreateRequire(import.meta.url);const __filename = __nextLeakFileURLToPath(import.meta.url);const __dirname = __nextLeakDirname(__filename);
2
2
  import {
3
3
  renderHtmlReport
4
- } from "./chunk-G2777DKL.js";
5
- import "./chunk-UU4RXOZO.js";
4
+ } from "./chunk-VKPGB5MG.js";
5
+ import "./chunk-QUFL2FXU.js";
6
6
  import "./chunk-E464SO7G.js";
7
7
  import "./chunk-6XYFBOL2.js";
8
8
  export {
package/dist/index.js CHANGED
@@ -41,16 +41,16 @@ import {
41
41
  sourceIndexAt,
42
42
  summarizeBaseline,
43
43
  validateTarget
44
- } from "./chunk-L5KWU2RU.js";
44
+ } from "./chunk-HDCOHSBY.js";
45
45
  import {
46
46
  renderHtmlReport
47
- } from "./chunk-G2777DKL.js";
47
+ } from "./chunk-VKPGB5MG.js";
48
48
  import {
49
49
  classifyTrend
50
- } from "./chunk-UU4RXOZO.js";
50
+ } from "./chunk-QUFL2FXU.js";
51
51
  import {
52
52
  renderIssueMarkdown
53
- } from "./chunk-YPVBX3QA.js";
53
+ } from "./chunk-ULTMNWRX.js";
54
54
  import "./chunk-E464SO7G.js";
55
55
  import "./chunk-6XYFBOL2.js";
56
56
  export {
@@ -1,7 +1,7 @@
1
1
  import { createRequire as __nextLeakCreateRequire } from 'node:module';import { fileURLToPath as __nextLeakFileURLToPath } from 'node:url';import { dirname as __nextLeakDirname } from 'node:path';const require = __nextLeakCreateRequire(import.meta.url);const __filename = __nextLeakFileURLToPath(import.meta.url);const __dirname = __nextLeakDirname(__filename);
2
2
  import {
3
3
  renderIssueMarkdown
4
- } from "./chunk-YPVBX3QA.js";
4
+ } from "./chunk-ULTMNWRX.js";
5
5
  import "./chunk-E464SO7G.js";
6
6
  import "./chunk-6XYFBOL2.js";
7
7
  export {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "next-leak",
3
- "version": "0.12.1",
3
+ "version": "0.12.2",
4
4
  "description": "Find out whether your Next.js app actually leaks memory — how much, on which route, and whose fault it is.",
5
5
  "keywords": [
6
6
  "nextjs",