Command line
shotlist [<name>…] [options] With one or more names, shotlist takes those recipes. With no names and no options, it lists the recipes it found.
Options
| Option | Effect |
|---|---|
--all | Take every recipe |
--install | Copy each image to the recipe's install destination |
--check | Re-take and compare against the installed images |
--diff | With --check, write a three-panel diff image |
--json | With --check, write the report to stdout as JSON |
--lint | Parse every config, recipe, macro and data file, report everything wrong, and stop |
--warnings | With --lint, also report what is legal but probably unmeant |
--keep-going | Carry on past a recipe that fails, and list them all at the end |
--init | Write a starter configuration and recipe; never overwrites |
--config <file> | Use a specific configuration file |
--login <name> | Sign in and save the named session |
--using <macro> | With --login, sign in by running a macro |
--untrusted | Run with the restrictions listed below |
--allow <destination> | Approve one network protocol, host and port; repeatable |
--allow-path <dir> | Permit an extra directory; repeatable |
--allow-env <name> | Permit a recipe to read this variable from the run's environment; repeatable |
--deny <name> | Forbid a file or folder name; repeatable |
--work-limit <name=value> | Change one numerical Work limit for this Run; repeatable |
--help | Print the option list |
--version | Print the version |
--login
Opens a browser at site.url, waits while you sign in, and writes the session named on the command line. With --using the browser is headless and a macro signs in instead, so the run names the host and the variables the macro may read before it starts.
$ npx shotlist --login admin --using sign-in --allow-env ADMIN_PASSWORD
Signing in at https://app.example.com with `sign-in`, which may type ADMIN_PASSWORD into it.
✓ wrote .shotlist/admin.json
left out 23 cookies and 1 origin for google.com, accounts.google.com, youtube.com | Line | When |
|---|---|
Signing in at <url> with `<macro>` | Before the browser starts, with --using |
A browser is open at <url> | Signing in by hand, before it waits for you |
left out N cookies … | Something was dropped as not this site |
! `site.sessions.<name>.keep` names … | The session declares keep |
! this session has no `verify` selector … | Something was dropped and nothing could check what remains |
Nothing is written when the sign-in did not take, or when what is left after narrowing no longer satisfies verify. An --untrusted run is refused a session, so --login has no untrusted mode.
Exit codes
| Code | Meaning |
|---|---|
0 | Every recipe succeeded; with --check, nothing changed |
non-zero | A recipe failed, or a checked screenshot changed |
--lint | Non-zero when a file has an error; warnings alone leave it at 0 |
Environment variables
| Variable | Effect |
|---|---|
SHOTLIST_ENV | Names a recipe may read, in addition to --allow-env |
SHOTLIST_ENV_DENY | Variable names no config key or flag may grant; globs allowed, subtracted last |
SHOTLIST_DENY | Forbidden names, separated by commas or colons |
SHOTLIST_ALLOW | Network destinations approved by the operator, separated by commas or whitespace |
SHOTLIST_WORK_LIMITS | Work limit changes as comma-separated name=value settings |
SHOTLIST_UNTRUSTED | Set to 1 for the effect of --untrusted |
These are read from the process environment. shotlist does not load a .env file — that name is one it refuses to open — so anything kept there has to be in the environment before the command runs. See Capture a page behind a sign-in.
Network destinations
A bare host approves HTTPS on port 443. Prefix it with *. to approve its HTTPS subdomains. HTTP, WebSocket and non-default ports use a full destination, such as http://localhost:4321 or wss://events.example.com. Protocols match exactly; HTTP and WebSocket connections use separate approvals. An approved HTTP destination also permits a redirect to HTTPS port 443 on the same host. A TCP readiness probe uses tcp://localhost:4321.
Check statuses
| Status | Meaning |
|---|---|
same | Within the configured threshold and tolerance |
changed | Outside them |
new | Nothing is committed at the destination |
skipped | The recipe installs nowhere, so there is nothing to compare |
failed | The recipe could not be taken |
cancelled | The caller cancelled this recipe |
not-attempted | An earlier failure stopped the request |
JSON report
Written by --check --json. Human-readable output moves to stderr.
{
"changed": 1,
"total": 1,
"results": [
{
"name": "order-row",
"status": "changed",
"ratio": 0.0213,
"shot": "screenshots/out/order-row.png",
"against": "content/guide/images/order-row.png",
"diff": "screenshots/out/diff/order-row.png"
}
],
"failures": [],
"drift": [{ "field": "chromium", "was": "141.0.0.0", "now": "139.0.0.0" }],
"operatorDestinations": []
} | Field | Type | Meaning |
|---|---|---|
changed | number | How many screenshots differ |
total | number | How many were compared |
drift | array | Differences between this machine and the recorded baseline |
results[].name | string | Recipe name |
results[].status | string | One of the statuses above |
results[].ratio | number | Fraction of pixels that differ |
results[].shot | string | Path to the image just taken |
results[].against | string | Path to the committed image |
results[].diff | string | Path to the diff image, with --diff |
results[].ignored | number | Regions omitted from comparison |
failures | array | Site, browser or baseline failures outside one recipe |
operatorDestinations | array | Canonical destinations granted by the operator |
cancellation | object | Request cancellation detail, when cancelled |
warnings | array | Progress-observer warnings |
Work limits
| Name | Default | Measures |
|---|---|---|
recipeBytes | 1048576 | Bytes in one recipe document |
macroBytes | 1048576 | Bytes in one macro document |
dataBytes | 10485760 | Bytes in one data document |
authoredSteps | 1000 | Steps written in one document |
stepDepth | 32 | Nested Step depth |
macroDepth | 32 | Nested Macro expansion depth |
expandedSteps | 5000 | Steps after Macro expansion |
executedSteps | 10000 | Steps run by a recipe or sign-in |
eachItems | 1000 | Items processed by one each Step |
matchingCharacters | 256 | Characters in one matching pattern |
recipeMilliseconds | 600000 | Elapsed recipe work |
teardownSteps | 1000 | Steps run during teardown |
teardownMilliseconds | 60000 | Elapsed teardown work |
Baseline file
--install writes shotlist.baseline.json beside the configuration file, recording the shotlist, Playwright and Chromium versions and the platform that produced the images.
Node.js API
openRun() takes explicit Operator authority and opens the configuration and Library. Capture and Checking requests run through that object.
import { lint, openRun, type CheckReport } from 'shotlist'
const authority = { untrusted: false }
const run = openRun(authority, 'shotlist.config.yaml')
await run.capture({ recipes: ['order-row'], install: true })
const controller = new AbortController()
const report: CheckReport = await run.check({
all: true,
diff: true,
keepGoing: true,
signal: controller.signal,
onProgress: async (progress) => console.log(progress.type),
})
const review = lint(authority, 'shotlist.config.yaml') | Export | Purpose |
|---|---|
openRun() | Open a Project and Library under Operator authority |
run.capture() | Capture a named selection and optionally install it |
run.check() | Compare a named selection with installed images |
lint() | Review a Project under Operator authority |
readSession() | Read a configured Session by name through a Run |
signIn() | Write a configured Session by name through a Run |
parseConfig(), parseRecipe(), parseMacro(), parseLibrary(), parseQuery() | Validate a value already in memory |
A request selects { recipes: names } or { all: true }. keepGoing, signal and onProgress apply to Capture and Checking; install applies to Capture and diff to Checking. Progress observers are awaited in order. Their failures appear in warnings rather than changing a Recipe result.
Capture installs images only after every selected Recipe and owned resource has finished cleanup. Its report separates Recipe results, resource failures, Installation outcomes and Baseline recording. A Run accepts one request at a time and remains reusable after that request settles.
JSON Schemas
| File | Describes |
|---|---|
shotlist/dist/config.schema.json | shotlist.config.yaml |
shotlist/dist/recipe.schema.json | A recipe |
shotlist/dist/macro.schema.json | A macro |
Reference one from a file for editor completion and inline validation:
# yaml-language-server: $schema=../../node_modules/shotlist/dist/recipe.schema.json The config's schema was dist/schema.json before 0.4.0, which named one of three files "the schema". That file is still written and shotlist/schema.json still resolves to the config schema, so an existing YAML header or editor setting keeps working. Both go at 1.0.