{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "$id": "https://axx.nimbusxr.us/schemas/v0/axx.schema.json",
  "$defs": {
    "Active": {
      "properties": {
        "enabled": {
          "type": "boolean"
        },
        "onNoTags": {
          "type": "string",
          "enum": [
            "fallback",
            "error"
          ],
          "description": "OnNoTags decides what happens when the selected scenarios have no tags:\n\"fallback\" (start all enabled services, default) or \"error\"."
        }
      },
      "additionalProperties": false,
      "type": "object",
      "description": "Active configures tag-based application startup."
    },
    "Duration": {
      "oneOf": [
        {
          "type": "string",
          "pattern": "^\\s*([0-9]+(\\.[0-9]+)?(ns|us|µs|ms|s|m|h))+\\s*$",
          "examples": [
            "30s",
            "1m30s",
            "250ms"
          ]
        },
        {
          "type": "number",
          "minimum": 0,
          "description": "seconds"
        }
      ]
    },
    "FixtureConformance": {
      "properties": {
        "name": {
          "type": "string",
          "description": "Name is shown in check output."
        },
        "filePatterns": {
          "items": {
            "type": "string"
          },
          "type": "array",
          "minItems": 1,
          "description": "FilePatterns are globs (relative to baseDir) of the files to validate."
        },
        "schemaType": {
          "type": "string",
          "description": "SchemaType is the family whose oracle validates the files: avro,\njson, yaml, xml, protobuf or dataset.",
          "examples": [
            "avro",
            "json",
            "yaml",
            "xml",
            "protobuf",
            "dataset"
          ]
        },
        "schemaRef": {
          "type": "string",
          "description": "SchemaRef is the schema, relative to baseDir (unlike factory.schema,\nwhich is relative to the factory file)."
        }
      },
      "additionalProperties": false,
      "type": "object",
      "required": [
        "name",
        "filePatterns",
        "schemaType",
        "schemaRef"
      ],
      "description": "FixtureConformance validates matched files against a schema."
    },
    "FixtureLint": {
      "properties": {
        "emit": {
          "type": "boolean",
          "description": "Emit writes one cross-file-unique lint rule per declared identity.\nDefault: true."
        },
        "output": {
          "type": "string",
          "description": "Output is the generated rules file, relative to baseDir. Default:\naxx-lint.generated.yaml. Wire it in with lint.include."
        }
      },
      "additionalProperties": false,
      "type": "object",
      "description": "FixtureLint configures the generated lint rules."
    },
    "FixtureOutput": {
      "properties": {
        "ignored": {
          "type": "boolean",
          "description": "Ignored makes outputs gitignored derivations instead of committed\nfiles: the tool maintains exact .gitignore entries and `axx fixtures\ngenerate` materializes them. Default: false."
        }
      },
      "additionalProperties": false,
      "type": "object",
      "description": "FixtureOutput holds output defaults."
    },
    "Fixtures": {
      "properties": {
        "baseDir": {
          "type": "string",
          "description": "BaseDir is the resource root every fixtures path resolves against,\nrelative to axx.yaml. Default: the directory of axx.yaml."
        },
        "factories": {
          "items": {
            "type": "string"
          },
          "type": "array",
          "description": "Factories are globs (relative to baseDir) locating factory specs.\nDefault: [\"**.factory.yaml\"]."
        },
        "sources": {
          "items": {
            "type": "string"
          },
          "type": "array",
          "description": "Sources are additional roots, relative to baseDir (\"../\" reaches\noutside it), walked for factory, prototype and fixture sources exactly\nlike baseDir, so fixtures can sit next to generated files that live\nelsewhere (a compose-mounted WireMock directory, say). Roots must\nexist and be disjoint from baseDir and each other."
        },
        "conformance": {
          "items": {
            "$ref": "#/$defs/FixtureConformance"
          },
          "type": "array",
          "description": "Conformance rules validate files no factory manages through a\nfamily's schema oracle."
        },
        "lint": {
          "$ref": "#/$defs/FixtureLint",
          "description": "Lint configures the lint rules generated from identity declarations."
        },
        "output": {
          "$ref": "#/$defs/FixtureOutput",
          "description": "Output holds module-wide output defaults; factories override them\nwith factory.output."
        }
      },
      "additionalProperties": false,
      "type": "object",
      "description": "Fixtures configures `axx fixtures`, the fixture factory: *.factory.yaml, *.fixture.yaml and *.prototype.yaml sources expanded into schema-governed fixture files, with the ownership manifest, drift and conformance checks."
    },
    "Lint": {
      "properties": {
        "include": {
          "$ref": "#/$defs/StringList",
          "description": "Include lists rule files merged into this section (for example the\naxx-lint.generated.yaml written by `axx fixtures generate`), resolved\nrelative to the including file. Included files contribute rules and\nfurther includes only; they must not have a config block."
        },
        "config": {
          "$ref": "#/$defs/LintConfig",
          "description": "Config holds the settings shared by every rule."
        },
        "rules": {
          "items": {
            "$ref": "#/$defs/LintRule"
          },
          "type": "array",
          "description": "Rules are the isolation rules, run in order (included rules follow)."
        }
      },
      "additionalProperties": false,
      "type": "object",
      "description": "Lint configures `axx lint`: test-data isolation rules."
    },
    "LintConfig": {
      "properties": {
        "baseDir": {
          "type": "string",
          "description": "BaseDir is the directory file patterns are resolved against, relative\nto axx.yaml. Default: the directory of axx.yaml."
        },
        "mode": {
          "type": "string",
          "enum": [
            "error",
            "warn"
          ],
          "description": "Mode is \"error\" (violations make `axx lint` exit 3, default) or \"warn\"\n(violations are reported but do not fail)."
        },
        "maxFileSize": {
          "type": "integer",
          "minimum": 1,
          "description": "MaxFileSize skips files larger than this many bytes, with a warning.\nDefault: 5242880 (5 MB)."
        },
        "maxReportedValues": {
          "type": "integer",
          "minimum": 1,
          "description": "MaxReportedValues is how many duplicate values the human output lists\nper rule before truncating. Default: 3. Machine formats list all."
        },
        "maxReportedLocations": {
          "type": "integer",
          "minimum": 1,
          "description": "MaxReportedLocations is how many locations the human output lists per\nduplicate value before truncating. Default: 5. Machine formats list all."
        }
      },
      "additionalProperties": false,
      "type": "object",
      "description": "LintConfig holds the settings shared by every lint rule."
    },
    "LintRule": {
      "properties": {
        "name": {
          "type": "string",
          "description": "Name identifies the rule in reports."
        },
        "description": {
          "type": "string",
          "description": "Description says why the values must be unique; shown with violations."
        },
        "filePatterns": {
          "$ref": "#/$defs/StringList",
          "description": "FilePatterns are glob patterns (Java glob syntax: *, **, ?, [abc],\n{a,b}) selecting the files to scan, relative to baseDir. The part\nbefore the first wildcard is a directory (it may start with ../); the\nrest is matched against the end of each file path below it, so\n\"seeds/*.yaml\" also finds YAML files in subdirectories of seeds. A\npattern that ends in \"/\" matches folders, and all the files below\neach folder it matches count as one file: a scenario whose data spans\nseveral files keeps them in a folder of its own (\"seeds/*/\"), whose\nfiles may share values that no other file may."
        },
        "excludePatterns": {
          "$ref": "#/$defs/StringList",
          "description": "ExcludePatterns are glob patterns removing files from the match set,\nmatched against the whole path relative to baseDir; ** crosses\ndirectories, e.g. \"**.fixture.yaml\"."
        },
        "type": {
          "type": "string",
          "enum": [
            "regex",
            "jsonpath"
          ],
          "description": "Type is \"regex\" (default: extract values with regex) or \"jsonpath\"\n(read each file, by its extension, as JSON, YAML, XML or CSV, and\nextract the values at jsonPath)."
        },
        "regex": {
          "type": "string",
          "description": "Regex is a Java regular expression, applied in MULTILINE mode (^ and $\nmatch at line boundaries). Each match contributes the text of its\nfirst participating capture group."
        },
        "jsonPath": {
          "type": "string",
          "description": "JSONPath selects the values of a jsonpath rule, e.g. \"order.id\",\n\"payments[*].id\" or \"$.items[0].sku\" (exact field paths with array\nindexes or [*]); other JSONPath expressions (\"$..id\", filters) are\nevaluated like Jayway JsonPath. Only scalar values count. A seed's rows\nread alike in YAML, flat XML (\u003cdataset\u003e) and CSV: \"$['public.closures'][*]['id']\";\nin other XML, attributes are \"@name\" fields (\"line[*].@sku\") and an\nelement with only text is a value."
        },
        "validation": {
          "type": "string",
          "enum": [
            "global-unique",
            "file-unique",
            "cross-file-unique"
          ],
          "description": "Validation is \"global-unique\" (default: a value may occur once),\n\"file-unique\" (no repeats within a file; files may share values) or\n\"cross-file-unique\" (repeats within one file are fine; no two files\nmay share a value). A folder a folder pattern matches counts as one\nfile."
        },
        "mode": {
          "type": "string",
          "enum": [
            "error",
            "warn"
          ],
          "description": "Mode overrides config.mode for this rule: \"error\" or \"warn\"."
        },
        "ignoreValues": {
          "$ref": "#/$defs/ScalarList",
          "description": "IgnoreValues are values never reported (intentionally shared constants)."
        }
      },
      "additionalProperties": false,
      "type": "object",
      "required": [
        "name",
        "filePatterns"
      ],
      "description": "LintRule is one test-data isolation rule."
    },
    "OpenAPI": {
      "properties": {
        "levels": {
          "additionalProperties": {
            "type": "string"
          },
          "type": "object"
        }
      },
      "additionalProperties": false,
      "type": "object",
      "description": "OpenAPI holds default validation levels, keyed by validation message key (e.g."
    },
    "Reporter": {
      "oneOf": [
        {
          "type": "string",
          "enum": [
            "pretty",
            "progress",
            "compact",
            "junit",
            "messages",
            "cucumber-json",
            "html",
            "agent",
            "teamcity"
          ]
        },
        {
          "properties": {
            "pretty": {
              "type": "string",
              "description": "output file"
            },
            "progress": {
              "type": "string",
              "description": "output file"
            },
            "compact": {
              "type": "string",
              "description": "output file"
            },
            "junit": {
              "type": "string",
              "description": "output file"
            },
            "messages": {
              "type": "string",
              "description": "output file"
            },
            "cucumber-json": {
              "type": "string",
              "description": "output file"
            },
            "html": {
              "type": "string",
              "description": "output file"
            },
            "agent": {
              "type": "string",
              "description": "output file"
            },
            "teamcity": {
              "type": "string",
              "description": "output file"
            }
          },
          "additionalProperties": false,
          "type": "object",
          "maxProperties": 1,
          "minProperties": 1
        }
      ]
    },
    "Run": {
      "properties": {
        "paths": {
          "items": {
            "type": "string"
          },
          "type": "array",
          "description": "Paths are feature files or directories. Default: [\"features\"]."
        },
        "tags": {
          "type": "string",
          "description": "Tags is a default tag expression, e.g. \"not @wip\". It narrows whole runs\nand the directories named on the command line; a feature file named\nthere runs the scenarios it selects whatever their tags."
        },
        "uses": {
          "items": {
            "type": "string"
          },
          "type": "array",
          "description": "Uses keeps the scenarios that use a step of one of these packs, like\n[mobile-ios] for the scenarios that drive an iOS app: a profile per\nplatform, with no tags. Like tags, it narrows whole runs, not the\nfeature files named on the command line."
        },
        "workers": {
          "$ref": "#/$defs/Workers",
          "description": "Workers is the number of scenarios run in parallel: a number or \"auto\"\n(number of CPUs). Default: auto."
        },
        "exclusive": {
          "items": {
            "type": "string"
          },
          "type": "array",
          "description": "Exclusive lists tags whose scenarios run alone, after the parallel phase."
        },
        "order": {
          "type": "string",
          "description": "Order is \"defined\" (default) or \"random[:seed]\"."
        },
        "timeouts": {
          "$ref": "#/$defs/Timeouts"
        },
        "watch": {
          "type": "boolean",
          "description": "Watch shows what the scenarios do, as they do it: the packs that can\nshow it on screen do (browsers and devices in their windows). Same as\naxx run --watch."
        },
        "slowdown": {
          "$ref": "#/$defs/Duration",
          "description": "Slowdown pauses after each action of a watched run (a click, a tap, a\ntyped field), so a person can follow it, e.g. \"500ms\". Same as\naxx run --slowdown."
        },
        "reporters": {
          "items": {
            "$ref": "#/$defs/Reporter"
          },
          "type": "array",
          "description": "Reporters, e.g. [\"pretty\", {\"junit\": \"build/axx/junit.xml\"}]."
        }
      },
      "additionalProperties": false,
      "type": "object",
      "description": "Run controls execution."
    },
    "ScalarList": {
      "items": {
        "oneOf": [
          {
            "type": "string"
          },
          {
            "type": "number"
          },
          {
            "type": "boolean"
          }
        ]
      },
      "type": "array"
    },
    "Services": {
      "additionalProperties": {
        "properties": {
          "enabled": {
            "type": "boolean",
            "description": "Enabled defaults to true."
          },
          "dir": {
            "type": "string",
            "description": "Dir is the working directory, relative to axx.yaml."
          },
          "command": {
            "oneOf": [
              {
                "type": "string",
                "description": "command line, split into words without a shell unless shell: true"
              },
              {
                "items": {
                  "type": "string"
                },
                "type": "array",
                "minItems": 1,
                "description": "argv"
              }
            ],
            "description": "Command starts the service: a string (split like a shell word list, without\na shell) or an argv list. Set shell: true to run it through the shell."
          },
          "shell": {
            "type": "boolean",
            "description": "Shell runs command and cleanup through /bin/sh -c (cmd /C on Windows)."
          },
          "env": {
            "additionalProperties": {
              "type": "string"
            },
            "type": "object"
          },
          "dependsOn": {
            "items": {
              "type": "string"
            },
            "type": "array",
            "description": "DependsOn names services that must be ready first."
          },
          "ready": {
            "properties": {
              "http": {
                "properties": {
                  "url": {
                    "oneOf": [
                      {
                        "type": "string"
                      },
                      {
                        "items": {
                          "type": "string"
                        },
                        "type": "array"
                      }
                    ]
                  }
                },
                "additionalProperties": false,
                "type": "object",
                "required": [
                  "url"
                ]
              },
              "tcp": {
                "type": "string",
                "description": "TCP is a host:port that must accept connections."
              },
              "exec": {
                "oneOf": [
                  {
                    "type": "string",
                    "description": "command line, split into words without a shell unless shell: true"
                  },
                  {
                    "items": {
                      "type": "string"
                    },
                    "type": "array",
                    "minItems": 1,
                    "description": "argv"
                  }
                ],
                "description": "Exec is a command that must exit 0."
              },
              "log": {
                "type": "string",
                "description": "Log is a regular expression matched against the service's output."
              },
              "timeout": {
                "oneOf": [
                  {
                    "type": "string",
                    "pattern": "^\\s*([0-9]+(\\.[0-9]+)?(ns|us|µs|ms|s|m|h))+\\s*$",
                    "examples": [
                      "30s",
                      "1m30s",
                      "250ms"
                    ]
                  },
                  {
                    "type": "number",
                    "minimum": 0,
                    "description": "seconds"
                  }
                ],
                "description": "Timeout for the service to become ready. Default: 60s."
              },
              "interval": {
                "oneOf": [
                  {
                    "type": "string",
                    "pattern": "^\\s*([0-9]+(\\.[0-9]+)?(ns|us|µs|ms|s|m|h))+\\s*$",
                    "examples": [
                      "30s",
                      "1m30s",
                      "250ms"
                    ]
                  },
                  {
                    "type": "number",
                    "minimum": 0,
                    "description": "seconds"
                  }
                ],
                "description": "Interval between checks. Default: 1s."
              }
            },
            "additionalProperties": false,
            "type": "object"
          },
          "stop": {
            "properties": {
              "signal": {
                "type": "string",
                "enum": [
                  "SIGTERM",
                  "SIGINT"
                ],
                "description": "Signal sent first: SIGTERM (default) or SIGINT."
              },
              "grace": {
                "oneOf": [
                  {
                    "type": "string",
                    "pattern": "^\\s*([0-9]+(\\.[0-9]+)?(ns|us|µs|ms|s|m|h))+\\s*$",
                    "examples": [
                      "30s",
                      "1m30s",
                      "250ms"
                    ]
                  },
                  {
                    "type": "number",
                    "minimum": 0,
                    "description": "seconds"
                  }
                ],
                "description": "Grace period before the process group is killed. Default: 10s."
              }
            },
            "additionalProperties": false,
            "type": "object"
          },
          "cleanup": {
            "oneOf": [
              {
                "type": "string",
                "description": "command line, split into words without a shell unless shell: true"
              },
              {
                "items": {
                  "type": "string"
                },
                "type": "array",
                "minItems": 1,
                "description": "argv"
              }
            ],
            "description": "Cleanup runs after the service stops, even if it crashed."
          },
          "active": {
            "properties": {
              "tags": {
                "items": {
                  "type": "string"
                },
                "type": "array",
                "description": "Tags start the service for the scenarios that have one of them."
              },
              "uses": {
                "items": {
                  "type": "string"
                },
                "type": "array",
                "description": "Uses starts the service for the scenarios that use a step of one of these\npacks, like [mobile-android]: no tag needed."
              }
            },
            "additionalProperties": false,
            "type": "object"
          },
          "debug": {
            "properties": {
              "command": {
                "oneOf": [
                  {
                    "type": "string",
                    "description": "command line, split into words without a shell unless shell: true"
                  },
                  {
                    "items": {
                      "type": "string"
                    },
                    "type": "array",
                    "minItems": 1,
                    "description": "argv"
                  }
                ],
                "description": "Command replaces the normal command in debug mode."
              },
              "debugger": {
                "properties": {
                  "type": {
                    "type": "string",
                    "enum": [
                      "java",
                      "go",
                      "nodejs",
                      "python"
                    ]
                  },
                  "port": {
                    "type": "integer"
                  },
                  "host": {
                    "type": "string"
                  },
                  "module": {
                    "type": "string"
                  },
                  "mode": {
                    "type": "string",
                    "enum": [
                      "ide-listens",
                      "app-listens"
                    ],
                    "description": "Mode is \"ide-listens\" (the IDE listens and the service connects, e.g. JDWP\nserver=n; default for java) or \"app-listens\" (delve, --inspect, debugpy)."
                  }
                },
                "additionalProperties": false,
                "type": "object",
                "required": [
                  "port"
                ]
              },
              "onUnavailable": {
                "type": "string",
                "enum": [
                  "fail",
                  "fallback",
                  "retry"
                ],
                "description": "OnUnavailable decides what happens when the debugger is not listening:\nfail, fallback (run the normal command) or retry (default)."
              },
              "retry": {
                "properties": {
                  "attempts": {
                    "type": "integer"
                  },
                  "delay": {
                    "oneOf": [
                      {
                        "type": "string",
                        "pattern": "^\\s*([0-9]+(\\.[0-9]+)?(ns|us|µs|ms|s|m|h))+\\s*$",
                        "examples": [
                          "30s",
                          "1m30s",
                          "250ms"
                        ]
                      },
                      {
                        "type": "number",
                        "minimum": 0,
                        "description": "seconds"
                      }
                    ]
                  }
                },
                "additionalProperties": false,
                "type": "object"
              }
            },
            "additionalProperties": false,
            "type": "object"
          }
        },
        "additionalProperties": false,
        "type": "object",
        "required": [
          "command"
        ],
        "description": "Service is an application or piece of infrastructure managed by axx."
      },
      "type": "object"
    },
    "StringList": {
      "oneOf": [
        {
          "type": "string"
        },
        {
          "items": {
            "type": "string"
          },
          "type": "array"
        }
      ]
    },
    "Timeouts": {
      "properties": {
        "step": {
          "$ref": "#/$defs/Duration"
        },
        "scenario": {
          "$ref": "#/$defs/Duration"
        },
        "hook": {
          "$ref": "#/$defs/Duration"
        }
      },
      "additionalProperties": false,
      "type": "object",
      "description": "Timeouts bound steps, scenarios and hooks."
    },
    "Workers": {
      "oneOf": [
        {
          "type": "integer",
          "minimum": 1
        },
        {
          "type": "string",
          "enum": [
            "auto"
          ]
        }
      ]
    }
  },
  "properties": {
    "version": {
      "type": "integer",
      "enum": [
        1
      ],
      "description": "Schema version of this file. Currently 1."
    },
    "run": {
      "$ref": "#/$defs/Run",
      "description": "Run controls which features run and how."
    },
    "resources": {
      "items": {
        "type": "string"
      },
      "type": "array",
      "description": "Resources are directories that relative resource paths in steps (seed\nfiles, schemas, payloads) resolve against, in order, once the feature\nfile's own directory has not got the file. The directory of axx.yaml is\nalways searched last."
    },
    "properties": {
      "additionalProperties": {
        "type": "string"
      },
      "type": "object",
      "description": "Properties back ${sys:name} references. `-D name=value` overrides them."
    },
    "openapi": {
      "$ref": "#/$defs/OpenAPI",
      "description": "OpenAPI holds default OpenAPI validation settings for REST services."
    },
    "active": {
      "$ref": "#/$defs/Active",
      "description": "Active starts only the applications needed by the selected scenarios."
    },
    "services": {
      "$ref": "#/$defs/Services",
      "description": "Services are the applications under test (and their infrastructure),\nstarted before scenarios run and stopped afterwards, in declaration order."
    },
    "packs": {
      "additionalProperties": true,
      "type": "object",
      "description": "Packs holds pack settings keyed by pack name."
    },
    "lint": {
      "$ref": "#/$defs/Lint",
      "description": "Lint configures `axx lint`: test-data isolation rules that keep values\nsuch as seed ids unique so scenarios can run in parallel."
    },
    "fixtures": {
      "$ref": "#/$defs/Fixtures",
      "description": "Fixtures configures `axx fixtures` (fixture factories)."
    },
    "profiles": {
      "additionalProperties": true,
      "type": "object",
      "description": "Profiles are named overlays selected with --profile or AXX_PROFILE."
    }
  },
  "additionalProperties": false,
  "type": "object",
  "title": "axx.yaml",
  "description": "Configuration for axx, the acceptance testing framework that verifies running software black-box (https://axx.nimbusxr.us)."
}
