{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "$id": "https://wetwareworld.com/schema/wwd/0.1/manifest.schema.json",
  "title": "WWD 0.1 package manifest",
  "description": "The single package entry point. Unifies content types, relationships, a BagIt-style file manifest and external asset records. Located at wwd/manifest.json inside the ZIP.",
  "type": "object",
  "required": ["wwd", "mediaType", "root", "files"],

  "properties": {
    "wwd": { "const": "0.1" },
    "mediaType": { "const": "application/vnd.wwd+zip" },
    "package_id": {
      "type": "string",
      "pattern": "^(urn:uuid:[0-9a-fA-F-]{36}|https?://.+)$"
    },
    "root": {
      "$ref": "#/$defs/part_name",
      "$comment": "Exactly one root design document."
    },
    "producer": { "type": "string" },
    "producer_version": { "type": "string" },

    "extensionsUsed": {
      "type": "array",
      "items": { "$ref": "#/$defs/extension_uri" },
      "uniqueItems": true
    },
    "extensionsRequired": {
      "type": "array",
      "items": { "$ref": "#/$defs/extension_uri" },
      "uniqueItems": true,
      "$comment": "MUST be a subset of extensionsUsed. A consumer that does not support a required extension MUST refuse semantic processing before manufacture, simulation or acceptance."
    },

    "files": {
      "type": "array",
      "items": { "$ref": "#/$defs/file_record" },
      "$comment": "Every internal payload except mimetype and the manifest itself."
    },

    "external": {
      "type": "array",
      "items": { "$ref": "#/$defs/external_record" }
    },

    "relationships": {
      "type": "array",
      "items": { "$ref": "#/$defs/relationship" }
    }
  },

  "$defs": {

    "part_name": {
      "type": "string",
      "minLength": 1,
      "pattern": "^(?!/)(?!.*//)(?!.*(^|/)\\.\\.?(/|$))[^\\\\\u0000]+$",
      "$comment": "Relative, forward slashes, no empty/./.. segments, no NUL, no backslash."
    },

    "extension_uri": {
      "type": "string",
      "pattern": "^https?://.+/[0-9]+$",
      "$comment": "Versioned absolute URI; the version is part of the identifier."
    },

    "integrity": {
      "type": "string",
      "pattern": "^sha256-[A-Za-z0-9+/]{43}=$",
      "$comment": "Subresource-Integrity syntax. The digest is content identity; a URI is only a retrieval location."
    },

    "role": {
      "enum": ["design", "geometry", "field_data", "assay_evidence",
               "simulation_model", "solver_deck", "simulation_result",
               "measured_result_data", "preview", "protocol", "provenance", "opaque"],
      "$comment": "role 'opaque' means a rewriter MUST preserve the bytes it does not understand."
    },

    "file_record": {
      "type": "object",
      "required": ["name", "mediaType", "bytes", "integrity", "role"],
      "properties": {
        "name": { "$ref": "#/$defs/part_name" },
        "mediaType": { "type": "string", "minLength": 1 },
        "bytes": { "type": "integer", "minimum": 0 },
        "integrity": { "$ref": "#/$defs/integrity" },
        "role": { "$ref": "#/$defs/role" }
      }
    },

    "external_record": {
      "type": "object",
      "required": ["name", "uri", "bytes", "integrity", "mediaType"],
      "properties": {
        "name": { "type": "string", "minLength": 1 },
        "uri": {
          "type": "string",
          "pattern": "^[a-z][a-z0-9+.-]*://[^@\\s]*$",
          "$comment": "Credentials MUST NOT be embedded in the URI."
        },
        "bytes": { "type": "integer", "minimum": 0 },
        "integrity": { "$ref": "#/$defs/integrity" },
        "mediaType": { "type": "string" },
        "accessed": { "type": "string", "format": "date-time" },
        "mirrors": {
          "type": "array",
          "items": { "type": "string" },
          "$comment": "Every mirror must return the same verified content."
        },
        "tree_inventory": {
          "type": "string",
          "$comment": "Directory-tree assets such as OME-Zarr need a deterministic inventory whose integrity covers the whole tree; hashing a mutable root metadata file alone is insufficient."
        }
      }
    },

    "relationship": {
      "type": "object",
      "required": ["type", "source", "target", "mode"],
      "properties": {
        "type": { "type": "string", "minLength": 1 },
        "source": { "type": "string" },
        "target": { "type": "string" },
        "mode": { "enum": ["internal", "external"] }
      }
    }
  }
}
