@typeslayer/analyze-trace 0.0.0 → 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +85 -0
- package/bin/typeslayer-analyze-trace.mjs +42 -0
- package/dist/index.d.ts +4307 -0
- package/dist/index.js +669 -0
- package/dist/index.js.map +1 -0
- package/package.json +33 -2
- package/src/analyze-trace.ts +99 -0
- package/src/constants.ts +2 -0
- package/src/depth-limits.ts +106 -0
- package/src/get-duplicate-node-modules.ts +46 -0
- package/src/get-hotspots.ts +245 -0
- package/src/index.ts +4 -0
- package/src/node-module-paths.ts +44 -0
- package/src/spans.ts +137 -0
- package/src/utils.ts +185 -0
- package/tsconfig.json +13 -0
- package/tsup.config.ts +11 -0
package/dist/index.js
ADDED
|
@@ -0,0 +1,669 @@
|
|
|
1
|
+
// src/analyze-trace.ts
|
|
2
|
+
import { existsSync as existsSync3 } from "fs";
|
|
3
|
+
import { readFile as readFile2, writeFile } from "fs/promises";
|
|
4
|
+
import { join as join2 } from "path";
|
|
5
|
+
import {
|
|
6
|
+
TRACE_JSON_FILENAME,
|
|
7
|
+
TYPES_JSON_FILENAME,
|
|
8
|
+
traceJsonSchema,
|
|
9
|
+
typesJsonSchema
|
|
10
|
+
} from "@typeslayer/validate";
|
|
11
|
+
|
|
12
|
+
// src/constants.ts
|
|
13
|
+
var ANALYZE_TRACE_FILENAME = "analyze-trace.json";
|
|
14
|
+
|
|
15
|
+
// src/depth-limits.ts
|
|
16
|
+
import {
|
|
17
|
+
depthLimits,
|
|
18
|
+
event_checktypes__checkCrossProductUnion_DepthLimit,
|
|
19
|
+
event_checktypes__checkTypeRelatedTo_DepthLimit,
|
|
20
|
+
event_checktypes__getTypeAtFlowNode_DepthLimit,
|
|
21
|
+
event_checktypes__instantiateType_DepthLimit,
|
|
22
|
+
event_checktypes__recursiveTypeRelatedTo_DepthLimit,
|
|
23
|
+
event_checktypes__removeSubtypes_DepthLimit,
|
|
24
|
+
event_checktypes__traceUnionsOrIntersectionsTooLarge_DepthLimit,
|
|
25
|
+
event_checktypes__typeRelatedToDiscriminatedType_DepthLimit
|
|
26
|
+
} from "@typeslayer/validate";
|
|
27
|
+
var createDepthLimits = (traceFile) => {
|
|
28
|
+
const limitNamesSet = new Set(
|
|
29
|
+
depthLimits.map((d) => d.shape.name)
|
|
30
|
+
);
|
|
31
|
+
const limitEvents = traceFile.filter((event) => limitNamesSet.has(event.name));
|
|
32
|
+
return {
|
|
33
|
+
checkCrossProductUnion_DepthLimit: limitEvents.filter(
|
|
34
|
+
(event) => event_checktypes__checkCrossProductUnion_DepthLimit.safeParse(event).success
|
|
35
|
+
).sort((a, b) => a.args.size - b.args.size),
|
|
36
|
+
checkTypeRelatedTo_DepthLimit: limitEvents.filter(
|
|
37
|
+
(event) => event_checktypes__checkTypeRelatedTo_DepthLimit.safeParse(event).success
|
|
38
|
+
).sort((a, b) => a.args.depth - b.args.depth),
|
|
39
|
+
getTypeAtFlowNode_DepthLimit: limitEvents.filter(
|
|
40
|
+
(event) => event_checktypes__getTypeAtFlowNode_DepthLimit.safeParse(event).success
|
|
41
|
+
).sort((a, b) => a.args.flowId - b.args.flowId),
|
|
42
|
+
instantiateType_DepthLimit: limitEvents.filter(
|
|
43
|
+
(event) => event_checktypes__instantiateType_DepthLimit.safeParse(event).success
|
|
44
|
+
).sort((a, b) => a.args.instantiationDepth - b.args.instantiationDepth),
|
|
45
|
+
recursiveTypeRelatedTo_DepthLimit: limitEvents.filter(
|
|
46
|
+
(event) => event_checktypes__recursiveTypeRelatedTo_DepthLimit.safeParse(event).success
|
|
47
|
+
).sort((a, b) => a.args.depth - b.args.depth),
|
|
48
|
+
removeSubtypes_DepthLimit: limitEvents.filter(
|
|
49
|
+
(event) => event_checktypes__removeSubtypes_DepthLimit.safeParse(event).success
|
|
50
|
+
),
|
|
51
|
+
traceUnionsOrIntersectionsTooLarge_DepthLimit: limitEvents.filter(
|
|
52
|
+
(event) => event_checktypes__traceUnionsOrIntersectionsTooLarge_DepthLimit.safeParse(
|
|
53
|
+
event
|
|
54
|
+
).success
|
|
55
|
+
).sort(
|
|
56
|
+
(a, b) => a.args.sourceSize * a.args.targetSize - b.args.sourceSize * b.args.targetSize
|
|
57
|
+
),
|
|
58
|
+
typeRelatedToDiscriminatedType_DepthLimit: limitEvents.filter(
|
|
59
|
+
(event) => event_checktypes__typeRelatedToDiscriminatedType_DepthLimit.safeParse(
|
|
60
|
+
event
|
|
61
|
+
).success
|
|
62
|
+
).sort((a, b) => a.args.numCombinations - b.args.numCombinations)
|
|
63
|
+
};
|
|
64
|
+
};
|
|
65
|
+
|
|
66
|
+
// src/get-duplicate-node-modules.ts
|
|
67
|
+
import { existsSync } from "fs";
|
|
68
|
+
import { readFile } from "fs/promises";
|
|
69
|
+
import { join } from "path";
|
|
70
|
+
async function getPackageVersion(packagePath2) {
|
|
71
|
+
const packageJsonPath = join(packagePath2, "package.json");
|
|
72
|
+
console.log("packageJsonPath", packageJsonPath);
|
|
73
|
+
if (!existsSync(packageJsonPath)) {
|
|
74
|
+
console.warn(
|
|
75
|
+
`Package.json not found at ${packageJsonPath}. This may not be a node module.`
|
|
76
|
+
);
|
|
77
|
+
return "unknown";
|
|
78
|
+
}
|
|
79
|
+
const jsonString = await readFile(packageJsonPath, "utf-8");
|
|
80
|
+
const jsonObj = JSON.parse(jsonString);
|
|
81
|
+
return jsonObj.version;
|
|
82
|
+
}
|
|
83
|
+
var getDuplicateNodeModules = async (nodeModulePaths2) => {
|
|
84
|
+
const duplicates = [];
|
|
85
|
+
for (const [packageName2, packagePaths] of Object.entries(nodeModulePaths2)) {
|
|
86
|
+
if (packagePaths.length < 2) {
|
|
87
|
+
continue;
|
|
88
|
+
}
|
|
89
|
+
const instances = [];
|
|
90
|
+
for (const packagePath2 of packagePaths) {
|
|
91
|
+
instances.push({
|
|
92
|
+
path: packagePath2,
|
|
93
|
+
version: await getPackageVersion(packagePath2)
|
|
94
|
+
});
|
|
95
|
+
}
|
|
96
|
+
duplicates.push({
|
|
97
|
+
name: packageName2,
|
|
98
|
+
instances
|
|
99
|
+
});
|
|
100
|
+
}
|
|
101
|
+
return duplicates;
|
|
102
|
+
};
|
|
103
|
+
|
|
104
|
+
// src/get-hotspots.ts
|
|
105
|
+
import { normalize } from "path";
|
|
106
|
+
import {
|
|
107
|
+
createTypeRegistry
|
|
108
|
+
} from "@typeslayer/validate";
|
|
109
|
+
var getHotspots = async (hotPathsTree, typesFile, options) => getHotspotsWorker({
|
|
110
|
+
span: hotPathsTree,
|
|
111
|
+
currentFile: void 0,
|
|
112
|
+
typeRegistry: createTypeRegistry(typesFile),
|
|
113
|
+
options
|
|
114
|
+
});
|
|
115
|
+
async function getHotspotsWorker({
|
|
116
|
+
span,
|
|
117
|
+
currentFile,
|
|
118
|
+
typeRegistry,
|
|
119
|
+
options
|
|
120
|
+
}) {
|
|
121
|
+
if (span.event.cat === "check") {
|
|
122
|
+
currentFile = span.event.args.path;
|
|
123
|
+
}
|
|
124
|
+
const children = [];
|
|
125
|
+
if (span.children.length) {
|
|
126
|
+
const sortedChildren = span.children.sort(
|
|
127
|
+
(a, b) => b.duration - a.duration
|
|
128
|
+
);
|
|
129
|
+
for (const child of sortedChildren) {
|
|
130
|
+
children.push(
|
|
131
|
+
...await getHotspotsWorker({
|
|
132
|
+
span: child,
|
|
133
|
+
currentFile,
|
|
134
|
+
typeRegistry,
|
|
135
|
+
options
|
|
136
|
+
})
|
|
137
|
+
);
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
if (span.event.name !== "root") {
|
|
141
|
+
const hotFrame = await makeHotFrame({
|
|
142
|
+
span,
|
|
143
|
+
children,
|
|
144
|
+
typeRegistry
|
|
145
|
+
});
|
|
146
|
+
if (hotFrame) {
|
|
147
|
+
return [hotFrame];
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
return children;
|
|
151
|
+
}
|
|
152
|
+
var notFound = {
|
|
153
|
+
children: [],
|
|
154
|
+
resolvedType: {
|
|
155
|
+
id: -1,
|
|
156
|
+
display: "[Type Not Found]",
|
|
157
|
+
flags: []
|
|
158
|
+
}
|
|
159
|
+
};
|
|
160
|
+
function getHotType({
|
|
161
|
+
id,
|
|
162
|
+
typeRegistry
|
|
163
|
+
}) {
|
|
164
|
+
function worker(id2, ancestorIds) {
|
|
165
|
+
if (id2 === -1) {
|
|
166
|
+
return notFound;
|
|
167
|
+
}
|
|
168
|
+
const resolvedType2 = typeRegistry[id2];
|
|
169
|
+
if (!resolvedType2) {
|
|
170
|
+
throw new Error(`Type ${id2} not found`);
|
|
171
|
+
}
|
|
172
|
+
const children = [];
|
|
173
|
+
if (ancestorIds.indexOf(id2) < 0) {
|
|
174
|
+
ancestorIds.push(id2);
|
|
175
|
+
const properties = Object.keys(resolvedType2);
|
|
176
|
+
for (const property of properties) {
|
|
177
|
+
switch (property) {
|
|
178
|
+
case "aliasTypeArguments":
|
|
179
|
+
case "intersectionTypes":
|
|
180
|
+
case "typeArguments":
|
|
181
|
+
case "unionTypes": {
|
|
182
|
+
const typeIds = resolvedType2[property];
|
|
183
|
+
if (!Array.isArray(typeIds)) {
|
|
184
|
+
throw new Error(`Expected array for ${property}`);
|
|
185
|
+
}
|
|
186
|
+
for (const typeId of typeIds) {
|
|
187
|
+
const child = worker(typeId, ancestorIds);
|
|
188
|
+
if (child) {
|
|
189
|
+
children.push(child);
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
continue;
|
|
193
|
+
}
|
|
194
|
+
case "aliasType":
|
|
195
|
+
case "conditionalCheckType":
|
|
196
|
+
case "conditionalExtendsType":
|
|
197
|
+
case "conditionalFalseType":
|
|
198
|
+
case "conditionalTrueType":
|
|
199
|
+
case "constraintType":
|
|
200
|
+
case "evolvingArrayElementType":
|
|
201
|
+
case "evolvingArrayFinalType":
|
|
202
|
+
case "indexedAccessIndexType":
|
|
203
|
+
case "indexedAccessObjectType":
|
|
204
|
+
case "instantiatedType":
|
|
205
|
+
case "keyofType":
|
|
206
|
+
case "reverseMappedConstraintType":
|
|
207
|
+
case "reverseMappedMappedType":
|
|
208
|
+
case "reverseMappedSourceType":
|
|
209
|
+
case "substitutionBaseType": {
|
|
210
|
+
const typeId = resolvedType2[property];
|
|
211
|
+
const child = worker(typeId, ancestorIds);
|
|
212
|
+
if (child) {
|
|
213
|
+
children.push(child);
|
|
214
|
+
}
|
|
215
|
+
break;
|
|
216
|
+
}
|
|
217
|
+
case "destructuringPattern":
|
|
218
|
+
case "display":
|
|
219
|
+
case "firstDeclaration":
|
|
220
|
+
case "flags":
|
|
221
|
+
case "id":
|
|
222
|
+
case "intrinsicName":
|
|
223
|
+
case "isTuple":
|
|
224
|
+
case "recursionId":
|
|
225
|
+
case "referenceLocation":
|
|
226
|
+
case "symbolName":
|
|
227
|
+
break;
|
|
228
|
+
default:
|
|
229
|
+
property;
|
|
230
|
+
throw new Error(`Unexpected property ${property}`);
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
ancestorIds.pop();
|
|
234
|
+
}
|
|
235
|
+
return {
|
|
236
|
+
resolvedType: resolvedType2,
|
|
237
|
+
children
|
|
238
|
+
};
|
|
239
|
+
}
|
|
240
|
+
return worker(id, []);
|
|
241
|
+
}
|
|
242
|
+
async function makeHotFrame({
|
|
243
|
+
span,
|
|
244
|
+
children,
|
|
245
|
+
typeRegistry
|
|
246
|
+
}) {
|
|
247
|
+
const { event, duration } = span;
|
|
248
|
+
const timeMs = Math.round(duration / 1e3);
|
|
249
|
+
switch (event.name) {
|
|
250
|
+
// case "findSourceFile":
|
|
251
|
+
// TODO (https://github.com/microsoft/typescript-analyze-trace/issues/2)
|
|
252
|
+
case "checkSourceFile": {
|
|
253
|
+
const filePath = event.args.path;
|
|
254
|
+
const normalizedPath = normalize(filePath);
|
|
255
|
+
return {
|
|
256
|
+
description: `Check file ${normalizedPath}`,
|
|
257
|
+
timeMs,
|
|
258
|
+
path: normalizedPath,
|
|
259
|
+
children
|
|
260
|
+
};
|
|
261
|
+
}
|
|
262
|
+
case "structuredTypeRelatedTo":
|
|
263
|
+
return {
|
|
264
|
+
description: `Compare types ${event.args.sourceId} and ${event.args.targetId}`,
|
|
265
|
+
timeMs,
|
|
266
|
+
children,
|
|
267
|
+
types: [
|
|
268
|
+
getHotType({
|
|
269
|
+
id: event.args.sourceId,
|
|
270
|
+
typeRegistry
|
|
271
|
+
}),
|
|
272
|
+
getHotType({
|
|
273
|
+
id: event.args.targetId,
|
|
274
|
+
typeRegistry
|
|
275
|
+
})
|
|
276
|
+
]
|
|
277
|
+
};
|
|
278
|
+
case "getVariancesWorker":
|
|
279
|
+
return {
|
|
280
|
+
description: `Determine variance of type ${event.args.id}`,
|
|
281
|
+
timeMs,
|
|
282
|
+
children,
|
|
283
|
+
types: [getHotType({ id: event.args.id, typeRegistry })]
|
|
284
|
+
};
|
|
285
|
+
case "checkExpression":
|
|
286
|
+
case "checkVariableDeclaration": {
|
|
287
|
+
const filePath = event.args.path;
|
|
288
|
+
const path = filePath ? { path: normalize(filePath) } : {};
|
|
289
|
+
const frame = {
|
|
290
|
+
description: event.name,
|
|
291
|
+
timeMs,
|
|
292
|
+
...path,
|
|
293
|
+
children: []
|
|
294
|
+
};
|
|
295
|
+
return frame;
|
|
296
|
+
}
|
|
297
|
+
default:
|
|
298
|
+
return void 0;
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
// src/node-module-paths.ts
|
|
303
|
+
import {
|
|
304
|
+
packageNameRegex
|
|
305
|
+
} from "@typeslayer/validate";
|
|
306
|
+
function getNodeModulePaths(traceJson) {
|
|
307
|
+
const nodeModulePaths2 = {};
|
|
308
|
+
traceJson.forEach((event) => {
|
|
309
|
+
if (event.name !== "findSourceFile") {
|
|
310
|
+
return;
|
|
311
|
+
}
|
|
312
|
+
const path = event.args.fileName;
|
|
313
|
+
if (path) {
|
|
314
|
+
while (true) {
|
|
315
|
+
const match = packageNameRegex.exec(path);
|
|
316
|
+
if (!match) {
|
|
317
|
+
break;
|
|
318
|
+
}
|
|
319
|
+
const packageName2 = match[1];
|
|
320
|
+
const packagePath2 = match.input.substring(
|
|
321
|
+
0,
|
|
322
|
+
match.index + match[0].length
|
|
323
|
+
);
|
|
324
|
+
if (packageName2 in nodeModulePaths2) {
|
|
325
|
+
const paths = nodeModulePaths2[packageName2];
|
|
326
|
+
if (paths && paths.indexOf(packagePath2) < 0) {
|
|
327
|
+
paths.push(packagePath2);
|
|
328
|
+
}
|
|
329
|
+
} else {
|
|
330
|
+
nodeModulePaths2[packageName2] = [packagePath2];
|
|
331
|
+
}
|
|
332
|
+
}
|
|
333
|
+
}
|
|
334
|
+
});
|
|
335
|
+
return nodeModulePaths2;
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
// src/spans.ts
|
|
339
|
+
import {
|
|
340
|
+
eventPhase
|
|
341
|
+
} from "@typeslayer/validate";
|
|
342
|
+
function createSpans(traceFile) {
|
|
343
|
+
const unclosedStack = [];
|
|
344
|
+
const spans = [];
|
|
345
|
+
traceFile.forEach((event) => {
|
|
346
|
+
switch (event.ph) {
|
|
347
|
+
case eventPhase.begin:
|
|
348
|
+
unclosedStack.push(event);
|
|
349
|
+
return;
|
|
350
|
+
case eventPhase.end: {
|
|
351
|
+
const beginEvent = unclosedStack.pop();
|
|
352
|
+
if (!beginEvent) {
|
|
353
|
+
throw new Error("Unmatched end event");
|
|
354
|
+
}
|
|
355
|
+
spans.push({
|
|
356
|
+
event: beginEvent,
|
|
357
|
+
start: beginEvent.ts,
|
|
358
|
+
end: event.ts,
|
|
359
|
+
duration: event.ts - beginEvent.ts,
|
|
360
|
+
children: []
|
|
361
|
+
});
|
|
362
|
+
break;
|
|
363
|
+
}
|
|
364
|
+
case eventPhase.complete: {
|
|
365
|
+
const start = event.ts;
|
|
366
|
+
const duration = event.dur ?? 0;
|
|
367
|
+
spans.push({
|
|
368
|
+
event,
|
|
369
|
+
start,
|
|
370
|
+
end: start + duration,
|
|
371
|
+
duration,
|
|
372
|
+
children: []
|
|
373
|
+
});
|
|
374
|
+
break;
|
|
375
|
+
}
|
|
376
|
+
case eventPhase.instantGlobal:
|
|
377
|
+
case eventPhase.metadata:
|
|
378
|
+
return;
|
|
379
|
+
default:
|
|
380
|
+
event;
|
|
381
|
+
}
|
|
382
|
+
});
|
|
383
|
+
const parseResult2 = {
|
|
384
|
+
firstSpanStart: Math.min(...spans.map((span) => span.start)),
|
|
385
|
+
lastSpanEnd: Math.max(...spans.map((span) => span.end)),
|
|
386
|
+
spans,
|
|
387
|
+
unclosedStack
|
|
388
|
+
};
|
|
389
|
+
return parseResult2;
|
|
390
|
+
}
|
|
391
|
+
function createSpanTree(parseResult2, options) {
|
|
392
|
+
const { firstSpanStart, lastSpanEnd, spans, unclosedStack } = parseResult2;
|
|
393
|
+
for (let i = unclosedStack.length - 1; i >= 0; i--) {
|
|
394
|
+
const event = unclosedStack[i];
|
|
395
|
+
const start = event.ts;
|
|
396
|
+
const end = lastSpanEnd;
|
|
397
|
+
spans.push({
|
|
398
|
+
event,
|
|
399
|
+
start,
|
|
400
|
+
end,
|
|
401
|
+
duration: end - start,
|
|
402
|
+
children: []
|
|
403
|
+
});
|
|
404
|
+
}
|
|
405
|
+
spans.sort((a, b) => a.start - b.start);
|
|
406
|
+
const root = {
|
|
407
|
+
event: {
|
|
408
|
+
name: "root",
|
|
409
|
+
cat: "program"
|
|
410
|
+
},
|
|
411
|
+
start: firstSpanStart,
|
|
412
|
+
end: lastSpanEnd,
|
|
413
|
+
duration: lastSpanEnd - firstSpanStart,
|
|
414
|
+
children: []
|
|
415
|
+
};
|
|
416
|
+
const stack = [root];
|
|
417
|
+
for (const span of spans) {
|
|
418
|
+
let i = stack.length - 1;
|
|
419
|
+
for (; i > 0; i--) {
|
|
420
|
+
const curr = stack[i];
|
|
421
|
+
if (curr.end > span.start) {
|
|
422
|
+
stack.length = i + 1;
|
|
423
|
+
break;
|
|
424
|
+
}
|
|
425
|
+
}
|
|
426
|
+
const thresholdDuration = options.forceMillis * 1e3;
|
|
427
|
+
const isAboveThresholdDuration = span.duration >= thresholdDuration;
|
|
428
|
+
const parent = stack[i];
|
|
429
|
+
const parentDuration = parent.end - parent.start;
|
|
430
|
+
const isSignificantPortionOfParent = span.duration >= parentDuration * options.minSpanParentPercentage;
|
|
431
|
+
if (isAboveThresholdDuration || isSignificantPortionOfParent) {
|
|
432
|
+
parent.children.push(span);
|
|
433
|
+
stack.push(span);
|
|
434
|
+
}
|
|
435
|
+
}
|
|
436
|
+
return root;
|
|
437
|
+
}
|
|
438
|
+
|
|
439
|
+
// src/utils.ts
|
|
440
|
+
import { existsSync as existsSync2 } from "fs";
|
|
441
|
+
import { stat } from "fs/promises";
|
|
442
|
+
import {
|
|
443
|
+
event_checktypes__checkCrossProductUnion_DepthLimit as event_checktypes__checkCrossProductUnion_DepthLimit2,
|
|
444
|
+
event_checktypes__checkTypeRelatedTo_DepthLimit as event_checktypes__checkTypeRelatedTo_DepthLimit2,
|
|
445
|
+
event_checktypes__getTypeAtFlowNode_DepthLimit as event_checktypes__getTypeAtFlowNode_DepthLimit2,
|
|
446
|
+
event_checktypes__instantiateType_DepthLimit as event_checktypes__instantiateType_DepthLimit2,
|
|
447
|
+
event_checktypes__recursiveTypeRelatedTo_DepthLimit as event_checktypes__recursiveTypeRelatedTo_DepthLimit2,
|
|
448
|
+
event_checktypes__removeSubtypes_DepthLimit as event_checktypes__removeSubtypes_DepthLimit2,
|
|
449
|
+
event_checktypes__traceUnionsOrIntersectionsTooLarge_DepthLimit as event_checktypes__traceUnionsOrIntersectionsTooLarge_DepthLimit2,
|
|
450
|
+
event_checktypes__typeRelatedToDiscriminatedType_DepthLimit as event_checktypes__typeRelatedToDiscriminatedType_DepthLimit2,
|
|
451
|
+
resolvedType,
|
|
452
|
+
traceEvent
|
|
453
|
+
} from "@typeslayer/validate";
|
|
454
|
+
import { z } from "zod/v4";
|
|
455
|
+
var absolutePath = z.string().refine(
|
|
456
|
+
(path) => {
|
|
457
|
+
return path.startsWith("/") || path.startsWith("C:\\") || path.startsWith("D:\\");
|
|
458
|
+
},
|
|
459
|
+
{
|
|
460
|
+
message: "Path must be absolute"
|
|
461
|
+
}
|
|
462
|
+
);
|
|
463
|
+
var project = z.object({
|
|
464
|
+
configFilePath: absolutePath.optional(),
|
|
465
|
+
tracePath: absolutePath,
|
|
466
|
+
typesPath: absolutePath
|
|
467
|
+
});
|
|
468
|
+
var projectResult = z.object({
|
|
469
|
+
project,
|
|
470
|
+
stdout: z.string(),
|
|
471
|
+
stderr: z.string(),
|
|
472
|
+
exitCode: z.number().optional(),
|
|
473
|
+
signal: z.enum(["SIGINT", "SIGTERM"]).optional()
|
|
474
|
+
});
|
|
475
|
+
var hotType = z.object({
|
|
476
|
+
resolvedType,
|
|
477
|
+
get children() {
|
|
478
|
+
return z.array(hotType);
|
|
479
|
+
}
|
|
480
|
+
});
|
|
481
|
+
var hotSpot = z.object({
|
|
482
|
+
description: z.string(),
|
|
483
|
+
timeMs: z.number(),
|
|
484
|
+
get children() {
|
|
485
|
+
return z.array(hotSpot);
|
|
486
|
+
},
|
|
487
|
+
path: absolutePath.optional(),
|
|
488
|
+
types: z.array(hotType).optional(),
|
|
489
|
+
startLine: z.number().optional(),
|
|
490
|
+
startChar: z.number().optional(),
|
|
491
|
+
startOffset: z.number().optional(),
|
|
492
|
+
endLine: z.number().optional(),
|
|
493
|
+
endChar: z.number().optional(),
|
|
494
|
+
endOffset: z.number().optional()
|
|
495
|
+
});
|
|
496
|
+
var duplicatedPackageInstance = z.object({
|
|
497
|
+
path: absolutePath,
|
|
498
|
+
version: z.string()
|
|
499
|
+
});
|
|
500
|
+
var duplicatedPackage = z.object({
|
|
501
|
+
name: z.string(),
|
|
502
|
+
instances: z.array(duplicatedPackageInstance)
|
|
503
|
+
});
|
|
504
|
+
var rootSpan = z.object({
|
|
505
|
+
name: z.literal("root"),
|
|
506
|
+
cat: z.literal("program")
|
|
507
|
+
});
|
|
508
|
+
var eventSpan = z.object({
|
|
509
|
+
event: z.union([traceEvent, rootSpan]),
|
|
510
|
+
start: z.number(),
|
|
511
|
+
end: z.number(),
|
|
512
|
+
duration: z.number(),
|
|
513
|
+
get children() {
|
|
514
|
+
return z.array(eventSpan);
|
|
515
|
+
}
|
|
516
|
+
});
|
|
517
|
+
var microseconds = z.number();
|
|
518
|
+
var packageName = z.string();
|
|
519
|
+
var packagePath = z.string();
|
|
520
|
+
var nodeModulePaths = z.record(packageName, z.array(packagePath));
|
|
521
|
+
var parseResult = z.object({
|
|
522
|
+
firstSpanStart: z.number(),
|
|
523
|
+
lastSpanEnd: z.number(),
|
|
524
|
+
spans: z.array(eventSpan),
|
|
525
|
+
unclosedStack: z.array(traceEvent)
|
|
526
|
+
});
|
|
527
|
+
var analyzeTraceOptions = z.object({
|
|
528
|
+
/** Events of at least this duration (in milliseconds) will reported unconditionally */
|
|
529
|
+
forceMillis: z.number(),
|
|
530
|
+
/** Events of less than this duration (in milliseconds) will suppressed unconditionally */
|
|
531
|
+
skipMillis: z.number(),
|
|
532
|
+
/** Expand types when printing */
|
|
533
|
+
expandTypes: z.boolean(),
|
|
534
|
+
/** force showing spans that are some percentage of their parent, independent of parent time */
|
|
535
|
+
minSpanParentPercentage: z.number(),
|
|
536
|
+
/** the minimum number of emitted imports from a declaration file or bundle */
|
|
537
|
+
importExpressionThreshold: z.number()
|
|
538
|
+
});
|
|
539
|
+
var isFile = async (path) => {
|
|
540
|
+
return stat(path).then((stats) => stats.isFile()).catch((_) => false);
|
|
541
|
+
};
|
|
542
|
+
var throwIfNotDirectory = async (path) => {
|
|
543
|
+
if (!existsSync2(path) || !(await stat(path))?.isDirectory()) {
|
|
544
|
+
throw new Error(`${path} is not a directory`);
|
|
545
|
+
}
|
|
546
|
+
return path;
|
|
547
|
+
};
|
|
548
|
+
var analyzeTraceResult = z.object({
|
|
549
|
+
/** Events that were not closed */
|
|
550
|
+
unterminatedEvents: z.array(traceEvent),
|
|
551
|
+
/** Hot spots in the trace */
|
|
552
|
+
hotSpots: z.array(hotSpot),
|
|
553
|
+
/** Packages that are duplicated in the trace */
|
|
554
|
+
duplicatePackages: z.array(duplicatedPackage),
|
|
555
|
+
/** Paths to all node modules used in the trace */
|
|
556
|
+
nodeModulePaths,
|
|
557
|
+
/** Depth limit events grouped by their event name */
|
|
558
|
+
depthLimits: z.object({
|
|
559
|
+
checkCrossProductUnion_DepthLimit: z.array(
|
|
560
|
+
event_checktypes__checkCrossProductUnion_DepthLimit2
|
|
561
|
+
),
|
|
562
|
+
checkTypeRelatedTo_DepthLimit: z.array(
|
|
563
|
+
event_checktypes__checkTypeRelatedTo_DepthLimit2
|
|
564
|
+
),
|
|
565
|
+
getTypeAtFlowNode_DepthLimit: z.array(
|
|
566
|
+
event_checktypes__getTypeAtFlowNode_DepthLimit2
|
|
567
|
+
),
|
|
568
|
+
instantiateType_DepthLimit: z.array(
|
|
569
|
+
event_checktypes__instantiateType_DepthLimit2
|
|
570
|
+
),
|
|
571
|
+
recursiveTypeRelatedTo_DepthLimit: z.array(
|
|
572
|
+
event_checktypes__recursiveTypeRelatedTo_DepthLimit2
|
|
573
|
+
),
|
|
574
|
+
removeSubtypes_DepthLimit: z.array(
|
|
575
|
+
event_checktypes__removeSubtypes_DepthLimit2
|
|
576
|
+
),
|
|
577
|
+
traceUnionsOrIntersectionsTooLarge_DepthLimit: z.array(
|
|
578
|
+
event_checktypes__traceUnionsOrIntersectionsTooLarge_DepthLimit2
|
|
579
|
+
),
|
|
580
|
+
typeRelatedToDiscriminatedType_DepthLimit: z.array(
|
|
581
|
+
event_checktypes__typeRelatedToDiscriminatedType_DepthLimit2
|
|
582
|
+
)
|
|
583
|
+
})
|
|
584
|
+
});
|
|
585
|
+
|
|
586
|
+
// src/analyze-trace.ts
|
|
587
|
+
function validateOptions(options) {
|
|
588
|
+
if (options.forceMillis < options.skipMillis) {
|
|
589
|
+
throw new Error("forceMillis cannot be less than skipMillis");
|
|
590
|
+
}
|
|
591
|
+
}
|
|
592
|
+
var validateTraceDir = async (traceDir) => {
|
|
593
|
+
await throwIfNotDirectory(traceDir);
|
|
594
|
+
const typesFilePath = join2(traceDir, TYPES_JSON_FILENAME);
|
|
595
|
+
if (!existsSync3(typesFilePath)) {
|
|
596
|
+
throw new Error(
|
|
597
|
+
`types.json must exist in ${traceDir}. first run --generateTrace`
|
|
598
|
+
);
|
|
599
|
+
}
|
|
600
|
+
const typesFileJson = JSON.parse(await readFile2(typesFilePath, "utf8"));
|
|
601
|
+
const typesFile = typesJsonSchema.parse(typesFileJson);
|
|
602
|
+
const traceFilePath = join2(traceDir, TRACE_JSON_FILENAME);
|
|
603
|
+
if (!existsSync3(traceFilePath)) {
|
|
604
|
+
throw new Error(
|
|
605
|
+
`trace.json must exist in ${traceDir}. first run --generateTrace`
|
|
606
|
+
);
|
|
607
|
+
}
|
|
608
|
+
const traceFileJson = JSON.parse(await readFile2(traceFilePath, "utf8"));
|
|
609
|
+
const traceFile = traceJsonSchema.parse(traceFileJson);
|
|
610
|
+
return {
|
|
611
|
+
traceFile,
|
|
612
|
+
typesFile
|
|
613
|
+
};
|
|
614
|
+
};
|
|
615
|
+
var defaultOptions = {
|
|
616
|
+
forceMillis: 500,
|
|
617
|
+
skipMillis: 100,
|
|
618
|
+
expandTypes: true,
|
|
619
|
+
minSpanParentPercentage: 0.6,
|
|
620
|
+
importExpressionThreshold: 10
|
|
621
|
+
};
|
|
622
|
+
var analyzeTrace = async ({
|
|
623
|
+
traceDir,
|
|
624
|
+
options = defaultOptions
|
|
625
|
+
}) => {
|
|
626
|
+
validateOptions(options);
|
|
627
|
+
const { traceFile, typesFile } = await validateTraceDir(traceDir);
|
|
628
|
+
const nodeModulePaths2 = getNodeModulePaths(traceFile);
|
|
629
|
+
const spans = createSpans(traceFile);
|
|
630
|
+
const hotPathsTree = createSpanTree(spans, options);
|
|
631
|
+
const result = {
|
|
632
|
+
nodeModulePaths: nodeModulePaths2,
|
|
633
|
+
unterminatedEvents: spans.unclosedStack.reverse(),
|
|
634
|
+
hotSpots: await getHotspots(hotPathsTree, typesFile, options),
|
|
635
|
+
duplicatePackages: await getDuplicateNodeModules(nodeModulePaths2),
|
|
636
|
+
depthLimits: createDepthLimits(traceFile)
|
|
637
|
+
};
|
|
638
|
+
await writeFile(
|
|
639
|
+
join2(traceDir, ANALYZE_TRACE_FILENAME),
|
|
640
|
+
JSON.stringify(result, null, 2)
|
|
641
|
+
);
|
|
642
|
+
return result;
|
|
643
|
+
};
|
|
644
|
+
export {
|
|
645
|
+
ANALYZE_TRACE_FILENAME,
|
|
646
|
+
absolutePath,
|
|
647
|
+
analyzeTrace,
|
|
648
|
+
analyzeTraceOptions,
|
|
649
|
+
analyzeTraceResult,
|
|
650
|
+
createDepthLimits,
|
|
651
|
+
defaultOptions,
|
|
652
|
+
duplicatedPackage,
|
|
653
|
+
duplicatedPackageInstance,
|
|
654
|
+
eventSpan,
|
|
655
|
+
hotSpot,
|
|
656
|
+
hotType,
|
|
657
|
+
isFile,
|
|
658
|
+
microseconds,
|
|
659
|
+
nodeModulePaths,
|
|
660
|
+
packageName,
|
|
661
|
+
packagePath,
|
|
662
|
+
parseResult,
|
|
663
|
+
project,
|
|
664
|
+
projectResult,
|
|
665
|
+
rootSpan,
|
|
666
|
+
throwIfNotDirectory,
|
|
667
|
+
validateOptions
|
|
668
|
+
};
|
|
669
|
+
//# sourceMappingURL=index.js.map
|