@memlab/core 1.1.4 → 1.1.7

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 (34) hide show
  1. package/dist/__tests__/parser/HeapParser.test.js +2 -2
  2. package/dist/__tests__/parser/NodeHeap.test.js +5 -5
  3. package/dist/__tests__/parser/StringNode.test.js +1 -1
  4. package/dist/__tests__/parser/traverse/HeapNodeTraverse.test.js +2 -2
  5. package/dist/index.d.ts +5 -1
  6. package/dist/index.js +22 -2
  7. package/dist/lib/Config.d.ts +16 -9
  8. package/dist/lib/Config.js +15 -0
  9. package/dist/lib/FileManager.js +4 -2
  10. package/dist/lib/HeapAnalyzer.js +25 -9
  11. package/dist/lib/NodeHeap.d.ts +52 -9
  12. package/dist/lib/NodeHeap.js +72 -21
  13. package/dist/lib/PackageInfoLoader.d.ts +7 -0
  14. package/dist/lib/PackageInfoLoader.js +66 -0
  15. package/dist/lib/Serializer.js +48 -25
  16. package/dist/lib/Types.d.ts +119 -35
  17. package/dist/lib/Utils.js +24 -9
  18. package/dist/lib/heap-data/HeapSnapshot.d.ts +1 -0
  19. package/dist/lib/heap-data/HeapSnapshot.js +3 -30
  20. package/dist/lib/heap-data/HeapStringNode.js +2 -0
  21. package/dist/lib/heap-data/MemLabTagStore.d.ts +23 -0
  22. package/dist/lib/heap-data/MemLabTagStore.js +110 -0
  23. package/dist/trace-cluster/TraceBucket.js +6 -1
  24. package/dist/trace-cluster/strategies/MLTraceSimilarityStrategy.d.ts +15 -0
  25. package/dist/trace-cluster/strategies/MLTraceSimilarityStrategy.js +61 -0
  26. package/dist/trace-cluster/strategies/machine-learning/DistanceMatrix.d.ts +11 -0
  27. package/dist/trace-cluster/strategies/machine-learning/DistanceMatrix.js +54 -0
  28. package/dist/trace-cluster/strategies/machine-learning/HAC.d.ts +17 -0
  29. package/dist/trace-cluster/strategies/machine-learning/HAC.js +122 -0
  30. package/dist/trace-cluster/strategies/machine-learning/Ngram.d.ts +11 -0
  31. package/dist/trace-cluster/strategies/machine-learning/Ngram.js +22 -0
  32. package/dist/trace-cluster/strategies/machine-learning/TfidfVectorizer.d.ts +38 -0
  33. package/dist/trace-cluster/strategies/machine-learning/TfidfVectorizer.js +144 -0
  34. package/package.json +1 -1
@@ -99,7 +99,7 @@ function JSONifyDetachedHTMLElement(node, args, options) {
99
99
  // options for elem.__reactProps$xxx
100
100
  const propsOptions = Object.assign({}, options);
101
101
  propsOptions.forceJSONifyDepth = 1;
102
- for (const edge of node.references) {
102
+ iterateSelectedEdges(node, (edge) => {
103
103
  const key = JSONifyEdgeNameAndType(edge);
104
104
  if (Utils_1.default.isReactFiberEdge(edge)) {
105
105
  info[key] = JSONifyNode(edge.toNode, args, fiberOptions);
@@ -110,7 +110,8 @@ function JSONifyDetachedHTMLElement(node, args, options) {
110
110
  else {
111
111
  info[key] = JSONifyNodeInShort(edge.toNode);
112
112
  }
113
- }
113
+ return null;
114
+ });
114
115
  return info;
115
116
  }
116
117
  function calculateReturnTrace(node, cache) {
@@ -128,15 +129,16 @@ function calculateReturnTrace(node, cache) {
128
129
  const objectNodeUsefulProps = new Set(['_context']);
129
130
  function JSONifyNodeOneLevel(node) {
130
131
  const info = Object.create(null);
131
- for (const edge of node.references) {
132
+ iterateSelectedEdges(node, (edge) => {
132
133
  const key = JSONifyEdgeNameAndType(edge);
133
134
  info[key] = JSONifyNodeShallow(edge.toNode);
134
- }
135
+ return null;
136
+ });
135
137
  return info;
136
138
  }
137
139
  function JSONifyNodeShallow(node) {
138
140
  const info = Object.create(null);
139
- for (const edge of node.references) {
141
+ iterateSelectedEdges(node, (edge) => {
140
142
  const key = JSONifyEdgeNameAndType(edge);
141
143
  if (objectNodeUsefulProps.has(edge.name_or_index)) {
142
144
  info[key] = JSONifyNodeShallow(edge.toNode);
@@ -144,7 +146,8 @@ function JSONifyNodeShallow(node) {
144
146
  else {
145
147
  info[key] = JSONifyNodeInShort(edge.toNode);
146
148
  }
147
- }
149
+ return null;
150
+ });
148
151
  return info;
149
152
  }
150
153
  const fiberNodeUsefulProps = new Set([
@@ -154,15 +157,17 @@ const fiberNodeUsefulProps = new Set([
154
157
  ]);
155
158
  function JSONifyFiberNodeShallow(node) {
156
159
  const info = Object.create(null);
157
- for (const edge of node.references) {
160
+ iterateSelectedEdges(node, (edge) => {
158
161
  const key = JSONifyEdgeNameAndType(edge);
159
162
  if (fiberNodeUsefulProps.has(edge.name_or_index) &&
160
163
  Utils_1.default.isObjectNode(edge.toNode)) {
161
164
  info[key] = JSONifyNodeShallow(edge.toNode);
162
- continue;
163
165
  }
164
- info[key] = JSONifyNodeInShort(edge.toNode);
165
- }
166
+ else {
167
+ info[key] = JSONifyNodeInShort(edge.toNode);
168
+ }
169
+ return null;
170
+ });
166
171
  return info;
167
172
  }
168
173
  // calculate the summary of return chain of the FiberNode
@@ -187,7 +192,9 @@ function JSONifyFiberNodeReturnTrace(node, args, options) {
187
192
  }
188
193
  const parentInfo = getNodeNameInJSON(parent, args);
189
194
  key = `${key}: --return (property)---> ${parentInfo}`;
190
- const info = JSONifyFiberNodeShallow(parent);
195
+ const info = Config_1.default.includeObjectInfoInTraceReturnChain
196
+ ? JSONifyFiberNodeShallow(parent)
197
+ : Object.create(null);
191
198
  trace[key] = info;
192
199
  }
193
200
  return trace;
@@ -206,25 +213,27 @@ function JSONifyFiberNode(node, args, options) {
206
213
  propsOptions.forceJSONifyDepth = 1;
207
214
  }
208
215
  propsOptions.forceJSONifyDepth--;
209
- for (const edge of node.references) {
216
+ iterateSelectedEdges(node, (edge) => {
210
217
  const key = JSONifyEdgeNameAndType(edge);
211
218
  info[key] =
212
- propsOptions.forceJSONifyDepth >= 1
219
+ propsOptions.forceJSONifyDepth && propsOptions.forceJSONifyDepth >= 1
213
220
  ? JSONifyNode(edge.toNode, args, propsOptions)
214
221
  : JSONifyNodeInShort(edge.toNode);
215
- }
222
+ return null;
223
+ });
216
224
  return info;
217
225
  }
218
226
  function JSONifyClosure(node, args, options) {
219
227
  const info = Object.create(null);
220
- for (const edge of node.references) {
228
+ iterateSelectedEdges(node, (edge) => {
221
229
  if (edge.name_or_index === 'shared' ||
222
230
  edge.name_or_index === 'context' ||
223
231
  edge.name_or_index === 'displayName') {
224
232
  const key = filterJSONPropName(edge.name_or_index);
225
233
  info[key] = JSONifyNode(edge.toNode, args, options);
226
234
  }
227
- }
235
+ return null;
236
+ });
228
237
  return info;
229
238
  }
230
239
  function JSONifyNumberNode(node,
@@ -238,7 +247,7 @@ _options) {
238
247
  }
239
248
  function JSONifyCode(node, args, options) {
240
249
  const info = Object.create(null);
241
- for (const edge of node.references) {
250
+ iterateSelectedEdges(node, (edge) => {
242
251
  if (edge.name_or_index === 'name_or_scope_info' &&
243
252
  edge.toNode.name === '(function scope info)') {
244
253
  const key = 'variables with non-number values in closure scope chain';
@@ -251,14 +260,15 @@ function JSONifyCode(node, args, options) {
251
260
  const key = filterJSONPropName(edge.name_or_index);
252
261
  info[key] = JSONifyNode(edge.toNode, args, options);
253
262
  }
254
- }
263
+ return null;
264
+ });
255
265
  return info;
256
266
  }
257
267
  function JSONifyContext(node, args, options) {
258
268
  const info = Object.create(null);
259
269
  const key = 'variables in scope (used by nested closures)';
260
270
  const closure_vars = (info[key] = Object.create(null));
261
- for (const edge of node.references) {
271
+ iterateSelectedEdges(node, (edge) => {
262
272
  const key = filterJSONPropName(edge.name_or_index);
263
273
  if (edge.type === 'context') {
264
274
  closure_vars[key] = JSONifyNodeInShort(edge.toNode);
@@ -266,15 +276,27 @@ function JSONifyContext(node, args, options) {
266
276
  else if (edge.type === '') {
267
277
  info[key] = JSONifyNode(edge.toNode, args, options);
268
278
  }
269
- }
279
+ return null;
280
+ });
270
281
  return info;
271
282
  }
283
+ function iterateSelectedEdges(node, callback) {
284
+ let edgesProcessed = 0;
285
+ node.forEachReference((edge) => {
286
+ if (edge.type === 'internal') {
287
+ if (edge.name_or_index === 'map' || edge.is_index) {
288
+ return;
289
+ }
290
+ }
291
+ if (edgesProcessed++ > 100) {
292
+ return { stop: true };
293
+ }
294
+ return callback(edge);
295
+ });
296
+ }
272
297
  function JSONifyOrdinaryValue(node, args, options) {
273
298
  const info = Object.create(null);
274
- for (const edge of node.references) {
275
- if (edge.name_or_index === 'map' && edge.type === 'internal') {
276
- continue;
277
- }
299
+ iterateSelectedEdges(node, (edge) => {
278
300
  const key = JSONifyEdgeNameAndType(edge);
279
301
  const toNode = edge.toNode;
280
302
  const toNodeName = toNode.name;
@@ -293,7 +315,8 @@ function JSONifyOrdinaryValue(node, args, options) {
293
315
  else {
294
316
  info[key] = JSONifyNodeInShort(toNode);
295
317
  }
296
- }
318
+ return null;
319
+ });
297
320
  return info;
298
321
  }
299
322
  function JSONifyNode(node, args, options) {
@@ -32,7 +32,6 @@ export declare type AnyOptions = Record<string, unknown>;
32
32
  export declare type UnusedOptions = Record<string, never>;
33
33
  /** @internal */
34
34
  export declare type Command = [string, string[], AnyOptions];
35
- export declare type Predicator<T> = (node: T) => boolean;
36
35
  /** @internal */
37
36
  export declare type HeapNodeIdSet = Set<number>;
38
37
  /** @internal */
@@ -86,6 +85,22 @@ export declare type CLIArgs = {
86
85
  'local-puppeteer': boolean;
87
86
  'snapshot-dir': string;
88
87
  };
88
+ /**
89
+ * the predicate callback is used to decide if a
90
+ * entity of type `T`.
91
+ * For more concrete examples on where it is used,
92
+ * check out {@link findAnyReference}, {@link findAnyReferrer},
93
+ * and {@link findReferrers}.
94
+ *
95
+ * @typeParam T - the type of the entity to be checked
96
+ * @param entity - the entity to be checked
97
+ * @returns whether the entity passes the predicate check
98
+ */
99
+ export declare type Predicator<T> = (entity: T) => boolean;
100
+ /**
101
+ * Data structure for holding cookies.
102
+ * For concrete example, check out {@link cookies}.
103
+ */
89
104
  export declare type Cookies = Array<{
90
105
  name: string;
91
106
  value: string;
@@ -120,6 +135,12 @@ export interface E2EScenarioSynthesizerConstructor {
120
135
  new (config: Config): IE2EScenarioSynthesizer;
121
136
  }
122
137
  /** @internal */
138
+ export interface IPackageInfo {
139
+ name: string;
140
+ version: string;
141
+ packageLocation?: string;
142
+ }
143
+ /** @internal */
123
144
  export interface IRunningMode {
124
145
  setConfig(config: Config): void;
125
146
  beforeRunning(visitPlan: IE2EScenarioVisitPlan): void;
@@ -282,9 +303,10 @@ export interface ILeakFilter {
282
303
  /**
283
304
  * Lifecycle function callback that is invoked initially once before calling any
284
305
  * leak filter function.
306
+ * For concrete example, check out {@link beforeLeakFilter}.
285
307
  *
286
- * @param snaphost - heap snapshot see {@link IHeapSnapshot}
287
- * @param leakedNodeIds - the set of leaked object (node) ids.
308
+ * @param snapshot heap snapshot see {@link IHeapSnapshot}
309
+ * @param leakedNodeIds the set of leaked object (node) ids.
288
310
  */
289
311
  export declare type InitLeakFilterCallback = (snapshot: IHeapSnapshot, leakedNodeIds: HeapNodeIdSet) => void;
290
312
  /**
@@ -293,6 +315,8 @@ export declare type InitLeakFilterCallback = (snapshot: IHeapSnapshot, leakedNod
293
315
  * allocated but not released from the target interaction
294
316
  * in the heap snapshot.
295
317
  *
318
+ * For concrete examples, check out {@link leakFilter}.
319
+ *
296
320
  * @param node - the node that is kept alive in the memory in the heap snapshot
297
321
  * @param snapshot - the snapshot of target interaction
298
322
  * @param leakedNodeIds - the set of leaked node ids
@@ -311,6 +335,11 @@ export declare type LeakFilterCallback = (node: IHeapNode, snapshot: IHeapSnapsh
311
335
  /**
312
336
  * The callback defines browser interactions which are
313
337
  * used by memlab to interact with the web app under test.
338
+ * For concrete examples, check out {@link action} or {@link back}.
339
+ *
340
+ * @param page the puppeteer [`Page`](https://pptr.dev/api/puppeteer.page)
341
+ * object, which provides APIs to interact with the web browser
342
+ * @returns no return value
314
343
  */
315
344
  export declare type InteractionsCallback = (page: Page, args?: OperationArgs) => Promise<void>;
316
345
  /**
@@ -451,6 +480,10 @@ export interface IScenario {
451
480
  * `back` is the callback function that specifies how memlab should
452
481
  * back/revert the `action` callback. Think of it as an undo action.
453
482
  *
483
+ * * **Parameters**:
484
+ * * page: `Page` | the puppeteer [`Page`](https://pptr.dev/api/puppeteer.page)
485
+ * object, which provides APIs to interact with the web browser
486
+ *
454
487
  * * **Examples**:
455
488
  * ```typescript
456
489
  * const scenario = {
@@ -674,7 +707,12 @@ export interface IDataBuilder {
674
707
  }
675
708
  /**
676
709
  * Callback function to provide if the page is loaded.
710
+ * For concrete example, check out {@link isPageLoaded}.
677
711
  * @param page - puppeteer's [Page](https://pptr.dev/api/puppeteer.page/) object.
712
+ * @returns a boolean value, if it returns `true`, memlab will consider
713
+ * the navigation completes, if it returns `false`, memlab will keep calling
714
+ * this callback until it returns `true`. This is an async callback, you can
715
+ * also `await` and returns `true` until some async logic is resolved.
678
716
  */
679
717
  export declare type CheckPageLoadCallback = (page: Page) => Promise<boolean>;
680
718
  /** @internal */
@@ -724,16 +762,42 @@ export declare type E2EStepInfo = IE2EStepBasic & {
724
762
  delay?: number;
725
763
  metrics: Record<string, number>;
726
764
  };
727
- /** @internal */
765
+ /**
766
+ * This data structure contains the input configuration for the browser and
767
+ * output data from the browser. You can retrieve the instance of this type
768
+ * through {@link RunMetaInfo}.
769
+ */
728
770
  export interface IBrowserInfo {
771
+ /**
772
+ * browser version
773
+ */
729
774
  _browserVersion: string;
775
+ /**
776
+ * configuration for puppeteer
777
+ */
730
778
  _puppeteerConfig: LaunchOptions;
779
+ /**
780
+ * all web console output
781
+ */
731
782
  _consoleMessages: string[];
732
783
  }
784
+ /**
785
+ * This data structure holds the information about memlab run.
786
+ * You can retrieve the instance of this type through {@link getRunMetaInfo}.
787
+ */
733
788
  export declare type RunMetaInfo = {
789
+ /** @internal */
734
790
  app: string;
791
+ /** @internal */
735
792
  interaction: string;
793
+ /**
794
+ * type of the memlab run
795
+ */
736
796
  type: string;
797
+ /**
798
+ * input configuration for the browser and
799
+ * output data from the browser
800
+ */
737
801
  browserInfo: IBrowserInfo;
738
802
  };
739
803
  /**
@@ -743,6 +807,13 @@ export declare type RunMetaInfo = {
743
807
  * {@link IHeapNode} and {@link IHeapEdge}.
744
808
  */
745
809
  export interface IHeapSnapshot {
810
+ /**
811
+ * flag indicating if the heap snapshot has included
812
+ * the post-processing meta data (e.g., shortest path to GC root,
813
+ * dominator info and retainer size etc.)
814
+ * @internal
815
+ */
816
+ isProcessed: boolean;
746
817
  /** @internal */
747
818
  snapshot: RawHeapSnapshot;
748
819
  /**
@@ -755,11 +826,11 @@ export interface IHeapSnapshot {
755
826
  * ```typescript
756
827
  * import type {IHeapSnapshot, IHeapNode} from '@memlab/core';
757
828
  * import {dumpNodeHeapSnapshot} from '@memlab/core';
758
- * import {getHeapFromFile} from '@memlab/heap-analysis';
829
+ * import {getFullHeapFromFile} from '@memlab/heap-analysis';
759
830
  *
760
831
  * (async function () {
761
832
  * const heapFile = dumpNodeHeapSnapshot();
762
- * const heap: IHeapSnapshot = await getHeapFromFile(heapFile);
833
+ * const heap: IHeapSnapshot = await getFullHeapFromFile(heapFile);
763
834
  *
764
835
  * // get the total number of heap objects
765
836
  * heap.nodes.length;
@@ -781,11 +852,11 @@ export interface IHeapSnapshot {
781
852
  * ```typescript
782
853
  * import type {IHeapSnapshot, IHeapEdge} from '@memlab/core';
783
854
  * import {dumpNodeHeapSnapshot} from '@memlab/core';
784
- * import {getHeapFromFile} from '@memlab/heap-analysis';
855
+ * import {getFullHeapFromFile} from '@memlab/heap-analysis';
785
856
  *
786
857
  * (async function () {
787
858
  * const heapFile = dumpNodeHeapSnapshot();
788
- * const heap: IHeapSnapshot = await getHeapFromFile(heapFile);
859
+ * const heap: IHeapSnapshot = await getFullHeapFromFile(heapFile);
789
860
  *
790
861
  * // get the total number of heap references
791
862
  * heap.edges.length;
@@ -807,11 +878,11 @@ export interface IHeapSnapshot {
807
878
  * ```typescript
808
879
  * import type {IHeapSnapshot} from '@memlab/core';
809
880
  * import {dumpNodeHeapSnapshot} from '@memlab/core';
810
- * import {getHeapFromFile} from '@memlab/heap-analysis';
881
+ * import {getFullHeapFromFile} from '@memlab/heap-analysis';
811
882
  *
812
883
  * (async function () {
813
884
  * const heapFile = dumpNodeHeapSnapshot();
814
- * const heap: IHeapSnapshot = await getHeapFromFile(heapFile);
885
+ * const heap: IHeapSnapshot = await getFullHeapFromFile(heapFile);
815
886
  *
816
887
  * const node = heap.getNodeById(351);
817
888
  * node?.id; // should be 351
@@ -829,7 +900,7 @@ export interface IHeapSnapshot {
829
900
  * ```typescript
830
901
  * // save as example.test.ts
831
902
  * import type {IHeapSnapshot, Nullable} from '@memlab/core';
832
- * import {config, getNodeInnocentHeap} from '@memlab/core';
903
+ * import {config, takeNodeMinimalHeap} from '@memlab/core';
833
904
  *
834
905
  * class TestObject {
835
906
  * public arr1 = [1, 2, 3];
@@ -841,7 +912,7 @@ export interface IHeapSnapshot {
841
912
  *
842
913
  * let obj: Nullable<TestObject> = new TestObject();
843
914
  * // get a heap snapshot of the current program state
844
- * let heap: IHeapSnapshot = await getNodeInnocentHeap();
915
+ * let heap: IHeapSnapshot = await takeNodeMinimalHeap();
845
916
  *
846
917
  * // call some function that may add references to obj
847
918
  * rabbitHole(obj)
@@ -849,7 +920,7 @@ export interface IHeapSnapshot {
849
920
  * expect(heap.hasObjectWithClassName('TestObject')).toBe(true);
850
921
  * obj = null;
851
922
  *
852
- * heap = await getNodeInnocentHeap();
923
+ * heap = await takeNodeMinimalHeap();
853
924
  * // if rabbitHole does not have any side effect that
854
925
  * // adds new references to obj, then obj can be GCed
855
926
  * expect(heap.hasObjectWithClassName('TestObject')).toBe(false);
@@ -868,7 +939,7 @@ export interface IHeapSnapshot {
868
939
  * * **Examples**:
869
940
  * ```typescript
870
941
  * import type {IHeapSnapshot} from '@memlab/core';
871
- * import {getNodeInnocentHeap} from '@memlab/core';
942
+ * import {takeNodeMinimalHeap} from '@memlab/core';
872
943
  *
873
944
  * class TestObject {
874
945
  * public arr1 = [1, 2, 3];
@@ -878,7 +949,7 @@ export interface IHeapSnapshot {
878
949
  * (async function () {
879
950
  * const obj = new TestObject();
880
951
  * // get a heap snapshot of the current program state
881
- * const heap: IHeapSnapshot = await getNodeInnocentHeap();
952
+ * const heap: IHeapSnapshot = await takeNodeMinimalHeap();
882
953
  *
883
954
  * const node = heap.getAnyObjectWithClassName('TestObject');
884
955
  * console.log(node?.name); // should be 'TestObject'
@@ -897,14 +968,14 @@ export interface IHeapSnapshot {
897
968
  * ```typescript
898
969
  * import type {IHeapSnapshot} from '@memlab/core';
899
970
  * import {dumpNodeHeapSnapshot} from '@memlab/core';
900
- * import {getHeapFromFile} from '@memlab/heap-analysis';
971
+ * import {getFullHeapFromFile} from '@memlab/heap-analysis';
901
972
  *
902
973
  * (async function () {
903
974
  * // eslint-disable-next-line @typescript-eslint/no-unused-vars
904
975
  * const object = {'memlab-test-heap-property': 'memlab-test-heap-value'};
905
976
  *
906
977
  * const heapFile = dumpNodeHeapSnapshot();
907
- * const heap: IHeapSnapshot = await getHeapFromFile(heapFile);
978
+ * const heap: IHeapSnapshot = await getFullHeapFromFile(heapFile);
908
979
  *
909
980
  * // should be true
910
981
  * console.log(heap.hasObjectWithPropertyName('memlab-test-heap-property'));
@@ -924,7 +995,7 @@ export interface IHeapSnapshot {
924
995
  *
925
996
  * ```typescript
926
997
  * import type {IHeapSnapshot, AnyValue} from '@memlab/core';
927
- * import {config, getNodeInnocentHeap, tagObject} from '@memlab/core';
998
+ * import {config, takeNodeMinimalHeap, tagObject} from '@memlab/core';
928
999
  *
929
1000
  * test('memory test', async () => {
930
1001
  * config.muteConsole = true;
@@ -938,7 +1009,7 @@ export interface IHeapSnapshot {
938
1009
  *
939
1010
  * o2 = null;
940
1011
  *
941
- * const heap: IHeapSnapshot = await getNodeInnocentHeap();
1012
+ * const heap: IHeapSnapshot = await takeNodeMinimalHeap();
942
1013
  *
943
1014
  * // expect object with marker "memlab-mark-1" exists
944
1015
  * expect(heap.hasObjectWithTag('memlab-mark-1')).toBe(true);
@@ -962,16 +1033,16 @@ export interface IHeapSnapshot {
962
1033
  * @readonly it is not recommended to modify any `IHeapLocation` instance
963
1034
  *
964
1035
  * * **Examples**: V8 or hermes heap snapshot can be parsed by the
965
- * {@link getHeapFromFile} API.
1036
+ * {@link getFullHeapFromFile} API.
966
1037
  *
967
1038
  * ```typescript
968
1039
  * import type {IHeapSnapshot, IHeapNode, IHeapLocation} from '@memlab/core';
969
1040
  * import {dumpNodeHeapSnapshot} from '@memlab/core';
970
- * import {getHeapFromFile} from '@memlab/heap-analysis';
1041
+ * import {getFullHeapFromFile} from '@memlab/heap-analysis';
971
1042
  *
972
1043
  * (async function () {
973
1044
  * const heapFile = dumpNodeHeapSnapshot();
974
- * const heap: IHeapSnapshot = await getHeapFromFile(heapFile);
1045
+ * const heap: IHeapSnapshot = await getFullHeapFromFile(heapFile);
975
1046
  *
976
1047
  * // iterate over each node (heap object)
977
1048
  * heap.nodes.forEach((node: IHeapNode, i: number) => {
@@ -1024,16 +1095,16 @@ export interface IHeapEdgeBasic {
1024
1095
  * @readonly it is not recommended to modify any `IHeapEdge` instance
1025
1096
  *
1026
1097
  * * **Examples**: V8 or hermes heap snapshot can be parsed by the
1027
- * {@link getHeapFromFile} API.
1098
+ * {@link getFullHeapFromFile} API.
1028
1099
  *
1029
1100
  * ```typescript
1030
1101
  * import type {IHeapSnapshot, IHeapEdge} from '@memlab/core';
1031
1102
  * import {dumpNodeHeapSnapshot} from '@memlab/core';
1032
- * import {getHeapFromFile} from '@memlab/heap-analysis';
1103
+ * import {getFullHeapFromFile} from '@memlab/heap-analysis';
1033
1104
  *
1034
1105
  * (async function () {
1035
1106
  * const heapFile = dumpNodeHeapSnapshot();
1036
- * const heap: IHeapSnapshot = await getHeapFromFile(heapFile);
1107
+ * const heap: IHeapSnapshot = await getFullHeapFromFile(heapFile);
1037
1108
  *
1038
1109
  * // iterate over each edge (JS reference in heap)
1039
1110
  * heap.edges.forEach((edge: IHeapEdge, i: number) => {
@@ -1087,11 +1158,11 @@ export interface IHeapEdge extends IHeapEdgeBasic {
1087
1158
  * ```typescript
1088
1159
  * import type {IHeapSnapshot, IHeapEdges} from '@memlab/core';
1089
1160
  * import {dumpNodeHeapSnapshot} from '@memlab/core';
1090
- * import {getHeapFromFile} from '@memlab/heap-analysis';
1161
+ * import {getFullHeapFromFile} from '@memlab/heap-analysis';
1091
1162
  *
1092
1163
  * (async function () {
1093
1164
  * const heapFile = dumpNodeHeapSnapshot();
1094
- * const heap: IHeapSnapshot = await getHeapFromFile(heapFile);
1165
+ * const heap: IHeapSnapshot = await getFullHeapFromFile(heapFile);
1095
1166
  *
1096
1167
  * const edges: IHeapEdges = heap.edges;
1097
1168
  * edges.length;
@@ -1148,6 +1219,13 @@ export interface IHeapNodeBasic {
1148
1219
  */
1149
1220
  id: number;
1150
1221
  }
1222
+ /**
1223
+ * Executes a provided callback once for JavaScript references.
1224
+ * For concrete examples, check out {@link forEachReference}
1225
+ * or {@link forEachReferrer}.
1226
+ * @param callback the callback for each JavaScript reference from a collection
1227
+ * @returns this API returns void
1228
+ */
1151
1229
  export declare type EdgeIterationCallback = (edge: IHeapEdge) => Optional<{
1152
1230
  stop: boolean;
1153
1231
  }>;
@@ -1159,16 +1237,16 @@ export declare type EdgeIterationCallback = (edge: IHeapEdge) => Optional<{
1159
1237
  * @readonly it is not recommended to modify any `IHeapNode` instance
1160
1238
  *
1161
1239
  * * **Examples**: V8 or hermes heap snapshot can be parsed by the
1162
- * {@link getHeapFromFile} API.
1240
+ * {@link getFullHeapFromFile} API.
1163
1241
  *
1164
1242
  * ```typescript
1165
1243
  * import type {IHeapSnapshot, IHeapNode} from '@memlab/core';
1166
1244
  * import {dumpNodeHeapSnapshot} from '@memlab/core';
1167
- * import {getHeapFromFile} from '@memlab/heap-analysis';
1245
+ * import {getFullHeapFromFile} from '@memlab/heap-analysis';
1168
1246
  *
1169
1247
  * (async function () {
1170
1248
  * const heapFile = dumpNodeHeapSnapshot();
1171
- * const heap: IHeapSnapshot = await getHeapFromFile(heapFile);
1249
+ * const heap: IHeapSnapshot = await getFullHeapFromFile(heapFile);
1172
1250
  *
1173
1251
  * // iterate over each node (heap object)
1174
1252
  * heap.nodes.forEach((node: IHeapNode, i: number) => {
@@ -1467,16 +1545,16 @@ export interface IHeapNode extends IHeapNodeBasic {
1467
1545
  * @readonly it is not recommended to modify any `IHeapStringNode` instance
1468
1546
  *
1469
1547
  * * **Examples**: V8 or hermes heap snapshot can be parsed by the
1470
- * {@link getHeapFromFile} API.
1548
+ * {@link getFullHeapFromFile} API.
1471
1549
  *
1472
1550
  * ```typescript
1473
1551
  * import type {IHeapSnapshot, IHeapNode, IHeapStringNode} from '@memlab/core';
1474
1552
  * import {dumpNodeHeapSnapshot} from '@memlab/core';
1475
- * import {getHeapFromFile} from '@memlab/heap-analysis';
1553
+ * import {getFullHeapFromFile} from '@memlab/heap-analysis';
1476
1554
  *
1477
1555
  * (async function () {
1478
1556
  * const heapFile = dumpNodeHeapSnapshot();
1479
- * const heap: IHeapSnapshot = await getHeapFromFile(heapFile);
1557
+ * const heap: IHeapSnapshot = await getFullHeapFromFile(heapFile);
1480
1558
  *
1481
1559
  * // iterate over each node (heap object)
1482
1560
  * heap.nodes.forEach((node: IHeapNode, i: number) => {
@@ -1508,11 +1586,11 @@ export interface IHeapStringNode extends IHeapNode {
1508
1586
  * ```typescript
1509
1587
  * import type {IHeapSnapshot, IHeapNodes} from '@memlab/core';
1510
1588
  * import {dumpNodeHeapSnapshot} from '@memlab/core';
1511
- * import {getHeapFromFile} from '@memlab/heap-analysis';
1589
+ * import {getFullHeapFromFile} from '@memlab/heap-analysis';
1512
1590
  *
1513
1591
  * (async function () {
1514
1592
  * const heapFile = dumpNodeHeapSnapshot();
1515
- * const heap: IHeapSnapshot = await getHeapFromFile(heapFile);
1593
+ * const heap: IHeapSnapshot = await getFullHeapFromFile(heapFile);
1516
1594
  *
1517
1595
  * const nodes: IHeapNodes = heap.nodes;
1518
1596
  * nodes.length;
@@ -1647,5 +1725,11 @@ export interface IClusterStrategy {
1647
1725
  diffTraces: (newLeakTraces: LeakTrace[], existingLeakTraces: LeakTrace[]) => TraceDiff;
1648
1726
  }
1649
1727
  /** @internal */
1728
+ export interface IHeapConfig {
1729
+ isCliInteractiveMode: boolean;
1730
+ currentHeapFile: Optional<string>;
1731
+ currentHeap: Optional<IHeapSnapshot>;
1732
+ }
1733
+ /** @internal */
1650
1734
  export declare type ErrorWithMessage = Pick<Error, 'message'>;
1651
1735
  //# sourceMappingURL=Types.d.ts.map
package/dist/lib/Utils.js CHANGED
@@ -390,7 +390,6 @@ function setFiberNodeAttribute(node, flag) {
390
390
  if (!node || !isFiberNode(node)) {
391
391
  return;
392
392
  }
393
- // eslint-disable-next-line no-bitwise
394
393
  node.attributes |= flag;
395
394
  }
396
395
  function hasFiberNodeAttribute(node, flag) {
@@ -447,7 +446,6 @@ function filterNodesInPlace(idSet, snapshot, cb) {
447
446
  function applyToNodes(idSet, snapshot, cb, options = {}) {
448
447
  let ids = Array.from(idSet.keys());
449
448
  if (options.shuffle) {
450
- // eslint-disable-next-line fb-www/unsafe-math-random
451
449
  ids.sort(() => Math.random() - 0.5);
452
450
  }
453
451
  else if (options.reverse) {
@@ -508,7 +506,6 @@ function loadScenario(filename) {
508
506
  }
509
507
  let scenario;
510
508
  try {
511
- // eslint-disable-next-line @typescript-eslint/no-var-requires
512
509
  scenario = require(filepath);
513
510
  scenario = checkScenarioInstance(scenario);
514
511
  if (scenario.name == null) {
@@ -534,6 +531,12 @@ function handleSnapshotError(e) {
534
531
  }
535
532
  function getSnapshotFromFile(filename, options) {
536
533
  return __awaiter(this, void 0, void 0, function* () {
534
+ const heapConfig = Config_1.default.heapConfig;
535
+ if (heapConfig &&
536
+ heapConfig.currentHeapFile === filename &&
537
+ heapConfig.currentHeap) {
538
+ return heapConfig.currentHeap;
539
+ }
537
540
  Console_1.default.overwrite('parsing ' + filename + ' ...');
538
541
  let ret = null;
539
542
  try {
@@ -812,7 +815,6 @@ function loadRunMetaInfo(metaFile = undefined) {
812
815
  try {
813
816
  const content = fs_1.default.readFileSync(file, 'UTF-8');
814
817
  return JSON.parse(content);
815
- // eslint-disable-next-line fb-www/no-unused-catch-bindings
816
818
  }
817
819
  catch (_) {
818
820
  throw haltOrThrow('Run info missing. Please make sure `memlab run` is complete.');
@@ -1271,9 +1273,23 @@ function getSnapshotDirForAnalysis() {
1271
1273
  return dir;
1272
1274
  }
1273
1275
  function getSingleSnapshotFileForAnalysis() {
1274
- const path = Config_1.default.useExternalSnapshot && Config_1.default.externalSnapshotFilePaths[0]
1275
- ? Config_1.default.externalSnapshotFilePaths[0]
1276
- : getSnapshotFilePathWithTabType(/(final)|(target)|(baseline)/);
1276
+ let path = null;
1277
+ // if an external snapshot file is specified
1278
+ if (Config_1.default.useExternalSnapshot &&
1279
+ Config_1.default.externalSnapshotFilePaths.length > 0) {
1280
+ path =
1281
+ Config_1.default.externalSnapshotFilePaths[Config_1.default.externalSnapshotFilePaths.length - 1];
1282
+ // if running in interactive heap analysis mode
1283
+ }
1284
+ else if (Config_1.default.heapConfig &&
1285
+ Config_1.default.heapConfig.isCliInteractiveMode &&
1286
+ Config_1.default.heapConfig.currentHeapFile) {
1287
+ path = Config_1.default.heapConfig.currentHeapFile;
1288
+ // search for snapshot labeled as baseline, target, or final
1289
+ }
1290
+ else {
1291
+ path = getSnapshotFilePathWithTabType(/(final)|(target)|(baseline)/);
1292
+ }
1277
1293
  return resolveSnapshotFilePath(path);
1278
1294
  }
1279
1295
  function getSnapshotFilePath(tab) {
@@ -1286,7 +1302,7 @@ function getSnapshotFilePath(tab) {
1286
1302
  }
1287
1303
  return Config_1.default.externalSnapshotFilePaths[tab.idx - 1];
1288
1304
  }
1289
- // eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/no-explicit-any
1305
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
1290
1306
  function equalOrMatch(v1, v2) {
1291
1307
  const t1 = typeof v1;
1292
1308
  const t2 = typeof v2;
@@ -1421,7 +1437,6 @@ function isURLEqual(url1, url2) {
1421
1437
  try {
1422
1438
  u1 = new URL(url1);
1423
1439
  u2 = new URL(url2);
1424
- // eslint-disable-next-line fb-www/no-unused-catch-bindings
1425
1440
  }
1426
1441
  catch (_e) {
1427
1442
  return false;
@@ -12,6 +12,7 @@ import type { IHeapNode, IHeapNodes, IHeapEdges, IHeapSnapshot, HeapNodeTypes, H
12
12
  import HeapNode from './HeapNode';
13
13
  export default class HeapSnapshot implements IHeapSnapshot {
14
14
  snapshot: RawHeapSnapshot;
15
+ isProcessed: boolean;
15
16
  nodes: IHeapNodes;
16
17
  _nodeCount: number;
17
18
  edges: IHeapEdges;