@memlab/core 1.0.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 (130) hide show
  1. package/README.md +11 -0
  2. package/dist/__tests__/parser/HeapParser.test.d.ts +11 -0
  3. package/dist/__tests__/parser/HeapParser.test.d.ts.map +1 -0
  4. package/dist/__tests__/parser/HeapParser.test.js +54 -0
  5. package/dist/__tests__/parser/NodeHeap.test.d.ts +11 -0
  6. package/dist/__tests__/parser/NodeHeap.test.d.ts.map +1 -0
  7. package/dist/__tests__/parser/NodeHeap.test.js +96 -0
  8. package/dist/__tests__/parser/StringNode.test.d.ts +11 -0
  9. package/dist/__tests__/parser/StringNode.test.d.ts.map +1 -0
  10. package/dist/__tests__/parser/StringNode.test.js +61 -0
  11. package/dist/__tests__/parser/traverse/HeapNodeTraverse.test.d.ts +16 -0
  12. package/dist/__tests__/parser/traverse/HeapNodeTraverse.test.d.ts.map +1 -0
  13. package/dist/__tests__/parser/traverse/HeapNodeTraverse.test.js +140 -0
  14. package/dist/__tests__/utils/utils.test.d.ts +11 -0
  15. package/dist/__tests__/utils/utils.test.d.ts.map +1 -0
  16. package/dist/__tests__/utils/utils.test.js +81 -0
  17. package/dist/index.d.ts +29 -0
  18. package/dist/index.d.ts.map +1 -0
  19. package/dist/index.js +62 -0
  20. package/dist/lib/BaseOption.d.ts +31 -0
  21. package/dist/lib/BaseOption.d.ts.map +1 -0
  22. package/dist/lib/BaseOption.js +109 -0
  23. package/dist/lib/BrowserInfo.d.ts +33 -0
  24. package/dist/lib/BrowserInfo.d.ts.map +1 -0
  25. package/dist/lib/BrowserInfo.js +117 -0
  26. package/dist/lib/Config.d.ts +203 -0
  27. package/dist/lib/Config.d.ts.map +1 -0
  28. package/dist/lib/Config.js +427 -0
  29. package/dist/lib/Console.d.ts +67 -0
  30. package/dist/lib/Console.d.ts.map +1 -0
  31. package/dist/lib/Console.js +344 -0
  32. package/dist/lib/Constant.d.ts +38 -0
  33. package/dist/lib/Constant.d.ts.map +1 -0
  34. package/dist/lib/Constant.js +58 -0
  35. package/dist/lib/FileManager.d.ts +69 -0
  36. package/dist/lib/FileManager.d.ts.map +1 -0
  37. package/dist/lib/FileManager.js +309 -0
  38. package/dist/lib/HeapAnalyzer.d.ts +51 -0
  39. package/dist/lib/HeapAnalyzer.d.ts.map +1 -0
  40. package/dist/lib/HeapAnalyzer.js +719 -0
  41. package/dist/lib/HeapParser.d.ts +19 -0
  42. package/dist/lib/HeapParser.d.ts.map +1 -0
  43. package/dist/lib/HeapParser.js +128 -0
  44. package/dist/lib/InternalValueSetter.d.ts +14 -0
  45. package/dist/lib/InternalValueSetter.d.ts.map +1 -0
  46. package/dist/lib/InternalValueSetter.js +43 -0
  47. package/dist/lib/NodeHeap.d.ts +16 -0
  48. package/dist/lib/NodeHeap.d.ts.map +1 -0
  49. package/dist/lib/NodeHeap.js +62 -0
  50. package/dist/lib/ProcessManager.d.ts +25 -0
  51. package/dist/lib/ProcessManager.d.ts.map +1 -0
  52. package/dist/lib/ProcessManager.js +67 -0
  53. package/dist/lib/Serializer.d.ts +49 -0
  54. package/dist/lib/Serializer.d.ts.map +1 -0
  55. package/dist/lib/Serializer.js +702 -0
  56. package/dist/lib/StringLoader.d.ts +26 -0
  57. package/dist/lib/StringLoader.d.ts.map +1 -0
  58. package/dist/lib/StringLoader.js +290 -0
  59. package/dist/lib/Types.d.ts +432 -0
  60. package/dist/lib/Types.d.ts.map +1 -0
  61. package/dist/lib/Types.js +11 -0
  62. package/dist/lib/Utils.d.ts +223 -0
  63. package/dist/lib/Utils.d.ts.map +1 -0
  64. package/dist/lib/Utils.js +1736 -0
  65. package/dist/lib/heap-data/HeapEdge.d.ts +27 -0
  66. package/dist/lib/heap-data/HeapEdge.d.ts.map +1 -0
  67. package/dist/lib/heap-data/HeapEdge.js +75 -0
  68. package/dist/lib/heap-data/HeapLocation.d.ts +22 -0
  69. package/dist/lib/heap-data/HeapLocation.d.ts.map +1 -0
  70. package/dist/lib/heap-data/HeapLocation.js +40 -0
  71. package/dist/lib/heap-data/HeapNode.d.ts +55 -0
  72. package/dist/lib/heap-data/HeapNode.d.ts.map +1 -0
  73. package/dist/lib/heap-data/HeapNode.js +344 -0
  74. package/dist/lib/heap-data/HeapSnapshot.d.ts +85 -0
  75. package/dist/lib/heap-data/HeapSnapshot.d.ts.map +1 -0
  76. package/dist/lib/heap-data/HeapSnapshot.js +462 -0
  77. package/dist/lib/heap-data/HeapStringNode.d.ts +18 -0
  78. package/dist/lib/heap-data/HeapStringNode.d.ts.map +1 -0
  79. package/dist/lib/heap-data/HeapStringNode.js +43 -0
  80. package/dist/lib/heap-data/HeapUtils.d.ts +17 -0
  81. package/dist/lib/heap-data/HeapUtils.d.ts.map +1 -0
  82. package/dist/lib/heap-data/HeapUtils.js +25 -0
  83. package/dist/logger/LeakClusterLogger.d.ts +40 -0
  84. package/dist/logger/LeakClusterLogger.d.ts.map +1 -0
  85. package/dist/logger/LeakClusterLogger.js +228 -0
  86. package/dist/logger/LeakTraceDetailsLogger.d.ts +19 -0
  87. package/dist/logger/LeakTraceDetailsLogger.d.ts.map +1 -0
  88. package/dist/logger/LeakTraceDetailsLogger.js +50 -0
  89. package/dist/modes/BaseMode.d.ts +30 -0
  90. package/dist/modes/BaseMode.d.ts.map +1 -0
  91. package/dist/modes/BaseMode.js +95 -0
  92. package/dist/modes/InteractionTestMode.d.ts +23 -0
  93. package/dist/modes/InteractionTestMode.d.ts.map +1 -0
  94. package/dist/modes/InteractionTestMode.js +46 -0
  95. package/dist/modes/MeasureMode.d.ts +23 -0
  96. package/dist/modes/MeasureMode.d.ts.map +1 -0
  97. package/dist/modes/MeasureMode.js +58 -0
  98. package/dist/modes/RunningModes.d.ts +15 -0
  99. package/dist/modes/RunningModes.d.ts.map +1 -0
  100. package/dist/modes/RunningModes.js +40 -0
  101. package/dist/paths/TraceFinder.d.ts +31 -0
  102. package/dist/paths/TraceFinder.d.ts.map +1 -0
  103. package/dist/paths/TraceFinder.js +537 -0
  104. package/dist/trace-cluster/ClusterUtils.d.ts +14 -0
  105. package/dist/trace-cluster/ClusterUtils.d.ts.map +1 -0
  106. package/dist/trace-cluster/ClusterUtils.js +17 -0
  107. package/dist/trace-cluster/ClusterUtilsHelper.d.ts +38 -0
  108. package/dist/trace-cluster/ClusterUtilsHelper.d.ts.map +1 -0
  109. package/dist/trace-cluster/ClusterUtilsHelper.js +373 -0
  110. package/dist/trace-cluster/ClusteringHeuristics.d.ts +22 -0
  111. package/dist/trace-cluster/ClusteringHeuristics.d.ts.map +1 -0
  112. package/dist/trace-cluster/ClusteringHeuristics.js +23 -0
  113. package/dist/trace-cluster/EvalutationMetric.d.ts +22 -0
  114. package/dist/trace-cluster/EvalutationMetric.d.ts.map +1 -0
  115. package/dist/trace-cluster/EvalutationMetric.js +158 -0
  116. package/dist/trace-cluster/TraceBucket.d.ts +36 -0
  117. package/dist/trace-cluster/TraceBucket.d.ts.map +1 -0
  118. package/dist/trace-cluster/TraceBucket.js +238 -0
  119. package/dist/trace-cluster/TraceElement.d.ts +71 -0
  120. package/dist/trace-cluster/TraceElement.d.ts.map +1 -0
  121. package/dist/trace-cluster/TraceElement.js +182 -0
  122. package/dist/trace-cluster/strategies/TraceAsClusterStrategy.d.ts +15 -0
  123. package/dist/trace-cluster/strategies/TraceAsClusterStrategy.d.ts.map +1 -0
  124. package/dist/trace-cluster/strategies/TraceAsClusterStrategy.js +37 -0
  125. package/dist/trace-cluster/strategies/TraceSimilarityStrategy.d.ts +15 -0
  126. package/dist/trace-cluster/strategies/TraceSimilarityStrategy.d.ts.map +1 -0
  127. package/dist/trace-cluster/strategies/TraceSimilarityStrategy.js +60 -0
  128. package/package.json +60 -0
  129. package/static/run-meta.json +10 -0
  130. package/static/visit-order.json +27 -0
@@ -0,0 +1,1736 @@
1
+ "use strict";
2
+ /**
3
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
4
+ *
5
+ * This source code is licensed under the MIT license found in the
6
+ * LICENSE file in the root directory of this source tree.
7
+ *
8
+ * @emails oncall+ws_labs
9
+ * @format
10
+ */
11
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
12
+ if (k2 === undefined) k2 = k;
13
+ var desc = Object.getOwnPropertyDescriptor(m, k);
14
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
15
+ desc = { enumerable: true, get: function() { return m[k]; } };
16
+ }
17
+ Object.defineProperty(o, k2, desc);
18
+ }) : (function(o, m, k, k2) {
19
+ if (k2 === undefined) k2 = k;
20
+ o[k2] = m[k];
21
+ }));
22
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
23
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
24
+ }) : function(o, v) {
25
+ o["default"] = v;
26
+ });
27
+ var __importStar = (this && this.__importStar) || function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
35
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
36
+ return new (P || (P = Promise))(function (resolve, reject) {
37
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
38
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
39
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
40
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
41
+ });
42
+ };
43
+ var __importDefault = (this && this.__importDefault) || function (mod) {
44
+ return (mod && mod.__esModule) ? mod : { "default": mod };
45
+ };
46
+ Object.defineProperty(exports, "__esModule", { value: true });
47
+ exports.resolveSnapshotFilePath = void 0;
48
+ const fs_1 = __importDefault(require("fs"));
49
+ const path_1 = __importDefault(require("path"));
50
+ const process_1 = __importDefault(require("process"));
51
+ const Config_1 = __importStar(require("./Config"));
52
+ const Console_1 = __importDefault(require("./Console"));
53
+ const Constant_1 = __importDefault(require("./Constant"));
54
+ const HeapParser_1 = __importDefault(require("./HeapParser"));
55
+ const BrowserInfo_1 = __importDefault(require("./BrowserInfo"));
56
+ const memCache = Object.create(null);
57
+ const FileManager_1 = __importDefault(require("./FileManager"));
58
+ const __1 = require("..");
59
+ // For more details see ReactWorkTags.js of React
60
+ const reactWorkTag = {
61
+ FunctionComponent: 0,
62
+ ClassComponent: 1,
63
+ IndeterminateComponent: 2,
64
+ HostRoot: 3,
65
+ HostPortal: 4,
66
+ HostComponent: 5,
67
+ HostText: 6,
68
+ Fragment: 7,
69
+ Mode: 8,
70
+ ContextConsumer: 9,
71
+ ContextProvider: 10,
72
+ ForwardRef: 11,
73
+ Profiler: 12,
74
+ SuspenseComponent: 13,
75
+ MemoComponent: 14,
76
+ SimpleMemoComponent: 15,
77
+ LazyComponent: 16,
78
+ IncompleteClassComponent: 17,
79
+ DehydratedFragment: 18,
80
+ SuspenseListComponent: 19,
81
+ ScopeComponent: 21,
82
+ OffscreenComponent: 22,
83
+ LegacyHiddenComponent: 23,
84
+ CacheComponent: 24,
85
+ };
86
+ const reactTagIdToName = [];
87
+ Object.entries(reactWorkTag).forEach(workTag => (reactTagIdToName[workTag[1]] = workTag[0]));
88
+ function _getReactWorkTagName(tagId) {
89
+ if (typeof tagId === 'string') {
90
+ tagId = parseInt(tagId, 10);
91
+ }
92
+ if (typeof tagId !== 'number' || tagId !== tagId) {
93
+ return null;
94
+ }
95
+ return reactTagIdToName[tagId];
96
+ }
97
+ function isHermesInternalObject(node) {
98
+ return (node.type === 'number' ||
99
+ node.name === 'HiddenClass' ||
100
+ node.name === 'Environment' ||
101
+ node.name === 'ArrayStorage' ||
102
+ node.name === 'SegmentedArray' ||
103
+ node.name === 'WeakValueMap' ||
104
+ node.name === 'HashMapEntry');
105
+ }
106
+ function isStackTraceFrame(node) {
107
+ if (!node || node.type !== 'hidden') {
108
+ return false;
109
+ }
110
+ return node.name === 'system / StackTraceFrame';
111
+ }
112
+ // returns true if it is detached DOM element or detached FiberNode
113
+ // NOTE: Doesn't work for FiberNode without detachedness field
114
+ function isDetached(node) {
115
+ if (Config_1.default.snapshotHasDetachedness) {
116
+ return node.is_detached;
117
+ }
118
+ return node.name.startsWith('Detached ');
119
+ }
120
+ function isFiberNode(node) {
121
+ if (!node || node.type !== 'object') {
122
+ return false;
123
+ }
124
+ const name = node.name;
125
+ return name === 'FiberNode' || name === 'Detached FiberNode';
126
+ }
127
+ // quickly check the detachedness field
128
+ // need to call hasHostRoot(node) before this function
129
+ // does not traverse and check the existance of HostRoot
130
+ // NOTE: Doesn't work for FiberNode without detachedness field
131
+ function isDetachedFiberNode(node) {
132
+ return isFiberNode(node) && isDetached(node);
133
+ }
134
+ // this function returns a more general sense of DOM nodes. Specifically,
135
+ // any detached DOM nodes (e.g., HTMLXXElement, IntersectionObserver etc.)
136
+ // that are not internal nodes.
137
+ function isDetachedDOMNode(node, args = {}) {
138
+ if (!node || typeof node.name !== 'string') {
139
+ return false;
140
+ }
141
+ const name = node.name;
142
+ if (isFiberNode(node)) {
143
+ return false;
144
+ }
145
+ if (name === 'Detached InternalNode' && args.ignoreInternalNode) {
146
+ return false;
147
+ }
148
+ return isDetached(node);
149
+ }
150
+ function isWeakMapEdge(edge) {
151
+ if (!edge || typeof edge.name_or_index !== 'string') {
152
+ return false;
153
+ }
154
+ if (edge.name_or_index.indexOf('WeakMap') < 0) {
155
+ return false;
156
+ }
157
+ return true;
158
+ }
159
+ function isWeakMapEdgeToKey(edge) {
160
+ if (!isWeakMapEdge(edge)) {
161
+ return false;
162
+ }
163
+ const weakMapKeyObjectId = getWeakMapEdgeKeyId(edge);
164
+ const toNodeObjectId = edge.toNode.id;
165
+ // in WeakMap, keys are weakly referenced
166
+ if (weakMapKeyObjectId === toNodeObjectId) {
167
+ return true;
168
+ }
169
+ return false;
170
+ }
171
+ function isWeakMapEdgeToValue(edge) {
172
+ if (!isWeakMapEdge(edge)) {
173
+ return false;
174
+ }
175
+ const weakMapKeyObjectId = getWeakMapEdgeKeyId(edge);
176
+ const toNodeObjectId = edge.toNode.id;
177
+ // in WeakMap, keys are weakly referenced
178
+ if (weakMapKeyObjectId !== toNodeObjectId) {
179
+ return true;
180
+ }
181
+ return false;
182
+ }
183
+ function isEssentialEdge(nodeIndex, edgeType, rootNodeIndex) {
184
+ // According to Chrome Devtools, most shortcut edges are non-essential
185
+ // except at the root node, which have special meaning of marking user
186
+ // global objects
187
+ // NOTE: However, bound function may have a shortcut edge to the bound
188
+ // host object
189
+ return (edgeType !== 'weak' &&
190
+ (edgeType !== 'shortcut' || nodeIndex === rootNodeIndex));
191
+ }
192
+ function isFiberNodeDeletionsEdge(edge) {
193
+ if (!edge || !edge.fromNode || !edge.toNode) {
194
+ return false;
195
+ }
196
+ if (!isFiberNode(edge.fromNode)) {
197
+ return false;
198
+ }
199
+ return edge.name_or_index === 'deletions';
200
+ }
201
+ function isBlinkRootNode(node) {
202
+ if (!node || !node.name) {
203
+ return false;
204
+ }
205
+ return (node.type === 'synthetic' &&
206
+ (node.name === 'Blink cross-thread roots' || node.name === 'Blink roots'));
207
+ }
208
+ function isPendingActivityNode(node) {
209
+ if (!node || !node.name) {
210
+ return false;
211
+ }
212
+ return node.type === 'synthetic' && node.name === 'Pending activities';
213
+ }
214
+ function isRootNode(node, opt = {}) {
215
+ if (!node || !node.name) {
216
+ return false;
217
+ }
218
+ // consider Hermes snapshot GC roots
219
+ if (Config_1.default.jsEngine === 'hermes') {
220
+ return node.name === '(GC roots)' || node.name === '(GC Roots)';
221
+ }
222
+ // the window object
223
+ if (node.type === 'native' && node.name.indexOf('Window') === 0) {
224
+ return true;
225
+ }
226
+ if (node.type === 'synthetic' && node.name === '(GC roots)') {
227
+ return true;
228
+ }
229
+ if (!opt.excludeBlinkRoot && isBlinkRootNode(node)) {
230
+ return true;
231
+ }
232
+ if (!opt.excludePendingActivity && isPendingActivityNode(node)) {
233
+ return true;
234
+ }
235
+ return false;
236
+ }
237
+ // in Hermes engine, directProp edge is a shortcut reference
238
+ // and is less useful for debugging leak trace
239
+ const directPropRegex = /^directProp\d+$/;
240
+ function isDirectPropEdge(edge) {
241
+ return directPropRegex.test(`${edge.name_or_index}`);
242
+ }
243
+ function isReturnEdge(edge) {
244
+ if (!edge) {
245
+ return false;
246
+ }
247
+ if (typeof edge.name_or_index !== 'string') {
248
+ return false;
249
+ }
250
+ return edge.name_or_index.startsWith('return');
251
+ }
252
+ function isReactPropsEdge(edge) {
253
+ if (!edge) {
254
+ return false;
255
+ }
256
+ if (typeof edge.name_or_index !== 'string') {
257
+ return false;
258
+ }
259
+ return edge.name_or_index.startsWith('__reactProps$');
260
+ }
261
+ function isReactFiberEdge(edge) {
262
+ if (!edge) {
263
+ return false;
264
+ }
265
+ if (typeof edge.name_or_index !== 'string') {
266
+ return false;
267
+ }
268
+ return edge.name_or_index.startsWith('__reactFiber$');
269
+ }
270
+ function hasReactEdges(node) {
271
+ if (!node) {
272
+ return false;
273
+ }
274
+ let ret = false;
275
+ node.forEachReference((edge) => {
276
+ if (isReactFiberEdge(edge) || isReactPropsEdge(edge)) {
277
+ ret = true;
278
+ }
279
+ return { stop: true };
280
+ });
281
+ return ret;
282
+ }
283
+ // HostRoot's stateNode should be a FiberRootNode
284
+ function isHostRoot(node) {
285
+ if (!isFiberNode(node)) {
286
+ return false;
287
+ }
288
+ const stateNode = getToNodeByEdge(node, 'stateNode', 'property');
289
+ return !!stateNode && stateNode.name === 'FiberRootNode';
290
+ }
291
+ function getReactFiberNode(node, propName) {
292
+ if (!node || !isFiberNode(node)) {
293
+ return;
294
+ }
295
+ const targetNode = getToNodeByEdge(node, propName, 'property');
296
+ return isFiberNode(targetNode) ? targetNode : undefined;
297
+ }
298
+ // check if the current node's parent has the node as a child
299
+ function checkIsChildOfParent(node) {
300
+ const parent = getToNodeByEdge(node, 'return', 'property');
301
+ let matched = false;
302
+ iterateChildFiberNodes(parent, child => {
303
+ if (child.id === node.id) {
304
+ matched = true;
305
+ return { stop: true };
306
+ }
307
+ });
308
+ return matched;
309
+ }
310
+ // iterate through immediate children
311
+ function iterateChildFiberNodes(node, cb) {
312
+ if (!isFiberNode(node)) {
313
+ return;
314
+ }
315
+ const visited = new Set();
316
+ let cur = getReactFiberNode(node, 'child');
317
+ while (cur && isFiberNode(cur) && !visited.has(cur.id)) {
318
+ const ret = cb(cur);
319
+ visited.add(cur.id);
320
+ if (ret && ret.stop) {
321
+ break;
322
+ }
323
+ cur = getReactFiberNode(cur, 'sibling');
324
+ }
325
+ }
326
+ function iterateDescendantFiberNodes(node, iteratorCB) {
327
+ if (!isFiberNode(node)) {
328
+ return;
329
+ }
330
+ const visited = new Set();
331
+ const stack = [node];
332
+ while (stack.length > 0) {
333
+ const cur = stack.pop();
334
+ if (!cur) {
335
+ continue;
336
+ }
337
+ const ret = iteratorCB(cur);
338
+ visited.add(cur.id);
339
+ if (ret && ret.stop) {
340
+ break;
341
+ }
342
+ iterateChildFiberNodes(cur, child => {
343
+ if (visited.has(child.id)) {
344
+ return;
345
+ }
346
+ stack.push(child);
347
+ });
348
+ }
349
+ }
350
+ function getNodesIdSet(snapshot) {
351
+ const set = new Set();
352
+ snapshot.nodes.forEach(node => {
353
+ set.add(node.id);
354
+ });
355
+ return set;
356
+ }
357
+ // given a set of nodes S, return a subset S' where
358
+ // no nodes are dominaeted by nodes in S
359
+ function getConditionalDominatorIds(ids, snapshot, condCb) {
360
+ const dominatorIds = new Set();
361
+ // set all node ids
362
+ applyToNodes(ids, snapshot, node => {
363
+ if (condCb(node)) {
364
+ dominatorIds.add(node.id);
365
+ }
366
+ });
367
+ // traverse the dominators and remove the node
368
+ // if one of it's dominators is already in the set
369
+ applyToNodes(ids, snapshot, node => {
370
+ const visited = new Set([node.id]);
371
+ let cur = node.dominatorNode;
372
+ while (cur) {
373
+ if (visited.has(cur.id)) {
374
+ break;
375
+ }
376
+ if (dominatorIds.has(cur.id)) {
377
+ dominatorIds.delete(node.id);
378
+ break;
379
+ }
380
+ visited.add(cur.id);
381
+ cur = cur.dominatorNode;
382
+ }
383
+ });
384
+ return dominatorIds;
385
+ }
386
+ const ALTERNATE_NODE_FLAG = 0b1;
387
+ const REGULAR_NODE_FLAG = 0b10;
388
+ function setFiberNodeAttribute(node, flag) {
389
+ if (!node || !isFiberNode(node)) {
390
+ return;
391
+ }
392
+ // eslint-disable-next-line no-bitwise
393
+ node.attributes |= flag;
394
+ }
395
+ function hasFiberNodeAttribute(node, flag) {
396
+ if (!isFiberNode(node)) {
397
+ return false;
398
+ }
399
+ return !!(node.attributes & flag);
400
+ }
401
+ function setIsAlternateNode(node) {
402
+ setFiberNodeAttribute(node, ALTERNATE_NODE_FLAG);
403
+ }
404
+ function isAlternateNode(node) {
405
+ return hasFiberNodeAttribute(node, ALTERNATE_NODE_FLAG);
406
+ }
407
+ function setIsRegularFiberNode(node) {
408
+ setFiberNodeAttribute(node, REGULAR_NODE_FLAG);
409
+ }
410
+ function isRegularFiberNode(node) {
411
+ return hasFiberNodeAttribute(node, REGULAR_NODE_FLAG);
412
+ }
413
+ // The Fiber tree starts with a special type of Fiber node (HostRoot).
414
+ function hasHostRoot(node) {
415
+ if (node && node.is_detached) {
416
+ return false;
417
+ }
418
+ let cur = node;
419
+ const visitedIds = new Set();
420
+ const visitedNodes = new Set();
421
+ while (cur && isFiberNode(cur)) {
422
+ if (cur.id == null || visitedIds.has(cur.id)) {
423
+ break;
424
+ }
425
+ visitedNodes.add(cur);
426
+ visitedIds.add(cur.id);
427
+ if (isHostRoot(cur)) {
428
+ return true;
429
+ }
430
+ cur = getReactFiberNode(cur, 'return');
431
+ }
432
+ for (const visitedNode of visitedNodes) {
433
+ visitedNode.markAsDetached();
434
+ }
435
+ return false;
436
+ }
437
+ function filterNodesInPlace(idSet, snapshot, cb) {
438
+ const ids = Array.from(idSet.keys());
439
+ for (const id of ids) {
440
+ const node = snapshot.getNodeById(id);
441
+ if (node && !cb(node, snapshot)) {
442
+ idSet.delete(id);
443
+ }
444
+ }
445
+ }
446
+ function applyToNodes(idSet, snapshot, cb, options = {}) {
447
+ let ids = Array.from(idSet.keys());
448
+ if (options.shuffle) {
449
+ // eslint-disable-next-line fb-www/unsafe-math-random
450
+ ids.sort(() => Math.random() - 0.5);
451
+ }
452
+ else if (options.reverse) {
453
+ ids = ids.reverse();
454
+ }
455
+ for (const id of ids) {
456
+ const node = snapshot.getNodeById(id);
457
+ if (!node) {
458
+ Console_1.default.warning(`node @${id} is not found`);
459
+ return;
460
+ }
461
+ cb(node, snapshot);
462
+ }
463
+ }
464
+ function checkScenarioInstance(s) {
465
+ if (typeof s !== 'object' ||
466
+ typeof s.url !== 'function' ||
467
+ (s.action && typeof s.action !== 'function') ||
468
+ (s.back && typeof s.back !== 'function') ||
469
+ (s.repeat && typeof s.repeat !== 'function') ||
470
+ (s.isPageLoaded && typeof s.isPageLoaded !== 'function') ||
471
+ (s.leakFilter && typeof s.leakFilter !== 'function') ||
472
+ (s.beforeLeakFilter && typeof s.beforeLeakFilter !== 'function')) {
473
+ throw new Error('Invalid senario');
474
+ }
475
+ return s;
476
+ }
477
+ function loadLeakFilter(filename) {
478
+ const filepath = resolveFilePath(filename);
479
+ if (!filepath || !fs_1.default.existsSync(filepath)) {
480
+ // add a throw to silent the type error
481
+ throw haltOrThrow(`Leak filter definition file doesn't exist: ${filepath}`);
482
+ }
483
+ try {
484
+ // eslint-disable-next-line @typescript-eslint/no-var-requires
485
+ let filter = require(filepath);
486
+ if (typeof filter === 'function') {
487
+ return { leakFilter: filter };
488
+ }
489
+ filter = (filter === null || filter === void 0 ? void 0 : filter.default) || filter;
490
+ if (typeof filter === 'function') {
491
+ return { leakFilter: filter };
492
+ }
493
+ if (typeof (filter === null || filter === void 0 ? void 0 : filter.leakFilter) === 'function') {
494
+ return filter;
495
+ }
496
+ throw haltOrThrow(`Invalid leak filter in ${filepath}`);
497
+ }
498
+ catch (ex) {
499
+ throw haltOrThrow('Invalid leak filter definition file: ' + filename);
500
+ }
501
+ }
502
+ function loadScenario(filename) {
503
+ const filepath = resolveFilePath(filename);
504
+ if (!filepath || !fs_1.default.existsSync(filepath)) {
505
+ // add a throw to silent the type error
506
+ throw haltOrThrow(`Scenario file doesn't exist: ${filepath}`);
507
+ }
508
+ let scenario;
509
+ try {
510
+ // eslint-disable-next-line @typescript-eslint/no-var-requires
511
+ scenario = require(filepath);
512
+ scenario = checkScenarioInstance(scenario);
513
+ if (scenario.name == null) {
514
+ scenario.name = () => path_1.default.basename(filename);
515
+ }
516
+ return scenario;
517
+ }
518
+ catch (ex) {
519
+ throw haltOrThrow('Invalid scenario file: ' + filename);
520
+ }
521
+ }
522
+ function getScenarioName(scenario) {
523
+ if (!scenario.name) {
524
+ return Constant_1.default.namePrefixForScenarioFromFile;
525
+ }
526
+ return Constant_1.default.namePrefixForScenarioFromFile + '-' + scenario.name();
527
+ }
528
+ function handleSnapshotError(e) {
529
+ haltOrThrow(e, {
530
+ primaryMessageToPrint: 'Error parsing heap snapshot',
531
+ secondaryMessageToPrint: 'Please pass in a valid heap snapshot file',
532
+ });
533
+ }
534
+ function getSnapshotFromFile(filename, options) {
535
+ return __awaiter(this, void 0, void 0, function* () {
536
+ Console_1.default.overwrite('parsing ' + filename + ' ...');
537
+ let ret = null;
538
+ try {
539
+ ret = yield HeapParser_1.default.parse(filename, options);
540
+ }
541
+ catch (e) {
542
+ handleSnapshotError(getError(e));
543
+ }
544
+ return ret;
545
+ });
546
+ }
547
+ function getSnapshotNodeIdsFromFile(filename, options) {
548
+ return __awaiter(this, void 0, void 0, function* () {
549
+ Console_1.default.overwrite('lightweight parsing ' + filename + ' ...');
550
+ let ret = new Set();
551
+ try {
552
+ ret = yield HeapParser_1.default.getNodeIdsFromFile(filename, options);
553
+ }
554
+ catch (e) {
555
+ handleSnapshotError(getError(e));
556
+ }
557
+ return ret;
558
+ });
559
+ }
560
+ const weakMapKeyRegExp = /@(\d+)\) ->/;
561
+ function getWeakMapEdgeKeyId(edge) {
562
+ const name = edge.name_or_index;
563
+ if (typeof name !== 'string') {
564
+ return -1;
565
+ }
566
+ const ret = name.match(weakMapKeyRegExp);
567
+ if (!ret) {
568
+ return -1;
569
+ }
570
+ return Number(ret[1]);
571
+ }
572
+ function isDocumentDOMTreesRoot(node) {
573
+ if (!node) {
574
+ return false;
575
+ }
576
+ return node.type === 'synthetic' && node.name === '(Document DOM trees)';
577
+ }
578
+ function getEdgeByNameAndType(node, edgeName, type) {
579
+ if (!node) {
580
+ return null;
581
+ }
582
+ return node.findReference((edge) => edge.name_or_index === edgeName &&
583
+ (type === undefined || edge.type === type));
584
+ }
585
+ function getEdgeStartsWithName(node, prefix) {
586
+ if (!node) {
587
+ return null;
588
+ }
589
+ return node.findReference(edge => typeof edge.name_or_index === 'string' &&
590
+ edge.name_or_index.startsWith(prefix));
591
+ }
592
+ function isStringNode(node) {
593
+ const type = node.type;
594
+ return (type === 'string' ||
595
+ type === 'sliced string' ||
596
+ type === 'concatenated string');
597
+ }
598
+ function isSlicedStringNode(node) {
599
+ return node.type === 'sliced string';
600
+ }
601
+ function getStringNodeValue(node) {
602
+ var _a, _b, _c;
603
+ if (!node) {
604
+ return '';
605
+ }
606
+ if (node.type === 'concatenated string') {
607
+ const firstNode = (_a = getEdgeByNameAndType(node, 'first')) === null || _a === void 0 ? void 0 : _a.toNode;
608
+ const secondNode = (_b = getEdgeByNameAndType(node, 'second')) === null || _b === void 0 ? void 0 : _b.toNode;
609
+ return getStringNodeValue(firstNode) + getStringNodeValue(secondNode);
610
+ }
611
+ if (isSlicedStringNode(node)) {
612
+ const parentNode = (_c = getEdgeByNameAndType(node, 'parent')) === null || _c === void 0 ? void 0 : _c.toNode;
613
+ return getStringNodeValue(parentNode);
614
+ }
615
+ return node.name;
616
+ }
617
+ function extractClosureNodeInfo(node) {
618
+ let name = _extractClosureNodeInfo(node);
619
+ // replace all [, ], (, and )
620
+ name = name.replace(/[[\]()]/g, '');
621
+ return name;
622
+ }
623
+ function _extractClosureNodeInfo(node) {
624
+ if (!node) {
625
+ return '';
626
+ }
627
+ const name = node.name === '' ? '<anonymous>' : node.name;
628
+ if (node.type !== 'closure') {
629
+ return name;
630
+ }
631
+ // node.shared
632
+ const sharedEdge = getEdgeByNameAndType(node, 'shared');
633
+ if (!sharedEdge) {
634
+ return name;
635
+ }
636
+ // node.shared.function_data
637
+ const sharedNode = sharedEdge.toNode;
638
+ const functionDataEdge = getEdgeByNameAndType(sharedNode, 'function_data');
639
+ if (!functionDataEdge) {
640
+ return name;
641
+ }
642
+ // node.shared.function_data[0]
643
+ const functionDataNode = functionDataEdge.toNode;
644
+ const displaynameEdge = getEdgeByNameAndType(functionDataNode, 0, 'hidden');
645
+ if (!displaynameEdge) {
646
+ return name;
647
+ }
648
+ // extract display name
649
+ const displayNameNode = displaynameEdge.toNode;
650
+ if (displayNameNode.type === 'concatenated string' ||
651
+ displayNameNode.type === 'string' ||
652
+ displayNameNode.type === 'sliced string') {
653
+ const str = getStringNodeValue(displayNameNode);
654
+ if (str !== '') {
655
+ return `${name} ${str}`;
656
+ }
657
+ }
658
+ return name;
659
+ }
660
+ function extractFiberNodeInfo(node) {
661
+ let name = _extractFiberNodeInfo(node);
662
+ const tagName = _extractFiberNodeTagInfo(node);
663
+ if (tagName) {
664
+ name += ` ${tagName}`;
665
+ }
666
+ // simplify redundant pattern:
667
+ // "(Detached )FiberNode X from X.react" -> "(Detached )FiberNode X"
668
+ const detachedPrefix = 'Detached ';
669
+ let prefix = '';
670
+ if (name.startsWith(detachedPrefix)) {
671
+ prefix = detachedPrefix;
672
+ name = name.substring(detachedPrefix.length);
673
+ }
674
+ const matches = name.match(/^FiberNode (\w+) \[from (\w+)\.react\]$/);
675
+ if (matches && matches[1] === matches[2]) {
676
+ name = `FiberNode ${matches[1]}`;
677
+ }
678
+ // replace all [, ], (, and )
679
+ name = name.replace(/[[\]()]/g, '');
680
+ return prefix + name;
681
+ }
682
+ function getNumberNodeValue(node) {
683
+ if (!node) {
684
+ return null;
685
+ }
686
+ if (Config_1.default.jsEngine === 'hermes') {
687
+ return +node.name;
688
+ }
689
+ const valueNode = getToNodeByEdge(node, 'value', 'internal');
690
+ if (!valueNode) {
691
+ return null;
692
+ }
693
+ return +valueNode.name;
694
+ }
695
+ function getBooleanNodeValue(node) {
696
+ if (node === null || node === undefined) {
697
+ return null;
698
+ }
699
+ if (Config_1.default.jsEngine === 'hermes') {
700
+ return node.name === 'true';
701
+ }
702
+ const valueNode = getToNodeByEdge(node, 'value', 'internal');
703
+ if (valueNode === null || valueNode === undefined) {
704
+ return null;
705
+ }
706
+ return valueNode.name === 'true';
707
+ }
708
+ function _extractFiberNodeTagInfo(node) {
709
+ if (!node) {
710
+ return null;
711
+ }
712
+ if (!isFiberNode(node)) {
713
+ return null;
714
+ }
715
+ const tagNode = getToNodeByEdge(node, 'tag', 'property');
716
+ if (!tagNode) {
717
+ return null;
718
+ }
719
+ if (tagNode.type !== 'number') {
720
+ return null;
721
+ }
722
+ const tagId = getNumberNodeValue(tagNode);
723
+ return _getReactWorkTagName(tagId);
724
+ }
725
+ function getToNodeByEdge(node, propName, propType) {
726
+ const edge = getEdgeByNameAndType(node, propName, propType);
727
+ if (!edge) {
728
+ return null;
729
+ }
730
+ return edge.toNode;
731
+ }
732
+ function getSymbolNodeValue(node) {
733
+ if (!node || node.name !== 'symbol') {
734
+ return null;
735
+ }
736
+ const nameNode = getToNodeByEdge(node, 'name');
737
+ if (!nameNode) {
738
+ return null;
739
+ }
740
+ return nameNode.name;
741
+ }
742
+ function _extractFiberNodeInfo(node) {
743
+ if (!node) {
744
+ return '';
745
+ }
746
+ const name = node.name;
747
+ if (!isFiberNode(node)) {
748
+ return name;
749
+ }
750
+ // extract FiberNode.type
751
+ const typeNode = getToNodeByEdge(node, 'type', 'property');
752
+ if (!typeNode) {
753
+ return name;
754
+ }
755
+ if (typeNode.type === 'string') {
756
+ return `${name} ${typeNode.name}`;
757
+ }
758
+ // extract FiberNode.type.render
759
+ const renderNode = getToNodeByEdge(typeNode, 'render');
760
+ if (renderNode && renderNode.name) {
761
+ return `${name} ${renderNode.name}`;
762
+ }
763
+ // if FiberNode.type or FiberNode.elementType is a symbol
764
+ let value = getSymbolNodeValue(typeNode);
765
+ if (value) {
766
+ return `${name} ${value}`;
767
+ }
768
+ const elementTypeNode = getToNodeByEdge(node, 'elementType', 'property');
769
+ value = getSymbolNodeValue(elementTypeNode);
770
+ if (value) {
771
+ return `${name} ${value}`;
772
+ }
773
+ // extract FiberNode.elementType.$$typeof
774
+ const typeofNode = getToNodeByEdge(elementTypeNode, '$$typeof', 'property');
775
+ value = getSymbolNodeValue(typeofNode);
776
+ if (value) {
777
+ return `${name} ${value}`;
778
+ }
779
+ // extract FiberNode.type.displayName
780
+ const displayNameNode = getToNodeByEdge(typeNode, 'displayName');
781
+ if (!displayNameNode) {
782
+ return name;
783
+ }
784
+ if (displayNameNode.type === 'string') {
785
+ return `${name} ${displayNameNode.name}`;
786
+ }
787
+ if (displayNameNode.type === 'concatenated string') {
788
+ return `${name} ${getStringNodeValue(displayNameNode)}`;
789
+ }
790
+ return name;
791
+ }
792
+ function extractHTMLElementNodeInfo(node) {
793
+ if (!node) {
794
+ return '';
795
+ }
796
+ const reactFiberEdge = getEdgeStartsWithName(node, '__reactFiber$');
797
+ if (!reactFiberEdge) {
798
+ return node.name;
799
+ }
800
+ return `${node.name} ${extractFiberNodeInfo(reactFiberEdge.toNode)}`;
801
+ }
802
+ function hasOnlyWeakReferrers(node) {
803
+ const referrer = node.findAnyReferrer((edge) => edge.type !== 'weak' && edge.type !== 'shortcut');
804
+ return !!referrer;
805
+ }
806
+ function getRunMetaFilePath() {
807
+ return Config_1.default.useExternalSnapshot
808
+ ? Config_1.default.externalRunMetaFile
809
+ : Config_1.default.runMetaFile;
810
+ }
811
+ function loadRunMetaInfo() {
812
+ const file = getRunMetaFilePath();
813
+ try {
814
+ const content = fs_1.default.readFileSync(file, 'UTF-8');
815
+ return JSON.parse(content);
816
+ // eslint-disable-next-line fb-www/no-unused-catch-bindings
817
+ }
818
+ catch (_) {
819
+ throw haltOrThrow('Run info missing. Please make sure `memlab run` is complete.');
820
+ }
821
+ }
822
+ function loadTargetInfoFromRunMeta() {
823
+ const meta = loadRunMetaInfo();
824
+ Config_1.default.targetApp = meta.app;
825
+ Config_1.default.targetTab = meta.interaction;
826
+ BrowserInfo_1.default.load(meta.browserInfo);
827
+ }
828
+ function getSnapshotSequenceFilePath() {
829
+ if (!Config_1.default.useExternalSnapshot) {
830
+ // load the snapshot sequence meta file from the default location
831
+ return Config_1.default.snapshotSequenceFile;
832
+ }
833
+ if (Config_1.default.externalSnapshotDir) {
834
+ // try to load the snap-seq.json file from the specified external dir
835
+ const metaFile = path_1.default.join(Config_1.default.externalSnapshotDir, 'snap-seq.json');
836
+ if (fs_1.default.existsSync(metaFile)) {
837
+ return metaFile;
838
+ }
839
+ }
840
+ // otherwise return the default meta file for external snapshots
841
+ return Config_1.default.externalSnapshotVisitOrderFile;
842
+ }
843
+ // this should be called only after exploration
844
+ function loadTabsOrder() {
845
+ try {
846
+ const content = fs_1.default.readFileSync(getSnapshotSequenceFilePath(), 'UTF-8');
847
+ return JSON.parse(content);
848
+ }
849
+ catch (_a) {
850
+ throw haltOrThrow('snapshot meta data invalid or missing');
851
+ }
852
+ }
853
+ // if true the leak trace is will be reported
854
+ function isInterestingPath(p) {
855
+ // do not filter paths when analyzing Hermes snapshots
856
+ if (Config_1.default.jsEngine === 'hermes') {
857
+ return true;
858
+ }
859
+ // if the path has pattern: Window -> [InternalNode]+ -> DetachedElement
860
+ if (Config_1.default.hideBrowserLeak && internalNodeRetainsDetachedElement(p)) {
861
+ return false;
862
+ }
863
+ // if the path has pattern: ShadowRoot -> DetachedElement
864
+ if (Config_1.default.hideBrowserLeak && shadowRootRetainsDetachedElement(p)) {
865
+ return false;
866
+ }
867
+ return true;
868
+ }
869
+ // return true if the heap node represents JS object or closure
870
+ function isObjectNode(node) {
871
+ if (isPlainJSObjectNode(node)) {
872
+ return true;
873
+ }
874
+ return node.type === 'closure';
875
+ }
876
+ // return true if the heap node represents JS object
877
+ function isPlainJSObjectNode(node) {
878
+ if (!node) {
879
+ return false;
880
+ }
881
+ if (Config_1.default.jsEngine === 'hermes') {
882
+ return node.name === 'Object' || node.name.startsWith('Object(');
883
+ }
884
+ return node.name === 'Object';
885
+ }
886
+ // check if the path has pattern:
887
+ // Window -> [InternalNode | Text]+ -> DetachedElement
888
+ function internalNodeRetainsDetachedElement(path) {
889
+ var _a, _b;
890
+ if (!path) {
891
+ return false;
892
+ }
893
+ let p = path;
894
+ // GC root is not Window
895
+ if (!p.node || !p.node.name.startsWith('Window')) {
896
+ return false;
897
+ }
898
+ p = p.next;
899
+ // Window is not poining to InternalNode
900
+ if (!p || !p.node || p.node.name !== 'InternalNode') {
901
+ return false;
902
+ }
903
+ // skip the rest InternalNode
904
+ while (((_a = p.node) === null || _a === void 0 ? void 0 : _a.name) === 'InternalNode' || ((_b = p.node) === null || _b === void 0 ? void 0 : _b.name) === 'Text') {
905
+ p = p.next;
906
+ if (!p) {
907
+ return false;
908
+ }
909
+ }
910
+ // check if the node is a detached element
911
+ return p && isDetachedDOMNode(p.node);
912
+ }
913
+ // check if the path has pattern: ShadowRoot -> DetachedElement
914
+ function shadowRootRetainsDetachedElement(path) {
915
+ let p = path;
916
+ // find the ShadowRoot
917
+ while (p && p.node && p.node.name !== 'ShadowRoot') {
918
+ p = p.next;
919
+ if (!p) {
920
+ return false;
921
+ }
922
+ }
923
+ p = p.next;
924
+ // check if the node is a detached element
925
+ return !!p && isDetachedDOMNode(p.node);
926
+ }
927
+ function pathHasDetachedHTMLNode(path) {
928
+ if (!path) {
929
+ return false;
930
+ }
931
+ let p = path;
932
+ while (p) {
933
+ if (p.node && isDetachedDOMNode(p.node)) {
934
+ return true;
935
+ }
936
+ p = p.next;
937
+ }
938
+ return false;
939
+ }
940
+ function pathHasEdgeWithIndex(path, idx) {
941
+ if (!path || typeof idx !== 'number') {
942
+ return false;
943
+ }
944
+ let p = path;
945
+ while (p) {
946
+ if (p.edge && p.edge.edgeIndex === idx) {
947
+ return true;
948
+ }
949
+ p = p.next;
950
+ }
951
+ return false;
952
+ }
953
+ function getLastNodeId(path) {
954
+ if (!path) {
955
+ return -1;
956
+ }
957
+ let p = path;
958
+ while (p) {
959
+ if (!p.next && p.node) {
960
+ return p.node.id;
961
+ }
962
+ p = p.next;
963
+ }
964
+ return -1;
965
+ }
966
+ function getReadablePercent(num) {
967
+ if (Number.isNaN(num)) {
968
+ return `${num}%`;
969
+ }
970
+ const v = num * 100;
971
+ let str = v.toFixed(2);
972
+ if (str.endsWith('.00')) {
973
+ str = str.slice(0, -3);
974
+ }
975
+ else if (str.endsWith('0')) {
976
+ str = str.slice(0, -1);
977
+ }
978
+ return str + '%';
979
+ }
980
+ function getReadableBytes(bytes) {
981
+ let n, suffix;
982
+ if (bytes === undefined || bytes === null) {
983
+ return '';
984
+ }
985
+ if (bytes >= 1e12) {
986
+ n = ((bytes / 1e11) | 0) / 10;
987
+ suffix = 'TB';
988
+ }
989
+ else if (bytes >= 1e9) {
990
+ n = ((bytes / 1e8) | 0) / 10;
991
+ suffix = 'GB';
992
+ }
993
+ else if (bytes >= 1e6) {
994
+ n = ((bytes / 1e5) | 0) / 10;
995
+ suffix = 'MB';
996
+ }
997
+ else if (bytes >= 1e3) {
998
+ n = ((bytes / 1e2) | 0) / 10;
999
+ suffix = 'KB';
1000
+ }
1001
+ else if (bytes > 1) {
1002
+ n = bytes;
1003
+ suffix = ' bytes';
1004
+ }
1005
+ else if (bytes >= 0) {
1006
+ n = bytes;
1007
+ suffix = ' byte';
1008
+ }
1009
+ else {
1010
+ return '';
1011
+ }
1012
+ return n + suffix;
1013
+ }
1014
+ function p1(n, divide) {
1015
+ return (((n * 10) / divide) | 0) / 10;
1016
+ }
1017
+ function getReadableTime(ms) {
1018
+ let time = ms;
1019
+ if (time < 1000) {
1020
+ return `${time}ms`;
1021
+ }
1022
+ time /= 1000;
1023
+ if (time < 60) {
1024
+ return `${p1(time, 1)}s`;
1025
+ }
1026
+ time /= 60;
1027
+ if (time < 60) {
1028
+ return `${p1(time, 1)}min`;
1029
+ }
1030
+ time /= 60;
1031
+ if (time < 24) {
1032
+ return `${p1(time, 1)}hr`;
1033
+ }
1034
+ time /= 24;
1035
+ return `${p1(time, 1)} days`;
1036
+ }
1037
+ function shouldShowMoreInfo(node) {
1038
+ if (!node || !node.name) {
1039
+ return false;
1040
+ }
1041
+ if (!Config_1.default.nodeToShowMoreInfo) {
1042
+ return false;
1043
+ }
1044
+ return Config_1.default.nodeToShowMoreInfo.has(node.name);
1045
+ }
1046
+ function isDebuggableNode(node) {
1047
+ if (!node) {
1048
+ return false;
1049
+ }
1050
+ if (node.type === 'native' && !isDetachedDOMNode(node)) {
1051
+ return false;
1052
+ }
1053
+ if (node.type === 'hidden' ||
1054
+ node.type === 'array' ||
1055
+ node.type === 'string' ||
1056
+ node.type === 'number' ||
1057
+ node.type === 'concatenated string' ||
1058
+ node.type === 'sliced string' ||
1059
+ node.type === 'code' ||
1060
+ node.name === 'system / Context') {
1061
+ return false;
1062
+ }
1063
+ return true;
1064
+ }
1065
+ function throwError(error) {
1066
+ if (error) {
1067
+ error.stack;
1068
+ }
1069
+ throw error;
1070
+ }
1071
+ function callAsync(f) {
1072
+ const promise = f();
1073
+ if (promise && promise.catch) {
1074
+ promise.catch((e) => {
1075
+ var _a;
1076
+ const parsedError = getError(e);
1077
+ Console_1.default.error(parsedError.message);
1078
+ Console_1.default.lowLevel((_a = parsedError.stack) !== null && _a !== void 0 ? _a : '');
1079
+ });
1080
+ }
1081
+ }
1082
+ function checkUninstalledLibrary(ex) {
1083
+ var _a;
1084
+ const stackStr = (_a = ex.stack) === null || _a === void 0 ? void 0 : _a.toString();
1085
+ if (stackStr === null || stackStr === void 0 ? void 0 : stackStr.includes('cannot open shared object file')) {
1086
+ haltOrThrow(ex, {
1087
+ primaryMessageToPrint: 'Could not launch Chrome. To run MemLab on a CentOS 8 devserver, please run the following command:\n',
1088
+ secondaryMessageToPrint: 'sudo dnf install nss libwayland-client libwayland-egl egl-wayland libpng15 mesa-libGL atk java-atk-wrapper at-spi2-atk gtk3 libXt',
1089
+ });
1090
+ }
1091
+ }
1092
+ function closePuppeteer(browser, pages, options = {}) {
1093
+ return __awaiter(this, void 0, void 0, function* () {
1094
+ if (Config_1.default.isLocalPuppeteer && !options.warmup) {
1095
+ yield Promise.all(pages.map(page => page.close()));
1096
+ yield browser.disconnect();
1097
+ }
1098
+ else {
1099
+ yield browser.close();
1100
+ }
1101
+ });
1102
+ }
1103
+ function camelCaseToReadableString(str) {
1104
+ let ret = '';
1105
+ const isUpperCase = (c) => /^[A-Z]$/.test(c);
1106
+ for (const c of str) {
1107
+ if (isUpperCase(c)) {
1108
+ ret += ret.length > 0 ? ' ' : '';
1109
+ ret += c.toLowerCase();
1110
+ }
1111
+ else {
1112
+ ret += c;
1113
+ }
1114
+ }
1115
+ return ret;
1116
+ }
1117
+ // Given a file path (relative or absolute),
1118
+ // this function tries to resolve to a absolute path that exists
1119
+ // in MemLab's directories.
1120
+ // if nothing is found, it returns null.
1121
+ function resolveFilePath(file) {
1122
+ if (!file) {
1123
+ return null;
1124
+ }
1125
+ const dirs = [
1126
+ Config_1.default.curDataDir,
1127
+ Config_1.default.persistentDataDir,
1128
+ Config_1.default.monoRepoDir,
1129
+ ];
1130
+ const paths = [file].concat(dirs.map(d => path_1.default.join(d, file)));
1131
+ for (const p of paths) {
1132
+ const filepath = path_1.default.resolve(p);
1133
+ if (fs_1.default.existsSync(filepath)) {
1134
+ return filepath;
1135
+ }
1136
+ }
1137
+ return null;
1138
+ }
1139
+ const snapshotNamePattern = /^s(\d+)\.heapsnapshot$/;
1140
+ function compareSnapshotName(f1, f2) {
1141
+ // if file name follows the 's{\d+}.heapsnapshot' pattern
1142
+ // then order based on the ascending order of the number
1143
+ const m1 = f1.match(snapshotNamePattern);
1144
+ const m2 = f2.match(snapshotNamePattern);
1145
+ if (m1 && m2) {
1146
+ return parseInt(m1[1], 10) - parseInt(m2[1], 10);
1147
+ }
1148
+ // otherwise sort in alpha numeric order
1149
+ return f1 < f2 ? -1 : f1 === f2 ? 0 : 1;
1150
+ }
1151
+ function getSnapshotFilesInDir(dir) {
1152
+ try {
1153
+ return fs_1.default
1154
+ .readdirSync(dir)
1155
+ .filter(file => file.endsWith('.heapsnapshot'))
1156
+ .sort(compareSnapshotName)
1157
+ .map(file => path_1.default.join(dir, file));
1158
+ }
1159
+ catch (ex) {
1160
+ throw __1.utils.haltOrThrow(__1.utils.getError(ex));
1161
+ }
1162
+ }
1163
+ function getSnapshotFilesFromTabsOrder(options = {}) {
1164
+ const tabsOrder = loadTabsOrder();
1165
+ const ret = [];
1166
+ const typesSeen = new Set();
1167
+ for (let i = 0; i < tabsOrder.length; i++) {
1168
+ const tab = tabsOrder[i];
1169
+ if (!tab.snapshot) {
1170
+ continue;
1171
+ }
1172
+ if (tab.type) {
1173
+ typesSeen.add(tab.type);
1174
+ }
1175
+ if (options.skipBeforeTabType &&
1176
+ !typesSeen.has(options.skipBeforeTabType)) {
1177
+ continue;
1178
+ }
1179
+ ret.push(getSnapshotFilePath(tab));
1180
+ }
1181
+ return ret;
1182
+ }
1183
+ // checks if the snapshots along with their meta data are complete
1184
+ function checkSnapshots(options = {}) {
1185
+ if (Config_1.default.skipSnapshot) {
1186
+ haltOrThrow('This command is run with `--no-snapshot`, skip snapshot check.');
1187
+ }
1188
+ let snapshotDir;
1189
+ if (options.snapshotDir) {
1190
+ snapshotDir = options.snapshotDir;
1191
+ }
1192
+ else if (Config_1.default.useExternalSnapshot) {
1193
+ snapshotDir = Config_1.default.externalSnapshotDir || '<missing>';
1194
+ }
1195
+ else {
1196
+ snapshotDir = FileManager_1.default.getCurDataDir({});
1197
+ }
1198
+ if (options.snapshotDir) {
1199
+ const snapshots = getSnapshotFilesInDir(snapshotDir);
1200
+ const min = options.minSnapshots || 1;
1201
+ if (snapshots.length < min) {
1202
+ __1.utils.haltOrThrow(`Directory has < ${min} snapshot files: ${options.snapshotDir}`);
1203
+ }
1204
+ return;
1205
+ }
1206
+ // check if any snapshot file is missing
1207
+ const tabsOrder = loadTabsOrder();
1208
+ const missingTabs = Object.create(null);
1209
+ let miss = 0;
1210
+ for (const tab of tabsOrder) {
1211
+ if (!tab.snapshot) {
1212
+ continue;
1213
+ }
1214
+ const file = getSnapshotFilePath(tab);
1215
+ if (!fs_1.default.existsSync(file)) {
1216
+ ++miss;
1217
+ missingTabs[tab.idx] = {
1218
+ name: tab.name,
1219
+ url: tab.url,
1220
+ type: tab.type,
1221
+ };
1222
+ }
1223
+ }
1224
+ if (miss > 0) {
1225
+ const msg = 'snapshot for the following tabs are missing:';
1226
+ const printCallback = () => {
1227
+ Console_1.default.warning(msg);
1228
+ Console_1.default.table(missingTabs);
1229
+ };
1230
+ haltOrThrow(msg + JSON.stringify(missingTabs, null, 2), {
1231
+ printCallback,
1232
+ });
1233
+ }
1234
+ }
1235
+ function resolveSnapshotFilePath(snapshotFile) {
1236
+ const file = resolveFilePath(snapshotFile);
1237
+ if (!file) {
1238
+ throw haltOrThrow(new Error(`Error: snapshot file doesn't exist ${snapshotFile}`));
1239
+ }
1240
+ return file;
1241
+ }
1242
+ exports.resolveSnapshotFilePath = resolveSnapshotFilePath;
1243
+ function getSnapshotDirForAnalysis() {
1244
+ const dir = Config_1.default.externalSnapshotDir;
1245
+ if (!dir) {
1246
+ throw __1.utils.haltOrThrow(new Error('external snapshot file not set'));
1247
+ }
1248
+ return dir;
1249
+ }
1250
+ function getSingleSnapshotFileForAnalysis() {
1251
+ const path = Config_1.default.useExternalSnapshot && Config_1.default.externalSnapshotFilePaths[0]
1252
+ ? Config_1.default.externalSnapshotFilePaths[0]
1253
+ : getSnapshotFilePathWithTabType(/(final)|(target)|(baseline)/);
1254
+ return resolveSnapshotFilePath(path);
1255
+ }
1256
+ function getSnapshotFilePath(tab) {
1257
+ if (!Config_1.default.useExternalSnapshot) {
1258
+ return path_1.default.join(Config_1.default.curDataDir, `s${tab.idx}.heapsnapshot`);
1259
+ }
1260
+ // if we are loading snapshot from external snapshot dir
1261
+ if (Config_1.default.externalSnapshotDir) {
1262
+ return path_1.default.join(Config_1.default.externalSnapshotDir, `s${tab.idx}.heapsnapshot`);
1263
+ }
1264
+ return Config_1.default.externalSnapshotFilePaths[tab.idx - 1];
1265
+ }
1266
+ // eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/no-explicit-any
1267
+ function equalOrMatch(v1, v2) {
1268
+ const t1 = typeof v1;
1269
+ const t2 = typeof v2;
1270
+ if (t1 === t2) {
1271
+ return v1 === v2;
1272
+ }
1273
+ if (t1 === 'string' && v2 instanceof RegExp) {
1274
+ return v2.test(v1);
1275
+ }
1276
+ if (t2 === 'string' && v1 instanceof RegExp) {
1277
+ return v1.test(v2);
1278
+ }
1279
+ return false;
1280
+ }
1281
+ function getSnapshotFilePathWithTabType(type) {
1282
+ checkSnapshots();
1283
+ const tabsOrder = loadTabsOrder();
1284
+ for (let i = tabsOrder.length - 1; i >= 0; --i) {
1285
+ const tab = tabsOrder[i];
1286
+ if (!tab.snapshot) {
1287
+ continue;
1288
+ }
1289
+ if (equalOrMatch(tab.type, type)) {
1290
+ return getSnapshotFilePath(tab);
1291
+ }
1292
+ }
1293
+ return null;
1294
+ }
1295
+ function isMeaningfulNode(node) {
1296
+ if (!node) {
1297
+ return false;
1298
+ }
1299
+ if (Config_1.default.nodeNameBlockList.has(node.name)) {
1300
+ return false;
1301
+ }
1302
+ if (isFiberNode(node)) {
1303
+ const displayName = extractFiberNodeInfo(node);
1304
+ if (Config_1.default.nodeNameBlockList.has(displayName)) {
1305
+ return false;
1306
+ }
1307
+ }
1308
+ // More details in https://github.com/ChromeDevTools/devtools-frontend
1309
+ // under front_end/heap_snapshot_worker/HeapSnapshot.ts
1310
+ if (node.name === 'system / NativeContext') {
1311
+ return false;
1312
+ }
1313
+ if (node.name === 'system / SourcePositionTableWithFrameCache') {
1314
+ return false;
1315
+ }
1316
+ if (node.name === '(map descriptors)') {
1317
+ return false;
1318
+ }
1319
+ if (node.type === 'code') {
1320
+ return false;
1321
+ }
1322
+ return true;
1323
+ }
1324
+ function isMeaningfulEdge(edge, options = {}) {
1325
+ const node = options.isForward ? edge.toNode : edge.fromNode;
1326
+ const source = options.isForward ? edge.fromNode : edge.toNode;
1327
+ // exclude self references
1328
+ if (source.id === node.id) {
1329
+ return false;
1330
+ }
1331
+ if (typeof edge.name_or_index === 'string' &&
1332
+ Config_1.default.edgeNameBlockList.has(edge.name_or_index)) {
1333
+ return false;
1334
+ }
1335
+ // shortcut edge may be meaningful edges
1336
+ // --forceUpdate (variable)---> [native_bind]
1337
+ // --bound_argument_0 (shortcut)---> [FiberNode]
1338
+ if (edge.type === 'weak' /* || edge.type === 'shortcut' */) {
1339
+ return false;
1340
+ }
1341
+ if (options.excludeWeakMapEdge && isWeakMapEdgeToKey(edge)) {
1342
+ return false;
1343
+ }
1344
+ if (options.visited && options.visited[node.nodeIndex]) {
1345
+ return false;
1346
+ }
1347
+ if (options.queued && options.queued[node.nodeIndex]) {
1348
+ return false;
1349
+ }
1350
+ if (!options.includeString && node.type === 'string') {
1351
+ return false;
1352
+ }
1353
+ if (edge.type === 'internal' && edge.name_or_index === 'code') {
1354
+ return false;
1355
+ }
1356
+ // More details about the following three special cases are available
1357
+ // in https://github.com/ChromeDevTools/devtools-frontend
1358
+ // under front_end/heap_snapshot_worker/HeapSnapshot.ts
1359
+ if (edge.type === 'hidden' && edge.name_or_index === 'sloppy_function_map') {
1360
+ return false;
1361
+ }
1362
+ if (edge.type === 'hidden' && node.name === 'system / NativeContext') {
1363
+ return false;
1364
+ }
1365
+ // In v8, (map descriptors) are fixed-length descriptors arrays used
1366
+ // to hold JS descriptors.
1367
+ if (node.type === 'array' && node.name === '(map descriptors)') {
1368
+ const index = edge.name_or_index;
1369
+ // only elements at particular indexes of (map descriptors) are holding
1370
+ // representative references to objects.
1371
+ if (index >= 2 || (typeof index === 'number' && index % 3 === 1)) {
1372
+ return false;
1373
+ }
1374
+ }
1375
+ if (!isMeaningfulNode(node)) {
1376
+ return false;
1377
+ }
1378
+ if (Config_1.default.jsEngine === 'hermes' && isDirectPropEdge(edge)) {
1379
+ return false;
1380
+ }
1381
+ if (Config_1.default.ignoreInternalNode && node.name.includes('InternalNode')) {
1382
+ return false;
1383
+ }
1384
+ if (Config_1.default.ignoreDevToolsConsoleLeak) {
1385
+ const name = edge.name_or_index;
1386
+ if (typeof name === 'string' && name.includes('DevTools console')) {
1387
+ return false;
1388
+ }
1389
+ }
1390
+ return true;
1391
+ }
1392
+ // check if two URLs are equivalent
1393
+ // for example, the following pairs are equal
1394
+ // 'https://test.com/?a=1&b=2&a=3', 'https://test.com?b=2&a=3&a=1'
1395
+ // 'https://test.com/p1/p2?a=1,b=2', 'https://test.com/p1/p2/?a=1,b=2'
1396
+ function isURLEqual(url1, url2) {
1397
+ let u1, u2;
1398
+ try {
1399
+ u1 = new URL(url1);
1400
+ u2 = new URL(url2);
1401
+ // eslint-disable-next-line fb-www/no-unused-catch-bindings
1402
+ }
1403
+ catch (_e) {
1404
+ return false;
1405
+ }
1406
+ // compare URL fields
1407
+ if (u1.protocol !== u2.protocol ||
1408
+ u1.host !== u2.host ||
1409
+ u1.hostname !== u2.hostname ||
1410
+ u1.port !== u2.port ||
1411
+ u1.hash !== u2.hash) {
1412
+ return false;
1413
+ }
1414
+ // compare path
1415
+ let p1 = u1.pathname;
1416
+ p1 = p1.endsWith('/') ? p1.slice(0, -1) : p1;
1417
+ let p2 = u2.pathname;
1418
+ p2 = p2.endsWith('/') ? p2.slice(0, -1) : p2;
1419
+ if (p1 !== p2) {
1420
+ return false;
1421
+ }
1422
+ // compare URL params
1423
+ const paramKeys = new Set([
1424
+ ...Array.from(u1.searchParams.keys()),
1425
+ ...Array.from(u2.searchParams.keys()),
1426
+ ]);
1427
+ for (const key of paramKeys) {
1428
+ const v1 = u1.searchParams.getAll(key).sort().join(' ');
1429
+ const v2 = u2.searchParams.getAll(key).sort().join(' ');
1430
+ if (v1 !== v2) {
1431
+ return false;
1432
+ }
1433
+ }
1434
+ return true;
1435
+ }
1436
+ function shuffleArray(array) {
1437
+ for (let i = array.length - 1; i > 0; i--) {
1438
+ const j = Math.floor(Math.random() * (i + 1));
1439
+ [array[i], array[j]] = [array[j], array[i]];
1440
+ }
1441
+ return array;
1442
+ }
1443
+ function getLeakedNode(path) {
1444
+ let p = path;
1445
+ const set = new Set([p]);
1446
+ while (p.next && !set.has(p.next)) {
1447
+ set.add(p.next);
1448
+ p = p.next;
1449
+ }
1450
+ if (!p || !p.node) {
1451
+ return null;
1452
+ }
1453
+ return p.node;
1454
+ }
1455
+ // print snapshot to file for local testing
1456
+ function dumpSnapshot(file, snapshot) {
1457
+ fs_1.default.writeFileSync(file, JSON.stringify(snapshot.snapshot.meta, null, 0), 'UTF-8');
1458
+ const dumpSection = (name, arr) => {
1459
+ let buf = '';
1460
+ fs_1.default.appendFileSync(file, `\n\n ${name}:\n\n`, 'UTF-8');
1461
+ for (let i = 0; i < arr.length; ++i) {
1462
+ buf += arr[i] + ',';
1463
+ if (buf.length > 1024 * 1024) {
1464
+ fs_1.default.appendFileSync(file, '\n\n' + buf, 'UTF-8');
1465
+ buf = '';
1466
+ }
1467
+ }
1468
+ fs_1.default.appendFileSync(file, '\n\n' + buf, 'UTF-8');
1469
+ buf = '';
1470
+ };
1471
+ dumpSection('nodes', snapshot.nodes);
1472
+ dumpSection('edges', snapshot.edges);
1473
+ dumpSection('locations', snapshot.locations);
1474
+ }
1475
+ function markAllDetachedFiberNode(snapshot) {
1476
+ Console_1.default.overwrite('marking all detached Fiber nodes...');
1477
+ snapshot.nodes.forEach(node => {
1478
+ // hasHostRoot checks and marks detached Fiber Nodes
1479
+ isFiberNode(node) && !hasHostRoot(node);
1480
+ });
1481
+ }
1482
+ function markAlternateFiberNode(snapshot) {
1483
+ Console_1.default.overwrite('marking alternate Fiber nodes...');
1484
+ snapshot.nodes.forEach(node => {
1485
+ // mark the fiber root node
1486
+ if (!isHostRoot(node)) {
1487
+ return;
1488
+ }
1489
+ iterateDescendantFiberNodes(node, (descendant) => {
1490
+ // check if the node is doubly linked to its parent
1491
+ if (checkIsChildOfParent(descendant)) {
1492
+ setIsRegularFiberNode(descendant);
1493
+ }
1494
+ // mark explicit alternate fiber node
1495
+ setIsAlternateNode(getToNodeByEdge(descendant, 'alternate', 'property'));
1496
+ });
1497
+ });
1498
+ }
1499
+ function getAllDominators(node) {
1500
+ const visited = new Set();
1501
+ const dominators = [];
1502
+ let cur = node;
1503
+ while (cur && !visited.has(cur.id)) {
1504
+ visited.add(cur.id);
1505
+ dominators.push(cur);
1506
+ cur = cur.dominatorNode;
1507
+ }
1508
+ return dominators;
1509
+ }
1510
+ function upperCaseFirstCharacter(text) {
1511
+ if (text.length === 0) {
1512
+ return text;
1513
+ }
1514
+ return text[0].toUpperCase() + text.substring(1);
1515
+ }
1516
+ function repeat(str, n) {
1517
+ let ret = '';
1518
+ for (let i = 0; i < n; ++i) {
1519
+ ret += str;
1520
+ }
1521
+ return ret;
1522
+ }
1523
+ function normalizeBaseUrl(url) {
1524
+ let ret = url;
1525
+ if (url.length > 0 &&
1526
+ !url.endsWith('.html') &&
1527
+ !url.endsWith('.htm') &&
1528
+ !url.endsWith('/')) {
1529
+ ret += '/';
1530
+ }
1531
+ return ret;
1532
+ }
1533
+ function haltOrThrow(errorInfo, options = {}) {
1534
+ var _a;
1535
+ const err = getError(errorInfo);
1536
+ const halt = () => __awaiter(this, void 0, void 0, function* () {
1537
+ var _b;
1538
+ if (options.printErrorBeforeHalting !== false) {
1539
+ // only print the error.message when there is no
1540
+ // primary message to print or there is no print callback
1541
+ if (!options.primaryMessageToPrint && !options.printCallback) {
1542
+ Console_1.default.error(err.message);
1543
+ }
1544
+ // only print stack trace in verbose mode
1545
+ if (Config_1.default.verbose) {
1546
+ Console_1.default.lowLevel((_b = err.stack) !== null && _b !== void 0 ? _b : '');
1547
+ }
1548
+ else {
1549
+ Console_1.default.topLevel('Use `memlab help` or `memlab <COMMAND> -h` to get helper text');
1550
+ }
1551
+ if (options.primaryMessageToPrint) {
1552
+ Console_1.default.error(options.primaryMessageToPrint);
1553
+ }
1554
+ if (options.secondaryMessageToPrint) {
1555
+ Console_1.default.lowLevel(options.secondaryMessageToPrint);
1556
+ }
1557
+ if (options.printCallback) {
1558
+ options.printCallback();
1559
+ }
1560
+ }
1561
+ throw process_1.default.exit(1);
1562
+ });
1563
+ const throwErr = () => {
1564
+ let message = '';
1565
+ // show primary message
1566
+ if (options.primaryMessageToPrint) {
1567
+ message = options.primaryMessageToPrint;
1568
+ }
1569
+ else {
1570
+ message = err.message;
1571
+ }
1572
+ // append secondary message
1573
+ if (options.secondaryMessageToPrint) {
1574
+ message += `(${options.secondaryMessageToPrint})`;
1575
+ }
1576
+ // if already specified a primary message,
1577
+ // append the error.message at the end
1578
+ if (options.primaryMessageToPrint) {
1579
+ if (message.length > 0 && !message.endsWith('.')) {
1580
+ message += '. ';
1581
+ }
1582
+ message += err.message;
1583
+ }
1584
+ err.message = message;
1585
+ throw err;
1586
+ };
1587
+ const handling = (_a = options === null || options === void 0 ? void 0 : options.errorHandling) !== null && _a !== void 0 ? _a : Config_1.default.errorHandling;
1588
+ switch (handling) {
1589
+ case Config_1.ErrorHandling.Halt:
1590
+ halt();
1591
+ break;
1592
+ case Config_1.ErrorHandling.Throw:
1593
+ throwErr();
1594
+ break;
1595
+ }
1596
+ throw 'unreachable';
1597
+ }
1598
+ function getError(maybeError) {
1599
+ if (maybeError instanceof Error) {
1600
+ return maybeError;
1601
+ }
1602
+ return convertToError(maybeError);
1603
+ }
1604
+ function convertToError(maybeError) {
1605
+ if (isErrorWithMessage(maybeError)) {
1606
+ return new Error(maybeError.message);
1607
+ }
1608
+ try {
1609
+ const msg = typeof maybeError === 'string' ? maybeError : JSON.stringify(maybeError);
1610
+ return new Error(msg);
1611
+ }
1612
+ catch (_a) {
1613
+ // fallback in case there's an error stringifying the maybeError
1614
+ // like with circular references for example.
1615
+ return new Error(String(maybeError));
1616
+ }
1617
+ }
1618
+ function isErrorWithMessage(error) {
1619
+ return (typeof error === 'object' &&
1620
+ error !== null &&
1621
+ 'message' in error &&
1622
+ typeof error.message === 'string');
1623
+ }
1624
+ // check if a node is dominated by an array referenced as 'deletions'
1625
+ // React stores unmounted fiber nodes that will be deleted soon in
1626
+ // a 'deletions' array.
1627
+ function isNodeDominatedByDeletionsArray(node) {
1628
+ const dominators = getAllDominators(node);
1629
+ return dominators.some(dominator => {
1630
+ const edges = dominator.referrers;
1631
+ return edges.some(e => e.name_or_index === 'deletions');
1632
+ });
1633
+ }
1634
+ let uindex = 1;
1635
+ function getUniqueID() {
1636
+ return `${process_1.default.pid}-${Date.now()}-${uindex++}`;
1637
+ }
1638
+ exports.default = {
1639
+ applyToNodes,
1640
+ callAsync,
1641
+ camelCaseToReadableString,
1642
+ checkSnapshots,
1643
+ checkUninstalledLibrary,
1644
+ checkIsChildOfParent,
1645
+ closePuppeteer,
1646
+ dumpSnapshot,
1647
+ equalOrMatch,
1648
+ extractClosureNodeInfo,
1649
+ extractFiberNodeInfo,
1650
+ extractHTMLElementNodeInfo,
1651
+ filterNodesInPlace,
1652
+ getAllDominators,
1653
+ getConditionalDominatorIds,
1654
+ getError,
1655
+ getEdgeByNameAndType,
1656
+ getLastNodeId,
1657
+ getLeakedNode,
1658
+ getNodesIdSet,
1659
+ getNumberNodeValue,
1660
+ getReadableBytes,
1661
+ getReadablePercent,
1662
+ getReadableTime,
1663
+ getRunMetaFilePath,
1664
+ getScenarioName,
1665
+ getSingleSnapshotFileForAnalysis,
1666
+ getSnapshotDirForAnalysis,
1667
+ getSnapshotFilesInDir,
1668
+ getSnapshotFilesFromTabsOrder,
1669
+ getSnapshotFromFile,
1670
+ getSnapshotNodeIdsFromFile,
1671
+ getSnapshotSequenceFilePath,
1672
+ getSnapshotFilePath,
1673
+ getSnapshotFilePathWithTabType,
1674
+ getStringNodeValue,
1675
+ getToNodeByEdge,
1676
+ getUniqueID,
1677
+ getWeakMapEdgeKeyId,
1678
+ haltOrThrow,
1679
+ hasHostRoot,
1680
+ hasOnlyWeakReferrers,
1681
+ hasReactEdges,
1682
+ isAlternateNode,
1683
+ isBlinkRootNode,
1684
+ isDebuggableNode,
1685
+ isDetachedFiberNode,
1686
+ isDetachedDOMNode,
1687
+ isDirectPropEdge,
1688
+ isDocumentDOMTreesRoot,
1689
+ isEssentialEdge,
1690
+ isFiberNode,
1691
+ isFiberNodeDeletionsEdge,
1692
+ isHermesInternalObject,
1693
+ isHostRoot,
1694
+ isInterestingPath,
1695
+ isMeaningfulEdge,
1696
+ isMeaningfulNode,
1697
+ isNodeDominatedByDeletionsArray,
1698
+ isObjectNode,
1699
+ isPendingActivityNode,
1700
+ isPlainJSObjectNode,
1701
+ isReactFiberEdge,
1702
+ isReactPropsEdge,
1703
+ isRegularFiberNode,
1704
+ isReturnEdge,
1705
+ isRootNode,
1706
+ isSlicedStringNode,
1707
+ isStackTraceFrame,
1708
+ isStringNode,
1709
+ isURLEqual,
1710
+ isWeakMapEdge,
1711
+ isWeakMapEdgeToKey,
1712
+ isWeakMapEdgeToValue,
1713
+ iterateChildFiberNodes,
1714
+ iterateDescendantFiberNodes,
1715
+ loadRunMetaInfo,
1716
+ loadLeakFilter,
1717
+ loadScenario,
1718
+ loadTabsOrder,
1719
+ loadTargetInfoFromRunMeta,
1720
+ markAllDetachedFiberNode,
1721
+ markAlternateFiberNode,
1722
+ memCache,
1723
+ normalizeBaseUrl,
1724
+ pathHasDetachedHTMLNode,
1725
+ pathHasEdgeWithIndex,
1726
+ repeat,
1727
+ resolveFilePath,
1728
+ resolveSnapshotFilePath,
1729
+ setIsAlternateNode,
1730
+ setIsRegularFiberNode,
1731
+ shouldShowMoreInfo,
1732
+ shuffleArray,
1733
+ throwError,
1734
+ upperCaseFirstCharacter,
1735
+ getBooleanNodeValue,
1736
+ };