#16

feat: multi-config support (extends, scenarios, list, --project)

Opensld0Antwants to merge intofrom

Hi there again! In the project where we use webreel, the number of demo videos keeps growing. A single config becomes hard to manage, and there's no built-in way to run selective subsets in CI/CD pipelines.

This PR is a working prototype of multi-config support. The goal is to start a conversation about the right API design for scaling webreel to bigger projects and automated pipelines. Everything here is open for discussion -- naming, semantics, the whole surface area.

What the prototype includes

Config: extends

A config can inherit from a base file. Child values override base, theme and sfx get shallow sub-key merge, videos are combined. Chains work up to 10 levels deep, circular refs are detected.

// base.json
{
  "baseUrl": "https://myapp.com",
  "viewport": "desktop",
  "defaultDelay": 500,
  "theme": { "cursor": { "size": 32, "hotspot": "top-left" } }
}
  // webreel.config.json — inherits baseUrl, viewport, defaultDelay
  // theme.cursor merges to { "size": 32, "hotspot": "center" }
  {
    "extends": "./base.json",
    "theme": { "cursor": { "hotspot": "center" } },
    "videos": {
      "hero": { "url": "/", "steps": [{ "action": "pause", "ms": 500 }] },
      "login": { "url": "/login", "steps": [{ "action": "pause", "ms": 500 }] }
    }
  }

Config: scenarios

Split videos across multiple files or inline groups. Entries can be glob strings (each matched file loaded as a standalone config) or inline objects with their own videos. extends: true on an inline scenario inherits top-level defaults from the root config. videos is optional when scenarios is present.

  // webreel.config.json
  {
    "baseUrl": "https://myapp.com",
    "viewport": { "width": 1920, "height": 1080 },
    "scenarios": [
      "./demos/*.webreel.json",
      {
        "extends": true,
        "videos": {
          "inline-demo": { "url": "/demo", "steps": [] }
        }
      }
    ],
    "videos": {
      "hero": { "url": "/", "steps": [] }
    }
  }
  // demos/login.webreel.json — discovered by glob
  {
    "videos": {
      "login": {
        "url": "https://myapp.com/login",
        "steps": [{ "action": "pause", "ms": 500 }]
      }
    }
  }

TypeScript equivalent with defineScenario:

  import { defineConfig, defineScenario } from "webreel";

  export default defineConfig({
    baseUrl: "https://myapp.com",
    scenarios: [
      "./demos/*.webreel.ts",
      defineScenario({
        extends: true,
        videos: {
          login: { url: "/login", steps: [] },
        },
      }),
    ],
  });

CLI: repeatable --config

All commands (record, preview, composite, validate, list) now accept -c/--config multiple times to load several config files in one run. Glob patterns are supported.

  $ webreel record -c web.json -c mobile.json

  Recording: hero
  Done: videos/hero.mp4
  Recording: login
  Done: videos/login.mp4
  Recording: mobile-hero
  Done: videos/mobile-hero.mp4
  Recording: mobile-login
  Done: videos/mobile-login.mp4
  $ webreel record -c "configs/*.json"

  Recording: hero
  Done: videos/hero.mp4
  Recording: login
  Done: videos/login.mp4

CLI: --project filtering

-p/--project filters videos by exact name or glob pattern. Repeatable. Available on record, composite, and list. When combined with positional video names, results are unioned.

  # exact name
  $ webreel record -p hero

  Recording: hero
  Done: videos/hero.mp4
  # glob pattern
  $ webreel record -c web.json -c mobile.json -p "mobile-*"

  Recording: mobile-hero
  Done: videos/mobile-hero.mp4
  Recording: mobile-login
  Done: videos/mobile-login.mp4
  # multiple patterns
  $ webreel record -p hero -p "mobile-*"

  Recording: hero
  Done: videos/hero.mp4
  Recording: mobile-hero
  Done: videos/mobile-hero.mp4
  Recording: mobile-login
  Done: videos/mobile-login.mp4
  # union with positional names
  $ webreel record login -p "mobile-*"

  Recording: login
  Done: videos/login.mp4
  Recording: mobile-hero
  Done: videos/mobile-hero.mp4
  Recording: mobile-login
  Done: videos/mobile-login.mp4

CLI: new webreel list command

Lists all videos across configs with source file, URL, viewport, and step count. Supports --json for scripting and CI integration.

  $ webreel list

  3 video(s):

    hero (webreel.config.json)
      / 1920x1080 3 step(s)
    login (demos/login.webreel.json)
      /login 1920x1080 5 step(s)
    inline-demo (webreel.config.json [scenario #1])
      /demo 1920x1080 2 step(s)
  $ webreel list -c web.json -c mobile.json -p "mobile-*"

  2 video(s):

    mobile-hero (mobile.json)
      / 393x852 3 step(s)
    mobile-login (mobile.json)
      /login 393x852 5 step(s)
  $ webreel list --json

  [
    { "name": "hero", "url": "/", "source": "webreel.config.json", "steps": 3,
      "viewport": { "width": 1920, "height": 1080 }, "output": "videos/hero.mp4" },
    { "name": "login", "url": "/login", "source": "demos/login.webreel.json", "steps": 5,
      "viewport": { "width": 1920, "height": 1080 }, "output": "videos/login.mp4" }
  ]

CLI: updated webreel validate

Validates multiple configs and checks for duplicate video names across them.

  $ webreel validate -c web.json -c mobile.json

  web.json: valid
  mobile.json: valid
  Cross-config: 4 video(s), no duplicates

Current state

This is a proof of concept meant to explore the design space. The code works and is tested, but I'm sharing it early to align on direction before polishing. Backward compatibility with single-config usage is preserved, loadWebreelConfig return type is unchanged, extends and scenarios only exist on input types.

Motivation

Two main use cases drove this:

  1. Large projects: dozens of demo videos organized by feature, viewport, or theme. Splitting configs across files with shared defaults keeps things maintainable.
  2. CI/CD: running selective subsets of recordings per pipeline stage, filtering by pattern, validating across configs before recording.

Looking for feedback on

The entire API design is open. I'd love to hear your thoughts on how you envision multi-config support for webreel - naming conventions, config structure, CLI flags, public API surface, merge semantics, anything. Happy to rework this from scratch based on your vision for the project.

Activity

8
vercel[bot]8mo ago

@sld0Ant is attempting to deploy a commit to the Vercel Labs Team on Vercel.

A member of the Team first needs to authorize it.

vercel[bot]reviewed8mo ago
vercel[bot]reviewed8mo ago
ctate8mo ago

Thanks for this thoughtful contribution! The design is well considered — extends chains with circular detection, scenario merging, glob discovery, and the list command with --json are all things we'd want for scaling to larger projects. The test coverage is excellent too.

A few things to address before merging:

Bug: webreel validate -c config.json on a single config with a broken extends path (e.g. pointing to a nonexistent file) reports "valid" because it only runs schema validation. The full resolution check only kicks in when multiple configs are passed. Could you run loadWebreelConfig (or loadFullConfig) unconditionally so resolution errors are always caught?

Oversight: preview is the only command that didn't get the --project flag — was that intentional?

Design question: The --project flag filters videos by name, but webreel doesn't have a "project" concept — would --filter be clearer? Open to discussion on this.

Minor: loadFullConfig parses each config file 2-3x (once in loadWebreelConfig, then again to check for scenarios). Not a blocker, but worth a cleanup pass to parse once and thread the raw result through.

Everything else looks great. Backward compatibility is preserved, the docs updates are thorough, and the new dependencies are solid choices. Excited about this direction for multi-config support.

sld0Ant8mo ago

Thanks for this thoughtful contribution! The design is well considered — extends chains with circular detection, scenario merging, glob discovery, and the list command with --json are all things we'd want for scaling to larger projects. The test coverage is excellent too.

A few things to address before merging:

Bug: webreel validate -c config.json on a single config with a broken extends path (e.g. pointing to a nonexistent file) reports "valid" because it only runs schema validation. The full resolution check only kicks in when multiple configs are passed. Could you run loadWebreelConfig (or loadFullConfig) unconditionally so resolution errors are always caught?

Oversight: preview is the only command that didn't get the --project flag — was that intentional?

Design question: The --project flag filters videos by name, but webreel doesn't have a "project" concept — would --filter be clearer? Open to discussion on this.

Minor: loadFullConfig parses each config file 2-3x (once in loadWebreelConfig, then again to check for scenarios). Not a blocker, but worth a cleanup pass to parse once and thread the raw result through.

Everything else looks great. Backward compatibility is preserved, the docs updates are thorough, and the new dependencies are solid choices. Excited about this direction for multi-config support.

Thanks for the detailed review! All feedback has been addressed in 742c3a2:

  • Bug: validate now runs loadWebreelConfig unconditionally after schema validation for JSON configs, so broken extends paths are caught even with a single config
  • Oversight: Added --filter to the preview command
  • Design: Renamed --project to --filter (-f) across all commands (record, composite, list, preview), updated internal function names, error messages, tests, and docs
  • Minor: Refactored loadFullConfig and resolveScenarios to parse each config once via an internal loadWebreelConfigFull that returns both the resolved config and the raw resolved object, eliminating the redundant loadRawConfig + resolveExtends calls