From b8339ed5e3e1f0acf1d285cdd25e6c86a6a138c3 Mon Sep 17 00:00:00 2001 From: hkonsti <45428767+hkonsti@users.noreply.github.com> Date: Tue, 7 Jul 2026 14:54:31 -0700 Subject: [PATCH] feat: add docs.re.video redirect service --- package-lock.json | 8 + packages/docs-redirect/README.md | 45 ++++ packages/docs-redirect/data/old-urls.txt | 286 +++++++++++++++++++++ packages/docs-redirect/package.json | 14 ++ packages/docs-redirect/src/build-map.mjs | 257 +++++++++++++++++++ packages/docs-redirect/src/redirects.json | 288 ++++++++++++++++++++++ packages/docs-redirect/src/server.mjs | 67 +++++ 7 files changed, 965 insertions(+) create mode 100644 packages/docs-redirect/README.md create mode 100644 packages/docs-redirect/data/old-urls.txt create mode 100644 packages/docs-redirect/package.json create mode 100644 packages/docs-redirect/src/build-map.mjs create mode 100644 packages/docs-redirect/src/redirects.json create mode 100644 packages/docs-redirect/src/server.mjs diff --git a/package-lock.json b/package-lock.json index 1abc708f..1ec861c1 100644 --- a/package-lock.json +++ b/package-lock.json @@ -4734,6 +4734,10 @@ "resolved": "packages/docs", "link": true }, + "node_modules/@revideo/docs-redirect": { + "resolved": "packages/docs-redirect", + "link": true + }, "node_modules/@revideo/e2e": { "resolved": "packages/e2e", "link": true @@ -25474,6 +25478,10 @@ "typedoc-plugin-markdown": "^4.12.0" } }, + "packages/docs-redirect": { + "name": "@revideo/docs-redirect", + "version": "0.0.0" + }, "packages/docs/node_modules/@types/node": { "version": "22.13.10", "resolved": "https://registry.npmjs.org/@types/node/-/node-22.13.10.tgz", diff --git a/packages/docs-redirect/README.md b/packages/docs-redirect/README.md new file mode 100644 index 00000000..75d7dd02 --- /dev/null +++ b/packages/docs-redirect/README.md @@ -0,0 +1,45 @@ +# @revideo/docs-redirect + +A tiny redirect service that replaces the old documentation site at +`docs.re.video`. Every old URL is forwarded with a `301` to its new home under +`https://midrender.com/revideo/docs`. + +## How it works + +- **`data/old-urls.txt`** — the authoritative list of old paths, a snapshot of + `docs.re.video`'s `sitemap.xml` (286 URLs). +- **`src/build-map.mjs`** — generates `src/redirects.json` by mapping every old + path to its new page. It reads the current docs (`packages/docs/src/content`) + to compute new URLs and to guarantee no redirect points at a 404: + - prose pages are mapped explicitly (the old Docusaurus site used custom + `slug`s, and `motion-canvas/*` became `animations/*`); + - TypeDoc API pages move from `/api///` to + `/api-reference////`, resolved by locating each + symbol in the new tree; + - anything without an exact new page (a handful of removed API symbols such as + the `meta` module and `Presenter`) falls back to the closest section index. +- **`src/redirects.json`** — the generated map (committed). Regenerate with + `npm run build` whenever the docs are restructured. +- **`src/server.mjs`** — a zero-dependency HTTP server that looks up the request + path and issues the redirect. Unknown paths fall back to the docs home. Runs + under Node or Bun. + +## Usage + +```bash +npm run build # regenerate src/redirects.json from the current docs +npm start # start the server (PORT env, default 8080) +``` + +Health check: `GET /healthz` → `200 ok`. + +## Deploying + +Point `docs.re.video` at this service (any Node/Bun host or container). The +server is stateless and keeps the map in memory. Example: + +```bash +PORT=8080 node src/server.mjs +# or +PORT=8080 bun src/server.mjs +``` diff --git a/packages/docs-redirect/data/old-urls.txt b/packages/docs-redirect/data/old-urls.txt new file mode 100644 index 00000000..9cbfc54b --- /dev/null +++ b/packages/docs-redirect/data/old-urls.txt @@ -0,0 +1,286 @@ +/ +/3d-animations-with-threejs +/api/2d +/api/2d/code +/api/2d/code/CodeFragment +/api/2d/code/CodeFragmentDrawingInfo +/api/2d/code/CodeHighlighter +/api/2d/code/CodeScope +/api/2d/code/CodeSignalContext +/api/2d/code/CodeSignalHelpers +/api/2d/code/HighlightResult +/api/2d/code/LezerHighlighter +/api/2d/code/RawCodeFragment +/api/2d/components +/api/2d/components/Audio +/api/2d/components/Bezier +/api/2d/components/BezierOverlayInfo +/api/2d/components/Circle +/api/2d/components/CircleProps +/api/2d/components/Code +/api/2d/components/CodeProps +/api/2d/components/CubicBezier +/api/2d/components/CubicBezierProps +/api/2d/components/Curve +/api/2d/components/CurveProps +/api/2d/components/DrawHooks +/api/2d/components/DrawTokenHook +/api/2d/components/FunctionComponent +/api/2d/components/Grid +/api/2d/components/GridProps +/api/2d/components/Icon +/api/2d/components/IconProps +/api/2d/components/Img +/api/2d/components/ImgProps +/api/2d/components/JSXProps +/api/2d/components/Knot +/api/2d/components/KnotProps +/api/2d/components/Latex +/api/2d/components/LatexProps +/api/2d/components/Layout +/api/2d/components/LayoutProps +/api/2d/components/Line +/api/2d/components/LineProps +/api/2d/components/Media +/api/2d/components/MediaProps +/api/2d/components/Node +/api/2d/components/NodeConstructor +/api/2d/components/NodeProps +/api/2d/components/Path +/api/2d/components/PathProps +/api/2d/components/Polygon +/api/2d/components/PolygonProps +/api/2d/components/QuadBezier +/api/2d/components/QuadBezierProps +/api/2d/components/Ray +/api/2d/components/RayProps +/api/2d/components/Rect +/api/2d/components/RectProps +/api/2d/components/Rive +/api/2d/components/RiveProps +/api/2d/components/SVG +/api/2d/components/SVGDocument +/api/2d/components/SVGDocumentData +/api/2d/components/SVGProps +/api/2d/components/SVGShape +/api/2d/components/SVGShapeData +/api/2d/components/Shape +/api/2d/components/ShapeProps +/api/2d/components/Spline +/api/2d/components/SplineProps +/api/2d/components/Txt +/api/2d/components/TxtProps +/api/2d/components/Video +/api/2d/components/VideoProps +/api/2d/components/View2D +/api/2d/components/View2DProps +/api/2d/curves +/api/2d/curves/CircleSegment +/api/2d/curves/CubicBezierSegment +/api/2d/curves/CurveDrawingInfo +/api/2d/curves/CurvePoint +/api/2d/curves/CurveProfile +/api/2d/curves/KnotInfo +/api/2d/curves/LineSegment +/api/2d/curves/Polynomial +/api/2d/curves/Polynomial2D +/api/2d/curves/QuadBezierSegment +/api/2d/curves/Segment +/api/2d/decorators +/api/2d/decorators/FiltersSignalContext +/api/2d/decorators/PropertyMetadata +/api/2d/partials +/api/2d/partials/Filter +/api/2d/partials/FilterProps +/api/2d/partials/Gradient +/api/2d/partials/GradientProps +/api/2d/partials/GradientStop +/api/2d/partials/Pattern +/api/2d/partials/PatternProps +/api/2d/scenes +/api/2d/scenes/Scene2D +/api/2d/utils +/api/core +/api/core/app +/api/core/app/AssetInfo +/api/core/app/LogLevel +/api/core/app/LogPayload +/api/core/app/Logger +/api/core/app/PlaybackState +/api/core/app/PlaybackStatus +/api/core/app/Player +/api/core/app/PlayerSettings +/api/core/app/PlayerState +/api/core/app/Presenter +/api/core/app/PresenterInfo +/api/core/app/PresenterSettings +/api/core/app/PresenterState +/api/core/app/Project +/api/core/app/ProjectMetadata +/api/core/app/ProjectSettings +/api/core/app/Renderer +/api/core/app/RendererResult +/api/core/app/RendererSettings +/api/core/app/RendererState +/api/core/app/SharedWebGLContext +/api/core/app/Stage +/api/core/app/StageSettings +/api/core/app/Versions +/api/core/decorators +/api/core/events +/api/core/events/AsyncEventDispatcher +/api/core/events/AsyncEventHandler +/api/core/events/EventDispatcher +/api/core/events/EventDispatcherBase +/api/core/events/EventHandler +/api/core/events/FlagDispatcher +/api/core/events/Subscribable +/api/core/events/SubscribableValueEvent +/api/core/events/ValueDispatcher +/api/core/exporter +/api/core/exporter/Exporter +/api/core/exporter/ExporterClass +/api/core/exporter/FFmpegExporterClient +/api/core/exporter/WasmExporter +/api/core/flow +/api/core/flow/EveryCallback +/api/core/flow/EveryTimer +/api/core/flow/LoopCallback +/api/core/media +/api/core/media/AudioData +/api/core/media/AudioManager +/api/core/meta +/api/core/meta/BoolMetaField +/api/core/meta/ColorMetaField +/api/core/meta/EnumMetaField +/api/core/meta/ExporterMetaField +/api/core/meta/MetaField +/api/core/meta/MetaOption +/api/core/meta/NumberMetaField +/api/core/meta/RangeMetaField +/api/core/meta/StringMetaField +/api/core/meta/Vector2MetaField +/api/core/plugin +/api/core/plugin/Plugin +/api/core/scenes +/api/core/scenes/CachedSceneData +/api/core/scenes/FullSceneDescription +/api/core/scenes/GeneratorScene +/api/core/scenes/Inspectable +/api/core/scenes/LifecycleEvents +/api/core/scenes/Random +/api/core/scenes/Scene +/api/core/scenes/SceneConstructor +/api/core/scenes/SceneDescription +/api/core/scenes/SceneDescriptionReload +/api/core/scenes/SceneRenderEvent +/api/core/scenes/SceneState +/api/core/scenes/Slide +/api/core/scenes/Slides +/api/core/scenes/ThreadGeneratorFactory +/api/core/scenes/Threadable +/api/core/scenes/Variables +/api/core/signals +/api/core/signals/CompoundSignalContext +/api/core/signals/Computed +/api/core/signals/ComputedContext +/api/core/signals/DependencyContext +/api/core/signals/PromiseHandle +/api/core/signals/Signal +/api/core/signals/SignalContext +/api/core/signals/SignalExtensions +/api/core/signals/SignalGetter +/api/core/signals/SignalSetter +/api/core/signals/SignalTween +/api/core/threading +/api/core/threading/Promisable +/api/core/threading/Thread +/api/core/threading/ThreadsCallback +/api/core/threading/ThreadsFactory +/api/core/transitions +/api/core/tweening +/api/core/tweening/InterpolationFunction +/api/core/tweening/Spring +/api/core/tweening/TimingFunction +/api/core/types +/api/core/types/BBox +/api/core/types/Center +/api/core/types/Direction +/api/core/types/Matrix2D +/api/core/types/Origin +/api/core/types/Spacing +/api/core/types/Type +/api/core/types/Vector2 +/api/core/types/WebGLConvertible +/api/core/utils +/api/core/utils/DetailedError +/api/core/utils/ExperimentalError +/api/core/utils/Reference +/api/core/utils/ReferenceReceiver +/api/player-react/player +/api/renderer/renderVideo +/category/api-reference +/category/building-web-apps +/category/code-snippets +/category/common-issues +/category/designing-videos +/category/get-help +/category/motion-canvas-guide +/category/performance-considerations +/category/platform +/category/revideoffmpeg +/category/revideoplayer-react +/category/revideorenderer +/category/upgrading +/category/usage-guide +/changing-object-size-over-time-with-signals +/code-animations +/common-issues/ffmpeg +/common-issues/slow-rendering +/configuration +/custom-components +/custom-font +/decoders +/designing-animations +/emojis +/experimental +/ffmpeg/concatenateMedia +/ffmpeg/mergeAudioWithVideo +/filters-and-effects +/flow +/get-help/discord +/hierarchy +/hls-video +/installation-and-setup +/layouts +/logging +/logical-separation +/moving-manipulating-objects +/node-parent-reloading +/parameterized-video +/platform/introduction +/platform/render-endpoint +/positioning +/preview-with-player +/project-structure +/project-variables +/random-values +/references +/render-endpoint +/renderer/renderPartialVideo +/rendering-in-production +/rendering-videos +/rive-animations +/saas-template +/search +/shaders +/signals +/spawners +/streaming-text +/time-events +/transitions +/transparent-video +/tweening +/understanding-scene-flow +/upgrading/from_0.2.x_to_0.3 +/upgrading/from_0.3.x_to_0.4 diff --git a/packages/docs-redirect/package.json b/packages/docs-redirect/package.json new file mode 100644 index 00000000..92581815 --- /dev/null +++ b/packages/docs-redirect/package.json @@ -0,0 +1,14 @@ +{ + "name": "@revideo/docs-redirect", + "version": "0.0.0", + "private": true, + "description": "Redirect service that forwards old docs.re.video URLs to the new docs under midrender.com/revideo/docs.", + "type": "module", + "scripts": { + "build": "node src/build-map.mjs", + "start": "node src/server.mjs" + }, + "engines": { + "node": ">=18" + } +} diff --git a/packages/docs-redirect/src/build-map.mjs b/packages/docs-redirect/src/build-map.mjs new file mode 100644 index 00000000..c11af2e6 --- /dev/null +++ b/packages/docs-redirect/src/build-map.mjs @@ -0,0 +1,257 @@ +// Generates redirects.json: a map from every old docs.re.video path to its new +// URL under https://midrender.com/revideo/docs. +// +// Inputs: +// - data/old-urls.txt the authoritative list of old paths (a snapshot +// of docs.re.video's sitemap.xml) +// - ../docs/src/content/** the current Nextra docs, used both to compute +// new URLs and to validate that every target +// actually exists (so we never emit a redirect to +// a 404). +// +// Run: npm run build (from packages/docs-redirect) + +import {readdirSync, readFileSync, statSync, writeFileSync} from 'node:fs'; +import {dirname, join, relative} from 'node:path'; +import {fileURLToPath} from 'node:url'; + +const here = dirname(fileURLToPath(import.meta.url)); +const OLD_URLS = join(here, '..', 'data', 'old-urls.txt'); +const CONTENT_DIR = join(here, '..', '..', 'docs', 'src', 'content'); +const OUT = join(here, 'redirects.json'); + +const TARGET_ORIGIN = 'https://midrender.com'; +const NEW_BASE = '/revideo/docs'; + +// The old Docusaurus site used custom `slug` frontmatter, so old prose URLs +// don't match the folder tree. This maps each old slug to its new +// content-relative route (no extension). Derived from the archived Docusaurus +// frontmatter; the only structural rename was `motion-canvas/*` -> `animations/*` +// (plus `intro` -> the docs home). Every target is validated below. +const PROSE = { + '/3d-animations-with-threejs': + 'guide/designing-animations/3d-animations-with-threejs', + '/api/player-react/player': 'api-reference/player-react/player', + '/api/renderer/renderVideo': 'api-reference/renderer/renderVideo', + '/changing-object-size-over-time-with-signals': + 'code-snippets/changing-object-size-over-time-with-signals', + '/code-animations': 'animations/code-animations', + '/common-issues/ffmpeg': 'common-issues/ffmpeg', + '/common-issues/slow-rendering': 'common-issues/slow-rendering', + '/configuration': 'animations/configuration', + '/custom-components': 'animations/custom-components', + '/custom-font': 'animations/custom-font', + '/decoders': 'guide/performance/decoders', + '/designing-animations': 'guide/designing-animations/designing-animations', + '/emojis': 'guide/designing-animations/emojis', + '/experimental': 'animations/experimental', + '/ffmpeg/concatenateMedia': 'api-reference/revideo-ffmpeg/concatenate-media', + '/ffmpeg/mergeAudioWithVideo': + 'api-reference/revideo-ffmpeg/merge-audio-with-video', + '/filters-and-effects': 'animations/filters-and-effects', + '/flow': 'animations/flow', + '/hierarchy': 'animations/hierarchy', + '/hls-video': 'code-snippets/hls-video', + '/installation-and-setup': 'guide/installation-and-setup', + '/layouts': 'animations/layouts', + '/logging': 'animations/logging', + '/logical-separation': 'guide/designing-animations/logical-separation', + '/moving-manipulating-objects': 'code-snippets/moving-objects', + '/node-parent-reloading': 'guide/performance/node-parent-reloading', + '/parameterized-video': 'guide/parameterized-video', + '/platform/introduction': 'platform/introduction', + '/platform/render-endpoint': 'platform/render-endpoint', + '/positioning': 'animations/positioning', + '/preview-with-player': 'guide/building-webapps/using-the-player', + '/project-structure': 'guide/project-structure', + '/project-variables': 'animations/project-variables', + '/random-values': 'animations/random', + '/references': 'animations/references', + '/render-endpoint': 'guide/building-webapps/rendering-endpoint', + '/renderer/renderPartialVideo': 'api-reference/renderer/renderPartialVideo', + '/rendering-in-production': 'guide/building-webapps/deploy-rendering-service', + '/rendering-videos': 'guide/rendering-videos', + '/rive-animations': 'guide/designing-animations/rive-animations', + '/saas-template': 'guide/building-webapps/saas-template', + '/shaders': 'animations/shaders', + '/signals': 'animations/signals', + '/spawners': 'animations/spawners', + '/streaming-text': 'code-snippets/streaming-text', + '/time-events': 'animations/time-events', + '/transitions': 'animations/transitions', + '/transparent-video': 'code-snippets/transparent-video', + '/tweening': 'animations/tweening', + '/understanding-scene-flow': 'guide/understanding-scene-flow', +}; + +// Docusaurus auto-generated `/category/*` landing pages. Only the root and +// `api-reference` sections have index pages in the new docs, so the rest point +// at a representative leaf page in the same section (closest guess). +const CATEGORY = { + '/category/api-reference': 'api-reference', + '/category/usage-guide': 'guide/installation-and-setup', + '/category/motion-canvas-guide': 'animations/flow', + '/category/designing-videos': + 'guide/designing-animations/designing-animations', + '/category/building-web-apps': 'guide/building-webapps/rendering-endpoint', + '/category/code-snippets': 'code-snippets/moving-objects', + '/category/common-issues': 'common-issues/ffmpeg', + '/category/get-help': 'get-help/discord', + '/category/performance-considerations': 'guide/performance/decoders', + '/category/platform': 'platform/introduction', + '/category/revideoffmpeg': 'api-reference/revideo-ffmpeg/concatenate-media', + '/category/revideoplayer-react': 'api-reference/player-react/player', + '/category/revideorenderer': 'api-reference/renderer/renderVideo', + '/category/upgrading': 'upgrading/from_0.2.x_to_0.3', +}; + +// TypeDoc symbol pages moved from /api/// to +// /api-reference////. We resolve by locating +// the symbol in the new tree. +const API_KINDS = [ + 'classes', + 'functions', + 'interfaces', + 'type-aliases', + 'enumerations', + 'variables', +]; + +/** Recursively collect every doc route (content-relative, no extension). */ +function collectRoutes(dir) { + const routes = new Set(); + const walk = current => { + for (const entry of readdirSync(current)) { + const full = join(current, entry); + if (statSync(full).isDirectory()) { + walk(full); + } else if (/\.mdx?$/.test(entry)) { + let route = relative(CONTENT_DIR, full).replace(/\.mdx?$/, ''); + route = route.replace(/(^|\/)index$/, ''); // index -> its folder + routes.add(route); + } + } + }; + walk(dir); + return routes; +} + +const routes = collectRoutes(CONTENT_DIR); +const has = route => routes.has(route); +const toUrl = route => TARGET_ORIGIN + NEW_BASE + (route ? '/' + route : ''); + +/** Find the new route for an old TypeDoc symbol, else null. */ +function resolveApiSymbol(pkg, mod, symbol) { + for (const kind of API_KINDS) { + const candidate = `api-reference/${pkg}/${mod}/${kind}/${symbol}`; + if (has(candidate)) return candidate; + } + return null; +} + +const oldPaths = readFileSync(OLD_URLS, 'utf8') + .split('\n') + .map(l => l.trim()) + .filter(Boolean); + +const redirects = {}; +const fallbacks = []; // old paths that couldn't map exactly (closest guess used) +const missingTargets = []; // sanity: a mapping pointed at a non-existent page + +function record(oldPath, route, {fallback = false} = {}) { + // Every target must be a real route. The docs home ('') and 'api-reference' + // both resolve via their index pages, so has() already returns true for them. + if (!has(route)) missingTargets.push(`${oldPath} -> ${route}`); + redirects[oldPath] = toUrl(route); + if (fallback) fallbacks.push(`${oldPath} -> /${route}`); +} + +for (const oldPath of oldPaths) { + // 1. Specials. + if (oldPath === '/' || oldPath === '/search') { + record(oldPath, ''); + continue; + } + // 2. Prose pages (must run before generic /api handling). + if (PROSE[oldPath]) { + record(oldPath, PROSE[oldPath]); + continue; + } + // 3. Category landing pages. + if (CATEGORY[oldPath]) { + record(oldPath, CATEGORY[oldPath]); + continue; + } + // 3.5 Identity: pages that kept their path (old URL == a real new route). + // Covers Docusaurus pages that used the default folder path instead of a + // custom slug (e.g. /get-help/discord, /upgrading/*). + if (has(oldPath.slice(1))) { + record(oldPath, oldPath.slice(1)); + continue; + } + // 4. TypeDoc API pages. + if (oldPath.startsWith('/api/')) { + const parts = oldPath.slice(1).split('/'); // ['api', pkg, mod?, symbol?] + const [, pkg, mod, symbol] = parts; + if (parts.length === 2) { + // /api/ package index + record( + oldPath, + has(`api-reference/${pkg}`) ? `api-reference/${pkg}` : '', + { + fallback: !has(`api-reference/${pkg}`), + }, + ); + } else if (parts.length === 3) { + // /api// module index + if (has(`api-reference/${pkg}/${mod}`)) { + record(oldPath, `api-reference/${pkg}/${mod}`); + } else if (has(`api-reference/${pkg}`)) { + record(oldPath, `api-reference/${pkg}`, {fallback: true}); + } else { + record(oldPath, '', {fallback: true}); + } + } else { + // /api/// + const resolved = resolveApiSymbol(pkg, mod, symbol); + if (resolved) { + record(oldPath, resolved); + } else if (has(`api-reference/${pkg}/${mod}`)) { + record(oldPath, `api-reference/${pkg}/${mod}`, {fallback: true}); + } else if (has(`api-reference/${pkg}`)) { + record(oldPath, `api-reference/${pkg}`, {fallback: true}); + } else { + record(oldPath, '', {fallback: true}); + } + } + continue; + } + // 5. Anything else: send to the docs home (closest guess). + record(oldPath, '', {fallback: true}); +} + +// Emit sorted for stable diffs. +const sorted = Object.fromEntries( + Object.keys(redirects) + .sort() + .map(k => [k, redirects[k]]), +); +writeFileSync(OUT, JSON.stringify(sorted, null, 2) + '\n'); + +// Report. +console.log(`old URLs: ${oldPaths.length}`); +console.log(`redirects: ${Object.keys(redirects).length}`); +console.log( + `exact: ${Object.keys(redirects).length - fallbacks.length}`, +); +console.log(`closest-guess: ${fallbacks.length}`); +if (fallbacks.length) { + console.log('\nclosest-guess redirects (no exact new page):'); + for (const f of fallbacks) console.log(` ${f}`); +} +if (missingTargets.length) { + console.error('\nERROR: mappings point at non-existent pages:'); + for (const m of missingTargets) console.error(` ${m}`); + process.exit(1); +} +console.log(`\nwrote ${relative(join(here, '..', '..', '..'), OUT)}`); diff --git a/packages/docs-redirect/src/redirects.json b/packages/docs-redirect/src/redirects.json new file mode 100644 index 00000000..359e06c4 --- /dev/null +++ b/packages/docs-redirect/src/redirects.json @@ -0,0 +1,288 @@ +{ + "/": "https://midrender.com/revideo/docs", + "/3d-animations-with-threejs": "https://midrender.com/revideo/docs/guide/designing-animations/3d-animations-with-threejs", + "/api/2d": "https://midrender.com/revideo/docs/api-reference/2d", + "/api/2d/code": "https://midrender.com/revideo/docs/api-reference/2d/code", + "/api/2d/code/CodeFragment": "https://midrender.com/revideo/docs/api-reference/2d/code/interfaces/CodeFragment", + "/api/2d/code/CodeFragmentDrawingInfo": "https://midrender.com/revideo/docs/api-reference/2d/code/interfaces/CodeFragmentDrawingInfo", + "/api/2d/code/CodeHighlighter": "https://midrender.com/revideo/docs/api-reference/2d/code/interfaces/CodeHighlighter", + "/api/2d/code/CodeScope": "https://midrender.com/revideo/docs/api-reference/2d/code/interfaces/CodeScope", + "/api/2d/code/CodeSignalContext": "https://midrender.com/revideo/docs/api-reference/2d/code/classes/CodeSignalContext", + "/api/2d/code/CodeSignalHelpers": "https://midrender.com/revideo/docs/api-reference/2d/code/interfaces/CodeSignalHelpers", + "/api/2d/code/HighlightResult": "https://midrender.com/revideo/docs/api-reference/2d/code/interfaces/HighlightResult", + "/api/2d/code/LezerHighlighter": "https://midrender.com/revideo/docs/api-reference/2d/code/classes/LezerHighlighter", + "/api/2d/code/RawCodeFragment": "https://midrender.com/revideo/docs/api-reference/2d/code/interfaces/RawCodeFragment", + "/api/2d/components": "https://midrender.com/revideo/docs/api-reference/2d/components", + "/api/2d/components/Audio": "https://midrender.com/revideo/docs/api-reference/2d/components/classes/Audio", + "/api/2d/components/Bezier": "https://midrender.com/revideo/docs/api-reference/2d/components/classes/Bezier", + "/api/2d/components/BezierOverlayInfo": "https://midrender.com/revideo/docs/api-reference/2d/components/interfaces/BezierOverlayInfo", + "/api/2d/components/Circle": "https://midrender.com/revideo/docs/api-reference/2d/components/classes/Circle", + "/api/2d/components/CircleProps": "https://midrender.com/revideo/docs/api-reference/2d/components/interfaces/CircleProps", + "/api/2d/components/Code": "https://midrender.com/revideo/docs/api-reference/2d/components/classes/Code", + "/api/2d/components/CodeProps": "https://midrender.com/revideo/docs/api-reference/2d/components/interfaces/CodeProps", + "/api/2d/components/CubicBezier": "https://midrender.com/revideo/docs/api-reference/2d/components/classes/CubicBezier", + "/api/2d/components/CubicBezierProps": "https://midrender.com/revideo/docs/api-reference/2d/components/interfaces/CubicBezierProps", + "/api/2d/components/Curve": "https://midrender.com/revideo/docs/api-reference/2d/components/classes/Curve", + "/api/2d/components/CurveProps": "https://midrender.com/revideo/docs/api-reference/2d/components/interfaces/CurveProps", + "/api/2d/components/DrawHooks": "https://midrender.com/revideo/docs/api-reference/2d/components/interfaces/DrawHooks", + "/api/2d/components/DrawTokenHook": "https://midrender.com/revideo/docs/api-reference/2d/components/interfaces/DrawTokenHook", + "/api/2d/components/FunctionComponent": "https://midrender.com/revideo/docs/api-reference/2d/components/interfaces/FunctionComponent", + "/api/2d/components/Grid": "https://midrender.com/revideo/docs/api-reference/2d/components/classes/Grid", + "/api/2d/components/GridProps": "https://midrender.com/revideo/docs/api-reference/2d/components/interfaces/GridProps", + "/api/2d/components/Icon": "https://midrender.com/revideo/docs/api-reference/2d/components/classes/Icon", + "/api/2d/components/IconProps": "https://midrender.com/revideo/docs/api-reference/2d/components/interfaces/IconProps", + "/api/2d/components/Img": "https://midrender.com/revideo/docs/api-reference/2d/components/classes/Img", + "/api/2d/components/ImgProps": "https://midrender.com/revideo/docs/api-reference/2d/components/interfaces/ImgProps", + "/api/2d/components/JSXProps": "https://midrender.com/revideo/docs/api-reference/2d/components/interfaces/JSXProps", + "/api/2d/components/Knot": "https://midrender.com/revideo/docs/api-reference/2d/components/classes/Knot", + "/api/2d/components/KnotProps": "https://midrender.com/revideo/docs/api-reference/2d/components/interfaces/KnotProps", + "/api/2d/components/Latex": "https://midrender.com/revideo/docs/api-reference/2d/components/classes/Latex", + "/api/2d/components/LatexProps": "https://midrender.com/revideo/docs/api-reference/2d/components/interfaces/LatexProps", + "/api/2d/components/Layout": "https://midrender.com/revideo/docs/api-reference/2d/components/classes/Layout", + "/api/2d/components/LayoutProps": "https://midrender.com/revideo/docs/api-reference/2d/components/interfaces/LayoutProps", + "/api/2d/components/Line": "https://midrender.com/revideo/docs/api-reference/2d/components/classes/Line", + "/api/2d/components/LineProps": "https://midrender.com/revideo/docs/api-reference/2d/components/interfaces/LineProps", + "/api/2d/components/Media": "https://midrender.com/revideo/docs/api-reference/2d/components/classes/Media", + "/api/2d/components/MediaProps": "https://midrender.com/revideo/docs/api-reference/2d/components/interfaces/MediaProps", + "/api/2d/components/Node": "https://midrender.com/revideo/docs/api-reference/2d/components/classes/Node", + "/api/2d/components/NodeConstructor": "https://midrender.com/revideo/docs/api-reference/2d/components/interfaces/NodeConstructor", + "/api/2d/components/NodeProps": "https://midrender.com/revideo/docs/api-reference/2d/components/interfaces/NodeProps", + "/api/2d/components/Path": "https://midrender.com/revideo/docs/api-reference/2d/components/classes/Path", + "/api/2d/components/PathProps": "https://midrender.com/revideo/docs/api-reference/2d/components/interfaces/PathProps", + "/api/2d/components/Polygon": "https://midrender.com/revideo/docs/api-reference/2d/components/classes/Polygon", + "/api/2d/components/PolygonProps": "https://midrender.com/revideo/docs/api-reference/2d/components/interfaces/PolygonProps", + "/api/2d/components/QuadBezier": "https://midrender.com/revideo/docs/api-reference/2d/components/classes/QuadBezier", + "/api/2d/components/QuadBezierProps": "https://midrender.com/revideo/docs/api-reference/2d/components/interfaces/QuadBezierProps", + "/api/2d/components/Ray": "https://midrender.com/revideo/docs/api-reference/2d/components/classes/Ray", + "/api/2d/components/RayProps": "https://midrender.com/revideo/docs/api-reference/2d/components/interfaces/RayProps", + "/api/2d/components/Rect": "https://midrender.com/revideo/docs/api-reference/2d/components/classes/Rect", + "/api/2d/components/RectProps": "https://midrender.com/revideo/docs/api-reference/2d/components/interfaces/RectProps", + "/api/2d/components/Rive": "https://midrender.com/revideo/docs/api-reference/2d/components/classes/Rive", + "/api/2d/components/RiveProps": "https://midrender.com/revideo/docs/api-reference/2d/components/interfaces/RiveProps", + "/api/2d/components/SVG": "https://midrender.com/revideo/docs/api-reference/2d/components/classes/SVG", + "/api/2d/components/SVGDocument": "https://midrender.com/revideo/docs/api-reference/2d/components/interfaces/SVGDocument", + "/api/2d/components/SVGDocumentData": "https://midrender.com/revideo/docs/api-reference/2d/components/interfaces/SVGDocumentData", + "/api/2d/components/SVGProps": "https://midrender.com/revideo/docs/api-reference/2d/components/interfaces/SVGProps", + "/api/2d/components/SVGShape": "https://midrender.com/revideo/docs/api-reference/2d/components/interfaces/SVGShape", + "/api/2d/components/SVGShapeData": "https://midrender.com/revideo/docs/api-reference/2d/components/interfaces/SVGShapeData", + "/api/2d/components/Shape": "https://midrender.com/revideo/docs/api-reference/2d/components/classes/Shape", + "/api/2d/components/ShapeProps": "https://midrender.com/revideo/docs/api-reference/2d/components/interfaces/ShapeProps", + "/api/2d/components/Spline": "https://midrender.com/revideo/docs/api-reference/2d/components/classes/Spline", + "/api/2d/components/SplineProps": "https://midrender.com/revideo/docs/api-reference/2d/components/interfaces/SplineProps", + "/api/2d/components/Txt": "https://midrender.com/revideo/docs/api-reference/2d/components/classes/Txt", + "/api/2d/components/TxtProps": "https://midrender.com/revideo/docs/api-reference/2d/components/interfaces/TxtProps", + "/api/2d/components/Video": "https://midrender.com/revideo/docs/api-reference/2d/components/classes/Video", + "/api/2d/components/VideoProps": "https://midrender.com/revideo/docs/api-reference/2d/components/interfaces/VideoProps", + "/api/2d/components/View2D": "https://midrender.com/revideo/docs/api-reference/2d/components/classes/View2D", + "/api/2d/components/View2DProps": "https://midrender.com/revideo/docs/api-reference/2d/components/interfaces/View2DProps", + "/api/2d/curves": "https://midrender.com/revideo/docs/api-reference/2d/curves", + "/api/2d/curves/CircleSegment": "https://midrender.com/revideo/docs/api-reference/2d/curves/classes/CircleSegment", + "/api/2d/curves/CubicBezierSegment": "https://midrender.com/revideo/docs/api-reference/2d/curves/classes/CubicBezierSegment", + "/api/2d/curves/CurveDrawingInfo": "https://midrender.com/revideo/docs/api-reference/2d/curves/interfaces/CurveDrawingInfo", + "/api/2d/curves/CurvePoint": "https://midrender.com/revideo/docs/api-reference/2d/curves/interfaces/CurvePoint", + "/api/2d/curves/CurveProfile": "https://midrender.com/revideo/docs/api-reference/2d/curves/interfaces/CurveProfile", + "/api/2d/curves/KnotInfo": "https://midrender.com/revideo/docs/api-reference/2d/curves/interfaces/KnotInfo", + "/api/2d/curves/LineSegment": "https://midrender.com/revideo/docs/api-reference/2d/curves/classes/LineSegment", + "/api/2d/curves/Polynomial": "https://midrender.com/revideo/docs/api-reference/2d/curves/classes/Polynomial", + "/api/2d/curves/Polynomial2D": "https://midrender.com/revideo/docs/api-reference/2d/curves/classes/Polynomial2D", + "/api/2d/curves/QuadBezierSegment": "https://midrender.com/revideo/docs/api-reference/2d/curves/classes/QuadBezierSegment", + "/api/2d/curves/Segment": "https://midrender.com/revideo/docs/api-reference/2d/curves/classes/Segment", + "/api/2d/decorators": "https://midrender.com/revideo/docs/api-reference/2d/decorators", + "/api/2d/decorators/FiltersSignalContext": "https://midrender.com/revideo/docs/api-reference/2d/decorators/classes/FiltersSignalContext", + "/api/2d/decorators/PropertyMetadata": "https://midrender.com/revideo/docs/api-reference/2d/decorators/interfaces/PropertyMetadata", + "/api/2d/partials": "https://midrender.com/revideo/docs/api-reference/2d/partials", + "/api/2d/partials/Filter": "https://midrender.com/revideo/docs/api-reference/2d/partials/classes/Filter", + "/api/2d/partials/FilterProps": "https://midrender.com/revideo/docs/api-reference/2d/partials/interfaces/FilterProps", + "/api/2d/partials/Gradient": "https://midrender.com/revideo/docs/api-reference/2d/partials/classes/Gradient", + "/api/2d/partials/GradientProps": "https://midrender.com/revideo/docs/api-reference/2d/partials/interfaces/GradientProps", + "/api/2d/partials/GradientStop": "https://midrender.com/revideo/docs/api-reference/2d/partials/interfaces/GradientStop", + "/api/2d/partials/Pattern": "https://midrender.com/revideo/docs/api-reference/2d/partials/classes/Pattern", + "/api/2d/partials/PatternProps": "https://midrender.com/revideo/docs/api-reference/2d/partials/interfaces/PatternProps", + "/api/2d/scenes": "https://midrender.com/revideo/docs/api-reference/2d/scenes", + "/api/2d/scenes/Scene2D": "https://midrender.com/revideo/docs/api-reference/2d/scenes/classes/Scene2D", + "/api/2d/utils": "https://midrender.com/revideo/docs/api-reference/2d/utils", + "/api/core": "https://midrender.com/revideo/docs/api-reference/core", + "/api/core/app": "https://midrender.com/revideo/docs/api-reference/core/app", + "/api/core/app/AssetInfo": "https://midrender.com/revideo/docs/api-reference/core/app/interfaces/AssetInfo", + "/api/core/app/LogLevel": "https://midrender.com/revideo/docs/api-reference/core/app/enumerations/LogLevel", + "/api/core/app/LogPayload": "https://midrender.com/revideo/docs/api-reference/core/app/interfaces/LogPayload", + "/api/core/app/Logger": "https://midrender.com/revideo/docs/api-reference/core/app/classes/Logger", + "/api/core/app/PlaybackState": "https://midrender.com/revideo/docs/api-reference/core/app/enumerations/PlaybackState", + "/api/core/app/PlaybackStatus": "https://midrender.com/revideo/docs/api-reference/core/app/classes/PlaybackStatus", + "/api/core/app/Player": "https://midrender.com/revideo/docs/api-reference/core/app/classes/Player", + "/api/core/app/PlayerSettings": "https://midrender.com/revideo/docs/api-reference/core/app/interfaces/PlayerSettings", + "/api/core/app/PlayerState": "https://midrender.com/revideo/docs/api-reference/core/app/interfaces/PlayerState", + "/api/core/app/Presenter": "https://midrender.com/revideo/docs/api-reference/core/app", + "/api/core/app/PresenterInfo": "https://midrender.com/revideo/docs/api-reference/core/app", + "/api/core/app/PresenterSettings": "https://midrender.com/revideo/docs/api-reference/core/app", + "/api/core/app/PresenterState": "https://midrender.com/revideo/docs/api-reference/core/app", + "/api/core/app/Project": "https://midrender.com/revideo/docs/api-reference/core/app/interfaces/Project", + "/api/core/app/ProjectMetadata": "https://midrender.com/revideo/docs/api-reference/core/app", + "/api/core/app/ProjectSettings": "https://midrender.com/revideo/docs/api-reference/core/app/interfaces/ProjectSettings", + "/api/core/app/Renderer": "https://midrender.com/revideo/docs/api-reference/core/app/classes/Renderer", + "/api/core/app/RendererResult": "https://midrender.com/revideo/docs/api-reference/core/app/enumerations/RendererResult", + "/api/core/app/RendererSettings": "https://midrender.com/revideo/docs/api-reference/core/app/interfaces/RendererSettings", + "/api/core/app/RendererState": "https://midrender.com/revideo/docs/api-reference/core/app/enumerations/RendererState", + "/api/core/app/SharedWebGLContext": "https://midrender.com/revideo/docs/api-reference/core/app/classes/SharedWebGLContext", + "/api/core/app/Stage": "https://midrender.com/revideo/docs/api-reference/core/app/classes/Stage", + "/api/core/app/StageSettings": "https://midrender.com/revideo/docs/api-reference/core/app/interfaces/StageSettings", + "/api/core/app/Versions": "https://midrender.com/revideo/docs/api-reference/core/app/interfaces/Versions", + "/api/core/decorators": "https://midrender.com/revideo/docs/api-reference/core/decorators", + "/api/core/events": "https://midrender.com/revideo/docs/api-reference/core/events", + "/api/core/events/AsyncEventDispatcher": "https://midrender.com/revideo/docs/api-reference/core/events/classes/AsyncEventDispatcher", + "/api/core/events/AsyncEventHandler": "https://midrender.com/revideo/docs/api-reference/core/events/interfaces/AsyncEventHandler", + "/api/core/events/EventDispatcher": "https://midrender.com/revideo/docs/api-reference/core/events/classes/EventDispatcher", + "/api/core/events/EventDispatcherBase": "https://midrender.com/revideo/docs/api-reference/core/events/classes/EventDispatcherBase", + "/api/core/events/EventHandler": "https://midrender.com/revideo/docs/api-reference/core/events/interfaces/EventHandler", + "/api/core/events/FlagDispatcher": "https://midrender.com/revideo/docs/api-reference/core/events/classes/FlagDispatcher", + "/api/core/events/Subscribable": "https://midrender.com/revideo/docs/api-reference/core/events/classes/Subscribable", + "/api/core/events/SubscribableValueEvent": "https://midrender.com/revideo/docs/api-reference/core/events/classes/SubscribableValueEvent", + "/api/core/events/ValueDispatcher": "https://midrender.com/revideo/docs/api-reference/core/events/classes/ValueDispatcher", + "/api/core/exporter": "https://midrender.com/revideo/docs/api-reference/core/exporter", + "/api/core/exporter/Exporter": "https://midrender.com/revideo/docs/api-reference/core/exporter/interfaces/Exporter", + "/api/core/exporter/ExporterClass": "https://midrender.com/revideo/docs/api-reference/core/exporter/interfaces/ExporterClass", + "/api/core/exporter/FFmpegExporterClient": "https://midrender.com/revideo/docs/api-reference/core/exporter/classes/FFmpegExporterClient", + "/api/core/exporter/WasmExporter": "https://midrender.com/revideo/docs/api-reference/core/exporter/classes/WasmExporter", + "/api/core/flow": "https://midrender.com/revideo/docs/api-reference/core/flow", + "/api/core/flow/EveryCallback": "https://midrender.com/revideo/docs/api-reference/core/flow/interfaces/EveryCallback", + "/api/core/flow/EveryTimer": "https://midrender.com/revideo/docs/api-reference/core/flow/interfaces/EveryTimer", + "/api/core/flow/LoopCallback": "https://midrender.com/revideo/docs/api-reference/core/flow/interfaces/LoopCallback", + "/api/core/media": "https://midrender.com/revideo/docs/api-reference/core/media", + "/api/core/media/AudioData": "https://midrender.com/revideo/docs/api-reference/core/media", + "/api/core/media/AudioManager": "https://midrender.com/revideo/docs/api-reference/core/media", + "/api/core/meta": "https://midrender.com/revideo/docs/api-reference/core", + "/api/core/meta/BoolMetaField": "https://midrender.com/revideo/docs/api-reference/core", + "/api/core/meta/ColorMetaField": "https://midrender.com/revideo/docs/api-reference/core", + "/api/core/meta/EnumMetaField": "https://midrender.com/revideo/docs/api-reference/core", + "/api/core/meta/ExporterMetaField": "https://midrender.com/revideo/docs/api-reference/core", + "/api/core/meta/MetaField": "https://midrender.com/revideo/docs/api-reference/core", + "/api/core/meta/MetaOption": "https://midrender.com/revideo/docs/api-reference/core", + "/api/core/meta/NumberMetaField": "https://midrender.com/revideo/docs/api-reference/core", + "/api/core/meta/RangeMetaField": "https://midrender.com/revideo/docs/api-reference/core", + "/api/core/meta/StringMetaField": "https://midrender.com/revideo/docs/api-reference/core", + "/api/core/meta/Vector2MetaField": "https://midrender.com/revideo/docs/api-reference/core", + "/api/core/plugin": "https://midrender.com/revideo/docs/api-reference/core/plugin", + "/api/core/plugin/Plugin": "https://midrender.com/revideo/docs/api-reference/core/plugin/interfaces/Plugin", + "/api/core/scenes": "https://midrender.com/revideo/docs/api-reference/core/scenes", + "/api/core/scenes/CachedSceneData": "https://midrender.com/revideo/docs/api-reference/core/scenes/interfaces/CachedSceneData", + "/api/core/scenes/FullSceneDescription": "https://midrender.com/revideo/docs/api-reference/core/scenes/interfaces/FullSceneDescription", + "/api/core/scenes/GeneratorScene": "https://midrender.com/revideo/docs/api-reference/core/scenes/classes/GeneratorScene", + "/api/core/scenes/Inspectable": "https://midrender.com/revideo/docs/api-reference/core/scenes/interfaces/Inspectable", + "/api/core/scenes/LifecycleEvents": "https://midrender.com/revideo/docs/api-reference/core/scenes/classes/LifecycleEvents", + "/api/core/scenes/Random": "https://midrender.com/revideo/docs/api-reference/core/scenes/classes/Random", + "/api/core/scenes/Scene": "https://midrender.com/revideo/docs/api-reference/core/scenes/interfaces/Scene", + "/api/core/scenes/SceneConstructor": "https://midrender.com/revideo/docs/api-reference/core/scenes/interfaces/SceneConstructor", + "/api/core/scenes/SceneDescription": "https://midrender.com/revideo/docs/api-reference/core/scenes/interfaces/SceneDescription", + "/api/core/scenes/SceneDescriptionReload": "https://midrender.com/revideo/docs/api-reference/core/scenes/interfaces/SceneDescriptionReload", + "/api/core/scenes/SceneRenderEvent": "https://midrender.com/revideo/docs/api-reference/core/scenes/enumerations/SceneRenderEvent", + "/api/core/scenes/SceneState": "https://midrender.com/revideo/docs/api-reference/core/scenes/enumerations/SceneState", + "/api/core/scenes/Slide": "https://midrender.com/revideo/docs/api-reference/core/scenes/interfaces/Slide", + "/api/core/scenes/Slides": "https://midrender.com/revideo/docs/api-reference/core/scenes/classes/Slides", + "/api/core/scenes/ThreadGeneratorFactory": "https://midrender.com/revideo/docs/api-reference/core/scenes/interfaces/ThreadGeneratorFactory", + "/api/core/scenes/Threadable": "https://midrender.com/revideo/docs/api-reference/core/scenes/interfaces/Threadable", + "/api/core/scenes/Variables": "https://midrender.com/revideo/docs/api-reference/core/scenes/classes/Variables", + "/api/core/signals": "https://midrender.com/revideo/docs/api-reference/core/signals", + "/api/core/signals/CompoundSignalContext": "https://midrender.com/revideo/docs/api-reference/core/signals/classes/CompoundSignalContext", + "/api/core/signals/Computed": "https://midrender.com/revideo/docs/api-reference/core/signals/interfaces/Computed", + "/api/core/signals/ComputedContext": "https://midrender.com/revideo/docs/api-reference/core/signals/classes/ComputedContext", + "/api/core/signals/DependencyContext": "https://midrender.com/revideo/docs/api-reference/core/signals/classes/DependencyContext", + "/api/core/signals/PromiseHandle": "https://midrender.com/revideo/docs/api-reference/core/signals/interfaces/PromiseHandle", + "/api/core/signals/Signal": "https://midrender.com/revideo/docs/api-reference/core/signals/interfaces/Signal", + "/api/core/signals/SignalContext": "https://midrender.com/revideo/docs/api-reference/core/signals/classes/SignalContext", + "/api/core/signals/SignalExtensions": "https://midrender.com/revideo/docs/api-reference/core/signals/interfaces/SignalExtensions", + "/api/core/signals/SignalGetter": "https://midrender.com/revideo/docs/api-reference/core/signals/interfaces/SignalGetter", + "/api/core/signals/SignalSetter": "https://midrender.com/revideo/docs/api-reference/core/signals/interfaces/SignalSetter", + "/api/core/signals/SignalTween": "https://midrender.com/revideo/docs/api-reference/core/signals/interfaces/SignalTween", + "/api/core/threading": "https://midrender.com/revideo/docs/api-reference/core/threading", + "/api/core/threading/Promisable": "https://midrender.com/revideo/docs/api-reference/core/threading/interfaces/Promisable", + "/api/core/threading/Thread": "https://midrender.com/revideo/docs/api-reference/core/threading/classes/Thread", + "/api/core/threading/ThreadsCallback": "https://midrender.com/revideo/docs/api-reference/core/threading/interfaces/ThreadsCallback", + "/api/core/threading/ThreadsFactory": "https://midrender.com/revideo/docs/api-reference/core/threading/interfaces/ThreadsFactory", + "/api/core/transitions": "https://midrender.com/revideo/docs/api-reference/core/transitions", + "/api/core/tweening": "https://midrender.com/revideo/docs/api-reference/core/tweening", + "/api/core/tweening/InterpolationFunction": "https://midrender.com/revideo/docs/api-reference/core/tweening/interfaces/InterpolationFunction", + "/api/core/tweening/Spring": "https://midrender.com/revideo/docs/api-reference/core/tweening/interfaces/Spring", + "/api/core/tweening/TimingFunction": "https://midrender.com/revideo/docs/api-reference/core/tweening/interfaces/TimingFunction", + "/api/core/types": "https://midrender.com/revideo/docs/api-reference/core/types", + "/api/core/types/BBox": "https://midrender.com/revideo/docs/api-reference/core/types/classes/BBox", + "/api/core/types/Center": "https://midrender.com/revideo/docs/api-reference/core/types/enumerations/Center", + "/api/core/types/Direction": "https://midrender.com/revideo/docs/api-reference/core/types/enumerations/Direction", + "/api/core/types/Matrix2D": "https://midrender.com/revideo/docs/api-reference/core/types/classes/Matrix2D", + "/api/core/types/Origin": "https://midrender.com/revideo/docs/api-reference/core/types/enumerations/Origin", + "/api/core/types/Spacing": "https://midrender.com/revideo/docs/api-reference/core/types/classes/Spacing", + "/api/core/types/Type": "https://midrender.com/revideo/docs/api-reference/core/types/interfaces/Type", + "/api/core/types/Vector2": "https://midrender.com/revideo/docs/api-reference/core/types/classes/Vector2", + "/api/core/types/WebGLConvertible": "https://midrender.com/revideo/docs/api-reference/core/types/interfaces/WebGLConvertible", + "/api/core/utils": "https://midrender.com/revideo/docs/api-reference/core/utils", + "/api/core/utils/DetailedError": "https://midrender.com/revideo/docs/api-reference/core/utils/classes/DetailedError", + "/api/core/utils/ExperimentalError": "https://midrender.com/revideo/docs/api-reference/core/utils/classes/ExperimentalError", + "/api/core/utils/Reference": "https://midrender.com/revideo/docs/api-reference/core/utils/interfaces/Reference", + "/api/core/utils/ReferenceReceiver": "https://midrender.com/revideo/docs/api-reference/core/utils/interfaces/ReferenceReceiver", + "/api/player-react/player": "https://midrender.com/revideo/docs/api-reference/player-react/player", + "/api/renderer/renderVideo": "https://midrender.com/revideo/docs/api-reference/renderer/renderVideo", + "/category/api-reference": "https://midrender.com/revideo/docs/api-reference", + "/category/building-web-apps": "https://midrender.com/revideo/docs/guide/building-webapps/rendering-endpoint", + "/category/code-snippets": "https://midrender.com/revideo/docs/code-snippets/moving-objects", + "/category/common-issues": "https://midrender.com/revideo/docs/common-issues/ffmpeg", + "/category/designing-videos": "https://midrender.com/revideo/docs/guide/designing-animations/designing-animations", + "/category/get-help": "https://midrender.com/revideo/docs/get-help/discord", + "/category/motion-canvas-guide": "https://midrender.com/revideo/docs/animations/flow", + "/category/performance-considerations": "https://midrender.com/revideo/docs/guide/performance/decoders", + "/category/platform": "https://midrender.com/revideo/docs/platform/introduction", + "/category/revideoffmpeg": "https://midrender.com/revideo/docs/api-reference/revideo-ffmpeg/concatenate-media", + "/category/revideoplayer-react": "https://midrender.com/revideo/docs/api-reference/player-react/player", + "/category/revideorenderer": "https://midrender.com/revideo/docs/api-reference/renderer/renderVideo", + "/category/upgrading": "https://midrender.com/revideo/docs/upgrading/from_0.2.x_to_0.3", + "/category/usage-guide": "https://midrender.com/revideo/docs/guide/installation-and-setup", + "/changing-object-size-over-time-with-signals": "https://midrender.com/revideo/docs/code-snippets/changing-object-size-over-time-with-signals", + "/code-animations": "https://midrender.com/revideo/docs/animations/code-animations", + "/common-issues/ffmpeg": "https://midrender.com/revideo/docs/common-issues/ffmpeg", + "/common-issues/slow-rendering": "https://midrender.com/revideo/docs/common-issues/slow-rendering", + "/configuration": "https://midrender.com/revideo/docs/animations/configuration", + "/custom-components": "https://midrender.com/revideo/docs/animations/custom-components", + "/custom-font": "https://midrender.com/revideo/docs/animations/custom-font", + "/decoders": "https://midrender.com/revideo/docs/guide/performance/decoders", + "/designing-animations": "https://midrender.com/revideo/docs/guide/designing-animations/designing-animations", + "/emojis": "https://midrender.com/revideo/docs/guide/designing-animations/emojis", + "/experimental": "https://midrender.com/revideo/docs/animations/experimental", + "/ffmpeg/concatenateMedia": "https://midrender.com/revideo/docs/api-reference/revideo-ffmpeg/concatenate-media", + "/ffmpeg/mergeAudioWithVideo": "https://midrender.com/revideo/docs/api-reference/revideo-ffmpeg/merge-audio-with-video", + "/filters-and-effects": "https://midrender.com/revideo/docs/animations/filters-and-effects", + "/flow": "https://midrender.com/revideo/docs/animations/flow", + "/get-help/discord": "https://midrender.com/revideo/docs/get-help/discord", + "/hierarchy": "https://midrender.com/revideo/docs/animations/hierarchy", + "/hls-video": "https://midrender.com/revideo/docs/code-snippets/hls-video", + "/installation-and-setup": "https://midrender.com/revideo/docs/guide/installation-and-setup", + "/layouts": "https://midrender.com/revideo/docs/animations/layouts", + "/logging": "https://midrender.com/revideo/docs/animations/logging", + "/logical-separation": "https://midrender.com/revideo/docs/guide/designing-animations/logical-separation", + "/moving-manipulating-objects": "https://midrender.com/revideo/docs/code-snippets/moving-objects", + "/node-parent-reloading": "https://midrender.com/revideo/docs/guide/performance/node-parent-reloading", + "/parameterized-video": "https://midrender.com/revideo/docs/guide/parameterized-video", + "/platform/introduction": "https://midrender.com/revideo/docs/platform/introduction", + "/platform/render-endpoint": "https://midrender.com/revideo/docs/platform/render-endpoint", + "/positioning": "https://midrender.com/revideo/docs/animations/positioning", + "/preview-with-player": "https://midrender.com/revideo/docs/guide/building-webapps/using-the-player", + "/project-structure": "https://midrender.com/revideo/docs/guide/project-structure", + "/project-variables": "https://midrender.com/revideo/docs/animations/project-variables", + "/random-values": "https://midrender.com/revideo/docs/animations/random", + "/references": "https://midrender.com/revideo/docs/animations/references", + "/render-endpoint": "https://midrender.com/revideo/docs/guide/building-webapps/rendering-endpoint", + "/renderer/renderPartialVideo": "https://midrender.com/revideo/docs/api-reference/renderer/renderPartialVideo", + "/rendering-in-production": "https://midrender.com/revideo/docs/guide/building-webapps/deploy-rendering-service", + "/rendering-videos": "https://midrender.com/revideo/docs/guide/rendering-videos", + "/rive-animations": "https://midrender.com/revideo/docs/guide/designing-animations/rive-animations", + "/saas-template": "https://midrender.com/revideo/docs/guide/building-webapps/saas-template", + "/search": "https://midrender.com/revideo/docs", + "/shaders": "https://midrender.com/revideo/docs/animations/shaders", + "/signals": "https://midrender.com/revideo/docs/animations/signals", + "/spawners": "https://midrender.com/revideo/docs/animations/spawners", + "/streaming-text": "https://midrender.com/revideo/docs/code-snippets/streaming-text", + "/time-events": "https://midrender.com/revideo/docs/animations/time-events", + "/transitions": "https://midrender.com/revideo/docs/animations/transitions", + "/transparent-video": "https://midrender.com/revideo/docs/code-snippets/transparent-video", + "/tweening": "https://midrender.com/revideo/docs/animations/tweening", + "/understanding-scene-flow": "https://midrender.com/revideo/docs/guide/understanding-scene-flow", + "/upgrading/from_0.2.x_to_0.3": "https://midrender.com/revideo/docs/upgrading/from_0.2.x_to_0.3", + "/upgrading/from_0.3.x_to_0.4": "https://midrender.com/revideo/docs/upgrading/from_0.3.x_to_0.4" +} diff --git a/packages/docs-redirect/src/server.mjs b/packages/docs-redirect/src/server.mjs new file mode 100644 index 00000000..1c98370f --- /dev/null +++ b/packages/docs-redirect/src/server.mjs @@ -0,0 +1,67 @@ +// Redirect server for the old docs.re.video site. Every request is looked up in +// redirects.json (generated by build-map.mjs) and answered with a 301 to the +// matching page under https://midrender.com/revideo/docs. Unknown paths fall +// back to the docs home. +// +// Zero dependencies: runs the same under `node src/server.mjs` or +// `bun src/server.mjs`. + +import {readFileSync} from 'node:fs'; +import {createServer} from 'node:http'; +import {dirname, join} from 'node:path'; +import {fileURLToPath} from 'node:url'; + +const here = dirname(fileURLToPath(import.meta.url)); +const redirects = JSON.parse( + readFileSync(join(here, 'redirects.json'), 'utf8'), +); + +const PORT = process.env.PORT || 8080; +const HOME = 'https://midrender.com/revideo/docs'; + +/** Normalize an incoming path so it matches the map keys (no trailing slash). */ +function normalize(pathname) { + let p = pathname; + try { + p = decodeURIComponent(pathname); + } catch { + // Malformed percent-encoding: fall back to the raw value. + } + if (p.length > 1 && p.endsWith('/')) p = p.replace(/\/+$/, ''); + return p || '/'; +} + +const server = createServer((req, res) => { + const url = new URL(req.url, 'http://localhost'); + const path = normalize(url.pathname); + + // Health check for load balancers / uptime probes. + if (path === '/healthz') { + res.writeHead(200, {'content-type': 'text/plain'}); + res.end('ok'); + return; + } + + const exact = redirects[path]; + if (exact) { + // Preserve the query string; the fragment (#...) is never sent to the server + // and is retained by the browser automatically. + res.writeHead(301, { + location: exact + url.search, + 'cache-control': 'public, max-age=86400', + }); + res.end(); + return; + } + + // Unknown path: closest guess is the docs home. Use 302 so we don't + // permanently cache a guess for a URL we didn't explicitly map. + res.writeHead(302, {location: HOME + url.search}); + res.end(); +}); + +server.listen(PORT, () => { + console.log( + `docs-redirect listening on :${PORT} (${Object.keys(redirects).length} routes)`, + ); +});