{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "$id": "https://canadiancommercefeedproject.ca/open/model/canonical-commerce-product-model.json",
  "title": "Canonical Commerce Product Model",
  "description": "CCFP channel-neutral product graph. Merchant instances are private by default. A field is required only when this schema or an applicable rule says so.",
  "version": "0.1.1-alpha",
  "type": "object",
  "additionalProperties": false,
  "required": [
    "modelVersion",
    "catalogId",
    "generatedAt",
    "entities",
    "assertions"
  ],
  "properties": {
    "modelVersion": {
      "const": "0.1.0-alpha"
    },
    "catalogId": {
      "type": "string",
      "minLength": 1,
      "maxLength": 128
    },
    "generatedAt": {
      "type": "string",
      "format": "date-time"
    },
    "defaultVisibility": {
      "const": "private",
      "default": "private"
    },
    "entities": {
      "type": "array",
      "items": {
        "$ref": "#/$defs/entity"
      }
    },
    "assertions": {
      "type": "array",
      "items": {
        "$ref": "#/$defs/assertion"
      }
    },
    "provenance": {
      "type": "array",
      "items": {
        "$ref": "#/$defs/provenance"
      },
      "default": []
    }
  },
  "$defs": {
    "entity": {
      "type": "object",
      "additionalProperties": false,
      "required": [
        "id",
        "type"
      ],
      "properties": {
        "id": {
          "type": "string",
          "pattern": "^[A-Za-z0-9][A-Za-z0-9._:-]{0,127}$"
        },
        "type": {
          "enum": [
            "brand",
            "product_family",
            "product",
            "variant",
            "offer",
            "media_asset"
          ]
        },
        "parentId": {
          "type": "string"
        },
        "sourceIdentity": {
          "type": "string",
          "maxLength": 512
        }
      }
    },
    "assertion": {
      "type": "object",
      "additionalProperties": false,
      "required": [
        "entityId",
        "fieldId",
        "state"
      ],
      "properties": {
        "entityId": {
          "type": "string"
        },
        "fieldId": {
          "type": "string",
          "pattern": "^[a-z][a-z0-9_]*(\\.[a-z][a-z0-9_]*)+$"
        },
        "state": {
          "enum": [
            "KNOWN",
            "UNKNOWN",
            "NOT_APPLICABLE",
            "WITHHELD"
          ]
        },
        "value": {},
        "locale": {
          "type": "string",
          "pattern": "^[a-z]{2,3}(-[A-Z]{2})?$"
        },
        "market": {
          "type": "string",
          "pattern": "^[A-Z]{2}$"
        },
        "sourceId": {
          "type": "string"
        },
        "observedAt": {
          "type": "string",
          "format": "date-time"
        }
      },
      "allOf": [
        {
          "if": {
            "properties": {
              "state": {
                "const": "KNOWN"
              }
            }
          },
          "then": {
            "required": [
              "value"
            ]
          },
          "else": {
            "not": {
              "required": [
                "value"
              ]
            }
          }
        }
      ]
    },
    "provenance": {
      "type": "object",
      "additionalProperties": false,
      "required": [
        "id",
        "sourceType",
        "locator"
      ],
      "properties": {
        "id": {
          "type": "string"
        },
        "sourceType": {
          "enum": [
            "feed",
            "api",
            "webpage",
            "merchant_system",
            "manual",
            "derived"
          ]
        },
        "locator": {
          "type": "string"
        },
        "retrievedAt": {
          "type": "string",
          "format": "date-time"
        },
        "contentHash": {
          "type": "string"
        }
      }
    }
  },
  "x-ccfp": {
    "stability": "alpha",
    "defaultDataVisibility": "private",
    "epistemicRule": "Only KNOWN assertions carry a value. UNKNOWN, NOT_APPLICABLE, and WITHHELD are distinct states.",
    "entityGrain": [
      "brand",
      "product_family",
      "product",
      "variant",
      "offer",
      "media_asset"
    ]
  }
}
