{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "$id": "https://budvo.nulldaily.com/schemas/addon-manifest-v1.json",
  "title": "Budvo add-on manifest (v1)",
  "description": "manifest.json at the root of a Budvo add-on. See https://budvo.nulldaily.com/docs/addons/manifest",
  "type": "object",
  "required": [
    "manifest",
    "id",
    "name",
    "version",
    "kind",
    "runtime",
    "publisher",
    "compat",
    "entry",
    "permissions"
  ],
  "additionalProperties": false,
  "properties": {
    "$schema": {
      "type": "string"
    },
    "manifest": {
      "const": 1,
      "description": "Manifest format version. Always 1 for now."
    },
    "id": {
      "type": "string",
      "pattern": "^[a-z0-9]+(-[a-z0-9]+)*$",
      "minLength": 3,
      "maxLength": 40,
      "description": "Lowercase letters, digits and dashes, 3 to 40 characters. Unique."
    },
    "name": {
      "type": "string",
      "minLength": 1,
      "maxLength": 40
    },
    "version": {
      "type": "string",
      "pattern": "^(0|[1-9]\\d*)\\.(0|[1-9]\\d*)\\.(0|[1-9]\\d*)(-[0-9A-Za-z.-]+)?(\\+[0-9A-Za-z.-]+)?$",
      "description": "Semver of the add-on, like 1.2.0."
    },
    "kind": {
      "enum": [
        "theme",
        "feature",
        "connector"
      ]
    },
    "runtime": {
      "enum": [
        "theme@1",
        "app@1",
        "connector@1"
      ]
    },
    "publisher": {
      "type": "string",
      "minLength": 1,
      "maxLength": 60
    },
    "summary": {
      "type": "string",
      "maxLength": 200,
      "description": "One sentence for the catalog."
    },
    "compat": {
      "type": "object",
      "required": [
        "budvo"
      ],
      "additionalProperties": false,
      "properties": {
        "budvo": {
          "type": "string",
          "minLength": 1,
          "description": "Semver range of Budvo server versions, like \">=0.4.0 <1.0.0\"."
        },
        "sdk": {
          "type": "string",
          "minLength": 1,
          "description": "Semver range of the SDK / bridge protocol (app@1), like \"^1.0.0\"."
        }
      }
    },
    "entry": {
      "type": "string",
      "minLength": 1,
      "pattern": "^(?!/)(?!.*\\\\)(?!(.*/)?\\.\\.?(/|$)).+$",
      "description": "theme@1: the tokens file. app@1: the HTML page loaded in the sandbox. A path inside the package."
    },
    "icon": {
      "type": "string",
      "pattern": "^(?!/)(?!.*\\\\)(?!(.*/)?\\.\\.?(/|$)).+\\.(png|svg)$",
      "description": "A PNG or SVG in the package, about 256x256."
    },
    "permissions": {
      "type": "array",
      "uniqueItems": true,
      "items": {
        "enum": [
          "summary:read",
          "debts:read",
          "goals:read",
          "jar:read",
          "jar:pledge",
          "storage",
          "net",
          "league:share"
        ]
      }
    },
    "pledge": {
      "type": "object",
      "required": [
        "daily_max_cents",
        "reason"
      ],
      "additionalProperties": false,
      "properties": {
        "daily_max_cents": {
          "type": "integer",
          "minimum": 1,
          "description": "The most the add-on may pledge per day."
        },
        "reason": {
          "type": "string",
          "minLength": 1,
          "maxLength": 200,
          "description": "A sentence shown to the owner."
        }
      }
    },
    "league": {
      "type": "object",
      "required": [
        "shares"
      ],
      "additionalProperties": false,
      "properties": {
        "shares": {
          "type": "string",
          "minLength": 1,
          "maxLength": 120,
          "description": "What the add-on shares with a league, shown to the owner and each person, like \"display name, picks and points\"."
        }
      }
    },
    "network": {
      "type": "array",
      "uniqueItems": true,
      "items": {
        "type": "string",
        "pattern": "^(?=.{1,253}$)([a-z0-9]([a-z0-9-]{0,61}[a-z0-9])?\\.)+[a-z]{2,63}$",
        "description": "A hostname, like api.example.com. No scheme, port, path or wildcard."
      }
    },
    "ui": {
      "type": "object",
      "additionalProperties": false,
      "properties": {
        "nav": {
          "type": "object",
          "required": [
            "label",
            "icon"
          ],
          "additionalProperties": false,
          "properties": {
            "label": {
              "type": "string",
              "minLength": 1,
              "maxLength": 24
            },
            "icon": {
              "type": "string",
              "pattern": "^[a-z0-9]+(-[a-z0-9]+)*$",
              "description": "A Lucide icon name, like paw-print."
            }
          }
        }
      }
    },
    "description": {
      "type": "string",
      "description": "Catalog: long description (markdown). Defaults to README.md."
    },
    "tags": {
      "type": "array",
      "maxItems": 8,
      "items": {
        "type": "string",
        "pattern": "^[a-z0-9-]{1,24}$"
      }
    },
    "notes": {
      "type": "string",
      "description": "Catalog: release notes for this version (markdown)."
    },
    "price_cents": {
      "type": "integer",
      "minimum": 0
    },
    "billing": {
      "enum": [
        "free",
        "one_time",
        "subscription"
      ]
    },
    "currency": {
      "type": "string",
      "pattern": "^[A-Z]{3}$"
    },
    "min_server_version": {
      "type": "string",
      "description": "Deprecated: servers before add-ons v1 read this. Must equal the lowest version compat.budvo allows."
    }
  },
  "allOf": [
    {
      "if": {
        "properties": {
          "runtime": {
            "const": "theme@1"
          }
        },
        "required": [
          "runtime"
        ]
      },
      "then": {
        "allOf": [
          {
            "x-message": "theme@1 add-ons must have kind \"theme\"",
            "properties": {
              "kind": {
                "const": "theme"
              }
            }
          },
          {
            "x-message": "theme@1 add-ons can't ask for permissions, pledges, network access or a nav page",
            "properties": {
              "permissions": {
                "maxItems": 0
              }
            },
            "not": {
              "anyOf": [
                {
                  "required": [
                    "pledge"
                  ]
                },
                {
                  "required": [
                    "ui"
                  ]
                },
                {
                  "required": [
                    "network"
                  ],
                  "properties": {
                    "network": {
                      "minItems": 1
                    }
                  }
                }
              ]
            }
          }
        ]
      }
    },
    {
      "if": {
        "properties": {
          "runtime": {
            "const": "app@1"
          }
        },
        "required": [
          "runtime"
        ]
      },
      "then": {
        "allOf": [
          {
            "x-message": "app@1 add-ons must have kind \"feature\"",
            "properties": {
              "kind": {
                "const": "feature"
              }
            }
          },
          {
            "x-message": "app@1 add-ons need compat.sdk, like \"^1.0.0\"",
            "properties": {
              "compat": {
                "required": [
                  "sdk"
                ]
              }
            }
          },
          {
            "x-message": "an app@1 entry must be an .html page",
            "properties": {
              "entry": {
                "pattern": "\\.html?$"
              }
            }
          }
        ]
      }
    },
    {
      "if": {
        "properties": {
          "runtime": {
            "const": "connector@1"
          }
        },
        "required": [
          "runtime"
        ]
      },
      "then": {
        "x-message": "connector@1 add-ons must have kind \"connector\"",
        "properties": {
          "kind": {
            "const": "connector"
          }
        }
      }
    },
    {
      "if": {
        "properties": {
          "permissions": {
            "contains": {
              "const": "jar:pledge"
            }
          }
        },
        "required": [
          "permissions"
        ]
      },
      "then": {
        "x-message": "the jar:pledge permission needs a pledge object with daily_max_cents and reason",
        "required": [
          "pledge"
        ]
      },
      "else": {
        "x-message": "pledge is only for add-ons with the jar:pledge permission",
        "not": {
          "required": [
            "pledge"
          ]
        }
      }
    },
    {
      "if": {
        "properties": {
          "permissions": {
            "contains": {
              "const": "net"
            }
          }
        },
        "required": [
          "permissions"
        ]
      },
      "then": {
        "x-message": "the net permission needs at least one hostname in network",
        "required": [
          "network"
        ],
        "properties": {
          "network": {
            "minItems": 1
          }
        }
      },
      "else": {
        "x-message": "network lists hosts for net.fetch; add the net permission or leave it empty",
        "properties": {
          "network": {
            "maxItems": 0
          }
        }
      }
    },
    {
      "if": {
        "properties": {
          "permissions": {
            "contains": {
              "const": "league:share"
            }
          }
        },
        "required": [
          "permissions"
        ]
      },
      "then": {
        "x-message": "the league:share permission needs a league object with shares",
        "required": [
          "league"
        ]
      },
      "else": {
        "x-message": "league is only for add-ons with the league:share permission",
        "not": {
          "required": [
            "league"
          ]
        }
      }
    }
  ]
}
