sparkforensics-server 0.1.0 → 0.2.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 (440) hide show
  1. package/README.md +6 -0
  2. package/index.js +40 -49
  3. package/lib/static-files.js +10 -6
  4. package/package.json +20 -1
  5. package/public/assets/AutoscalingChurn-CFJkiqxW.js +1 -0
  6. package/public/assets/CacheUtilization-kTKZBzDA.js +1 -0
  7. package/public/assets/CachingOpportunity-CU70EWcF.js +1 -0
  8. package/public/assets/ColdStart-hOI-WOvm.js +1 -0
  9. package/public/assets/ConfigAudit-B4oAksZY.js +1 -0
  10. package/public/assets/CoreUsageArea-D-dR3SQk.js +1 -0
  11. package/public/assets/Dashboard-MDhohsq4.js +4 -0
  12. package/public/assets/DuplicatePlanSubtree-54RKsfI7.js +1 -0
  13. package/public/assets/DurationHistogram-CviQyPNM.js +1 -0
  14. package/public/assets/EmptyState-v2ehFncc.js +1 -0
  15. package/public/assets/ExecutorUtilization-vHyFHgL6.js +1 -0
  16. package/public/assets/GcPressure-DINuhcno.js +1 -0
  17. package/public/assets/ImpactEstimate-C6GzFOY9.js +1 -0
  18. package/public/assets/IncompleteRun-a1kuCnU6.js +1 -0
  19. package/public/assets/JobFailures-CYwPfUhR.js +1 -0
  20. package/public/assets/MemoryUtilization-Dlfj0e_f.js +1 -0
  21. package/public/assets/OverBroadcast-BMu7Y8l9.js +1 -0
  22. package/public/assets/PartitionSizing-CvMGfSVB.js +1 -0
  23. package/public/assets/PlanExplorer-xYFme_Rx.js +1 -0
  24. package/public/assets/PlanGraphRoute-a3OGg6ET.js +1 -0
  25. package/public/assets/RetryWaste-JKRjOZ-J.js +1 -0
  26. package/public/assets/RowPagination-DypWbnic.js +1 -0
  27. package/public/assets/ShuffleIO-Mi8HiVu-.js +1 -0
  28. package/public/assets/Skew-C3iOScTC.js +1 -0
  29. package/public/assets/SlowHost-DKBg18v-.js +1 -0
  30. package/public/assets/SmallFiles-CCCB6AIu.js +1 -0
  31. package/public/assets/SpeculationWaste-DUNupw0_.js +1 -0
  32. package/public/assets/Spill-BlLdatik.js +1 -0
  33. package/public/assets/StageFailed-qtY7C7Yo.js +1 -0
  34. package/public/assets/StageFindingGroup-CoVdEZYs.js +1 -0
  35. package/public/assets/StageHeader-uDBLHgdg.js +1 -0
  36. package/public/assets/StagePill-CYCMdlIx.js +1 -0
  37. package/public/assets/StageShape-zs0mAjQJ.js +1 -0
  38. package/public/assets/StageSlowness-C7vg0u5Q.js +1 -0
  39. package/public/assets/Straggler-4BRGAQP4.js +1 -0
  40. package/public/assets/TaskFailures-CSjO23Kg.js +1 -0
  41. package/public/assets/TinyTask-6Zuovcn5.js +1 -0
  42. package/public/assets/UnderBroadcast-CePjo0ZV.js +1 -0
  43. package/public/assets/WidgetLeadSummary-DRNRVvn7.js +1 -0
  44. package/public/assets/{charts-vendor-NMqKV0H4.js → charts-vendor-Cf1-wzeH.js} +2 -2
  45. package/public/assets/downsample-D8XmJk6i.js +1 -0
  46. package/public/assets/finding-anchor-4TmpXd7j.js +1 -0
  47. package/public/assets/impact-sort-Cq6KOLqh.js +1 -0
  48. package/public/assets/inbox-DBFovbUX.js +1 -0
  49. package/public/assets/index-CHjV2-jB.js +9 -0
  50. package/public/assets/index-DMEtaP4u.css +1 -0
  51. package/public/assets/parser-worker-BMPPE37w.js +64 -0
  52. package/public/assets/plan-graph-vendor-B96a-e4N.js +7 -0
  53. package/public/assets/react-vendor-DA6opS-q.js +9 -0
  54. package/public/assets/select-D5fK-za0.js +1 -0
  55. package/public/assets/toggle-group-lbIXRG0W.js +1 -0
  56. package/public/assets/usePagedRows-CFoZrPr4.js +1 -0
  57. package/public/assets/useRecentFiles-BPGQ6XII.js +13 -0
  58. package/public/assets/useSortMode-BOPpfLhJ.js +1 -0
  59. package/public/docs/404.html +3 -3
  60. package/public/docs/assets/{app.Bxy4_HVA.js → app.BgzsaTS3.js} +1 -1
  61. package/public/docs/assets/aqe-loop.IwQSATHw.svg +1 -0
  62. package/public/docs/assets/aqe-loop.dark.DGbaxqJE.svg +1 -0
  63. package/public/docs/assets/broadcast-vs-shuffle.Db4WY1XK.svg +1 -0
  64. package/public/docs/assets/broadcast-vs-shuffle.dark.C7Bxs0mG.svg +1 -0
  65. package/public/docs/assets/cache-lifecycle.dark.B-hS7AgU.svg +1 -0
  66. package/public/docs/assets/cache-lifecycle.rEOVYQNU.svg +1 -0
  67. package/public/docs/assets/chunks/@localSearchIndexroot.PQ5JepCp.js +1 -0
  68. package/public/docs/assets/chunks/{VPLocalSearchBox.L5jNOIR3.js → VPLocalSearchBox.DOq3t-On.js} +1 -1
  69. package/public/docs/assets/chunks/duplicate-plan-subtree.dark.Cdp70QhV.js +1 -0
  70. package/public/docs/assets/chunks/{framework.CNDaxUod.js → framework.DSg0KOwT.js} +1 -1
  71. package/public/docs/assets/chunks/retry-escalation-ladder.dark.DHipdJgZ.js +1 -0
  72. package/public/docs/assets/chunks/theme.BP14rGBx.js +2 -0
  73. package/public/docs/assets/cold-start-timeline.DxC_Sc7w.svg +1 -0
  74. package/public/docs/assets/cold-start-timeline.dark.CZ17YcAG.svg +1 -0
  75. package/public/docs/assets/columnar-layout.PghGeOEA.svg +1 -0
  76. package/public/docs/assets/columnar-layout.dark.BVNlz0ff.svg +1 -0
  77. package/public/docs/assets/container-memory.DIO0AnIm.svg +1 -0
  78. package/public/docs/assets/container-memory.dark.CP-5zuCl.svg +1 -0
  79. package/public/docs/assets/contributor-guide_architecture_board-widgets.md.B-OsL91z.js +1 -0
  80. package/public/docs/assets/contributor-guide_architecture_board-widgets.md.B-OsL91z.lean.js +1 -0
  81. package/public/docs/assets/contributor-guide_architecture_detector-contract.md.BOeH4d1J.js +1 -0
  82. package/public/docs/assets/contributor-guide_architecture_detector-contract.md.BOeH4d1J.lean.js +1 -0
  83. package/public/docs/assets/contributor-guide_architecture_drill-down.md.BtPdlM7r.js +1 -0
  84. package/public/docs/assets/contributor-guide_architecture_drill-down.md.BtPdlM7r.lean.js +1 -0
  85. package/public/docs/assets/{contributor-guide_architecture_impact-estimation.md.CzlXOnCq.js → contributor-guide_architecture_impact-estimation.md.DYCDPgkh.js} +1 -1
  86. package/public/docs/assets/{contributor-guide_architecture_impact-estimation.md.CzlXOnCq.lean.js → contributor-guide_architecture_impact-estimation.md.DYCDPgkh.lean.js} +1 -1
  87. package/public/docs/assets/{contributor-guide_architecture_index.md.94lNbF-4.js → contributor-guide_architecture_index.md.3TO9ic6w.js} +1 -1
  88. package/public/docs/assets/{contributor-guide_architecture_index.md.94lNbF-4.lean.js → contributor-guide_architecture_index.md.3TO9ic6w.lean.js} +1 -1
  89. package/public/docs/assets/{contributor-guide_architecture_overview.md.mQFlN66o.js → contributor-guide_architecture_overview.md.CehiRmGn.js} +1 -1
  90. package/public/docs/assets/{contributor-guide_architecture_overview.md.mQFlN66o.lean.js → contributor-guide_architecture_overview.md.CehiRmGn.lean.js} +1 -1
  91. package/public/docs/assets/contributor-guide_architecture_state-and-history.md.m3S3UdMk.js +1 -0
  92. package/public/docs/assets/contributor-guide_architecture_state-and-history.md.m3S3UdMk.lean.js +1 -0
  93. package/public/docs/assets/contributor-guide_architecture_widget-rendering.md.DbqPf2OT.js +1 -0
  94. package/public/docs/assets/contributor-guide_architecture_widget-rendering.md.DbqPf2OT.lean.js +1 -0
  95. package/public/docs/assets/{contributor-guide_architecture_worker-protocol.md.BaS_L10g.js → contributor-guide_architecture_worker-protocol.md.B93qJ_tT.js} +2 -2
  96. package/public/docs/assets/{contributor-guide_architecture_worker-protocol.md.BaS_L10g.lean.js → contributor-guide_architecture_worker-protocol.md.B93qJ_tT.lean.js} +1 -1
  97. package/public/docs/assets/contributor-guide_contributing.md.CvRsdr6J.js +1 -0
  98. package/public/docs/assets/contributor-guide_contributing.md.CvRsdr6J.lean.js +1 -0
  99. package/public/docs/assets/{contributor-guide_development-setup.md.CpUMVtIK.js → contributor-guide_development-setup.md.DvAN_9mK.js} +1 -1
  100. package/public/docs/assets/{contributor-guide_development-setup.md.CpUMVtIK.lean.js → contributor-guide_development-setup.md.DvAN_9mK.lean.js} +1 -1
  101. package/public/docs/assets/{contributor-guide_testing.md.C0q-BJfl.js → contributor-guide_testing.md.6rIKqSyY.js} +1 -1
  102. package/public/docs/assets/{contributor-guide_testing.md.C0q-BJfl.lean.js → contributor-guide_testing.md.6rIKqSyY.lean.js} +1 -1
  103. package/public/docs/assets/dag-stages.DSz_S937.svg +1 -0
  104. package/public/docs/assets/dag-stages.dark.F72UzxH4.svg +1 -0
  105. package/public/docs/assets/driver-executor.D5pQ7YN1.svg +1 -0
  106. package/public/docs/assets/driver-executor.dark.BmX9cPvh.svg +1 -0
  107. package/public/docs/assets/duplicate-plan-subtree.B4cvN6fj.svg +1 -0
  108. package/public/docs/assets/duplicate-plan-subtree.dark.Dw8wS0Ag.svg +1 -0
  109. package/public/docs/assets/{index.md.CETnDZzR.js → index.md.CHJVslga.js} +1 -1
  110. package/public/docs/assets/{index.md.CETnDZzR.lean.js → index.md.CHJVslga.lean.js} +1 -1
  111. package/public/docs/assets/join-strategy.C_FvrCEo.svg +1 -0
  112. package/public/docs/assets/join-strategy.dark.ChMLnNII.svg +1 -0
  113. package/public/docs/assets/memory-borrowing.BqQRJg0u.svg +1 -0
  114. package/public/docs/assets/memory-borrowing.dark.Yhh20O9C.svg +1 -0
  115. package/public/docs/assets/memory-regions.XHvO7jHG.svg +1 -0
  116. package/public/docs/assets/memory-regions.dark.D4TP9_08.svg +1 -0
  117. package/public/docs/assets/repartition-vs-coalesce.BovLRrpj.svg +1 -0
  118. package/public/docs/assets/repartition-vs-coalesce.dark.BhAczKZQ.svg +1 -0
  119. package/public/docs/assets/retry-escalation-ladder.DyTKJJmZ.svg +1 -0
  120. package/public/docs/assets/retry-escalation-ladder.dark.BdsabtU3.svg +1 -0
  121. package/public/docs/assets/shuffle-map-reduce.KuOEZVmg.svg +1 -0
  122. package/public/docs/assets/shuffle-map-reduce.dark.BgQZnFSb.svg +1 -0
  123. package/public/docs/assets/spill-classification.BU2euYDO.svg +1 -0
  124. package/public/docs/assets/spill-classification.dark.D7i1M40d.svg +1 -0
  125. package/public/docs/assets/{style.D2mY0HUs.css → style.DXOMCXxn.css} +1 -1
  126. package/public/docs/assets/tuning-reference_anti-patterns.md.Df1YMIHu.js +1 -0
  127. package/public/docs/assets/tuning-reference_anti-patterns.md.Df1YMIHu.lean.js +1 -0
  128. package/public/docs/assets/tuning-reference_aqe.md.BIsCtLzm.js +1 -0
  129. package/public/docs/assets/tuning-reference_aqe.md.BIsCtLzm.lean.js +1 -0
  130. package/public/docs/assets/tuning-reference_bottleneck-broadcast-sizing.md.CEstB3Ia.js +1 -0
  131. package/public/docs/assets/tuning-reference_bottleneck-broadcast-sizing.md.CEstB3Ia.lean.js +1 -0
  132. package/public/docs/assets/tuning-reference_bottleneck-cold-start.md.CEuy-72y.js +7 -0
  133. package/public/docs/assets/tuning-reference_bottleneck-cold-start.md.CEuy-72y.lean.js +1 -0
  134. package/public/docs/assets/tuning-reference_bottleneck-duplicate-plan-subtree.md.CIohQDfn.js +1 -0
  135. package/public/docs/assets/tuning-reference_bottleneck-duplicate-plan-subtree.md.CIohQDfn.lean.js +1 -0
  136. package/public/docs/assets/tuning-reference_bottleneck-failures.md.4z5BXGJ2.js +6 -0
  137. package/public/docs/assets/tuning-reference_bottleneck-failures.md.4z5BXGJ2.lean.js +1 -0
  138. package/public/docs/assets/tuning-reference_bottleneck-gc.md.DSxzZRK7.js +6 -0
  139. package/public/docs/assets/tuning-reference_bottleneck-gc.md.DSxzZRK7.lean.js +1 -0
  140. package/public/docs/assets/tuning-reference_bottleneck-job-failure-rate.md.BaJl__1W.js +8 -0
  141. package/public/docs/assets/tuning-reference_bottleneck-job-failure-rate.md.BaJl__1W.lean.js +1 -0
  142. package/public/docs/assets/tuning-reference_bottleneck-memory-utilization.md.DbP-SJZc.js +7 -0
  143. package/public/docs/assets/tuning-reference_bottleneck-memory-utilization.md.DbP-SJZc.lean.js +1 -0
  144. package/public/docs/assets/tuning-reference_bottleneck-retry-waste.md.D5JMjVOt.js +1 -0
  145. package/public/docs/assets/tuning-reference_bottleneck-retry-waste.md.D5JMjVOt.lean.js +1 -0
  146. package/public/docs/assets/tuning-reference_bottleneck-shuffle.md.CM-nTmIH.js +12 -0
  147. package/public/docs/assets/tuning-reference_bottleneck-shuffle.md.CM-nTmIH.lean.js +1 -0
  148. package/public/docs/assets/tuning-reference_bottleneck-skew.md.BdUwiDhn.js +14 -0
  149. package/public/docs/assets/tuning-reference_bottleneck-skew.md.BdUwiDhn.lean.js +1 -0
  150. package/public/docs/assets/tuning-reference_bottleneck-slow-host.md.BlIo6UDW.js +7 -0
  151. package/public/docs/assets/tuning-reference_bottleneck-slow-host.md.BlIo6UDW.lean.js +1 -0
  152. package/public/docs/assets/tuning-reference_bottleneck-small-files.md.B8kloyx8.js +5 -0
  153. package/public/docs/assets/tuning-reference_bottleneck-small-files.md.B8kloyx8.lean.js +1 -0
  154. package/public/docs/assets/tuning-reference_bottleneck-spill.md.PNH7mITt.js +6 -0
  155. package/public/docs/assets/tuning-reference_bottleneck-spill.md.PNH7mITt.lean.js +1 -0
  156. package/public/docs/assets/tuning-reference_bottleneck-straggler.md.DY36fHN5.js +7 -0
  157. package/public/docs/assets/tuning-reference_bottleneck-straggler.md.DY36fHN5.lean.js +1 -0
  158. package/public/docs/assets/tuning-reference_bottleneck-tiny-tasks.md.QTV7O8kU.js +8 -0
  159. package/public/docs/assets/tuning-reference_bottleneck-tiny-tasks.md.QTV7O8kU.lean.js +1 -0
  160. package/public/docs/assets/tuning-reference_bottleneck-utilization.md.DTiueZC3.js +5 -0
  161. package/public/docs/assets/tuning-reference_bottleneck-utilization.md.DTiueZC3.lean.js +1 -0
  162. package/public/docs/assets/tuning-reference_caching.md.B7aQ8asB.js +1 -0
  163. package/public/docs/assets/tuning-reference_caching.md.B7aQ8asB.lean.js +1 -0
  164. package/public/docs/assets/tuning-reference_cluster-config.md.ZVmDGsQ3.js +1 -0
  165. package/public/docs/assets/tuning-reference_cluster-config.md.ZVmDGsQ3.lean.js +1 -0
  166. package/public/docs/assets/tuning-reference_config.md.UvveiWG3.js +1 -0
  167. package/public/docs/assets/tuning-reference_config.md.UvveiWG3.lean.js +1 -0
  168. package/public/docs/assets/tuning-reference_data-formats.md.bjCAWH3N.js +1 -0
  169. package/public/docs/assets/tuning-reference_data-formats.md.bjCAWH3N.lean.js +1 -0
  170. package/public/docs/assets/tuning-reference_index.md.BQ_NooMV.js +1 -0
  171. package/public/docs/assets/tuning-reference_index.md.BQ_NooMV.lean.js +1 -0
  172. package/public/docs/assets/tuning-reference_intro.md.CobD-lGB.js +1 -0
  173. package/public/docs/assets/tuning-reference_intro.md.CobD-lGB.lean.js +1 -0
  174. package/public/docs/assets/tuning-reference_joins.md.BtKs_CuW.js +1 -0
  175. package/public/docs/assets/tuning-reference_joins.md.BtKs_CuW.lean.js +1 -0
  176. package/public/docs/assets/tuning-reference_memory-model.md.DhT-n4y3.js +1 -0
  177. package/public/docs/assets/tuning-reference_memory-model.md.DhT-n4y3.lean.js +1 -0
  178. package/public/docs/assets/tuning-reference_metrics.md.mLOh7Apj.js +1 -0
  179. package/public/docs/assets/tuning-reference_metrics.md.mLOh7Apj.lean.js +1 -0
  180. package/public/docs/assets/tuning-reference_partitioning.md.q0zKF_8X.js +1 -0
  181. package/public/docs/assets/tuning-reference_partitioning.md.q0zKF_8X.lean.js +1 -0
  182. package/public/docs/assets/tuning-reference_pyspark.md.DDCfvN9t.js +6 -0
  183. package/public/docs/assets/tuning-reference_pyspark.md.DDCfvN9t.lean.js +1 -0
  184. package/public/docs/assets/tuning-reference_shuffle.md.BZZ7R4Ix.js +1 -0
  185. package/public/docs/assets/tuning-reference_shuffle.md.BZZ7R4Ix.lean.js +1 -0
  186. package/public/docs/assets/tuning-reference_spark-architecture.md.Dwzm5avO.js +1 -0
  187. package/public/docs/assets/tuning-reference_spark-architecture.md.Dwzm5avO.lean.js +1 -0
  188. package/public/docs/assets/tuning-reference_table-formats.md.D6wj-2dX.js +1 -0
  189. package/public/docs/assets/tuning-reference_table-formats.md.D6wj-2dX.lean.js +1 -0
  190. package/public/docs/assets/udf-execution-models.BUFDICuG.svg +1 -0
  191. package/public/docs/assets/udf-execution-models.dark.YTNS6GDq.svg +1 -0
  192. package/public/docs/assets/user-guide_alternative-log-retrieval.md.sU3KGarf.js +1 -0
  193. package/public/docs/assets/user-guide_alternative-log-retrieval.md.sU3KGarf.lean.js +1 -0
  194. package/public/docs/assets/user-guide_getting-started.md.DtEM37MK.js +3 -0
  195. package/public/docs/assets/user-guide_getting-started.md.DtEM37MK.lean.js +1 -0
  196. package/public/docs/assets/{user-guide_mcp-tools.md.DwYI1d6C.js → user-guide_mcp-tools.md.C8MiIu7F.js} +35 -5
  197. package/public/docs/assets/{user-guide_mcp-tools.md.DwYI1d6C.lean.js → user-guide_mcp-tools.md.C8MiIu7F.lean.js} +1 -1
  198. package/public/docs/assets/{user-guide_run-comparison.md.5-oaOfTu.js → user-guide_run-comparison.md.S0TWWmLY.js} +1 -1
  199. package/public/docs/assets/{user-guide_run-comparison.md.5-oaOfTu.lean.js → user-guide_run-comparison.md.S0TWWmLY.lean.js} +1 -1
  200. package/public/docs/assets/user-guide_understanding-findings.md.D0R_Y-R2.js +1 -0
  201. package/public/docs/assets/{user-guide_understanding-findings.md.CK2WZ0Tf.lean.js → user-guide_understanding-findings.md.D0R_Y-R2.lean.js} +1 -1
  202. package/public/docs/contributor-guide/architecture/board-widgets.html +7 -7
  203. package/public/docs/contributor-guide/architecture/detector-contract.html +7 -7
  204. package/public/docs/contributor-guide/architecture/drill-down.html +7 -7
  205. package/public/docs/contributor-guide/architecture/impact-estimation.html +7 -7
  206. package/public/docs/contributor-guide/architecture/index.html +7 -7
  207. package/public/docs/contributor-guide/architecture/overview.html +7 -7
  208. package/public/docs/contributor-guide/architecture/state-and-history.html +7 -7
  209. package/public/docs/contributor-guide/architecture/widget-rendering.html +7 -7
  210. package/public/docs/contributor-guide/architecture/worker-protocol.html +8 -8
  211. package/public/docs/contributor-guide/contributing.html +7 -7
  212. package/public/docs/contributor-guide/development-setup.html +7 -7
  213. package/public/docs/contributor-guide/testing.html +7 -7
  214. package/public/docs/hashmap.json +1 -1
  215. package/public/docs/index.html +7 -7
  216. package/public/docs/tuning-reference/anti-patterns.html +29 -0
  217. package/public/docs/tuning-reference/aqe.html +29 -0
  218. package/public/docs/tuning-reference/bottleneck-broadcast-sizing.html +29 -0
  219. package/public/docs/tuning-reference/bottleneck-cold-start.html +35 -0
  220. package/public/docs/tuning-reference/bottleneck-duplicate-plan-subtree.html +30 -0
  221. package/public/docs/tuning-reference/bottleneck-failures.html +35 -0
  222. package/public/docs/tuning-reference/bottleneck-gc.html +34 -0
  223. package/public/docs/tuning-reference/bottleneck-job-failure-rate.html +37 -0
  224. package/public/docs/tuning-reference/bottleneck-memory-utilization.html +35 -0
  225. package/public/docs/tuning-reference/bottleneck-retry-waste.html +30 -0
  226. package/public/docs/tuning-reference/bottleneck-shuffle.html +40 -0
  227. package/public/docs/tuning-reference/bottleneck-skew.html +42 -0
  228. package/public/docs/tuning-reference/bottleneck-slow-host.html +35 -0
  229. package/public/docs/tuning-reference/bottleneck-small-files.html +33 -0
  230. package/public/docs/tuning-reference/bottleneck-spill.html +34 -0
  231. package/public/docs/tuning-reference/bottleneck-straggler.html +35 -0
  232. package/public/docs/tuning-reference/bottleneck-tiny-tasks.html +36 -0
  233. package/public/docs/tuning-reference/bottleneck-utilization.html +34 -0
  234. package/public/docs/tuning-reference/caching.html +29 -0
  235. package/public/docs/tuning-reference/cluster-config.html +29 -0
  236. package/public/docs/tuning-reference/config.html +29 -0
  237. package/public/docs/tuning-reference/data-formats.html +29 -0
  238. package/public/docs/tuning-reference/index.html +29 -0
  239. package/public/docs/tuning-reference/intro.html +29 -0
  240. package/public/docs/tuning-reference/joins.html +29 -0
  241. package/public/docs/tuning-reference/memory-model.html +29 -0
  242. package/public/docs/tuning-reference/metrics.html +29 -0
  243. package/public/docs/tuning-reference/partitioning.html +29 -0
  244. package/public/docs/tuning-reference/pyspark.html +34 -0
  245. package/public/docs/tuning-reference/shuffle.html +29 -0
  246. package/public/docs/tuning-reference/spark-architecture.html +29 -0
  247. package/public/docs/tuning-reference/table-formats.html +29 -0
  248. package/public/docs/user-guide/alternative-log-retrieval.html +29 -0
  249. package/public/docs/user-guide/getting-started.html +8 -8
  250. package/public/docs/user-guide/mcp-tools.html +41 -11
  251. package/public/docs/user-guide/run-comparison.html +7 -7
  252. package/public/docs/user-guide/understanding-findings.html +7 -7
  253. package/public/index.html +6 -6
  254. package/vendor-core/analyzer.js +74 -74
  255. package/vendor-core/cli/budgets.js +13 -27
  256. package/vendor-core/cli/collect-run.js +43 -19
  257. package/vendor-core/core-count.js +25 -27
  258. package/vendor-core/core-locality-ratio.js +4 -11
  259. package/vendor-core/core-time-series.js +6 -12
  260. package/vendor-core/core-usage-locality.js +3 -4
  261. package/vendor-core/detectors.js +256 -375
  262. package/vendor-core/docs-config.js +69 -21
  263. package/vendor-core/docs-content/chapters/01-intro.md +32 -0
  264. package/vendor-core/docs-content/chapters/02-spark-architecture.md +76 -0
  265. package/vendor-core/docs-content/chapters/03-memory-model.md +73 -0
  266. package/vendor-core/docs-content/chapters/04-partitioning.md +65 -0
  267. package/vendor-core/docs-content/chapters/05-joins.md +62 -0
  268. package/vendor-core/docs-content/chapters/06-shuffle.md +59 -0
  269. package/vendor-core/docs-content/chapters/07-data-formats.md +81 -0
  270. package/vendor-core/docs-content/chapters/07b-table-formats.md +56 -0
  271. package/vendor-core/docs-content/chapters/08-caching.md +58 -0
  272. package/vendor-core/docs-content/chapters/09-pyspark.md +78 -0
  273. package/vendor-core/docs-content/chapters/10-aqe.md +167 -0
  274. package/vendor-core/docs-content/chapters/11-cluster-config.md +170 -0
  275. package/vendor-core/docs-content/chapters/12-anti-patterns.md +171 -0
  276. package/vendor-core/docs-content/chapters/14-metrics.md +87 -0
  277. package/vendor-core/docs-content/chapters/15-config.md +93 -0
  278. package/vendor-core/docs-content/chapters/nav-index.json +370 -0
  279. package/vendor-core/docs-content/detection/cache.md +6 -0
  280. package/vendor-core/docs-content/detection/cfg.md +15 -0
  281. package/vendor-core/docs-content/detection/chrn.md +7 -0
  282. package/vendor-core/docs-content/detection/cold.md +4 -0
  283. package/vendor-core/docs-content/detection/cstor.md +4 -0
  284. package/vendor-core/docs-content/detection/fail.md +5 -0
  285. package/vendor-core/docs-content/detection/gc.md +4 -0
  286. package/vendor-core/docs-content/detection/host.md +5 -0
  287. package/vendor-core/docs-content/detection/incmp.md +6 -0
  288. package/vendor-core/docs-content/detection/jobs.md +4 -0
  289. package/vendor-core/docs-content/detection/local.md +7 -0
  290. package/vendor-core/docs-content/detection/mem.md +10 -0
  291. package/vendor-core/docs-content/detection/part.md +5 -0
  292. package/vendor-core/docs-content/detection/plan.md +14 -0
  293. package/vendor-core/docs-content/detection/retry.md +4 -0
  294. package/vendor-core/docs-content/detection/sfail.md +5 -0
  295. package/vendor-core/docs-content/detection/shape.md +5 -0
  296. package/vendor-core/docs-content/detection/shfl.md +4 -0
  297. package/vendor-core/docs-content/detection/skew.md +6 -0
  298. package/vendor-core/docs-content/detection/slow.md +6 -0
  299. package/vendor-core/docs-content/detection/spec.md +7 -0
  300. package/vendor-core/docs-content/detection/spill.md +7 -0
  301. package/vendor-core/docs-content/detection/strag.md +5 -0
  302. package/vendor-core/docs-content/detection/tiny.md +4 -0
  303. package/vendor-core/docs-content/detection/util.md +4 -0
  304. package/vendor-core/docs-content/diagrams/aqe-loop.dark.svg +1 -0
  305. package/vendor-core/docs-content/diagrams/aqe-loop.svg +1 -0
  306. package/vendor-core/docs-content/diagrams/broadcast-vs-shuffle.dark.svg +1 -0
  307. package/vendor-core/docs-content/diagrams/broadcast-vs-shuffle.svg +1 -0
  308. package/vendor-core/docs-content/diagrams/cache-lifecycle.dark.svg +1 -0
  309. package/vendor-core/docs-content/diagrams/cache-lifecycle.svg +1 -0
  310. package/vendor-core/docs-content/diagrams/cold-start-timeline.dark.svg +1 -0
  311. package/vendor-core/docs-content/diagrams/cold-start-timeline.svg +1 -0
  312. package/vendor-core/docs-content/diagrams/columnar-layout.dark.svg +1 -0
  313. package/vendor-core/docs-content/diagrams/columnar-layout.svg +1 -0
  314. package/vendor-core/docs-content/diagrams/container-memory.dark.svg +1 -0
  315. package/vendor-core/docs-content/diagrams/container-memory.svg +1 -0
  316. package/vendor-core/docs-content/diagrams/dag-stages.dark.svg +1 -0
  317. package/vendor-core/docs-content/diagrams/dag-stages.svg +1 -0
  318. package/vendor-core/docs-content/diagrams/driver-executor.dark.svg +1 -0
  319. package/vendor-core/docs-content/diagrams/driver-executor.svg +1 -0
  320. package/vendor-core/docs-content/diagrams/duplicate-plan-subtree.dark.svg +1 -0
  321. package/vendor-core/docs-content/diagrams/duplicate-plan-subtree.svg +1 -0
  322. package/vendor-core/docs-content/diagrams/join-strategy.dark.svg +1 -0
  323. package/vendor-core/docs-content/diagrams/join-strategy.svg +1 -0
  324. package/vendor-core/docs-content/diagrams/memory-borrowing.dark.svg +1 -0
  325. package/vendor-core/docs-content/diagrams/memory-borrowing.svg +1 -0
  326. package/vendor-core/docs-content/diagrams/memory-regions.dark.svg +1 -0
  327. package/vendor-core/docs-content/diagrams/memory-regions.svg +1 -0
  328. package/vendor-core/docs-content/diagrams/repartition-vs-coalesce.dark.svg +1 -0
  329. package/vendor-core/docs-content/diagrams/repartition-vs-coalesce.svg +1 -0
  330. package/vendor-core/docs-content/diagrams/retry-escalation-ladder.dark.svg +1 -0
  331. package/vendor-core/docs-content/diagrams/retry-escalation-ladder.svg +1 -0
  332. package/vendor-core/docs-content/diagrams/shuffle-map-reduce.dark.svg +1 -0
  333. package/vendor-core/docs-content/diagrams/shuffle-map-reduce.svg +1 -0
  334. package/vendor-core/docs-content/diagrams/spill-classification.dark.svg +1 -0
  335. package/vendor-core/docs-content/diagrams/spill-classification.svg +1 -0
  336. package/vendor-core/docs-content/diagrams/udf-execution-models.dark.svg +1 -0
  337. package/vendor-core/docs-content/diagrams/udf-execution-models.svg +1 -0
  338. package/vendor-core/docs-content/tuning/broadcast-sizing.md +78 -0
  339. package/vendor-core/docs-content/tuning/cold-start.md +81 -0
  340. package/vendor-core/docs-content/tuning/duplicate-plan-subtree.md +45 -0
  341. package/vendor-core/docs-content/tuning/failures.md +124 -0
  342. package/vendor-core/docs-content/tuning/gc.md +110 -0
  343. package/vendor-core/docs-content/tuning/job-failure-rate.md +101 -0
  344. package/vendor-core/docs-content/tuning/memory-utilization.md +58 -0
  345. package/vendor-core/docs-content/tuning/retry-waste.md +90 -0
  346. package/vendor-core/docs-content/tuning/shuffle.md +154 -0
  347. package/vendor-core/docs-content/tuning/skew.md +123 -0
  348. package/vendor-core/docs-content/tuning/slow-host.md +117 -0
  349. package/vendor-core/docs-content/tuning/small-files.md +99 -0
  350. package/vendor-core/docs-content/tuning/spill.md +114 -0
  351. package/vendor-core/docs-content/tuning/straggler.md +103 -0
  352. package/vendor-core/docs-content/tuning/tiny-tasks.md +94 -0
  353. package/vendor-core/docs-content/tuning/utilization.md +90 -0
  354. package/vendor-core/docs-site-config.js +10 -17
  355. package/vendor-core/efficiency-model.js +7 -13
  356. package/vendor-core/etl-phases.js +3 -5
  357. package/vendor-core/event-handlers.js +232 -134
  358. package/vendor-core/event-schemas.js +48 -114
  359. package/vendor-core/evidence-availability.js +5 -10
  360. package/vendor-core/evidence-report.js +72 -122
  361. package/vendor-core/export-data.js +48 -0
  362. package/vendor-core/finding-action-label.js +4 -10
  363. package/vendor-core/finding-filter-predicate.js +3 -7
  364. package/vendor-core/finding-generic-recommendation.js +112 -0
  365. package/vendor-core/finding-names.js +51 -0
  366. package/vendor-core/format-utils.js +112 -38
  367. package/vendor-core/impact-band.js +18 -24
  368. package/vendor-core/impact-estimator.js +38 -74
  369. package/vendor-core/ingest.js +7 -13
  370. package/vendor-core/job-groups.js +3 -6
  371. package/vendor-core/list-runs.js +278 -0
  372. package/vendor-core/load-vendored.js +6 -12
  373. package/vendor-core/log-header-peek.js +81 -0
  374. package/vendor-core/lz4-block.js +4 -6
  375. package/vendor-core/mcp-server-factory.js +38 -8
  376. package/vendor-core/mcp-tools.js +105 -76
  377. package/vendor-core/model-assembler.js +8 -16
  378. package/vendor-core/occupancy.js +5 -9
  379. package/vendor-core/parser-worker.js +18 -27
  380. package/vendor-core/plan-dot.js +2 -5
  381. package/vendor-core/plan-duration-attribution.js +78 -29
  382. package/vendor-core/plan-graph-model.js +126 -69
  383. package/vendor-core/plan-node-detail.js +31 -17
  384. package/vendor-core/plan-summary.js +19 -8
  385. package/vendor-core/recommendation-rollup.js +35 -39
  386. package/vendor-core/redact.js +72 -16
  387. package/vendor-core/rolling-log-reassembly.js +4 -6
  388. package/vendor-core/run-comparison.js +65 -70
  389. package/vendor-core/scaling-sim.js +5 -7
  390. package/vendor-core/session-snapshot.js +1 -1
  391. package/vendor-core/shs-fetch.js +4 -6
  392. package/vendor-core/shs-load.js +9 -13
  393. package/vendor-core/shs-request.js +1 -1
  394. package/vendor-core/stage-quantiles.js +14 -0
  395. package/vendor-core/types.js +78 -18
  396. package/vendor-core/wasted-core-hours.js +7 -12
  397. package/public/assets/AutoscalingChurn-CP5OjN6v.js +0 -1
  398. package/public/assets/CacheUtilization-BjyawXL6.js +0 -1
  399. package/public/assets/CachingOpportunity-DV2ABG2G.js +0 -1
  400. package/public/assets/ConfigAudit-DMiBhAoG.js +0 -1
  401. package/public/assets/CoreUsageArea-CGYIgPnt.js +0 -1
  402. package/public/assets/Dashboard-D1_IrWRs.js +0 -12
  403. package/public/assets/ExecutorTimeline-DkaAGqQT.js +0 -1
  404. package/public/assets/Failures-Crngoicu.js +0 -1
  405. package/public/assets/GcPressure-CFXm4BnA.js +0 -1
  406. package/public/assets/IncompleteRun-BWEJTb13.js +0 -1
  407. package/public/assets/JobFailures-BmjxCA-h.js +0 -1
  408. package/public/assets/MemoryUtilization-Bu2uQJbd.js +0 -1
  409. package/public/assets/PlanExplorer-DKUwF0uz.js +0 -1
  410. package/public/assets/PlanGraphRoute-BjPSugIG.js +0 -1
  411. package/public/assets/ShuffleIO--Rur3neu.js +0 -1
  412. package/public/assets/Spill-DIMpt8c2.js +0 -1
  413. package/public/assets/TaskSkew-BDr1DbeI.js +0 -1
  414. package/public/assets/index-4id52G-A.js +0 -9
  415. package/public/assets/index-D9kwVYIO.css +0 -1
  416. package/public/assets/parser-worker-00O9WEQC.js +0 -65
  417. package/public/assets/plan-graph-vendor-vZQHG2EJ.js +0 -7
  418. package/public/assets/react-vendor-u4pyvC5Y.js +0 -9
  419. package/public/assets/useRecentFiles-46iBU9zJ.js +0 -5
  420. package/public/docs/assets/chunks/@localSearchIndexroot.DclgKtlT.js +0 -1
  421. package/public/docs/assets/chunks/theme.CoT6y5bf.js +0 -2
  422. package/public/docs/assets/contributor-guide_architecture_board-widgets.md.BtjoE_1k.js +0 -1
  423. package/public/docs/assets/contributor-guide_architecture_board-widgets.md.BtjoE_1k.lean.js +0 -1
  424. package/public/docs/assets/contributor-guide_architecture_detector-contract.md.DQyCHQNM.js +0 -1
  425. package/public/docs/assets/contributor-guide_architecture_detector-contract.md.DQyCHQNM.lean.js +0 -1
  426. package/public/docs/assets/contributor-guide_architecture_drill-down.md.BKfyDQck.js +0 -1
  427. package/public/docs/assets/contributor-guide_architecture_drill-down.md.BKfyDQck.lean.js +0 -1
  428. package/public/docs/assets/contributor-guide_architecture_state-and-history.md.DaqttKgQ.js +0 -1
  429. package/public/docs/assets/contributor-guide_architecture_state-and-history.md.DaqttKgQ.lean.js +0 -1
  430. package/public/docs/assets/contributor-guide_architecture_widget-rendering.md.Bk6Iyj9S.js +0 -1
  431. package/public/docs/assets/contributor-guide_architecture_widget-rendering.md.Bk6Iyj9S.lean.js +0 -1
  432. package/public/docs/assets/contributor-guide_contributing.md.C-lKs70I.js +0 -1
  433. package/public/docs/assets/contributor-guide_contributing.md.C-lKs70I.lean.js +0 -1
  434. package/public/docs/assets/user-guide_getting-started.md.BJdD7a7_.js +0 -3
  435. package/public/docs/assets/user-guide_getting-started.md.BJdD7a7_.lean.js +0 -1
  436. package/public/docs/assets/user-guide_understanding-findings.md.CK2WZ0Tf.js +0 -1
  437. package/public/vendor/spark-doc/anchors.json +0 -205
  438. package/public/vendor/spark-doc/index.html +0 -4202
  439. package/public/vendor/spark-doc/landing.html +0 -418
  440. package/public/vendor/spark-doc/meta.html +0 -970
package/README.md ADDED
@@ -0,0 +1,6 @@
1
+ # sparkforensics-server
2
+
3
+ Local companion server for the SparkForensics dashboard.
4
+
5
+ See the [main README](https://github.com/shuffle-works/sparkforensics#readme)
6
+ for install instructions and usage.
package/index.js CHANGED
@@ -4,27 +4,18 @@ import { realpathSync, existsSync } from 'node:fs';
4
4
  import { dirname, join } from 'node:path';
5
5
  import { fileURLToPath, pathToFileURL } from 'node:url';
6
6
  import { resolveStaticRoot, serveStatic } from './lib/static-files.js';
7
- // Unlike loadCreateMcpServer below, which falls back between a vendored copy
8
- // and the repo-root packages/core/src/ sibling because *that file's location*
9
- // varies by install mode, there's no equivalent fallback for the sdk package itself:
10
- // mcp-server-factory imports it unconditionally too (vendored .js or repo
11
- // .ts, either way), and packages/server/package.json
12
- // (like the root package.json) declares '@modelcontextprotocol/sdk' as a direct
13
- // dependency, so plain node module resolution finds it in both the published
14
- // and monorepo-dev layouts.
7
+ // No install-mode fallback needed for the sdk package (unlike core below):
8
+ // packages/server/package.json declares '@modelcontextprotocol/sdk' as a direct
9
+ // dependency, so node resolution finds it in both published and monorepo layouts.
15
10
  import { StreamableHTTPServerTransport } from '@modelcontextprotocol/sdk/server/streamableHttp.js';
16
11
 
17
12
  const DEFAULT_PORT = 4173;
18
13
  const DEFAULT_HOST = '127.0.0.1';
19
14
 
20
- // Mirrors resolveStaticRoot's public/-vs-../dist fallback: packages/server/vendor-core/
21
- // exists only in a published/standalone install (populated by
22
- // scripts/vendor-core.mjs at publish time, which pre-strips the vendored core's
23
- // TypeScript to plain .js since Node refuses to type-strip .ts under
24
- // node_modules); in the monorepo it falls back to the real repo-root
25
- // packages/core/src/ sibling directory. load-vendored.js itself is the one
26
- // module this bootstrap has to locate by hand (see its own header comment);
27
- // every other core module then loads through its exported loadVendored().
15
+ // vendor-core/ exists only in a published install (populated by vendor-core.mjs
16
+ // at publish time); the monorepo falls back to the packages/core/src/ sibling.
17
+ // load-vendored.js is the one module located by hand; the rest load through its
18
+ // exported loadVendored().
28
19
  async function loadVendoredCore(dir, moduleName, opts) {
29
20
  const vendoredHelper = join(dir, 'vendor-core', 'load-vendored.js');
30
21
  const helperPath = existsSync(vendoredHelper) ? vendoredHelper : join(dir, '..', 'core', 'src', 'load-vendored.js');
@@ -45,12 +36,9 @@ async function loadHandleShsProxy(dir) {
45
36
  export function createServer({
46
37
  staticRoot,
47
38
  fetchImpl = fetch,
48
- // Test-only override for loadCreateMcpServer: lets test/index.test.js
49
- // inject a promise that deliberately rejects, to prove a real failure
50
- // still surfaces as a /mcp error response rather than being silently
51
- // swallowed by the no-op .catch() below, without racing the real
52
- // packages/server/vendor-core/ directory that test/bin.test.js
53
- // separately populates and tears down.
39
+ // Test-only override: lets test/index.test.js inject a rejecting promise to
40
+ // prove a real failure still surfaces as a /mcp error, not swallowed by the
41
+ // no-op .catch() below.
54
42
  loadCreateMcpServer: loadCreateMcpServerImpl = loadCreateMcpServer,
55
43
  } = {}) {
56
44
  const dir = dirname(fileURLToPath(import.meta.url));
@@ -58,18 +46,10 @@ export function createServer({
58
46
  const createMcpServerPromise = loadCreateMcpServerImpl(dir);
59
47
  const handleShsProxyPromise = loadHandleShsProxy(dir);
60
48
  handleShsProxyPromise.catch(() => {});
61
- // This promise is only ever awaited later, inside the /mcp branch below,
62
- // so a request that never hits /mcp (most routing tests, and most real
63
- // traffic to '/' or '/shs-proxy') leaves it completely unconsumed. If it
64
- // rejects in that case (e.g. packages/server/vendor-core/mcp-server-factory.js
65
- // transiently exists-but-is-mid-rewrite while test/bin.test.js's own real
66
- // `npm pack` is running concurrently in another test file/worker), Node
67
- // reports an unhandled rejection even though nothing actually needed the
68
- // result yet. A promise's rejection is independently observable by every
69
- // consumer that awaits/.catches it, so attaching this no-op handler here
70
- // does not hide a real failure from the /mcp handler's own `await
71
- // createMcpServerPromise` below; it only marks this one rejection as
72
- // "handled" for Node's unhandledRejection bookkeeping.
49
+ // Only awaited later in the /mcp branch, so a request that never hits /mcp
50
+ // leaves it unconsumed; a rejection there would be an unhandled rejection.
51
+ // This no-op handler marks it handled without hiding the failure from the
52
+ // /mcp handler's own await (rejections are observable per-consumer).
73
53
  createMcpServerPromise.catch(() => {});
74
54
 
75
55
  return http.createServer(async (req, res) => {
@@ -79,16 +59,14 @@ export function createServer({
79
59
  const handleShsProxy = await handleShsProxyPromise;
80
60
  await handleShsProxy(req, res, { fetchImpl });
81
61
  } else if (pathname === '/mcp') {
82
- // Stateless mode (sessionIdGenerator: undefined) allows a transport to
83
- // handle exactly one HTTP request; the SDK throws if it's reused, so a
84
- // fresh server+transport pair is created per request (matches the SDK's
85
- // own simpleStatelessStreamableHttp.ts example).
62
+ // Stateless mode (sessionIdGenerator: undefined): a transport handles
63
+ // one request and the SDK throws on reuse, so make a fresh
64
+ // server+transport pair per request.
86
65
  const createMcpServer = await createMcpServerPromise;
87
66
  const mcpServer = createMcpServer();
88
- // allowedHosts is matched against the raw Host header (host:port, no
89
- // stripping) by the SDK, so the port must be derived from the actual
90
- // listening socket rather than hardcoded: tests bind to an ephemeral
91
- // port via listen(0, ...).
67
+ // allowedHosts is matched against the raw Host header (host:port), so
68
+ // derive the port from the actual socket, not a hardcode: tests bind to
69
+ // an ephemeral port via listen(0, ...).
92
70
  const localPort = req.socket.localPort;
93
71
  const mcpTransport = new StreamableHTTPServerTransport({
94
72
  sessionIdGenerator: undefined,
@@ -135,13 +113,26 @@ function parsePort(argv) {
135
113
  return DEFAULT_PORT;
136
114
  }
137
115
 
138
- // Run directly (node index.js / npx sparkforensics-server). argv[1] is
139
- // the bin symlink path when launched via the published `.bin` entry, so it
140
- // must be realpath-resolved before comparing against this module's real path.
116
+ const USAGE = `Usage: sparkforensics-server [--port <n>]
117
+
118
+ Serves the SparkForensics dashboard locally and proxies Spark History
119
+ Server fetches on your behalf (server-to-server, so no browser CORS
120
+ restriction). Binds to 127.0.0.1 only. Default port ${DEFAULT_PORT},
121
+ overridden by --port or the PORT env var.
122
+
123
+ See https://github.com/shuffle-works/sparkforensics#readme for details.
124
+ `;
125
+
126
+ // argv[1] is the bin symlink path via the published `.bin` entry, so
127
+ // realpath-resolve it before comparing against this module's real path.
141
128
  const invokedDirectly = process.argv[1] && realpathSync(process.argv[1]) === fileURLToPath(import.meta.url);
142
129
  if (invokedDirectly) {
143
- const port = parsePort(process.argv);
144
- startServer({ port }).then(() => {
145
- for (const line of serverStartupGuidance(port)) console.log(line);
146
- });
130
+ if (process.argv.includes('--help') || process.argv.includes('-h')) {
131
+ process.stderr.write(USAGE);
132
+ } else {
133
+ const port = parsePort(process.argv);
134
+ startServer({ port }).then(() => {
135
+ for (const line of serverStartupGuidance(port)) console.log(line);
136
+ });
137
+ }
147
138
  }
@@ -15,16 +15,20 @@ const CONTENT_TYPES = {
15
15
 
16
16
  export function resolveStaticRoot(dir) {
17
17
  const bundled = join(dir, 'public');
18
- return existsSync(bundled) ? bundled : join(dir, '..', 'dist');
18
+ // Monorepo dev fallback (no public/ yet): dir is packages/server, and the
19
+ // Vite build output lives at the repo root, two levels up.
20
+ return existsSync(bundled) ? bundled : join(dir, '..', '..', 'dist');
19
21
  }
20
22
 
21
23
  export async function serveStatic(req, res, root) {
22
- // Note: deliberately not routed through `new URL()`: its WHATWG dot-segment
23
- // resolution collapses `/../../etc/passwd` to `/etc/passwd` before we ever see
24
- // it, which would let a traversal attempt sail past the guard below as a
25
- // harmless 404 instead of being caught and rejected with 403.
24
+ // Deliberately not via `new URL()`: its dot-segment resolution collapses
25
+ // `/../../etc/passwd` before we see it, sailing a traversal past the guard
26
+ // below as a 404 instead of a 403.
26
27
  const pathname = decodeURIComponent(req.url.split('?')[0]);
27
- const relative = pathname === '/' ? 'index.html' : pathname.replace(/^\/+/, '');
28
+ const stripped = pathname.replace(/^\/+/, '');
29
+ // Any directory-style request (trailing slash, including '/' itself) seeks
30
+ // that directory's index.html, same as a browser navigating to it directly.
31
+ const relative = pathname.endsWith('/') ? `${stripped}index.html` : stripped;
28
32
  const target = normalize(join(root, relative));
29
33
 
30
34
  // Path-traversal guard: the resolved path must stay within root.
package/package.json CHANGED
@@ -1,7 +1,26 @@
1
1
  {
2
2
  "name": "sparkforensics-server",
3
- "version": "0.1.0",
3
+ "version": "0.2.0",
4
4
  "type": "module",
5
+ "license": "MIT",
6
+ "description": "Local companion server for the SparkForensics dashboard: serves the app and proxies Spark History Server fetches to avoid browser CORS restrictions.",
7
+ "repository": {
8
+ "type": "git",
9
+ "url": "git+https://github.com/shuffle-works/sparkforensics.git"
10
+ },
11
+ "bugs": {
12
+ "url": "https://github.com/shuffle-works/sparkforensics/issues"
13
+ },
14
+ "homepage": "https://github.com/shuffle-works/sparkforensics#readme",
15
+ "keywords": [
16
+ "apache-spark",
17
+ "spark",
18
+ "spark-history-server",
19
+ "server",
20
+ "proxy",
21
+ "cors",
22
+ "dashboard"
23
+ ],
5
24
  "bin": {
6
25
  "sparkforensics-server": "index.js"
7
26
  },
@@ -0,0 +1 @@
1
+ import{r as C,j as e}from"./react-vendor-DA6opS-q.js";import{W as k,T as A,I as T,D as B}from"./index-CHjV2-jB.js";import{I as E}from"./ImpactEstimate-C6GzFOY9.js";import{W as N}from"./WidgetLeadSummary-DRNRVvn7.js";import{j as w,C as f}from"./useRecentFiles-BPGQ6XII.js";import{d as I}from"./downsample-D8XmJk6i.js";import{k as R,C as K,X as L,Y as $,T as _,f as z,a as b,l as D}from"./charts-vendor-Cf1-wzeH.js";import"./plan-graph-vendor-B96a-e4N.js";const F=60,G=6e4,H=220;function O(t,s,a,c,r){if(t.length===0&&s.length===0)return[];const x=Math.max(r,c-a),l=Math.max(1,Math.ceil(x/r)),i=[];for(let n=0;n<l;n++)i.push({tStart:a+n*r,adds:0,removes:0});const u=n=>Math.min(l-1,Math.max(0,Math.floor((n-a)/r)));for(const n of t)i[u(n.timestamp)].adds++;for(const n of s)i[u(n.timestamp)].removes++;return i}function Y(t,s){const a=Math.max(1,s-t);return Math.max(G,Math.ceil(a/F))}function U(t){if(t<=0)return 1;const a=10**Math.floor(Math.log10(t)),c=t/a;return(c<=1?1:c<=2?2:c<=5?5:10)*a}function W(t){const s=t.reduce((c,r)=>Math.max(c,Math.abs(r.added),Math.abs(r.removed)),0),a=U(s);return{domain:[-a,a],ticks:[-a,-a/2,0,a/2,a]}}function M(t){return Math.abs(t.height)<1?null:e.jsx(D,{...t})}function X({finding:t}){return e.jsxs("div",{className:"flex flex-col gap-1 transition-colors",children:[e.jsxs("p",{className:"text-xs text-muted-foreground",children:["Short-lived executors: ",e.jsxs("strong",{children:[t.value,"%"]})]}),e.jsx(E,{finding:t}),e.jsxs("p",{className:"flex flex-wrap items-start gap-2 text-sm",children:[e.jsx(T,{impactBand:t.impactBand,className:"mt-1.5"}),e.jsxs("span",{children:[t.recommendation," ",e.jsx(B,{anchor:"#config-autoscale-bounds",children:"Tuning executorIdleTimeout and the min/max bounds"}),"."]})]})]})}const se=C.memo(function({appModel:s,activeFileId:a,catalog:c=[],defaultCollapsed:r=!0}){const x=C.useMemo(()=>{const o=s.executors.added,m=s.executors.removed;if(o.length===0&&m.length===0)return{hasEvents:!1,added:o,removed:m};const g=s.app,h=g?.startTime??0,v=g?.endTime??h,S=Y(h,v),j=I(O(o,m,h,v,S)).map(p=>({t:`${Math.round((p.tStart-h)/1e3)}s`,added:p.adds,removed:-p.removes})),y=W(j);return{hasEvents:!0,added:o,removed:m,rows:j,yAxisScale:y}},[s,a]);if(!x.hasEvents)return e.jsx(k,{title:"Autoscaling Churn",summary:e.jsx("span",{className:"text-muted-foreground text-xs",children:"No executor add/remove events (static allocation)."}),children:e.jsx("p",{className:"text-muted-foreground text-xs",children:"No executor add/remove events (static allocation)."})});const{added:l,removed:i,rows:u,yAxisScale:n}=x,d=c.find(o=>o.type==="autoscalingChurn");return e.jsx(k,{title:"Autoscaling Churn",impactBand:d?.impactBand,badges:d&&e.jsx(A,{type:"autoscalingChurn",impactBand:d.impactBand}),defaultCollapsed:r,summary:e.jsx(N,{value:`${l.length} added / ${i.length} removed`,context:d?`${d.value}% short-lived executors, ${d.impactBand}`:"executor churn events"}),children:e.jsxs("div",{className:"space-y-2",children:[d&&e.jsx(X,{finding:d}),e.jsx("p",{className:"text-muted-foreground text-xs",children:"executors added / removed over time"}),e.jsx(w,{ariaLabel:"Executor added and removed counts per time bucket over the run.",height:H,table:{caption:"Executor added/removed counts by time bucket",columns:["Time","Executors added","Executors removed"],rows:u.map(o=>[o.t,o.added,Math.abs(o.removed)]),align:["right","right","right"]},children:e.jsxs(R,{data:u,children:[e.jsx(K,{stroke:f.muted,strokeOpacity:.2}),e.jsx(L,{dataKey:"t",tick:{fontSize:10}}),e.jsx($,{tick:{fontSize:10},width:32,domain:n.domain,ticks:n.ticks}),e.jsx(_,{formatter:(o,m)=>[Math.abs(Number(o)),m]}),e.jsx(z,{wrapperStyle:{fontSize:10}}),e.jsx(b,{dataKey:"added",name:"Added",stackId:"churn",fill:f.clean,shape:M}),e.jsx(b,{dataKey:"removed",name:"Removed",stackId:"churn",fill:f.muted,shape:M})]})})]})})});export{se as AutoscalingChurn,O as bucketChurn};
@@ -0,0 +1 @@
1
+ import{r as o,j as s}from"./react-vendor-DA6opS-q.js";import{L as v,at as C,W as M,P as $,Z as L,H as P,_ as c,Q as A,J as d,I as R,n as j,j as l,T as F,D as E,S as _,A as H,ar as U,as as W}from"./index-CHjV2-jB.js";import{u as O}from"./finding-anchor-4TmpXd7j.js";import{I as V}from"./ImpactEstimate-C6GzFOY9.js";import{R as S}from"./RowPagination-DypWbnic.js";import{u as D}from"./usePagedRows-CFoZrPr4.js";import{W as G}from"./WidgetLeadSummary-DRNRVvn7.js";import"./charts-vendor-Cf1-wzeH.js";import"./plan-graph-vendor-B96a-e4N.js";const z=70;function J(a){if(!a)return"—";const t=[];return a.useMemory&&t.push("Memory"),a.useDisk&&t.push("Disk"),a.deserialized?t.push("Deserialized"):(a.useMemory||a.useDisk)&&t.push("Serialized"),(a.replication??1)>1&&t.push(`x${a.replication}`),t.join(" · ")||"—"}function k(a){return a.name||`RDD ${a.id}`}function Q(a){const t=_(k(a));return t.length<=z?t:`${t.slice(0,z)}…`}function X(a){const t=U("pct",W(a));return a.variant==="diskSpillover"?`${t} spilled to disk: ${l(a.diskSize??0)} disk / ${l(a.memorySize??0)} memory`:`${t} cached (${a.numCachedPartitions??0}/${a.numPartitions??0} partitions)`}function Z({finding:a}){const t=O([a]),m=a.rddName??`RDD ${a.rddId}`;return s.jsxs("div",{ref:t.ref,tabIndex:t.tabIndex,"data-flashed":t.dataFlashed,className:`space-y-1 transition-colors ${t.flashClassName}`,children:[s.jsxs("p",{className:"flex flex-wrap items-start gap-2",children:[s.jsx(R,{impactBand:a.impactBand,className:"mt-1.5"}),s.jsx("span",{children:m})]}),s.jsx(H,{children:s.jsx("p",{className:"text-xs text-muted-foreground",children:X(a)})}),s.jsx(V,{finding:a}),s.jsx("p",{className:"text-sm",children:a.recommendation})]})}const re=o.memo(function({appModel:t,catalog:m}){const[b,h]=o.useState(0),[w,u]=o.useState(0),x=t.app?.rddInfo,r=o.useMemo(()=>x?[...x.values()].filter(e=>(e.storageLevel?.useMemory||e.storageLevel?.useDisk)&&(e.numCachedPartitions??0)>0).sort((e,i)=>(i.memorySize??0)+(i.diskSize??0)-((e.memorySize??0)+(e.diskSize??0))):[],[x]),n=o.useMemo(()=>m.filter(e=>e.type==="cacheUtilization").sort((e,i)=>v[e.impactBand]-v[i.impactBand]),[m]),p=D(r,b,h),f=C(),B=f?n.findIndex(e=>e===f.finding):null,g=D(n,w,u,B);if(r.length===0)return null;const I=e=>n.filter(i=>i.rddId===e),T=r.reduce((e,i)=>e+(i.memorySize??0)+(i.diskSize??0),0);return s.jsx("div",{"data-widget":"cache-utilization",children:s.jsxs(M,{title:"Cache Storage",impactBand:j(n),badges:s.jsxs(s.Fragment,{children:[n.length>0?s.jsx(F,{type:"cacheUtilization",impactBand:j(n)??"info"}):null,s.jsxs("span",{className:"text-xs text-muted-foreground",children:[r.length," cached RDD",r.length===1?"":"s",", see"," ",s.jsx(E,{anchor:"#memory-model",children:"how Spark accounts cached memory"})]})]}),defaultCollapsed:!0,summary:s.jsx(G,{value:l(T),context:"cached across memory + disk"}),children:[s.jsxs($,{children:[s.jsx(L,{children:s.jsxs(P,{children:[s.jsx(c,{children:"RDD"}),s.jsx(c,{children:"Storage level"}),s.jsx(c,{className:"text-right",children:"Cached partitions"}),s.jsx(c,{className:"text-right",children:"Memory"}),s.jsx(c,{className:"text-right",children:"Disk"})]})}),s.jsx(A,{children:p.visible.map(e=>{const i=k(e),N=Q(e),y=I(e.id);return s.jsxs(P,{children:[s.jsxs(d,{className:"max-w-80 truncate",title:i!==N?i:void 0,children:[N,y.length>0&&s.jsx(R,{impactBand:j(y)??"info",className:"ml-1.5"})]}),s.jsx(d,{children:J(e.storageLevel)}),s.jsxs(d,{className:"text-right",children:[e.numCachedPartitions," / ",e.numPartitions]}),s.jsx(d,{className:"text-right",children:l(e.memorySize??0)}),s.jsx(d,{className:"text-right",children:l(e.diskSize??0)})]},e.id)})})]}),s.jsx(S,{page:p.effectivePage,totalPages:p.totalPages,onPrev:()=>h(e=>e-1),onNext:()=>h(e=>e+1)}),n.length>0?s.jsxs("div",{className:"mt-3 space-y-2 text-sm",children:[g.visible.map(e=>s.jsx(Z,{finding:e},e.id)),s.jsx(S,{page:g.effectivePage,totalPages:g.totalPages,onPrev:()=>u(e=>e-1),onNext:()=>u(e=>e+1)})]}):null]})})});export{re as CacheUtilization};
@@ -0,0 +1 @@
1
+ import{r as x,j as e}from"./react-vendor-DA6opS-q.js";import{c as R,at as v,W as b,P as N,Z as w,H as h,_ as l,Q as C,A as T,T as B,J as d,Y as u,j as I}from"./index-CHjV2-jB.js";import{u as $}from"./finding-anchor-4TmpXd7j.js";import{I as P}from"./ImpactEstimate-C6GzFOY9.js";import{R as k}from"./RowPagination-DypWbnic.js";import{u as O}from"./usePagedRows-CFoZrPr4.js";import{W}from"./WidgetLeadSummary-DRNRVvn7.js";import{R as A}from"./useRecentFiles-BPGQ6XII.js";import"./charts-vendor-Cf1-wzeH.js";import"./plan-graph-vendor-B96a-e4N.js";const E=[["circle",{cx:"18",cy:"18",r:"3",key:"1xkwt0"}],["circle",{cx:"6",cy:"6",r:"3",key:"1lh9wr"}],["path",{d:"M6 21V9a9 9 0 0 0 9 9",key:"7kw0sc"}]],H=R("git-merge",E);function M({relations:a,operator:t}){const r=t==="union"?"union":"join";return e.jsx("span",{className:"inline-flex flex-wrap items-baseline",children:a.map((i,o)=>e.jsxs("span",{children:[o>0?e.jsxs(e.Fragment,{children:[" ",e.jsx("span",{className:"mx-1 font-semibold uppercase text-xs tracking-wide text-primary",children:r})," "]}):null,i.relation]},i.relation))})}function S({finding:a}){const t=$([a]),r=a.variant==="composite";return e.jsxs(h,{ref:t.ref,tabIndex:t.tabIndex,"data-flashed":t.dataFlashed,className:`transition-colors ${t.flashClassName}`,children:[e.jsx(d,{children:e.jsxs("span",{className:"inline-flex items-center gap-2",children:[r?e.jsx(H,{className:"size-3.5 text-muted-foreground","aria-hidden":"true"}):null,r&&a.relations&&a.relations.length>0?e.jsx(M,{relations:a.relations,operator:a.operator}):e.jsx("span",{children:a.relation}),r?e.jsx(u,{variant:"outline",className:"text-xs uppercase border-dashed",children:a.operator?.toUpperCase()}):e.jsx(u,{variant:"secondary",className:"text-xs uppercase",children:a.format})]})}),e.jsxs(d,{children:[a.value," queries"]}),e.jsx(d,{className:"text-right",children:I(a.totalReadBytes??0)}),e.jsxs(d,{children:[e.jsx(P,{finding:a}),e.jsx("p",{className:"mt-1",children:a.recommendation})]})]})}const U=x.memo(function({catalog:t,defaultCollapsed:r=!0}){const[i,o]=x.useState(0),c=x.useMemo(()=>t.filter(s=>s.type==="cachingOpportunity").sort((s,n)=>(n.totalReadBytes??0)-(s.totalReadBytes??0)||(typeof n.value=="number"?n.value:0)-(typeof s.value=="number"?s.value:0)),[t]),p=v(),j=p?c.findIndex(s=>s===p.finding):null,{totalPages:y,effectivePage:f,visible:g}=O(c,i,o,j);if(c.length===0)return null;const m=c[0];return e.jsx(b,{title:"Caching Opportunities",impactBand:"info",badges:e.jsx(B,{type:"cachingOpportunity",impactBand:"info"}),defaultCollapsed:r,summary:e.jsx(W,{value:`${m.value}×`,context:`read by ${m.value} queries: ${m.relation}`}),children:e.jsxs("div",{className:"space-y-3",children:[e.jsxs(N,{children:[e.jsx(w,{children:e.jsxs(h,{children:[e.jsx(l,{children:"Relation"}),e.jsx(l,{children:"Read by"}),e.jsx(l,{className:"text-right",children:"Data read"}),e.jsx(l,{children:"Recommendation"})]})}),e.jsx(C,{children:g.map((s,n)=>e.jsx(S,{finding:s},`${s.format}:${s.relation}:${(s.executionIds??[]).join(",")}:${n}`))})]}),e.jsxs(T,{children:[e.jsx(A,{confidence:"low"}),e.jsx("p",{className:"text-xs text-muted-foreground",children:"Reuse is inferred from plan structure, not confirmed by execution. Verify before caching."})]}),e.jsx(k,{page:f,totalPages:y,onPrev:()=>o(s=>s-1),onNext:()=>o(s=>s+1)})]})})});export{U as CachingOpportunity};
@@ -0,0 +1 @@
1
+ import{r as n,j as e}from"./react-vendor-DA6opS-q.js";import{L as d,at as C,W as v,T as y,I as N}from"./index-CHjV2-jB.js";import{W as P}from"./WidgetLeadSummary-DRNRVvn7.js";import{u as T}from"./finding-anchor-4TmpXd7j.js";import{c as b}from"./impact-sort-Cq6KOLqh.js";import{I as w}from"./ImpactEstimate-C6GzFOY9.js";import{u as M,S as A}from"./useSortMode-BOPpfLhJ.js";import{R as E}from"./RowPagination-DypWbnic.js";import{u as O}from"./usePagedRows-CFoZrPr4.js";import"./charts-vendor-Cf1-wzeH.js";import"./plan-graph-vendor-B96a-e4N.js";import"./toggle-group-lbIXRG0W.js";import"./useRecentFiles-BPGQ6XII.js";function l(s){return`${s.value}s`}function W({finding:s}){const o=T([s]);return e.jsxs("li",{ref:o.ref,tabIndex:o.tabIndex,"data-flashed":o.dataFlashed,className:`space-y-1 text-sm transition-colors ${o.flashClassName}`,children:[e.jsxs("div",{className:"flex flex-wrap items-center gap-2",children:[e.jsx(N,{impactBand:s.impactBand}),e.jsx("strong",{children:l(s)})]}),e.jsx(w,{finding:s}),e.jsx("p",{className:"text-muted-foreground pt-1",children:s.recommendation})]})}const U=n.memo(function({catalog:o,defaultCollapsed:p=!0}){const{sortMode:u,setSortMode:g,orderBy:x}=M(),[m,f]=n.useState(!p),[h,r]=n.useState(0),a=o.filter(t=>t.type==="coldStart").slice().sort((t,R)=>d[t.impactBand]-d[R.impactBand]),c=x(a,t=>[t],t=>t.stageId??null),i=C(),j=i?c.findIndex(t=>t===i.finding):null,{totalPages:S,effectivePage:I,visible:B}=O(c,h,r,j);return a.length===0?null:e.jsxs(v,{title:"Cold Start",impactBand:a[0].impactBand,badges:e.jsxs(e.Fragment,{children:[e.jsx(y,{type:"coldStart",impactBand:a[0].impactBand}),b(a,a.length,m)?e.jsx(A,{mode:u,onChange:g}):null]}),open:m,onOpenChange:f,summary:e.jsx(P,{value:l(a[0]),context:`${a.length} issue${a.length===1?"":"s"} flagged`}),children:[e.jsx("ul",{className:"space-y-2",children:B.map(t=>e.jsx(W,{finding:t},t.id))}),e.jsx(E,{page:I,totalPages:S,onPrev:()=>r(t=>t-1),onNext:()=>r(t=>t+1)})]})});export{U as ColdStart};
@@ -0,0 +1 @@
1
+ import{r as m,j as e}from"./react-vendor-DA6opS-q.js";import{ag as A,L as g,at as I,A as r,W as f,n as R,T as k,I as B,D as b}from"./index-CHjV2-jB.js";import{R as c}from"./useRecentFiles-BPGQ6XII.js";import{u as w}from"./finding-anchor-4TmpXd7j.js";import{I as P}from"./ImpactEstimate-C6GzFOY9.js";import{R as S}from"./RowPagination-DypWbnic.js";import{u as D}from"./usePagedRows-CFoZrPr4.js";import{W as T}from"./WidgetLeadSummary-DRNRVvn7.js";import"./charts-vendor-Cf1-wzeH.js";import"./plan-graph-vendor-B96a-e4N.js";function W({finding:n}){const t=w([n]);return e.jsxs("li",{ref:t.ref,tabIndex:t.tabIndex,"data-flashed":t.dataFlashed,className:`space-y-1 transition-colors ${t.flashClassName}`,children:[e.jsxs("div",{className:"flex items-center gap-2",children:[e.jsx(B,{impactBand:n.impactBand}),e.jsx("code",{className:"text-xs",children:n.property}),e.jsx("span",{className:"text-xs text-muted-foreground",children:"="}),e.jsx("code",{className:"rounded bg-accent/10 px-1.5 py-0.5 font-mono text-xs text-accent",children:String(n.value)})]}),e.jsx(P,{finding:n}),e.jsxs("p",{className:"text-sm",children:[n.recommendation,n.docAnchor?e.jsxs(e.Fragment,{children:[" ",e.jsxs(b,{anchor:n.docAnchor,children:["Why ",n.property," matters"]})]}):null]})]})}const H=m.memo(function({appModel:t,configFindings:l,defaultCollapsed:h=!0}){const[j,d]=m.useState(0),o=m.useMemo(()=>(l??A(t.app)).filter(s=>s.type==="configAudit").slice().sort((s,a)=>g[s.impactBand]-g[a.impactBand]),[l,t]),u=I(),y=u?o.findIndex(s=>s===u.finding):null,{totalPages:v,effectivePage:C,visible:N}=D(o,j,d,y);if(o.length===0){const s=t.evidenceAvailability?.entries.find(i=>i.key==="sparkConfiguration");let a;if(s)s.state==="present"?a="No misconfigurations detected.":s.state==="unknown"&&s.reasonCode==="parseIncomplete"?a=e.jsxs(e.Fragment,{children:[e.jsxs("span",{children:[s.summary," "]}),e.jsx(r,{children:e.jsx(c,{evidenceKey:"sparkConfiguration"})})]}):a=e.jsxs(e.Fragment,{children:[e.jsx("span",{children:"No environment info captured in this log. "}),e.jsx(r,{children:e.jsx(c,{evidenceKey:"sparkConfiguration"})})]});else{const i=t.app?.evidenceInputs;a=(i?i.environmentUpdates>0:Object.keys(t.app?.config??{}).length>0||!!t.app?.resources)?"No misconfigurations detected.":e.jsxs(e.Fragment,{children:[e.jsx("span",{children:"No environment info captured in this log. "}),e.jsx(r,{children:e.jsx(c,{evidenceKey:"sparkConfiguration"})})]})}const p=e.jsx("p",{className:"text-muted-foreground text-xs",children:a});return e.jsx(f,{title:"Config Sanity",compact:!0,summary:p,children:p})}const x=R(o);return e.jsxs(f,{title:"Config Sanity",impactBand:x,badges:e.jsx(k,{type:"configAudit",impactBand:x??"info"}),statusBadge:e.jsx(r,{children:e.jsx(c,{evidenceKey:"sparkConfiguration"})}),defaultCollapsed:h,summary:e.jsx(T,{value:`${o.length}`,context:`misconfiguration${o.length===1?"":"s"} flagged`}),children:[e.jsx("ul",{className:"space-y-4",children:N.map(s=>e.jsx(W,{finding:s},s.id))}),e.jsx(S,{page:C,totalPages:v,onPrev:()=>d(s=>s-1),onNext:()=>d(s=>s+1)})]})});export{H as ConfigAudit};
@@ -0,0 +1 @@
1
+ import{r as O,j as e}from"./react-vendor-DA6opS-q.js";import{az as G,W as B,A as T,D as q,I as X,T as W}from"./index-CHjV2-jB.js";import{R as $,j as J,C as l}from"./useRecentFiles-BPGQ6XII.js";import{d as Q}from"./downsample-D8XmJk6i.js";import{E as V}from"./EmptyState-v2ehFncc.js";import{I as Z}from"./ImpactEstimate-C6GzFOY9.js";import{R as tt}from"./RowPagination-DypWbnic.js";import{u as et}from"./usePagedRows-CFoZrPr4.js";import{W as at}from"./WidgetLeadSummary-DRNRVvn7.js";import{I as ot}from"./inbox-DBFovbUX.js";import{A as st,C as nt,X as it,Y as rt,T as ct,f as lt,b as mt}from"./charts-vendor-Cf1-wzeH.js";import"./plan-graph-vendor-B96a-e4N.js";const D=["PROCESS_LOCAL","NODE_LOCAL","RACK_LOCAL","NO_PREF","ANY"];function dt(M,{bucketWidthMs:n=6e4,tiers:y=D}={}){const m=M.filter(a=>(a.completedAt??0)>(a.submittedAt??0)&&(a.executorRunTime??0)>0);if(m.length===0)return{labels:[],series:{}};const x=m.reduce((a,s)=>Math.min(a,s.submittedAt??a),1/0),v=m.reduce((a,s)=>Math.max(a,s.completedAt??a),-1/0),c=Math.max(1,Math.ceil((v-x)/n)),i={};for(const a of y)i[a]=new Array(c).fill(0);const g=new Array(c).fill(0);for(const a of m){const s=a.completedAt??0,h=a.submittedAt??0,u=s-h,S=(a.executorRunTime??0)/u,b=(a.localityStats??[]).reduce((t,d)=>t+d.count,0)||1,N=new Map((a.localityStats??[]).map(t=>[t.locality,t.count/b]));for(let t=0;t<c;t++){const d=x+t*n,L=d+n,p=Math.min(s,L)-Math.max(h,d);if(p<=0)continue;const k=p/n;for(const[C,I]of N){const f=S*I*k;i[C]?i[C][t]+=f:g[t]+=f}}}g.some(a=>a>0)&&(i.OTHER=g);const o=new Array(c).fill(0);for(const a of Object.keys(i))for(let s=0;s<c;s++)o[s]+=i[a][s];const E=o.reduce((a,s)=>Math.max(a,s),0);i.idle=o.map(a=>Math.max(0,E-a));const A=[];for(let a=0;a<c;a++)A.push(x+a*n);return{labels:A,series:i}}const ut=240,pt=60,z={PROCESS_LOCAL:"Process-local",NODE_LOCAL:"Node-local",RACK_LOCAL:"Rack-local",NO_PREF:"No preference",ANY:"Any",OTHER:"Other",idle:"Idle"},H={PROCESS_LOCAL:l.clean,NODE_LOCAL:l.accent,RACK_LOCAL:l.warning,NO_PREF:l.muted,ANY:l.critical,OTHER:l.muted,idle:l.muted},F="Core Usage by Locality",vt=O.memo(function({appModel:n,catalog:y,activeFileId:m,defaultCollapsed:x=!0}){const[v,c]=O.useState(0),i=O.useMemo(()=>{const t=[...n.stages.values()];if(!t.some(r=>(r.executorRunTime??0)>0&&(r.completedAt??0)>(r.submittedAt??0)))return{hasActivity:!1};const L=n.app,p=L?.startTime??0,k=L?.endTime??p,C=Math.max(6e4,Math.ceil(Math.max(1,k-p)/pt)),{labels:I,series:f}=dt(t,{bucketWidthMs:C}),w=[...D.filter(r=>f[r]),...f.OTHER?["OTHER"]:[],"idle"],P=I.map((r,_)=>{const j={t:Math.round((r-p)/1e3)};for(const R of w)j[R]=f[R]?.[_]??0;return j}),K=Q(P),U=w.filter(r=>r!=="idle"),Y=P.reduce((r,_)=>Math.max(r,U.reduce((j,R)=>j+_[R],0)),0);return{hasActivity:!0,order:w,points:P,sampled:K,peakCores:Y}},[n,m]),g=O.useMemo(()=>G([...n.stages.values()],{topN:1/0}),[n,m]),o=y.find(t=>t.type==="coreLocality"),E=y.some(t=>t.type==="memoryUtilization"&&t.variant==="idleCores"),A=g.topStages.filter(t=>t.nonLocalTasks>0),{totalPages:a,effectivePage:s,visible:h}=et(A,v,c);if(!i.hasActivity)return e.jsx(B,{title:F,summary:e.jsx("span",{className:"text-muted-foreground text-xs",children:"No stage activity to plot."}),children:e.jsx(V,{tone:"neutral",icon:ot,title:"No stage activity to plot."})});const{order:u,points:S,sampled:b,peakCores:N}=i;return e.jsx(B,{title:F,impactBand:o?.impactBand,badges:o?e.jsx(W,{type:"coreLocality",impactBand:o.impactBand}):null,defaultCollapsed:x,summary:e.jsx(at,{value:`${Math.round(N)} cores`,context:"peak concurrent, by locality"}),children:e.jsxs("div",{className:"space-y-2",children:[e.jsx(T,{children:e.jsxs("p",{className:"text-muted-foreground text-xs",children:["approximate: stage-level attribution",o?null:e.jsxs(e.Fragment,{children:[", see ",e.jsx(q,{anchor:"#bottleneck-utilization",children:"how utilization is measured"})]})]})}),o?e.jsxs("p",{className:"flex flex-wrap items-start gap-2 text-sm",children:[e.jsx(X,{impactBand:o.impactBand,className:"mt-1.5"}),e.jsxs("span",{children:[e.jsxs("strong",{children:[o.value,"% non-local"]}),": ",o.recommendation]})]}):null,o?e.jsx(Z,{finding:o}):null,e.jsx(T,{children:o?e.jsx($,{confidence:o.confidence,validationRequired:o.validationRequired}):null}),e.jsx(J,{ariaLabel:"Approximate concurrent core usage over time, stacked by task locality tier, with idle capacity shown. See the legend for tier colors.",height:ut,table:{caption:"Concurrent core usage over time by locality tier",columns:["Time (s)",...u.map(t=>z[t]??t)],rows:S.map(t=>[t.t,...u.map(d=>Math.round(t[d]))]),align:Array.from({length:u.length+1},()=>"right")},children:e.jsxs(st,{data:b,children:[e.jsx(nt,{vertical:!1,stroke:l.muted,strokeOpacity:.2}),e.jsx(it,{dataKey:"t",tick:{fontSize:10},label:{value:"seconds from app start",position:"insideBottom",offset:-2,fontSize:10}}),e.jsx(rt,{tick:{fontSize:10},width:36,label:{value:"avg concurrent cores",angle:-90,position:"insideLeft",fontSize:10}}),e.jsx(ct,{}),e.jsx(lt,{wrapperStyle:{fontSize:10}}),u.map(t=>e.jsx(mt,{type:"monotone",dataKey:t,name:z[t]??t,stackId:"cores",stroke:H[t]??l.muted,fill:H[t]??l.muted,fillOpacity:t==="idle"?.15:.8,isAnimationActive:!1},t))]})}),e.jsx(T,{children:h.length>0?e.jsxs(e.Fragment,{children:[e.jsx("ul",{className:"space-y-1 text-xs text-muted-foreground",children:h.map(t=>e.jsxs("li",{children:["Stage ",t.stageId,": ",Math.round(t.ratio*100),"% non-local (",t.nonLocalTasks,"/",t.taskCount," tasks)"]},t.stageId))}),e.jsx(tt,{page:s,totalPages:a,onPrev:()=>c(t=>t-1),onNext:()=>c(t=>t+1)})]}):null}),e.jsx(T,{children:E?e.jsx("p",{className:"text-xs text-muted-foreground",children:"Idle cores also flagged → see Memory Utilization widget"}):null})]})})});export{vt as CoreUsageArea};