From 8bd63dc140d025ce7b5644ea74aea099f065f0b3 Mon Sep 17 00:00:00 2001 From: Dave Shanley Date: Thu, 21 Jul 2022 15:19:23 -0400 Subject: [PATCH] Large cleanup and tuneup of messages Various messages fixed, small glitches resolved. --- cmd/dashboard.go | 1 + cmd/html_report.go | 2 +- cmd/lint.go | 60 ++++++++++++++----- cmd/shared_functions.go | 23 +++++++ cui/dashboard.go | 5 +- functions/core/alphabetical.go | 6 +- functions/core/pattern.go | 14 ++--- functions/core/schema.go | 2 +- functions/core/schema_test.go | 2 +- functions/core/truthy.go | 2 +- functions/openapi/no_eval_descriptions.go | 2 +- functions/openapi/oas_schema.go | 20 +++++-- functions/openapi/operation_parameters.go | 12 ++-- .../openapi/operation_parameters_test.go | 8 +-- functions/openapi/operation_single_tag.go | 2 +- ...ag_defined.go => operation_tag_defined.go} | 4 +- ..._test.go => operation_tag_defined_test.go} | 2 +- functions/openapi/path_parameters.go | 12 ++-- functions/openapi/path_parameters_test.go | 8 +-- functions/openapi/paths_kebab_case.go | 1 - functions/openapi/paths_kebab_case_test.go | 4 +- functions/openapi/polymorphic_anyOf.go | 2 +- functions/openapi/polymorphic_oneOf.go | 2 +- go.mod | 3 +- go.sum | 6 ++ .../ui/build/static/js/vacuumReport.js | 14 ++--- .../header/header-statistic-component.ts | 4 +- model/results.go | 6 +- motor/rule_applicator.go | 10 ++-- motor/rule_applicator_test.go | 28 ++++----- rulesets/ruleset_functions.go | 36 +++++------ 31 files changed, 190 insertions(+), 113 deletions(-) rename functions/openapi/{tag_defined.go => operation_tag_defined.go} (95%) rename functions/openapi/{tag_defined_test.go => operation_tag_defined_test.go} (95%) diff --git a/cmd/dashboard.go b/cmd/dashboard.go index 23cddb1d..f0320c1c 100644 --- a/cmd/dashboard.go +++ b/cmd/dashboard.go @@ -73,6 +73,7 @@ func GetDashboardCommand() *cobra.Command { } dash := cui.CreateDashboard(resultSet, specIndex, specInfo) + dash.Version = Version dash.Render() return nil }, diff --git a/cmd/html_report.go b/cmd/html_report.go index 72264364..293cce81 100644 --- a/cmd/html_report.go +++ b/cmd/html_report.go @@ -25,7 +25,7 @@ func GetHTMLReportCommand() *cobra.Command { return &cobra.Command{ SilenceUsage: true, - SilenceErrors: true, + SilenceErrors: false, Use: "html-report", Short: "Generate an HTML report of a linting run", Long: "Generate an interactive and useful HTML report. Default output " + diff --git a/cmd/lint.go b/cmd/lint.go index 604773dd..525e0351 100644 --- a/cmd/lint.go +++ b/cmd/lint.go @@ -8,6 +8,7 @@ import ( "github.com/daveshanley/vacuum/model" "github.com/daveshanley/vacuum/motor" "github.com/daveshanley/vacuum/rulesets" + "github.com/dustin/go-humanize" "github.com/pterm/pterm" "github.com/spf13/cobra" "io/ioutil" @@ -35,6 +36,7 @@ func GetLintCommand() *cobra.Command { rulesetFlag, _ := cmd.Flags().GetString("ruleset") silent, _ := cmd.Flags().GetBool("silent") functionsFlag, _ := cmd.Flags().GetString("functions") + failSeverityFlag, _ := cmd.Flags().GetString("fail-severity") if !silent { PrintBanner() @@ -112,10 +114,14 @@ func GetLintCommand() *cobra.Command { fi, _ := os.Stat(args[0]) duration := time.Since(start) + warnings := resultSet.GetWarnCount() + errors := resultSet.GetErrorCount() + informs := resultSet.GetInfoCount() + if !detailsFlag { RenderSummary(resultSet, args, silent) RenderTime(timeFlag, duration, fi) - return nil + return CheckFailureSeverity(failSeverityFlag, errors, warnings, informs) } var cats []*model.RuleCategory @@ -164,9 +170,9 @@ func GetLintCommand() *cobra.Command { pterm.Println() } // Blank line + RenderSummary(resultSet, args, silent) RenderTime(timeFlag, duration, fi) - - return nil + return CheckFailureSeverity(failSeverityFlag, errors, warnings, informs) }, } @@ -175,6 +181,7 @@ func GetLintCommand() *cobra.Command { cmd.Flags().BoolP("errors", "e", false, "Show errors only") cmd.Flags().StringP("category", "c", "", "Show a single category of results") cmd.Flags().BoolP("silent", "x", false, "Show nothing except the result.") + cmd.Flags().StringP("fail-severity", "n", "error", "Results of this level or above will trigger a failure exit code") return cmd } @@ -187,13 +194,25 @@ func processResults(results []*model.RuleFunctionResult, specData []string, snip if !snippets { tableData = [][]string{{"Line / Column", "Severity", "Message", "Path"}} } - for _, r := range results { + for i, r := range results { + if i > 200 { + tableData = append(tableData, []string{"", "", pterm.LightRed(fmt.Sprintf("...%d "+ + "more violations not rendered.", len(results)-200)), ""}) + break + } if snippets { tableData = [][]string{{"Line / Column", "Severity", "Message", "Path"}} } - start := fmt.Sprintf("(%v:%v)", r.StartNode.Line, r.StartNode.Column) - + startLine := 0 + startCol := 0 + if r.StartNode != nil { + startLine = r.StartNode.Line + } + if r.StartNode != nil { + startCol = r.StartNode.Column + } + start := fmt.Sprintf("(%v:%v)", startLine, startCol) m := r.Message p := r.Path if len(r.Path) > 60 { @@ -228,6 +247,7 @@ func processResults(results []*model.RuleFunctionResult, specData []string, snip _ = pterm.DefaultTable.WithHasHeader().WithData(tableData).Render() renderCodeSnippet(r, specData) } + i++ } if !snippets && !silent { @@ -271,8 +291,8 @@ func RenderSummary(rs *model.RuleResultSet, args []string, silent bool) { if len(errors) > 0 || len(warn) > 0 || len(info) > 0 { - tableData = append(tableData, []string{cat.Name, fmt.Sprintf("%d", len(errors)), - fmt.Sprintf("%d", len(warn)), fmt.Sprintf("%d", len(info))}) + tableData = append(tableData, []string{cat.Name, fmt.Sprintf("%v", humanize.Comma(int64(len(errors)))), + fmt.Sprintf("%v", humanize.Comma(int64(len(warn)))), fmt.Sprintf("%v", humanize.Comma(int64(len(info))))}) } } @@ -281,24 +301,34 @@ func RenderSummary(rs *model.RuleResultSet, args []string, silent bool) { if !silent { pterm.DefaultTable.WithHasHeader().WithData(tableData).Render() pterm.Println() - pterm.Printf(">> run 'vacuum %s -d' to see full details", args[0]) - pterm.Println() pterm.Println() } } - if rs.GetErrorCount() > 0 { + errors := rs.GetErrorCount() + warnings := rs.GetWarnCount() + informs := rs.GetInfoCount() + errorsHuman := humanize.Comma(int64(rs.GetErrorCount())) + warningsHuman := humanize.Comma(int64(rs.GetWarnCount())) + informsHuman := humanize.Comma(int64(rs.GetInfoCount())) + + if errors > 0 { pterm.DefaultHeader.WithBackgroundStyle(pterm.NewStyle(pterm.BgRed)).WithMargin(10).Printf( - "Linting failed with %d errors", rs.GetErrorCount()) + "Linting failed with %v errors, %v warnings and %v informs", errorsHuman, warningsHuman, informsHuman) return } - if rs.GetWarnCount() > 0 { + if warnings > 0 { pterm.DefaultHeader.WithBackgroundStyle(pterm.NewStyle(pterm.BgYellow)).WithMargin(10).Printf( - "Linting passed, but with %d warnings", rs.GetWarnCount()) + "Linting passed, but with %v warnings and %v informs", warningsHuman, informsHuman) return } + if informs > 0 { + pterm.DefaultHeader.WithBackgroundStyle(pterm.NewStyle(pterm.BgGreen)).WithMargin(10).Printf( + "Linting passed, %v informs reported", informsHuman) + } + pterm.DefaultHeader.WithBackgroundStyle(pterm.NewStyle(pterm.BgGreen)).WithMargin(10).Println( - "Linting passed, great job!") + "Linting passed, A perfect score! well done!") } diff --git a/cmd/shared_functions.go b/cmd/shared_functions.go index 050171c2..b15a6085 100644 --- a/cmd/shared_functions.go +++ b/cmd/shared_functions.go @@ -61,3 +61,26 @@ func LoadCustomFunctions(functionsFlag string) (map[string]model.RuleFunction, e } return nil, nil } + +func CheckFailureSeverity(failSeverityFlag string, errors int, warnings int, informs int) error { + if failSeverityFlag != "error" { + switch failSeverityFlag { + case "warn": + if errors > 0 && warnings > 0 { + return fmt.Errorf("failed linting, with %d errors and %d warnings", errors, warnings) + } + return nil + case "info": + if errors > 0 && warnings > 0 && informs > 0 { + return fmt.Errorf("failed linting, with %d errors, %d warnings and %d informs", + errors, warnings, informs) + } + return nil + } + } else { + if errors > 0 { + return fmt.Errorf("failed linting, with %d errors", errors) + } + } + return nil +} diff --git a/cui/dashboard.go b/cui/dashboard.go index 574d06ce..eafbbcf8 100644 --- a/cui/dashboard.go +++ b/cui/dashboard.go @@ -1,6 +1,7 @@ package cui import ( + "fmt" "github.com/daveshanley/vacuum/model" ui "github.com/gizak/termui/v3" "github.com/gizak/termui/v3/widgets" @@ -32,6 +33,7 @@ type Dashboard struct { violationViewActive bool helpViewActive bool uiEvents <-chan ui.Event + Version string } func CreateDashboard(resultSet *model.RuleResultSet, index *index.SpecIndex, info *datamodel.SpecInfo) *Dashboard { @@ -203,8 +205,7 @@ func (dash *Dashboard) setGrid() { p := widgets.NewParagraph() // todo: bring in correct versioning. - - p.Text = "vacuum v0.0.1: " + + p.Text = fmt.Sprintf("vacuum %v: ", dash.Version) + "[Select Category](fg:white,bg:clear,md:bold) = ,\u2B05\uFE0F\u27A1\uFE0F/S,X | " + "[Change Rule](fg:white,bg:clear,md:bold) = \u2B06\u2B07/A,Z | " + "[Select / Leave Rule](fg:white,bg:clear,md:bold) = / " diff --git a/functions/core/alphabetical.go b/functions/core/alphabetical.go index 79fec642..fdba4ece 100644 --- a/functions/core/alphabetical.go +++ b/functions/core/alphabetical.go @@ -58,7 +58,7 @@ func (a Alphabetical) RunRule(nodes []*yaml.Node, context model.RuleFunctionCont if keyedBy == "" { results = append(results, model.RuleFunctionResult{ - Message: fmt.Sprintf("%s: '%s' is a map/object. %s", context.Rule.Description, + Message: fmt.Sprintf("%s: `%s` is a map/object. %s", context.Rule.Description, node.Value, a.GetSchema().ErrorMessage), StartNode: node, EndNode: node, @@ -165,7 +165,7 @@ func compareStringArray(strArr []string, context model.RuleFunctionContext) []mo s := strings.Compare(strArr[x], strArr[x+1]) if s > 0 { results = append(results, model.RuleFunctionResult{ - Message: fmt.Sprintf("%s: '%s' must be placed before '%s' (alphabetical)", + Message: fmt.Sprintf("%s: `%s` must be placed before `%s` (alphabetical)", context.Rule.Description, strArr[x+1], strArr[x]), }) @@ -191,7 +191,7 @@ func (a Alphabetical) checkNumberArrayIsSorted(arr *yaml.Node, context model.Rul } } - errmsg := "%s: '%v' is less than '%v', they need to be swapped (numerical ordering)" + errmsg := "%s: `%v` is less than `%v`, they need to be swapped (numerical ordering)" if len(floatArray) > 0 { if !sort.Float64sAreSorted(floatArray) { diff --git a/functions/core/pattern.go b/functions/core/pattern.go index a94cd3b5..9548542a 100644 --- a/functions/core/pattern.go +++ b/functions/core/pattern.go @@ -93,22 +93,22 @@ func (p Pattern) RunRule(nodes []*yaml.Node, context model.RuleFunctionContext) rx, err := p.getPatternFromCache(p.match, context.Rule) if err != nil { results = append(results, model.RuleFunctionResult{ - Message: fmt.Sprintf("%s: '%s' cannot be compiled into a regular expression: %s", + Message: fmt.Sprintf("%s: `%s` cannot be compiled into a regular expression: %s", context.Rule.Description, p.match, err.Error()), StartNode: node, EndNode: node, - Path: pathValue, + Path: fmt.Sprintf("%s.%s", pathValue, currentField), Rule: context.Rule, }) } else { pathValue = fmt.Sprintf("%s.%s", pathValue, currentField) if !rx.MatchString(node.Value) { results = append(results, model.RuleFunctionResult{ - Message: fmt.Sprintf("%s: '%s' does not match the expression '%s'", context.Rule.Description, + Message: fmt.Sprintf("%s: `%s` does not match the expression `%s`", context.Rule.Description, node.Value, p.match), StartNode: node, EndNode: node, - Path: pathValue, + Path: fmt.Sprintf("%s.%s", pathValue, currentField), Rule: context.Rule, }) } @@ -124,16 +124,16 @@ func (p Pattern) RunRule(nodes []*yaml.Node, context model.RuleFunctionContext) context.Rule.Description, err.Error()), StartNode: node, EndNode: node, - Path: pathValue, + Path: fmt.Sprintf("%s.%s", pathValue, currentField), Rule: context.Rule, }) } else { if rx.MatchString(node.Value) { results = append(results, model.RuleFunctionResult{ - Message: fmt.Sprintf("%s: matches the expression '%s'", context.Rule.Description, p.notMatch), + Message: fmt.Sprintf("%s: matches the expression `%s`", context.Rule.Description, p.notMatch), StartNode: node, EndNode: node, - Path: pathValue, + Path: fmt.Sprintf("%s.%s", pathValue, currentField), Rule: context.Rule, }) } diff --git a/functions/core/schema.go b/functions/core/schema.go index b45c0925..88d0cad7 100644 --- a/functions/core/schema.go +++ b/functions/core/schema.go @@ -54,7 +54,7 @@ func (sch Schema) RunRule(nodes []*yaml.Node, context model.RuleFunctionContext) if _, ok := forceValidation.(bool); ok { r := model.BuildFunctionResultString(fmt.Sprintf("%s: %s", context.Rule.Description, - fmt.Sprintf("'%s', is missing and is required", context.RuleAction.Field))) + fmt.Sprintf("`%s`, is missing and is required", context.RuleAction.Field))) r.StartNode = node r.EndNode = node.Content[len(node.Content)-1] r.Rule = context.Rule diff --git a/functions/core/schema_test.go b/functions/core/schema_test.go index 30a940ce..4040bd56 100644 --- a/functions/core/schema_test.go +++ b/functions/core/schema_test.go @@ -194,6 +194,6 @@ func TestOpenAPISchema_MissingFieldForceValidation(t *testing.T) { res := def.RunRule(nodes, ctx) assert.Len(t, res, 1) - assert.Equal(t, "schema must be valid: 'lolly', is missing and is required", res[0].Message) + assert.Equal(t, "schema must be valid: `lolly`, is missing and is required", res[0].Message) } diff --git a/functions/core/truthy.go b/functions/core/truthy.go index 72384e86..60b84849 100644 --- a/functions/core/truthy.go +++ b/functions/core/truthy.go @@ -52,7 +52,7 @@ func (t *Truthy) RunRule(nodes []*yaml.Node, context model.RuleFunctionContext) if !utils.IsNodeMap(fieldNode) && !utils.IsNodeArray(fieldNodeValue) { results = append(results, model.RuleFunctionResult{ - Message: fmt.Sprintf("%s: '%s' must be set", context.Rule.Description, context.RuleAction.Field), + Message: fmt.Sprintf("%s: `%s` must be set", context.Rule.Description, context.RuleAction.Field), StartNode: node, EndNode: node.Content[len(node.Content)-1], Path: pathValue, diff --git a/functions/openapi/no_eval_descriptions.go b/functions/openapi/no_eval_descriptions.go index b4b8beef..7b8bcc20 100644 --- a/functions/openapi/no_eval_descriptions.go +++ b/functions/openapi/no_eval_descriptions.go @@ -44,7 +44,7 @@ func (ne NoEvalInDescriptions) RunRule(nodes []*yaml.Node, context model.RuleFun endNode := desc.Node results = append(results, model.RuleFunctionResult{ - Message: fmt.Sprintf("description contains content with '%s', forbidden", pattern), + Message: fmt.Sprintf("description contains content with `%s`, forbidden", pattern), StartNode: startNode, EndNode: endNode, Path: desc.Path, diff --git a/functions/openapi/oas_schema.go b/functions/openapi/oas_schema.go index f6871003..29dec3e5 100644 --- a/functions/openapi/oas_schema.go +++ b/functions/openapi/oas_schema.go @@ -80,17 +80,29 @@ func (os OASSchema) RunRule(nodes []*yaml.Node, context model.RuleFunctionContex //validate using faster, more accurate resolver. if validationError := schema.Validate(*info.SpecJSON); validationError != nil { - failure := validationError.(*jsonschema.ValidationError) - for _, fail := range failure.Causes { + + if failure, ok := validationError.(*jsonschema.ValidationError); ok { + for _, fail := range failure.Causes { + results = append(results, model.RuleFunctionResult{ + Message: fmt.Sprintf("OpenAPI specification is invalid: %s %v", fail.KeywordLocation, + fail.Message), + StartNode: nodes[0], + EndNode: nodes[0], + Path: "$", + Rule: context.Rule, + }) + } + } + if failure, ok := validationError.(*jsonschema.InvalidJSONTypeError); ok { results = append(results, model.RuleFunctionResult{ - Message: fmt.Sprintf("OpenAPI specification is invalid: %s %v", fail.KeywordLocation, - fail.Message), + Message: fmt.Sprintf("OpenAPI specification is invalid: %v", failure.Error()), StartNode: nodes[0], EndNode: nodes[0], Path: "$", Rule: context.Rule, }) } + } return results } diff --git a/functions/openapi/operation_parameters.go b/functions/openapi/operation_parameters.go index 0d647a0e..9f7ce324 100644 --- a/functions/openapi/operation_parameters.go +++ b/functions/openapi/operation_parameters.go @@ -57,7 +57,7 @@ func (op OperationParameters) RunRule(nodes []*yaml.Node, context model.RuleFunc if strings.Contains(key, "~1") { results = append(results, model.RuleFunctionResult{ - Message: fmt.Sprintf("There is a '~1' character in this '%s' operation at '%s", + Message: fmt.Sprintf("There is a `~1` character in this `%s` operation at '%s", currentVerb, currentPath), StartNode: nil, EndNode: nil, @@ -74,7 +74,7 @@ func (op OperationParameters) RunRule(nodes []*yaml.Node, context model.RuleFunc endNode := utils.FindLastChildNode(startNode) results = append(results, model.RuleFunctionResult{ - Message: fmt.Sprintf("the '%s' operation at path '%s' contains an "+ + Message: fmt.Sprintf("the `%s` operation at path `%s` contains an "+ "empty parameter", currentVerb, currentPath), StartNode: startNode, EndNode: endNode, @@ -92,7 +92,7 @@ func (op OperationParameters) RunRule(nodes []*yaml.Node, context model.RuleFunc if seenParamInLocations[paramInNode.Value] { if paramInNode.Value == "body" { results = append(results, model.RuleFunctionResult{ - Message: fmt.Sprintf("the '%s' operation at path '%s' contains a "+ + Message: fmt.Sprintf("the `%s` operation at path `%s` contains a "+ "duplicate param in:body definition", currentVerb, currentPath), StartNode: startNode, EndNode: endNode, @@ -104,7 +104,7 @@ func (op OperationParameters) RunRule(nodes []*yaml.Node, context model.RuleFunc if paramInNode.Value == "body" || paramInNode.Value == "formData" { if seenParamInLocations["formData"] || seenParamInLocations["body"] { results = append(results, model.RuleFunctionResult{ - Message: fmt.Sprintf("the '%s' operation at path '%s' "+ + Message: fmt.Sprintf("the `%s` operation at path `%s` "+ "contains parameters using both in:body and in:formData", currentVerb, currentPath), StartNode: startNode, @@ -118,8 +118,8 @@ func (op OperationParameters) RunRule(nodes []*yaml.Node, context model.RuleFunc } } else { rfr := model.RuleFunctionResult{ - Message: fmt.Sprintf("the '%s' operation at path '%s' contains a "+ - "parameter with no 'in' value", currentVerb, currentPath), + Message: fmt.Sprintf("the `%s` operation at path `%s` contains a "+ + "parameter with no `in` value", currentVerb, currentPath), StartNode: startNode, EndNode: endNode, Path: resultPath, diff --git a/functions/openapi/operation_parameters_test.go b/functions/openapi/operation_parameters_test.go index eb332f8a..3eb95f8f 100644 --- a/functions/openapi/operation_parameters_test.go +++ b/functions/openapi/operation_parameters_test.go @@ -110,7 +110,7 @@ func TestOperationParameters_RunRule_DuplicateId(t *testing.T) { res := def.RunRule(nodes, ctx) assert.Len(t, res, 1) - assert.Equal(t, "the 'get' operation parameter at path '/users/{id}', index 1 has a duplicate name 'id'", res[0].Message) + assert.Equal(t, "the `get` operation parameter at path `/users/{id}`, index 1 has a duplicate name `id`", res[0].Message) } func TestOperationParameters_RunRule_DuplicateId_MultipleVerbs(t *testing.T) { @@ -145,7 +145,7 @@ func TestOperationParameters_RunRule_DuplicateId_MultipleVerbs(t *testing.T) { res := def.RunRule(nodes, ctx) assert.Len(t, res, 1) - assert.Equal(t, "the 'get' operation parameter at path '/users/{id}', index 1 has a duplicate name 'id'", res[0].Message) + assert.Equal(t, "the `get` operation parameter at path `/users/{id}`, index 1 has a duplicate name `id`", res[0].Message) } func TestOperationParameters_RunRule_DuplicateInBody(t *testing.T) { @@ -174,7 +174,7 @@ func TestOperationParameters_RunRule_DuplicateInBody(t *testing.T) { res := def.RunRule(nodes, ctx) assert.Len(t, res, 1) - assert.Equal(t, "the 'post' operation at path '/snakes/cakes' contains a duplicate param in:body definition", res[0].Message) + assert.Equal(t, "the `post` operation at path `/snakes/cakes` contains a duplicate param in:body definition", res[0].Message) } func TestOperationParameters_RunRule_FormDataAndBody(t *testing.T) { @@ -203,5 +203,5 @@ func TestOperationParameters_RunRule_FormDataAndBody(t *testing.T) { res := def.RunRule(nodes, ctx) assert.Len(t, res, 1) - assert.Equal(t, "the 'post' operation at path '/snakes/cakes' contains parameters using both in:body and in:formData", res[0].Message) + assert.Equal(t, "the `post` operation at path `/snakes/cakes` contains parameters using both in:body and in:formData", res[0].Message) } diff --git a/functions/openapi/operation_single_tag.go b/functions/openapi/operation_single_tag.go index e6656028..1d7d3663 100644 --- a/functions/openapi/operation_single_tag.go +++ b/functions/openapi/operation_single_tag.go @@ -45,7 +45,7 @@ func (ost OperationSingleTag) RunRule(nodes []*yaml.Node, context model.RuleFunc lastNode := utils.FindLastChildNode(tagsNode) results = append(results, model.RuleFunctionResult{ - Message: fmt.Sprintf("the '%s' operation at path '%s' contains more "+ + Message: fmt.Sprintf("the `%s` operation at path `%s` contains more "+ "than one tag (%d is too many)'", method, path, len(tags)), StartNode: tagsNode, EndNode: lastNode, diff --git a/functions/openapi/tag_defined.go b/functions/openapi/operation_tag_defined.go similarity index 95% rename from functions/openapi/tag_defined.go rename to functions/openapi/operation_tag_defined.go index 20bf1ff1..f45406d7 100644 --- a/functions/openapi/tag_defined.go +++ b/functions/openapi/operation_tag_defined.go @@ -84,8 +84,8 @@ func (td TagDefined) RunRule(nodes []*yaml.Node, context model.RuleFunctionConte endNode = opTagsNode.Content[j+1] } results = append(results, model.RuleFunctionResult{ - Message: fmt.Sprintf("the '%s' operation at path '%s' contains a "+ - "tag '%s', that is not defined in the global document tags", + Message: fmt.Sprintf("the `%s` operation at path `%s` contains a "+ + "tag `%s`, that is not defined in the global document tags", currentVerb, currentPath, operationTag.Value), StartNode: operationTag, EndNode: endNode, diff --git a/functions/openapi/tag_defined_test.go b/functions/openapi/operation_tag_defined_test.go similarity index 95% rename from functions/openapi/tag_defined_test.go rename to functions/openapi/operation_tag_defined_test.go index d83c94fd..8bb101d8 100644 --- a/functions/openapi/tag_defined_test.go +++ b/functions/openapi/operation_tag_defined_test.go @@ -99,6 +99,6 @@ paths: res := def.RunRule(nodes, ctx) assert.Len(t, res, 1) - assert.Equal(t, "the 'get' operation at path '/ember' contains a tag 'such_a_naughty_dog', "+ + assert.Equal(t, "the `get` operation at path `/ember` contains a tag `such_a_naughty_dog`, "+ "that is not defined in the global document tags", res[0].Message) } diff --git a/functions/openapi/path_parameters.go b/functions/openapi/path_parameters.go index 03104a59..3723c350 100644 --- a/functions/openapi/path_parameters.go +++ b/functions/openapi/path_parameters.go @@ -67,7 +67,7 @@ func (pp PathParameters) RunRule(nodes []*yaml.Node, context model.RuleFunctionC // check if it's been seen if seenPaths[currentPathNormalized] != "" { res := model.BuildFunctionResultString( - fmt.Sprintf("Paths '%s' and '%s' must not be equivalent, paths must be unique", + fmt.Sprintf("Paths `%s` and `%s` must not be equivalent, paths must be unique", seenPaths[currentPathNormalized], currentPath)) res.StartNode = operationNode res.EndNode = operationNode @@ -85,7 +85,7 @@ func (pp PathParameters) RunRule(nodes []*yaml.Node, context model.RuleFunctionC param := strRx.ReplaceAllString(pathParam, "") if pathElements[param] { res := model.BuildFunctionResultString( - fmt.Sprintf("Path '%s' must not use the parameter '%s' multiple times", + fmt.Sprintf("Path `%s` must not use the parameter `%s` multiple times", currentPath, param)) res.StartNode = operationNode res.EndNode = operationNode @@ -236,7 +236,7 @@ func (pp PathParameters) ensureAllDefinedPathParamsAreUsedInPath(path string, al } } if !foundInElements { - err := fmt.Sprintf("parameter '%s' must be used in path '%s'", param, path) + err := fmt.Sprintf("parameter `%s` must be used in path `%s`", param, path) res := model.BuildFunctionResultString(err) res.StartNode = startNode res.EndNode = endNode @@ -262,7 +262,7 @@ func (pp PathParameters) ensureAllExpectedParamsInPathAreDefined(path string, al } for p := range pathElements { if !pp.segmentExistsInPathParams(p, e, top) { - err := fmt.Sprintf("Operation must define parameter '%s' as expected by path '%s'", p, path) + err := fmt.Sprintf("Operation must define parameter `%s` as expected by path `%s`", p, path) res := model.BuildFunctionResultString(err) res.StartNode = startNode res.EndNode = endNode @@ -309,10 +309,10 @@ func (pp PathParameters) isPathParamNamedAndRequired(in, required, name *yaml.No var errMsg string if currentVerb == "top" { - errMsg = fmt.Sprintf("%s must have 'required' parameter that is set to 'true'", + errMsg = fmt.Sprintf("%s must have `required` parameter that is set to `true`", currentPath) } else { - errMsg = fmt.Sprintf("%s %s must have 'required' parameter that is set to 'true'", + errMsg = fmt.Sprintf("%s %s must have `required` parameter that is set to `true`", currentPath, currentVerb) } diff --git a/functions/openapi/path_parameters_test.go b/functions/openapi/path_parameters_test.go index 3499b7dd..344dfadc 100644 --- a/functions/openapi/path_parameters_test.go +++ b/functions/openapi/path_parameters_test.go @@ -127,7 +127,7 @@ func TestPathParameters_RunRule_TopParameterCheck_MissingRequired(t *testing.T) res := def.RunRule(nodes, ctx) assert.Len(t, res, 1) - assert.Equal(t, "/musical/{melody}/{pizza} must have 'required' parameter that is set to 'true'", res[0].Message) + assert.Equal(t, "/musical/{melody}/{pizza} must have `required` parameter that is set to `true`", res[0].Message) } func TestPathParameters_RunRule_TopParameterCheck_RequiredShouldBeTrue(t *testing.T) { @@ -158,7 +158,7 @@ func TestPathParameters_RunRule_TopParameterCheck_RequiredShouldBeTrue(t *testin res := def.RunRule(nodes, ctx) assert.Len(t, res, 1) - assert.Equal(t, "/musical/{melody}/{pizza} must have 'required' parameter that is set to 'true'", res[0].Message) + assert.Equal(t, "/musical/{melody}/{pizza} must have `required` parameter that is set to `true`", res[0].Message) } func TestPathParameters_RunRule_TopParameterCheck_MultipleDefinitionsOfParam(t *testing.T) { @@ -252,7 +252,7 @@ func TestPathParameters_RunRule_TopParameterCheck_MissingParamDefInOp(t *testing res := def.RunRule(nodes, ctx) assert.Len(t, res, 1) - assert.Equal(t, "Operation must define parameter 'cake' as expected by path '/musical/{melody}/{pizza}/{cake}'", + assert.Equal(t, "Operation must define parameter `cake` as expected by path `/musical/{melody}/{pizza}/{cake}`", res[0].Message) } @@ -305,6 +305,6 @@ paths: res := def.RunRule([]*yaml.Node{&rootNode}, ctx) assert.Len(t, res, 1) - assert.Equal(t, "Operation must define parameter 'cake' as expected by path '/musical/{melody}/{pizza}/{cake}'", + assert.Equal(t, "Operation must define parameter `cake` as expected by path `/musical/{melody}/{pizza}/{cake}`", res[0].Message) } diff --git a/functions/openapi/paths_kebab_case.go b/functions/openapi/paths_kebab_case.go index c04b4517..a1571dac 100644 --- a/functions/openapi/paths_kebab_case.go +++ b/functions/openapi/paths_kebab_case.go @@ -67,7 +67,6 @@ func checkPathCase(path string) (bool, []string) { if !pathKebabCaseRegex.MatchString(seg) { // check if it's a variable, if so, skip if seg == "" { - found = append(found, "!empty segment!") continue } if seg[0] == '{' && seg[len(seg)-1] == '}' { diff --git a/functions/openapi/paths_kebab_case_test.go b/functions/openapi/paths_kebab_case_test.go index 14b033b7..85f73bea 100644 --- a/functions/openapi/paths_kebab_case_test.go +++ b/functions/openapi/paths_kebab_case_test.go @@ -38,7 +38,7 @@ paths: summary: this is also doomed '/ok//ok': get: - summary: should we complain?` + summary: should we complain? nah` path := "$" @@ -56,6 +56,6 @@ paths: def := PathsKebabCase{} res := def.RunRule(nodes, ctx) - assert.Len(t, res, 3) + assert.Len(t, res, 2) } diff --git a/functions/openapi/polymorphic_anyOf.go b/functions/openapi/polymorphic_anyOf.go index 12720756..c1a204c4 100644 --- a/functions/openapi/polymorphic_anyOf.go +++ b/functions/openapi/polymorphic_anyOf.go @@ -34,7 +34,7 @@ func (pm PolymorphicAnyOf) RunRule(nodes []*yaml.Node, context model.RuleFunctio for _, ref := range refs { results = append(results, model.RuleFunctionResult{ - Message: fmt.Sprintf("'anyOf' polymorphic reference: %s", context.Rule.Description), + Message: fmt.Sprintf("`anyOf` polymorphic reference: %s", context.Rule.Description), StartNode: ref.Node, EndNode: ref.Node, Path: ref.Path, diff --git a/functions/openapi/polymorphic_oneOf.go b/functions/openapi/polymorphic_oneOf.go index b88a8a99..ed692f50 100644 --- a/functions/openapi/polymorphic_oneOf.go +++ b/functions/openapi/polymorphic_oneOf.go @@ -31,7 +31,7 @@ func (pm PolymorphicOneOf) RunRule(nodes []*yaml.Node, context model.RuleFunctio for _, ref := range refs { results = append(results, model.RuleFunctionResult{ - Message: fmt.Sprintf("'oneOf' polymorphic reference: %s", context.Rule.Description), + Message: fmt.Sprintf("`oneOf` polymorphic reference: %s", context.Rule.Description), StartNode: ref.Node, EndNode: ref.Node, Path: ref.Path, diff --git a/go.mod b/go.mod index f706b95d..8101a8fa 100644 --- a/go.mod +++ b/go.mod @@ -4,11 +4,12 @@ go 1.16 require ( github.com/alecthomas/chroma v0.10.0 + github.com/dustin/go-humanize v1.0.0 github.com/ghodss/yaml v1.0.0 github.com/gizak/termui/v3 v3.1.0 github.com/json-iterator/go v1.1.12 github.com/mitchellh/mapstructure v1.5.0 - github.com/pb33f/libopenapi v0.0.1 + github.com/pb33f/libopenapi v0.0.4 github.com/pterm/pterm v0.12.42 github.com/santhosh-tekuri/jsonschema/v5 v5.0.0 github.com/spf13/cobra v1.5.0 diff --git a/go.sum b/go.sum index 534edbd1..01cefcc4 100644 --- a/go.sum +++ b/go.sum @@ -29,6 +29,8 @@ github.com/dlclark/regexp2 v1.4.0/go.mod h1:2pZnwuY/m+8K6iRw6wQdMtk+rH5tNGR1i55k github.com/dprotaso/go-yit v0.0.0-20191028211022-135eb7262960/go.mod h1:9HQzr9D/0PGwMEbC3d5AB7oi67+h4TsQqItC1GVYG58= github.com/dprotaso/go-yit v0.0.0-20220510233725-9ba8df137936 h1:PRxIJD8XjimM5aTknUK9w6DHLDox2r2M3DI4i2pnd3w= github.com/dprotaso/go-yit v0.0.0-20220510233725-9ba8df137936/go.mod h1:ttYvX5qlB+mlV1okblJqcSMtR4c52UKxDiX9GRBS8+Q= +github.com/dustin/go-humanize v1.0.0 h1:VSnTsYCnlFHaM2/igO1h6X3HA71jcobQuxemgkq4zYo= +github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4= github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= @@ -102,6 +104,10 @@ github.com/onsi/gomega v1.19.0 h1:4ieX6qQjPP/BfC3mpsAtIGGlxTWPeA3Inl/7DtXw1tw= github.com/onsi/gomega v1.19.0/go.mod h1:LY+I3pBVzYsTBU1AnDwOSxaYi9WoWiqgwooUqq9yPro= github.com/pb33f/libopenapi v0.0.1 h1:yyUWh6Vkp0mYDnR0WIXjoUWjNFTw5bOvWj3ny49Lmps= github.com/pb33f/libopenapi v0.0.1/go.mod h1:UcUNPQcwq4ojgQgthV+zbeUs25lhDlD4bM9Da8n2vdU= +github.com/pb33f/libopenapi v0.0.2 h1:H/OKd3ROvQ7/91ymgD9ohvewosjtMyEN3RGynvzmfdE= +github.com/pb33f/libopenapi v0.0.2/go.mod h1:UcUNPQcwq4ojgQgthV+zbeUs25lhDlD4bM9Da8n2vdU= +github.com/pb33f/libopenapi v0.0.4 h1:FQeMETHFtW6+5R3swxT6CLvME1qLvfdsSEgjAYEGGsg= +github.com/pb33f/libopenapi v0.0.4/go.mod h1:UcUNPQcwq4ojgQgthV+zbeUs25lhDlD4bM9Da8n2vdU= github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= diff --git a/html-report/ui/build/static/js/vacuumReport.js b/html-report/ui/build/static/js/vacuumReport.js index 69348071..73963529 100644 --- a/html-report/ui/build/static/js/vacuumReport.js +++ b/html-report/ui/build/static/js/vacuumReport.js @@ -1,5 +1,5 @@ /*! For license information please see vacuumReport.js.LICENSE.txt */ -(()=>{"use strict";var e={408:(e,t,o)=>{o.d(t,{Z:()=>l});var r=o(81),i=o.n(r),a=o(645),n=o.n(a)()(i());n.push([e.id,':root{--global-font-size:15px;--global-line-height:1.4em;--global-space:10px;--font-stack:Menlo,Monaco,Lucida Console,Liberation Mono,DejaVu Sans Mono,Bitstream Vera Sans Mono,Courier New,monospace,serif;--mono-font-stack:Menlo,Monaco,Lucida Console,Liberation Mono,DejaVu Sans Mono,Bitstream Vera Sans Mono,Courier New,monospace,serif;--background-color:#fff;--page-width:60em;--font-color:#151515;--invert-font-color:#fff;--primary-color:#1a95e0;--secondary-color:#727578;--error-color:#d20962;--progress-bar-background:#727578;--progress-bar-fill:#151515;--code-bg-color:#e8eff2;--input-style:solid;--display-h1-decoration:none}*{box-sizing:border-box;text-rendering:geometricPrecision}::-moz-selection{background:var(--primary-color);color:var(--invert-font-color)}::selection{background:var(--primary-color);color:var(--invert-font-color)}body{font-size:var(--global-font-size);color:var(--font-color);line-height:var(--global-line-height);margin:0;font-family:var(--font-stack);word-wrap:break-word;background-color:var(--background-color)}.logo,h1,h2,h3,h4,h5,h6{line-height:var(--global-line-height)}a{cursor:pointer;color:var(--primary-color);text-decoration:none}a:hover{background-color:var(--primary-color);color:var(--invert-font-color)}em{font-size:var(--global-font-size);font-style:italic;font-family:var(--font-stack);color:var(--font-color)}blockquote,code,em,strong{line-height:var(--global-line-height)}.logo,blockquote,code,footer,h1,h2,h3,h4,h5,h6,header,li,ol,p,section,ul{float:none;margin:0;padding:0}.logo,blockquote,h1,ol,p,ul{margin-top:calc(var(--global-space) * 2);margin-bottom:calc(var(--global-space) * 2)}.logo,h1{position:relative;display:inline-block;display:table-cell;padding:calc(var(--global-space) * 2) 0 calc(var(--global-space) * 2);margin:0;overflow:hidden;font-weight:600}h1::after{content:"====================================================================================================";position:absolute;bottom:5px;left:0;display:var(--display-h1-decoration)}.logo+*,h1+*{margin-top:0}h2,h3,h4,h5,h6{position:relative;margin-bottom:var(--global-line-height);font-weight:600}blockquote{position:relative;padding-left:calc(var(--global-space) * 2);padding-left:2ch;overflow:hidden}blockquote::after{content:">\\A>\\A>\\A>\\A>\\A>\\A>\\A>\\A>\\A>\\A>\\A>\\A>\\A>\\A>\\A>\\A>\\A>\\A>\\A>\\A>\\A>\\A>\\A>\\A>\\A>\\A>\\A>\\A>\\A>\\A>\\A>\\A>\\A>\\A>\\A>\\A>\\A>\\A>\\A>\\A>\\A>\\A>\\A>\\A>\\A>\\A>\\A>\\A>\\A>\\A>\\A>\\A>\\A>\\A>\\A>\\A>\\A>\\A>\\A>\\A>\\A>\\A>\\A>\\A>\\A>\\A>\\A>\\A>\\A>\\A>\\A>\\A>\\A>\\A>\\A>\\A>\\A>\\A>\\A>\\A>\\A>\\A>\\A>\\A>\\A>\\A>\\A>\\A>\\A>\\A>\\A>\\A>\\A>\\A>\\A>\\A>\\A>\\A>\\A>";white-space:pre;position:absolute;top:0;left:0;line-height:var(--global-line-height);color:#9ca2ab}code{font-weight:inherit;background-color:var(--code-bg-color);font-family:var(--mono-font-stack)}code::after,code::before{content:"`";display:inline}pre code::after,pre code::before{content:""}pre{display:block;word-break:break-all;word-wrap:break-word;color:var(--secondary-color);background-color:var(--background-color);border:1px solid var(--secondary-color);padding:var(--global-space);white-space:pre-wrap;white-space:-moz-pre-wrap;white-space:-pre-wrap;white-space:-o-pre-wrap}pre code{overflow-x:scroll;padding:0;margin:0;display:inline-block;min-width:100%;font-family:var(--mono-font-stack)}.terminal .logo,.terminal blockquote,.terminal code,.terminal h1,.terminal h2,.terminal h3,.terminal h4,.terminal h5,.terminal h6,.terminal strong{font-size:var(--global-font-size);font-style:normal;font-family:var(--font-stack);color:var(--font-color)}.terminal-prompt{position:relative;white-space:nowrap}.terminal-prompt::before{content:"> "}.terminal-prompt::after{content:"";-webkit-animation:cursor .8s infinite;animation:cursor .8s infinite;background:var(--primary-color);border-radius:0;display:inline-block;height:1em;margin-left:.2em;width:3px;bottom:-2px;position:relative}@-webkit-keyframes cursor{0%{opacity:0}50%{opacity:1}to{opacity:0}}@keyframes cursor{0%{opacity:0}50%{opacity:1}to{opacity:0}}li,li>ul>li{position:relative;display:block;padding-left:calc(var(--global-space) * 2)}nav>ul>li{padding-left:0}li::after{position:absolute;top:0;left:0}ul>li::after{content:"-"}nav ul>li::after{content:""}ol li::before{content:counters(item, ".") ". ";counter-increment:item}ol ol li::before{content:counters(item, ".") " ";counter-increment:item}.terminal-menu li::after,.terminal-menu li::before{display:none}ol{counter-reset:item}ol li:nth-child(n+10)::after{left:-7px}ol ol{margin-top:0;margin-bottom:0}.terminal-menu{width:100%}.terminal-nav{display:flex;flex-direction:column;align-items:flex-start}ul ul{margin-top:0;margin-bottom:0}.terminal-menu ul{list-style-type:none;padding:0!important;display:flex;flex-direction:column;width:100%;flex-grow:1;font-size:var(--global-font-size);margin-top:0}.terminal-menu li{display:flex;margin:0 0 .5em 0;padding:0}ol.terminal-toc li{border-bottom:1px dotted var(--secondary-color);padding:0;margin-bottom:15px}.terminal-menu li:last-child{margin-bottom:0}ol.terminal-toc li a{margin:4px 4px 4px 0;background:var(--background-color);position:relative;top:6px;text-align:left;padding-right:4px}.terminal-menu li a:not(.btn){text-decoration:none;display:block;width:100%;border:none;color:var(--secondary-color)}.terminal-menu li a.active{color:var(--font-color)}.terminal-menu li a:hover{background:0 0;color:inherit}ol.terminal-toc li::before{content:counters(item, ".") ". ";counter-increment:item;position:absolute;right:0;background:var(--background-color);padding:4px 0 4px 4px;bottom:-8px}ol.terminal-toc li a:hover{background:var(--primary-color);color:var(--invert-font-color)}hr{position:relative;overflow:hidden;margin:calc(var(--global-space) * 4) 0;border:0;border-bottom:1px dashed var(--secondary-color)}p{margin:0 0 var(--global-line-height);color:var(--global-font-color)}.container{max-width:var(--page-width)}.container,.container-fluid{margin:0 auto;padding:0 calc(var(--global-space) * 2)}img{width:100%}.progress-bar{height:8px;background-color:var(--progress-bar-background);margin:12px 0}.progress-bar.progress-bar-show-percent{margin-top:38px}.progress-bar-filled{background-color:var(--progress-bar-fill);height:100%;transition:width .3s ease;position:relative;width:0}.progress-bar-filled::before{content:"";border:6px solid transparent;border-top-color:var(--progress-bar-fill);position:absolute;top:-12px;right:-6px}.progress-bar-filled::after{color:var(--progress-bar-fill);content:attr(data-filled);display:block;font-size:12px;white-space:nowrap;position:absolute;border:6px solid transparent;top:-38px;right:0;transform:translateX(50%)}.progress-bar-no-arrow>.progress-bar-filled::after,.progress-bar-no-arrow>.progress-bar-filled::before{content:"";display:none;visibility:hidden;opacity:0}table{width:100%;border-collapse:collapse;margin:var(--global-line-height) 0;color:var(--font-color);font-size:var(--global-font-size)}table td,table th{vertical-align:top;border:1px solid var(--font-color);line-height:var(--global-line-height);padding:calc(var(--global-space)/ 2);font-size:1em}table thead th{font-size:1em}table tfoot tr th{font-weight:500}table caption{font-size:1em;margin:0 0 1em 0}table tbody td:first-child{font-weight:700;color:var(--secondary-color)}.form{width:100%}fieldset{border:1px solid var(--font-color);padding:1em}label{font-size:1em;color:var(--font-color)}input[type=email],input[type=number],input[type=password],input[type=search],input[type=text]{border:1px var(--input-style) var(--font-color);width:100%;padding:.7em .5em;font-size:1em;font-family:var(--font-stack);-webkit-appearance:none;border-radius:0}input[type=email]:active,input[type=email]:focus,input[type=number]:active,input[type=number]:focus,input[type=password]:active,input[type=password]:focus,input[type=search]:active,input[type=search]:focus,input[type=text]:active,input[type=text]:focus{outline:0;-webkit-appearance:none;border:1px solid var(--font-color)}input[type=email]:not(:placeholder-shown):invalid,input[type=number]:not(:placeholder-shown):invalid,input[type=password]:not(:placeholder-shown):invalid,input[type=search]:not(:placeholder-shown):invalid,input[type=text]:not(:placeholder-shown):invalid{border-color:var(--error-color)}input,textarea{color:var(--font-color);background-color:var(--background-color)}input::-webkit-input-placeholder,textarea::-webkit-input-placeholder{color:var(--secondary-color)!important;opacity:1}input::-moz-placeholder,textarea::-moz-placeholder{color:var(--secondary-color)!important;opacity:1}input:-ms-input-placeholder,textarea:-ms-input-placeholder{color:var(--secondary-color)!important;opacity:1}input::-ms-input-placeholder,textarea::-ms-input-placeholder{color:var(--secondary-color)!important;opacity:1}input::placeholder,textarea::placeholder{color:var(--secondary-color)!important;opacity:1}textarea{height:auto;width:100%;resize:none;border:1px var(--input-style) var(--font-color);padding:.5em;font-size:1em;font-family:var(--font-stack);-webkit-appearance:none;border-radius:0}textarea:focus{outline:0;-webkit-appearance:none;border:1px solid var(--font-color)}textarea:not(:placeholder-shown):invalid{border-color:var(--error-color)}input:-webkit-autofill,input:-webkit-autofill:focus textarea:-webkit-autofill,input:-webkit-autofill:hover,select:-webkit-autofill,select:-webkit-autofill:focus,select:-webkit-autofill:hover,textarea:-webkit-autofill:hover textarea:-webkit-autofill:focus{border:1px solid var(--font-color);-webkit-text-fill-color:var(--font-color);box-shadow:0 0 0 1000px var(--invert-font-color) inset;-webkit-box-shadow:0 0 0 1000px var(--invert-font-color) inset;transition:background-color 5000s ease-in-out 0s}.form-group{margin-bottom:var(--global-line-height);overflow:auto}.btn{border-style:solid;border-width:1px;display:inline-flex;align-items:center;justify-content:center;cursor:pointer;outline:0;padding:.65em 2em;font-size:1em;font-family:inherit;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;position:relative;z-index:1}.btn:active{box-shadow:none}.btn.btn-ghost{border-color:var(--font-color);color:var(--font-color);background-color:transparent}.btn.btn-ghost:focus,.btn.btn-ghost:hover{border-color:var(--tertiary-color);color:var(--tertiary-color);z-index:2}.btn.btn-ghost:hover{background-color:transparent}.btn-block{width:100%;display:flex}.btn-default{background-color:var(--font-color);border-color:var(--invert-font-color);color:var(--invert-font-color)}.btn-default:focus:not(.btn-ghost),.btn-default:hover{background-color:var(--secondary-color);color:var(--invert-font-color)}.btn-default.btn-ghost:focus,.btn-default.btn-ghost:hover{border-color:var(--secondary-color);color:var(--secondary-color);z-index:2}.btn-error{color:var(--invert-font-color);background-color:var(--error-color);border:1px solid var(--error-color)}.btn-error:focus:not(.btn-ghost),.btn-error:hover{background-color:var(--error-color);border-color:var(--error-color)}.btn-error.btn-ghost{border-color:var(--error-color);color:var(--error-color)}.btn-error.btn-ghost:focus,.btn-error.btn-ghost:hover{border-color:var(--error-color);color:var(--error-color);z-index:2}.btn-primary{color:var(--invert-font-color);background-color:var(--primary-color);border:1px solid var(--primary-color)}.btn-primary:focus:not(.btn-ghost),.btn-primary:hover{background-color:var(--primary-color);border-color:var(--primary-color)}.btn-primary.btn-ghost{border-color:var(--primary-color);color:var(--primary-color)}.btn-primary.btn-ghost:focus,.btn-primary.btn-ghost:hover{border-color:var(--primary-color);color:var(--primary-color);z-index:2}.btn-small{padding:.5em 1.3em!important;font-size:.9em!important}.btn-group{overflow:auto}.btn-group .btn{float:left}.btn-group .btn-ghost:not(:first-child){margin-left:-1px}.terminal-card{border:1px solid var(--secondary-color)}.terminal-card>header{color:var(--invert-font-color);text-align:center;background-color:var(--secondary-color);padding:.5em 0}.terminal-card>div:first-of-type{padding:var(--global-space)}.terminal-timeline{position:relative;padding-left:70px}.terminal-timeline::before{content:\' \';background:var(--secondary-color);display:inline-block;position:absolute;left:35px;width:2px;height:100%;z-index:400}.terminal-timeline .terminal-card{margin-bottom:25px}.terminal-timeline .terminal-card::before{content:\' \';background:var(--invert-font-color);border:2px solid var(--secondary-color);display:inline-block;position:absolute;margin-top:25px;left:26px;width:15px;height:15px;z-index:400}.terminal-alert{color:var(--font-color);padding:1em;border:1px solid var(--font-color);margin-bottom:var(--global-space)}.terminal-alert-error{color:var(--error-color);border-color:var(--error-color)}.terminal-alert-primary{color:var(--primary-color);border-color:var(--primary-color)}@media screen and (max-width:960px){label{display:block;width:100%}pre::-webkit-scrollbar{height:3px}}@media screen and (max-width:480px){form{width:100%}}@media only screen and (min-width:30em){.terminal-nav{flex-direction:row;align-items:center}.terminal-menu ul{flex-direction:row;justify-items:flex-end;align-items:center;justify-content:flex-end;margin-top:calc(var(--global-space) * 2)}.terminal-menu li{margin:0;margin-right:2em}.terminal-menu li:last-child{margin-right:0}}.terminal-media:not(:last-child){margin-bottom:1.25rem}.terminal-media-left{padding-right:var(--global-space)}.terminal-media-left,.terminal-media-right{display:table-cell;vertical-align:top}.terminal-media-right{padding-left:var(--global-space)}.terminal-media-body{display:table-cell;vertical-align:top}.terminal-media-heading{font-size:1em;font-weight:700}.terminal-media-content{margin-top:.3rem}.terminal-placeholder{background-color:var(--secondary-color);text-align:center;color:var(--font-color);font-size:1rem;border:1px solid var(--secondary-color)}figure>img{padding:0}.terminal-avatarholder{width:calc(var(--global-space) * 5);height:calc(var(--global-space) * 5)}.terminal-avatarholder img{padding:0}figure{margin:0}figure>figcaption{color:var(--secondary-color);text-align:center}.hljs{display:block;overflow-x:auto;padding:.5em;background:var(--block-background-color);color:var(--font-color)}.hljs-comment,.hljs-quote{color:var(--secondary-color)}.hljs-variable{color:var(--font-color)}.hljs-built_in,.hljs-keyword,.hljs-name,.hljs-selector-tag,.hljs-tag{color:var(--primary-color)}.hljs-addition,.hljs-attribute,.hljs-literal,.hljs-section,.hljs-string,.hljs-template-tag,.hljs-template-variable,.hljs-title,.hljs-type{color:var(--secondary-color)}.hljs-string{color:var(--secondary-color)}.hljs-deletion,.hljs-meta,.hljs-selector-attr,.hljs-selector-pseudo{color:var(--primary-color)}.hljs-doctag{color:var(--secondary-color)}.hljs-attr{color:var(--primary-color)}.hljs-bullet,.hljs-link,.hljs-symbol{color:var(--primary-color)}.hljs-emphasis{font-style:italic}.hljs-strong{font-weight:700}',""]);const l=n},645:e=>{e.exports=function(e){var t=[];return t.toString=function(){return this.map((function(t){var o="",r=void 0!==t[5];return t[4]&&(o+="@supports (".concat(t[4],") {")),t[2]&&(o+="@media ".concat(t[2]," {")),r&&(o+="@layer".concat(t[5].length>0?" ".concat(t[5]):""," {")),o+=e(t),r&&(o+="}"),t[2]&&(o+="}"),t[4]&&(o+="}"),o})).join("")},t.i=function(e,o,r,i,a){"string"==typeof e&&(e=[[null,e,void 0]]);var n={};if(r)for(var l=0;l0?" ".concat(d[5]):""," {").concat(d[1],"}")),d[5]=a),o&&(d[2]?(d[1]="@media ".concat(d[2]," {").concat(d[1],"}"),d[2]=o):d[2]=o),i&&(d[4]?(d[1]="@supports (".concat(d[4],") {").concat(d[1],"}"),d[4]=i):d[4]="".concat(i)),t.push(d))}},t}},81:e=>{e.exports=function(e){return e[1]}},379:e=>{var t=[];function o(e){for(var o=-1,r=0;r{var t={};e.exports=function(e,o){var r=function(e){if(void 0===t[e]){var o=document.querySelector(e);if(window.HTMLIFrameElement&&o instanceof window.HTMLIFrameElement)try{o=o.contentDocument.head}catch(e){o=null}t[e]=o}return t[e]}(e);if(!r)throw new Error("Couldn't find a style target. This probably means that the value for the 'insert' parameter is invalid.");r.appendChild(o)}},216:e=>{e.exports=function(e){var t=document.createElement("style");return e.setAttributes(t,e.attributes),e.insert(t,e.options),t}},565:(e,t,o)=>{e.exports=function(e){var t=o.nc;t&&e.setAttribute("nonce",t)}},795:e=>{e.exports=function(e){var t=e.insertStyleElement(e);return{update:function(o){!function(e,t,o){var r="";o.supports&&(r+="@supports (".concat(o.supports,") {")),o.media&&(r+="@media ".concat(o.media," {"));var i=void 0!==o.layer;i&&(r+="@layer".concat(o.layer.length>0?" ".concat(o.layer):""," {")),r+=o.css,i&&(r+="}"),o.media&&(r+="}"),o.supports&&(r+="}");var a=o.sourceMap;a&&"undefined"!=typeof btoa&&(r+="\n/*# sourceMappingURL=data:application/json;base64,".concat(btoa(unescape(encodeURIComponent(JSON.stringify(a))))," */")),t.styleTagTransform(r,e,t.options)}(t,e,o)},remove:function(){!function(e){if(null===e.parentNode)return!1;e.parentNode.removeChild(e)}(t)}}}},589:e=>{e.exports=function(e,t){if(t.styleSheet)t.styleSheet.cssText=e;else{for(;t.firstChild;)t.removeChild(t.firstChild);t.appendChild(document.createTextNode(e))}}}},t={};function o(r){var i=t[r];if(void 0!==i)return i.exports;var a=t[r]={id:r,exports:{}};return e[r](a,a.exports,o),a.exports}o.n=e=>{var t=e&&e.__esModule?()=>e.default:()=>e;return o.d(t,{a:t}),t},o.d=(e,t)=>{for(var r in t)o.o(t,r)&&!o.o(e,r)&&Object.defineProperty(e,r,{enumerable:!0,get:t[r]})},o.o=(e,t)=>Object.prototype.hasOwnProperty.call(e,t),o.nc=void 0,(()=>{var e=o(379),t=o.n(e),r=o(795),i=o.n(r),a=o(569),n=o.n(a),l=o(565),s=o.n(l),c=o(216),d=o.n(c),p=o(589),h=o.n(p),u=o(408),v={};v.styleTagTransform=h(),v.setAttributes=s(),v.insert=n().bind(null,"head"),v.domAPI=i(),v.insertStyleElement=d(),t()(u.Z,v),u.Z&&u.Z.locals&&u.Z.locals;const m=window.ShadowRoot&&(void 0===window.ShadyCSS||window.ShadyCSS.nativeShadow)&&"adoptedStyleSheets"in Document.prototype&&"replace"in CSSStyleSheet.prototype,f=Symbol(),g=new Map;class b{constructor(e,t){if(this._$cssResult$=!0,t!==f)throw Error("CSSResult is not constructable. Use `unsafeCSS` or `css` instead.");this.cssText=e}get styleSheet(){let e=g.get(this.cssText);return m&&void 0===e&&(g.set(this.cssText,e=new CSSStyleSheet),e.replaceSync(this.cssText)),e}toString(){return this.cssText}}const y=(e,...t)=>{const o=1===e.length?e[0]:t.reduce(((t,o,r)=>t+(e=>{if(!0===e._$cssResult$)return e.cssText;if("number"==typeof e)return e;throw Error("Value passed to 'css' function must be a 'css' function result: "+e+". Use 'unsafeCSS' to pass non-literal values, but take care to ensure page security.")})(o)+e[r+1]),e[0]);return new b(o,f)},w=m?e=>e:e=>e instanceof CSSStyleSheet?(e=>{let t="";for(const o of e.cssRules)t+=o.cssText;return(e=>new b("string"==typeof e?e:e+"",f))(t)})(e):e;var x;const A=window.trustedTypes,k=A?A.emptyScript:"",$=window.reactiveElementPolyfillSupport,_={toAttribute(e,t){switch(t){case Boolean:e=e?k:null;break;case Object:case Array:e=null==e?e:JSON.stringify(e)}return e},fromAttribute(e,t){let o=e;switch(t){case Boolean:o=null!==e;break;case Number:o=null===e?null:Number(e);break;case Object:case Array:try{o=JSON.parse(e)}catch(e){o=null}}return o}},S=(e,t)=>t!==e&&(t==t||e==e),E={attribute:!0,type:String,converter:_,reflect:!1,hasChanged:S};class C extends HTMLElement{constructor(){super(),this._$Et=new Map,this.isUpdatePending=!1,this.hasUpdated=!1,this._$Ei=null,this.o()}static addInitializer(e){var t;null!==(t=this.l)&&void 0!==t||(this.l=[]),this.l.push(e)}static get observedAttributes(){this.finalize();const e=[];return this.elementProperties.forEach(((t,o)=>{const r=this._$Eh(o,t);void 0!==r&&(this._$Eu.set(r,o),e.push(r))})),e}static createProperty(e,t=E){if(t.state&&(t.attribute=!1),this.finalize(),this.elementProperties.set(e,t),!t.noAccessor&&!this.prototype.hasOwnProperty(e)){const o="symbol"==typeof e?Symbol():"__"+e,r=this.getPropertyDescriptor(e,o,t);void 0!==r&&Object.defineProperty(this.prototype,e,r)}}static getPropertyDescriptor(e,t,o){return{get(){return this[t]},set(r){const i=this[e];this[t]=r,this.requestUpdate(e,i,o)},configurable:!0,enumerable:!0}}static getPropertyOptions(e){return this.elementProperties.get(e)||E}static finalize(){if(this.hasOwnProperty("finalized"))return!1;this.finalized=!0;const e=Object.getPrototypeOf(this);if(e.finalize(),this.elementProperties=new Map(e.elementProperties),this._$Eu=new Map,this.hasOwnProperty("properties")){const e=this.properties,t=[...Object.getOwnPropertyNames(e),...Object.getOwnPropertySymbols(e)];for(const o of t)this.createProperty(o,e[o])}return this.elementStyles=this.finalizeStyles(this.styles),!0}static finalizeStyles(e){const t=[];if(Array.isArray(e)){const o=new Set(e.flat(1/0).reverse());for(const e of o)t.unshift(w(e))}else void 0!==e&&t.push(w(e));return t}static _$Eh(e,t){const o=t.attribute;return!1===o?void 0:"string"==typeof o?o:"string"==typeof e?e.toLowerCase():void 0}o(){var e;this._$Ep=new Promise((e=>this.enableUpdating=e)),this._$AL=new Map,this._$Em(),this.requestUpdate(),null===(e=this.constructor.l)||void 0===e||e.forEach((e=>e(this)))}addController(e){var t,o;(null!==(t=this._$Eg)&&void 0!==t?t:this._$Eg=[]).push(e),void 0!==this.renderRoot&&this.isConnected&&(null===(o=e.hostConnected)||void 0===o||o.call(e))}removeController(e){var t;null===(t=this._$Eg)||void 0===t||t.splice(this._$Eg.indexOf(e)>>>0,1)}_$Em(){this.constructor.elementProperties.forEach(((e,t)=>{this.hasOwnProperty(t)&&(this._$Et.set(t,this[t]),delete this[t])}))}createRenderRoot(){var e;const t=null!==(e=this.shadowRoot)&&void 0!==e?e:this.attachShadow(this.constructor.shadowRootOptions);return((e,t)=>{m?e.adoptedStyleSheets=t.map((e=>e instanceof CSSStyleSheet?e:e.styleSheet)):t.forEach((t=>{const o=document.createElement("style"),r=window.litNonce;void 0!==r&&o.setAttribute("nonce",r),o.textContent=t.cssText,e.appendChild(o)}))})(t,this.constructor.elementStyles),t}connectedCallback(){var e;void 0===this.renderRoot&&(this.renderRoot=this.createRenderRoot()),this.enableUpdating(!0),null===(e=this._$Eg)||void 0===e||e.forEach((e=>{var t;return null===(t=e.hostConnected)||void 0===t?void 0:t.call(e)}))}enableUpdating(e){}disconnectedCallback(){var e;null===(e=this._$Eg)||void 0===e||e.forEach((e=>{var t;return null===(t=e.hostDisconnected)||void 0===t?void 0:t.call(e)}))}attributeChangedCallback(e,t,o){this._$AK(e,o)}_$ES(e,t,o=E){var r,i;const a=this.constructor._$Eh(e,o);if(void 0!==a&&!0===o.reflect){const n=(null!==(i=null===(r=o.converter)||void 0===r?void 0:r.toAttribute)&&void 0!==i?i:_.toAttribute)(t,o.type);this._$Ei=e,null==n?this.removeAttribute(a):this.setAttribute(a,n),this._$Ei=null}}_$AK(e,t){var o,r,i;const a=this.constructor,n=a._$Eu.get(e);if(void 0!==n&&this._$Ei!==n){const e=a.getPropertyOptions(n),l=e.converter,s=null!==(i=null!==(r=null===(o=l)||void 0===o?void 0:o.fromAttribute)&&void 0!==r?r:"function"==typeof l?l:null)&&void 0!==i?i:_.fromAttribute;this._$Ei=n,this[n]=s(t,e.type),this._$Ei=null}}requestUpdate(e,t,o){let r=!0;void 0!==e&&(((o=o||this.constructor.getPropertyOptions(e)).hasChanged||S)(this[e],t)?(this._$AL.has(e)||this._$AL.set(e,t),!0===o.reflect&&this._$Ei!==e&&(void 0===this._$EC&&(this._$EC=new Map),this._$EC.set(e,o))):r=!1),!this.isUpdatePending&&r&&(this._$Ep=this._$E_())}async _$E_(){this.isUpdatePending=!0;try{await this._$Ep}catch(e){Promise.reject(e)}const e=this.scheduleUpdate();return null!=e&&await e,!this.isUpdatePending}scheduleUpdate(){return this.performUpdate()}performUpdate(){var e;if(!this.isUpdatePending)return;this.hasUpdated,this._$Et&&(this._$Et.forEach(((e,t)=>this[t]=e)),this._$Et=void 0);let t=!1;const o=this._$AL;try{t=this.shouldUpdate(o),t?(this.willUpdate(o),null===(e=this._$Eg)||void 0===e||e.forEach((e=>{var t;return null===(t=e.hostUpdate)||void 0===t?void 0:t.call(e)})),this.update(o)):this._$EU()}catch(e){throw t=!1,this._$EU(),e}t&&this._$AE(o)}willUpdate(e){}_$AE(e){var t;null===(t=this._$Eg)||void 0===t||t.forEach((e=>{var t;return null===(t=e.hostUpdated)||void 0===t?void 0:t.call(e)})),this.hasUpdated||(this.hasUpdated=!0,this.firstUpdated(e)),this.updated(e)}_$EU(){this._$AL=new Map,this.isUpdatePending=!1}get updateComplete(){return this.getUpdateComplete()}getUpdateComplete(){return this._$Ep}shouldUpdate(e){return!0}update(e){void 0!==this._$EC&&(this._$EC.forEach(((e,t)=>this._$ES(t,this[t],e))),this._$EC=void 0),this._$EU()}updated(e){}firstUpdated(e){}}var z;C.finalized=!0,C.elementProperties=new Map,C.elementStyles=[],C.shadowRootOptions={mode:"open"},null==$||$({ReactiveElement:C}),(null!==(x=globalThis.reactiveElementVersions)&&void 0!==x?x:globalThis.reactiveElementVersions=[]).push("1.3.2");const j=globalThis.trustedTypes,P=j?j.createPolicy("lit-html",{createHTML:e=>e}):void 0,R=`lit$${(Math.random()+"").slice(9)}$`,N="?"+R,L=`<${N}>`,O=document,T=(e="")=>O.createComment(e),M=e=>null===e||"object"!=typeof e&&"function"!=typeof e,U=Array.isArray,H=/<(?:(!--|\/[^a-zA-Z])|(\/?[a-zA-Z][^>\s]*)|(\/?$))/g,I=/-->/g,q=/>/g,D=/>|[ \n \r](?:([^\s"'>=/]+)([ \n \r]*=[ \n \r]*(?:[^ \n \r"'`<>=]|("|')|))|$)/g,V=/'/g,B=/"/g,F=/^(?:script|style|textarea|title)$/i,G=e=>(t,...o)=>({_$litType$:e,strings:t,values:o}),K=G(1),W=(G(2),Symbol.for("lit-noChange")),Z=Symbol.for("lit-nothing"),J=new WeakMap,X=O.createTreeWalker(O,129,null,!1),Q=(e,t)=>{const o=e.length-1,r=[];let i,a=2===t?"":"",n=H;for(let t=0;t"===s[0]?(n=null!=i?i:H,c=-1):void 0===s[1]?c=-2:(c=n.lastIndex-s[2].length,l=s[1],n=void 0===s[3]?D:'"'===s[3]?B:V):n===B||n===V?n=D:n===I||n===q?n=H:(n=D,i=void 0);const p=n===D&&e[t+1].startsWith("/>")?" ":"";a+=n===H?o+L:c>=0?(r.push(l),o.slice(0,c)+"$lit$"+o.slice(c)+R+p):o+R+(-2===c?(r.push(void 0),t):p)}const l=a+(e[o]||"")+(2===t?"":"");if(!Array.isArray(e)||!e.hasOwnProperty("raw"))throw Error("invalid template strings array");return[void 0!==P?P.createHTML(l):l,r]};class Y{constructor({strings:e,_$litType$:t},o){let r;this.parts=[];let i=0,a=0;const n=e.length-1,l=this.parts,[s,c]=Q(e,t);if(this.el=Y.createElement(s,o),X.currentNode=this.el.content,2===t){const e=this.el.content,t=e.firstChild;t.remove(),e.append(...t.childNodes)}for(;null!==(r=X.nextNode())&&l.length0){r.textContent=j?j.emptyScript:"";for(let o=0;o{var t;return U(e)||"function"==typeof(null===(t=e)||void 0===t?void 0:t[Symbol.iterator])})(e)?this.S(e):this.$(e)}M(e,t=this._$AB){return this._$AA.parentNode.insertBefore(e,t)}k(e){this._$AH!==e&&(this._$AR(),this._$AH=this.M(e))}$(e){this._$AH!==Z&&M(this._$AH)?this._$AA.nextSibling.data=e:this.k(O.createTextNode(e)),this._$AH=e}T(e){var t;const{values:o,_$litType$:r}=e,i="number"==typeof r?this._$AC(e):(void 0===r.el&&(r.el=Y.createElement(r.h,this.options)),r);if((null===(t=this._$AH)||void 0===t?void 0:t._$AD)===i)this._$AH.m(o);else{const e=new te(i,this),t=e.p(this.options);e.m(o),this.k(t),this._$AH=e}}_$AC(e){let t=J.get(e.strings);return void 0===t&&J.set(e.strings,t=new Y(e)),t}S(e){U(this._$AH)||(this._$AH=[],this._$AR());const t=this._$AH;let o,r=0;for(const i of e)r===t.length?t.push(o=new oe(this.M(T()),this.M(T()),this,this.options)):o=t[r],o._$AI(i),r++;r2||""!==o[0]||""!==o[1]?(this._$AH=Array(o.length-1).fill(new String),this.strings=o):this._$AH=Z}get tagName(){return this.element.tagName}get _$AU(){return this._$AM._$AU}_$AI(e,t=this,o,r){const i=this.strings;let a=!1;if(void 0===i)e=ee(this,e,t,0),a=!M(e)||e!==this._$AH&&e!==W,a&&(this._$AH=e);else{const r=e;let n,l;for(e=i[0],n=0;n{var r,i;const a=null!==(r=null==o?void 0:o.renderBefore)&&void 0!==r?r:t;let n=a._$litPart$;if(void 0===n){const e=null!==(i=null==o?void 0:o.renderBefore)&&void 0!==i?i:null;a._$litPart$=n=new oe(t.insertBefore(T(),e),e,void 0,null!=o?o:{})}return n._$AI(e),n})(t,this.renderRoot,this.renderOptions)}connectedCallback(){var e;super.connectedCallback(),null===(e=this._$Dt)||void 0===e||e.setConnected(!0)}disconnectedCallback(){var e;super.disconnectedCallback(),null===(e=this._$Dt)||void 0===e||e.setConnected(!1)}render(){return W}}he.finalized=!0,he._$litElement$=!0,null===(de=globalThis.litElementHydrateSupport)||void 0===de||de.call(globalThis,{LitElement:he});const ue=globalThis.litElementPolyfillSupport;null==ue||ue({LitElement:he}),(null!==(pe=globalThis.litElementVersions)&&void 0!==pe?pe:globalThis.litElementVersions=[]).push("3.2.0");const ve=e=>t=>"function"==typeof t?((e,t)=>(window.customElements.define(e,t),t))(e,t):((e,t)=>{const{kind:o,elements:r}=t;return{kind:o,elements:r,finisher(t){window.customElements.define(e,t)}}})(e,t),me=(e,t)=>"method"===t.kind&&t.descriptor&&!("value"in t.descriptor)?{...t,finisher(o){o.createProperty(t.key,e)}}:{kind:"field",key:Symbol(),placement:"own",descriptor:{},originalKey:t.key,initializer(){"function"==typeof t.initializer&&(this[t.key]=t.initializer.call(this))},finisher(o){o.createProperty(t.key,e)}};function fe(e){return(t,o)=>void 0!==o?((e,t,o)=>{t.constructor.createProperty(o,e)})(e,t,o):me(e,t)}var ge;null===(ge=window.HTMLSlotElement)||void 0===ge||ge.prototype.assignedElements;class be extends he{get _slottedChildren(){const e=this.shadowRoot.querySelector("slot");if(e)return e.assignedElements({flatten:!0})}}const ye="categoryActivated",we=y` +(()=>{"use strict";var e={408:(e,t,o)=>{o.d(t,{Z:()=>l});var r=o(81),i=o.n(r),a=o(645),n=o.n(a)()(i());n.push([e.id,':root{--global-font-size:15px;--global-line-height:1.4em;--global-space:10px;--font-stack:Menlo,Monaco,Lucida Console,Liberation Mono,DejaVu Sans Mono,Bitstream Vera Sans Mono,Courier New,monospace,serif;--mono-font-stack:Menlo,Monaco,Lucida Console,Liberation Mono,DejaVu Sans Mono,Bitstream Vera Sans Mono,Courier New,monospace,serif;--background-color:#fff;--page-width:60em;--font-color:#151515;--invert-font-color:#fff;--primary-color:#1a95e0;--secondary-color:#727578;--error-color:#d20962;--progress-bar-background:#727578;--progress-bar-fill:#151515;--code-bg-color:#e8eff2;--input-style:solid;--display-h1-decoration:none}*{box-sizing:border-box;text-rendering:geometricPrecision}::-moz-selection{background:var(--primary-color);color:var(--invert-font-color)}::selection{background:var(--primary-color);color:var(--invert-font-color)}body{font-size:var(--global-font-size);color:var(--font-color);line-height:var(--global-line-height);margin:0;font-family:var(--font-stack);word-wrap:break-word;background-color:var(--background-color)}.logo,h1,h2,h3,h4,h5,h6{line-height:var(--global-line-height)}a{cursor:pointer;color:var(--primary-color);text-decoration:none}a:hover{background-color:var(--primary-color);color:var(--invert-font-color)}em{font-size:var(--global-font-size);font-style:italic;font-family:var(--font-stack);color:var(--font-color)}blockquote,code,em,strong{line-height:var(--global-line-height)}.logo,blockquote,code,footer,h1,h2,h3,h4,h5,h6,header,li,ol,p,section,ul{float:none;margin:0;padding:0}.logo,blockquote,h1,ol,p,ul{margin-top:calc(var(--global-space) * 2);margin-bottom:calc(var(--global-space) * 2)}.logo,h1{position:relative;display:inline-block;display:table-cell;padding:calc(var(--global-space) * 2) 0 calc(var(--global-space) * 2);margin:0;overflow:hidden;font-weight:600}h1::after{content:"====================================================================================================";position:absolute;bottom:5px;left:0;display:var(--display-h1-decoration)}.logo+*,h1+*{margin-top:0}h2,h3,h4,h5,h6{position:relative;margin-bottom:var(--global-line-height);font-weight:600}blockquote{position:relative;padding-left:calc(var(--global-space) * 2);padding-left:2ch;overflow:hidden}blockquote::after{content:">\\A>\\A>\\A>\\A>\\A>\\A>\\A>\\A>\\A>\\A>\\A>\\A>\\A>\\A>\\A>\\A>\\A>\\A>\\A>\\A>\\A>\\A>\\A>\\A>\\A>\\A>\\A>\\A>\\A>\\A>\\A>\\A>\\A>\\A>\\A>\\A>\\A>\\A>\\A>\\A>\\A>\\A>\\A>\\A>\\A>\\A>\\A>\\A>\\A>\\A>\\A>\\A>\\A>\\A>\\A>\\A>\\A>\\A>\\A>\\A>\\A>\\A>\\A>\\A>\\A>\\A>\\A>\\A>\\A>\\A>\\A>\\A>\\A>\\A>\\A>\\A>\\A>\\A>\\A>\\A>\\A>\\A>\\A>\\A>\\A>\\A>\\A>\\A>\\A>\\A>\\A>\\A>\\A>\\A>\\A>\\A>\\A>\\A>\\A>";white-space:pre;position:absolute;top:0;left:0;line-height:var(--global-line-height);color:#9ca2ab}code{font-weight:inherit;background-color:var(--code-bg-color);font-family:var(--mono-font-stack)}code::after,code::before{content:"`";display:inline}pre code::after,pre code::before{content:""}pre{display:block;word-break:break-all;word-wrap:break-word;color:var(--secondary-color);background-color:var(--background-color);border:1px solid var(--secondary-color);padding:var(--global-space);white-space:pre-wrap;white-space:-moz-pre-wrap;white-space:-pre-wrap;white-space:-o-pre-wrap}pre code{overflow-x:scroll;padding:0;margin:0;display:inline-block;min-width:100%;font-family:var(--mono-font-stack)}.terminal .logo,.terminal blockquote,.terminal code,.terminal h1,.terminal h2,.terminal h3,.terminal h4,.terminal h5,.terminal h6,.terminal strong{font-size:var(--global-font-size);font-style:normal;font-family:var(--font-stack);color:var(--font-color)}.terminal-prompt{position:relative;white-space:nowrap}.terminal-prompt::before{content:"> "}.terminal-prompt::after{content:"";-webkit-animation:cursor .8s infinite;animation:cursor .8s infinite;background:var(--primary-color);border-radius:0;display:inline-block;height:1em;margin-left:.2em;width:3px;bottom:-2px;position:relative}@-webkit-keyframes cursor{0%{opacity:0}50%{opacity:1}to{opacity:0}}@keyframes cursor{0%{opacity:0}50%{opacity:1}to{opacity:0}}li,li>ul>li{position:relative;display:block;padding-left:calc(var(--global-space) * 2)}nav>ul>li{padding-left:0}li::after{position:absolute;top:0;left:0}ul>li::after{content:"-"}nav ul>li::after{content:""}ol li::before{content:counters(item, ".") ". ";counter-increment:item}ol ol li::before{content:counters(item, ".") " ";counter-increment:item}.terminal-menu li::after,.terminal-menu li::before{display:none}ol{counter-reset:item}ol li:nth-child(n+10)::after{left:-7px}ol ol{margin-top:0;margin-bottom:0}.terminal-menu{width:100%}.terminal-nav{display:flex;flex-direction:column;align-items:flex-start}ul ul{margin-top:0;margin-bottom:0}.terminal-menu ul{list-style-type:none;padding:0!important;display:flex;flex-direction:column;width:100%;flex-grow:1;font-size:var(--global-font-size);margin-top:0}.terminal-menu li{display:flex;margin:0 0 .5em 0;padding:0}ol.terminal-toc li{border-bottom:1px dotted var(--secondary-color);padding:0;margin-bottom:15px}.terminal-menu li:last-child{margin-bottom:0}ol.terminal-toc li a{margin:4px 4px 4px 0;background:var(--background-color);position:relative;top:6px;text-align:left;padding-right:4px}.terminal-menu li a:not(.btn){text-decoration:none;display:block;width:100%;border:none;color:var(--secondary-color)}.terminal-menu li a.active{color:var(--font-color)}.terminal-menu li a:hover{background:0 0;color:inherit}ol.terminal-toc li::before{content:counters(item, ".") ". ";counter-increment:item;position:absolute;right:0;background:var(--background-color);padding:4px 0 4px 4px;bottom:-8px}ol.terminal-toc li a:hover{background:var(--primary-color);color:var(--invert-font-color)}hr{position:relative;overflow:hidden;margin:calc(var(--global-space) * 4) 0;border:0;border-bottom:1px dashed var(--secondary-color)}p{margin:0 0 var(--global-line-height);color:var(--global-font-color)}.container{max-width:var(--page-width)}.container,.container-fluid{margin:0 auto;padding:0 calc(var(--global-space) * 2)}img{width:100%}.progress-bar{height:8px;background-color:var(--progress-bar-background);margin:12px 0}.progress-bar.progress-bar-show-percent{margin-top:38px}.progress-bar-filled{background-color:var(--progress-bar-fill);height:100%;transition:width .3s ease;position:relative;width:0}.progress-bar-filled::before{content:"";border:6px solid transparent;border-top-color:var(--progress-bar-fill);position:absolute;top:-12px;right:-6px}.progress-bar-filled::after{color:var(--progress-bar-fill);content:attr(data-filled);display:block;font-size:12px;white-space:nowrap;position:absolute;border:6px solid transparent;top:-38px;right:0;transform:translateX(50%)}.progress-bar-no-arrow>.progress-bar-filled::after,.progress-bar-no-arrow>.progress-bar-filled::before{content:"";display:none;visibility:hidden;opacity:0}table{width:100%;border-collapse:collapse;margin:var(--global-line-height) 0;color:var(--font-color);font-size:var(--global-font-size)}table td,table th{vertical-align:top;border:1px solid var(--font-color);line-height:var(--global-line-height);padding:calc(var(--global-space)/ 2);font-size:1em}table thead th{font-size:1em}table tfoot tr th{font-weight:500}table caption{font-size:1em;margin:0 0 1em 0}table tbody td:first-child{font-weight:700;color:var(--secondary-color)}.form{width:100%}fieldset{border:1px solid var(--font-color);padding:1em}label{font-size:1em;color:var(--font-color)}input[type=email],input[type=number],input[type=password],input[type=search],input[type=text]{border:1px var(--input-style) var(--font-color);width:100%;padding:.7em .5em;font-size:1em;font-family:var(--font-stack);-webkit-appearance:none;border-radius:0}input[type=email]:active,input[type=email]:focus,input[type=number]:active,input[type=number]:focus,input[type=password]:active,input[type=password]:focus,input[type=search]:active,input[type=search]:focus,input[type=text]:active,input[type=text]:focus{outline:0;-webkit-appearance:none;border:1px solid var(--font-color)}input[type=email]:not(:placeholder-shown):invalid,input[type=number]:not(:placeholder-shown):invalid,input[type=password]:not(:placeholder-shown):invalid,input[type=search]:not(:placeholder-shown):invalid,input[type=text]:not(:placeholder-shown):invalid{border-color:var(--error-color)}input,textarea{color:var(--font-color);background-color:var(--background-color)}input::-webkit-input-placeholder,textarea::-webkit-input-placeholder{color:var(--secondary-color)!important;opacity:1}input::-moz-placeholder,textarea::-moz-placeholder{color:var(--secondary-color)!important;opacity:1}input:-ms-input-placeholder,textarea:-ms-input-placeholder{color:var(--secondary-color)!important;opacity:1}input::-ms-input-placeholder,textarea::-ms-input-placeholder{color:var(--secondary-color)!important;opacity:1}input::placeholder,textarea::placeholder{color:var(--secondary-color)!important;opacity:1}textarea{height:auto;width:100%;resize:none;border:1px var(--input-style) var(--font-color);padding:.5em;font-size:1em;font-family:var(--font-stack);-webkit-appearance:none;border-radius:0}textarea:focus{outline:0;-webkit-appearance:none;border:1px solid var(--font-color)}textarea:not(:placeholder-shown):invalid{border-color:var(--error-color)}input:-webkit-autofill,input:-webkit-autofill:focus textarea:-webkit-autofill,input:-webkit-autofill:hover,select:-webkit-autofill,select:-webkit-autofill:focus,select:-webkit-autofill:hover,textarea:-webkit-autofill:hover textarea:-webkit-autofill:focus{border:1px solid var(--font-color);-webkit-text-fill-color:var(--font-color);box-shadow:0 0 0 1000px var(--invert-font-color) inset;-webkit-box-shadow:0 0 0 1000px var(--invert-font-color) inset;transition:background-color 5000s ease-in-out 0s}.form-group{margin-bottom:var(--global-line-height);overflow:auto}.btn{border-style:solid;border-width:1px;display:inline-flex;align-items:center;justify-content:center;cursor:pointer;outline:0;padding:.65em 2em;font-size:1em;font-family:inherit;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;position:relative;z-index:1}.btn:active{box-shadow:none}.btn.btn-ghost{border-color:var(--font-color);color:var(--font-color);background-color:transparent}.btn.btn-ghost:focus,.btn.btn-ghost:hover{border-color:var(--tertiary-color);color:var(--tertiary-color);z-index:2}.btn.btn-ghost:hover{background-color:transparent}.btn-block{width:100%;display:flex}.btn-default{background-color:var(--font-color);border-color:var(--invert-font-color);color:var(--invert-font-color)}.btn-default:focus:not(.btn-ghost),.btn-default:hover{background-color:var(--secondary-color);color:var(--invert-font-color)}.btn-default.btn-ghost:focus,.btn-default.btn-ghost:hover{border-color:var(--secondary-color);color:var(--secondary-color);z-index:2}.btn-error{color:var(--invert-font-color);background-color:var(--error-color);border:1px solid var(--error-color)}.btn-error:focus:not(.btn-ghost),.btn-error:hover{background-color:var(--error-color);border-color:var(--error-color)}.btn-error.btn-ghost{border-color:var(--error-color);color:var(--error-color)}.btn-error.btn-ghost:focus,.btn-error.btn-ghost:hover{border-color:var(--error-color);color:var(--error-color);z-index:2}.btn-primary{color:var(--invert-font-color);background-color:var(--primary-color);border:1px solid var(--primary-color)}.btn-primary:focus:not(.btn-ghost),.btn-primary:hover{background-color:var(--primary-color);border-color:var(--primary-color)}.btn-primary.btn-ghost{border-color:var(--primary-color);color:var(--primary-color)}.btn-primary.btn-ghost:focus,.btn-primary.btn-ghost:hover{border-color:var(--primary-color);color:var(--primary-color);z-index:2}.btn-small{padding:.5em 1.3em!important;font-size:.9em!important}.btn-group{overflow:auto}.btn-group .btn{float:left}.btn-group .btn-ghost:not(:first-child){margin-left:-1px}.terminal-card{border:1px solid var(--secondary-color)}.terminal-card>header{color:var(--invert-font-color);text-align:center;background-color:var(--secondary-color);padding:.5em 0}.terminal-card>div:first-of-type{padding:var(--global-space)}.terminal-timeline{position:relative;padding-left:70px}.terminal-timeline::before{content:\' \';background:var(--secondary-color);display:inline-block;position:absolute;left:35px;width:2px;height:100%;z-index:400}.terminal-timeline .terminal-card{margin-bottom:25px}.terminal-timeline .terminal-card::before{content:\' \';background:var(--invert-font-color);border:2px solid var(--secondary-color);display:inline-block;position:absolute;margin-top:25px;left:26px;width:15px;height:15px;z-index:400}.terminal-alert{color:var(--font-color);padding:1em;border:1px solid var(--font-color);margin-bottom:var(--global-space)}.terminal-alert-error{color:var(--error-color);border-color:var(--error-color)}.terminal-alert-primary{color:var(--primary-color);border-color:var(--primary-color)}@media screen and (max-width:960px){label{display:block;width:100%}pre::-webkit-scrollbar{height:3px}}@media screen and (max-width:480px){form{width:100%}}@media only screen and (min-width:30em){.terminal-nav{flex-direction:row;align-items:center}.terminal-menu ul{flex-direction:row;justify-items:flex-end;align-items:center;justify-content:flex-end;margin-top:calc(var(--global-space) * 2)}.terminal-menu li{margin:0;margin-right:2em}.terminal-menu li:last-child{margin-right:0}}.terminal-media:not(:last-child){margin-bottom:1.25rem}.terminal-media-left{padding-right:var(--global-space)}.terminal-media-left,.terminal-media-right{display:table-cell;vertical-align:top}.terminal-media-right{padding-left:var(--global-space)}.terminal-media-body{display:table-cell;vertical-align:top}.terminal-media-heading{font-size:1em;font-weight:700}.terminal-media-content{margin-top:.3rem}.terminal-placeholder{background-color:var(--secondary-color);text-align:center;color:var(--font-color);font-size:1rem;border:1px solid var(--secondary-color)}figure>img{padding:0}.terminal-avatarholder{width:calc(var(--global-space) * 5);height:calc(var(--global-space) * 5)}.terminal-avatarholder img{padding:0}figure{margin:0}figure>figcaption{color:var(--secondary-color);text-align:center}.hljs{display:block;overflow-x:auto;padding:.5em;background:var(--block-background-color);color:var(--font-color)}.hljs-comment,.hljs-quote{color:var(--secondary-color)}.hljs-variable{color:var(--font-color)}.hljs-built_in,.hljs-keyword,.hljs-name,.hljs-selector-tag,.hljs-tag{color:var(--primary-color)}.hljs-addition,.hljs-attribute,.hljs-literal,.hljs-section,.hljs-string,.hljs-template-tag,.hljs-template-variable,.hljs-title,.hljs-type{color:var(--secondary-color)}.hljs-string{color:var(--secondary-color)}.hljs-deletion,.hljs-meta,.hljs-selector-attr,.hljs-selector-pseudo{color:var(--primary-color)}.hljs-doctag{color:var(--secondary-color)}.hljs-attr{color:var(--primary-color)}.hljs-bullet,.hljs-link,.hljs-symbol{color:var(--primary-color)}.hljs-emphasis{font-style:italic}.hljs-strong{font-weight:700}',""]);const l=n},645:e=>{e.exports=function(e){var t=[];return t.toString=function(){return this.map((function(t){var o="",r=void 0!==t[5];return t[4]&&(o+="@supports (".concat(t[4],") {")),t[2]&&(o+="@media ".concat(t[2]," {")),r&&(o+="@layer".concat(t[5].length>0?" ".concat(t[5]):""," {")),o+=e(t),r&&(o+="}"),t[2]&&(o+="}"),t[4]&&(o+="}"),o})).join("")},t.i=function(e,o,r,i,a){"string"==typeof e&&(e=[[null,e,void 0]]);var n={};if(r)for(var l=0;l0?" ".concat(d[5]):""," {").concat(d[1],"}")),d[5]=a),o&&(d[2]?(d[1]="@media ".concat(d[2]," {").concat(d[1],"}"),d[2]=o):d[2]=o),i&&(d[4]?(d[1]="@supports (".concat(d[4],") {").concat(d[1],"}"),d[4]=i):d[4]="".concat(i)),t.push(d))}},t}},81:e=>{e.exports=function(e){return e[1]}},379:e=>{var t=[];function o(e){for(var o=-1,r=0;r{var t={};e.exports=function(e,o){var r=function(e){if(void 0===t[e]){var o=document.querySelector(e);if(window.HTMLIFrameElement&&o instanceof window.HTMLIFrameElement)try{o=o.contentDocument.head}catch(e){o=null}t[e]=o}return t[e]}(e);if(!r)throw new Error("Couldn't find a style target. This probably means that the value for the 'insert' parameter is invalid.");r.appendChild(o)}},216:e=>{e.exports=function(e){var t=document.createElement("style");return e.setAttributes(t,e.attributes),e.insert(t,e.options),t}},565:(e,t,o)=>{e.exports=function(e){var t=o.nc;t&&e.setAttribute("nonce",t)}},795:e=>{e.exports=function(e){var t=e.insertStyleElement(e);return{update:function(o){!function(e,t,o){var r="";o.supports&&(r+="@supports (".concat(o.supports,") {")),o.media&&(r+="@media ".concat(o.media," {"));var i=void 0!==o.layer;i&&(r+="@layer".concat(o.layer.length>0?" ".concat(o.layer):""," {")),r+=o.css,i&&(r+="}"),o.media&&(r+="}"),o.supports&&(r+="}");var a=o.sourceMap;a&&"undefined"!=typeof btoa&&(r+="\n/*# sourceMappingURL=data:application/json;base64,".concat(btoa(unescape(encodeURIComponent(JSON.stringify(a))))," */")),t.styleTagTransform(r,e,t.options)}(t,e,o)},remove:function(){!function(e){if(null===e.parentNode)return!1;e.parentNode.removeChild(e)}(t)}}}},589:e=>{e.exports=function(e,t){if(t.styleSheet)t.styleSheet.cssText=e;else{for(;t.firstChild;)t.removeChild(t.firstChild);t.appendChild(document.createTextNode(e))}}}},t={};function o(r){var i=t[r];if(void 0!==i)return i.exports;var a=t[r]={id:r,exports:{}};return e[r](a,a.exports,o),a.exports}o.n=e=>{var t=e&&e.__esModule?()=>e.default:()=>e;return o.d(t,{a:t}),t},o.d=(e,t)=>{for(var r in t)o.o(t,r)&&!o.o(e,r)&&Object.defineProperty(e,r,{enumerable:!0,get:t[r]})},o.o=(e,t)=>Object.prototype.hasOwnProperty.call(e,t),o.nc=void 0,(()=>{var e=o(379),t=o.n(e),r=o(795),i=o.n(r),a=o(569),n=o.n(a),l=o(565),s=o.n(l),c=o(216),d=o.n(c),p=o(589),h=o.n(p),u=o(408),v={};v.styleTagTransform=h(),v.setAttributes=s(),v.insert=n().bind(null,"head"),v.domAPI=i(),v.insertStyleElement=d(),t()(u.Z,v),u.Z&&u.Z.locals&&u.Z.locals;const m=window.ShadowRoot&&(void 0===window.ShadyCSS||window.ShadyCSS.nativeShadow)&&"adoptedStyleSheets"in Document.prototype&&"replace"in CSSStyleSheet.prototype,f=Symbol(),g=new Map;class b{constructor(e,t){if(this._$cssResult$=!0,t!==f)throw Error("CSSResult is not constructable. Use `unsafeCSS` or `css` instead.");this.cssText=e}get styleSheet(){let e=g.get(this.cssText);return m&&void 0===e&&(g.set(this.cssText,e=new CSSStyleSheet),e.replaceSync(this.cssText)),e}toString(){return this.cssText}}const y=(e,...t)=>{const o=1===e.length?e[0]:t.reduce(((t,o,r)=>t+(e=>{if(!0===e._$cssResult$)return e.cssText;if("number"==typeof e)return e;throw Error("Value passed to 'css' function must be a 'css' function result: "+e+". Use 'unsafeCSS' to pass non-literal values, but take care to ensure page security.")})(o)+e[r+1]),e[0]);return new b(o,f)},w=m?e=>e:e=>e instanceof CSSStyleSheet?(e=>{let t="";for(const o of e.cssRules)t+=o.cssText;return(e=>new b("string"==typeof e?e:e+"",f))(t)})(e):e;var x;const A=window.trustedTypes,k=A?A.emptyScript:"",$=window.reactiveElementPolyfillSupport,_={toAttribute(e,t){switch(t){case Boolean:e=e?k:null;break;case Object:case Array:e=null==e?e:JSON.stringify(e)}return e},fromAttribute(e,t){let o=e;switch(t){case Boolean:o=null!==e;break;case Number:o=null===e?null:Number(e);break;case Object:case Array:try{o=JSON.parse(e)}catch(e){o=null}}return o}},S=(e,t)=>t!==e&&(t==t||e==e),E={attribute:!0,type:String,converter:_,reflect:!1,hasChanged:S};class C extends HTMLElement{constructor(){super(),this._$Et=new Map,this.isUpdatePending=!1,this.hasUpdated=!1,this._$Ei=null,this.o()}static addInitializer(e){var t;null!==(t=this.l)&&void 0!==t||(this.l=[]),this.l.push(e)}static get observedAttributes(){this.finalize();const e=[];return this.elementProperties.forEach(((t,o)=>{const r=this._$Eh(o,t);void 0!==r&&(this._$Eu.set(r,o),e.push(r))})),e}static createProperty(e,t=E){if(t.state&&(t.attribute=!1),this.finalize(),this.elementProperties.set(e,t),!t.noAccessor&&!this.prototype.hasOwnProperty(e)){const o="symbol"==typeof e?Symbol():"__"+e,r=this.getPropertyDescriptor(e,o,t);void 0!==r&&Object.defineProperty(this.prototype,e,r)}}static getPropertyDescriptor(e,t,o){return{get(){return this[t]},set(r){const i=this[e];this[t]=r,this.requestUpdate(e,i,o)},configurable:!0,enumerable:!0}}static getPropertyOptions(e){return this.elementProperties.get(e)||E}static finalize(){if(this.hasOwnProperty("finalized"))return!1;this.finalized=!0;const e=Object.getPrototypeOf(this);if(e.finalize(),this.elementProperties=new Map(e.elementProperties),this._$Eu=new Map,this.hasOwnProperty("properties")){const e=this.properties,t=[...Object.getOwnPropertyNames(e),...Object.getOwnPropertySymbols(e)];for(const o of t)this.createProperty(o,e[o])}return this.elementStyles=this.finalizeStyles(this.styles),!0}static finalizeStyles(e){const t=[];if(Array.isArray(e)){const o=new Set(e.flat(1/0).reverse());for(const e of o)t.unshift(w(e))}else void 0!==e&&t.push(w(e));return t}static _$Eh(e,t){const o=t.attribute;return!1===o?void 0:"string"==typeof o?o:"string"==typeof e?e.toLowerCase():void 0}o(){var e;this._$Ep=new Promise((e=>this.enableUpdating=e)),this._$AL=new Map,this._$Em(),this.requestUpdate(),null===(e=this.constructor.l)||void 0===e||e.forEach((e=>e(this)))}addController(e){var t,o;(null!==(t=this._$Eg)&&void 0!==t?t:this._$Eg=[]).push(e),void 0!==this.renderRoot&&this.isConnected&&(null===(o=e.hostConnected)||void 0===o||o.call(e))}removeController(e){var t;null===(t=this._$Eg)||void 0===t||t.splice(this._$Eg.indexOf(e)>>>0,1)}_$Em(){this.constructor.elementProperties.forEach(((e,t)=>{this.hasOwnProperty(t)&&(this._$Et.set(t,this[t]),delete this[t])}))}createRenderRoot(){var e;const t=null!==(e=this.shadowRoot)&&void 0!==e?e:this.attachShadow(this.constructor.shadowRootOptions);return((e,t)=>{m?e.adoptedStyleSheets=t.map((e=>e instanceof CSSStyleSheet?e:e.styleSheet)):t.forEach((t=>{const o=document.createElement("style"),r=window.litNonce;void 0!==r&&o.setAttribute("nonce",r),o.textContent=t.cssText,e.appendChild(o)}))})(t,this.constructor.elementStyles),t}connectedCallback(){var e;void 0===this.renderRoot&&(this.renderRoot=this.createRenderRoot()),this.enableUpdating(!0),null===(e=this._$Eg)||void 0===e||e.forEach((e=>{var t;return null===(t=e.hostConnected)||void 0===t?void 0:t.call(e)}))}enableUpdating(e){}disconnectedCallback(){var e;null===(e=this._$Eg)||void 0===e||e.forEach((e=>{var t;return null===(t=e.hostDisconnected)||void 0===t?void 0:t.call(e)}))}attributeChangedCallback(e,t,o){this._$AK(e,o)}_$ES(e,t,o=E){var r,i;const a=this.constructor._$Eh(e,o);if(void 0!==a&&!0===o.reflect){const n=(null!==(i=null===(r=o.converter)||void 0===r?void 0:r.toAttribute)&&void 0!==i?i:_.toAttribute)(t,o.type);this._$Ei=e,null==n?this.removeAttribute(a):this.setAttribute(a,n),this._$Ei=null}}_$AK(e,t){var o,r,i;const a=this.constructor,n=a._$Eu.get(e);if(void 0!==n&&this._$Ei!==n){const e=a.getPropertyOptions(n),l=e.converter,s=null!==(i=null!==(r=null===(o=l)||void 0===o?void 0:o.fromAttribute)&&void 0!==r?r:"function"==typeof l?l:null)&&void 0!==i?i:_.fromAttribute;this._$Ei=n,this[n]=s(t,e.type),this._$Ei=null}}requestUpdate(e,t,o){let r=!0;void 0!==e&&(((o=o||this.constructor.getPropertyOptions(e)).hasChanged||S)(this[e],t)?(this._$AL.has(e)||this._$AL.set(e,t),!0===o.reflect&&this._$Ei!==e&&(void 0===this._$EC&&(this._$EC=new Map),this._$EC.set(e,o))):r=!1),!this.isUpdatePending&&r&&(this._$Ep=this._$E_())}async _$E_(){this.isUpdatePending=!0;try{await this._$Ep}catch(e){Promise.reject(e)}const e=this.scheduleUpdate();return null!=e&&await e,!this.isUpdatePending}scheduleUpdate(){return this.performUpdate()}performUpdate(){var e;if(!this.isUpdatePending)return;this.hasUpdated,this._$Et&&(this._$Et.forEach(((e,t)=>this[t]=e)),this._$Et=void 0);let t=!1;const o=this._$AL;try{t=this.shouldUpdate(o),t?(this.willUpdate(o),null===(e=this._$Eg)||void 0===e||e.forEach((e=>{var t;return null===(t=e.hostUpdate)||void 0===t?void 0:t.call(e)})),this.update(o)):this._$EU()}catch(e){throw t=!1,this._$EU(),e}t&&this._$AE(o)}willUpdate(e){}_$AE(e){var t;null===(t=this._$Eg)||void 0===t||t.forEach((e=>{var t;return null===(t=e.hostUpdated)||void 0===t?void 0:t.call(e)})),this.hasUpdated||(this.hasUpdated=!0,this.firstUpdated(e)),this.updated(e)}_$EU(){this._$AL=new Map,this.isUpdatePending=!1}get updateComplete(){return this.getUpdateComplete()}getUpdateComplete(){return this._$Ep}shouldUpdate(e){return!0}update(e){void 0!==this._$EC&&(this._$EC.forEach(((e,t)=>this._$ES(t,this[t],e))),this._$EC=void 0),this._$EU()}updated(e){}firstUpdated(e){}}var z;C.finalized=!0,C.elementProperties=new Map,C.elementStyles=[],C.shadowRootOptions={mode:"open"},null==$||$({ReactiveElement:C}),(null!==(x=globalThis.reactiveElementVersions)&&void 0!==x?x:globalThis.reactiveElementVersions=[]).push("1.3.2");const j=globalThis.trustedTypes,P=j?j.createPolicy("lit-html",{createHTML:e=>e}):void 0,N=`lit$${(Math.random()+"").slice(9)}$`,R="?"+N,L=`<${R}>`,O=document,T=(e="")=>O.createComment(e),M=e=>null===e||"object"!=typeof e&&"function"!=typeof e,U=Array.isArray,H=/<(?:(!--|\/[^a-zA-Z])|(\/?[a-zA-Z][^>\s]*)|(\/?$))/g,I=/-->/g,q=/>/g,D=/>|[ \n \r](?:([^\s"'>=/]+)([ \n \r]*=[ \n \r]*(?:[^ \n \r"'`<>=]|("|')|))|$)/g,V=/'/g,B=/"/g,F=/^(?:script|style|textarea|title)$/i,G=e=>(t,...o)=>({_$litType$:e,strings:t,values:o}),K=G(1),W=(G(2),Symbol.for("lit-noChange")),Z=Symbol.for("lit-nothing"),J=new WeakMap,X=O.createTreeWalker(O,129,null,!1),Q=(e,t)=>{const o=e.length-1,r=[];let i,a=2===t?"":"",n=H;for(let t=0;t"===s[0]?(n=null!=i?i:H,c=-1):void 0===s[1]?c=-2:(c=n.lastIndex-s[2].length,l=s[1],n=void 0===s[3]?D:'"'===s[3]?B:V):n===B||n===V?n=D:n===I||n===q?n=H:(n=D,i=void 0);const p=n===D&&e[t+1].startsWith("/>")?" ":"";a+=n===H?o+L:c>=0?(r.push(l),o.slice(0,c)+"$lit$"+o.slice(c)+N+p):o+N+(-2===c?(r.push(void 0),t):p)}const l=a+(e[o]||"")+(2===t?"":"");if(!Array.isArray(e)||!e.hasOwnProperty("raw"))throw Error("invalid template strings array");return[void 0!==P?P.createHTML(l):l,r]};class Y{constructor({strings:e,_$litType$:t},o){let r;this.parts=[];let i=0,a=0;const n=e.length-1,l=this.parts,[s,c]=Q(e,t);if(this.el=Y.createElement(s,o),X.currentNode=this.el.content,2===t){const e=this.el.content,t=e.firstChild;t.remove(),e.append(...t.childNodes)}for(;null!==(r=X.nextNode())&&l.length0){r.textContent=j?j.emptyScript:"";for(let o=0;o{var t;return U(e)||"function"==typeof(null===(t=e)||void 0===t?void 0:t[Symbol.iterator])})(e)?this.S(e):this.$(e)}M(e,t=this._$AB){return this._$AA.parentNode.insertBefore(e,t)}k(e){this._$AH!==e&&(this._$AR(),this._$AH=this.M(e))}$(e){this._$AH!==Z&&M(this._$AH)?this._$AA.nextSibling.data=e:this.k(O.createTextNode(e)),this._$AH=e}T(e){var t;const{values:o,_$litType$:r}=e,i="number"==typeof r?this._$AC(e):(void 0===r.el&&(r.el=Y.createElement(r.h,this.options)),r);if((null===(t=this._$AH)||void 0===t?void 0:t._$AD)===i)this._$AH.m(o);else{const e=new te(i,this),t=e.p(this.options);e.m(o),this.k(t),this._$AH=e}}_$AC(e){let t=J.get(e.strings);return void 0===t&&J.set(e.strings,t=new Y(e)),t}S(e){U(this._$AH)||(this._$AH=[],this._$AR());const t=this._$AH;let o,r=0;for(const i of e)r===t.length?t.push(o=new oe(this.M(T()),this.M(T()),this,this.options)):o=t[r],o._$AI(i),r++;r2||""!==o[0]||""!==o[1]?(this._$AH=Array(o.length-1).fill(new String),this.strings=o):this._$AH=Z}get tagName(){return this.element.tagName}get _$AU(){return this._$AM._$AU}_$AI(e,t=this,o,r){const i=this.strings;let a=!1;if(void 0===i)e=ee(this,e,t,0),a=!M(e)||e!==this._$AH&&e!==W,a&&(this._$AH=e);else{const r=e;let n,l;for(e=i[0],n=0;n{var r,i;const a=null!==(r=null==o?void 0:o.renderBefore)&&void 0!==r?r:t;let n=a._$litPart$;if(void 0===n){const e=null!==(i=null==o?void 0:o.renderBefore)&&void 0!==i?i:null;a._$litPart$=n=new oe(t.insertBefore(T(),e),e,void 0,null!=o?o:{})}return n._$AI(e),n})(t,this.renderRoot,this.renderOptions)}connectedCallback(){var e;super.connectedCallback(),null===(e=this._$Dt)||void 0===e||e.setConnected(!0)}disconnectedCallback(){var e;super.disconnectedCallback(),null===(e=this._$Dt)||void 0===e||e.setConnected(!1)}render(){return W}}he.finalized=!0,he._$litElement$=!0,null===(de=globalThis.litElementHydrateSupport)||void 0===de||de.call(globalThis,{LitElement:he});const ue=globalThis.litElementPolyfillSupport;null==ue||ue({LitElement:he}),(null!==(pe=globalThis.litElementVersions)&&void 0!==pe?pe:globalThis.litElementVersions=[]).push("3.2.0");const ve=e=>t=>"function"==typeof t?((e,t)=>(window.customElements.define(e,t),t))(e,t):((e,t)=>{const{kind:o,elements:r}=t;return{kind:o,elements:r,finisher(t){window.customElements.define(e,t)}}})(e,t),me=(e,t)=>"method"===t.kind&&t.descriptor&&!("value"in t.descriptor)?{...t,finisher(o){o.createProperty(t.key,e)}}:{kind:"field",key:Symbol(),placement:"own",descriptor:{},originalKey:t.key,initializer(){"function"==typeof t.initializer&&(this[t.key]=t.initializer.call(this))},finisher(o){o.createProperty(t.key,e)}};function fe(e){return(t,o)=>void 0!==o?((e,t,o)=>{t.constructor.createProperty(o,e)})(e,t,o):me(e,t)}var ge;null===(ge=window.HTMLSlotElement)||void 0===ge||ge.prototype.assignedElements;class be extends he{get _slottedChildren(){const e=this.shadowRoot.querySelector("slot");if(e)return e.assignedElements({flatten:!0})}}const ye="categoryActivated",we=y` ul { margin: 0; padding: 0; @@ -138,7 +138,7 @@
-
`}_violationClicked(){let e;this._renderedCode?e=this._renderedCode:(e=this._slottedChildren[0],this._renderedCode=e);const t={detail:{message:this.message,id:this.ruleId,startLine:this.startLine,startCol:this.startCol,endLine:this.endLine,endCol:this.endCol,path:this.path,category:this.category,howToFix:this.howToFix,violationId:this.violationId,renderedCode:e},bubbles:!0,composed:!0};this.dispatchEvent(new CustomEvent("violationSelected",t))}};Pe.styles=ze,je([fe({type:String})],Pe.prototype,"message",void 0),je([fe({type:String})],Pe.prototype,"category",void 0),je([fe({type:String})],Pe.prototype,"ruleId",void 0),je([fe({type:Number})],Pe.prototype,"startLine",void 0),je([fe({type:Number})],Pe.prototype,"startCol",void 0),je([fe({type:Number})],Pe.prototype,"endLine",void 0),je([fe({type:Number})],Pe.prototype,"endCol",void 0),je([fe({type:String})],Pe.prototype,"path",void 0),je([fe({type:String})],Pe.prototype,"howToFix",void 0),je([fe({type:Boolean})],Pe.prototype,"selected",void 0),Pe=je([ve("category-rule-result")],Pe);const Re=y` + `}_violationClicked(){let e;this._renderedCode?e=this._renderedCode:(e=this._slottedChildren[0],this._renderedCode=e);const t={detail:{message:this.message,id:this.ruleId,startLine:this.startLine,startCol:this.startCol,endLine:this.endLine,endCol:this.endCol,path:this.path,category:this.category,howToFix:this.howToFix,violationId:this.violationId,renderedCode:e},bubbles:!0,composed:!0};this.dispatchEvent(new CustomEvent("violationSelected",t))}};Pe.styles=ze,je([fe({type:String})],Pe.prototype,"message",void 0),je([fe({type:String})],Pe.prototype,"category",void 0),je([fe({type:String})],Pe.prototype,"ruleId",void 0),je([fe({type:Number})],Pe.prototype,"startLine",void 0),je([fe({type:Number})],Pe.prototype,"startCol",void 0),je([fe({type:Number})],Pe.prototype,"endLine",void 0),je([fe({type:Number})],Pe.prototype,"endCol",void 0),je([fe({type:String})],Pe.prototype,"path",void 0),je([fe({type:String})],Pe.prototype,"howToFix",void 0),je([fe({type:Boolean})],Pe.prototype,"selected",void 0),Pe=je([ve("category-rule-result")],Pe);const Ne=y` .rule-icon { font-family: 'Arial'; font-size: var(--sl-font-size-small); @@ -273,7 +273,7 @@ overflow-y: hidden; } } -`,Ne=K` +`,Re=K` ${this.numResults-this.maxViolations} more violations not rendered, There are just too many! - `);const t=this._expandState?Le:Ne;return K` + `);const t=this._expandState?Le:Re;return K` - `}_ruleSelected(){if(this.open)this._violations.style.display="none",this._expandState=!1;else{this._violations.style.display="block";const e=this.parentElement.parentElement.offsetHeight-60*this.totalRulesViolated;this._violations.style.maxHeight=e+"px",this._expandState=!0}this.open=!this.open,this.dispatchEvent(new CustomEvent("ruleSelected",{bubbles:!0,composed:!0,detail:{id:this.ruleId}})),this.requestUpdate()}_violationSelected(e){this._slottedChildren.forEach((t=>{t.selected=e.detail.violationId==t.violationId}))}};var Me;Te.styles=Re,Oe([fe()],Te.prototype,"totalRulesViolated",void 0),Oe([fe()],Te.prototype,"maxViolations",void 0),Oe([fe()],Te.prototype,"truncated",void 0),Oe([fe()],Te.prototype,"ruleId",void 0),Oe([fe()],Te.prototype,"description",void 0),Oe([fe()],Te.prototype,"numResults",void 0),Oe([fe()],Te.prototype,"ruleIcon",void 0),Oe([fe()],Te.prototype,"open",void 0),Oe([(Me=".violations",(({finisher:e,descriptor:t})=>(o,r)=>{var i;if(void 0===r){const r=null!==(i=o.originalKey)&&void 0!==i?i:o.key,a=null!=t?{kind:"method",placement:"prototype",key:r,descriptor:t(o.key)}:{...o,key:r};return null!=e&&(a.finisher=function(t){e(t,r)}),a}{const i=o.constructor;void 0!==t&&Object.defineProperty(o,r,t(r)),null==e||e(i,r)}})({descriptor:e=>{const t={get(){var e,t;return null!==(t=null===(e=this.renderRoot)||void 0===e?void 0:e.querySelector(Me))&&void 0!==t?t:null},enumerable:!0,configurable:!0};return t}}))],Te.prototype,"_violations",void 0),Te=Oe([ve("category-rule")],Te);const Ue=y` + `}_ruleSelected(){if(this.open)this._violations.style.display="none",this._expandState=!1;else{this._violations.style.display="block";const e=this.parentElement.parentElement.offsetHeight-60*this.totalRulesViolated;this._violations.style.maxHeight=e+"px",this._expandState=!0}this.open=!this.open,this.dispatchEvent(new CustomEvent("ruleSelected",{bubbles:!0,composed:!0,detail:{id:this.ruleId}})),this.requestUpdate()}_violationSelected(e){this._slottedChildren.forEach((t=>{t.selected=e.detail.violationId==t.violationId}))}};var Me;Te.styles=Ne,Oe([fe()],Te.prototype,"totalRulesViolated",void 0),Oe([fe()],Te.prototype,"maxViolations",void 0),Oe([fe()],Te.prototype,"truncated",void 0),Oe([fe()],Te.prototype,"ruleId",void 0),Oe([fe()],Te.prototype,"description",void 0),Oe([fe()],Te.prototype,"numResults",void 0),Oe([fe()],Te.prototype,"ruleIcon",void 0),Oe([fe()],Te.prototype,"open",void 0),Oe([(Me=".violations",(({finisher:e,descriptor:t})=>(o,r)=>{var i;if(void 0===r){const r=null!==(i=o.originalKey)&&void 0!==i?i:o.key,a=null!=t?{kind:"method",placement:"prototype",key:r,descriptor:t(o.key)}:{...o,key:r};return null!=e&&(a.finisher=function(t){e(t,r)}),a}{const i=o.constructor;void 0!==t&&Object.defineProperty(o,r,t(r)),null==e||e(i,r)}})({descriptor:e=>{const t={get(){var e,t;return null!==(t=null===(e=this.renderRoot)||void 0===e?void 0:e.querySelector(Me))&&void 0!==t?t:null},enumerable:!0,configurable:!0};return t}}))],Te.prototype,"_violations",void 0),Te=Oe([ve("category-rule")],Te);const Ue=y` ul.rule { margin: 0; padding: 0; @@ -1061,7 +1061,7 @@ } `]}render(){return K`
- ${this.value}${this.percentage?"%":""} + ${this.value.toLocaleString()}${this.percentage?"%":""} ${this.label}
- `}colorForScore(){if(this.preset)return this.preset;switch(!0){case this.value<=10:return"error";case this.value>10&&this.value<20:return"warn-400";case this.value>=20&&this.value<30:return"warn-300";case this.value>=30&&this.value<40:return"warn-200";case this.value>=40&&this.value<50:return"warn";case this.value>=50&&this.value<65:return"ok-400";case this.value>=65&&this.value<75:return"ok-300";case this.value>=75&&this.value<95:return"ok-200";case this.value>=95:default:return"ok"}}};Ke([fe()],We.prototype,"value",void 0),Ke([fe()],We.prototype,"preset",void 0),Ke([fe()],We.prototype,"percentage",void 0),Ke([fe()],We.prototype,"label",void 0),We=Ke([ve("header-statistic")],We)})()})(); \ No newline at end of file + `}colorForScore(){if(this.preset)return this.preset;switch(!0){case this.value<=10:return"error";case this.value>10&&this.value<20:return"warn-400";case this.value>=20&&this.value<30:return"warn-300";case this.value>=30&&this.value<40:return"warn-200";case this.value>=40&&this.value<50:return"warn";case this.value>=50&&this.value<65:return"ok-400";case this.value>=65&&this.value<75:return"ok-300";case this.value>=75&&this.value<95:return"ok-200";case this.value>=95:default:return"ok"}}};Ke([fe({type:Number})],We.prototype,"value",void 0),Ke([fe()],We.prototype,"preset",void 0),Ke([fe()],We.prototype,"percentage",void 0),Ke([fe()],We.prototype,"label",void 0),We=Ke([ve("header-statistic")],We)})()})(); \ No newline at end of file diff --git a/html-report/ui/src/components/header/header-statistic-component.ts b/html-report/ui/src/components/header/header-statistic-component.ts index c34cddf4..6896b0ba 100644 --- a/html-report/ui/src/components/header/header-statistic-component.ts +++ b/html-report/ui/src/components/header/header-statistic-component.ts @@ -104,7 +104,7 @@ export class HeaderStatisticComponent extends BaseComponent { return [staticCss]; } - @property() + @property( {type: Number}) value: number; @property() @@ -119,7 +119,7 @@ export class HeaderStatisticComponent extends BaseComponent { render() { return html`
- ${this.value}${this.percentage ? '%' : ''} + ${this.value.toLocaleString()}${this.percentage ? '%' : ''} ${this.label}
`; diff --git a/model/results.go b/model/results.go index f4686a8b..426471a6 100644 --- a/model/results.go +++ b/model/results.go @@ -51,10 +51,14 @@ func NewRuleResultSet(results []RuleFunctionResult) *RuleResultSet { pointerResults = append(pointerResults, &n) } - return &RuleResultSet{ + rrs := &RuleResultSet{ Results: pointerResults, categoryMap: make(map[*RuleCategory][]*RuleFunctionResult), } + defer rrs.GetErrorCount() + defer rrs.GetInfoCount() + defer rrs.GetWarnCount() + return rrs } // NewRuleResultSetPointer will encapsulate a set of results into a set, that can then be queried. diff --git a/motor/rule_applicator.go b/motor/rule_applicator.go index d8d346cb..3fa6ef88 100644 --- a/motor/rule_applicator.go +++ b/motor/rule_applicator.go @@ -78,11 +78,11 @@ func ApplyRulesToRuleSet(execution *RuleSetExecution) *RuleSetExecutionResult { specUnresolved = *specInfo.RootNode specResolved = specUnresolved - // create an index - index := index.NewSpecIndex(&specResolved) + // create resolved and un-resolved indexes. + indexResolved := index.NewSpecIndex(&specResolved) // create a resolver - resolverInstance := resolver.NewResolver(index) + resolverInstance := resolver.NewResolver(indexResolved) // resolve the doc resolverInstance.Resolve() @@ -137,8 +137,8 @@ func ApplyRulesToRuleSet(execution *RuleSetExecution) *RuleSetExecutionResult { ruleResults: &ruleResults, wg: &ruleWaitGroup, errors: &errors, - index: index, specInfo: specInfo, + index: indexResolved, customFunctions: execution.CustomFunctions, } go runRule(ctx) @@ -150,7 +150,7 @@ func ApplyRulesToRuleSet(execution *RuleSetExecution) *RuleSetExecutionResult { return &RuleSetExecutionResult{ RuleSetExecution: execution, Results: ruleResults, - Index: index, + Index: indexResolved, SpecInfo: specInfo, Errors: errors, } diff --git a/motor/rule_applicator_test.go b/motor/rule_applicator_test.go index 9de91927..96f4eea8 100644 --- a/motor/rule_applicator_test.go +++ b/motor/rule_applicator_test.go @@ -632,7 +632,7 @@ func TestRuleSet_ContactProperties(t *testing.T) { results, _ := ApplyRules(rs, []byte(yml)) assert.NotNil(t, results) - assert.Equal(t, "Contact details are incomplete: 'url' must be set", results[0].Message) + assert.Equal(t, "Contact details are incomplete: `url` must be set", results[0].Message) } @@ -651,7 +651,7 @@ func TestRuleSet_InfoContact(t *testing.T) { results, _ := ApplyRules(rs, []byte(yml)) assert.NotNil(t, results) - assert.Equal(t, "Info section is missing contact details: 'contact' must be set", results[0].Message) + assert.Equal(t, "Info section is missing contact details: `contact` must be set", results[0].Message) } @@ -672,7 +672,7 @@ func TestRuleSet_InfoDescription(t *testing.T) { results, _ := ApplyRules(rs, []byte(yml)) assert.NotNil(t, results) - assert.Equal(t, "Info section is missing a description: 'description' must be set", results[0].Message) + assert.Equal(t, "Info section is missing a description: `description` must be set", results[0].Message) } @@ -694,7 +694,7 @@ func TestRuleSet_InfoLicense(t *testing.T) { results, _ := ApplyRules(rs, []byte(yml)) assert.NotNil(t, results) - assert.Equal(t, "Info section should contain a license: 'license' must be set", results[0].Message) + assert.Equal(t, "Info section should contain a license: `license` must be set", results[0].Message) } @@ -718,7 +718,7 @@ func TestRuleSet_InfoLicenseUrl(t *testing.T) { results, _ := ApplyRules(rs, []byte(yml)) assert.NotNil(t, results) - assert.Equal(t, "License should contain an url: 'url' must be set", results[0].Message) + assert.Equal(t, "License should contain an url: `url` must be set", results[0].Message) } @@ -736,7 +736,7 @@ func TestRuleSet_NoEvalInMarkdown(t *testing.T) { results, _ := ApplyRules(rs, []byte(yml)) assert.NotNil(t, results) - assert.Equal(t, "description contains content with 'eval\\(', forbidden", results[0].Message) + assert.Equal(t, "description contains content with `eval\\(`, forbidden", results[0].Message) } @@ -754,7 +754,7 @@ func TestRuleSet_NoScriptInMarkdown(t *testing.T) { results, _ := ApplyRules(rs, []byte(yml)) assert.NotNil(t, results) - assert.Equal(t, "description contains content with '' tags", Id: noScriptTagsInMarkdown, Formats: model.AllFormats, - Description: "Markdown descriptions must not have '