{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "$id": "https://thinwrap.dev/catalog.schema.json",
  "title": "Thinwrap catalog",
  "description": "Schema for https://thinwrap.dev/catalog.json. Documented at /catalog-spec. No compatibility contract — ingesters adapt to changes.",
  "type": "object",
  "required": ["version", "generatedAt", "packages"],
  "properties": {
    "$schema": { "type": "string", "format": "uri" },
    "version": { "type": "string", "const": "1" },
    "generatedAt": { "type": "string", "format": "date-time" },
    "packages": {
      "type": "array",
      "items": { "$ref": "#/$defs/Package" }
    }
  },
  "$defs": {
    "Package": {
      "type": "object",
      "required": [
        "repoName",
        "language",
        "scope",
        "registry",
        "registryUrl",
        "packageName",
        "version",
        "agentDocUrl",
        "agentDocFreshness",
        "providers"
      ],
      "properties": {
        "repoName": {
          "type": "string",
          "enum": [
            "notifications-ts",
            "notifications-php",
            "location-ts",
            "location-php"
          ]
        },
        "language": { "type": "string", "enum": ["typescript", "php"] },
        "scope": { "type": "string", "enum": ["notifications", "location"] },
        "registry": { "type": "string", "enum": ["npm", "packagist"] },
        "registryUrl": { "type": "string", "format": "uri" },
        "packageName": { "type": "string", "minLength": 1 },
        "version": { "type": "string", "minLength": 1 },
        "agentDocUrl": { "type": "string", "format": "uri" },
        "agentDocFreshness": { "type": "string", "enum": ["current", "stale"] },
        "providers": { "type": "array", "items": { "$ref": "#/$defs/Provider" } }
      },
      "additionalProperties": false
    },
    "Provider": {
      "type": "object",
      "required": [
        "providerId",
        "humanName",
        "auth",
        "regionalEndpoints",
        "rateLimitDocsUrl"
      ],
      "properties": {
        "providerId": { "type": "string", "minLength": 1 },
        "humanName": { "type": "string", "minLength": 1 },
        "channel": {
          "type": "string",
          "enum": ["email", "sms", "push", "chat"]
        },
        "operation": {
          "type": "string",
          "enum": ["routing", "matrix", "geocoding", "isochrone"]
        },
        "operations": { "type": "array", "items": { "type": "string" } },
        "auth": {
          "type": "object",
          "required": ["method", "tokenLifecycle", "tokenCacheHookSupported"],
          "properties": {
            "method": {
              "type": "string",
              "enum": [
                "header",
                "query",
                "basic",
                "bearer",
                "rs256-jwt",
                "es256-jwt",
                "webhook-url",
                "aws-sig-v4"
              ]
            },
            "tokenLifecycle": {
              "type": "string",
              "enum": ["opaque-static", "per-call-signed"]
            },
            "tokenCacheHookSupported": { "type": "boolean" }
          },
          "additionalProperties": false
        },
        "regionalEndpoints": { "type": "array", "items": { "type": "string" } },
        "rateLimitDocsUrl": {
          "oneOf": [{ "type": "string", "format": "uri" }, { "type": "null" }]
        },
        "novuProviderId": {
          "oneOf": [{ "type": "string" }, { "type": "null" }]
        }
      },
      "additionalProperties": false,
      "$comment": "novuProviderId is intentionally NOT in `required`. Notifications providers carry it (string or null for non-Novu-mapped providers); location providers omit it entirely. Keeping it optional lets a single Provider definition validate both scopes; the per-scope `channel` vs `operation` distinction is enforced by the source frontmatter validator (src/utils/frontmatter-validator.ts), which is scope-aware."
    }
  }
}
