{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "$id": "https://docs.keepclub.app/schema/course-v2.schema.json",
  "title": "keep club course format 2",
  "description": "The parsed data model for a human-authored course.keep.yml file.",
  "type": "object",
  "additionalProperties": false,
  "required": [
    "schemaVersion",
    "courseId",
    "cards"
  ],
  "properties": {
    "schemaVersion": {
      "const": 2
    },
    "courseId": {
      "$ref": "#/$defs/id"
    },
    "title": {
      "$ref": "#/$defs/title"
    },
    "shortTitle": {
      "$ref": "#/$defs/shortTitle"
    },
    "tagline": {
      "$ref": "#/$defs/tagline"
    },
    "description": {
      "$ref": "#/$defs/markdown"
    },
    "contentLanguage": {
      "$ref": "#/$defs/languageTag"
    },
    "instructionLanguage": {
      "$ref": "#/$defs/languageTag"
    },
    "authors": {
      "type": "array",
      "maxItems": 32,
      "items": {
        "$ref": "#/$defs/person"
      }
    },
    "license": {
      "$ref": "#/$defs/license"
    },
    "source": {
      "$ref": "#/$defs/courseSource"
    },
    "sections": {
      "type": "array",
      "maxItems": 1000,
      "items": {
        "$ref": "#/$defs/section"
      }
    },
    "groups": {
      "type": "array",
      "maxItems": 250,
      "items": {
        "$ref": "#/$defs/group"
      }
    },
    "cards": {
      "type": "array",
      "minItems": 1,
      "maxItems": 50000,
      "items": {
        "$ref": "#/$defs/card"
      }
    },
    "theme": {
      "$ref": "#/$defs/theme"
    },
    "extensions": {
      "$ref": "#/$defs/extensions"
    }
  },
  "$defs": {
    "id": {
      "type": "string",
      "minLength": 1,
      "maxLength": 128,
      "pattern": "^[a-z0-9][a-z0-9._-]{0,127}$"
    },
    "title": {
      "type": "string",
      "minLength": 1,
      "maxLength": 200
    },
    "shortTitle": {
      "type": "string",
      "minLength": 1,
      "maxLength": 60
    },
    "tagline": {
      "type": "string",
      "minLength": 1,
      "maxLength": 200
    },
    "markdown": {
      "type": "string",
      "minLength": 1,
      "maxLength": 262144
    },
    "languageTag": {
      "type": "string",
      "minLength": 2,
      "maxLength": 63,
      "pattern": "^[A-Za-z]{2,8}(?:-[A-Za-z0-9]{1,8})*$"
    },
    "httpsUrl": {
      "type": "string",
      "minLength": 9,
      "maxLength": 2048,
      "pattern": "^https://[^\\s]+$"
    },
    "email": {
      "type": "string",
      "minLength": 3,
      "maxLength": 254,
      "pattern": "^[^\\s@]+@[^\\s@]+\\.[^\\s@]+$"
    },
    "assetPath": {
      "type": "string",
      "minLength": 1,
      "maxLength": 240,
      "pattern": "^(?!/)(?![A-Za-z][A-Za-z0-9+.-]*:)(?!.*(?:^|/)\\.\\.?/)(?!.*(?:^|/)\\.\\.?$)(?!.*//)(?!.*\\\\)(?!.*%(?:2[eE]|2[fF]|5[cC]))[^\\u0000-\\u001F\\u007F]+$"
    },
    "person": {
      "type": "object",
      "additionalProperties": false,
      "required": [
        "name"
      ],
      "properties": {
        "name": {
          "type": "string",
          "minLength": 1,
          "maxLength": 200
        },
        "website": {
          "$ref": "#/$defs/httpsUrl"
        },
        "email": {
          "$ref": "#/$defs/email"
        }
      }
    },
    "credit": {
      "type": "object",
      "additionalProperties": false,
      "required": [
        "name"
      ],
      "properties": {
        "name": {
          "type": "string",
          "minLength": 1,
          "maxLength": 200
        },
        "website": {
          "$ref": "#/$defs/httpsUrl"
        }
      }
    },
    "license": {
      "type": "object",
      "additionalProperties": false,
      "minProperties": 1,
      "properties": {
        "identifier": {
          "type": "string",
          "minLength": 1,
          "maxLength": 100
        },
        "name": {
          "type": "string",
          "minLength": 1,
          "maxLength": 200
        },
        "website": {
          "$ref": "#/$defs/httpsUrl"
        },
        "attribution": {
          "type": "string",
          "minLength": 1,
          "maxLength": 2000
        }
      }
    },
    "courseSource": {
      "type": "object",
      "additionalProperties": false,
      "minProperties": 1,
      "properties": {
        "website": {
          "$ref": "#/$defs/httpsUrl"
        },
        "repository": {
          "$ref": "#/$defs/httpsUrl"
        }
      }
    },
    "section": {
      "type": "object",
      "additionalProperties": false,
      "required": [
        "sectionId",
        "title"
      ],
      "properties": {
        "sectionId": {
          "$ref": "#/$defs/id"
        },
        "title": {
          "$ref": "#/$defs/title"
        },
        "description": {
          "$ref": "#/$defs/markdown"
        },
        "extensions": {
          "$ref": "#/$defs/extensions"
        }
      }
    },
    "group": {
      "type": "object",
      "additionalProperties": false,
      "required": [
        "groupId",
        "title",
        "sectionIds"
      ],
      "properties": {
        "groupId": {
          "$ref": "#/$defs/id"
        },
        "title": {
          "$ref": "#/$defs/title"
        },
        "description": {
          "$ref": "#/$defs/markdown"
        },
        "sectionIds": {
          "type": "array",
          "minItems": 1,
          "maxItems": 1000,
          "uniqueItems": true,
          "items": {
            "$ref": "#/$defs/id"
          }
        },
        "extensions": {
          "$ref": "#/$defs/extensions"
        }
      }
    },
    "card": {
      "type": "object",
      "additionalProperties": false,
      "required": [
        "cardId"
      ],
      "properties": {
        "cardId": {
          "$ref": "#/$defs/id"
        },
        "front": {
          "$ref": "#/$defs/markdown"
        },
        "back": {
          "type": "string",
          "maxLength": 262144
        },
        "sectionId": {
          "$ref": "#/$defs/id"
        },
        "tags": {
          "type": "array",
          "maxItems": 64,
          "items": {
            "type": "string",
            "minLength": 1,
            "maxLength": 100
          }
        },
        "media": {
          "type": "array",
          "maxItems": 16,
          "items": {
            "$ref": "#/$defs/media"
          }
        },
        "extensions": {
          "$ref": "#/$defs/extensions"
        }
      },
      "anyOf": [
        {
          "required": [
            "front"
          ]
        },
        {
          "required": [
            "media"
          ],
          "properties": {
            "media": {
              "contains": {
                "type": "object",
                "required": [
                  "side"
                ],
                "properties": {
                  "side": {
                    "const": "front"
                  }
                }
              }
            }
          }
        }
      ]
    },
    "media": {
      "type": "object",
      "additionalProperties": false,
      "required": [
        "side",
        "mediaType",
        "source"
      ],
      "properties": {
        "mediaId": {
          "$ref": "#/$defs/id"
        },
        "side": {
          "enum": [
            "front",
            "back"
          ]
        },
        "mediaType": {
          "enum": [
            "image",
            "audio",
            "video"
          ]
        },
        "source": {
          "$ref": "#/$defs/assetPath"
        },
        "mimeType": {
          "type": "string",
          "minLength": 3,
          "maxLength": 100,
          "pattern": "^(?:image|audio|video)/[A-Za-z0-9!#$&^_.+-]+$"
        },
        "alternativeText": {
          "type": "string",
          "minLength": 1,
          "maxLength": 2000
        },
        "decorative": {
          "type": "boolean"
        },
        "caption": {
          "$ref": "#/$defs/markdown"
        },
        "posterImage": {
          "$ref": "#/$defs/assetPath"
        },
        "transcript": {
          "$ref": "#/$defs/markdown"
        },
        "captionTracks": {
          "type": "array",
          "maxItems": 16,
          "items": {
            "$ref": "#/$defs/captionTrack"
          }
        },
        "durationSeconds": {
          "type": "number",
          "exclusiveMinimum": 0,
          "maximum": 86400
        },
        "width": {
          "type": "integer",
          "minimum": 1,
          "maximum": 32768
        },
        "height": {
          "type": "integer",
          "minimum": 1,
          "maximum": 32768
        },
        "credit": {
          "$ref": "#/$defs/credit"
        },
        "extensions": {
          "$ref": "#/$defs/extensions"
        }
      },
      "oneOf": [
        {
          "title": "Image media fields",
          "properties": {
            "mediaType": {
              "const": "image"
            }
          },
          "not": {
            "anyOf": [
              {
                "required": [
                  "posterImage"
                ]
              },
              {
                "required": [
                  "transcript"
                ]
              },
              {
                "required": [
                  "captionTracks"
                ]
              },
              {
                "required": [
                  "durationSeconds"
                ]
              }
            ]
          }
        },
        {
          "title": "Audio media fields",
          "properties": {
            "mediaType": {
              "const": "audio"
            }
          },
          "not": {
            "anyOf": [
              {
                "required": [
                  "alternativeText"
                ]
              },
              {
                "required": [
                  "decorative"
                ]
              },
              {
                "required": [
                  "posterImage"
                ]
              },
              {
                "required": [
                  "captionTracks"
                ]
              },
              {
                "required": [
                  "width"
                ]
              },
              {
                "required": [
                  "height"
                ]
              }
            ]
          }
        },
        {
          "title": "Video media fields",
          "properties": {
            "mediaType": {
              "const": "video"
            }
          },
          "not": {
            "anyOf": [
              {
                "required": [
                  "alternativeText"
                ]
              },
              {
                "required": [
                  "decorative"
                ]
              }
            ]
          }
        }
      ]
    },
    "captionTrack": {
      "type": "object",
      "additionalProperties": false,
      "required": [
        "source",
        "language"
      ],
      "properties": {
        "source": {
          "$ref": "#/$defs/assetPath"
        },
        "language": {
          "$ref": "#/$defs/languageTag"
        },
        "label": {
          "type": "string",
          "minLength": 1,
          "maxLength": 100
        },
        "default": {
          "type": "boolean"
        }
      }
    },
    "theme": {
      "type": "object",
      "additionalProperties": false,
      "minProperties": 1,
      "properties": {
        "accentColor": {
          "$ref": "#/$defs/color"
        },
        "accentColorDark": {
          "$ref": "#/$defs/color"
        },
        "accentInkColor": {
          "$ref": "#/$defs/color"
        },
        "accentInkColorDark": {
          "$ref": "#/$defs/color"
        },
        "paperColor": {
          "$ref": "#/$defs/color"
        },
        "paperColorDark": {
          "$ref": "#/$defs/color"
        },
        "shelfArtwork": {
          "$ref": "#/$defs/assetPath"
        },
        "sectionArtwork": {
          "$ref": "#/$defs/assetPath"
        },
        "loadingArtwork": {
          "$ref": "#/$defs/assetPath"
        },
        "loadingText": {
          "type": "string",
          "minLength": 1,
          "maxLength": 120
        },
        "loadingAnimation": {
          "enum": [
            "none",
            "gentle-bob",
            "pulse"
          ]
        }
      }
    },
    "color": {
      "type": "string",
      "pattern": "^#[0-9A-Fa-f]{6}(?:[0-9A-Fa-f]{2})?$"
    },
    "extensions": {
      "type": "object",
      "maxProperties": 100,
      "propertyNames": {
        "pattern": "^[a-z][a-z0-9-]*(?:\\.[a-z][a-z0-9-]*)+(?:/[A-Za-z0-9._~-]+)*$"
      },
      "additionalProperties": true
    }
  }
}
