@reicek/neataptic-ts 0.1.24 → 0.1.26

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 (215) hide show
  1. package/.github/copilot-instructions.md +11 -0
  2. package/.github/skills/trace-analyzer-extension/SKILL.md +3 -3
  3. package/.github/skills/trace-analyzer-extension/assets/extension-checklist.md +1 -1
  4. package/.github/skills/trace-analyzer-extension/references/analyzer-extension-workflow.md +1 -1
  5. package/.github/skills/trace-audit-reporting/SKILL.md +3 -3
  6. package/.github/skills/trace-audit-reporting/references/trace-analysis-workflow.md +1 -1
  7. package/.github/workflows/ci.yml +3 -3
  8. package/.github/workflows/deploy-pages.yml +6 -6
  9. package/.github/workflows/manual_release_pipeline.yml +3 -3
  10. package/.github/workflows/publish.yml +18 -19
  11. package/.github/workflows/release_dispatch.yml +3 -3
  12. package/package.json +26 -20
  13. package/plans/Flappy_Bird_Folder_Documentation_Pass.md +4 -4
  14. package/plans/README.md +24 -0
  15. package/plans/Roadmap.md +62 -40
  16. package/plans/analyze-trace-solid-split.plans.md +66 -0
  17. package/plans/architecture-solid-split.plans.md +9 -15
  18. package/plans/asciiMaze-typescript-repair.plans.md +1 -1
  19. package/plans/generate-docs-solid-split.plans.md +87 -0
  20. package/plans/methods-docs.plans.md +25 -1
  21. package/plans/methods-solid-split.plans.md +14 -14
  22. package/plans/neat-docs.plans.md +9 -1
  23. package/plans/neat-test-surface-repair.plans.md +1 -1
  24. package/plans/render-docs-html-solid-split.plans.md +68 -0
  25. package/plans/src-no-explicit-any-cleanup.plans.md +1 -1
  26. package/plans/utils-docs.plans.md +6 -1
  27. package/scripts/analyze-trace/analyze-trace.analysis.ts +479 -0
  28. package/scripts/analyze-trace/analyze-trace.constants.ts +35 -0
  29. package/scripts/analyze-trace/analyze-trace.io.ts +69 -0
  30. package/scripts/analyze-trace/analyze-trace.report.ts +100 -0
  31. package/scripts/analyze-trace/analyze-trace.shared.ts +116 -0
  32. package/scripts/analyze-trace/analyze-trace.ts +45 -0
  33. package/scripts/analyze-trace/analyze-trace.types.ts +72 -0
  34. package/scripts/assets/theme.css +80 -23
  35. package/scripts/copy-examples.ts +239 -0
  36. package/scripts/export-onnx.ts +223 -0
  37. package/scripts/generate-bench-tables.ts +378 -37
  38. package/scripts/generate-docs/generate-docs.constants.ts +107 -0
  39. package/scripts/generate-docs/generate-docs.order.ts +355 -0
  40. package/scripts/generate-docs/generate-docs.state.ts +31 -0
  41. package/scripts/generate-docs/generate-docs.targets.ts +165 -0
  42. package/scripts/generate-docs/generate-docs.ts +63 -0
  43. package/scripts/generate-docs/generate-docs.types.ts +112 -0
  44. package/scripts/generate-docs/output/generate-docs.output.folder-index.utils.ts +167 -0
  45. package/scripts/generate-docs/output/generate-docs.output.ordering.utils.ts +353 -0
  46. package/scripts/generate-docs/output/generate-docs.output.readme.utils.ts +420 -0
  47. package/scripts/generate-docs/output/generate-docs.output.ts +123 -0
  48. package/scripts/generate-docs/output/generate-docs.output.warnings.utils.ts +219 -0
  49. package/scripts/generate-docs/symbols/generate-docs.symbols.collection.utils.ts +365 -0
  50. package/scripts/generate-docs/symbols/generate-docs.symbols.jsdoc.utils.ts +373 -0
  51. package/scripts/generate-docs/symbols/generate-docs.symbols.normalize.utils.ts +155 -0
  52. package/scripts/generate-docs/symbols/generate-docs.symbols.render.utils.ts +149 -0
  53. package/scripts/generate-docs/symbols/generate-docs.symbols.signature.utils.ts +289 -0
  54. package/scripts/generate-docs/symbols/generate-docs.symbols.ts +11 -0
  55. package/scripts/mermaid-cli.mjs +102 -22
  56. package/scripts/mermaid-cli.ts +736 -0
  57. package/scripts/render-docs-html/render-docs-html.assets.ts +54 -0
  58. package/scripts/render-docs-html/render-docs-html.mermaid.ts +245 -0
  59. package/scripts/{render-docs-html.sidebar.ts → render-docs-html/render-docs-html.navigation.ts} +141 -144
  60. package/scripts/render-docs-html/render-docs-html.pages.ts +333 -0
  61. package/scripts/render-docs-html/render-docs-html.shared.ts +333 -0
  62. package/scripts/render-docs-html/render-docs-html.types.ts +42 -0
  63. package/scripts/render-docs-html.ts +23 -587
  64. package/scripts/run-docs.ts +238 -0
  65. package/scripts/write-dist-docs-pkg.ts +40 -0
  66. package/src/README.md +75 -75
  67. package/src/architecture/connection/README.md +5 -5
  68. package/src/architecture/layer/README.md +508 -508
  69. package/src/architecture/network/README.md +1458 -1458
  70. package/src/architecture/network/activate/README.md +694 -694
  71. package/src/architecture/network/bootstrap/README.md +77 -77
  72. package/src/architecture/network/connect/README.md +74 -74
  73. package/src/architecture/network/deterministic/README.md +135 -135
  74. package/src/architecture/network/evolve/README.md +364 -364
  75. package/src/architecture/network/gating/README.md +130 -130
  76. package/src/architecture/network/genetic/README.md +399 -399
  77. package/src/architecture/network/mutate/README.md +897 -897
  78. package/src/architecture/network/onnx/README.md +720 -720
  79. package/src/architecture/network/onnx/export/README.md +728 -728
  80. package/src/architecture/network/onnx/export/layers/README.md +450 -450
  81. package/src/architecture/network/onnx/import/README.md +618 -618
  82. package/src/architecture/network/onnx/schema/README.md +32 -32
  83. package/src/architecture/network/prune/README.md +245 -245
  84. package/src/architecture/network/remove/README.md +135 -135
  85. package/src/architecture/network/runtime/README.md +106 -106
  86. package/src/architecture/network/serialize/README.md +542 -542
  87. package/src/architecture/network/slab/README.md +608 -608
  88. package/src/architecture/network/standalone/README.md +212 -212
  89. package/src/architecture/network/stats/README.md +84 -84
  90. package/src/architecture/network/topology/README.md +465 -465
  91. package/src/architecture/network/training/README.md +200 -200
  92. package/src/architecture/node/README.md +5 -5
  93. package/src/architecture/nodePool/README.md +14 -14
  94. package/src/methods/README.md +99 -99
  95. package/src/methods/activation/README.md +189 -189
  96. package/src/methods/cost/README.md +131 -131
  97. package/src/methods/rate/README.md +86 -86
  98. package/src/multithreading/README.md +77 -77
  99. package/src/multithreading/workers/browser/README.md +8 -8
  100. package/src/multithreading/workers/node/README.md +8 -8
  101. package/src/neat/README.md +148 -148
  102. package/src/neat/adaptive/README.md +120 -120
  103. package/src/neat/adaptive/acceptance/README.md +40 -40
  104. package/src/neat/adaptive/complexity/README.md +137 -137
  105. package/src/neat/adaptive/core/README.md +197 -197
  106. package/src/neat/adaptive/lineage/README.md +90 -90
  107. package/src/neat/adaptive/mutation/README.md +284 -284
  108. package/src/neat/compat/README.md +43 -43
  109. package/src/neat/compat/core/README.md +90 -90
  110. package/src/neat/diversity/README.md +35 -35
  111. package/src/neat/diversity/core/README.md +88 -88
  112. package/src/neat/evaluate/README.md +85 -85
  113. package/src/neat/evaluate/auto-distance/README.md +75 -75
  114. package/src/neat/evaluate/entropy-compat/README.md +37 -37
  115. package/src/neat/evaluate/entropy-sharing/README.md +43 -43
  116. package/src/neat/evaluate/fitness/README.md +23 -23
  117. package/src/neat/evaluate/novelty/README.md +120 -120
  118. package/src/neat/evaluate/objectives/README.md +17 -17
  119. package/src/neat/evaluate/shared/README.md +94 -94
  120. package/src/neat/evolve/README.md +96 -96
  121. package/src/neat/evolve/adaptive/README.md +60 -60
  122. package/src/neat/evolve/objectives/README.md +63 -63
  123. package/src/neat/evolve/offspring/README.md +56 -56
  124. package/src/neat/evolve/population/README.md +171 -171
  125. package/src/neat/evolve/runtime/README.md +79 -79
  126. package/src/neat/evolve/speciation/README.md +74 -74
  127. package/src/neat/evolve/warnings/README.md +10 -10
  128. package/src/neat/export/README.md +114 -114
  129. package/src/neat/helpers/README.md +50 -50
  130. package/src/neat/init/README.md +9 -9
  131. package/src/neat/lineage/core/README.md +101 -101
  132. package/src/neat/multiobjective/category/README.md +74 -74
  133. package/src/neat/multiobjective/crowding/README.md +272 -272
  134. package/src/neat/multiobjective/dominance/README.md +171 -171
  135. package/src/neat/multiobjective/fronts/README.md +68 -68
  136. package/src/neat/multiobjective/metrics/README.md +43 -43
  137. package/src/neat/multiobjective/objectives/README.md +31 -31
  138. package/src/neat/multiobjective/shared/README.md +27 -27
  139. package/src/neat/mutation/README.md +97 -97
  140. package/src/neat/mutation/add-conn/README.md +115 -115
  141. package/src/neat/mutation/add-node/README.md +126 -126
  142. package/src/neat/mutation/flow/README.md +149 -149
  143. package/src/neat/mutation/repair/README.md +185 -185
  144. package/src/neat/mutation/select/README.md +117 -117
  145. package/src/neat/mutation/shared/README.md +32 -32
  146. package/src/neat/objectives/README.md +25 -25
  147. package/src/neat/objectives/core/README.md +67 -67
  148. package/src/neat/pruning/README.md +40 -40
  149. package/src/neat/pruning/core/README.md +171 -171
  150. package/src/neat/pruning/facade/README.md +32 -32
  151. package/src/neat/rng/README.md +104 -104
  152. package/src/neat/rng/core/README.md +137 -137
  153. package/src/neat/rng/facade/README.md +50 -50
  154. package/src/neat/selection/README.md +111 -111
  155. package/src/neat/selection/core/README.md +227 -227
  156. package/src/neat/selection/facade/README.md +61 -61
  157. package/src/neat/shared/README.md +163 -163
  158. package/src/neat/speciation/README.md +31 -31
  159. package/src/neat/speciation/threshold/README.md +35 -35
  160. package/src/neat/species/README.md +25 -25
  161. package/src/neat/species/core/README.md +20 -20
  162. package/src/neat/species/core/shared/README.md +18 -18
  163. package/src/neat/species/history/context/README.md +22 -22
  164. package/src/neat/telemetry/accessors/README.md +58 -58
  165. package/src/neat/telemetry/exports/README.md +233 -233
  166. package/src/neat/telemetry/facade/README.md +252 -252
  167. package/src/neat/telemetry/facade/archive/README.md +57 -57
  168. package/src/neat/telemetry/facade/buffer/README.md +43 -43
  169. package/src/neat/telemetry/facade/lineage/README.md +12 -12
  170. package/src/neat/telemetry/facade/objectives/README.md +44 -44
  171. package/src/neat/telemetry/facade/runtime/README.md +26 -26
  172. package/src/neat/telemetry/facade/species/README.md +27 -27
  173. package/src/neat/telemetry/metrics/README.md +696 -696
  174. package/src/neat/telemetry/recorder/README.md +57 -57
  175. package/src/neat/telemetry/types/README.md +32 -32
  176. package/src/neat/topology-intent/README.md +75 -75
  177. package/src/utils/README.md +193 -193
  178. package/test/examples/asciiMaze/browser-entry/README.md +92 -92
  179. package/test/examples/asciiMaze/dashboardManager/README.md +109 -109
  180. package/test/examples/asciiMaze/dashboardManager/telemetry/README.md +28 -28
  181. package/test/examples/asciiMaze/evolutionEngine/README.md +1527 -1527
  182. package/test/examples/asciiMaze/mazeMovement/README.md +105 -105
  183. package/test/examples/asciiMaze/mazeMovement/finalization/README.md +16 -16
  184. package/test/examples/asciiMaze/mazeMovement/policy/README.md +57 -57
  185. package/test/examples/asciiMaze/mazeMovement/runtime/README.md +52 -52
  186. package/test/examples/asciiMaze/mazeMovement/shaping/README.md +46 -46
  187. package/test/examples/flappy_bird/browser-entry/README.md +508 -508
  188. package/test/examples/flappy_bird/browser-entry/host/README.md +101 -101
  189. package/test/examples/flappy_bird/browser-entry/host/resize/README.md +144 -144
  190. package/test/examples/flappy_bird/browser-entry/network-view/README.md +194 -194
  191. package/test/examples/flappy_bird/browser-entry/playback/README.md +278 -278
  192. package/test/examples/flappy_bird/browser-entry/playback/background/README.md +129 -129
  193. package/test/examples/flappy_bird/browser-entry/playback/background/ground-grid/README.md +502 -502
  194. package/test/examples/flappy_bird/browser-entry/playback/frame-render/README.md +139 -139
  195. package/test/examples/flappy_bird/browser-entry/playback/snapshot/README.md +10 -10
  196. package/test/examples/flappy_bird/browser-entry/playback/trail/README.md +43 -43
  197. package/test/examples/flappy_bird/browser-entry/playback/worker-channel/README.md +30 -30
  198. package/test/examples/flappy_bird/browser-entry/runtime/README.md +59 -59
  199. package/test/examples/flappy_bird/browser-entry/visualization/README.md +276 -276
  200. package/test/examples/flappy_bird/browser-entry/worker-channel/README.md +16 -16
  201. package/test/examples/flappy_bird/constants/README.md +1070 -1070
  202. package/test/examples/flappy_bird/environment/README.md +22 -22
  203. package/test/examples/flappy_bird/evaluation/README.md +32 -32
  204. package/test/examples/flappy_bird/evaluation/rollout/README.md +141 -141
  205. package/test/examples/flappy_bird/flappy-evolution-worker/README.md +425 -425
  206. package/test/examples/flappy_bird/simulation-shared/README.md +170 -170
  207. package/test/examples/flappy_bird/simulation-shared/observation/README.md +109 -109
  208. package/test/examples/flappy_bird/trainer/README.md +325 -325
  209. package/test/examples/flappy_bird/trainer/evaluation/README.md +74 -74
  210. package/scripts/analyze-trace.ts +0 -590
  211. package/scripts/copy-examples.mjs +0 -114
  212. package/scripts/export-onnx.mjs +0 -86
  213. package/scripts/generate-bench-tables.mjs +0 -182
  214. package/scripts/generate-docs.ts +0 -2900
  215. package/scripts/write-dist-docs-pkg.mjs +0 -16
@@ -0,0 +1,69 @@
1
+ import fs from 'node:fs';
2
+
3
+ import { DEFAULT_TOP_COUNT } from './analyze-trace.constants.js';
4
+ import type { CliOptions, TraceFile } from './analyze-trace.types.js';
5
+
6
+ /**
7
+ * Resolves CLI flags for one trace-analyzer invocation.
8
+ *
9
+ * Supported inputs:
10
+ * - positional trace path
11
+ * - `--top=NUMBER`
12
+ * - `--top NUMBER`
13
+ *
14
+ * @param argumentValues - CLI arguments after the script path.
15
+ * @returns Parsed CLI options.
16
+ */
17
+ export function resolveCliOptions(
18
+ argumentValues: readonly string[],
19
+ ): CliOptions {
20
+ const pathArgument = argumentValues.find(
21
+ (argument) => !argument.startsWith('--'),
22
+ );
23
+ if (!pathArgument) {
24
+ throw new Error(
25
+ 'Missing trace path. Example: npm run trace:analyze -- test/examples/flappy_bird/Trace.json',
26
+ );
27
+ }
28
+
29
+ return {
30
+ tracePath: pathArgument,
31
+ topCount: resolveRequestedTopCount(argumentValues),
32
+ };
33
+ }
34
+
35
+ /**
36
+ * Reads and parses one trace JSON file.
37
+ *
38
+ * @param traceFilePath - Absolute or relative trace file path.
39
+ * @returns Parsed trace file payload.
40
+ */
41
+ export function loadTrace(traceFilePath: string): TraceFile {
42
+ const traceFileContents = fs.readFileSync(traceFilePath, 'utf8');
43
+ return JSON.parse(traceFileContents) as TraceFile;
44
+ }
45
+
46
+ /**
47
+ * Resolves the requested top-count override from CLI flags.
48
+ *
49
+ * @param argumentValues - CLI arguments after the script path.
50
+ * @returns Positive top-count value or the repo default.
51
+ */
52
+ function resolveRequestedTopCount(argumentValues: readonly string[]): number {
53
+ const inlineTopArgument = argumentValues.find((argument) =>
54
+ argument.startsWith('--top='),
55
+ );
56
+ const splitTopArgumentIndex = argumentValues.findIndex(
57
+ (argument) => argument === '--top',
58
+ );
59
+
60
+ const requestedTopCount = inlineTopArgument
61
+ ? Number.parseInt(inlineTopArgument.slice('--top='.length), 10)
62
+ : splitTopArgumentIndex >= 0
63
+ ? Number.parseInt(argumentValues[splitTopArgumentIndex + 1] ?? '', 10)
64
+ : DEFAULT_TOP_COUNT;
65
+
66
+ return Number.isFinite(requestedTopCount) && requestedTopCount > 0
67
+ ? requestedTopCount
68
+ : DEFAULT_TOP_COUNT;
69
+ }
@@ -0,0 +1,100 @@
1
+ import { formatDistribution, formatMs } from './analyze-trace.shared.js';
2
+ import type { TraceAnalysis } from './analyze-trace.types.js';
3
+
4
+ /**
5
+ * Builds the textual report lines for one analyzed trace.
6
+ *
7
+ * @param traceAnalysis - Fully analyzed trace payload.
8
+ * @returns Report lines in display order.
9
+ */
10
+ export function buildTraceReportLines(traceAnalysis: TraceAnalysis): string[] {
11
+ const reportLines: string[] = [];
12
+
13
+ appendSection(reportLines, 'Trace', [
14
+ `File: ${traceAnalysis.traceFilePath}`,
15
+ `Window: ${formatMs(traceAnalysis.timeRange.windowMs)} across ${traceAnalysis.traceEventCount.toLocaleString()} events`,
16
+ `Frames: ${traceAnalysis.beginFrameCount.toLocaleString()} begin, ${traceAnalysis.droppedFrameCount.toLocaleString()} dropped`,
17
+ ]);
18
+
19
+ appendSection(
20
+ reportLines,
21
+ 'Thread Summary',
22
+ traceAnalysis.threadSummaries.map(
23
+ (threadSummary) =>
24
+ `${threadSummary.label}: total=${formatMs(threadSummary.totalDurationMs)}, ` +
25
+ `RunTask=${formatMs(threadSummary.runTaskDurationMs)}, ` +
26
+ `>16.7ms=${threadSummary.longTaskCount16ms}, ` +
27
+ `>50ms=${threadSummary.longTaskCount50ms}, ` +
28
+ `max=${formatMs(threadSummary.maxTaskMs)}`,
29
+ ),
30
+ );
31
+
32
+ appendSection(reportLines, 'Animation Frames', [
33
+ `FireAnimationFrame: ${formatDistribution(traceAnalysis.fireAnimationFrameDurations)}`,
34
+ `FunctionCall: ${formatDistribution(traceAnalysis.functionCallDurations)}`,
35
+ ]);
36
+
37
+ appendSection(
38
+ reportLines,
39
+ 'Longest Events',
40
+ traceAnalysis.longestEvents.map(
41
+ (longEvent) =>
42
+ `${formatMs(longEvent.durationMs)} | ${longEvent.threadLabel} | ${longEvent.eventName}${
43
+ longEvent.scriptUrl ? ` | ${longEvent.scriptUrl}` : ''
44
+ }`,
45
+ ),
46
+ );
47
+
48
+ appendSection(
49
+ reportLines,
50
+ 'Top Events',
51
+ traceAnalysis.hottestEvents.map(
52
+ (eventSummary) =>
53
+ `${eventSummary.name}: count=${eventSummary.count.toLocaleString()}, total=${formatMs(eventSummary.totalMs)}, max=${formatMs(eventSummary.maxMs)}`,
54
+ ),
55
+ );
56
+
57
+ appendSection(
58
+ reportLines,
59
+ 'Top Function Calls',
60
+ traceAnalysis.hottestFunctionCalls.map(
61
+ (functionCallSummary) =>
62
+ `${functionCallSummary.name}: count=${functionCallSummary.count.toLocaleString()}, total=${formatMs(functionCallSummary.totalMs)}, max=${formatMs(functionCallSummary.maxMs)}`,
63
+ ),
64
+ );
65
+
66
+ return reportLines;
67
+ }
68
+
69
+ /**
70
+ * Prints one analyzed trace report to stdout.
71
+ *
72
+ * @param traceAnalysis - Fully analyzed trace payload.
73
+ * @returns Nothing.
74
+ */
75
+ export function printTraceReport(traceAnalysis: TraceAnalysis): void {
76
+ for (const reportLine of buildTraceReportLines(traceAnalysis)) {
77
+ console.log(reportLine);
78
+ }
79
+ }
80
+
81
+ /**
82
+ * Appends one report section to the output line buffer.
83
+ *
84
+ * @param reportLines - Mutable output line buffer.
85
+ * @param title - Section title.
86
+ * @param contentLines - Section body lines.
87
+ * @returns Nothing.
88
+ */
89
+ function appendSection(
90
+ reportLines: string[],
91
+ title: string,
92
+ contentLines: readonly string[],
93
+ ): void {
94
+ if (reportLines.length > 0) {
95
+ reportLines.push('');
96
+ }
97
+
98
+ reportLines.push(`[${title}]`);
99
+ reportLines.push(...contentLines);
100
+ }
@@ -0,0 +1,116 @@
1
+ import type { Primitive } from './analyze-trace.types.js';
2
+
3
+ /**
4
+ * Safely resolves a nested primitive value from a trace event args object.
5
+ *
6
+ * @param value - Candidate object tree.
7
+ * @param pathSegments - Nested path segments to follow.
8
+ * @returns Primitive value when the path resolves cleanly.
9
+ */
10
+ export function readNestedPrimitive(
11
+ value: unknown,
12
+ pathSegments: readonly string[],
13
+ ): Primitive {
14
+ let currentValue: unknown = value;
15
+
16
+ for (const pathSegment of pathSegments) {
17
+ if (!currentValue || typeof currentValue !== 'object') {
18
+ return undefined;
19
+ }
20
+
21
+ currentValue = (currentValue as Record<string, unknown>)[pathSegment];
22
+ }
23
+
24
+ if (
25
+ currentValue == null ||
26
+ typeof currentValue === 'string' ||
27
+ typeof currentValue === 'number' ||
28
+ typeof currentValue === 'boolean'
29
+ ) {
30
+ return currentValue;
31
+ }
32
+
33
+ return undefined;
34
+ }
35
+
36
+ /**
37
+ * Formats a duration in milliseconds for stable textual output.
38
+ *
39
+ * @param durationMs - Duration in milliseconds.
40
+ * @returns Fixed two-decimal duration string.
41
+ */
42
+ export function formatMs(durationMs: number): string {
43
+ return `${durationMs.toFixed(2)}ms`;
44
+ }
45
+
46
+ /**
47
+ * Computes a nearest-rank percentile from an already sorted numeric array.
48
+ *
49
+ * @param sortedDurations - Sorted numeric sample set.
50
+ * @param rank - Percentile rank in the inclusive range `[0, 1]`.
51
+ * @returns Percentile value or `0` when the list is empty.
52
+ */
53
+ export function percentile(
54
+ sortedDurations: readonly number[],
55
+ rank: number,
56
+ ): number {
57
+ if (sortedDurations.length === 0) {
58
+ return 0;
59
+ }
60
+
61
+ const clampedRank = Math.max(0, Math.min(1, rank));
62
+ const index = Math.min(
63
+ sortedDurations.length - 1,
64
+ Math.floor(clampedRank * (sortedDurations.length - 1)),
65
+ );
66
+
67
+ return sortedDurations[index];
68
+ }
69
+
70
+ /**
71
+ * Formats a compact percentile summary for one ascending-sorted duration collection.
72
+ *
73
+ * @param sortedDurationsMs - Ascending-sorted duration samples in milliseconds.
74
+ * @returns Human-readable summary string.
75
+ */
76
+ export function formatDistribution(
77
+ sortedDurationsMs: readonly number[],
78
+ ): string {
79
+ if (sortedDurationsMs.length === 0) {
80
+ return 'count=0';
81
+ }
82
+
83
+ return [
84
+ `count=${sortedDurationsMs.length.toLocaleString()}`,
85
+ `p50=${formatMs(percentile(sortedDurationsMs, 0.5))}`,
86
+ `p90=${formatMs(percentile(sortedDurationsMs, 0.9))}`,
87
+ `p99=${formatMs(percentile(sortedDurationsMs, 0.99))}`,
88
+ `max=${formatMs(sortedDurationsMs.at(-1) ?? 0)}`,
89
+ ].join(', ');
90
+ }
91
+
92
+ /**
93
+ * Creates a stable lookup key for one process id.
94
+ *
95
+ * @param pid - Process identifier from the trace.
96
+ * @returns Stable process key when the id exists.
97
+ */
98
+ export function createProcessKey(pid: number | undefined): string | undefined {
99
+ return typeof pid === 'number' ? `pid:${pid}` : undefined;
100
+ }
101
+
102
+ /**
103
+ * Creates a stable lookup key for one process/thread pair.
104
+ *
105
+ * @param pid - Process identifier from the trace.
106
+ * @param tid - Thread identifier from the trace.
107
+ * @returns Stable thread key when both ids exist.
108
+ */
109
+ export function createThreadKey(
110
+ pid: number | undefined,
111
+ tid: number | undefined,
112
+ ): string | undefined {
113
+ return typeof pid === 'number' && typeof tid === 'number'
114
+ ? `pid:${pid}:tid:${tid}`
115
+ : undefined;
116
+ }
@@ -0,0 +1,45 @@
1
+ /**
2
+ * Reads a Chrome or Perfetto trace export and prints a compact performance
3
+ * report.
4
+ *
5
+ * The entrypoint stays intentionally small: CLI parsing, trace loading,
6
+ * analysis, and report rendering each live in focused sibling modules.
7
+ *
8
+ * Usage:
9
+ * npm run trace:analyze -- test/examples/flappy_bird/Trace-20260309T191949.json
10
+ * npm run docs:build-scripts && node dist-docs/scripts/analyze-trace/analyze-trace.js path/to/trace.json --top=20
11
+ */
12
+ import path from 'node:path';
13
+
14
+ import { analyzeTraceEvents } from './analyze-trace.analysis.js';
15
+ import { loadTrace, resolveCliOptions } from './analyze-trace.io.js';
16
+ import { printTraceReport } from './analyze-trace.report.js';
17
+
18
+ /**
19
+ * CLI entry point.
20
+ *
21
+ * @returns Nothing.
22
+ */
23
+ function main(): void {
24
+ // Step 1: Resolve the CLI request and load the raw trace.
25
+ const cliOptions = resolveCliOptions(process.argv.slice(2));
26
+ const traceFilePath = path.resolve(cliOptions.tracePath);
27
+ const traceFile = loadTrace(traceFilePath);
28
+ const traceEvents = traceFile.traceEvents ?? [];
29
+
30
+ if (traceEvents.length === 0) {
31
+ throw new Error(`Trace contains no events: ${traceFilePath}`);
32
+ }
33
+
34
+ // Step 2: Derive deterministic report data from the raw trace.
35
+ const traceAnalysis = analyzeTraceEvents({
36
+ traceFilePath,
37
+ traceEvents,
38
+ topCount: cliOptions.topCount,
39
+ });
40
+
41
+ // Step 3: Render the final textual report.
42
+ printTraceReport(traceAnalysis);
43
+ }
44
+
45
+ main();
@@ -0,0 +1,72 @@
1
+ /** Primitive trace metadata value supported by the nested args reader. */
2
+ export type Primitive = string | number | boolean | null | undefined;
3
+
4
+ /** Minimal Chrome or Perfetto trace event shape used by the analyzer. */
5
+ export interface TraceEvent {
6
+ pid?: number;
7
+ tid?: number;
8
+ ts?: number;
9
+ dur?: number;
10
+ tdur?: number;
11
+ ph?: string;
12
+ cat?: string;
13
+ name?: string;
14
+ args?: Record<string, unknown>;
15
+ }
16
+
17
+ /** Top-level trace file shape expected from exported JSON captures. */
18
+ export interface TraceFile {
19
+ traceEvents?: TraceEvent[];
20
+ }
21
+
22
+ /** Parsed CLI options for one analyzer invocation. */
23
+ export interface CliOptions {
24
+ tracePath: string;
25
+ topCount: number;
26
+ }
27
+
28
+ /** Summary metrics for one thread label in the trace. */
29
+ export interface ThreadSummary {
30
+ label: string;
31
+ totalDurationMs: number;
32
+ runTaskDurationMs: number;
33
+ longTaskCount16ms: number;
34
+ longTaskCount50ms: number;
35
+ maxTaskMs: number;
36
+ }
37
+
38
+ /** Aggregated duration rollup for one trace event or one script URL. */
39
+ export interface AggregatedDuration {
40
+ name: string;
41
+ count: number;
42
+ totalMs: number;
43
+ maxMs: number;
44
+ }
45
+
46
+ /** Longest single complete event entry used by the report. */
47
+ export interface LongEventSummary {
48
+ durationMs: number;
49
+ threadLabel: string;
50
+ eventName: string;
51
+ scriptUrl?: string;
52
+ }
53
+
54
+ /** Visible trace window spanned by the capture. */
55
+ export interface TraceTimeRange {
56
+ windowMs: number;
57
+ }
58
+
59
+ /** Fully analyzed trace payload used by the reporting layer. */
60
+ export interface TraceAnalysis {
61
+ traceFilePath: string;
62
+ traceEventCount: number;
63
+ timeRange: TraceTimeRange;
64
+ droppedFrameCount: number;
65
+ beginFrameCount: number;
66
+ threadSummaries: ThreadSummary[];
67
+ hottestEvents: AggregatedDuration[];
68
+ hottestFunctionCalls: AggregatedDuration[];
69
+ longestEvents: LongEventSummary[];
70
+ fireAnimationFrameDurations: number[];
71
+ functionCallDurations: number[];
72
+ }
@@ -22,14 +22,11 @@
22
22
  --double-border: 3px double rgba(15, 181, 255, 0.88);
23
23
  --double-border-soft: 3px double rgba(10, 142, 160, 0.58);
24
24
  --panel-glow:
25
- inset 0 0 0 1px rgba(15, 181, 255, 0.16),
26
- 0 0 18px rgba(15, 181, 255, 0.12);
25
+ inset 0 0 0 1px rgba(15, 181, 255, 0.16), 0 0 18px rgba(15, 181, 255, 0.12);
27
26
  --panel-glow-strong:
28
- inset 0 0 0 1px rgba(15, 181, 255, 0.2),
29
- 0 0 24px rgba(0, 229, 255, 0.15);
27
+ inset 0 0 0 1px rgba(15, 181, 255, 0.2), 0 0 24px rgba(0, 229, 255, 0.15);
30
28
  --focus-ring:
31
- 0 0 0 2px rgba(255, 154, 46, 0.9),
32
- 0 0 0 5px rgba(15, 181, 255, 0.2);
29
+ 0 0 0 2px rgba(255, 154, 46, 0.9), 0 0 0 5px rgba(15, 181, 255, 0.2);
33
30
  }
34
31
  * {
35
32
  box-sizing: border-box;
@@ -60,13 +57,35 @@ body::before {
60
57
  inset: 0;
61
58
  pointer-events: none;
62
59
  background:
63
- radial-gradient(circle at 12% 18%, rgba(255, 255, 255, 0.2) 0 1px, transparent 1.5px),
64
- radial-gradient(circle at 78% 12%, rgba(159, 220, 255, 0.18) 0 1px, transparent 1.5px),
65
- radial-gradient(circle at 68% 42%, rgba(0, 229, 255, 0.14) 0 1px, transparent 1.5px),
66
- radial-gradient(circle at 28% 72%, rgba(255, 255, 255, 0.12) 0 1px, transparent 1.5px),
60
+ radial-gradient(
61
+ circle at 12% 18%,
62
+ rgba(255, 255, 255, 0.2) 0 1px,
63
+ transparent 1.5px
64
+ ),
65
+ radial-gradient(
66
+ circle at 78% 12%,
67
+ rgba(159, 220, 255, 0.18) 0 1px,
68
+ transparent 1.5px
69
+ ),
70
+ radial-gradient(
71
+ circle at 68% 42%,
72
+ rgba(0, 229, 255, 0.14) 0 1px,
73
+ transparent 1.5px
74
+ ),
75
+ radial-gradient(
76
+ circle at 28% 72%,
77
+ rgba(255, 255, 255, 0.12) 0 1px,
78
+ transparent 1.5px
79
+ ),
67
80
  linear-gradient(rgba(10, 142, 160, 0.05) 1px, transparent 1px),
68
81
  linear-gradient(90deg, rgba(10, 142, 160, 0.04) 1px, transparent 1px);
69
- background-size: auto, auto, auto, auto, 100% 28px, 28px 100%;
82
+ background-size:
83
+ auto,
84
+ auto,
85
+ auto,
86
+ auto,
87
+ 100% 28px,
88
+ 28px 100%;
70
89
  opacity: 0.45;
71
90
  z-index: -1;
72
91
  }
@@ -89,8 +108,11 @@ a:focus-visible {
89
108
  position: sticky;
90
109
  top: 0;
91
110
  z-index: 30;
92
- background:
93
- linear-gradient(180deg, rgba(0, 0, 0, 0.96), rgba(3, 8, 18, 0.94));
111
+ background: linear-gradient(
112
+ 180deg,
113
+ rgba(0, 0, 0, 0.96),
114
+ rgba(3, 8, 18, 0.94)
115
+ );
94
116
  backdrop-filter: blur(8px);
95
117
  border-bottom: var(--double-border-soft);
96
118
  box-shadow:
@@ -225,7 +247,11 @@ a:focus-visible {
225
247
  max-height: calc(100vh - 90px);
226
248
  overflow: auto;
227
249
  background:
228
- radial-gradient(circle at top left, rgba(0, 229, 255, 0.08), transparent 36%),
250
+ radial-gradient(
251
+ circle at top left,
252
+ rgba(0, 229, 255, 0.08),
253
+ transparent 36%
254
+ ),
229
255
  linear-gradient(180deg, rgba(3, 17, 31, 0.98), rgba(0, 0, 0, 0.98));
230
256
  border: var(--double-border);
231
257
  border-radius: 0;
@@ -281,14 +307,21 @@ a:focus-visible {
281
307
  position: relative;
282
308
  padding: 12px;
283
309
  border: var(--double-border-soft);
284
- background:
285
- linear-gradient(180deg, rgba(3, 17, 31, 0.96), rgba(0, 0, 0, 0.98));
310
+ background: linear-gradient(
311
+ 180deg,
312
+ rgba(3, 17, 31, 0.96),
313
+ rgba(0, 0, 0, 0.98)
314
+ );
286
315
  box-shadow: var(--panel-glow);
287
316
  }
288
317
  .group-priority {
289
318
  padding: 12px;
290
319
  background:
291
- radial-gradient(circle at top left, rgba(255, 154, 46, 0.08), transparent 34%),
320
+ radial-gradient(
321
+ circle at top left,
322
+ rgba(255, 154, 46, 0.08),
323
+ transparent 34%
324
+ ),
292
325
  linear-gradient(180deg, rgba(3, 17, 31, 0.98), rgba(0, 0, 0, 0.98));
293
326
  border: var(--double-border);
294
327
  box-shadow: var(--panel-glow-strong);
@@ -318,7 +351,11 @@ a:focus-visible {
318
351
  border-radius: 0;
319
352
  border: var(--double-border-soft);
320
353
  background:
321
- radial-gradient(circle at top left, rgba(0, 229, 255, 0.08), transparent 40%),
354
+ radial-gradient(
355
+ circle at top left,
356
+ rgba(0, 229, 255, 0.08),
357
+ transparent 40%
358
+ ),
322
359
  linear-gradient(180deg, rgba(3, 17, 31, 0.98), rgba(0, 0, 0, 0.98));
323
360
  box-shadow: var(--panel-glow);
324
361
  }
@@ -432,7 +469,11 @@ a:focus-visible {
432
469
  bottom: -1px;
433
470
  width: var(--pipe-rail-thickness);
434
471
  transform: translateX(-50%);
435
- background: linear-gradient(180deg, rgba(15, 181, 255, 0.94), rgba(10, 142, 160, 0.78));
472
+ background: linear-gradient(
473
+ 180deg,
474
+ rgba(15, 181, 255, 0.94),
475
+ rgba(10, 142, 160, 0.78)
476
+ );
436
477
  box-shadow: 0 0 8px rgba(0, 229, 255, 0.16);
437
478
  transition:
438
479
  background 120ms ease,
@@ -446,7 +487,11 @@ a:focus-visible {
446
487
  top: 50%;
447
488
  height: var(--pipe-rail-thickness);
448
489
  transform: translateY(-50%);
449
- background: linear-gradient(90deg, rgba(15, 181, 255, 0.94), rgba(10, 142, 160, 0.78));
490
+ background: linear-gradient(
491
+ 90deg,
492
+ rgba(15, 181, 255, 0.94),
493
+ rgba(10, 142, 160, 0.78)
494
+ );
450
495
  box-shadow: 0 0 8px rgba(0, 229, 255, 0.16);
451
496
  transition:
452
497
  background 120ms ease,
@@ -479,7 +524,11 @@ a:focus-visible {
479
524
  .pipe-nav-item:hover .nav-rail-cell-last::after,
480
525
  .pipe-nav-item.current .nav-rail-cell::before,
481
526
  .pipe-nav-item.current .nav-rail-cell-last::after {
482
- background: linear-gradient(180deg, rgba(255, 154, 46, 0.98), rgba(255, 92, 255, 0.72));
527
+ background: linear-gradient(
528
+ 180deg,
529
+ rgba(255, 154, 46, 0.98),
530
+ rgba(255, 92, 255, 0.72)
531
+ );
483
532
  box-shadow: 0 0 10px rgba(255, 154, 46, 0.22);
484
533
  }
485
534
  .pipe-nav-item:hover .nav-link-label,
@@ -631,7 +680,11 @@ table {
631
680
  display: block;
632
681
  overflow: auto;
633
682
  border: var(--double-border-soft);
634
- background: linear-gradient(180deg, rgba(3, 17, 31, 0.98), rgba(0, 0, 0, 0.98));
683
+ background: linear-gradient(
684
+ 180deg,
685
+ rgba(3, 17, 31, 0.98),
686
+ rgba(0, 0, 0, 0.98)
687
+ );
635
688
  box-shadow: var(--panel-glow);
636
689
  }
637
690
  th,
@@ -649,7 +702,11 @@ blockquote {
649
702
  padding: 12px 14px;
650
703
  border-left: 3px double var(--accent-orange);
651
704
  background:
652
- radial-gradient(circle at top left, rgba(255, 92, 255, 0.08), transparent 36%),
705
+ radial-gradient(
706
+ circle at top left,
707
+ rgba(255, 92, 255, 0.08),
708
+ transparent 36%
709
+ ),
653
710
  linear-gradient(180deg, rgba(12, 8, 24, 0.98), rgba(0, 0, 0, 0.98));
654
711
  border-radius: 0;
655
712
  box-shadow: var(--panel-glow);