Skip to content

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
--allTake every recipe
--installCopy each image to the recipe's install destination
--checkRe-take and compare against the installed images
--diffWith --check, write a three-panel diff image
--jsonWith --check, write the report to stdout as JSON
--lintParse every config, recipe, macro and data file, report everything wrong, and stop
--warningsWith --lint, also report what is legal but probably unmeant
--keep-goingCarry on past a recipe that fails, and list them all at the end
--initWrite 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
--untrustedRun 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
--helpPrint the option list
--versionPrint 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.

Signing in
$ 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
0Every recipe succeeded; with --check, nothing changed
non-zeroA recipe failed, or a checked screenshot changed
--lintNon-zero when a file has an error; warnings alone leave it at 0

Environment variables

Variable Effect
SHOTLIST_ENVNames a recipe may read, in addition to --allow-env
SHOTLIST_ENV_DENYVariable names no config key or flag may grant; globs allowed, subtracted last
SHOTLIST_DENYForbidden names, separated by commas or colons
SHOTLIST_ALLOWNetwork destinations approved by the operator, separated by commas or whitespace
SHOTLIST_WORK_LIMITSWork limit changes as comma-separated name=value settings
SHOTLIST_UNTRUSTEDSet 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
sameWithin the configured threshold and tolerance
changedOutside them
newNothing is committed at the destination
skippedThe recipe installs nowhere, so there is nothing to compare
failedThe recipe could not be taken
cancelledThe caller cancelled this recipe
not-attemptedAn earlier failure stopped the request

JSON report

Written by --check --json. Human-readable output moves to stderr.

report.json
{
  "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
changednumberHow many screenshots differ
totalnumberHow many were compared
driftarrayDifferences between this machine and the recorded baseline
results[].namestringRecipe name
results[].statusstringOne of the statuses above
results[].rationumberFraction of pixels that differ
results[].shotstringPath to the image just taken
results[].againststringPath to the committed image
results[].diffstringPath to the diff image, with --diff
results[].ignorednumberRegions omitted from comparison
failuresarraySite, browser or baseline failures outside one recipe
operatorDestinationsarrayCanonical destinations granted by the operator
cancellationobjectRequest cancellation detail, when cancelled
warningsarrayProgress-observer warnings

Work limits

Name Default Measures
recipeBytes1048576Bytes in one recipe document
macroBytes1048576Bytes in one macro document
dataBytes10485760Bytes in one data document
authoredSteps1000Steps written in one document
stepDepth32Nested Step depth
macroDepth32Nested Macro expansion depth
expandedSteps5000Steps after Macro expansion
executedSteps10000Steps run by a recipe or sign-in
eachItems1000Items processed by one each Step
matchingCharacters256Characters in one matching pattern
recipeMilliseconds600000Elapsed recipe work
teardownSteps1000Steps run during teardown
teardownMilliseconds60000Elapsed 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.jsonshotlist.config.yaml
shotlist/dist/recipe.schema.jsonA recipe
shotlist/dist/macro.schema.jsonA macro

Reference one from a file for editor completion and inline validation:

screenshots/recipes/order-row.yaml
# 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.