{
  "$schema": "http://json-schema.org/draft-07/schema#",
  "$id": "https://gina.io/schema/routing.json",
  "title": "Gina routing.json",
  "description": "Route definitions for a Gina bundle. Each key is a route name; the value defines the URL pattern, HTTP method, controller action, and optional middleware chain.",
  "type": "object",
  "additionalProperties": {
    "$ref": "#/definitions/route"
  },
  "definitions": {
    "route": {
      "type": "object",
      "required": ["url", "method", "param"],
      "additionalProperties": true,
      "properties": {
        "namespace": {
          "type": "string",
          "description": "Controller namespace. Maps to controller.<namespace>.js. Groups related routes under the same controller file."
        },
        "url": {
          "type": "string",
          "description": "URL pattern. Use :param for named segments. Multiple patterns can be comma-separated (e.g. \"/item/:id, /item/:id/:slug\"). The webroot is prepended automatically unless param.ignoreWebRoot is set."
        },
        "method": {
          "type": "string",
          "description": "Accepted HTTP method(s), comma-separated. Case-insensitive.",
          "examples": ["GET", "POST", "PUT", "DELETE", "GET, POST", "GET, POST, PUT, DELETE"]
        },
        "requirements": {
          "type": "object",
          "description": "Validation patterns for URL parameters. Keys match :param names. Values are regex strings (prefixed with \"/\") or validator references (prefixed with \"validator::\").",
          "additionalProperties": {
            "type": "string"
          },
          "examples": [{ "id": "/^[0-9a-f]{8}-/i" }, { "slug": "/^[a-z0-9-]+$/" }]
        },
        "param": {
          "type": "object",
          "description": "Controller action mapping and static/dynamic params passed to the action.",
          "required": ["control"],
          "additionalProperties": true,
          "properties": {
            "control": {
              "type": "string",
              "description": "Controller action name (camelCase method on the bundle controller)."
            },
            "file": {
              "type": "string",
              "description": "Template file path relative to the bundle views directory (without extension). Defaults to the route name."
            },
            "path": {
              "type": "string",
              "description": "Redirect target path. Used with control: \"redirect\"."
            },
            "code": {
              "type": "number",
              "description": "HTTP status code for redirects (e.g. 301, 302).",
              "examples": [301, 302]
            },
            "ignoreWebRoot": {
              "type": "boolean",
              "description": "When true, the bundle webroot prefix is not prepended to the URL."
            },
            "title": {
              "type": "string",
              "description": "Page title passed to the template."
            },
            "isPopinContext": {
              "type": "boolean",
              "description": "Set to true for routes rendered inside a popin overlay."
            }
          }
        },
        "middleware": {
          "type": "array",
          "description": "Ordered list of middleware to run before the controller action. Format: \"middlewares.<namespace>.<name>\".",
          "items": {
            "type": "string",
            "examples": ["middlewares.passport.jwtLogin", "middlewares.global.requirePasswordCheck"]
          }
        },
        "middlewareIgnored": {
          "type": "array",
          "description": "Middleware names to skip for this route (overrides global middleware).",
          "items": {
            "type": "string"
          }
        },
        "bundle": {
          "type": "string",
          "description": "Target bundle name for cross-bundle routing. The request is proxied to the named bundle."
        },
        "hostname": {
          "type": "string",
          "description": "Override target hostname for cross-bundle or external routing."
        },
        "scopes": {
          "type": "array",
          "description": "Allowed scopes for this route. Defaults to the current bundle scope.",
          "items": {
            "type": "string",
            "enum": ["local", "beta", "production", "testing"]
          }
        },
        "cache": {
          "description": "Response caching configuration. Use a string for the cache type shorthand, or an object for full control.",
          "oneOf": [
            {
              "type": "string",
              "enum": ["memory", "fs"],
              "description": "Cache type shorthand."
            },
            {
              "type": "object",
              "properties": {
                "type": {
                  "type": "string",
                  "enum": ["memory", "fs"],
                  "description": "Cache backend."
                },
                "ttl": {
                  "type": "number",
                  "description": "Time-to-live in seconds."
                },
                "visibility": {
                  "type": "string",
                  "enum": ["public", "private"],
                  "description": "Cache-Control visibility directive. Defaults to \"private\".",
                  "default": "private"
                },
                "sliding": {
                  "type": "boolean",
                  "description": "Enable sliding window eviction. Defaults to false.",
                  "default": false
                },
                "maxAge": {
                  "type": "number",
                  "description": "Absolute ceiling in seconds when sliding is enabled."
                },
                "invalidateOnEvents": {
                  "type": "array",
                  "description": "Event names that invalidate the cached response.",
                  "items": { "type": "string" }
                }
              },
              "additionalProperties": false
            }
          ]
        },
        "_comment": {
          "type": "string",
          "description": "Internal documentation comment. Stripped at server init."
        },
        "_sample": {
          "type": "string",
          "description": "Example URL for this route. Ignored at runtime."
        }
      }
    }
  }
}
