{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "$id": "https://kanboombo.com/developers/plugins/plugin.schema.json",
  "title": "plugin.json: a Kanboombo calendar plug-in's manifest, contract 1",
  "description": "Generated from Kanboombo's plugins.schema.json. The checker (kanboombo-plugin.mjs check) is the authority; this helps your editor.",
  "$ref": "#/$defs/PluginManifest",
  "$defs": {
    "PluginAudience": {
      "description": "public: anyone signed in, and a Mac's proxy. private: the readers set at host.kanboombo.com (people and organizations). local: runs on your Mac only; refused by the Server until its slice ships.",
      "type": "string",
      "enum": [
        "public",
        "private",
        "local"
      ]
    },
    "PluginCadence": {
      "description": "How often the series has a new record. It tells readers what to expect; it does not schedule anything.",
      "type": "string",
      "enum": [
        "business-day",
        "daily",
        "weekly",
        "monthly",
        "quarterly",
        "irregular"
      ]
    },
    "PluginDates": {
      "description": "observation: a record's date is the day it describes. publication: the day it was published, with period naming what it describes.",
      "type": "string",
      "enum": [
        "observation",
        "publication"
      ]
    },
    "PluginId": {
      "description": "A plug-in's id: lowercase letters, digits and hyphens, starting with a letter, 2 to 48 characters. It is also its folder's name, and it is never reused.",
      "type": "string",
      "pattern": "^[a-z][a-z0-9-]{1,47}$",
      "errorMessage": "A plug-in id is lowercase letters, digits and hyphens, starting with a letter, 2 to 48 characters",
      "examples": [
        "dop-economy"
      ]
    },
    "PluginKey": {
      "description": "A key for a source, a series or a field: lowercase letters, digits, hyphens and underscores, starting with a letter.",
      "type": "string",
      "pattern": "^[a-z][a-z0-9_-]{0,47}$",
      "errorMessage": "A key is lowercase letters, digits, hyphens and underscores, starting with a letter",
      "examples": [
        "usd-dop",
        "compra"
      ]
    },
    "PluginLicence": {
      "description": "Who holds the data and what you may do with it. Readers see the attribution beside every record.",
      "type": "object",
      "properties": {
        "holder": {
          "description": "Who publishes the data, such as Banco Central de la República Dominicana.",
          "type": "string",
          "minLength": 1,
          "maxLength": 200
        },
        "attribution": {
          "description": "The short name shown beside its records, such as Banco Central.",
          "type": "string",
          "minLength": 1,
          "maxLength": 40
        },
        "terms": {
          "description": "A link to the terms the data is published under, or null when there are none to link.",
          "type": [
            "string",
            "null"
          ],
          "maxLength": 500
        },
        "retain": {
          "description": "Whether Kanboombo may keep the records: yes, no (they can be purged) or unknown.",
          "type": "string",
          "enum": [
            "yes",
            "no",
            "unknown"
          ]
        }
      },
      "required": [
        "holder",
        "terms",
        "retain"
      ],
      "additionalProperties": false
    },
    "PluginLimits": {
      "description": "Lower limits than the defaults, if your fetcher needs less. They can never be raised.",
      "type": "object",
      "properties": {
        "seconds": {
          "description": "How long a run may take, 1 to 120. Default 120.",
          "type": "integer",
          "minimum": 1,
          "maximum": 120
        },
        "megabytes": {
          "description": "How much memory a run may use, 16 to 256. Default 256.",
          "type": "integer",
          "minimum": 16,
          "maximum": 256
        }
      },
      "additionalProperties": false
    },
    "PluginLinks": {
      "description": "The hosts an item record may link to, besides every source's host.",
      "type": "object",
      "properties": {
        "hosts": {
          "description": "Lowercase host names, such as www.bancentral.gov.do. At most 16.",
          "type": "array",
          "items": {
            "type": "string",
            "maxLength": 253
          }
        }
      },
      "required": [
        "hosts"
      ],
      "additionalProperties": false
    },
    "PluginManifest": {
      "description": "plugin.json: what your plug-in downloads, what it writes, when it runs and who may read it. Unknown keys are refused.",
      "type": "object",
      "properties": {
        "$schema": {
          "description": "Your editor's schema for plugin.json, such as https://kanboombo.com/developers/plugins/plugin.schema.json. Ignored by Kanboombo.",
          "type": "string",
          "maxLength": 500
        },
        "contract": {
          "description": "The contract this plug-in follows: 1.",
          "const": 1
        },
        "id": {
          "description": "The plug-in's id, the same as its folder's name.",
          "$ref": "#/$defs/PluginId"
        },
        "name": {
          "description": "Its name per language, 80 characters or fewer each.",
          "$ref": "#/$defs/PluginNames"
        },
        "version": {
          "description": "major.minor.patch. Raise it with every change you publish.",
          "type": "string",
          "pattern": "^[0-9]+\\.[0-9]+\\.[0-9]+$",
          "errorMessage": "Expected a version such as 1.0.0"
        },
        "timezone": {
          "description": "A named IANA zone. It decides today, how schedule is read, and how source URLs are filled.",
          "type": "string",
          "minLength": 1,
          "maxLength": 100,
          "x-check": "supportedIanaTimezone",
          "errorMessage": "Expected a supported named IANA timezone"
        },
        "audience": {
          "description": "Who may read it: public, private or local.",
          "$ref": "#/$defs/PluginAudience"
        },
        "schedule": {
          "description": "Five-field cron, read in the manifest's time zone.",
          "type": "string",
          "maxLength": 100
        },
        "backfill": {
          "description": "How far back the first run fills: all, or a span such as 5y.",
          "anyOf": [
            {
              "const": "all"
            },
            {
              "$ref": "#/$defs/PluginWindow"
            }
          ]
        },
        "sources": {
          "description": "The files Kanboombo downloads for each run. At most 16.",
          "type": "array",
          "items": {
            "$ref": "#/$defs/PluginSource"
          }
        },
        "series": {
          "description": "What it writes. 1 to 64.",
          "type": "array",
          "items": {
            "$ref": "#/$defs/PluginSeries"
          }
        },
        "links": {
          "description": "Where an item's link may point besides your sources' own hosts.",
          "$ref": "#/$defs/PluginLinks"
        },
        "limits": {
          "description": "Lower time and memory limits, if you want them.",
          "$ref": "#/$defs/PluginLimits"
        },
        "licence": {
          "description": "Who holds the data, how it is credited, and whether it may be kept.",
          "$ref": "#/$defs/PluginLicence"
        }
      },
      "required": [
        "contract",
        "id",
        "name",
        "version",
        "timezone",
        "audience",
        "schedule",
        "backfill",
        "sources",
        "series",
        "licence"
      ],
      "additionalProperties": false
    },
    "PluginNames": {
      "description": "A name in each language the plug-in speaks, keyed by language code. A reader sees their own language, then en, then the first listed.",
      "type": "object",
      "additionalProperties": {
        "type": "string",
        "minLength": 1,
        "maxLength": 80
      }
    },
    "PluginRecordKind": {
      "description": "value: numbers on a date. event: something scheduled, happened or cancelled. item: a dated link with a title.",
      "type": "string",
      "enum": [
        "value",
        "event",
        "item"
      ]
    },
    "PluginSeries": {
      "description": "One kind of record your plug-in writes, and how readers see it.",
      "type": "object",
      "properties": {
        "key": {
          "description": "The series key every record of it names.",
          "$ref": "#/$defs/PluginKey"
        },
        "kind": {
          "description": "value, event or item.",
          "$ref": "#/$defs/PluginRecordKind"
        },
        "cadence": {
          "description": "How often it has a new record.",
          "$ref": "#/$defs/PluginCadence"
        },
        "dates": {
          "description": "Whether a record is dated by what it describes (observation) or by when it was published (publication).",
          "$ref": "#/$defs/PluginDates"
        },
        "name": {
          "description": "Its name per language, shown as the row's label.",
          "$ref": "#/$defs/PluginNames"
        },
        "unit": {
          "description": "What its numbers are in, such as DOP, m or %. Up to 16 characters. Screen readers say it after the figures; with % a change is shown in basis points and an event's value as a percentage.",
          "type": "string",
          "maxLength": 16
        },
        "fields": {
          "description": "A value series' numbers, by key, in the order they are shown. 1 to 16.",
          "type": "array",
          "items": {
            "$ref": "#/$defs/PluginKey"
          }
        },
        "fieldNames": {
          "description": "A label for each field, per language, for example compra: { es: Compra, en: Buy }.",
          "type": "object",
          "additionalProperties": {
            "$ref": "#/$defs/PluginNames"
          }
        },
        "change": {
          "description": "The field whose change from the previous record is shown, never coloured as good or bad.",
          "$ref": "#/$defs/PluginKey"
        },
        "snapshot": {
          "description": "A window each run fully restates: a stored record missing from it for three runs is withdrawn.",
          "$ref": "#/$defs/PluginSnapshot"
        }
      },
      "required": [
        "key",
        "kind",
        "cadence",
        "dates",
        "name"
      ],
      "additionalProperties": false
    },
    "PluginSnapshot": {
      "description": "Each run's batch is the whole truth for this window. A stored record inside it that is missing from three runs in a row is withdrawn.",
      "type": "object",
      "properties": {
        "back": {
          "description": "How far before today the window reaches.",
          "$ref": "#/$defs/PluginWindow"
        },
        "ahead": {
          "description": "How far after today it reaches, for scheduled events.",
          "$ref": "#/$defs/PluginWindow"
        }
      },
      "required": [
        "back",
        "ahead"
      ],
      "additionalProperties": false
    },
    "PluginSource": {
      "description": "One file Kanboombo downloads before each run and hands to your fetcher. https only; the host refuses private addresses and redirects to hosts no source names. {today}, {year} and {month} are filled in the manifest's time zone.",
      "type": "object",
      "properties": {
        "name": {
          "description": "The source's name: the key your fetcher finds it under in the run context's sources.",
          "$ref": "#/$defs/PluginKey"
        },
        "url": {
          "description": "Where to download it: https, with a lowercase host.",
          "type": "string",
          "maxLength": 500,
          "pattern": "^https://[a-z0-9.-]+(:[0-9]+)?/[^\\s]*$",
          "errorMessage": "A source is an https URL with a lowercase host",
          "examples": [
            "https://cdn.bancentral.gov.do/documents/estadisticas/mercado-cambiario/documents/TASA_DOLAR_REFERENCIA_MC.xlsx"
          ]
        },
        "maxBytes": {
          "description": "The largest download you expect, at most 20 MB (the default). A larger file fails the run.",
          "type": "integer",
          "minimum": 1,
          "maximum": 20971520
        }
      },
      "required": [
        "name",
        "url"
      ],
      "additionalProperties": false
    },
    "PluginWindow": {
      "description": "A span relative to the run's today: a number and a unit, d for days or y for years.",
      "type": "string",
      "pattern": "^[0-9]{1,4}[dy]$",
      "errorMessage": "Expected a span such as 60d or 5y",
      "examples": [
        "60d",
        "400d",
        "5y"
      ]
    }
  }
}
