{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "$id": "https://openspatials.com/developers/spec/0.5/openspatials-site.schema.json",
  "title": "OpenSpatials site file, version 0.5",
  "description": "Spec: src/spec/core/APP-PROFILE-AND-DIRECTORY.md. One file for a whole site, served at /.well-known/openspatials-site.json at the origin root only (RFC 8615: a .well-known folder below the root is not a well-known URI), listing its worlds by address and where each world's openspatials-world.json lives, with room for other site-wide information later: unknown fields are allowed and ignored, so the file can grow. Each entry: url, the world's start page (absolute, or relative to the site root); profile, the address of its openspatials-world.json (absolute, or relative to the site root), or, for a one-world site, the profile object itself (checked as a 0.5 world profile; a reader treats the site file's own address as that profile's address); name, the world's name. A reader takes, from the entries whose url is on the site file's own origin, the one whose url equals the destination's start page (same origin and path, ignoring query and fragment, and a final index.html), else the one whose url folder is the longest folder that contains it, then follows its profile inside the lookup's one deadline. Limit 64 KiB, the profile limit, checked on the bytes before parsing. IANA registration of the name at the 0.5.0 publication.",
  "type": "object",
  "allOf": [
    {
      "$ref": "#/$defs/noCode"
    },
    {
      "required": [
        "openspatials"
      ]
    }
  ],
  "required": [
    "worlds"
  ],
  "properties": {
    "openspatials": {
      "description": "The site file's version: \"0.5\".",
      "x-openspatials-reason": "unknown-version",
      "enum": [
        "0.5"
      ]
    },
    "worlds": {
      "description": "The worlds this site serves, one entry per world.",
      "type": "array",
      "maxItems": 1000,
      "items": {
        "$ref": "#/$defs/world"
      }
    }
  },
  "$defs": {
    "world": {
      "type": "object",
      "required": [
        "url",
        "profile"
      ],
      "properties": {
        "url": {
          "description": "The world's start page: absolute, or relative to the site root.",
          "$ref": "#/$defs/docUrl"
        },
        "profile": {
          "description": "The address of the world's openspatials-world.json (absolute, or relative to the site root), or, for a one-world site, the world profile itself.",
          "if": {
            "type": "object"
          },
          "then": {
            "$ref": "https://openspatials.com/developers/spec/0.5/openspatials-world-profile.schema.json"
          },
          "else": {
            "$ref": "#/$defs/docUrl"
          }
        },
        "name": {
          "type": "string",
          "minLength": 1,
          "maxLength": 64,
          "pattern": "\\S"
        }
      }
    },
    "docUrl": {
      "description": "An address relative to the site root, or an absolute https address (http on a loopback host for local testing). White space makes it invalid-file. Any other scheme, a private network host, user info or a protocol-relative address is unsafe-file.",
      "type": "string",
      "minLength": 1,
      "maxLength": 2048,
      "pattern": "^\\S+$",
      "if": {
        "type": "string",
        "pattern": "^\\S+$"
      },
      "then": {
        "x-openspatials-reason": "unsafe-file",
        "anyOf": [
          {
            "$ref": "#/$defs/httpsUrl"
          },
          {
            "$ref": "#/$defs/loopbackHttpUrl"
          },
          {
            "type": "string",
            "pattern": "^(?![A-Za-z][A-Za-z0-9+.-]*:)(?![\\\\/][\\\\/])\\S+$"
          }
        ]
      }
    },
    "httpsUrl": {
      "type": "string",
      "pattern": "^https://(?!(?:10|0)\\.\\d)(?!192\\.168\\.)(?!169\\.254\\.)(?!172\\.(?:1[6-9]|2\\d|3[01])\\.)(?!\\[[fF][cdeCDE])[^\\s/?#@\\\\]+(?:[/?#]\\S*)?$"
    },
    "loopbackHttpUrl": {
      "type": "string",
      "pattern": "^http://(?:127\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}|localhost|\\[::1\\])(?::\\d{1,5})?(?:[/?#]\\S*)?$"
    },
    "noCode": {
      "description": "04 section 1, closed list, at any depth: a key named script, scripts, eval or javascript; a key that is 'on' followed by a capital letter; a string value starting with javascript:, vbscript: or data:text/html (any case, after leading white space).",
      "x-openspatials-reason": "unsafe-file",
      "propertyNames": {
        "not": {
          "pattern": "^(?:script|scripts|eval|javascript|on[A-Z].*)$"
        }
      },
      "additionalProperties": {
        "$ref": "#/$defs/noCode"
      },
      "items": {
        "$ref": "#/$defs/noCode"
      },
      "not": {
        "type": "string",
        "pattern": "^\\s*(?:[Jj][Aa][Vv][Aa][Ss][Cc][Rr][Ii][Pp][Tt]:|[Vv][Bb][Ss][Cc][Rr][Ii][Pp][Tt]:|[Dd][Aa][Tt][Aa]:[Tt][Ee][Xx][Tt]/[Hh][Tt][Mm][Ll])"
      }
    }
  }
}
