{
  "$schema": "http://json-schema.org/draft-07/schema#",
  "$id": "https://emberpm.dev/ember.schema.json",
  "title": "ember.toml",
  "description": "Manifest for Ember, the Luau package manager: dependencies, tools, scripts, Roblox Studio configuration, and — for a package that gets published — its registry metadata.",
  "type": "object",
  "additionalProperties": false,
  "definitions": {
    "target": {
      "title": "Target",
      "description": "Where this package's Luau code runs, its entry point, and publish file selection.",
      "type": "object",
      "required": ["environment"],
      "additionalProperties": false,
      "properties": {
        "environment": {
          "description": "Runtime environment for this package's Luau code. Also names the folder it installs into, `packages/<environment>`; `roblox_server` installs into `packages/server`. `luau` is portable Luau, which installs in every environment.",
          "type": "string",
          "anyOf": [
            {
              "enum": [
                "roblox",
                "roblox_server",
                "lune",
                "luau",
                "lute",
                "seal",
                "zune"
              ]
            },
            {
              "const": "shared",
              "deprecated": true,
              "description": "Former name of `roblox`. Still accepted; write `roblox` instead."
            },
            {
              "const": "server",
              "deprecated": true,
              "description": "Former name of `roblox_server`. Still accepted; write `roblox_server` instead."
            }
          ]
        },
        "language": {
          "description": "What the shipped files are written in, separate from the environment. `alloy` and `loom` build to Luau: `embr publish` runs `alloy build` or `loom build` first, and `main` must name the built .luau file. A project takes packages in its own language or in plain Luau, so an Alloy, Loom, or Luwu package installs only in a project of its language.",
          "type": "string",
          "enum": ["luau", "alloy", "loom", "luwu"],
          "default": "luau"
        },
        "main": {
          "description": "Entry point of the package, e.g. `src/init.luau`. Optional: detected automatically when omitted, and not needed for workspace roots. Required for an Alloy or Loom package, where it names the built .luau file.",
          "type": "string",
          "examples": ["src/init.luau"]
        },
        "includes": {
          "description": "What goes into the published archive: plain file/dir paths (a dir covers everything under it) or globs like `src/*`. Empty means \"everything sensible\" (the default walk minus VCS/output dirs). ember.toml always ships.",
          "type": "array",
          "items": {
            "type": "string"
          },
          "examples": [["src", "LICENSE"]]
        },
        "excludes": {
          "description": "Subtracted from whatever `includes` (or the default walk) selected; same path-or-glob entries.",
          "type": "array",
          "items": {
            "type": "string"
          },
          "examples": [["src/tests"]]
        },
        "workspace": {
          "description": "Member globs relative to this manifest, e.g. `[\"packages/*\"]` (`!` negates, a literal `.` includes the root). Having members makes this a workspace root: publish/install there run for every member.",
          "type": "array",
          "items": {
            "type": "string"
          },
          "examples": [["packages/*"]]
        },
        "include": {
          "description": "Alias of `includes`.",
          "deprecated": true,
          "type": "array",
          "items": {
            "type": "string"
          }
        },
        "exclude": {
          "description": "Alias of `excludes`.",
          "deprecated": true,
          "type": "array",
          "items": {
            "type": "string"
          }
        },
        "workspace_members": {
          "description": "Alias of `workspace` (pesde's spelling).",
          "deprecated": true,
          "type": "array",
          "items": {
            "type": "string"
          }
        }
      }
    },
    "dependencyTarget": {
      "description": "Environment tree this dependency installs under, overriding the one it would land in. Any environment can be chosen. That takes a dependency for another environment on purpose, which the install refuses otherwise, and the packages under it are checked against the chosen environment. It applies only to a direct dependency of this manifest: elsewhere (an [overrides] value, or a workspace member's dependency while the root installs) the key is accepted but has no effect.",
      "anyOf": [
        {
          "enum": [
            "roblox",
            "roblox_server",
            "lune",
            "luau",
            "lute",
            "seal",
            "zune"
          ]
        },
        {
          "const": "shared",
          "deprecated": true,
          "description": "Former name of `roblox`. Still accepted; write `roblox` instead."
        },
        {
          "const": "server",
          "deprecated": true,
          "description": "Former name of `roblox_server`. Still accepted; write `roblox_server` instead."
        }
      ],
      "examples": ["roblox_server"]
    },
    "dependencyEntry": {
      "description": "Module inside the package its link file points at, overriding both what the package declares and what Ember guesses. For a package that names no entry point of its own, Ember guesses one (a lone Luau file, or one named after the package) and says so during install; this is how you correct that guess or supply what it could not. With or without a file extension, both work. Stated per package rather than per alias, so it also applies to a dependent's nested link to the same package; inside an [overrides] value the key is accepted but has no effect.",
      "type": "string",
      "minLength": 1,
      "examples": ["src/vow", "src/init.luau"]
    }
  },
  "properties": {
    "package": {
      "title": "Package",
      "description": "Identity and publish metadata. Required only by `embr publish` — a project that just consumes packages has none.",
      "type": "object",
      "required": ["name", "version"],
      "additionalProperties": false,
      "properties": {
        "name": {
          "description": "Package identifier in `scope/name` form: lowercase letters, digits, dashes, and underscores.",
          "type": "string",
          "pattern": "^[a-z0-9_-]+/[a-z0-9_-]+$",
          "examples": ["chief/core"]
        },
        "version": {
          "description": "Package version (semver).",
          "type": "string",
          "pattern": "^(0|[1-9]\\d*)\\.(0|[1-9]\\d*)\\.(0|[1-9]\\d*)(?:-((?:0|[1-9]\\d*|\\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\\.(?:0|[1-9]\\d*|\\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\\+([0-9a-zA-Z-]+(?:\\.[0-9a-zA-Z-]+)*))?$",
          "examples": ["0.1.0"]
        },
        "description": {
          "description": "Short description shown in the registry. At most 200 characters.",
          "type": "string",
          "maxLength": 200
        },
        "authors": {
          "description": "GitHub usernames of the authors. Listed users get publish access to the scope.",
          "type": "array",
          "items": {
            "type": "string",
            "minLength": 1
          }
        },
        "repository": {
          "description": "Source repository as `owner/repo` or a GitHub URL.",
          "type": "string",
          "examples": [
            "ryancundiff/chief",
            "https://github.com/ryancundiff/chief"
          ]
        },
        "license": {
          "description": "SPDX license identifier.",
          "type": "string",
          "examples": ["MIT", "Apache-2.0", "ISC"]
        },
        "logo": {
          "description": "Picture of the package that the registry shows in place of the box icon: a .png, .jpg, .jpeg, or .webp file in the package, relative to this manifest, at most 512 KiB. It always ships. SVG is not accepted.",
          "type": "string",
          "pattern": "\\.([Pp][Nn][Gg]|[Jj][Pp][Ee]?[Gg]|[Ww][Ee][Bb][Pp])$",
          "examples": ["assets/logo.png"]
        },
        "private": {
          "description": "When true, `embr publish` refuses this package. Typical for workspace roots that only exist to hold members.",
          "type": "boolean",
          "default": false
        }
      }
    },
    "target": {
      "description": "One [target] table, or several [[target]] tables for a package that ships one release for each target. The first target is the project's own. A target ID is the environment for Luau, else <environment>-<language>, such as roblox-alloy.",
      "anyOf": [
        { "$ref": "#/definitions/target" },
        {
          "type": "array",
          "minItems": 1,
          "items": { "$ref": "#/definitions/target" }
        }
      ]
    },
    "indices": {
      "title": "Indices",
      "description": "Named package indices that dependencies can reference via `index = \"<key>\"`.",
      "type": "object",
      "properties": {
        "default": {
          "description": "Overrides Ember's own registry as the index for dependencies that name no index.",
          "type": "string",
          "format": "uri"
        }
      },
      "additionalProperties": {
        "description": "Git URL of the index, or https://api.forest.dev for Forest.",
        "type": "string",
        "format": "uri",
        "examples": [
          "https://github.com/UpliftGames/wally-index",
          "https://github.com/pesde-pkg/index",
          "https://api.forest.dev"
        ]
      },
      "propertyNames": {
        "minLength": 1
      }
    },
    "dependencies": {
      "title": "Dependencies",
      "description": "Packages this project depends on. The key is the alias your code requires; the value names a registry package or a workspace member.",
      "type": "object",
      "additionalProperties": {
        "type": "object",
        "oneOf": [
          {
            "description": "A registry dependency, resolved from an index.",
            "required": ["name", "version"],
            "additionalProperties": false,
            "properties": {
              "name": {
                "description": "Package identifier in `scope/name` form.",
                "type": "string",
                "pattern": "^[a-z0-9_-]+/[a-z0-9_-]+$"
              },
              "version": {
                "description": "Semver requirement, e.g. `^4.0.0`. A bare `^` (or `*`) means \"latest\".",
                "type": "string",
                "minLength": 1,
                "examples": ["^", "^4.0.0", "~1.2", "=2.0.1"]
              },
              "index": {
                "description": "Key under [indices] to resolve from. Omitted means the `default` index (or Ember's registry).",
                "type": "string",
                "minLength": 1
              },
              "target": {
                "$ref": "#/definitions/dependencyTarget"
              },
              "entry": {
                "$ref": "#/definitions/dependencyEntry"
              }
            }
          },
          {
            "description": "A workspace dependency, resolved from a member of this workspace.",
            "required": ["workspace"],
            "additionalProperties": false,
            "properties": {
              "workspace": {
                "description": "Workspace member to depend on, in `scope/name` form.",
                "type": "string",
                "pattern": "^[a-z0-9_-]+/[a-z0-9_-]+$"
              },
              "version": {
                "description": "Optional semver requirement against the member's version. Ignored during development; it shapes the requirement published in place of the workspace link.",
                "type": "string",
                "minLength": 1,
                "examples": ["^"]
              },
              "target": {
                "$ref": "#/definitions/dependencyTarget"
              },
              "entry": {
                "$ref": "#/definitions/dependencyEntry"
              }
            }
          }
        ]
      }
    },
    "overrides": {
      "title": "Overrides",
      "description": "Replacements for dependencies of dependencies, keyed by dot-separated alias paths (\"Foo.Bar\" = the Bar dependency of my dependency Foo; commas separate multiple paths). Consulted only in the root manifest; publishing strips it.",
      "type": "object",
      "propertyNames": {
        "pattern": "^[^.\\s]+(\\.[^.\\s]+)+(\\s*,\\s*[^.\\s]+(\\.[^.\\s]+)+)*$"
      },
      "additionalProperties": {
        "oneOf": [
          {
            "type": "string",
            "description": "Alias of one of this manifest's own [dependencies] entries."
          },
          {
            "$ref": "#/properties/dependencies/additionalProperties"
          }
        ]
      }
    },
    "patches": {
      "title": "Patches",
      "description": "Diffs applied to dependencies at install time, written by `embr patch commit`. Consulted only in the root manifest; publishing strips it.",
      "type": "object",
      "propertyNames": {
        "description": "The exact package this patch is for, as `scope/name@version`. Exact because a patch is a claim about one published archive.",
        "pattern": "^[a-z0-9_-]+/[a-z0-9_-]+@(0|[1-9]\\d*)\\.(0|[1-9]\\d*)\\.(0|[1-9]\\d*)(?:-[0-9a-zA-Z.-]+)?(?:\\+[0-9a-zA-Z.-]+)?$"
      },
      "additionalProperties": {
        "description": "Path to the .patch file, relative to this manifest and inside the project — patches travel with the repository.",
        "type": "string",
        "minLength": 1,
        "examples": ["patches/evaera_promise@4.0.0.patch"]
      }
    },
    "tools": {
      "title": "Tools",
      "description": "GitHub-released binary tools, installed by `embr install` and run through shims. The key is the command name you type.",
      "type": "object",
      "additionalProperties": {
        "description": "Tool spec as `owner/repo@version` (exact release version, without a leading `v`).",
        "type": "string",
        "pattern": "^[^/@]+/[^/@]+@[^@]+$",
        "examples": ["rojo-rbx/rojo@7.7.0"]
      }
    },
    "scripts": {
      "title": "Scripts",
      "description": "Shell commands runnable with `embr run <key>`; bare `embr run` lists them. `build`, `test`, `start`, `serve` and `fmt`/`format` are also subcommands, so they drop the `run`. A key named `pre<name>` or `post<name>` is a hook, fired around that script or around `install`, `add` and `publish`.",
      "type": "object",
      "additionalProperties": {
        "description": "One shell command, or a list of commands to run at once with their output tagged `[1]`, `[2]`, … An empty list is an error.",
        "oneOf": [
          {
            "description": "A single shell command, inheriting embr's stdio.",
            "type": "string",
            "minLength": 1,
            "examples": ["rojo build -o game.rbxl"]
          },
          {
            "description": "Commands run at once, each on its own pty (pipes on Windows or when output is redirected). Every command runs to its end; embr exits with the first non-zero code.",
            "type": "array",
            "minItems": 1,
            "items": {
              "type": "string",
              "minLength": 1
            },
            "examples": [["larvae process -w", "rojo serve"]]
          }
        ]
      }
    },
    "studio": {
      "title": "Studio",
      "description": "What `embr studio open` opens in Roblox Studio: a published place (both IDs) or a local place file — one form only.",
      "type": "object",
      "oneOf": [
        {
          "description": "A published place, opened through the roblox-studio: protocol.",
          "required": ["universe", "place"],
          "additionalProperties": false,
          "properties": {
            "universe": {
              "description": "Universe (experience) ID the place belongs to, from the Creator Dashboard.",
              "type": "integer",
              "minimum": 1
            },
            "place": {
              "description": "Place ID inside the universe, from the Creator Dashboard or the place page URL.",
              "type": "integer",
              "minimum": 1
            }
          }
        },
        {
          "description": "A local place file, opened through the OS file association.",
          "required": ["file"],
          "additionalProperties": false,
          "properties": {
            "file": {
              "description": "Path to a .rbxl/.rbxlx place file, relative to ember.toml.",
              "type": "string",
              "pattern": "\\.[Rr][Bb][Xx][Ll][Xx]?$",
              "examples": ["game.rbxl"]
            }
          }
        }
      ]
    },
    "config": {
      "title": "Config",
      "description": "Per-environment install folders, and the style of the requires Ember writes. Each folder defaults to `packages/<environment>`.",
      "type": "object",
      "additionalProperties": false,
      "properties": {
        "requires": {
          "description": "How the requires that Ember writes look, in the roblox and roblox_server trees only. `string` writes string requires and turns a package's own `script.Parent.X` requires into strings. `instance` keeps a package's own instance requires, rewrites a require that reaches a dependency as an instance chain such as `script.Parent.packages.roblox.X`, and makes link files require through `script.Parent[\".ember\"]`.",
          "type": "string",
          "enum": ["string", "instance"],
          "default": "string"
        },
        "roblox-packages-out": {
          "description": "Install folder for roblox packages.",
          "type": "string",
          "default": "packages/roblox"
        },
        "server-packages-out": {
          "description": "Install folder for roblox_server packages.",
          "type": "string",
          "default": "packages/server"
        },
        "lune-packages-out": {
          "description": "Install folder for lune packages.",
          "type": "string",
          "default": "packages/lune"
        },
        "luau-packages-out": {
          "description": "Install folder for luau packages.",
          "type": "string",
          "default": "packages/luau"
        },
        "lute-packages-out": {
          "description": "Install folder for lute packages.",
          "type": "string",
          "default": "packages/lute"
        },
        "seal-packages-out": {
          "description": "Install folder for seal packages.",
          "type": "string",
          "default": "packages/seal"
        },
        "zune-packages-out": {
          "description": "Install folder for zune packages.",
          "type": "string",
          "default": "packages/zune"
        },
        "shared-packages-out": {
          "description": "Alias of `roblox-packages-out`.",
          "deprecated": true,
          "type": "string"
        }
      }
    }
  }
}
