@react-perfscope/core 0.5.0 → 0.7.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/index.cjs +87 -11
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +59 -2
- package/dist/index.d.ts +59 -2
- package/dist/index.js +82 -10
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.d.cts
CHANGED
|
@@ -171,6 +171,19 @@ type FrameStats = {
|
|
|
171
171
|
/** Approximate frames dropped across the recording (vs a 60fps budget). */
|
|
172
172
|
droppedFrames: number;
|
|
173
173
|
};
|
|
174
|
+
/** A component whose instances were unmounted but stayed retained (not
|
|
175
|
+
* garbage-collected) with a count that climbed across the recording — i.e. a
|
|
176
|
+
* suspected leak. Identifies WHICH component leaks and HOW MANY instances; the
|
|
177
|
+
* retainer chain (who holds them) is not observable from in-page JS. */
|
|
178
|
+
type LeakSuspect = {
|
|
179
|
+
component: string;
|
|
180
|
+
/** Total instances of this component unmounted during the recording. */
|
|
181
|
+
unmounted: number;
|
|
182
|
+
/** Unmounted instances still alive at the end (unmounted − collected). */
|
|
183
|
+
retained: number;
|
|
184
|
+
/** Growth rate of the retained-instance floor, in instances per minute. */
|
|
185
|
+
retainedSlopePerMin: number;
|
|
186
|
+
};
|
|
174
187
|
interface RecordingResult {
|
|
175
188
|
signals: Signal[];
|
|
176
189
|
startedAt: number;
|
|
@@ -181,12 +194,15 @@ interface RecordingResult {
|
|
|
181
194
|
/** requestAnimationFrame timestamps captured during the recording, for
|
|
182
195
|
* frame-rate / jank analysis. Attached at finalize. */
|
|
183
196
|
frames?: number[];
|
|
197
|
+
/** Components with a sustained rise in retained-after-unmount instances —
|
|
198
|
+
* suspected leaks. Attached at finalize; absent when none or unsupported. */
|
|
199
|
+
leakSuspects?: LeakSuspect[];
|
|
184
200
|
}
|
|
185
201
|
/** Collectors are usually keyed by the SignalKind they emit. A few (heap,
|
|
186
202
|
* self-profiling) drive a side-channel instead of emitting signals; `'heap'`
|
|
187
203
|
* widens the kind for the heap sampler, which attaches a time series via
|
|
188
204
|
* finalize rather than pushing signals. */
|
|
189
|
-
type CollectorKind = SignalKind | 'heap' | 'frame';
|
|
205
|
+
type CollectorKind = SignalKind | 'heap' | 'frame' | 'leak';
|
|
190
206
|
interface Collector {
|
|
191
207
|
readonly kind: CollectorKind;
|
|
192
208
|
activate(emit: (signal: Signal) => void): void;
|
|
@@ -200,6 +216,9 @@ interface Recorder {
|
|
|
200
216
|
use(collector: Collector): void;
|
|
201
217
|
}
|
|
202
218
|
|
|
219
|
+
/** Hard cap on retained signals; the oldest are dropped past this. Exported so
|
|
220
|
+
* downstream tests can assert the buffer never grows beyond it. */
|
|
221
|
+
declare const BUFFER_CAP = 10000;
|
|
203
222
|
declare function createRecorder(): Recorder;
|
|
204
223
|
|
|
205
224
|
/** A parsed source map (the JSON object), as returned by a FetchMap. */
|
|
@@ -241,6 +260,44 @@ declare function markSelfRequest(url: string): void;
|
|
|
241
260
|
/** True when `url` was fetched by react-perfscope itself. */
|
|
242
261
|
declare function isSelfRequest(url: string): boolean;
|
|
243
262
|
|
|
263
|
+
type Capabilities = Record<SignalKind, boolean>;
|
|
264
|
+
/** Which signal kinds the current browser can actually measure. The render and
|
|
265
|
+
* forced-reflow collectors are pure JS (React fibers / DOM-API instrumentation)
|
|
266
|
+
* so they work everywhere; the rest depend on PerformanceObserver entry types
|
|
267
|
+
* that Firefox and Safari do not implement (long tasks, layout shift, INP). */
|
|
268
|
+
declare function detectCapabilities(): Capabilities;
|
|
269
|
+
/** Signal kinds the current browser cannot measure — for telling the user a
|
|
270
|
+
* tab is empty because of the platform, not because nothing happened. */
|
|
271
|
+
declare function unsupportedKinds(): SignalKind[];
|
|
272
|
+
|
|
273
|
+
/** One reading of how many unmounted instances of a component are still
|
|
274
|
+
* retained (alive, not yet garbage-collected) at a point in time. */
|
|
275
|
+
type LeakSample = {
|
|
276
|
+
at: number;
|
|
277
|
+
retained: number;
|
|
278
|
+
};
|
|
279
|
+
/**
|
|
280
|
+
* Decide whether a component's retained-instance series indicates a leak.
|
|
281
|
+
*
|
|
282
|
+
* Mirrors {@link analyzeHeapTrend}: GC makes the raw retained count a sawtooth
|
|
283
|
+
* (instances pile up, then a sweep collects a batch), so the reliable signal is
|
|
284
|
+
* the *floor* — the post-GC troughs. We segment the series, take each segment's
|
|
285
|
+
* minimum, and regress those minima against time.
|
|
286
|
+
*
|
|
287
|
+
* A rising slope alone is not enough: a one-time step (a screen mounts N
|
|
288
|
+
* long-lived instances once, then plateaus) regresses positive but is not a
|
|
289
|
+
* leak. So we additionally require the recent half of the floor to still be
|
|
290
|
+
* rising, and gate on a minimum net growth in instances. This is what makes the
|
|
291
|
+
* detector robust to React StrictMode's intentional mount→unmount→remount
|
|
292
|
+
* churn, whose discarded fibers get collected and keep the floor flat.
|
|
293
|
+
*
|
|
294
|
+
* Returns null when there are too few samples to say anything.
|
|
295
|
+
*/
|
|
296
|
+
declare function analyzeLeakTrend(samples: LeakSample[]): {
|
|
297
|
+
leaking: boolean;
|
|
298
|
+
slopePerMin: number;
|
|
299
|
+
} | null;
|
|
300
|
+
|
|
244
301
|
type AnchorSignal = InteractionSignal | LongTaskSignal;
|
|
245
302
|
/** How strongly a member is linked to its episode anchor. `caused` means a
|
|
246
303
|
* member's source location matches one of the anchor's hot frames — a real
|
|
@@ -421,4 +478,4 @@ interface SelfProfilingCollector extends Collector {
|
|
|
421
478
|
*/
|
|
422
479
|
declare function createSelfProfilingCollector(): SelfProfilingCollector;
|
|
423
480
|
|
|
424
|
-
export { type AnchorSignal, type Collector, type CollectorKind, type CommitCause, type CreateSourceMapResolverOptions, type Episode, type EpisodeMember, type FetchMap, type ForcedReflowSignal, type FpsSample, type FrameCollector, type FrameStats, type HeapCollector, type HeapSample, type HeapTrend, type HeapTrendClass, type InpPhase, type InteractionCollector, type InteractionSignal, type LayoutShiftSignal, type LinkConfidence, type LongTaskAttribution, type LongTaskScript, type LongTaskSignal, type NetworkSignal, type ProfilerFrame, type ProfilerSample, type ProfilerStack, type ProfilerTrace, type Recorder, type RecordingResult, type RenderReason, type RenderSignal, type Signal, type SignalKind, type SourceMapResolver, type StackFrame, type WebVitalSignal, analyzeFrames, analyzeHeapTrend, attachLazyStack, attributeLongTaskSignals, attributeWindow, correlate, createForcedReflowCollector, createFrameCollector, createHeapCollector, createInteractionCollector, createLayoutShiftCollector, createLongTasksCollector, createNetworkCollector, createRecorder, createSelfProfilingCollector, createSourceMapResolver, createWebVitalsCollector, isSelfRequest, isUserResource, markSelfRequest, parseStack, resolveFrame };
|
|
481
|
+
export { type AnchorSignal, BUFFER_CAP, type Capabilities, type Collector, type CollectorKind, type CommitCause, type CreateSourceMapResolverOptions, type Episode, type EpisodeMember, type FetchMap, type ForcedReflowSignal, type FpsSample, type FrameCollector, type FrameStats, type HeapCollector, type HeapSample, type HeapTrend, type HeapTrendClass, type InpPhase, type InteractionCollector, type InteractionSignal, type LayoutShiftSignal, type LeakSample, type LeakSuspect, type LinkConfidence, type LongTaskAttribution, type LongTaskScript, type LongTaskSignal, type NetworkSignal, type ProfilerFrame, type ProfilerSample, type ProfilerStack, type ProfilerTrace, type Recorder, type RecordingResult, type RenderReason, type RenderSignal, type Signal, type SignalKind, type SourceMapResolver, type StackFrame, type WebVitalSignal, analyzeFrames, analyzeHeapTrend, analyzeLeakTrend, attachLazyStack, attributeLongTaskSignals, attributeWindow, correlate, createForcedReflowCollector, createFrameCollector, createHeapCollector, createInteractionCollector, createLayoutShiftCollector, createLongTasksCollector, createNetworkCollector, createRecorder, createSelfProfilingCollector, createSourceMapResolver, createWebVitalsCollector, detectCapabilities, isSelfRequest, isUserResource, markSelfRequest, parseStack, resolveFrame, unsupportedKinds };
|
package/dist/index.d.ts
CHANGED
|
@@ -171,6 +171,19 @@ type FrameStats = {
|
|
|
171
171
|
/** Approximate frames dropped across the recording (vs a 60fps budget). */
|
|
172
172
|
droppedFrames: number;
|
|
173
173
|
};
|
|
174
|
+
/** A component whose instances were unmounted but stayed retained (not
|
|
175
|
+
* garbage-collected) with a count that climbed across the recording — i.e. a
|
|
176
|
+
* suspected leak. Identifies WHICH component leaks and HOW MANY instances; the
|
|
177
|
+
* retainer chain (who holds them) is not observable from in-page JS. */
|
|
178
|
+
type LeakSuspect = {
|
|
179
|
+
component: string;
|
|
180
|
+
/** Total instances of this component unmounted during the recording. */
|
|
181
|
+
unmounted: number;
|
|
182
|
+
/** Unmounted instances still alive at the end (unmounted − collected). */
|
|
183
|
+
retained: number;
|
|
184
|
+
/** Growth rate of the retained-instance floor, in instances per minute. */
|
|
185
|
+
retainedSlopePerMin: number;
|
|
186
|
+
};
|
|
174
187
|
interface RecordingResult {
|
|
175
188
|
signals: Signal[];
|
|
176
189
|
startedAt: number;
|
|
@@ -181,12 +194,15 @@ interface RecordingResult {
|
|
|
181
194
|
/** requestAnimationFrame timestamps captured during the recording, for
|
|
182
195
|
* frame-rate / jank analysis. Attached at finalize. */
|
|
183
196
|
frames?: number[];
|
|
197
|
+
/** Components with a sustained rise in retained-after-unmount instances —
|
|
198
|
+
* suspected leaks. Attached at finalize; absent when none or unsupported. */
|
|
199
|
+
leakSuspects?: LeakSuspect[];
|
|
184
200
|
}
|
|
185
201
|
/** Collectors are usually keyed by the SignalKind they emit. A few (heap,
|
|
186
202
|
* self-profiling) drive a side-channel instead of emitting signals; `'heap'`
|
|
187
203
|
* widens the kind for the heap sampler, which attaches a time series via
|
|
188
204
|
* finalize rather than pushing signals. */
|
|
189
|
-
type CollectorKind = SignalKind | 'heap' | 'frame';
|
|
205
|
+
type CollectorKind = SignalKind | 'heap' | 'frame' | 'leak';
|
|
190
206
|
interface Collector {
|
|
191
207
|
readonly kind: CollectorKind;
|
|
192
208
|
activate(emit: (signal: Signal) => void): void;
|
|
@@ -200,6 +216,9 @@ interface Recorder {
|
|
|
200
216
|
use(collector: Collector): void;
|
|
201
217
|
}
|
|
202
218
|
|
|
219
|
+
/** Hard cap on retained signals; the oldest are dropped past this. Exported so
|
|
220
|
+
* downstream tests can assert the buffer never grows beyond it. */
|
|
221
|
+
declare const BUFFER_CAP = 10000;
|
|
203
222
|
declare function createRecorder(): Recorder;
|
|
204
223
|
|
|
205
224
|
/** A parsed source map (the JSON object), as returned by a FetchMap. */
|
|
@@ -241,6 +260,44 @@ declare function markSelfRequest(url: string): void;
|
|
|
241
260
|
/** True when `url` was fetched by react-perfscope itself. */
|
|
242
261
|
declare function isSelfRequest(url: string): boolean;
|
|
243
262
|
|
|
263
|
+
type Capabilities = Record<SignalKind, boolean>;
|
|
264
|
+
/** Which signal kinds the current browser can actually measure. The render and
|
|
265
|
+
* forced-reflow collectors are pure JS (React fibers / DOM-API instrumentation)
|
|
266
|
+
* so they work everywhere; the rest depend on PerformanceObserver entry types
|
|
267
|
+
* that Firefox and Safari do not implement (long tasks, layout shift, INP). */
|
|
268
|
+
declare function detectCapabilities(): Capabilities;
|
|
269
|
+
/** Signal kinds the current browser cannot measure — for telling the user a
|
|
270
|
+
* tab is empty because of the platform, not because nothing happened. */
|
|
271
|
+
declare function unsupportedKinds(): SignalKind[];
|
|
272
|
+
|
|
273
|
+
/** One reading of how many unmounted instances of a component are still
|
|
274
|
+
* retained (alive, not yet garbage-collected) at a point in time. */
|
|
275
|
+
type LeakSample = {
|
|
276
|
+
at: number;
|
|
277
|
+
retained: number;
|
|
278
|
+
};
|
|
279
|
+
/**
|
|
280
|
+
* Decide whether a component's retained-instance series indicates a leak.
|
|
281
|
+
*
|
|
282
|
+
* Mirrors {@link analyzeHeapTrend}: GC makes the raw retained count a sawtooth
|
|
283
|
+
* (instances pile up, then a sweep collects a batch), so the reliable signal is
|
|
284
|
+
* the *floor* — the post-GC troughs. We segment the series, take each segment's
|
|
285
|
+
* minimum, and regress those minima against time.
|
|
286
|
+
*
|
|
287
|
+
* A rising slope alone is not enough: a one-time step (a screen mounts N
|
|
288
|
+
* long-lived instances once, then plateaus) regresses positive but is not a
|
|
289
|
+
* leak. So we additionally require the recent half of the floor to still be
|
|
290
|
+
* rising, and gate on a minimum net growth in instances. This is what makes the
|
|
291
|
+
* detector robust to React StrictMode's intentional mount→unmount→remount
|
|
292
|
+
* churn, whose discarded fibers get collected and keep the floor flat.
|
|
293
|
+
*
|
|
294
|
+
* Returns null when there are too few samples to say anything.
|
|
295
|
+
*/
|
|
296
|
+
declare function analyzeLeakTrend(samples: LeakSample[]): {
|
|
297
|
+
leaking: boolean;
|
|
298
|
+
slopePerMin: number;
|
|
299
|
+
} | null;
|
|
300
|
+
|
|
244
301
|
type AnchorSignal = InteractionSignal | LongTaskSignal;
|
|
245
302
|
/** How strongly a member is linked to its episode anchor. `caused` means a
|
|
246
303
|
* member's source location matches one of the anchor's hot frames — a real
|
|
@@ -421,4 +478,4 @@ interface SelfProfilingCollector extends Collector {
|
|
|
421
478
|
*/
|
|
422
479
|
declare function createSelfProfilingCollector(): SelfProfilingCollector;
|
|
423
480
|
|
|
424
|
-
export { type AnchorSignal, type Collector, type CollectorKind, type CommitCause, type CreateSourceMapResolverOptions, type Episode, type EpisodeMember, type FetchMap, type ForcedReflowSignal, type FpsSample, type FrameCollector, type FrameStats, type HeapCollector, type HeapSample, type HeapTrend, type HeapTrendClass, type InpPhase, type InteractionCollector, type InteractionSignal, type LayoutShiftSignal, type LinkConfidence, type LongTaskAttribution, type LongTaskScript, type LongTaskSignal, type NetworkSignal, type ProfilerFrame, type ProfilerSample, type ProfilerStack, type ProfilerTrace, type Recorder, type RecordingResult, type RenderReason, type RenderSignal, type Signal, type SignalKind, type SourceMapResolver, type StackFrame, type WebVitalSignal, analyzeFrames, analyzeHeapTrend, attachLazyStack, attributeLongTaskSignals, attributeWindow, correlate, createForcedReflowCollector, createFrameCollector, createHeapCollector, createInteractionCollector, createLayoutShiftCollector, createLongTasksCollector, createNetworkCollector, createRecorder, createSelfProfilingCollector, createSourceMapResolver, createWebVitalsCollector, isSelfRequest, isUserResource, markSelfRequest, parseStack, resolveFrame };
|
|
481
|
+
export { type AnchorSignal, BUFFER_CAP, type Capabilities, type Collector, type CollectorKind, type CommitCause, type CreateSourceMapResolverOptions, type Episode, type EpisodeMember, type FetchMap, type ForcedReflowSignal, type FpsSample, type FrameCollector, type FrameStats, type HeapCollector, type HeapSample, type HeapTrend, type HeapTrendClass, type InpPhase, type InteractionCollector, type InteractionSignal, type LayoutShiftSignal, type LeakSample, type LeakSuspect, type LinkConfidence, type LongTaskAttribution, type LongTaskScript, type LongTaskSignal, type NetworkSignal, type ProfilerFrame, type ProfilerSample, type ProfilerStack, type ProfilerTrace, type Recorder, type RecordingResult, type RenderReason, type RenderSignal, type Signal, type SignalKind, type SourceMapResolver, type StackFrame, type WebVitalSignal, analyzeFrames, analyzeHeapTrend, analyzeLeakTrend, attachLazyStack, attributeLongTaskSignals, attributeWindow, correlate, createForcedReflowCollector, createFrameCollector, createHeapCollector, createInteractionCollector, createLayoutShiftCollector, createLongTasksCollector, createNetworkCollector, createRecorder, createSelfProfilingCollector, createSourceMapResolver, createWebVitalsCollector, detectCapabilities, isSelfRequest, isUserResource, markSelfRequest, parseStack, resolveFrame, unsupportedKinds };
|
package/dist/index.js
CHANGED
|
@@ -201,6 +201,74 @@ function parseStack(raw) {
|
|
|
201
201
|
return frames;
|
|
202
202
|
}
|
|
203
203
|
|
|
204
|
+
// src/capabilities.ts
|
|
205
|
+
function supportedEntryTypes() {
|
|
206
|
+
const PO = globalThis.PerformanceObserver;
|
|
207
|
+
return PO?.supportedEntryTypes;
|
|
208
|
+
}
|
|
209
|
+
function observerSupported(needed) {
|
|
210
|
+
const types = supportedEntryTypes();
|
|
211
|
+
if (!Array.isArray(types)) return true;
|
|
212
|
+
return needed.some((t) => types.includes(t));
|
|
213
|
+
}
|
|
214
|
+
function detectCapabilities() {
|
|
215
|
+
return {
|
|
216
|
+
"forced-reflow": true,
|
|
217
|
+
render: true,
|
|
218
|
+
"web-vital": true,
|
|
219
|
+
"layout-shift": observerSupported(["layout-shift"]),
|
|
220
|
+
"long-task": observerSupported(["long-animation-frame", "longtask"]),
|
|
221
|
+
interaction: observerSupported(["event"]),
|
|
222
|
+
network: observerSupported(["resource"])
|
|
223
|
+
};
|
|
224
|
+
}
|
|
225
|
+
function unsupportedKinds() {
|
|
226
|
+
const caps = detectCapabilities();
|
|
227
|
+
return Object.keys(caps).filter((k) => !caps[k]);
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
// src/leak-trend.ts
|
|
231
|
+
var MIN_SAMPLES = 4;
|
|
232
|
+
var FLOOR_SEGMENTS = 8;
|
|
233
|
+
var MIN_NET_GROWTH = 3;
|
|
234
|
+
function slope(points) {
|
|
235
|
+
const n = points.length;
|
|
236
|
+
if (n < 2) return 0;
|
|
237
|
+
let sx = 0;
|
|
238
|
+
let sy = 0;
|
|
239
|
+
let sxy = 0;
|
|
240
|
+
let sxx = 0;
|
|
241
|
+
for (const { x, y } of points) {
|
|
242
|
+
sx += x;
|
|
243
|
+
sy += y;
|
|
244
|
+
sxy += x * y;
|
|
245
|
+
sxx += x * x;
|
|
246
|
+
}
|
|
247
|
+
const denom = n * sxx - sx * sx;
|
|
248
|
+
if (denom === 0) return 0;
|
|
249
|
+
return (n * sxy - sx * sy) / denom;
|
|
250
|
+
}
|
|
251
|
+
function analyzeLeakTrend(samples) {
|
|
252
|
+
if (samples.length < MIN_SAMPLES) return null;
|
|
253
|
+
const t0 = samples[0].at;
|
|
254
|
+
const segSize = Math.max(1, Math.ceil(samples.length / FLOOR_SEGMENTS));
|
|
255
|
+
const floorPoints = [];
|
|
256
|
+
for (let i = 0; i < samples.length; i += segSize) {
|
|
257
|
+
const seg = samples.slice(i, i + segSize);
|
|
258
|
+
let min = Infinity;
|
|
259
|
+
for (const s of seg) min = Math.min(min, s.retained);
|
|
260
|
+
const mid = seg[Math.floor(seg.length / 2)];
|
|
261
|
+
floorPoints.push({ x: (mid.at - t0) / 6e4, y: min });
|
|
262
|
+
}
|
|
263
|
+
const slopePerMin = slope(floorPoints);
|
|
264
|
+
const spanMin = (samples[samples.length - 1].at - t0) / 6e4;
|
|
265
|
+
const projectedGrowth = slopePerMin * spanMin;
|
|
266
|
+
const recent = floorPoints.slice(Math.floor(floorPoints.length / 2));
|
|
267
|
+
const recentSlope = recent.length >= 2 ? slope(recent) : slopePerMin;
|
|
268
|
+
const leaking = projectedGrowth >= MIN_NET_GROWTH && recentSlope > 0;
|
|
269
|
+
return { leaking, slopePerMin };
|
|
270
|
+
}
|
|
271
|
+
|
|
204
272
|
// src/correlate.ts
|
|
205
273
|
var FRAME_MS = 16;
|
|
206
274
|
function isAnchor(signal) {
|
|
@@ -690,19 +758,19 @@ function createWebVitalsCollector() {
|
|
|
690
758
|
// src/collectors/heap.ts
|
|
691
759
|
var SAMPLE_INTERVAL_MS = 250;
|
|
692
760
|
var MAX_SAMPLES = 2e4;
|
|
693
|
-
var
|
|
694
|
-
var
|
|
761
|
+
var MIN_SAMPLES2 = 4;
|
|
762
|
+
var FLOOR_SEGMENTS2 = 8;
|
|
695
763
|
var MB = 1024 * 1024;
|
|
696
764
|
var GROWING_SLOPE = 1 * MB;
|
|
697
765
|
var LEAK_SLOPE = 5 * MB;
|
|
698
|
-
var
|
|
766
|
+
var MIN_NET_GROWTH2 = 2 * MB;
|
|
699
767
|
function readMemory() {
|
|
700
768
|
const perf = globalThis.performance;
|
|
701
769
|
const mem = perf?.memory;
|
|
702
770
|
if (!mem || typeof mem.usedJSHeapSize !== "number") return null;
|
|
703
771
|
return mem;
|
|
704
772
|
}
|
|
705
|
-
function
|
|
773
|
+
function slope2(points) {
|
|
706
774
|
const n = points.length;
|
|
707
775
|
if (n < 2) return 0;
|
|
708
776
|
let sx = 0;
|
|
@@ -720,9 +788,9 @@ function slope(points) {
|
|
|
720
788
|
return (n * sxy - sx * sy) / denom;
|
|
721
789
|
}
|
|
722
790
|
function analyzeHeapTrend(samples) {
|
|
723
|
-
if (samples.length <
|
|
791
|
+
if (samples.length < MIN_SAMPLES2) return null;
|
|
724
792
|
const t0 = samples[0].at;
|
|
725
|
-
const segSize = Math.max(1, Math.ceil(samples.length /
|
|
793
|
+
const segSize = Math.max(1, Math.ceil(samples.length / FLOOR_SEGMENTS2));
|
|
726
794
|
const floorPoints = [];
|
|
727
795
|
for (let i = 0; i < samples.length; i += segSize) {
|
|
728
796
|
const seg = samples.slice(i, i + segSize);
|
|
@@ -731,13 +799,13 @@ function analyzeHeapTrend(samples) {
|
|
|
731
799
|
const mid = seg[Math.floor(seg.length / 2)];
|
|
732
800
|
floorPoints.push({ x: (mid.at - t0) / 6e4, y: min });
|
|
733
801
|
}
|
|
734
|
-
const slopeBytesPerMin =
|
|
802
|
+
const slopeBytesPerMin = slope2(floorPoints);
|
|
735
803
|
const spanMin = (samples[samples.length - 1].at - t0) / 6e4;
|
|
736
804
|
const projectedGrowth = slopeBytesPerMin * spanMin;
|
|
737
805
|
const recent = floorPoints.slice(Math.floor(floorPoints.length / 2));
|
|
738
|
-
const recentSlope = recent.length >= 2 ?
|
|
806
|
+
const recentSlope = recent.length >= 2 ? slope2(recent) : slopeBytesPerMin;
|
|
739
807
|
let classification = "stable";
|
|
740
|
-
const sustained = projectedGrowth >=
|
|
808
|
+
const sustained = projectedGrowth >= MIN_NET_GROWTH2 && recentSlope >= GROWING_SLOPE;
|
|
741
809
|
if (sustained) {
|
|
742
810
|
classification = slopeBytesPerMin >= LEAK_SLOPE && recentSlope >= LEAK_SLOPE ? "leak-suspected" : "growing";
|
|
743
811
|
}
|
|
@@ -1066,8 +1134,10 @@ function createSelfProfilingCollector() {
|
|
|
1066
1134
|
};
|
|
1067
1135
|
}
|
|
1068
1136
|
export {
|
|
1137
|
+
BUFFER_CAP,
|
|
1069
1138
|
analyzeFrames,
|
|
1070
1139
|
analyzeHeapTrend,
|
|
1140
|
+
analyzeLeakTrend,
|
|
1071
1141
|
attachLazyStack,
|
|
1072
1142
|
attributeLongTaskSignals,
|
|
1073
1143
|
attributeWindow,
|
|
@@ -1083,10 +1153,12 @@ export {
|
|
|
1083
1153
|
createSelfProfilingCollector,
|
|
1084
1154
|
createSourceMapResolver,
|
|
1085
1155
|
createWebVitalsCollector,
|
|
1156
|
+
detectCapabilities,
|
|
1086
1157
|
isSelfRequest,
|
|
1087
1158
|
isUserResource,
|
|
1088
1159
|
markSelfRequest,
|
|
1089
1160
|
parseStack,
|
|
1090
|
-
resolveFrame
|
|
1161
|
+
resolveFrame,
|
|
1162
|
+
unsupportedKinds
|
|
1091
1163
|
};
|
|
1092
1164
|
//# sourceMappingURL=index.js.map
|