next-leak 0.4.2 → 0.4.3

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.
@@ -3657,10 +3657,10 @@ var require_ansi_styles = __commonJS({
3657
3657
  };
3658
3658
  var ansi2ansi = (n) => n;
3659
3659
  var rgb2rgb = (r, g, b) => [r, g, b];
3660
- var setLazyProperty = (object, property, get) => {
3660
+ var setLazyProperty = (object, property, get2) => {
3661
3661
  Object.defineProperty(object, property, {
3662
3662
  get: () => {
3663
- const value = get();
3663
+ const value = get2();
3664
3664
  Object.defineProperty(object, property, {
3665
3665
  value,
3666
3666
  enumerable: true,
@@ -36043,18 +36043,18 @@ var require_gen_mapping_umd = __commonJS({
36043
36043
  function cast(set) {
36044
36044
  return set;
36045
36045
  }
36046
- function get(setarr, key) {
36046
+ function get2(setarr, key) {
36047
36047
  return cast(setarr)._indexes[key];
36048
36048
  }
36049
36049
  function put(setarr, key) {
36050
- const index = get(setarr, key);
36050
+ const index = get2(setarr, key);
36051
36051
  if (index !== void 0) return index;
36052
36052
  const { array, _indexes: indexes } = cast(setarr);
36053
36053
  const length = array.push(key);
36054
36054
  return indexes[key] = length - 1;
36055
36055
  }
36056
36056
  function remove(setarr, key) {
36057
- const index = get(setarr, key);
36057
+ const index = get2(setarr, key);
36058
36058
  if (index === void 0) return;
36059
36059
  const { array, _indexes: indexes } = cast(setarr);
36060
36060
  for (let i = index + 1; i < array.length; i++) {
@@ -60212,7 +60212,7 @@ var require_family = __commonJS({
60212
60212
  });
60213
60213
  exports2._getKey = _getKey;
60214
60214
  exports2._getPattern = _getPattern;
60215
- exports2.get = get;
60215
+ exports2.get = get2;
60216
60216
  exports2.getAllNextSiblings = getAllNextSiblings;
60217
60217
  exports2.getAllPrevSiblings = getAllPrevSiblings;
60218
60218
  exports2.getAssignmentIdentifiers = getAssignmentIdentifiers;
@@ -60443,7 +60443,7 @@ var require_family = __commonJS({
60443
60443
  }
60444
60444
  return siblings;
60445
60445
  }
60446
- function get(key, context = true) {
60446
+ function get2(key, context = true) {
60447
60447
  if (context === true) context = this.context;
60448
60448
  const parts = key.split(".");
60449
60449
  if (parts.length === 1) {
@@ -65957,7 +65957,7 @@ var require_dist5 = __commonJS({
65957
65957
  var pathVar = getPathVar(env, platform);
65958
65958
  var separator = getSeparator(platform);
65959
65959
  var originalPath = env[pathVar];
65960
- return { push, unshift, get, restore };
65960
+ return { push, unshift, get: get2, restore };
65961
65961
  function push() {
65962
65962
  for (var _len = arguments.length, paths = Array(_len), _key = 0; _key < _len; _key++) {
65963
65963
  paths[_key] = arguments[_key];
@@ -65970,12 +65970,12 @@ var require_dist5 = __commonJS({
65970
65970
  }
65971
65971
  return change(false, paths);
65972
65972
  }
65973
- function get() {
65973
+ function get2() {
65974
65974
  return env[pathVar];
65975
65975
  }
65976
65976
  function restore() {
65977
65977
  env[pathVar] = originalPath;
65978
- return get();
65978
+ return get2();
65979
65979
  }
65980
65980
  function change(append, paths) {
65981
65981
  if (!append) {
@@ -65986,7 +65986,7 @@ var require_dist5 = __commonJS({
65986
65986
  addExistingPath(pathArray, env[pathVar], append);
65987
65987
  env[pathVar] = pathArray.join(separator);
65988
65988
  });
65989
- return get();
65989
+ return get2();
65990
65990
  }
65991
65991
  }
65992
65992
  function getPathArray(pathToAdd) {
@@ -86350,7 +86350,7 @@ var require_form_data = __commonJS({
86350
86350
  var CombinedStream = require_combined_stream();
86351
86351
  var util = __require("util");
86352
86352
  var path7 = __require("path");
86353
- var http = __require("http");
86353
+ var http2 = __require("http");
86354
86354
  var https = __require("https");
86355
86355
  var parseUrl = __require("url").parse;
86356
86356
  var fs = __require("fs");
@@ -86625,7 +86625,7 @@ var require_form_data = __commonJS({
86625
86625
  if (options.protocol === "https:") {
86626
86626
  request2 = https.request(options);
86627
86627
  } else {
86628
- request2 = http.request(options);
86628
+ request2 = http2.request(options);
86629
86629
  }
86630
86630
  this.getLength(function(err, length) {
86631
86631
  if (err && err !== "Unknown stream") {
@@ -93239,6 +93239,7 @@ async function runLoadPhase(options) {
93239
93239
  import { mkdir } from "fs/promises";
93240
93240
 
93241
93241
  // src/control-client.ts
93242
+ import http from "http";
93242
93243
  import { z as z5 } from "zod";
93243
93244
  var sampleSchema = z5.object({
93244
93245
  gcExposed: z5.boolean(),
@@ -93254,19 +93255,70 @@ var ControlError = class extends Error {
93254
93255
  this.name = "ControlError";
93255
93256
  }
93256
93257
  };
93257
- async function request(port, pathname) {
93258
- let response;
93259
- try {
93260
- response = await fetch(`http://127.0.0.1:${port}${pathname}`);
93261
- } catch (cause) {
93262
- throw new ControlError(
93263
- `control channel ${pathname} on port ${port} did not answer (${cause instanceof Error ? cause.message : String(cause)}) \u2014 the measured process is gone or its event loop is blocked`
93258
+ var DEADLINES_MS = {
93259
+ "/mem": 3e4,
93260
+ "/gc": 18e4,
93261
+ "/snapshot": 18e5
93262
+ };
93263
+ var DEFAULT_DEADLINE_MS = 3e4;
93264
+ function deadlineFor(pathname) {
93265
+ const operation = pathname.split("?")[0] ?? pathname;
93266
+ return DEADLINES_MS[operation] ?? DEFAULT_DEADLINE_MS;
93267
+ }
93268
+ function get(port, pathname, deadlineMs) {
93269
+ return new Promise((resolve, reject) => {
93270
+ let settled = false;
93271
+ const settle = (outcome) => {
93272
+ if (settled) {
93273
+ return;
93274
+ }
93275
+ settled = true;
93276
+ clearTimeout(timer);
93277
+ if ("ok" in outcome) {
93278
+ resolve(outcome.ok);
93279
+ } else {
93280
+ reject(outcome.error);
93281
+ }
93282
+ };
93283
+ const request2 = http.get({ host: "127.0.0.1", port, path: pathname }, (response) => {
93284
+ const chunks = [];
93285
+ response.on("data", (chunk) => chunks.push(chunk));
93286
+ response.once(
93287
+ "end",
93288
+ () => settle({
93289
+ ok: { status: response.statusCode ?? 0, body: Buffer.concat(chunks).toString("utf8") }
93290
+ })
93291
+ );
93292
+ });
93293
+ const timer = setTimeout(() => {
93294
+ settle({
93295
+ error: new ControlError(
93296
+ `control channel ${pathname} on port ${port} did not answer within ${Math.round(deadlineMs / 1e3)}s \u2014 the measured process is wedged, or this snapshot is larger than anything this tool has been validated on`
93297
+ )
93298
+ });
93299
+ request2.destroy();
93300
+ }, deadlineMs);
93301
+ timer.unref();
93302
+ request2.once(
93303
+ "error",
93304
+ (cause) => settle({
93305
+ error: new ControlError(
93306
+ `control channel ${pathname} on port ${port} did not answer (${cause.message}) \u2014 the measured process is gone or its event loop is blocked`
93307
+ )
93308
+ })
93264
93309
  );
93265
- }
93266
- if (!response.ok) {
93310
+ });
93311
+ }
93312
+ async function request(port, pathname, deadlineMs = deadlineFor(pathname)) {
93313
+ const response = await get(port, pathname, deadlineMs);
93314
+ if (response.status !== 200) {
93267
93315
  throw new ControlError(`control channel ${pathname} responded ${response.status}`);
93268
93316
  }
93269
- return response.json();
93317
+ try {
93318
+ return JSON.parse(response.body);
93319
+ } catch {
93320
+ throw new ControlError(`control channel ${pathname} answered with malformed JSON`);
93321
+ }
93270
93322
  }
93271
93323
  async function requestGc(port) {
93272
93324
  const sample = sampleSchema.parse(await request(port, "/gc"));
@@ -93444,6 +93496,7 @@ async function waitUntilSettled(controlPort, maxIdleMs, deps) {
93444
93496
  var defaultDeps = {
93445
93497
  launch: launchInstrumented,
93446
93498
  load: runLoadPhase,
93499
+ abandon: runAbandonPhase,
93447
93500
  sleep: (ms) => new Promise((resolve) => setTimeout(resolve, ms)),
93448
93501
  readMemory: requestMemory
93449
93502
  };
@@ -93502,7 +93555,7 @@ async function runRitual(options, deps = defaultDeps) {
93502
93555
  });
93503
93556
  return;
93504
93557
  }
93505
- const outcome = await runAbandonPhase({
93558
+ const outcome = await deps.abandon({
93506
93559
  url: `http://127.0.0.1:${app.appPort}${options.route}`,
93507
93560
  amount,
93508
93561
  connections,
package/dist/cli.js CHANGED
@@ -9,7 +9,7 @@ import {
9
9
  killActiveChildren,
10
10
  parseCliArgs,
11
11
  runMeasurement
12
- } from "./chunk-YDMJVVWG.js";
12
+ } from "./chunk-EQ2DV2YO.js";
13
13
  import "./chunk-2FZXZLZW.js";
14
14
  import "./chunk-XHPUAMJG.js";
15
15
  import "./chunk-6XYFBOL2.js";
@@ -1,4 +1,5 @@
1
1
  import type { HeapSample } from "./control-server.js";
2
+ declare function request(port: number, pathname: string, deadlineMs?: number): Promise<unknown>;
2
3
  /** Forces GC in the measured process and returns a settled memory sample. */
3
4
  export declare function requestGc(port: number): Promise<HeapSample>;
4
5
  /**
@@ -11,3 +12,6 @@ export declare function requestSnapshot(port: number, name: string): Promise<{
11
12
  file: string;
12
13
  sample: HeapSample;
13
14
  }>;
15
+ /** Test seam: the production deadlines would make a hung-server test take minutes. */
16
+ export declare const requestWithDeadline: typeof request;
17
+ export {};
package/dist/index.js CHANGED
@@ -39,7 +39,7 @@ import {
39
39
  sourceIndexAt,
40
40
  summarizeBaseline,
41
41
  validateTarget
42
- } from "./chunk-YDMJVVWG.js";
42
+ } from "./chunk-EQ2DV2YO.js";
43
43
  import {
44
44
  renderHtmlReport
45
45
  } from "./chunk-NNDGXIGQ.js";
package/dist/ritual.d.ts CHANGED
@@ -1,5 +1,6 @@
1
1
  import type { HeapSample } from "./control-server.js";
2
2
  import { launchInstrumented } from "./launcher.js";
3
+ import { runAbandonPhase } from "./abandon-load.js";
3
4
  import { runLoadPhase } from "./load.js";
4
5
  import { type TrendResult } from "./trend.js";
5
6
  export type RitualOptions = {
@@ -103,6 +104,8 @@ export type RitualResult = {
103
104
  export type RitualDeps = {
104
105
  launch: typeof launchInstrumented;
105
106
  load: typeof runLoadPhase;
107
+ /** Early-disconnect load; a seam like `load`, for the same reasons. */
108
+ abandon: typeof runAbandonPhase;
106
109
  sleep: (ms: number) => Promise<void>;
107
110
  /** GC-free read, polled while the app is under load. */
108
111
  readMemory: (port: number) => Promise<HeapSample>;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "next-leak",
3
- "version": "0.4.2",
3
+ "version": "0.4.3",
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",