{"version":3,"sources":["webpack:///./performance/constants.js","webpack:///./ci/pipeline_details/graph/api_utils.js","webpack:///./ci/pipeline_details/graph/perf_utils.js","webpack:///./ci/pipeline_details/graph/utils.js","webpack:///./ci/pipeline_details/utils/parsing_utils.js","webpack:///./ci/pipeline_details/utils/index.js","webpack:///./ci/pipeline_details/utils/drawing_utils.js","webpack:///./ci/pipeline_details/utils/unwrapping_utils.js","webpack:///./vue_shared/components/ci_icon/ci_icon.vue","webpack:///./vue_shared/components/ci_icon/ci_icon.vue?b66d","webpack:///vue_shared/components/ci_icon/ci_icon.vue","webpack:///./vue_shared/components/ci_icon/ci_icon.vue?9e67","webpack:///./performance/utils.js","webpack:///./ci/pipeline_details/constants.js"],"names":["PERFORMANCE_TYPE_MARK","PERFORMANCE_TYPE_MEASURE","SNIPPET_MARK_VIEW_APP_START","SNIPPET_MARK_EDIT_APP_START","SNIPPET_MARK_BLOBS_CONTENT","SNIPPET_MEASURE_BLOBS_CONTENT","WEBIDE_MARK_APP_START","WEBIDE_MARK_FILE_CLICKED","WEBIDE_MARK_FILE_FINISH","WEBIDE_MARK_REPO_EDITOR_START","WEBIDE_MARK_REPO_EDITOR_FINISH","WEBIDE_MARK_FETCH_BRANCH_DATA_START","WEBIDE_MARK_FETCH_BRANCH_DATA_FINISH","WEBIDE_MARK_FETCH_FILE_DATA_START","WEBIDE_MARK_FETCH_FILE_DATA_FINISH","WEBIDE_MARK_FETCH_FILES_START","WEBIDE_MARK_FETCH_FILES_FINISH","WEBIDE_MARK_FETCH_PROJECT_DATA_START","WEBIDE_MARK_FETCH_PROJECT_DATA_FINISH","WEBIDE_MEASURE_FILE_AFTER_INTERACTION","WEBIDE_MEASURE_FETCH_PROJECT_DATA","WEBIDE_MEASURE_FETCH_BRANCH_DATA","WEBIDE_MEASURE_FETCH_FILE_DATA","WEBIDE_MEASURE_BEFORE_VUE","WEBIDE_MEASURE_REPO_EDITOR","WEBIDE_MEASURE_FETCH_FILES","MR_DIFFS_MARK_FILE_TREE_START","MR_DIFFS_MARK_FILE_TREE_END","MR_DIFFS_MARK_DIFF_FILES_START","MR_DIFFS_MARK_FIRST_DIFF_FILE_SHOWN","MR_DIFFS_MARK_DIFF_FILES_END","MR_DIFFS_MEASURE_FILE_TREE_DONE","MR_DIFFS_MEASURE_DIFF_FILES_DONE","PIPELINES_DETAIL_LINKS_MARK_CALCULATE_START","PIPELINES_DETAIL_LINKS_MARK_CALCULATE_END","PIPELINES_DETAIL_LINKS_MEASURE_CALCULATION","PIPELINES_DETAIL_LINK_DURATION","PIPELINES_DETAIL_LINKS_TOTAL","PIPELINES_DETAIL_LINKS_JOB_RATIO","REPO_BLOB_LOAD_VIEWER_START","REPO_BLOB_SWITCH_TO_VIEWER_START","REPO_BLOB_LOAD_VIEWER_FINISH","REPO_BLOB_LOAD_VIEWER","REPO_BLOB_SWITCH_VIEWER","DESIGN_MARK_APP_START","DESIGN_MEASURE_BEFORE_APP","DESIGN_MAIN_IMAGE_OUTPUT","finishPerfMeasureAndSend","numLinks","numGroups","metricsPath","performanceMarkAndMeasure","mark","measures","name","start","window","requestAnimationFrame","_window$performance$g","duration","performance","getEntriesByName","data","histograms","value","path","stats","axios","post","catch","err","reportToSentry","addMulti","mainPipelineProjectPath","linkedPipeline","multiproject","project","fullPath","calculatePipelineLayersInfo","pipeline","componentName","shouldCollectMetrics","Boolean","layers","listByLayers","linksData","length","getQueryHeaders","etagResource","etagQueryHeaders","serializeGqlErr","gqlError","locations","message","flatMap","loc","Object","entries","flat","join","serializeLoadErrors","errors","graphQLErrors","networkError","_isEmpty","map","transformId","id","getIdFromGraphQLId","unwrapPipelineData","_data$project","upstream","downstream","stages","nodes","updatedStages","lookup","unwrapStagesWithNeedsAndLookup","stagesLookup","bind","validateConfigPaths","_value$graphqlResourc","graphqlResourceEtag","confirmJobConfirmationMessage","jobName","confirmAction","title","sprintf","s__","sanitize","modalHtmlMessage","__","primaryBtnText","deduplicate","item","itemIndex","arr","findIndex","test","source","target","getAllAncestors","nodeDict","needs","node","_nodeDict$node","filter","parseData","needsKey","NEEDS_PROPERTY","createNodeDict","allLinks","jobs","groupName","job","reduce","acc","needed","_nodeDict$needed","push","makeLinksFromNodes","links","targetNodeNeedsMinusSource","need","includes","filterByAncestors","arrayOfJobs","groups","parsedData","explicitParsedData","EXPLICIT_NEEDS_PROPERTY","pipelineLayers","createSankey","layer","generateColumnsFromLayersListMemoized","_memoize","idx","status","action","_stages$stageIdx","stageIdx","groupIdx","keepLatestDownstreamPipelines","downstreamPipelines","_pipeline$sourceJob","_pipeline$source_job","source_job","retried","sourceJob","newNode","size","forEach","validateParams","params","_pickBy","val","key","SUPPORTED_FILTER_PARAMETERS","createJobsHash","generateJobNeedsDict","keys","recursiveNeeds","_jobs$jobName","needsAcc","_acc$job","newNeeds","group","Infinity","uniqueValues","Array","from","Set","getPipelineDefaultTab","url","strippedUrl","parseUrlPathname","tabName","match","validPipelineTabNames","pipelineTabName","graphqlEtagPipelinePath","graphqlPath","pipelineId","graphqlEtagStagePath","stageId","width","height","nodeWidth","nodePadding","paddingForLabels","sankeyGenerator","sankey","nodeId","nodeAlign","sankeyLeft","extent","d","generateLinksData","containerID","modifier","containerEl","document","getElementById","link","d3","sourceId","targetId","modifiedSourceId","modifiedTargetId","sourceNodeEl","targetNodeEl","sourceNodeCoordinates","getBoundingClientRect","targetNodeCoordinates","containerCoordinates","paddingLeft","parseFloat","getComputedStyle","getPropertyValue","paddingTop","sourceNodeX","right","x","sourceNodeY","top","y","targetNodeX","targetNodeY","sourceNodeLeftX","left","firstPointCoordinateX","moveTo","straightLineDestinationX","controlPointX","lineTo","bezierCurveTo","ref","stageName","toString","unwrapNodesWithName","jobArray","prop","field","denodedStages","unwrappedNestedGroups","stage","groupsWithStageName","lookupMap","groupsWithJobs","denodedJobArray","explicitNeedsUnwrapped","unwrapJobWithNeeds","unwrapStagesWithNeeds","components","GlIcon","directives","GlTooltip","GlTooltipDirective","props","type","required","validator","icon","startsWith","showStatusText","default","showTooltip","useLink","computed","componentType","href","_this$status","text","ariaLabel","_this$status2","detailsPath","details_path","variant","component","_vm","this","_c","_self","rawName","modifiers","tag","staticClass","class","attrs","on","$event","$emit","_v","_s","_e","measure","end","CANCEL_REQUEST","testStatus","FAILED","SKIPPED","SUCCESS","ERROR","UNKNOWN","DEFAULT","DELETE_FAILURE","DRAW_FAILURE","LOAD_FAILURE","POST_FAILURE","jobsTabName","failedJobsTabName","testReportTabName","manualVariablesTabName","securityTabName","licensesTabName","codeQualityTabName","TOAST_MESSAGE","DEFAULT_FIELDS","label","columnClass","tdClass"],"mappings":"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","file":"28.39bf1362.chunk.js","sourcesContent":["export const PERFORMANCE_TYPE_MARK = 'mark';\nexport const PERFORMANCE_TYPE_MEASURE = 'measure';\n\n//\n// SNIPPET namespace\n//\n\n// Marks\nexport const SNIPPET_MARK_VIEW_APP_START = 'snippet-view-app-start';\nexport const SNIPPET_MARK_EDIT_APP_START = 'snippet-edit-app-start';\nexport const SNIPPET_MARK_BLOBS_CONTENT = 'snippet-blobs-content-finished';\n\n// Measures\nexport const SNIPPET_MEASURE_BLOBS_CONTENT = 'snippet-blobs-content';\n\n//\n// WebIDE namespace\n//\n\n// Marks\nexport const WEBIDE_MARK_APP_START = 'webide-app-start';\nexport const WEBIDE_MARK_FILE_CLICKED = 'webide-file-clicked';\nexport const WEBIDE_MARK_FILE_FINISH = 'webide-file-finished';\nexport const WEBIDE_MARK_REPO_EDITOR_START = 'webide-init-editor-start';\nexport const WEBIDE_MARK_REPO_EDITOR_FINISH = 'webide-init-editor-finish';\nexport const WEBIDE_MARK_FETCH_BRANCH_DATA_START = 'webide-getBranchData-start';\nexport const WEBIDE_MARK_FETCH_BRANCH_DATA_FINISH = 'webide-getBranchData-finish';\nexport const WEBIDE_MARK_FETCH_FILE_DATA_START = 'webide-getFileData-start';\nexport const WEBIDE_MARK_FETCH_FILE_DATA_FINISH = 'webide-getFileData-finish';\nexport const WEBIDE_MARK_FETCH_FILES_START = 'webide-getFiles-start';\nexport const WEBIDE_MARK_FETCH_FILES_FINISH = 'webide-getFiles-finish';\nexport const WEBIDE_MARK_FETCH_PROJECT_DATA_START = 'webide-getProjectData-start';\nexport const WEBIDE_MARK_FETCH_PROJECT_DATA_FINISH = 'webide-getProjectData-finish';\n\n// Measures\nexport const WEBIDE_MEASURE_FILE_AFTER_INTERACTION = 'webide-file-loading-after-interaction';\nexport const WEBIDE_MEASURE_FETCH_PROJECT_DATA = 'WebIDE: Project data';\nexport const WEBIDE_MEASURE_FETCH_BRANCH_DATA = 'WebIDE: Branch data';\nexport const WEBIDE_MEASURE_FETCH_FILE_DATA = 'WebIDE: File data';\nexport const WEBIDE_MEASURE_BEFORE_VUE = 'WebIDE: Before Vue app';\nexport const WEBIDE_MEASURE_REPO_EDITOR = 'WebIDE: Repo Editor';\nexport const WEBIDE_MEASURE_FETCH_FILES = 'WebIDE: Fetch Files';\n\n//\n// MR Diffs namespace\n//\n\n// Marks\nexport const MR_DIFFS_MARK_FILE_TREE_START = 'mr-diffs-mark-file-tree-start';\nexport const MR_DIFFS_MARK_FILE_TREE_END = 'mr-diffs-mark-file-tree-end';\nexport const MR_DIFFS_MARK_DIFF_FILES_START = 'mr-diffs-mark-diff-files-start';\nexport const MR_DIFFS_MARK_FIRST_DIFF_FILE_SHOWN = 'mr-diffs-mark-first-diff-file-shown';\nexport const MR_DIFFS_MARK_DIFF_FILES_END = 'mr-diffs-mark-diff-files-end';\n\n// Measures\nexport const MR_DIFFS_MEASURE_FILE_TREE_DONE = 'mr-diffs-measure-file-tree-done';\nexport const MR_DIFFS_MEASURE_DIFF_FILES_DONE = 'mr-diffs-measure-diff-files-done';\n\n//\n// Pipelines Detail namespace\n//\n\n// Marks\nexport const PIPELINES_DETAIL_LINKS_MARK_CALCULATE_START =\n  'pipelines-detail-links-mark-calculate-start';\nexport const PIPELINES_DETAIL_LINKS_MARK_CALCULATE_END =\n  'pipelines-detail-links-mark-calculate-end';\n\n// Measures\nexport const PIPELINES_DETAIL_LINKS_MEASURE_CALCULATION =\n  'Pipelines Detail Graph: Links Calculation';\n\n// Metrics\n// Note: These strings must match the backend\n// (defined in: app/services/ci/prometheus_metrics/observe_histograms_service.rb)\nexport const PIPELINES_DETAIL_LINK_DURATION = 'pipeline_graph_link_calculation_duration_seconds';\nexport const PIPELINES_DETAIL_LINKS_TOTAL = 'pipeline_graph_links_total';\nexport const PIPELINES_DETAIL_LINKS_JOB_RATIO = 'pipeline_graph_links_per_job_ratio';\n\n//\n// REPO BROWSER NAMESPACE\n//\n\n// Marks\nexport const REPO_BLOB_LOAD_VIEWER_START = 'blobviewer-load-viewer-start';\nexport const REPO_BLOB_SWITCH_TO_VIEWER_START = 'blobviewer-switch-to-viewerr-start';\nexport const REPO_BLOB_LOAD_VIEWER_FINISH = 'blobviewer-load-viewer-finish';\n\n// Measures\nexport const REPO_BLOB_LOAD_VIEWER = 'Repository File Viewer: loading the viewer';\nexport const REPO_BLOB_SWITCH_VIEWER = 'Repository File Viewer: switching the viewer';\n\n//\n// DESIGN MANAGEMENT NAMESPACE\n//\n\n// Marks\nexport const DESIGN_MARK_APP_START = 'design-app-start';\n\n// Measures\nexport const DESIGN_MEASURE_BEFORE_APP = 'Design Management: Before the Vue app';\nexport const DESIGN_MAIN_IMAGE_OUTPUT = 'Design Management: Single image preview';\n","import axios from '~/lib/utils/axios_utils';\nimport { reportToSentry } from '~/ci/utils';\n\nexport const reportPerformance = (path, stats) => {\n  // FIXME: https://gitlab.com/gitlab-org/gitlab/-/issues/330245\n  if (!path) {\n    return;\n  }\n\n  axios.post(path, stats).catch((err) => {\n    reportToSentry('links_inner_perf', `error: ${err}`);\n  });\n};\n","import {\n  PIPELINES_DETAIL_LINKS_MARK_CALCULATE_START,\n  PIPELINES_DETAIL_LINKS_MARK_CALCULATE_END,\n  PIPELINES_DETAIL_LINKS_MEASURE_CALCULATION,\n  PIPELINES_DETAIL_LINK_DURATION,\n  PIPELINES_DETAIL_LINKS_TOTAL,\n  PIPELINES_DETAIL_LINKS_JOB_RATIO,\n} from '~/performance/constants';\n\nimport { performanceMarkAndMeasure } from '~/performance/utils';\nimport { reportPerformance } from './api_utils';\n\nexport const beginPerfMeasure = () => {\n  performanceMarkAndMeasure({ mark: PIPELINES_DETAIL_LINKS_MARK_CALCULATE_START });\n};\n\nexport const finishPerfMeasureAndSend = (numLinks, numGroups, metricsPath) => {\n  performanceMarkAndMeasure({\n    mark: PIPELINES_DETAIL_LINKS_MARK_CALCULATE_END,\n    measures: [\n      {\n        name: PIPELINES_DETAIL_LINKS_MEASURE_CALCULATION,\n        start: PIPELINES_DETAIL_LINKS_MARK_CALCULATE_START,\n      },\n    ],\n  });\n\n  window.requestAnimationFrame(() => {\n    const duration = window.performance.getEntriesByName(\n      PIPELINES_DETAIL_LINKS_MEASURE_CALCULATION,\n    )[0]?.duration;\n\n    if (!duration) {\n      return;\n    }\n\n    const data = {\n      histograms: [\n        { name: PIPELINES_DETAIL_LINK_DURATION, value: duration / 1000 },\n        { name: PIPELINES_DETAIL_LINKS_TOTAL, value: numLinks },\n        {\n          name: PIPELINES_DETAIL_LINKS_JOB_RATIO,\n          value: numLinks / numGroups,\n        },\n      ],\n    };\n\n    reportPerformance(metricsPath, data);\n  });\n};\n","import { isEmpty } from 'lodash';\nimport { getIdFromGraphQLId, etagQueryHeaders } from '~/graphql_shared/utils';\nimport { reportToSentry } from '~/ci/utils';\n\nimport { listByLayers } from '~/ci/pipeline_details/utils/parsing_utils';\nimport { unwrapStagesWithNeedsAndLookup } from '~/ci/pipeline_details/utils/unwrapping_utils';\nimport { confirmAction } from '~/lib/utils/confirm_via_gl_modal/confirm_via_gl_modal';\nimport { sanitize } from '~/lib/dompurify';\nimport { __, s__, sprintf } from '~/locale';\nimport { beginPerfMeasure, finishPerfMeasureAndSend } from './perf_utils';\n\nexport { toggleQueryPollingByVisibility } from '~/graphql_shared/utils';\n\nconst addMulti = (mainPipelineProjectPath, linkedPipeline) => {\n  return {\n    ...linkedPipeline,\n    multiproject: mainPipelineProjectPath !== linkedPipeline.project.fullPath,\n  };\n};\n\nconst calculatePipelineLayersInfo = (pipeline, componentName, metricsPath) => {\n  const shouldCollectMetrics = Boolean(metricsPath);\n\n  if (shouldCollectMetrics) {\n    beginPerfMeasure();\n  }\n\n  let layers = null;\n\n  try {\n    layers = listByLayers(pipeline);\n\n    if (shouldCollectMetrics) {\n      finishPerfMeasureAndSend(layers.linksData.length, layers.numGroups, metricsPath);\n    }\n  } catch (err) {\n    reportToSentry(componentName, err);\n  }\n\n  return layers;\n};\n\nconst getQueryHeaders = (etagResource) =>\n  etagQueryHeaders('verify/ci/pipeline-graph', etagResource);\n\nconst serializeGqlErr = (gqlError) => {\n  const { locations = [], message = '', path = [] } = gqlError;\n\n  // eslint-disable-next-line @gitlab/require-i18n-strings\n  return `\n    ${message}.\n    Locations: ${locations\n      .flatMap((loc) => Object.entries(loc))\n      .flat(2)\n      .join(' ')}.\n    Path: ${path.join(', ')}.\n  `;\n};\n\nconst serializeLoadErrors = (errors) => {\n  const { gqlError, graphQLErrors, networkError, message } = errors;\n\n  if (!isEmpty(graphQLErrors)) {\n    return graphQLErrors.map((err) => serializeGqlErr(err)).join('; ');\n  }\n\n  if (!isEmpty(gqlError)) {\n    return serializeGqlErr(gqlError);\n  }\n\n  if (!isEmpty(networkError)) {\n    return `Network error: ${networkError.message}`; // eslint-disable-line @gitlab/require-i18n-strings\n  }\n\n  return message;\n};\n\nconst transformId = (linkedPipeline) => {\n  return { ...linkedPipeline, id: getIdFromGraphQLId(linkedPipeline.id) };\n};\n\nconst unwrapPipelineData = (mainPipelineProjectPath, data) => {\n  if (!data?.project?.pipeline) {\n    return null;\n  }\n\n  const { pipeline } = data.project;\n\n  const {\n    upstream,\n    downstream,\n    stages: { nodes: stages },\n  } = pipeline;\n\n  const { stages: updatedStages, lookup } = unwrapStagesWithNeedsAndLookup(stages);\n\n  return {\n    ...pipeline,\n    id: getIdFromGraphQLId(pipeline.id),\n    stages: updatedStages,\n    stagesLookup: lookup,\n    upstream: upstream\n      ? [upstream].map(addMulti.bind(null, mainPipelineProjectPath)).map(transformId)\n      : [],\n    downstream: downstream\n      ? downstream.nodes.map(addMulti.bind(null, mainPipelineProjectPath)).map(transformId)\n      : [],\n  };\n};\n\nconst validateConfigPaths = (value) => value.graphqlResourceEtag?.length > 0;\n\nconst confirmJobConfirmationMessage = (jobName, message) => {\n  return confirmAction(null, {\n    title: sprintf(s__('PipelineGraph|Are you sure you want to run %{jobName}?'), {\n      jobName: sanitize(jobName),\n    }),\n    modalHtmlMessage: `\n      <p>${sprintf(__('Custom confirmation message: %{message}'), {\n        message: sanitize(message),\n      })}</p>\n      <p>${s__('PipelineGraph|Do you want to continue?')}</p>\n    `,\n    primaryBtnText: sprintf(__('Yes, run %{jobName}'), {\n      jobName: sanitize(jobName),\n    }),\n  });\n};\n\nexport {\n  calculatePipelineLayersInfo,\n  getQueryHeaders,\n  serializeGqlErr,\n  serializeLoadErrors,\n  unwrapPipelineData,\n  validateConfigPaths,\n  confirmJobConfirmationMessage,\n};\n","import { memoize } from 'lodash';\nimport { EXPLICIT_NEEDS_PROPERTY, NEEDS_PROPERTY } from '../constants';\nimport { createSankey } from './drawing_utils';\nimport { createNodeDict } from './index';\n\n/*\n  A peformant alternative to lodash's isEqual. Because findIndex always finds\n  the first instance of a match, if the found index is not the first, we know\n  it is in fact a duplicate.\n*/\nconst deduplicate = (item, itemIndex, arr) => {\n  const foundIdx = arr.findIndex((test) => {\n    return test.source === item.source && test.target === item.target;\n  });\n\n  return foundIdx === itemIndex;\n};\n\nexport const makeLinksFromNodes = (nodes, nodeDict, { needsKey = NEEDS_PROPERTY } = {}) => {\n  const constantLinkValue = 10; // all links are the same weight\n  return nodes\n    .map(({ jobs, name: groupName }) =>\n      jobs.map((job) => {\n        const needs = job[needsKey] || [];\n\n        return needs.reduce((acc, needed) => {\n          // It's possible that we have an optional job, which\n          // is being needed by another job. In that scenario,\n          // the needed job doesn't exist, so we don't want to\n          // create link for it.\n          if (nodeDict[needed]?.name) {\n            acc.push({\n              source: nodeDict[needed].name,\n              target: groupName,\n              value: constantLinkValue,\n            });\n          }\n\n          return acc;\n        }, []);\n      }),\n    )\n    .flat(2);\n};\n\nexport const getAllAncestors = (nodes, nodeDict) => {\n  const needs = nodes\n    .map((node) => {\n      return nodeDict[node]?.needs || '';\n    })\n    .flat()\n    .filter(Boolean)\n    .filter(deduplicate);\n\n  if (needs.length) {\n    return [...needs, ...getAllAncestors(needs, nodeDict)];\n  }\n\n  return [];\n};\n\nexport const filterByAncestors = (links, nodeDict) =>\n  links.filter(({ target, source }) => {\n    /*\n\n    for every link, check out it's target\n    for every target, get the target node's needs\n    then drop the current link source from that list\n\n    call a function to get all ancestors, recursively\n    is the current link's source in the list of all parents?\n    then we drop this link\n\n  */\n    const targetNode = target;\n    const targetNodeNeeds = nodeDict[targetNode].needs;\n    const targetNodeNeedsMinusSource = targetNodeNeeds.filter((need) => need !== source);\n    const allAncestors = getAllAncestors(targetNodeNeedsMinusSource, nodeDict);\n    return !allAncestors.includes(source);\n  });\n\nexport const parseData = (nodes, { needsKey = NEEDS_PROPERTY } = {}) => {\n  const nodeDict = createNodeDict(nodes, { needsKey });\n  const allLinks = makeLinksFromNodes(nodes, nodeDict, { needsKey });\n  const filteredLinks = allLinks.filter(deduplicate);\n  const links = filterByAncestors(filteredLinks, nodeDict);\n\n  return { nodes, links };\n};\n\n/*\n  The number of nodes in the most populous generation drives the height of the graph.\n*/\n\nexport const getMaxNodes = (nodes) => {\n  const counts = nodes.reduce((acc, { layer }) => {\n    if (!acc[layer]) {\n      acc[layer] = 0;\n    }\n\n    acc[layer] += 1;\n\n    return acc;\n  }, []);\n\n  return Math.max(...counts);\n};\n\n/*\n  This utility accepts unwrapped pipeline data in the format returned from\n  our standard pipeline GraphQL query and returns a list of names by layer\n  for the layer view. It can be combined with the stageLookup on the pipeline\n  to generate columns by layer.\n*/\n\nexport const listByLayers = ({ stages }) => {\n  const arrayOfJobs = stages.flatMap(({ groups }) => groups);\n  const parsedData = parseData(arrayOfJobs);\n  const explicitParsedData = parseData(arrayOfJobs, { needsKey: EXPLICIT_NEEDS_PROPERTY });\n  const dataWithLayers = createSankey()(explicitParsedData);\n\n  const pipelineLayers = dataWithLayers.nodes.reduce((acc, { layer, name }) => {\n    /* sort groups by layer */\n\n    if (!acc[layer]) {\n      acc[layer] = [];\n    }\n\n    acc[layer].push(name);\n\n    return acc;\n  }, []);\n\n  return {\n    linksData: parsedData.links,\n    numGroups: arrayOfJobs.length,\n    pipelineLayers,\n  };\n};\n\nexport const generateColumnsFromLayersListBare = ({ stages, stagesLookup }, pipelineLayers) => {\n  return pipelineLayers.map((layers, idx) => {\n    /*\n      Look up the groups in each layer,\n      then add each set of layer groups to a stage-like object.\n    */\n\n    const groups = layers.map((id) => {\n      const { stageIdx, groupIdx } = stagesLookup[id];\n      return stages[stageIdx]?.groups?.[groupIdx];\n    });\n\n    return {\n      name: '',\n      id: `layer-${idx}`,\n      status: { action: null },\n      groups: groups.filter(Boolean),\n    };\n  });\n};\n\nexport const generateColumnsFromLayersListMemoized = memoize(generateColumnsFromLayersListBare);\n\nexport const keepLatestDownstreamPipelines = (downstreamPipelines = []) => {\n  return downstreamPipelines.filter((pipeline) => {\n    if (pipeline.source_job) {\n      return !pipeline?.source_job?.retried || false;\n    }\n\n    return !pipeline?.sourceJob?.retried || false;\n  });\n};\n","import { pickBy } from 'lodash';\nimport { parseUrlPathname } from '~/lib/utils/url_utility';\nimport {\n  NEEDS_PROPERTY,\n  SUPPORTED_FILTER_PARAMETERS,\n  validPipelineTabNames,\n  pipelineTabName,\n} from '../constants';\n/*\n    The following functions are the main engine in transforming the data as\n    received from the endpoint into the format the d3 graph expects.\n\n    Input is of the form:\n    [nodes]\n      nodes: [{category, name, jobs, size}]\n        category is the stage name\n        name is a group name; in the case that the group has one job, it is\n          also the job name\n        size is the number of parallel jobs\n        jobs: [{ name, needs}]\n          job name is either the same as the group name or group x/y\n          needs: [job-names]\n          needs is an array of job-name strings\n\n    Output is of the form:\n    { nodes: [node], links: [link] }\n      node: { name, category }, + unused info passed through\n      link: { source, target, value }, with source & target being node names\n        and value being a constant\n\n    We create nodes in the GraphQL update function, and then here we create the node dictionary,\n    then create links, and then dedupe the links, so that in the case where\n    job 4 depends on job 1 and job 2, and job 2 depends on job 1, we show only a single link\n    from job 1 to job 2 then another from job 2 to job 4.\n\n    CREATE LINKS\n    nodes.name -> target\n    nodes.name.needs.each -> source (source is the name of the group, not the parallel job)\n    10 -> value (constant)\n  */\n\nexport const createNodeDict = (nodes, { needsKey = NEEDS_PROPERTY } = {}) => {\n  return nodes.reduce((acc, node) => {\n    const newNode = {\n      ...node,\n      needs: node.jobs.map((job) => job[needsKey] || []).flat(),\n    };\n\n    if (node.size > 1) {\n      node.jobs.forEach((job) => {\n        acc[job.name] = newNode;\n      });\n    }\n\n    acc[node.name] = newNode;\n    return acc;\n  }, {});\n};\n\nexport const validateParams = (params) => {\n  return pickBy(params, (val, key) => SUPPORTED_FILTER_PARAMETERS.includes(key) && val);\n};\n\n/**\n * This function takes the stages array and transform it\n * into a hash where each key is a job name and the job data\n * is associated to that key.\n * @param {Array} stages\n * @returns {Object} - Hash of jobs\n */\nexport const createJobsHash = (stages = []) => {\n  const nodes = stages.flatMap(({ groups }) => groups);\n  return createNodeDict(nodes);\n};\n\n/**\n * This function takes the jobs hash generated by\n * `createJobsHash` function and returns an easier\n * structure to work with for needs relationship\n * where the key is the job name and the value is an\n * array of all the needs this job has recursively\n * (includes the needs of the needs)\n * @param {Object} jobs\n * @returns {Object} - Hash of jobs and array of needs\n */\nexport const generateJobNeedsDict = (jobs = {}) => {\n  const arrOfJobNames = Object.keys(jobs);\n\n  return arrOfJobNames.reduce((acc, value) => {\n    const recursiveNeeds = (jobName) => {\n      if (!jobs[jobName]?.needs) {\n        return [];\n      }\n\n      return jobs[jobName].needs\n        .reduce((needsAcc, job) => {\n          // It's possible that a needs refer to an optional job\n          // that is not defined in which case we don't add that entry\n          if (!jobs[job]) {\n            return needsAcc;\n          }\n\n          // If we already have the needs of a job in the accumulator,\n          // then we use the memoized data instead of the recursive call\n          // to save some performance.\n          const newNeeds = acc[job] ?? recursiveNeeds(job);\n\n          // In case it's a parallel job (size > 1), the name of the group\n          // and the job will be different. This mean we also need to add the group name\n          // to the list of `needs` to ensure we can properly reference it.\n          const group = jobs[job];\n          if (group.size > 1) {\n            return [...needsAcc, job, group.name, newNeeds];\n          }\n\n          return [...needsAcc, job, newNeeds];\n        }, [])\n        .flat(Infinity);\n    };\n\n    // To ensure we don't have duplicates job relationship when 2 jobs\n    // needed by another both depends on the same jobs, we remove any\n    // duplicates from the array.\n    const uniqueValues = Array.from(new Set(recursiveNeeds(value)));\n\n    return { ...acc, [value]: uniqueValues };\n  }, {});\n};\n\nexport const getPipelineDefaultTab = (url) => {\n  const strippedUrl = parseUrlPathname(url);\n  const regexp = /\\w*$/;\n  const [tabName] = strippedUrl.match(regexp);\n\n  if (tabName && validPipelineTabNames.includes(tabName)) return tabName;\n  if (tabName === '') return pipelineTabName;\n\n  return null;\n};\n\nexport const graphqlEtagPipelinePath = (graphqlPath, pipelineId) => {\n  return `${graphqlPath}pipelines/id/${pipelineId}`;\n};\n\nexport const graphqlEtagMergeRequestPipelines = (graphqlPath, mergeRequestId) => {\n  return `${graphqlPath}merge_requests/id/${mergeRequestId}`;\n};\n\nexport const graphqlEtagStagePath = (graphqlPath, stageId) => {\n  return `${graphqlPath}/stages/id/${stageId}`;\n};\n","import * as d3 from 'd3';\nimport { sankey, sankeyLeft } from 'd3-sankey';\n\n/*\n    createSankey calls the d3 layout to generate the relationships and positioning\n    values for the nodes and links in the graph.\n  */\n\nexport const createSankey = ({\n  width = 10,\n  height = 10,\n  nodeWidth = 10,\n  nodePadding = 10,\n  paddingForLabels = 1,\n} = {}) => {\n  const sankeyGenerator = sankey()\n    .nodeId(({ name }) => name)\n    .nodeAlign(sankeyLeft)\n    .nodeWidth(nodeWidth)\n    .nodePadding(nodePadding)\n    .extent([\n      [paddingForLabels, paddingForLabels],\n      [width - paddingForLabels, height - paddingForLabels],\n    ]);\n  return ({ nodes, links }) =>\n    sankeyGenerator({\n      nodes: nodes.map((d) => ({ ...d })),\n      links: links.map((d) => ({ ...d })),\n    });\n};\n\nexport const createUniqueLinkId = (stageName, jobName) => `${stageName}-${jobName}`;\n\n/**\n * This function expects its first argument data structure\n * to be the same shaped as the one generated by `parseData`,\n * which contains nodes and links. For each link,\n * we find the nodes in the graph, calculate their coordinates and\n * trace the lines that represent the needs of each job.\n * @param {Object} nodeDict - Resulting object of `parseData` with nodes and links\n * @param {String} containerID - Id for the svg the links will be draw in\n * @returns {Array} Links that contain all the information about them\n */\n\nexport const generateLinksData = (links, containerID, modifier = '') => {\n  const containerEl = document.getElementById(containerID);\n\n  return links.map((link) => {\n    const path = d3.path();\n\n    const sourceId = link.source;\n    const targetId = link.target;\n\n    const modifiedSourceId = `${sourceId}${modifier}`;\n    const modifiedTargetId = `${targetId}${modifier}`;\n\n    const sourceNodeEl = document.getElementById(modifiedSourceId);\n    const targetNodeEl = document.getElementById(modifiedTargetId);\n\n    const sourceNodeCoordinates = sourceNodeEl.getBoundingClientRect();\n    const targetNodeCoordinates = targetNodeEl.getBoundingClientRect();\n    const containerCoordinates = containerEl.getBoundingClientRect();\n\n    // Because we add the svg dynamically and calculate the coordinates\n    // with plain JS and not D3, we need to account for the fact that\n    // the coordinates we are getting are absolutes, but we want to draw\n    // relative to the svg container, which starts at `containerCoordinates(x,y)`\n    // so we substract these from the total. We also need to remove the padding\n    // from the total to make sure it's aligned properly. We then make the line\n    // positioned in the center of the job node by adding half the height\n    // of the job pill.\n    const paddingLeft = parseFloat(\n      window.getComputedStyle(containerEl, null).getPropertyValue('padding-left') || 0,\n    );\n    const paddingTop = parseFloat(\n      window.getComputedStyle(containerEl, null).getPropertyValue('padding-top') || 0,\n    );\n\n    const sourceNodeX = sourceNodeCoordinates.right - containerCoordinates.x - paddingLeft;\n    const sourceNodeY =\n      sourceNodeCoordinates.top -\n      containerCoordinates.y -\n      paddingTop +\n      sourceNodeCoordinates.height / 2;\n    const targetNodeX = targetNodeCoordinates.x - containerCoordinates.x - paddingLeft;\n    const targetNodeY =\n      targetNodeCoordinates.y -\n      containerCoordinates.y -\n      paddingTop +\n      sourceNodeCoordinates.height / 2;\n\n    const sourceNodeLeftX = sourceNodeCoordinates.left - containerCoordinates.x - paddingLeft;\n\n    // If the source and target X values are the same,\n    // it means the nodes are in the same column so we\n    // want to start the line on the left of the pill\n    // instead of the right to have a nice curve.\n    const firstPointCoordinateX = sourceNodeLeftX === targetNodeX ? sourceNodeLeftX : sourceNodeX;\n\n    // First point\n    path.moveTo(firstPointCoordinateX, sourceNodeY);\n\n    // Make cross-stages lines a straight line all the way\n    // until we can safely draw the bezier to look nice.\n    // The adjustment number here is a magic number to make things\n    // look nice and should change if the padding changes. This goes well\n    // with gl-px-9 which we translate with 100px here.\n    const straightLineDestinationX = targetNodeX - 100;\n    const controlPointX = straightLineDestinationX + (targetNodeX - straightLineDestinationX) / 2;\n\n    if (straightLineDestinationX > firstPointCoordinateX) {\n      path.lineTo(straightLineDestinationX, sourceNodeY);\n    }\n\n    // Add bezier curve. The first 4 coordinates are the 2 control\n    // points to create the curve, and the last one is the end point (x, y).\n    // We want our control points to be in the middle of the line\n    path.bezierCurveTo(\n      controlPointX,\n      sourceNodeY,\n      controlPointX,\n      targetNodeY,\n      targetNodeX,\n      targetNodeY,\n    );\n\n    return {\n      ...link,\n      source: sourceId,\n      target: targetId,\n      ref: createUniqueLinkId(sourceId, targetId),\n      path: path.toString(),\n    };\n  });\n};\n","import { reportToSentry } from '~/ci/utils';\nimport { EXPLICIT_NEEDS_PROPERTY, NEEDS_PROPERTY } from '../constants';\n\nconst unwrapGroups = (stages) => {\n  return stages.map((stage, idx) => {\n    const {\n      groups: { nodes: groups },\n    } = stage;\n\n    /*\n      Being peformance conscious here means we don't want to spread and copy the\n      group value just to add one parameter.\n    */\n    /* eslint-disable no-param-reassign */\n    const groupsWithStageName = groups.map((group) => {\n      group.stageName = stage.name;\n      return group;\n    });\n    /* eslint-enable no-param-reassign */\n\n    return { node: { ...stage, groups: groupsWithStageName }, lookup: { stageIdx: idx } };\n  });\n};\n\nconst unwrapNodesWithName = (jobArray, prop, field = 'name') => {\n  if (jobArray.length < 1) {\n    reportToSentry('unwrapping_utils', 'undefined_job_hunt, array empty from backend');\n  }\n\n  return jobArray.map((job) => {\n    if (job[prop]) {\n      return { ...job, [prop]: job[prop].nodes.map((item) => item[field] || '') };\n    }\n    return job;\n  });\n};\n\nconst unwrapJobWithNeeds = (denodedJobArray) => {\n  const explicitNeedsUnwrapped = unwrapNodesWithName(denodedJobArray, EXPLICIT_NEEDS_PROPERTY);\n  return unwrapNodesWithName(explicitNeedsUnwrapped, NEEDS_PROPERTY);\n};\n\nconst unwrapStagesWithNeedsAndLookup = (denodedStages) => {\n  const unwrappedNestedGroups = unwrapGroups(denodedStages);\n\n  const lookupMap = {};\n\n  const nodes = unwrappedNestedGroups.map(({ node, lookup }) => {\n    const { groups } = node;\n    const groupsWithJobs = groups.map((group, idx) => {\n      const jobs = unwrapJobWithNeeds(group.jobs.nodes);\n\n      lookupMap[group.name] = { ...lookup, groupIdx: idx };\n      return { ...group, jobs };\n    });\n\n    return { ...node, groups: groupsWithJobs };\n  });\n\n  return { stages: nodes, lookup: lookupMap };\n};\n\nconst unwrapStagesWithNeeds = (denodedStages) => {\n  return unwrapStagesWithNeedsAndLookup(denodedStages).stages;\n};\n\nexport {\n  unwrapGroups,\n  unwrapJobWithNeeds,\n  unwrapNodesWithName,\n  unwrapStagesWithNeeds,\n  unwrapStagesWithNeedsAndLookup,\n};\n","var render = function render(){var _vm=this,_c=_vm._self._c;return _c(_vm.componentType,{directives:[{name:\"gl-tooltip\",rawName:\"v-gl-tooltip.viewport.left\",modifiers:{\"viewport\":true,\"left\":true}}],tag:\"component\",staticClass:\"ci-icon gl-inline-flex gl-items-center gl-text-sm\",class:`ci-icon-variant-${_vm.variant}`,attrs:{\"variant\":_vm.variant,\"title\":_vm.title,\"aria-label\":_vm.ariaLabel,\"href\":_vm.href,\"data-testid\":\"ci-icon\"},on:{\"click\":function($event){return _vm.$emit('ciStatusBadgeClick')}}},[_c('span',{staticClass:\"ci-icon-gl-icon-wrapper\"},[_c('gl-icon',{attrs:{\"name\":_vm.icon}})],1),(_vm.showStatusText)?_c('span',{staticClass:\"gl-ml-2 gl-mr-3 gl-self-center gl-whitespace-nowrap gl-leading-1\",attrs:{\"data-testid\":\"ci-icon-text\"}},[_vm._v(_vm._s(_vm.status.text))]):_vm._e()])\n}\nvar staticRenderFns = []\n\nexport { render, staticRenderFns }","import mod from \"-!../../../../../../node_modules/thread-loader/dist/cjs.js??ref--13-0!../../../../../../node_modules/babel-loader/lib/index.js??ref--13-1!../../../../../../node_modules/vue-loader/lib/index.js??vue-loader-options!./ci_icon.vue?vue&type=script&lang=js\"; export default mod; export * from \"-!../../../../../../node_modules/thread-loader/dist/cjs.js??ref--13-0!../../../../../../node_modules/babel-loader/lib/index.js??ref--13-1!../../../../../../node_modules/vue-loader/lib/index.js??vue-loader-options!./ci_icon.vue?vue&type=script&lang=js\"","<script>\nimport { GlTooltipDirective, GlIcon } from '@gitlab/ui';\nimport { sprintf, __ } from '~/locale';\n\n/**\n * Renders CI icon based on API response shared between all places where it is used.\n *\n * Receives status object containing:\n * status: {\n *   icon: \"status_running\" // used to render the icon and CSS class\n *   text: \"Running\",\n *   detailsPath: '/project1/jobs/1' // can also be details_path\n * }\n *\n * You may use ~/graphql_shared/fragments/ci_icon.fragment.graphql to fetch this\n * from the GraphQL API.\n *\n */\n\nexport default {\n  components: {\n    GlIcon,\n  },\n  directives: {\n    GlTooltip: GlTooltipDirective,\n  },\n  props: {\n    status: {\n      type: Object,\n      required: true,\n      validator(status) {\n        const { icon } = status;\n        return typeof icon === 'string' && icon.startsWith('status');\n      },\n    },\n    showStatusText: {\n      type: Boolean,\n      required: false,\n      default: false,\n    },\n    showTooltip: {\n      type: Boolean,\n      required: false,\n      default: true,\n    },\n    useLink: {\n      type: Boolean,\n      default: true,\n      required: false,\n    },\n  },\n  computed: {\n    componentType() {\n      return this.href ? 'a' : 'span';\n    },\n    title() {\n      if (this.showTooltip) {\n        // show tooltip only when not showing text already\n        return !this.showStatusText ? this.status?.text : null;\n      }\n      return null;\n    },\n    ariaLabel() {\n      return sprintf(__('Status: %{status}'), { status: this.status?.text });\n    },\n    href() {\n      // href can come from GraphQL (camelCase) or REST API (snake_case)\n      if (this.useLink) {\n        return this.status.detailsPath || this.status.details_path;\n      }\n      return null;\n    },\n    icon() {\n      if (this.status.icon) {\n        return `${this.status.icon}_borderless`;\n      }\n      return null;\n    },\n    variant() {\n      switch (this.status.icon) {\n        case 'status_success':\n          return 'success';\n        case 'status_warning':\n        case 'status_pending':\n          return 'warning';\n        case 'status_failed':\n          return 'danger';\n        case 'status_running':\n          return 'info';\n        // default covers the styles for the remainder of CI\n        // statuses that are not explicitly stated here\n        default:\n          return 'neutral';\n      }\n    },\n  },\n};\n</script>\n<template>\n  <component\n    :is=\"componentType\"\n    v-gl-tooltip.viewport.left\n    class=\"ci-icon gl-inline-flex gl-items-center gl-text-sm\"\n    :class=\"`ci-icon-variant-${variant}`\"\n    :variant=\"variant\"\n    :title=\"title\"\n    :aria-label=\"ariaLabel\"\n    :href=\"href\"\n    data-testid=\"ci-icon\"\n    @click=\"$emit('ciStatusBadgeClick')\"\n  >\n    <span class=\"ci-icon-gl-icon-wrapper\"><gl-icon :name=\"icon\" /></span\n    ><span\n      v-if=\"showStatusText\"\n      class=\"gl-ml-2 gl-mr-3 gl-self-center gl-whitespace-nowrap gl-leading-1\"\n      data-testid=\"ci-icon-text\"\n      >{{ status.text }}</span\n    >\n  </component>\n</template>\n","import { render, staticRenderFns } from \"./ci_icon.vue?vue&type=template&id=884042fa\"\nimport script from \"./ci_icon.vue?vue&type=script&lang=js\"\nexport * from \"./ci_icon.vue?vue&type=script&lang=js\"\n\n\n/* normalize component */\nimport normalizer from \"!../../../../../../node_modules/vue-loader/lib/runtime/componentNormalizer.js\"\nvar component = normalizer(\n  script,\n  render,\n  staticRenderFns,\n  false,\n  null,\n  null,\n  null\n  \n)\n\nexport default component.exports","export const performanceMarkAndMeasure = ({ mark, measures = [] } = {}) => {\n  window.requestAnimationFrame(() => {\n    if (mark && !performance.getEntriesByName(mark).length) {\n      performance.mark(mark);\n    }\n    measures.forEach((measure) => {\n      performance.measure(measure.name, measure.start, measure.end);\n    });\n  });\n};\n","import { __, s__ } from '~/locale';\n\nexport const CANCEL_REQUEST = 'CANCEL_REQUEST';\nexport const SUPPORTED_FILTER_PARAMETERS = ['username', 'ref', 'status', 'source'];\nexport const NEEDS_PROPERTY = 'needs';\nexport const EXPLICIT_NEEDS_PROPERTY = 'previousStageJobsOrNeeds';\n\nexport const testStatus = {\n  FAILED: 'failed',\n  SKIPPED: 'skipped',\n  SUCCESS: 'success',\n  ERROR: 'error',\n  UNKNOWN: 'unknown',\n};\n\n/* Error constants shared across graphs */\nexport const DEFAULT = 'default';\nexport const DELETE_FAILURE = 'delete_pipeline_failure';\nexport const DRAW_FAILURE = 'draw_failure';\nexport const LOAD_FAILURE = 'load_failure';\nexport const POST_FAILURE = 'post_failure';\n\n// Pipeline tabs\n\nexport const pipelineTabName = 'graph';\nexport const jobsTabName = 'builds';\nexport const failedJobsTabName = 'failures';\nexport const testReportTabName = 'test_report';\nexport const manualVariablesTabName = 'manual_variables';\nexport const securityTabName = 'security';\nexport const licensesTabName = 'licenses';\nexport const codeQualityTabName = 'codequality_report';\n\nexport const validPipelineTabNames = [\n  jobsTabName,\n  failedJobsTabName,\n  testReportTabName,\n  securityTabName,\n  licensesTabName,\n  codeQualityTabName,\n  manualVariablesTabName,\n];\n\nexport const TOAST_MESSAGE = s__('Pipeline|Creating pipeline.');\n\nexport const DEFAULT_FIELDS = [\n  {\n    key: 'name',\n    label: __('Name'),\n    columnClass: 'gl-w-1/5',\n  },\n  {\n    key: 'stage',\n    label: __('Stage'),\n    columnClass: 'gl-w-1/5',\n  },\n  {\n    key: 'failureMessage',\n    label: __('Failure'),\n    columnClass: 'gl-w-2/5',\n  },\n  {\n    key: 'actions',\n    label: '',\n    tdClass: 'gl-text-right',\n    columnClass: 'gl-w-1/5',\n  },\n];\n"],"sourceRoot":""}