mirror of
https://github.com/heygen-com/hyperframes.git
synced 2026-09-01 19:42:03 +00:00
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.
This commit is contained in:
@@ -5,11 +5,15 @@ on:
|
||||
paths:
|
||||
- "docs/**"
|
||||
- "DOCS_GUIDELINES.md"
|
||||
- "packages/core/schemas/**"
|
||||
- "scripts/sync-schemas.ts"
|
||||
push:
|
||||
branches: [main]
|
||||
paths:
|
||||
- "docs/**"
|
||||
- "DOCS_GUIDELINES.md"
|
||||
- "packages/core/schemas/**"
|
||||
- "scripts/sync-schemas.ts"
|
||||
|
||||
concurrency:
|
||||
group: docs-${{ github.ref }}
|
||||
@@ -27,6 +31,9 @@ jobs:
|
||||
with:
|
||||
node-version: 22
|
||||
|
||||
- name: Check schema mirror (core → docs)
|
||||
run: npx tsx scripts/sync-schemas.ts --check
|
||||
|
||||
- name: Validate build
|
||||
working-directory: docs
|
||||
run: npx mint validate
|
||||
|
||||
@@ -0,0 +1,45 @@
|
||||
{
|
||||
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
||||
"$id": "https://hyperframes.heygen.com/schema/hyperframes.json",
|
||||
"title": "Hyperframes Project Config",
|
||||
"description": "Per-project configuration for a Hyperframes project (hyperframes.json). Tells `hyperframes add` which registry to pull items from and where to drop them in the project tree. Created by `hyperframes init`; users may edit it to point at custom registries or reshape their project layout.",
|
||||
"type": "object",
|
||||
"required": ["registry", "paths"],
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"$schema": {
|
||||
"type": "string",
|
||||
"format": "uri",
|
||||
"description": "JSON Schema URL — https://hyperframes.heygen.com/schema/hyperframes.json."
|
||||
},
|
||||
"registry": {
|
||||
"type": "string",
|
||||
"format": "uri",
|
||||
"minLength": 1,
|
||||
"description": "Base URL of the registry to pull items from. Point at the official Hyperframes registry or a custom one."
|
||||
},
|
||||
"paths": {
|
||||
"type": "object",
|
||||
"description": "Target paths for each item type, relative to the project root.",
|
||||
"required": ["blocks", "components", "assets"],
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"blocks": {
|
||||
"type": "string",
|
||||
"minLength": 1,
|
||||
"description": "Where `hyperframes:block` items land. Defaults to `compositions`."
|
||||
},
|
||||
"components": {
|
||||
"type": "string",
|
||||
"minLength": 1,
|
||||
"description": "Where `hyperframes:component` items land. Defaults to `compositions/components`."
|
||||
},
|
||||
"assets": {
|
||||
"type": "string",
|
||||
"minLength": 1,
|
||||
"description": "Where asset files (images, fonts, videos) land. Defaults to `assets`."
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,141 @@
|
||||
{
|
||||
"$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"]
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
{
|
||||
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
||||
"$id": "https://hyperframes.heygen.com/schema/registry.json",
|
||||
"title": "Hyperframes Registry Manifest",
|
||||
"description": "Top-level manifest describing all items in a Hyperframes registry.",
|
||||
"type": "object",
|
||||
"required": ["name", "homepage", "items"],
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"$schema": {
|
||||
"type": "string",
|
||||
"format": "uri"
|
||||
},
|
||||
"name": {
|
||||
"type": "string",
|
||||
"minLength": 1,
|
||||
"description": "Registry name (e.g. \"hyperframes\")."
|
||||
},
|
||||
"homepage": {
|
||||
"type": "string",
|
||||
"format": "uri",
|
||||
"description": "Registry homepage URL."
|
||||
},
|
||||
"items": {
|
||||
"type": "array",
|
||||
"description": "Items in this registry. Each entry is a shorthand reference; the full item manifest lives at <type-dir>/<name>/registry-item.json.",
|
||||
"items": {
|
||||
"type": "object",
|
||||
"required": ["name", "type"],
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"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"]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -18,6 +18,8 @@
|
||||
"build:hyperframes-runtime:modular": "bun run --filter @hyperframes/core build:hyperframes-runtime:modular",
|
||||
"verify:packed-manifests": "node scripts/verify-packed-manifests.mjs",
|
||||
"set-version": "tsx scripts/set-version.ts",
|
||||
"sync-schemas": "tsx scripts/sync-schemas.ts",
|
||||
"sync-schemas:check": "tsx scripts/sync-schemas.ts --check",
|
||||
"lint": "oxlint . && tsx scripts/lint-skills.ts",
|
||||
"lint:skills": "tsx scripts/lint-skills.ts",
|
||||
"lint:fix": "oxlint --fix .",
|
||||
|
||||
@@ -0,0 +1,60 @@
|
||||
#!/usr/bin/env tsx
|
||||
/**
|
||||
* Mirror JSON Schemas from `packages/core/schemas/` into `docs/schema/` so
|
||||
* Mintlify serves them at `https://hyperframes.heygen.com/schema/*`. The core
|
||||
* copies stay authoritative — they're exported from `@hyperframes/core` for
|
||||
* npm consumers — and this script is the single contract that prevents the
|
||||
* docs mirror from drifting.
|
||||
*
|
||||
* Usage:
|
||||
* bun run sync-schemas # copy core → docs
|
||||
* bun run sync-schemas --check # exit non-zero if copies are stale (CI)
|
||||
*
|
||||
* `docs/schema/hyperframes.json` is authored directly in docs (no source in
|
||||
* core) so it's skipped by this script.
|
||||
*/
|
||||
|
||||
import { readFileSync, writeFileSync } from "node:fs";
|
||||
import { join } from "node:path";
|
||||
|
||||
const ROOT = join(import.meta.dirname, "..");
|
||||
const SOURCE_DIR = join(ROOT, "packages/core/schemas");
|
||||
const TARGET_DIR = join(ROOT, "docs/schema");
|
||||
const MIRRORED = ["registry.json", "registry-item.json"];
|
||||
|
||||
function main() {
|
||||
const checkOnly = process.argv.includes("--check");
|
||||
let drift = 0;
|
||||
|
||||
for (const name of MIRRORED) {
|
||||
const source = readFileSync(join(SOURCE_DIR, name), "utf-8");
|
||||
const targetPath = join(TARGET_DIR, name);
|
||||
const target = (() => {
|
||||
try {
|
||||
return readFileSync(targetPath, "utf-8");
|
||||
} catch {
|
||||
return null;
|
||||
}
|
||||
})();
|
||||
|
||||
if (target === source) {
|
||||
console.log(` ✓ ${name} in sync`);
|
||||
continue;
|
||||
}
|
||||
|
||||
drift++;
|
||||
if (checkOnly) {
|
||||
console.error(` ✗ ${name} out of sync (run \`bun run sync-schemas\` to fix)`);
|
||||
continue;
|
||||
}
|
||||
writeFileSync(targetPath, source);
|
||||
console.log(` → ${name} updated`);
|
||||
}
|
||||
|
||||
if (checkOnly && drift > 0) {
|
||||
console.error(`\n${drift} schema${drift === 1 ? "" : "s"} drifted from source.`);
|
||||
process.exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
main();
|
||||
Reference in New Issue
Block a user