Dev Server
Editframe compositions reference local media directly, for example <ef-video src="clip.mp4">. A browser cannot decode a raw video file or fetch a local caption transcript by itself. The Editframe dev server fills that gap. It adds on-demand video transcoding, local image and caption serving, and URL signing, with no cloud API dependency.
Pick one integration, based on your framework.
- Vite (the
html/reacttemplates fromnpm create @editframe):@editframe/vite-plugin. - Next.js (the
nextjstemplate):@editframe/nextjs-plugin. - Any other toolchain (Webpack, Rspack, a custom Node.js server):
@editframe/dev-serverdirectly.
All three wrap the same implementation. A scaffolded project already includes the right one. See the editframe-create skill.
Vite Setup
// vite.config.ts
import { defineConfig } from "vite";
import { vitePluginEditframe } from "@editframe/vite-plugin";
export default defineConfig({
plugins: [
vitePluginEditframe({
root: "./src", // base directory local `src=` paths resolve against
cacheRoot: "./cache", // directory for cached transcoded assets
}),
],
});
The plugin mounts onto Vite's own dev server. Compositions make same-origin requests. No separate port and no CORS setup are necessary.
Next.js Setup
// next.config.mjs
import { withEditframe } from "@editframe/nextjs-plugin";
export default withEditframe(
{ root: "./src", cacheRoot: "./cache" },
{
// your existing Next.js config
},
);
withEditframe starts a sidecar HTTP server next to next dev, on port 3099 by default. Override the port with a port option. It also adds rewrites() rules that proxy Editframe's requests to that sidecar, so compositions still make same-origin requests. Run next dev as usual. No other setup is necessary.
The sidecar does not start in production. Deploy against real media (cloud renders, uploaded files) instead.
Framework-Agnostic Setup
For a toolchain with no Vite or Next.js integration, call @editframe/dev-server directly. Get six asset-processing functions from @editframe/assets. Provide URL-signing handlers with createProdEfHandlers.
import { createEditframeDevServer, createProdEfHandlers } from "@editframe/dev-server";
import {
generateTrack,
generateScrubTrack,
generateTrackFragmentIndex,
cacheImage,
findOrCreateCaptions,
md5FilePath,
} from "@editframe/assets";
import { Client, createURLToken } from "@editframe/api";
const server = createEditframeDevServer(
{ root: "./src", cacheRoot: "./cache" },
{ generateTrack, generateScrubTrack, generateTrackFragmentIndex, cacheImage, findOrCreateCaptions, md5FilePath },
createProdEfHandlers({
createURLToken,
getClient: () => new Client(process.env.EF_TOKEN),
}),
);
server.listen(3001, () => console.log("Editframe dev server running on http://localhost:3001"));
If your toolchain already exposes a Connect-compatible middleware stack (Express, Connect, or a custom Vite integration), call createEditframeRouter(...) with the same three arguments instead. Mount the result with .use(). createEditframeDevServer wraps that same router in its own standalone http.Server, for toolchains with no middleware stack of their own.
What It Enables
Once one integration runs, these capabilities work with no manual configuration and no direct requests from your own code.
- JIT video transcoding. Reference a local video file directly, for example
<ef-video src="clip.mp4">. The dev server transcodes it into streamable segments on first request, then serves cached segments after that. - Local image and caption serving.
<ef-image>and caption generation both work against local files with no setup. - A local files API that mirrors the shape of the production files API. Code written against local media keeps working after you switch to uploaded or cloud files.
- URL signing.
ef-configuration's defaultsigning-urlforwards to the real Editframe cloud API to mint a playback token. Set theEF_TOKENenvironment variable before you start your dev server. SetEF_HOSTto point at a non-default API host.
See the composition skill for how ef-video, ef-image, and ef-captions consume local media. See the editframe-api skill for EF_TOKEN and EF_HOST.
Visual Regression Testing (Vite Only)
This feature requires the Vitest-specific entry point. It adds server-side odiff-bin image comparison, since native binaries are not available in the browser.
// vitest.config.ts
import { defineConfig } from "vitest/config";
export default defineConfig(async () => {
const { vitePluginEditframe } = await import("@editframe/vite-plugin/src/index.vitest.js");
return {
plugins: [vitePluginEditframe({ root: "./test-assets", cacheRoot: "./test-assets" })],
test: { browser: { enabled: true, provider: "playwright" } },
};
});
Call it from your own test code, with no wrapper library:
const response = await fetch("/@ef-compare-snapshot", {
method: "POST",
body: JSON.stringify({
testName: "my-component",
snapshotName: "default-state",
dataUrl: canvas.toDataURL("image/png"),
threshold: 0.1, // color-distance threshold (odiff), lower = stricter, default 0.1
antialiasing: true, // ignore antialiasing-only differences, default true
acceptableDiffPercentage: 1.0, // max % differing pixels before failure, default 1.0
}),
});
const result = await response.json(); // { match: boolean, diffCount, diffPercentage, baselineCreated?, error? }
When no baseline exists yet, this call saves the actual image as the baseline. It returns { match: true, baselineCreated: true }. Two related endpoints cover the other cases: /@ef-compare-two-images compares two arbitrary images with no baseline, and /@ef-write-snapshot writes a baseline or actual image directly.
Snapshots live under {root}/test/__snapshots__/{testName}/{snapshotName}.{baseline,actual,diff}.png.
Debug Logging
Set DEBUG before you start your dev server, to trace a specific subsystem.
DEBUG=ef:dev-server:jit npm run dev # transcode middleware
DEBUG=ef:dev-server:assets npm run dev # local image/caption serving
DEBUG=ef:dev-server:files npm run dev # local files API
DEBUG=ef:dev-server:sign-url npm run dev # URL signing
DEBUG=ef:dev-server:snapshot npm run dev # visual regression testing