@memlab/core 1.0.6 → 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.
Files changed (78) hide show
  1. package/dist/index.d.ts +17 -0
  2. package/dist/index.js +17 -0
  3. package/dist/lib/Config.d.ts +6 -2
  4. package/dist/lib/Config.js +13 -2
  5. package/dist/lib/FileManager.d.ts +2 -0
  6. package/dist/lib/FileManager.js +1 -0
  7. package/dist/lib/HeapAnalyzer.d.ts +2 -2
  8. package/dist/lib/HeapAnalyzer.js +27 -60
  9. package/dist/lib/InternalValueSetter.d.ts +2 -0
  10. package/dist/lib/InternalValueSetter.js +2 -0
  11. package/dist/lib/Types.d.ts +363 -6
  12. package/dist/lib/Types.js +1 -0
  13. package/dist/lib/Utils.js +24 -1
  14. package/dist/lib/leak-filters/BaseLeakFilter.rule.d.ts +24 -0
  15. package/dist/lib/leak-filters/BaseLeakFilter.rule.js +22 -0
  16. package/dist/lib/leak-filters/LeakFilterRuleList.d.ts +13 -0
  17. package/dist/lib/leak-filters/LeakFilterRuleList.js +33 -0
  18. package/dist/lib/leak-filters/LeakObjectFilter.d.ts +19 -0
  19. package/dist/lib/leak-filters/LeakObjectFilter.js +36 -0
  20. package/dist/lib/leak-filters/rules/FilterByExternalFilter.rule.d.ts +19 -0
  21. package/dist/lib/leak-filters/rules/FilterByExternalFilter.rule.js +27 -0
  22. package/dist/lib/leak-filters/rules/FilterDetachedDOMElement.rule.d.ts +20 -0
  23. package/dist/lib/leak-filters/rules/FilterDetachedDOMElement.rule.js +40 -0
  24. package/dist/lib/leak-filters/rules/FilterHermesNode.rule.d.ts +16 -0
  25. package/dist/lib/leak-filters/rules/FilterHermesNode.rule.js +27 -0
  26. package/dist/lib/leak-filters/rules/FilterOverSizedNodeAsLeak.rule.d.ts +19 -0
  27. package/dist/lib/leak-filters/rules/FilterOverSizedNodeAsLeak.rule.js +27 -0
  28. package/dist/lib/leak-filters/rules/FilterStackTraceFrame.rule.d.ts +19 -0
  29. package/dist/lib/leak-filters/rules/FilterStackTraceFrame.rule.js +28 -0
  30. package/dist/lib/leak-filters/rules/FilterTrivialNode.rule.d.ts +20 -0
  31. package/dist/lib/leak-filters/rules/FilterTrivialNode.rule.js +33 -0
  32. package/dist/lib/leak-filters/rules/FilterUnmountedFiberNode.rule.d.ts +20 -0
  33. package/dist/lib/leak-filters/rules/FilterUnmountedFiberNode.rule.js +37 -0
  34. package/dist/trace-cluster/TraceBucket.d.ts +1 -0
  35. package/dist/trace-cluster/TraceBucket.js +12 -1
  36. package/package.json +1 -1
  37. package/dist/__tests__/parser/HeapParser.test.d.ts.map +0 -1
  38. package/dist/__tests__/parser/NodeHeap.test.d.ts.map +0 -1
  39. package/dist/__tests__/parser/StringNode.test.d.ts.map +0 -1
  40. package/dist/__tests__/parser/traverse/HeapNodeTraverse.test.d.ts.map +0 -1
  41. package/dist/__tests__/utils/utils.test.d.ts.map +0 -1
  42. package/dist/index.d.ts.map +0 -1
  43. package/dist/lib/BaseOption.d.ts.map +0 -1
  44. package/dist/lib/BrowserInfo.d.ts.map +0 -1
  45. package/dist/lib/Config.d.ts.map +0 -1
  46. package/dist/lib/Console.d.ts.map +0 -1
  47. package/dist/lib/Constant.d.ts.map +0 -1
  48. package/dist/lib/FileManager.d.ts.map +0 -1
  49. package/dist/lib/HeapAnalyzer.d.ts.map +0 -1
  50. package/dist/lib/HeapParser.d.ts.map +0 -1
  51. package/dist/lib/InternalValueSetter.d.ts.map +0 -1
  52. package/dist/lib/NodeHeap.d.ts.map +0 -1
  53. package/dist/lib/ProcessManager.d.ts.map +0 -1
  54. package/dist/lib/Serializer.d.ts.map +0 -1
  55. package/dist/lib/StringLoader.d.ts.map +0 -1
  56. package/dist/lib/Types.d.ts.map +0 -1
  57. package/dist/lib/Utils.d.ts.map +0 -1
  58. package/dist/lib/heap-data/HeapEdge.d.ts.map +0 -1
  59. package/dist/lib/heap-data/HeapLocation.d.ts.map +0 -1
  60. package/dist/lib/heap-data/HeapNode.d.ts.map +0 -1
  61. package/dist/lib/heap-data/HeapSnapshot.d.ts.map +0 -1
  62. package/dist/lib/heap-data/HeapStringNode.d.ts.map +0 -1
  63. package/dist/lib/heap-data/HeapUtils.d.ts.map +0 -1
  64. package/dist/logger/LeakClusterLogger.d.ts.map +0 -1
  65. package/dist/logger/LeakTraceDetailsLogger.d.ts.map +0 -1
  66. package/dist/modes/BaseMode.d.ts.map +0 -1
  67. package/dist/modes/InteractionTestMode.d.ts.map +0 -1
  68. package/dist/modes/MeasureMode.d.ts.map +0 -1
  69. package/dist/modes/RunningModes.d.ts.map +0 -1
  70. package/dist/paths/TraceFinder.d.ts.map +0 -1
  71. package/dist/trace-cluster/ClusterUtils.d.ts.map +0 -1
  72. package/dist/trace-cluster/ClusterUtilsHelper.d.ts.map +0 -1
  73. package/dist/trace-cluster/ClusteringHeuristics.d.ts.map +0 -1
  74. package/dist/trace-cluster/EvalutationMetric.d.ts.map +0 -1
  75. package/dist/trace-cluster/TraceBucket.d.ts.map +0 -1
  76. package/dist/trace-cluster/TraceElement.d.ts.map +0 -1
  77. package/dist/trace-cluster/strategies/TraceAsClusterStrategy.d.ts.map +0 -1
  78. package/dist/trace-cluster/strategies/TraceSimilarityStrategy.d.ts.map +0 -1
@@ -10,20 +10,32 @@
10
10
  import { ParsedArgs } from 'minimist';
11
11
  import type { LaunchOptions, Page } from 'puppeteer';
12
12
  import type { ErrorHandling, MemLabConfig } from './Config';
13
+ /** @internal */
13
14
  export declare type AnyValue = any;
15
+ /** @internal */
14
16
  export declare type RecordValue = string | number | boolean | null | RecordValue[] | {
15
17
  [key: string]: RecordValue;
16
18
  };
19
+ /** @internal */
17
20
  export declare type Nullable<T> = T | null;
21
+ /** @internal */
18
22
  export declare type Optional<T> = Nullable<T> | undefined;
23
+ /** @internal */
19
24
  export declare type AnyRecord = Record<string, RecordValue>;
25
+ /** @internal */
20
26
  export declare type AnyAyncFunction = (...args: AnyValue[]) => Promise<AnyValue>;
27
+ /** @internal */
21
28
  export declare type AnyFunction = (...args: AnyValue[]) => AnyValue;
29
+ /** @internal */
22
30
  export declare type AnyOptions = Record<string, unknown>;
31
+ /** @internal */
23
32
  export declare type UnusedOptions = Record<string, never>;
33
+ /** @internal */
24
34
  export declare type Command = [string, string[], AnyOptions];
25
35
  export declare type Predicator<T> = (node: T) => boolean;
26
- export declare type HeapNodeIdSet = Set<IHeapNode['id']>;
36
+ /** @internal */
37
+ export declare type HeapNodeIdSet = Set<number>;
38
+ /** @internal */
27
39
  export declare type HaltOrThrowOptions = {
28
40
  printErrorBeforeHalting?: boolean;
29
41
  errorHandling?: ErrorHandling;
@@ -31,10 +43,12 @@ export declare type HaltOrThrowOptions = {
31
43
  secondaryMessageToPrint?: string;
32
44
  printCallback?: () => void;
33
45
  };
46
+ /** @internal */
34
47
  export declare type CLIOptions = {
35
48
  cliArgs: ParsedArgs;
36
49
  configFromOptions?: AnyRecord;
37
50
  };
51
+ /** @internal */
38
52
  export declare type XvfbType = {
39
53
  start: (callback: (error: Error) => AnyValue | null) => void;
40
54
  stop: (callback: (error: Error) => AnyValue | null) => void;
@@ -42,6 +56,7 @@ export declare type XvfbType = {
42
56
  stopSync: () => void;
43
57
  display: () => string;
44
58
  };
59
+ /** @internal */
45
60
  export declare type CLIArgs = {
46
61
  verbose: boolean;
47
62
  app: string;
@@ -71,11 +86,11 @@ export declare type CLIArgs = {
71
86
  'local-puppeteer': boolean;
72
87
  'snapshot-dir': string;
73
88
  };
74
- declare type CookieItem = {
89
+ export declare type Cookies = Array<{
75
90
  name: string;
76
91
  value: string;
77
- };
78
- export declare type Cookies = Array<CookieItem>;
92
+ }>;
93
+ /** @internal */
79
94
  export interface IE2EScenarioSynthesizer {
80
95
  getAppName(): string;
81
96
  getOrigin(): Nullable<string>;
@@ -99,9 +114,11 @@ export interface IE2EScenarioSynthesizer {
99
114
  getExtraOperationsForStep(_stepInfo: E2EStepInfo): E2EOperation[];
100
115
  synthesis(baseline: string, target: string, intermediates: string[], options: E2ESynthesizerOptions): IE2EScenarioVisitPlan;
101
116
  }
117
+ /** @internal */
102
118
  export interface E2EScenarioSynthesizerConstructor {
103
119
  new (config: Config): IE2EScenarioSynthesizer;
104
120
  }
121
+ /** @internal */
105
122
  export interface IRunningMode {
106
123
  setConfig(config: Config): void;
107
124
  beforeRunning(visitPlan: IE2EScenarioVisitPlan): void;
@@ -117,37 +134,337 @@ export interface IRunningMode {
117
134
  getAdditionalMetrics(page: Page, tabInfo?: E2EStepInfo): Promise<E2EStepInfo['metrics']>;
118
135
  postProcessData(visitPlan: IE2EScenarioVisitPlan): void;
119
136
  }
137
+ /** @internal */
120
138
  export declare type Config = MemLabConfig;
139
+ /** @internal */
121
140
  export declare type QuickExperiment = {
122
141
  universe: string;
123
142
  experiment: string;
124
143
  group: string;
125
144
  };
145
+ /**
146
+ * The type for defining custom leak-filtering logic.
147
+ * * **Examples**:
148
+ * ```typescript
149
+ * const scenario = {
150
+ *
151
+ * };
152
+ *
153
+ * let map = Object.create(null);
154
+ * const beforeLeakFilter = (snapshot: IHeapSnapshot, _leakedNodeIds: HeapNodeIdSet): void => {
155
+ * map = initializeMapUsingSnapshot(snapshot);
156
+ * };
157
+ *
158
+ * // duplicated string with size > 1KB as memory leak
159
+ * const leakFilter = (node: IHeapNode): boolean => {
160
+ * if (node.type !== 'string' || node.retainedSize < 1000) {
161
+ * return false;
162
+ * }
163
+ * const str = utils.getStringNodeValue(node);
164
+ * return map[str] > 1;
165
+ * };
166
+ *
167
+ * export default {beforeLeakFilter, leakFilter};
168
+ * ```
169
+ */
126
170
  export interface ILeakFilter {
127
171
  beforeLeakFilter?: InitLeakFilterCallback;
128
172
  leakFilter: LeakFilterCallback;
129
173
  }
174
+ /**
175
+ * Lifecycle function callback that is invoked initially once before calling any
176
+ * leak filter function.
177
+ *
178
+ * @param snaphost - heap snapshot see {@link IHeapSnapshot}
179
+ * @param leakedNodeIds - the set of leaked object (node) ids.
180
+ */
130
181
  export declare type InitLeakFilterCallback = (snapshot: IHeapSnapshot, leakedNodeIds: HeapNodeIdSet) => void;
182
+ /**
183
+ * Callback that can be used to define a logic to filter the
184
+ * leaked objects. The callback is only called for every node
185
+ * allocated but not released from the target interaction
186
+ * in the heap snapshot.
187
+ *
188
+ * @param node - the node that is kept alive in the memory in the heap snapshot
189
+ * @param snapshot - the snapshot of target interaction
190
+ * @param leakedNodeIds - the set of leaked node ids
191
+ *
192
+ * @returns the value indicating whether the given node in the snapshot
193
+ * should be considered as leaked.
194
+ * * **Examples**:
195
+ * ```javascript
196
+ * // any node in the heap snapshot that is greater than 1MB
197
+ * function leakFilter(node, _snapshot, _leakedNodeIds) {
198
+ * return node.retainedSize > 1000000;
199
+ * };
200
+ * ```
201
+ */
131
202
  export declare type LeakFilterCallback = (node: IHeapNode, snapshot: IHeapSnapshot, leakedNodeIds: HeapNodeIdSet) => boolean;
203
+ /**
204
+ * The callback defines browser interactions which are
205
+ * used by memlab to interact with the web app under test.
206
+ */
132
207
  export declare type InteractionsCallback = (page: Page, args?: OperationArgs) => Promise<void>;
208
+ /**
209
+ * Test scenario specifies how you want a E2E test to interact with a web browser.
210
+ * The test scenario can be saved as a `.js` file and passed to the `memlab
211
+ * run --scenario` command:
212
+ * ```javascript
213
+ * // save as test.js and use in terminal:
214
+ * // $ memlab run --scenario test.js
215
+ *
216
+ * module.exports = {
217
+ * url: () => 'https://www.npmjs.com/',
218
+ * action: async () => ... ,
219
+ * back: async () => ... ,
220
+ * };
221
+ * ```
222
+ *
223
+ * The test scenario instance can also be passed to the
224
+ * [`run` API](../modules/api_src#run) exported by `@memlab/api`.
225
+ * ```typescript
226
+ * const {run} = require('@memlab/api');
227
+ *
228
+ * (async function () {
229
+ * const scenario = {
230
+ * url: () => 'https://www.facebook.com',
231
+ * action: async () => ... ,
232
+ * back: async () => ... ,
233
+ * };
234
+ * const leaks = await run({scenario});
235
+ * })();
236
+ * ```
237
+ */
133
238
  export interface IScenario {
239
+ /** @internal */
134
240
  name?: () => string;
241
+ /** @internal */
135
242
  app?: () => string;
136
- cookies?: () => CookieItem[];
243
+ /**
244
+ * If the page you are running memlab against requires authentication or
245
+ * specific cookie(s) to be set, you can pass them as
246
+ * a list of <name, value> pairs.
247
+ * @returns cookie list
248
+ * * **Examples**:
249
+ * ```typescript
250
+ * const scenario = {
251
+ * url: () => 'https://www.facebook.com/',
252
+ * cookies: () => [
253
+ * {"name":"cm_j","value":"none"},
254
+ * {"name":"datr","value":"yJvIY..."},
255
+ * {"name":"c_user","value":"8917..."},
256
+ * {"name":"xs","value":"95:9WQ..."},
257
+ * // ...
258
+ * ],
259
+ * };
260
+ * ```
261
+ */
262
+ cookies?: () => Cookies;
263
+ /**
264
+ * String value of the initial url of the page.
265
+ *
266
+ * @returns the string value of the initial url
267
+ * * **Examples**:
268
+ * ```typescript
269
+ * const scenario = {
270
+ * url: () => 'https://www.npmjs.com/',
271
+ * };
272
+ * ```
273
+ * If a test scenario only specifies the `url` callback (without the `action`
274
+ * callback), memlab will try to detect memory leaks from the initial page
275
+ * load. All objects allocated by the initial page load will be candidates
276
+ * for memory leak filtering.
277
+ */
137
278
  url: () => string;
279
+ /**
280
+ * `action` is the callback function that defines the interaction
281
+ * where you want to trigger memory leaks after the initial page load.
282
+ * All JS objects in browser allocated by the browser interactions triggered
283
+ * from the `action` callback will be candidates for memory leak filtering.
284
+ *
285
+ * * **Parameters**:
286
+ * * page: `Page` | the puppeteer [`Page`](https://pptr.dev/api/puppeteer.page)
287
+ * object, which provides APIs to interact with the web browser
288
+ *
289
+ * * **Examples**:
290
+ * ```typescript
291
+ * const scenario = {
292
+ * url: () => 'https://www.npmjs.com/',
293
+ * action: async (page) => {
294
+ * await page.click('a[href="/link"]');
295
+ * },
296
+ * back: async (page) => {
297
+ * await page.click('a[href="/back"]');
298
+ * },
299
+ * }
300
+ * ```
301
+ * Note: always clean up external puppeteer references to JS objects
302
+ * in the browser context.
303
+ * ```typescript
304
+ * const scenario = {
305
+ * url: () => 'https://www.npmjs.com/',
306
+ * action: async (page) => {
307
+ * const elements = await page.$x("//button[contains(., 'Text in Button')]");
308
+ * const [button] = elements;
309
+ * if (button) {
310
+ * await button.click();
311
+ * }
312
+ * // dispose external references to JS objects in browser context
313
+ * await promise.all(elements.map(e => e.dispose()));
314
+ * },
315
+ * back: async (page) => ... ,
316
+ * }
317
+ ```
318
+ */
138
319
  action?: InteractionsCallback;
320
+ /**
321
+ * `back` is the callback function that specifies how memlab should
322
+ * back/revert the `action` callback. Think of it as an undo action.
323
+ *
324
+ * * **Examples**:
325
+ * ```typescript
326
+ * const scenario = {
327
+ * url: () => 'https://www.npmjs.com/',
328
+ * action: async (page) => {
329
+ * await page.click('a[href="/link"]');
330
+ * },
331
+ * back: async (page) => {
332
+ * await page.click('a[href="/back"]');
333
+ * },
334
+ * }
335
+ * ```
336
+ * Check out [this page](/docs/how-memlab-works) on why
337
+ * memlab needs to undo/revert the `action` callback.
338
+ */
139
339
  back?: InteractionsCallback;
340
+ /**
341
+ * Specifies how many **extra** `action` and `back` actions performed by memlab.
342
+ * * **Examples**:
343
+ * ```typescript
344
+ * module.exports = {
345
+ * url: () => ... ,
346
+ * action: async (page) => ... ,
347
+ * back: async (page) => ... ,
348
+ * // browser interaction: two additional [ action -> back ]
349
+ * // init-load -> action -> back -> action -> back -> action -> back
350
+ * repeat: () => 2,
351
+ * };
352
+ * ```
353
+ */
140
354
  repeat?: () => number;
355
+ /**
356
+ * Optional callback function that checks if the web page is loaded
357
+ * after for initial page loading and subsequent browser interactions.
358
+ *
359
+ * If this callback is not provided, memlab by default
360
+ * considers a navigation to be finished when there are no network
361
+ * connections for at least 500ms.
362
+ *
363
+ * * **Parameters**:
364
+ * * page: `Page` | the puppeteer [`Page`](https://pptr.dev/api/puppeteer.page)
365
+ * object, which provides APIs to interact with the web browser
366
+ * * **Returns**: a boolean value, if it returns `true`, memlab will consider
367
+ * the navigation completes, if it returns `false`, memlab will keep calling
368
+ * this callback until it returns `true`. This is an async callback, you can
369
+ * also `await` and returns `true` until some async logic is resolved.
370
+ * * **Examples**:
371
+ * ```typescript
372
+ * module.exports = {
373
+ * url: () => ... ,
374
+ * action: async (page) => ... ,
375
+ * back: async (page) => ... ,
376
+ * isPageLoaded: async (page) => {
377
+ * await page.waitForNavigation({
378
+ * // consider navigation to be finished when there are
379
+ * // no more than 2 network connections for at least 500 ms.
380
+ * waitUntil: 'networkidle2',
381
+ * // Maximum navigation time in milliseconds
382
+ * timeout: 5000,
383
+ * });
384
+ * return true;
385
+ * },
386
+ * };
387
+ * ```
388
+ */
141
389
  isPageLoaded?: CheckPageLoadCallback;
390
+ /**
391
+ * Lifecycle function callback that is invoked initially once before
392
+ * the subsequent `leakFilter` function calls. This callback could
393
+ * be used to initialize some data stores or to do some one-off
394
+ * preprocessings.
395
+ *
396
+ * * **Parameters**:
397
+ * * snapshot: `IHeapSnapshot` | the final heap snapshot taken after
398
+ * all browser interactions are done.
399
+ * Check out {@link IHeapSnapshot} for more APIs that queries the heap snapshot.
400
+ * * leakedNodeIds: `Set<number>` | the set of ids of all JS heap objects
401
+ * allocated by the `action` call but not released after the `back` call
402
+ * in browser.
403
+ *
404
+ * * **Examples**:
405
+ * ```typescript
406
+ * module.exports = {
407
+ * url: () => ... ,
408
+ * action: async (page) => ... ,
409
+ * back: async (page) => ... ,
410
+ * beforeLeakFilter: (snapshot, leakedNodeIds) {
411
+ * // initialize some data stores
412
+ * },
413
+ * };
414
+ * ```
415
+ */
142
416
  beforeLeakFilter?: InitLeakFilterCallback;
417
+ /**
418
+ * This callback that defines how you want to filter out the
419
+ * leaked objects. The callback is called for every node (JS heap
420
+ * object in browser) allocated by the `action` callback, but not
421
+ * released after the `back` callback. Those objects could be caches
422
+ * that are retained in memory on purpose, or they are memory leaks.
423
+ *
424
+ * This optional callback allows you to define your own algorithm
425
+ * to cherry pick memory leaks for specific JS program under test.
426
+ *
427
+ * If this optional callback is not defined, memlab will use its
428
+ * built-in leak filter, which considers detached DOM elements
429
+ * and unmounted Fiber nodes (detached from React Fiber tree) as
430
+ * memory leaks.
431
+ *
432
+ * * **Parameters**:
433
+ * * node: `IHeapNode` | one of the heap object allocated but not released.
434
+ * * snapshot: `IHeapSnapshot` | the final heap snapshot taken after
435
+ * all browser interactions are done.
436
+ * Check out {@link IHeapSnapshot} for more APIs that queries the heap snapshot.
437
+ * * leakedNodeIds: `Set<number>` | the set of ids of all JS heap objects
438
+ * allocated by the `action` call but not released after the `back` call
439
+ * in browser.
440
+ *
441
+ * * **Returns**: the boolean value indicating whether the given node in
442
+ * the snapshot should be considered as leaked.
443
+ *
444
+ * * **Examples**:
445
+ * ```typescript
446
+ * module.exports = {
447
+ * url: () => ... ,
448
+ * action: async (page) => ... ,
449
+ * back: async (page) => ... ,
450
+ * leakFilter(node, snapshot, leakedNodeIds) {
451
+ * // any unreleased node (JS heap object) with 1MB+
452
+ * // retained size is considered a memory leak
453
+ * return node.retainedSize > 1000000;
454
+ * },
455
+ * };
456
+ * ```
457
+ */
143
458
  leakFilter?: LeakFilterCallback;
144
459
  }
460
+ /** @internal */
145
461
  export declare type LeakTracePathItem = {
146
462
  node?: IHeapNode;
147
463
  edge?: IHeapEdge;
148
464
  next?: LeakTracePathItem;
149
465
  edgeRetainSize?: number;
150
466
  };
467
+ /** @internal */
151
468
  export declare type TraceCluster = {
152
469
  id?: number;
153
470
  path: LeakTracePathItem;
@@ -157,11 +474,13 @@ export declare type TraceCluster = {
157
474
  leakedNodeIds?: HeapNodeIdSet;
158
475
  clusterMetaInfo?: TraceClusterMetaInfo;
159
476
  };
477
+ /** @internal */
160
478
  export declare type TraceClusterDiff = {
161
479
  staleClusters: TraceCluster[];
162
480
  clustersToAdd: TraceCluster[];
163
481
  allClusters: TraceCluster[][];
164
482
  };
483
+ /** @internal */
165
484
  export declare type LeakTraceElement = {
166
485
  kind: string;
167
486
  id?: number;
@@ -169,12 +488,15 @@ export declare type LeakTraceElement = {
169
488
  name_or_index?: string | number;
170
489
  type: string;
171
490
  };
491
+ /** @internal */
172
492
  export declare type LeakTrace = LeakTraceElement[];
493
+ /** @internal */
173
494
  export declare type TraceDiff = {
174
495
  staleClusters: LeakTrace[];
175
496
  clustersToAdd: LeakTrace[];
176
497
  allClusters: LeakTrace[][];
177
498
  };
499
+ /** @internal */
178
500
  export declare type TraceClusterMetaInfo = {
179
501
  cluster_id: number;
180
502
  creation_time: number;
@@ -189,14 +511,17 @@ export declare type TraceClusterMetaInfo = {
189
511
  leak_trace_handle?: string;
190
512
  meta_data: string;
191
513
  };
514
+ /** @internal */
192
515
  export interface E2EInteraction {
193
516
  kind: string;
194
517
  timeout?: number;
195
518
  }
519
+ /** @internal */
196
520
  export declare type E2EOperation = E2EInteraction & {
197
521
  selector: string;
198
522
  act(page: Page, opArgs?: OperationArgs): Promise<void>;
199
523
  };
524
+ /** @internal */
200
525
  export declare type E2ESynthesizerOptions = {
201
526
  name?: string;
202
527
  type?: string;
@@ -212,11 +537,17 @@ export declare type E2ESynthesizerOptions = {
212
537
  gk_enable?: string[];
213
538
  gk_disable?: string[];
214
539
  };
540
+ /** @internal */
215
541
  export interface IDataBuilder {
216
542
  className: string;
217
543
  state: Record<string, AnyValue>;
218
544
  }
545
+ /**
546
+ * Callback function to provide if the page is loaded.
547
+ * @param page - puppeteer's [Page](https://pptr.dev/api/puppeteer.page/) object.
548
+ */
219
549
  export declare type CheckPageLoadCallback = (page: Page) => Promise<boolean>;
550
+ /** @internal */
220
551
  export interface IE2EScenarioVisitPlan {
221
552
  name: string;
222
553
  appName: string;
@@ -230,6 +561,7 @@ export interface IE2EScenarioVisitPlan {
230
561
  dataBuilder: Optional<IDataBuilder>;
231
562
  isPageLoaded?: CheckPageLoadCallback;
232
563
  }
564
+ /** @internal */
233
565
  export declare type OperationArgs = {
234
566
  isPageLoaded?: CheckPageLoadCallback;
235
567
  showProgress?: boolean;
@@ -240,6 +572,7 @@ export declare type OperationArgs = {
240
572
  warmup?: boolean;
241
573
  noWaitAfterPageLoad?: boolean;
242
574
  };
575
+ /** @internal */
243
576
  export interface IE2EStepBasic {
244
577
  name: string;
245
578
  url: string;
@@ -252,6 +585,7 @@ export interface IE2EStepBasic {
252
585
  interactions: E2EOperation | Array<E2EOperation | InteractionsCallback>;
253
586
  postInteractions?: E2EOperation | Array<E2EOperation | InteractionsCallback>;
254
587
  }
588
+ /** @internal */
255
589
  export declare type E2EStepInfo = IE2EStepBasic & {
256
590
  snapshot: boolean;
257
591
  screenshot: boolean;
@@ -260,6 +594,7 @@ export declare type E2EStepInfo = IE2EStepBasic & {
260
594
  delay?: number;
261
595
  metrics: Record<string, number>;
262
596
  };
597
+ /** @internal */
263
598
  export interface IBrowserInfo {
264
599
  _browserVersion: string;
265
600
  _puppeteerConfig: LaunchOptions;
@@ -353,16 +688,27 @@ export interface IHeapNodes {
353
688
  forEach(callback: (node: IHeapNode, index: number) => void | boolean): void;
354
689
  forEachTraceable(callback: (node: IHeapNode, index: number) => void | boolean): void;
355
690
  }
691
+ /** @internal */
356
692
  export declare type HeapNodeFields = string[];
693
+ /** @internal */
357
694
  export declare type HeapNodeTypes = string[];
695
+ /** @internal */
358
696
  export declare type RawHeapNodeTypes = Array<HeapNodeTypes | string>;
697
+ /** @internal */
359
698
  export declare type HeapEdgeFields = string[];
699
+ /** @internal */
360
700
  export declare type HeapEdgeTypes = string[] | string;
701
+ /** @internal */
361
702
  export declare type RawHeapEdgeTypes = Array<HeapEdgeTypes | string>;
703
+ /** @internal */
362
704
  export declare type HeapTraceFunctionInfoFields = string[];
705
+ /** @internal */
363
706
  export declare type HeapTraceNodeFields = string[];
707
+ /** @internal */
364
708
  export declare type HeapSampleFields = string[];
709
+ /** @internal */
365
710
  export declare type HeapLocationFields = string[];
711
+ /** @internal */
366
712
  export declare type HeapSnapshotMeta = {
367
713
  node_fields: HeapNodeFields;
368
714
  node_types: RawHeapNodeTypes;
@@ -373,12 +719,14 @@ export declare type HeapSnapshotMeta = {
373
719
  sample_fields: HeapSampleFields;
374
720
  location_fields: HeapLocationFields;
375
721
  };
722
+ /** @internal */
376
723
  export declare type HeapSnapshotInfo = {
377
724
  meta: HeapSnapshotMeta;
378
725
  node_count: number;
379
726
  edge_count: number;
380
727
  trace_function_count: number;
381
728
  };
729
+ /** @internal */
382
730
  export declare type RawHeapSnapshot = {
383
731
  snapshot: HeapSnapshotInfo;
384
732
  nodes: number[];
@@ -389,12 +737,15 @@ export declare type RawHeapSnapshot = {
389
737
  locations: number[];
390
738
  strings: string[];
391
739
  };
740
+ /** @internal */
392
741
  export interface ISerializedInfo {
393
742
  [key: string]: string | number | boolean | ISerializedInfo;
394
743
  }
744
+ /** @internal */
395
745
  export declare type NumericDictionary = {
396
746
  [index: number]: number;
397
747
  };
748
+ /** @internal */
398
749
  export interface IOveralHeapInfo {
399
750
  fiberNodeSize: number;
400
751
  regularFiberNodeSize: number;
@@ -402,31 +753,37 @@ export interface IOveralHeapInfo {
402
753
  alternateFiberNodeSize: number;
403
754
  error: number;
404
755
  }
756
+ /** @internal */
405
757
  export interface IOveralLeakInfo extends Partial<IOveralHeapInfo> {
406
758
  leakedSize: number;
407
759
  leakedFiberNodeSize: number;
408
760
  leakedAlternateFiberNodeSize: number;
409
761
  }
762
+ /** @internal */
410
763
  export interface IMemoryAnalystOptions {
411
764
  snapshotDir?: string;
412
765
  minSnapshots?: number;
413
766
  }
767
+ /** @internal */
414
768
  export interface IMemoryAnalystSnapshotDiff {
415
769
  leakedHeapNodeIdSet: HeapNodeIdSet;
416
770
  snapshot: IHeapSnapshot;
417
771
  listOfLeakedHeapNodeIdSet: Array<HeapNodeIdSet>;
418
772
  }
773
+ /** @internal */
419
774
  export interface IMemoryAnalystHeapNodeLeakSummary extends Pick<IHeapNode, 'name' | 'type' | 'retainedSize'> {
420
775
  count: number;
421
776
  }
777
+ /** @internal */
422
778
  export interface IMemoryAnalystHeapNodeReferrenceStat {
423
779
  numberOfEdgesToNode: number;
424
780
  source: IHeapNode;
425
781
  edge: IHeapEdge;
426
782
  }
783
+ /** @internal */
427
784
  export interface IClusterStrategy {
428
785
  diffTraces: (newLeakTraces: LeakTrace[], existingLeakTraces: LeakTrace[]) => TraceDiff;
429
786
  }
787
+ /** @internal */
430
788
  export declare type ErrorWithMessage = Pick<Error, 'message'>;
431
- export {};
432
789
  //# sourceMappingURL=Types.d.ts.map
package/dist/lib/Types.js CHANGED
@@ -8,4 +8,5 @@
8
8
  * @emails oncall+ws_labs
9
9
  * @format
10
10
  */
11
+ /* eslint-disable @typescript-eslint/no-explicit-any */
11
12
  Object.defineProperty(exports, "__esModule", { value: true });
package/dist/lib/Utils.js CHANGED
@@ -356,7 +356,7 @@ function getNodesIdSet(snapshot) {
356
356
  return set;
357
357
  }
358
358
  // given a set of nodes S, return a subset S' where
359
- // no nodes are dominaeted by nodes in S
359
+ // no nodes are dominated by nodes in S
360
360
  function getConditionalDominatorIds(ids, snapshot, condCb) {
361
361
  const dominatorIds = new Set();
362
362
  // set all node ids
@@ -863,6 +863,10 @@ function isInterestingPath(p) {
863
863
  if (Config_1.default.hideBrowserLeak && shadowRootRetainsDetachedElement(p)) {
864
864
  return false;
865
865
  }
866
+ // if the path has pattern: StyleEngine -> InternalNode -> DetachedElement
867
+ if (Config_1.default.hideBrowserLeak && styleEngineRetainsDetachedElement(p)) {
868
+ return false;
869
+ }
866
870
  return true;
867
871
  }
868
872
  // return true if the heap node represents JS object or closure
@@ -923,6 +927,25 @@ function shadowRootRetainsDetachedElement(path) {
923
927
  // check if the node is a detached element
924
928
  return !!p && isDetachedDOMNode(p.node);
925
929
  }
930
+ // check if the path has pattern: StyleEngine -> InternalNode -> DetachedElement
931
+ function styleEngineRetainsDetachedElement(path) {
932
+ let p = path;
933
+ // find the StyleEngine
934
+ while (p && p.node && p.node.name !== 'StyleEngine') {
935
+ p = p.next;
936
+ if (!p) {
937
+ return false;
938
+ }
939
+ }
940
+ p = p.next;
941
+ // StyleEngine is not poining to InternalNode
942
+ if (!p || !p.node || p.node.name !== 'InternalNode') {
943
+ return false;
944
+ }
945
+ p = p.next;
946
+ // check if the InternalNode is pointing to a detached element
947
+ return !!p && isDetachedDOMNode(p.node);
948
+ }
926
949
  function pathHasDetachedHTMLNode(path) {
927
950
  if (!path) {
928
951
  return false;
@@ -0,0 +1,24 @@
1
+ /**
2
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
3
+ *
4
+ * This source code is licensed under the MIT license found in the
5
+ * LICENSE file in the root directory of this source tree.
6
+ *
7
+ * @emails oncall+ws_labs
8
+ * @format
9
+ */
10
+ import type { MemLabConfig } from '../Config';
11
+ import type { HeapNodeIdSet, IHeapNode, IHeapSnapshot } from '../Types';
12
+ /**
13
+ * Every leak object filter rule needs to give a label
14
+ * to each object passed to the filter
15
+ */
16
+ export declare enum LeakDecision {
17
+ LEAK = "leak",
18
+ MAYBE_LEAK = "maybe-leak",
19
+ NOT_LEAK = "not-leak"
20
+ }
21
+ export interface ILeakObjectFilterRule {
22
+ filter(config: MemLabConfig, node: IHeapNode, snapshot: IHeapSnapshot, leakedNodeIds: HeapNodeIdSet): LeakDecision;
23
+ }
24
+ //# sourceMappingURL=BaseLeakFilter.rule.d.ts.map