cvm
Internal course video authoring tool for publishing metadata, descriptions, thumbnails, and social posts.
Overview
cvm (Course Video Manager) is an internal authoring tool for the course
video publishing workflow — metadata, descriptions, thumbnails, and social
posting. It is vendored from an upstream project
(mattpocock/course-video-manager, snapshot SHA tracked in
.upstream-sha) and ported from React Router v7 to Next.js. It's used by
the course-production team, not end users.
Vendored components that haven't been refactored yet rely on a React Router compat shim
(app/lib/router-compat.tsx); new code should use next/navigation directly.
Stack
- Next.js 16 (App Router, Turbopack, React Compiler)
- Effect 4.0.0-beta.94 —
Context.Service<T>()(name, { make })pattern, globalThis-cachedManagedRuntimeatapp/server/runtime.ts, route handlers viarunRouteJson(app/server/route.ts) - Drizzle 1.0.0-rc.1 RQB — declarative
where: { ... }DSL viadefineRelations(@/db/schema) - Postgres + PGlite —
postgres-jsin production, in-process PGlite for tests (app/test-utils/pglite.ts) - AI SDK 6 + Anthropic, OpenAI, evalite, sandcastle
Develop
pnpm --filter cvm dev # app + stream-deck-forwarder in parallel
pnpm --filter cvm dev:next # app only, port 3010
pnpm --filter cvm build
pnpm --filter cvm typecheck
pnpm --filter cvm lintApp runs at http://localhost:3010. Copy .env.example to .env first —
DATABASE_URL and ANTHROPIC_API_KEY are the minimum needed to render past
the error page.
Test
pnpm --filter cvm test # vitest run
pnpm --filter cvm test:watch
pnpm --filter cvm test:coverage
pnpm --filter cvm test:e2e # playwrightEffect tests use @effect/vitest; integration tests compose layers with
Layer.provide (order matters — ClipOperations.layer requires
DrizzleService).
Build & deploy
pnpm --filter cvm buildDeployed on Vercel (albertonline-org-cvm) at
cvm.albertonline.org. Internal tool —
runtime needs real DATABASE_URL and API keys set as Vercel env vars.
Structure
app/api/— Next.js Route Handlers (nodejs runtime)app/courses/,app/videos/— App Router pages (force-dynamic)app/loaders/— typed loader functionsapp/features/— reducer-driven UI domains (course-view, video-editor, …)app/services/— Effect services (DB functions, clip operations, video, AI, OBS)app/server/— runtime + route-handler helpersapp/lib/router-compat.tsx— React Router compat shimdb/— Drizzle schema (cvmRelations)test-utils/— PGlite test DB harness, reducer tester
See the apps overview and the monorepo overview.