$schema: "http://json-schema.org/draft-07/schema#"
$id: "https://morphir.finos.org/schemas/morphir-ir-v4-document-tree-files.yaml"
title: "Morphir IR V4 Document Tree File Formats"
description: |
  JSON schemas for document tree file formats used in VFS (Virtual File System) mode.
  
  In VFS mode, Morphir IR distributions are stored as a directory tree with individual
  files for each type and value definition. This enables incremental updates, better
  tooling support, and easier navigation of large codebases.
  
  File types:
  - manifest.json: Distribution-level metadata
  - module.json: Module manifest (with optional inline definitions)
  - *.type.json: Individual type definition/specification files
  - *.value.json: Individual value definition/specification files
  
  Note: This schema references definitions from morphir-ir-v4.yaml for core types
  (Name, ModuleName, PackageName, TypeDefinition, ValueDefinition, etc.).

definitions:
  # Import core types from main schema (these would be $ref in practice)
  FormatVersion:
    anyOf:
      - const: 4
        description: "Canonical spelling of v4.0.0"
      - type: string
        pattern: "^4\\.(0|[1-9][0-9]*)\\.(0|[1-9][0-9]*)$"
        description: "Exact v4 release triplet"
    description: "The IR format version, governed by the v3-and-later formatVersion contract. Integer 4 is the canonical v4.0.0 baseline; exact release strings identify later revisions in the v4 family. Prerelease and build metadata are not allowed"
    examples: [4, "4.0.0", "4.1.0"]

  Name:
    oneOf:
      - type: string
        pattern: "^[a-z0-9]+(-[a-z0-9]+|-(\\([a-z]+\\)))*$"
        description: "Canonical string representation"
        examples: ["my-name", "user-id", "value-in-(usd)"]
      - type: array
        items:
          type: string
          pattern: "^[a-z][a-z0-9]*$"
        minItems: 1
        description: "Legacy array representation"
  
  ModuleName:
    oneOf:
      - type: string
        pattern: "^[a-z0-9-()]+(/[a-z0-9-()]+)*$"
        description: "Canonical string representation"
        examples: ["basics", "domain/users", "u-s/f-r-2052-a/data-tables"]
      - type: array
        items: { $ref: "#/definitions/Name" }
        minItems: 1
        description: "Legacy array representation"
  
  PackageName:
    $ref: "#/definitions/ModuleName"
    description: "Package identifier (same format as ModuleName)"
  
  AccessControlled:
    type: object
    required: ["access", "value"]
    properties:
      access:
        enum: ["Public", "Private"]
        description: "Visibility level"
      value: {}
  
  TypeDefinition:
    type: object
    description: "Type definition variant (TypeAliasDefinition, CustomTypeDefinition, or IncompleteTypeDefinition)"
    additionalProperties: true
  
  TypeSpecification:
    type: object
    description: "Type specification variant (TypeAliasSpecification, OpaqueTypeSpecification, etc.)"
    additionalProperties: true
  
  ValueDefinition:
    type: object
    description: "Value definition variant (wrapper object with ExpressionBody, NativeBody, etc.)"
    additionalProperties: true
  
  ValueSpecification:
    type: object
    required: ["inputs", "output"]
    properties:
      inputs:
        type: object
        additionalProperties: true
        description: "Input parameter types"
      output: {}
      description: "Output type"
    additionalProperties: true
  
  EntryPoint:
    type: object
    required: ["target", "kind"]
    properties:
      target:
        oneOf:
          - type: string
            pattern: "^[a-z0-9-()/]+:[a-z0-9-()/]+#[a-z0-9-()]+$"
          - type: array
            minItems: 3
            maxItems: 3
        description: "Fully-qualified name"
      kind:
        enum: ["main", "command", "handler", "job", "policy"]
        description: "Entry point kind"
      doc:
        oneOf:
          - type: string
          - type: array
            items: { type: string }
        description: "Optional documentation"

  # Common header fields for all VFS node files
  VfsNodeHeader:
    type: object
    required: ["formatVersion", "name"]
    properties:
      formatVersion:
        $ref: "#/definitions/FormatVersion"
      name:
        $ref: "#/definitions/Name"
        description: "The canonical name of the type or value (matches filename without suffix)"
    description: |
      Common header fields present in all granular definition files (*.type.json, *.value.json).
      The name field matches the filename (without the .type.json or .value.json suffix).
    examples:
      - { "formatVersion": 4, "name": "user" }
      - { "formatVersion": 4, "name": "get-user-by-email" }

  # Type Definition File (*.type.json)
  TypeDefinitionFile:
    allOf:
      - $ref: "#/definitions/VfsNodeHeader"
      - type: object
        required: []
        properties:
          doc:
            oneOf:
              - type: string
                description: "Single-line documentation (will be split on newlines)"
              - type: array
                items: { type: string }
                description: "Multi-line documentation (one string per line)"
            description: "Optional documentation for this type"
          def:
            type: object
            required: []
            properties:
              access:
                enum: ["Public", "Private"]
                description: "Access level (required for definitions in PackageDefinition)"
              doc:
                oneOf:
                  - type: string
                  - type: array
                    items: { type: string }
                description: "Optional documentation (can be at def level or top level)"
            additionalProperties: true
            description: |
              Type definition (implementation). Used in PackageDefinition.
              Must contain one of: TypeAliasDefinition, CustomTypeDefinition, IncompleteTypeDefinition.
              The access field is required when this file is part of a PackageDefinition.
          spec:
            type: object
            required: []
            properties:
              doc:
                oneOf:
                  - type: string
                  - type: array
                    items: { type: string }
                description: "Optional documentation"
            additionalProperties: true
            description: |
              Type specification (interface). Used in PackageSpecification.
              Must contain one of: TypeAliasSpecification, OpaqueTypeSpecification, 
              CustomTypeSpecification, DerivedTypeSpecification.
    description: |
      Type definition or specification file (*.type.json).
      
      Structure:
      - formatVersion: Required, shared v4 format version
      - name: Required, canonical name matching filename
      - doc: Optional, documentation (can be at top level or nested in def/spec)
      - def: Required if this is a definition (implementation), contains TypeDefinition variant
      - spec: Required if this is a specification (interface), contains TypeSpecification variant
      
      Exactly one of 'def' or 'spec' must be present.
    examples:
      - { "formatVersion": 4, "name": "user", "doc": "Represents a user", "def": { "access": "Public", "TypeAliasDefinition": { "typeParams": [], "typeExp": { "Record": { "fields": { "email": "morphir/sdk:string#string" } } } } } }
      - { "formatVersion": 4, "name": "int", "spec": { "doc": "Arbitrary precision integer", "OpaqueTypeSpecification": {} } }

  # Value Definition File (*.value.json)
  ValueDefinitionFile:
    allOf:
      - $ref: "#/definitions/VfsNodeHeader"
      - type: object
        required: []
        properties:
          doc:
            oneOf:
              - type: string
                description: "Single-line documentation"
              - type: array
                items: { type: string }
                description: "Multi-line documentation"
            description: "Optional documentation for this value"
          def:
            type: object
            required: []
            properties:
              access:
                enum: ["Public", "Private"]
                description: "Access level (required for definitions in PackageDefinition)"
              doc:
                oneOf:
                  - type: string
                  - type: array
                    items: { type: string }
                description: "Optional documentation"
            additionalProperties: true
            description: |
              Value definition (implementation). Used in PackageDefinition.
              Must contain one of: ExpressionBody, NativeBody, ExternalBody, IncompleteBody
              wrapped in their wrapper object format (e.g., { "ExpressionBody": { ... } }).
              The access field is required when this file is part of a PackageDefinition.
          spec:
            type: object
            required: []
            properties:
              doc:
                oneOf:
                  - type: string
                  - type: array
                    items: { type: string }
                description: "Optional documentation"
            additionalProperties: true
            description: |
              Value specification (interface). Used in PackageSpecification.
              Must contain: inputs (object mapping parameter names to types) and output (type).
    description: |
      Value definition or specification file (*.value.json).
      
      Structure:
      - formatVersion: Required, shared v4 format version
      - name: Required, canonical name matching filename
      - doc: Optional, documentation (can be at top level or nested in def/spec)
      - def: Required if this is a definition (implementation), contains ValueDefinition variant
      - spec: Required if this is a specification (interface), contains ValueSpecification
      
      Exactly one of 'def' or 'spec' must be present.
    examples:
      - { "formatVersion": 4, "name": "get-user-by-email", "def": { "access": "Public", "ExpressionBody": { "inputTypes": { "email": "morphir/sdk:string#string" }, "outputType": "morphir/sdk:maybe#maybe", "body": { "Variable": { "attributes": {}, "name": "email" } } } } }
      - { "formatVersion": 4, "name": "add", "spec": { "doc": "Add two integers", "inputs": { "a": "morphir/sdk:basics#int", "b": "morphir/sdk:basics#int" }, "output": "morphir/sdk:basics#int" } }

  # Module Manifest File (module.json)
  ModuleManifestFile:
    type: object
    required: ["formatVersion"]
    properties:
      formatVersion:
        $ref: "#/definitions/FormatVersion"
      path:
        $ref: "#/definitions/ModuleName"
        description: "Module path (canonical format, e.g., 'my-org/domain')"
      module:
        $ref: "#/definitions/ModuleName"
        description: "Alternative field name for module path (legacy compatibility)"
      doc:
        oneOf:
          - type: string
            description: "Single-line module documentation"
          - type: array
            items: { type: string }
            description: "Multi-line module documentation"
        description: "Optional module-level documentation"
      types:
        oneOf:
          - type: array
            items: { $ref: "#/definitions/Name" }
            description: "List of type names in this module (manifest style)"
          - type: object
            additionalProperties:
              allOf:
                - $ref: "#/definitions/AccessControlled"
                - properties:
                    value:
                      oneOf:
                        - type: object
                          required: ["doc", "value"]
                          properties:
                            doc: { type: string }
                            value: { $ref: "#/definitions/TypeDefinition" }
                        - $ref: "#/definitions/TypeDefinition"
            description: "Inline type definitions (inline style)"
      values:
        oneOf:
          - type: array
            items: { $ref: "#/definitions/Name" }
            description: "List of value names in this module (manifest style)"
          - type: object
            additionalProperties:
              allOf:
                - $ref: "#/definitions/AccessControlled"
                - properties:
                    value:
                      oneOf:
                        - type: object
                          required: ["doc", "value"]
                          properties:
                            doc: { type: string }
                            value: { $ref: "#/definitions/ValueDefinition" }
                        - $ref: "#/definitions/ValueDefinition"
            description: "Inline value definitions (inline style)"
    description: |
      Module manifest file (module.json).
      
      Supports two encoding styles:
      
      1. **Manifest Style (Granular)**: Lists type/value names, definitions in separate files
         - formatVersion: Required
         - path or module: Required, module path
         - doc: Optional, module documentation
         - types: Optional, array of type names
         - values: Optional, array of value names
      
      2. **Inline Style (Hybrid)**: Contains definitions directly
         - formatVersion: Required
         - path or module: Required, module path
         - doc: Optional, module documentation
         - types: Optional, object mapping type names to definitions
         - values: Optional, object mapping value names to definitions
      
      Either 'path' or 'module' field must be present (they are equivalent).
      The 'path' field is preferred; 'module' is accepted for backwards compatibility.
      Types and values can be arrays (manifest) or objects (inline), but not mixed.
    examples:
      - { "formatVersion": 4, "path": "my-org/domain", "types": ["user", "order"], "values": ["create-order"] }
      - { "formatVersion": 4, "module": "main/domain", "doc": "Domain model", "types": { "user": { "access": "Public", "TypeAliasDefinition": { "typeParams": [], "typeExp": "morphir/sdk:string#string" } } }, "values": {} }
      - { "formatVersion": 4, "path": "my-org/domain", "doc": "Domain model", "types": ["user", "order"], "values": ["create-order"] }
      - { "formatVersion": 4, "module": "my-org/domain", "doc": "Domain model (legacy field)", "types": ["user"], "values": [] }

  # Distribution Manifest File (manifest.json)
  DistributionManifestFile:
    type: object
    required: ["formatVersion", "distribution", "package"]
    properties:
      formatVersion:
        $ref: "#/definitions/FormatVersion"
      distribution:
        enum: ["Library", "Specs", "Application"]
        description: "Distribution type"
      package:
        $ref: "#/definitions/PackageName"
        description: "Package name (canonical path format)"
      version:
        type: string
        pattern: "^[0-9]+\\.[0-9]+\\.[0-9]+(-[a-zA-Z0-9.-]+)?(\\+[a-zA-Z0-9.-]+)?$"
        description: "Package version (semantic version)"
        examples: ["1.2.0", "2.0.0-alpha.1"]
      created:
        type: string
        format: "date-time"
        description: "ISO 8601 timestamp when distribution was created"
        examples: ["2026-01-15T12:00:00Z", "2026-01-15T12:00:00+00:00"]
      layout:
        enum: ["Classic", "VfsMode"]
        description: "Distribution layout mode (VfsMode for document tree)"
        default: "VfsMode"
      entryPoints:
        type: object
        additionalProperties: { $ref: "#/definitions/EntryPoint" }
        description: |
          Entry points (required for Application distributions, optional otherwise).
          Map of entry point names to EntryPoint definitions.
    description: |
      Distribution manifest/metadata file (manifest.json).
      
      Located at: `.morphir-dist/manifest.json`
      
      Contains distribution-level metadata:
      - formatVersion: Required, IR format version
      - distribution: Required, distribution type (Library, Specs, Application)
      - package: Required, package name
      - version: Optional, package version
      - created: Optional, creation timestamp (ISO 8601)
      - layout: Optional, distribution layout (defaults to VfsMode for document tree)
      - entryPoints: Optional, entry points (required for Application distributions)
    examples:
      - { "formatVersion": 4, "distribution": "Library", "package": "my-org/my-project", "version": "1.2.0", "created": "2026-01-15T12:00:00Z", "layout": "VfsMode" }
      - { "formatVersion": 4, "distribution": "Specs", "package": "morphir/sdk", "version": "3.0.0", "created": "2026-01-15T12:00:00Z", "layout": "VfsMode" }
      - { "formatVersion": 4, "distribution": "Application", "package": "my-org/my-cli", "version": "2.0.0", "created": "2026-01-15T12:00:00Z", "entryPoints": { "startup": { "target": "my-org/my-cli:main#run", "kind": "main" } } }
