Files
hyperframes/docs/schema/registry-item.json
T
Miguel Ángel 9ef864d1f2 fix(docs): serve hyperframes.json / registry JSON schemas (#304) (#305)
Closes #304.

## Summary

The three `/schema/*.json` URLs baked into every Hyperframes project as `\$schema` references are 404ing on the live docs site — blocking editor autocomplete and validation.

- \`https://hyperframes.heygen.com/schema/hyperframes.json\` — **404** (missing entirely)
- \`https://hyperframes.heygen.com/schema/registry.json\` — **404** (only in npm package)
- \`https://hyperframes.heygen.com/schema/registry-item.json\` — **404** (only in npm package)

Mintlify serves top-level non-MDX dirs in \`docs/\` at \`/\<dir>/*\` (confirmed by \`docs/logo/*.svg\` → \`/logo/*.svg\`). This PR drops the three schemas into \`docs/schema/\` so the URLs resolve.

## What changed

| File | Role |
|---|---|
| \`docs/schema/hyperframes.json\` | **New.** Authored from the \`ProjectConfig\` type in \`packages/cli/src/utils/projectConfig.ts\`. |
| \`docs/schema/registry.json\` | Mirror of \`packages/core/schemas/registry.json\`. |
| \`docs/schema/registry-item.json\` | Mirror of \`packages/core/schemas/registry-item.json\`. |
| \`scripts/sync-schemas.ts\` | Keeps the registry mirrors in lockstep with their authoritative copies in \`packages/core/schemas/\`. \`--check\` mode fails the Docs workflow on drift. |
| \`.github/workflows/docs.yml\` | Runs \`tsx scripts/sync-schemas.ts --check\` on every PR touching docs or core schemas. |
| \`package.json\` | \`sync-schemas\` / \`sync-schemas:check\` npm scripts. |

## Why not make \`packages/core/schemas/\` authoritative for \`hyperframes.json\` too?

\`hyperframes.json\` is CLI config, not a core type. Keeping the schema in \`docs/\` avoids an artificial dependency between \`@hyperframes/core\` and \`@hyperframes/cli\`. If the two ever need to align, we can flip the direction then.

## Verification

- \`bun run sync-schemas:check\` → \`2/2 in sync\`.
- Ajv (draft 2020-12, in-process) validation against 9 cases:
  - ✓ real factory-series-c-video config
  - ✓ default shape from \`hyperframes init\`
  - ✓ \`\$schema\` is optional
  - ✓ missing registry → rejected
  - ✓ missing paths.assets → rejected
  - ✓ extra top-level key → rejected
  - ✓ empty registry string → rejected
  - ✓ empty block path → rejected
  - ✓ missing paths entirely → rejected

## Test plan

- [x] \`tsx scripts/sync-schemas.ts --check\` passes locally
- [x] Schemas parse as valid JSON and validate real/default project configs
- [x] After merge: \`curl -sI https://hyperframes.heygen.com/schema/hyperframes.json\` returns 200 once Mintlify redeploys
- [x] Same check for \`/schema/registry.json\` and \`/schema/registry-item.json\`
- [x] VS Code autocomplete and error-highlighting work on \`hyperframes.json\` without extra config

## Notes

- The Docs workflow now triggers on \`packages/core/schemas/**\` and \`scripts/sync-schemas.ts\` in addition to \`docs/**\`, so a core-schemas change that forgets to run \`sync-schemas\` will fail CI instead of silently publishing stale docs.
- No runtime / API changes to any package; ship independent of a version bump.
2026-04-17 17:43:43 +02:00

142 lines
3.9 KiB
JSON

{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://hyperframes.heygen.com/schema/registry-item.json",
"title": "Hyperframes Registry Item",
"description": "Manifest for a single distributable item (example, block, or component).",
"type": "object",
"required": ["name", "type", "title", "description", "files"],
"properties": {
"$schema": {
"type": "string",
"format": "uri"
},
"name": {
"type": "string",
"pattern": "^[a-z0-9]([a-z0-9-]*[a-z0-9])?$",
"description": "Item name in kebab-case, must start and end with alphanumeric."
},
"type": {
"type": "string",
"enum": ["hyperframes:example", "hyperframes:block", "hyperframes:component"]
},
"title": {
"type": "string",
"minLength": 1
},
"description": {
"type": "string",
"minLength": 1
},
"tags": {
"type": "array",
"items": { "type": "string", "minLength": 1 }
},
"author": {
"type": "string",
"minLength": 1
},
"license": {
"type": "string",
"minLength": 1,
"description": "SPDX license identifier (e.g. \"Apache-2.0\", \"MIT\")."
},
"minCliVersion": {
"type": "string",
"pattern": "^\\d+\\.\\d+\\.\\d+(?:-[0-9A-Za-z-]+(?:\\.[0-9A-Za-z-]+)*)?$",
"description": "Minimum `hyperframes` CLI version required to install this item."
},
"deprecated": {
"type": "string",
"minLength": 1,
"description": "If set, the item is deprecated; the value is the reason or migration note."
},
"dimensions": {
"type": "object",
"required": ["width", "height"],
"additionalProperties": false,
"properties": {
"width": { "type": "integer", "minimum": 1 },
"height": { "type": "integer", "minimum": 1 }
}
},
"duration": {
"type": "number",
"exclusiveMinimum": 0,
"description": "Duration in seconds. Must be > 0."
},
"registryDependencies": {
"type": "array",
"items": {
"type": "string",
"pattern": "^[a-z0-9]([a-z0-9-]*[a-z0-9])?$"
}
},
"files": {
"type": "array",
"minItems": 1,
"items": {
"type": "object",
"required": ["path", "target", "type"],
"additionalProperties": false,
"properties": {
"path": {
"type": "string",
"minLength": 1,
"description": "Source path, relative to registry-item.json."
},
"target": {
"type": "string",
"minLength": 1,
"description": "Destination path in the user's project, relative to project root. Must not traverse outside the project (no `..` segments, no absolute paths).",
"not": {
"anyOf": [
{ "pattern": "(^|[/\\\\])\\.\\.([/\\\\]|$)" },
{ "pattern": "^[/\\\\]" },
{ "pattern": "^[A-Za-z]:[/\\\\]" }
]
}
},
"type": {
"type": "string",
"enum": [
"hyperframes:composition",
"hyperframes:asset",
"hyperframes:snippet",
"hyperframes:style",
"hyperframes:timeline"
]
}
}
}
},
"preview": {
"type": "object",
"additionalProperties": false,
"properties": {
"video": { "type": "string" },
"poster": { "type": "string" }
}
},
"relatedSkill": {
"type": "string",
"minLength": 1
}
},
"allOf": [
{
"if": {
"required": ["type"],
"properties": { "type": { "const": "hyperframes:component" } }
},
"then": {
"not": {
"anyOf": [{ "required": ["dimensions"] }, { "required": ["duration"] }]
}
},
"else": {
"required": ["dimensions", "duration"]
}
}
]
}