{
	"$schema": "https://json-schema.org/draft/2020-12/schema",
	"$id": "https://xript.dev/schema/manifest/v0.7.json",
	"title": "xript Manifest",
	"description": "Declarative manifest for xript-enabled applications. Defines the API surface, capabilities, and documentation for modders.",
	"type": "object",
	"required": ["xript", "name"],
	"properties": {
		"$schema": {
			"description": "Optional JSON Schema reference for editor autocomplete. Should point to the xript manifest schema URL.",
			"type": "string",
			"format": "uri"
		},
		"xript": {
			"description": "The xript specification version this manifest conforms to.",
			"type": "string",
			"pattern": "^\\d+\\.\\d+$",
			"examples": ["0.3", "0.6"]
		},
		"extends": {
			"description": "One or more base manifests to inherit from. Resolved and deep-merged before validation: maps key-merge, arrays append, scalars are child-wins, and duplicate ids are an error. Paths are filesystem-relative to this manifest. Resolution is transitive with cycle detection.",
			"oneOf": [
				{ "type": "string" },
				{ "type": "array", "items": { "type": "string" }, "minItems": 1 }
			]
		},
		"name": {
			"description": "Machine-readable identifier for this application. Used in generated package names and documentation URLs.",
			"type": "string",
			"pattern": "^[a-z][a-z0-9-]*$",
			"minLength": 1,
			"maxLength": 64
		},
		"version": {
			"description": "The version of this application's scripting API. Follows semver.",
			"type": "string",
			"pattern": "^\\d+\\.\\d+\\.\\d+(-[a-zA-Z0-9.]+)?$",
			"examples": ["1.0.0", "0.3.0-beta.1"]
		},
		"title": {
			"description": "Human-readable display name for the application.",
			"type": "string",
			"maxLength": 128
		},
		"description": {
			"description": "A brief description of what this application does and what modders can extend.",
			"type": "string",
			"maxLength": 1024
		},
		"bindings": {
			"description": "The functions and namespaces exposed to scripts. This is the API surface.",
			"type": "object",
			"additionalProperties": {
				"$ref": "#/$defs/binding"
			}
		},
		"capabilities": {
			"description": "Named capabilities that scripts can request. Each capability gates access to a set of bindings or behaviors. Keys are scope-only: a declared capability names a scope node (e.g. 'run', 'run.command', 'fs.addon') with no mode prefix. The 'read:'/'write:' axis is a property of a grant or require reference, not of the declaration.",
			"type": "object",
			"propertyNames": {
				"pattern": "^[a-z][a-z0-9-]*(\\.[a-z][a-z0-9-]*)*$"
			},
			"additionalProperties": {
				"$ref": "#/$defs/capability"
			}
		},
		"types": {
			"description": "Custom type definitions referenced by bindings. Enables rich type generation and documentation.",
			"type": "object",
			"additionalProperties": {
				"$ref": "#/$defs/typeDefinition"
			}
		},
		"hooks": {
			"description": "DEPRECATED in favor of event-typed slots: declare a slot whose 'accepts' is the event-handler type and fire it by calling its fills. Retained for back-compat; the host still fires registered hook handlers. Hooks are fired by the host and handled by scripts — the reverse direction of bindings.",
			"type": "object",
			"additionalProperties": {
				"$ref": "#/$defs/hook"
			}
		},
		"limits": {
			"$ref": "#/$defs/executionLimits"
		},
		"slots": {
			"description": "The canonical fill surface: typed plug-points the host declares and mods fill. Each slot declares an ID, accepted formats/kinds, and optional capability gating. A fill's shape is governed by the slot's 'accepts' type — a fragment, a role map, or an event handler are all fills of slots of the appropriate type.",
			"type": "array",
			"items": { "$ref": "#/$defs/slot" }
		},
		"events": {
			"description": "Discovery catalog of the named events this host emits or broadcasts and their payload types. Distinct from slots and from fragment handlers: an event-typed slot is an extension POINT that addons fill with handlers; a fragment's 'handlers' wire DOM responses on a fill; an entry here declares what the host itself emits, with no consumer presupposed — a sandbox script, the host's own UI, or an external subscriber may all listen. In short: bindings are what you can call, slots and handlers are what handles, events are what the host emits.",
			"type": "array",
			"items": { "$ref": "#/$defs/eventDefinition" }
		},
		"libraries": {
			"description": "Curated allow-list of libraries mod code may import. Keys are module specifiers (npm-style: '@example/doc', 'lodash'). The blanket import-deny stays in force; an approved specifier is the only thing that lifts it, and only for mods holding the library's capability. An imported library runs IN the sandbox at the importing mod's own privilege — approving one vouches 'this is sandbox-safe shared code I choose to offer', it grants no new power. The host supplies each library's source at runtime construction; xript provides the allow-listed link path, never the library itself.",
			"type": "object",
			"propertyNames": {
				"pattern": "^(@[a-z0-9-~][a-z0-9-._~]*\\/)?[a-z0-9-~][a-z0-9-._~]*$"
			},
			"additionalProperties": {
				"$ref": "#/$defs/library"
			}
		}
	},
	"$defs": {
		"library": {
			"description": "An approved importable library. The host registers the matching pre-bundled ES module source when constructing the runtime; the manifest entry declares that the specifier exists, what gates it, and what version contract the host vouches for.",
			"type": "object",
			"required": ["description"],
			"properties": {
				"description": {
					"type": "string",
					"description": "What this library provides to mod code. Shown in generated docs."
				},
				"capability": {
					"$ref": "#/$defs/capabilityRef",
					"description": "The capability a mod must hold to import this library. Omit for an ungated library available to every mod."
				},
				"version": {
					"type": "string",
					"description": "The version (or semver range) of the library the host ships. Informational: documents the contract mod authors compile against."
				},
				"deprecated": {
					"type": "string",
					"description": "If present, the library is deprecated; the value explains what to use instead."
				}
			},
			"additionalProperties": false
		},
		"capabilityRef": {
			"type": "string",
			"pattern": "^(read:|write:)?[a-z][a-z0-9-]*(\\.[a-z][a-z0-9-]*)*$",
			"description": "A capability name: an optional 'read:'/'write:' mode prefix followed by a dot-delimited scope. Each scope segment is lowercase-hyphen (e.g. 'modify-player'). Absent prefix means write (full access). Examples: 'fs.addon', 'read:fs.addon', 'write:run.command'."
		},
		"typeRef": {
			"description": "A reference to a type. Can be a primitive, a custom type name, an array type, or a union.",
			"oneOf": [
				{
					"type": "string",
					"description": "A primitive type name, custom type reference, or shorthand.",
					"examples": ["string", "number", "boolean", "void", "null", "Player", "string[]", "number[]"]
				},
				{
					"type": "object",
					"description": "A complex type expression.",
					"properties": {
						"array": {
							"$ref": "#/$defs/typeRef",
							"description": "An array of this type."
						},
						"union": {
							"type": "array",
							"items": { "$ref": "#/$defs/typeRef" },
							"minItems": 2,
							"description": "A union of multiple types."
						},
						"map": {
							"$ref": "#/$defs/typeRef",
							"description": "A string-keyed map with values of this type."
						},
						"optional": {
							"$ref": "#/$defs/typeRef",
							"description": "An optional (nullable) version of this type."
						}
					},
					"oneOf": [
						{ "required": ["array"] },
						{ "required": ["union"] },
						{ "required": ["map"] },
						{ "required": ["optional"] }
					],
					"additionalProperties": false
				}
			]
		},
		"parameter": {
			"description": "A function parameter with a name, type, and optional description.",
			"type": "object",
			"required": ["name", "type"],
			"properties": {
				"name": {
					"type": "string",
					"description": "The parameter name."
				},
				"type": {
					"$ref": "#/$defs/typeRef",
					"description": "The parameter's type."
				},
				"description": {
					"type": "string",
					"description": "What this parameter represents."
				},
				"default": {
					"description": "The default value if the parameter is omitted. Implies the parameter is optional."
				},
				"required": {
					"type": "boolean",
					"description": "Whether this parameter is required. Defaults to true unless a default value is provided.",
					"default": true
				}
			},
			"additionalProperties": false
		},
		"binding": {
			"description": "A function or namespace exposed to scripts.",
			"oneOf": [
				{ "$ref": "#/$defs/functionBinding" },
				{ "$ref": "#/$defs/namespaceBinding" }
			]
		},
		"functionBinding": {
			"description": "A callable function exposed to scripts.",
			"type": "object",
			"required": ["description"],
			"properties": {
				"description": {
					"type": "string",
					"description": "What this function does. Shown in generated docs and editor tooltips."
				},
				"params": {
					"type": "array",
					"items": { "$ref": "#/$defs/parameter" },
					"description": "The function's parameters, in order."
				},
				"returns": {
					"$ref": "#/$defs/typeRef",
					"description": "The return type. Omit for void functions."
				},
				"async": {
					"type": "boolean",
					"description": "Whether this function is asynchronous. Defaults to false.",
					"default": false
				},
				"capability": {
					"$ref": "#/$defs/capabilityRef",
					"description": "The capability required to call this function. If omitted, the function is always available. May carry a 'read:'/'write:' mode prefix and a dotted scope; a grant satisfies it by mode-lattice and prefix-subsumption."
				},
				"examples": {
					"type": "array",
					"items": { "$ref": "#/$defs/example" },
					"description": "Usage examples for documentation."
				},
				"deprecated": {
					"type": "string",
					"description": "If present, this function is deprecated. The value describes the migration path."
				}
			},
			"additionalProperties": false
		},
		"namespaceBinding": {
			"description": "A namespace that groups related functions.",
			"type": "object",
			"required": ["description", "members"],
			"properties": {
				"description": {
					"type": "string",
					"description": "What this namespace represents."
				},
				"members": {
					"type": "object",
					"additionalProperties": {
						"$ref": "#/$defs/binding"
					},
					"description": "The functions and sub-namespaces in this namespace."
				}
			},
			"additionalProperties": false
		},
		"capability": {
			"description": "A named capability that gates access to functionality.",
			"type": "object",
			"required": ["description"],
			"properties": {
				"description": {
					"type": "string",
					"description": "What this capability grants access to. Shown to the user when a script requests it."
				},
				"risk": {
					"type": "string",
					"enum": ["low", "medium", "high"],
					"description": "Advisory risk level. Helps users make informed decisions about granting capabilities.",
					"default": "low"
				},
				"reserved": {
					"type": "boolean",
					"default": false,
					"description": "When true, the capability is intentionally declared without yet gating a slot, binding, or hook — inherited for canon parity, or reserved for a future surface. Tooling treats it as aspirational rather than vestigial, and suppresses the unreferenced-capability warning."
				}
			},
			"additionalProperties": false
		},
		"typeDefinition": {
			"description": "A custom type definition.",
			"type": "object",
			"required": ["description"],
			"properties": {
				"description": {
					"type": "string",
					"description": "What this type represents."
				},
				"fields": {
					"type": "object",
					"additionalProperties": {
						"$ref": "#/$defs/fieldDefinition"
					},
					"description": "The fields of this type (for object/struct types)."
				},
				"values": {
					"type": "array",
					"items": { "type": "string" },
					"description": "The allowed values (for enum types)."
				},
				"open": {
					"type": "boolean",
					"default": false,
					"description": "When true, the `values` are the known set but any other string is also valid — an open enum. Codegen emits `... | (string & {})` so unlisted values (for example, addon-contributed ones) still type-check; xript does not enforce the closed set at runtime regardless."
				},
				"abstract": {
					"type": "boolean",
					"default": false,
					"description": "When true, this type is declared and described but not populated — it supplies neither fields nor values, standing as a contract hole that an extending manifest is expected to fill with concrete fields or values. An abstract type that survives into the resolved manifest is a defect ('abstract-type-unfilled')."
				},
				"refines": {
					"type": "boolean",
					"default": false,
					"description": "When true, this definition refines a concrete base type of the same name, deep-merging onto it field by field. Without it, redeclaring a concrete type name is a resolution error. Does not apply to abstract base types, which are filled rather than refined."
				}
			},
			"additionalProperties": false
		},
		"fieldDefinition": {
			"description": "A field within a custom type.",
			"type": "object",
			"required": ["type"],
			"properties": {
				"type": {
					"$ref": "#/$defs/typeRef"
				},
				"description": {
					"type": "string"
				},
				"optional": {
					"type": "boolean",
					"description": "Whether this field can be absent. Defaults to false.",
					"default": false
				},
				"default": {
					"description": "Default value if the field is absent. Documentation and codegen hint only; xript does not apply it at runtime."
				},
				"enum": {
					"type": "array",
					"items": {},
					"minItems": 1,
					"description": "Allowed literal values for this field. Codegen emits a literal union; xript does not enforce at runtime."
				},
				"open": {
					"type": "boolean",
					"default": false,
					"description": "When true, the `enum` values are the known set but any other string is also valid — an open enum. Codegen emits `... | (string & {})` so unlisted values still type-check; xript does not enforce the closed set at runtime regardless."
				}
			},
			"additionalProperties": false
		},
		"example": {
			"description": "A usage example for documentation.",
			"type": "object",
			"required": ["code"],
			"properties": {
				"title": {
					"type": "string",
					"description": "A short title for this example."
				},
				"code": {
					"type": "string",
					"description": "The JavaScript code demonstrating usage."
				},
				"description": {
					"type": "string",
					"description": "Explanation of what this example demonstrates."
				}
			},
			"additionalProperties": false
		},
		"hook": {
			"description": "A hook that scripts can register handlers for. Hooks are the reverse of bindings: the host fires them, and scripts handle them.",
			"type": "object",
			"required": ["description"],
			"properties": {
				"description": {
					"type": "string",
					"description": "What this hook represents and when it fires. Shown in generated docs."
				},
				"phases": {
					"type": "array",
					"items": {
						"type": "string",
						"enum": ["pre", "post", "done", "error"]
					},
					"minItems": 1,
					"uniqueItems": true,
					"description": "Lifecycle phases for this hook. If omitted, the hook is a simple notification with no lifecycle."
				},
				"params": {
					"type": "array",
					"items": { "$ref": "#/$defs/parameter" },
					"description": "The parameters passed to hook handlers when fired."
				},
				"capability": {
					"$ref": "#/$defs/capabilityRef",
					"description": "The capability required to register for this hook. If omitted, any script can register. May carry a 'read:'/'write:' mode prefix and a dotted scope; a grant satisfies it by mode-lattice and prefix-subsumption."
				},
				"async": {
					"type": "boolean",
					"description": "Whether handlers run asynchronously. Host-controlled. Defaults to false.",
					"default": false
				},
				"limits": {
					"$ref": "#/$defs/executionLimits",
					"description": "Per-handler execution limits. Overrides the manifest-level defaults for this hook's handlers."
				},
				"examples": {
					"type": "array",
					"items": { "$ref": "#/$defs/example" },
					"description": "Usage examples for documentation."
				},
				"deprecated": {
					"type": "string",
					"description": "If present, this hook is deprecated. The value describes the migration path."
				}
			},
			"additionalProperties": false
		},
		"slot": {
			"description": "A typed plug-point the host declares and mods fill. The 'accepts' type governs what a valid fill looks like and what the host does with it (mount it, call it, resolve it, or fire it).",
			"type": "object",
			"required": ["id", "accepts"],
			"properties": {
				"id": {
					"type": "string",
					"pattern": "^[a-z][a-z0-9.-]*$",
					"description": "Unique slot identifier (e.g. 'sidebar.left', 'statusbar.right')."
				},
				"accepts": {
					"type": "array",
					"items": { "type": "string" },
					"minItems": 1,
					"description": "The format(s) or kind(s) this slot takes from fills, e.g. 'text/html+jsml', 'application/javascript+esm', 'application/json', 'application/x-xript-role', 'application/x-xript-hook'."
				},
				"description": {
					"type": "string",
					"description": "Human-readable description of what this slot is for. Surfaced in generated docs and used by tooling."
				},
				"capability": {
					"$ref": "#/$defs/capabilityRef",
					"description": "Capability required to fill this slot. May carry a 'read:'/'write:' mode prefix and a dotted scope; a grant satisfies it by mode-lattice and prefix-subsumption."
				},
				"multiple": {
					"type": "boolean",
					"default": false,
					"description": "Whether multiple mods can fill this slot simultaneously."
				},
				"payload": {
					"type": "object",
					"additionalProperties": true,
					"description": "A JSON Schema (draft 2020-12) that a fill's payload for this slot must satisfy. The host validates each fill against it; an extending manifest may refine the contract."
				},
				"reserved": {
					"type": "boolean",
					"default": false,
					"description": "When true, the slot is intentionally declared without a current filler — inherited for canon parity, or reserved for future mods. Tooling treats it as aspirational rather than dead, and suppresses the unfilled-slot warning."
				},
				"style": {
					"type": "string",
					"enum": ["inherit", "isolated", "scoped"],
					"default": "inherit",
					"description": "Styling mode: 'inherit' applies host styles, 'isolated' prevents style bleed, 'scoped' exposes CSS custom properties."
				},
				"refines": {
					"type": "boolean",
					"default": false,
					"description": "When true, this slot definition refines a base slot of the same id, deep-merging onto it including its payload contract. Without it, redeclaring a slot id is a resolution error."
				}
			},
			"additionalProperties": false
		},
		"eventDefinition": {
			"description": "A named event the host emits or broadcasts. A discovery declaration, not an extension point: it states what the host fires and the shape of the payload, leaving consumers (sandbox scripts, the host UI, external subscribers) unpresupposed.",
			"type": "object",
			"required": ["id", "description"],
			"properties": {
				"id": {
					"type": "string",
					"pattern": "^[a-z][a-z0-9.-]*$",
					"description": "Unique event identifier (e.g. 'document.saved', 'selection.changed')."
				},
				"description": {
					"type": "string",
					"description": "What this event represents and when the host emits it. Shown in generated docs."
				},
				"payload": {
					"$ref": "#/$defs/typeRef",
					"description": "The type of the payload delivered with this event. Omit for events that carry no payload."
				},
				"capability": {
					"$ref": "#/$defs/capabilityRef",
					"description": "The capability required to subscribe to this event via 'events.on'. If omitted, any script can subscribe. Gating happens at registration time, reusing the hook gate model: a grant satisfies it by mode-lattice and prefix-subsumption."
				}
			},
			"additionalProperties": false
		},
		"executionLimits": {
			"description": "Default execution limits for scripts. Runtimes enforce these limits to prevent denial of service.",
			"type": "object",
			"properties": {
				"timeout_ms": {
					"type": "integer",
					"minimum": 1,
					"description": "Maximum execution time in milliseconds.",
					"default": 5000
				},
				"memory_mb": {
					"type": "integer",
					"minimum": 1,
					"description": "Maximum memory usage in megabytes.",
					"default": 64
				},
				"max_stack_depth": {
					"type": "integer",
					"minimum": 1,
					"description": "Maximum call stack depth.",
					"default": 256
				}
			},
			"additionalProperties": false
		}
	}
}
