versioncam 0.5.2 → 0.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (57) hide show
  1. package/CHANGELOG.md +93 -0
  2. package/README.md +95 -2
  3. package/dist/cli/commands/check.js +25 -0
  4. package/dist/cli/commands/check.js.map +1 -1
  5. package/dist/cli/commands/doctor.js +54 -1
  6. package/dist/cli/commands/doctor.js.map +1 -1
  7. package/dist/cli/commands/install.d.ts +40 -3
  8. package/dist/cli/commands/install.js +66 -11
  9. package/dist/cli/commands/install.js.map +1 -1
  10. package/dist/cli/commands/record.d.ts +6 -0
  11. package/dist/cli/commands/record.js +24 -3
  12. package/dist/cli/commands/record.js.map +1 -1
  13. package/dist/cli/commands/stability.js +10 -3
  14. package/dist/cli/commands/stability.js.map +1 -1
  15. package/dist/cli/usage.d.ts +1 -1
  16. package/dist/cli/usage.js +2 -0
  17. package/dist/cli/usage.js.map +1 -1
  18. package/dist/config.d.ts +37 -1
  19. package/dist/config.js +115 -3
  20. package/dist/config.js.map +1 -1
  21. package/dist/core/cursor.d.ts +22 -0
  22. package/dist/core/cursor.js +6 -0
  23. package/dist/core/cursor.js.map +1 -1
  24. package/dist/core/motion-defaults.d.ts +8 -0
  25. package/dist/core/motion-defaults.js +8 -0
  26. package/dist/core/motion-defaults.js.map +1 -1
  27. package/dist/core/timeline.d.ts +24 -0
  28. package/dist/core/timeline.js.map +1 -1
  29. package/dist/driver/clip.d.ts +6 -0
  30. package/dist/driver/clip.js +33 -2
  31. package/dist/driver/clip.js.map +1 -1
  32. package/dist/driver/file-window.d.ts +66 -0
  33. package/dist/driver/file-window.js +245 -0
  34. package/dist/driver/file-window.js.map +1 -0
  35. package/dist/driver/page-hooks.d.ts +23 -1
  36. package/dist/driver/page-hooks.js +36 -1
  37. package/dist/driver/page-hooks.js.map +1 -1
  38. package/dist/driver/replay.d.ts +263 -0
  39. package/dist/driver/replay.js +558 -0
  40. package/dist/driver/replay.js.map +1 -0
  41. package/dist/driver/session.d.ts +70 -4
  42. package/dist/driver/session.js +267 -29
  43. package/dist/driver/session.js.map +1 -1
  44. package/dist/driver/settle.js +6 -0
  45. package/dist/driver/settle.js.map +1 -1
  46. package/dist/index.d.ts +4 -2
  47. package/dist/index.js.map +1 -1
  48. package/dist/page/assets/index-CnRDAiUz.js +2 -0
  49. package/dist/page/index.html +1 -1
  50. package/dist/publish/protocol.d.ts +12 -1
  51. package/dist/publish/protocol.js.map +1 -1
  52. package/dsl.md +68 -1
  53. package/package.json +1 -1
  54. package/plugin/.claude-plugin/plugin.json +1 -1
  55. package/plugin/skills/versioncam/authoring.md +6 -1
  56. package/plugin/skills/versioncam/onboarding.md +9 -3
  57. package/dist/page/assets/index-BtUbUEdN.js +0 -2
package/CHANGELOG.md CHANGED
@@ -6,6 +6,99 @@ and versions follow [semantic versioning](https://semver.org/). While the
6
6
  major version is 0, a minor version may change the config or the clip DSL,
7
7
  and its entry here says how.
8
8
 
9
+ ## 0.7.0 (2026-11-02)
10
+
11
+ A minor version: the config gains `replay`, the clip DSL gains
12
+ `s.fileWindow`, a second form of `s.drag` and `modifiers` on `s.click`,
13
+ and `record` and `stability` take `--replay`. A config without `replay`
14
+ records as it did with 0.6.0, and the render config is unchanged, so a
15
+ publish needs nothing new of version.cam.
16
+
17
+ ### Added
18
+
19
+ - `replay` in the config records a clip's responses the first time it runs
20
+ and serves them on every run after, so a map's tiles, a model's answer or
21
+ a job's status are the same in every recording. `record` names the
22
+ requests by URL; each clip's replay is a HAR in `replay/` beside the
23
+ config, with binary bodies under `replay/bodies/` by hash, and is meant to
24
+ be committed. A request the replay covers and does not hold fails the clip
25
+ and names it. `pace` delivers a streamed response, an event stream over
26
+ `fetch` or `EventSource`, or lines of JSON, a piece at a time on the
27
+ page's clock. Authorization, cookie and API key headers are never written,
28
+ and `redact` leaves query parameters out.
29
+ - `s.fileWindow(files, options?)` draws a file window over the page, as
30
+ Finder draws one, to select files in and drag onto the app's dropzone,
31
+ which receives them as `File` objects through the events a real drag
32
+ sends: the upload a clip can film. A click selects a row, with Shift it
33
+ extends, with Meta it adds or removes.
34
+ - `s.drag(from, to, options?)`, from one locator or point to another, beside
35
+ the form in fractions of one box; and `modifiers` on `s.click`, for a
36
+ shift-click. Both strokes take Meta and Control as well as Shift and Alt.
37
+ - `versioncam record` and `stability` take `--replay auto|record|replay|live`.
38
+ `doctor` lists each clip's replay, and `check` points out one older than
39
+ ninety days.
40
+
41
+ ## 0.6.0 (2026-11-02)
42
+
43
+ A minor version: the clip DSL gains `s.scroll`, the config gains
44
+ `theme.cursor` and `motion.scrollDurationMs`, and a page's own smooth
45
+ scrolling is instant while it is recorded. A config that sets none of
46
+ the new fields renders and publishes as it did with 0.5.3, and records
47
+ the same unless its app scrolls smoothly from a script.
48
+
49
+ ### Added
50
+
51
+ - `s.scroll(target, where, options?)` scrolls a pane, or the page, on the
52
+ clip's clock: `{ by: px }`, `{ to: "top" | "bottom" | px }`, or
53
+ `{ into: locator, block? }`, one eased step a frame, so the frames inside
54
+ a scroll are the same on every run. The end is clamped to what the element
55
+ can scroll, and an element that cannot scroll at all is refused by name.
56
+ `motion.scrollDurationMs` (700) is its default length. A recording keeps
57
+ its scrolls in the timeline's new `scrolls`, which a recording without one
58
+ does not have.
59
+ - `theme.cursor: { style, ripple }` in the config. `style: "dark"` draws
60
+ the system's black arrow with a white hairline, as a screen recording of
61
+ a Mac shows it, and `ripple: false` draws no ring on a click. The default,
62
+ `{ style: "light", ripple: true }`, is the white arrow and the ring every
63
+ earlier version drew, to the byte.
64
+
65
+ ### Changed
66
+
67
+ - A page's script can no longer scroll smoothly while it is recorded:
68
+ `scrollIntoView`, `scrollTo`, `scrollBy` and `scroll` given
69
+ `{ behavior: "smooth" }` scroll at once, as a stylesheet's
70
+ `scroll-behavior: smooth` already did. The browser animated those scrolls
71
+ on its own clock, so the frames during one differed between runs. A clip
72
+ whose app scrolled itself that way now shows the scroll land in a frame;
73
+ `s.scroll` is how a clip shows one move.
74
+ - `versioncam publish` sends `theme.cursor` and `motion.scrollDurationMs`
75
+ only when the config changes them, so a config that changes neither
76
+ publishes exactly what 0.5.3 did. One that changes either needs
77
+ version.cam from cloud-v1.8.0, which knows both; before it, the publish
78
+ is refused with a message that names the key.
79
+
80
+ ## 0.5.3 (2026-11-01)
81
+
82
+ A patch version: the config and the clip DSL are as they were. The browser
83
+ installs on the Ubuntu that GitHub's `ubuntu-latest` became, and the README
84
+ says what the plans are and how the private beta gives Pro.
85
+
86
+ ### Fixed
87
+
88
+ - `versioncam install` on an Ubuntu newer than its Playwright knows, which
89
+ is GitHub's `ubuntu-latest` since it became 26.04 in October 2026: where
90
+ Playwright refuses the OS outright, the installer runs again as
91
+ `ubuntu24.04`, the newest Ubuntu it knows, whose build and system
92
+ libraries record the same. `login` and `preview` fetch their browser the
93
+ same way. An override set by hand in `PLAYWRIGHT_HOST_PLATFORM_OVERRIDE`
94
+ is left alone.
95
+
96
+ ### Changed
97
+
98
+ - The README's version.cam section states the plans, as planned: Free for
99
+ public repositories, Pro for private ones at $35 a project a month, Max
100
+ for ten projects at $145, and how the private beta gives Pro away.
101
+
9
102
  ## 0.5.2 (2026-10-29)
10
103
 
11
104
  A patch version: the config and the clip DSL are as they were. The README
package/README.md CHANGED
@@ -152,8 +152,9 @@ cannot say, that is a missing field. It is not a reason to fork.
152
152
  | `review` | `minSeconds: 9`, `maxSeconds: 11` | How long a clip should run. `versioncam review` holds clips to it. |
153
153
  | `cursorStart` | `{ x: 0.72, y: 0.28 }` | Where the cursor rests in the first frame, as fractions of the viewport. |
154
154
  | `clock` | `time: "2026-09-01T09:00:00Z"`, `timezoneId: "UTC"`, `locale: "en-GB"` | The instant, time zone and locale the app sees. |
155
- | `theme` | `accent: "#2f6fed"`, `cursorScale: 2.1` | The colour of ripples and rings, the cursor's size, and an optional `presentation`. See *Presentation*. |
156
- | `motion` | `MOTION` | Dwells, strokes and camera timing. `MOTION` is exported, with the reason for each number beside it. |
155
+ | `theme` | `accent: "#2f6fed"`, `cursorScale: 2.1`, `cursor: { style: "light", ripple: true }` | The colour of ripples and rings, the cursor's size and look, and an optional `presentation`. `cursor: { style: "dark", ripple: false }` draws the system's black arrow and no ring on a click, as a screen recording shows them. See *Presentation*. |
156
+ | `motion` | `MOTION` | Dwells, strokes, scrolls and camera timing. `MOTION` is exported, with the reason for each number beside it. |
157
+ | `replay` | none | Responses recorded once and served on every run after: tiles, a model's answers, a job's status. See *Live data*. |
157
158
  | `outDir` | `".versioncam"` | Where everything the recorder writes goes. Gitignore it. |
158
159
  | `scenes` | none | A module whose default export is `Record<string, Scene>`, keyed by scene id: `export default { [intro.id]: intro }`. A named export is not read, and a module with no default export is refused. |
159
160
  | `sequence` | none | Clip ids, in order, for `versioncam render --sequence`. |
@@ -255,6 +256,31 @@ for a straight one, `s.lasso` for a closed loop) are written in fractions of a
255
256
  target's box for the same reason. A clip built from locators survives a layout
256
257
  change.
257
258
 
259
+ A long answer or a list is read by scrolling it, and `s.scroll` does that on
260
+ the clip's clock, one step a frame, so every frame of it is the same on every
261
+ run. Give it the pane that scrolls, or `"page"`, and where to go: `{ by: 400 }`,
262
+ `{ to: "bottom" }`, or `{ into: s.byTestId("citation-3") }` to bring one thing
263
+ into view.
264
+
265
+ ```ts
266
+ await s.moveTo(s.byTestId("answer"));
267
+ await s.scroll(s.byTestId("answer"), { into: s.byTestId("citation-3") });
268
+ ```
269
+
270
+ The cursor stays put while the pane moves, so a clip moves it over the pane
271
+ first, where a hand on a mouse wheel would be.
272
+
273
+ An upload is filmed from `s.fileWindow`, a file window drawn over the page,
274
+ since the browser's own file dialog is outside it. The clip selects files in
275
+ it and drags them onto the app's dropzone, which receives them as real files
276
+ through the events a real drag sends. The window is staged; say so where the
277
+ clip is shown.
278
+
279
+ ```ts
280
+ const files = await s.fileWindow([{ name: "cases.csv", content: csv }], { title: "Case data" });
281
+ await s.drag(files.item(0), s.byTestId("dropzone"));
282
+ ```
283
+
258
284
  `npx versioncam dsl` prints every method with its options and defaults, and a
259
285
  test holds that list to the session's code. The same reference is
260
286
  *The clip DSL*.
@@ -360,11 +386,13 @@ the wreckage would help nobody.
360
386
  ```
361
387
  versioncam record [id…] drive the app and capture frames
362
388
  --draft 15fps at CSS scale: cheap, for writing clips
389
+ --replay <mode> auto, record, replay or live, over the config's replay
363
390
  versioncam check [id…] record every clip; exit 1 if any breaks
364
391
  --report <file> and write each clip's result there, as JSON
365
392
  versioncam stability [id…] record each clip twice the same way; stable or not
366
393
  --runs <n> how many times (default 2)
367
394
  --full at full quality rather than draft
395
+ --replay <mode> as for record
368
396
  ```
369
397
 
370
398
  ### Review
@@ -500,6 +528,19 @@ also reports each publish on GitHub: a check on its commit, and on a pull
500
528
  request a comment naming the clips that look different and the ones that
501
529
  failed.
502
530
 
531
+ ### Plans
532
+
533
+ Priced per project, never per seat, and planned until the first bill
534
+ confirms them. Free, for public repositories: three projects, ten clips a
535
+ project, thirty days of history, clips to sixty seconds, a gigabyte, the
536
+ pull request check and the badge. Pro, $35 a project a month or $350 a
537
+ year: private repositories too, unlimited history, a hundred clips a
538
+ project, and the team told by email, Slack or a GitHub issue when a clip
539
+ breaks. Max, $145 a month for ten projects with everything Pro has. The
540
+ recorder and the Action are free whatever the plan. While the beta is
541
+ private nothing is sold: Free works for anyone who signs in, and the first
542
+ ten accounts that write to hello@version.cam have Pro free until it ends.
543
+
503
544
  ### When a clip breaks
504
545
 
505
546
  A clip breaks when its check fails on the default branch: a control it uses
@@ -846,12 +887,64 @@ its CSS motion:
846
887
  up it finishes, as in a browser nobody is filming: its `finished` promise
847
888
  resolves and `animationend` fires, so a dialog that unmounts after its exit
848
889
  animation goes, and does not sit invisibly over the page.
890
+ - **Smooth scrolling is instant.** A stylesheet's `scroll-behavior: smooth`
891
+ and a script's `{ behavior: "smooth" }` both scroll at once, since the
892
+ browser would animate either on a clock the recorder does not move. A chat
893
+ that keeps its newest message in view jumps to it. To film a scroll, use
894
+ `s.scroll`.
849
895
  - **Not on the clock:** animations a script makes with `element.animate()`,
850
896
  Chromium's smooth scrolling of a mouse wheel, and transitions inside an
851
897
  iframe, which stay zeroed since nothing drives an iframe's clock. A clip
852
898
  that films one of the first two may differ between runs: `stability` says
853
899
  so.
854
900
 
901
+ ### Live data
902
+
903
+ A map's tiles, a model's answer, a job's status, a count that grows: each
904
+ comes from a server, at the server's speed and in its words of the day, so
905
+ two recordings of a clip that shows one differ wherever it reaches the page.
906
+ `replay` records those responses the first time a clip runs and serves them,
907
+ from a file, on every run after:
908
+
909
+ ```ts
910
+ replay: {
911
+ record: ["https://tiles.example.com/**", "**/api/chat/**"],
912
+ // A stream the page reads over time, one event every 300 ms of the clip.
913
+ pace: [{ url: "**/api/chat/*/stream", chunkMs: 300 }],
914
+ },
915
+ ```
916
+
917
+ | Field | Default | What it sets |
918
+ |---|---|---|
919
+ | `record` | required | The requests to record and replay: URL globs, as Playwright writes them, or regular expressions. |
920
+ | `mode` | `"auto"` | `"auto"` replays a clip whose file exists and records one whose file does not. `"record"` always records, `"replay"` fails without a file, `"live"` ignores it. `--replay <mode>` on `record` and `stability` overrides it for one run. |
921
+ | `dir` | `"replay"` | Where the files go, beside the config. Commit them: a replay is an input, like a fixture, and CI needs it to record the clip the same way. |
922
+ | `pace` | none | Responses the page reads a piece at a time, an event stream or lines of JSON: delivered one event or one line every `chunkMs` of the page's clock, so each piece lands on the same frame every run, at any frame rate. |
923
+ | `redact` | none | Query parameters left out of the file, and of the match, by name. |
924
+ | `onMissing` | `"fail"` | A request the replay covers and does not hold fails the clip, naming it, since a request that quietly went live is what this exists to stop. `"live"` sends it to the network with a warning instead. |
925
+
926
+ Each clip has its own file, `replay/<clip>.har`, a HAR that a browser's
927
+ developer tools can open. Text bodies are in it, where a diff can read them;
928
+ binary ones, tiles and images, sit beside it under `replay/bodies/`, named by
929
+ their hash, so clips that load the same tiles keep them once. The
930
+ `Authorization`, `Cookie`, `Set-Cookie` and `X-API-Key` headers are never
931
+ written. The bodies are written as the server sent them, so read a replay
932
+ before committing it if the server's answers can hold anything private.
933
+
934
+ A request the same URL answers several times, a job's status polled until it
935
+ is done, is answered in the order it was recorded, and the last answer again
936
+ after that. A request the clip answers itself, with `page.route` from
937
+ `offCamera`, never reaches the replay, so a hand-written mock still wins
938
+ where a clip has one. The frame loop waits for the replay's answers, so a
939
+ response arrives in the frame it was asked for on every run.
940
+
941
+ `doctor` lists each clip's replay, how many requests it holds and when it
942
+ was recorded, and `check` points out one older than ninety days. That is by
943
+ age alone: a clip that passes against its replay proves the app still asks
944
+ for what it asked for, not that the server still answers the same way. When
945
+ the app changes what it fetches, the clip fails and names the request; record
946
+ it again with `versioncam record <id> --replay record`.
947
+
855
948
  ### When a clip will not reproduce
856
949
 
857
950
  The recorder can only be as deterministic as the app it films. An app that is
@@ -1,6 +1,7 @@
1
1
  import { existsSync, readFileSync } from "node:fs";
2
2
  import { join, resolve } from "node:path";
3
3
  import { parseArgs } from "node:util";
4
+ import { STALE_DAYS, describeReplay } from "../../driver/replay.js";
4
5
  import { launchBrowser } from "../../driver/launch.js";
5
6
  import { RECORDER_VERSION, runClip } from "../../driver/clip.js";
6
7
  import { loadClips, loadConfig } from "../../loader.js";
@@ -51,6 +52,8 @@ async function checkAll(config, clips, reportPath) {
51
52
  await browser.close();
52
53
  }
53
54
  process.stdout.write(`\n${clips.length - failures.length}/${clips.length} clips still match the app.\n`);
55
+ for (const line of staleReplays(config, clips))
56
+ process.stdout.write(line);
54
57
  for (const { id, error } of failures) {
55
58
  process.stderr.write(`\n${id}\n ${error.message}\n`);
56
59
  }
@@ -59,6 +62,28 @@ async function checkAll(config, clips, reportPath) {
59
62
  }
60
63
  return failures.length > 0 ? 1 : 0;
61
64
  }
65
+ /**
66
+ * A line for each clip whose replay is older than `STALE_DAYS`.
67
+ *
68
+ * By age alone, and said to be: a clip that passes against its replay
69
+ * proves the app still makes the requests it made, not that the server
70
+ * still answers them that way, and nothing here can know whether it does.
71
+ * Three months is long enough that it is worth a person asking.
72
+ */
73
+ function staleReplays(config, clips) {
74
+ if (!config.replay)
75
+ return [];
76
+ const lines = [];
77
+ for (const clip of clips) {
78
+ const found = describeReplay(config.replay.dir, clip.id);
79
+ if (!found.exists || !(found.ageDays > STALE_DAYS))
80
+ continue;
81
+ lines.push(`warn ${clip.id}: its replay was recorded ${found.ageDays} days ago. ` +
82
+ "The clip shows the server's answers of that day; if they have changed, " +
83
+ `record it again with \`versioncam record ${clip.id} --replay record\`.\n`);
84
+ }
85
+ return lines;
86
+ }
62
87
  /**
63
88
  * What `--report` writes: each clip's result, the step a failure stopped at,
64
89
  * and where its picture is, for a machine to read. The Action builds its pull
@@ -1 +1 @@
1
- {"version":3,"file":"check.js","sourceRoot":"","sources":["../../../src/cli/commands/check.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"check.js","sourceRoot":"","sources":["../../../src/cli/commands/check.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AACnD,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAC1C,OAAO,EAAE,SAAS,EAAE,MAAM,WAAW,CAAC;AACtC,OAAO,EAAE,UAAU,EAAE,cAAc,EAAE,MAAM,wBAAwB,CAAC;AACpE,OAAO,EAAE,aAAa,EAAE,MAAM,wBAAwB,CAAC;AACvD,OAAO,EAAE,gBAAgB,EAAE,OAAO,EAAE,MAAM,sBAAsB,CAAC;AAGjE,OAAO,EAAE,SAAS,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AACxD,OAAO,EAAE,UAAU,EAAE,MAAM,WAAW,CAAC;AACvC,OAAO,EAAE,aAAa,EAAE,MAAM,yBAAyB,CAAC;AACxD,OAAO,EACL,YAAY,EACZ,WAAW,GAEZ,MAAM,yBAAyB,CAAC;AACjC,OAAO,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAE3C;;;;;;GAMG;AACH,MAAM,CAAC,KAAK,UAAU,KAAK,CAAC,IAAc;IACxC,MAAM,EAAE,WAAW,EAAE,MAAM,EAAE,GAAG,SAAS,CAAC;QACxC,IAAI,EAAE,IAAI;QACV,OAAO,EAAE,EAAE,MAAM,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,EAAE;QACvC,MAAM,EAAE,KAAK;QACb,gBAAgB,EAAE,IAAI;KACvB,CAAC,CAAC;IACH,MAAM,UAAU,GACd,OAAO,MAAM,CAAC,MAAM,KAAK,QAAQ,CAAC,CAAC,CAAC,OAAO,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;IAEpE,MAAM,MAAM,GAAG,MAAM,UAAU,CAAC,OAAO,CAAC,GAAG,EAAE,CAAC,CAAC;IAC/C,MAAM,KAAK,GAAG,MAAM,SAAS,CAAC,MAAM,EAAE,WAAuB,CAAC,CAAC;IAE/D,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACvB,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,sBAAsB,CAAC,CAAC;QAC7C,OAAO,CAAC,CAAC;IACX,CAAC;IAED,OAAO,UAAU,CAAC,MAAM,EAAE,GAAG,EAAE,CAAC,QAAQ,CAAC,MAAM,EAAE,KAAK,EAAE,UAAU,CAAC,CAAC,CAAC;AACvE,CAAC;AAED,KAAK,UAAU,QAAQ,CACrB,MAAsB,EACtB,KAAuB,EACvB,UAAyB;IAEzB,MAAM,OAAO,GAAG,MAAM,aAAa,EAAE,CAAC;IACtC,MAAM,QAAQ,GAAmC,EAAE,CAAC;IAEpD,IAAI,CAAC;QACH,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;YACzB,IAAI,CAAC;gBACH,MAAM,OAAO,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,CAAC,CAAC;gBACrC,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,SAAS,IAAI,CAAC,EAAE,IAAI,CAAC,CAAC;YAC7C,CAAC;YAAC,OAAO,KAAK,EAAE,CAAC;gBACf,QAAQ,CAAC,IAAI,CAAC,EAAE,EAAE,EAAE,IAAI,CAAC,EAAE,EAAE,KAAK,EAAE,KAAc,EAAE,CAAC,CAAC;gBACtD,aAAa,CAAC,IAAI,CAAC,MAAM,CAAC,aAAa,EAAE,IAAI,CAAC,EAAE,CAAC,EAAE,KAAc,CAAC,CAAC;gBACnE,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,SAAS,IAAI,CAAC,EAAE,IAAI,CAAC,CAAC;YAC7C,CAAC;QACH,CAAC;IACH,CAAC;YAAS,CAAC;QACT,MAAM,OAAO,CAAC,KAAK,EAAE,CAAC;IACxB,CAAC;IAED,OAAO,CAAC,MAAM,CAAC,KAAK,CAClB,KAAK,KAAK,CAAC,MAAM,GAAG,QAAQ,CAAC,MAAM,IAAI,KAAK,CAAC,MAAM,+BAA+B,CACnF,CAAC;IACF,KAAK,MAAM,IAAI,IAAI,YAAY,CAAC,MAAM,EAAE,KAAK,CAAC;QAAE,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;IAC3E,KAAK,MAAM,EAAE,EAAE,EAAE,KAAK,EAAE,IAAI,QAAQ,EAAE,CAAC;QACrC,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,KAAK,EAAE,OAAO,KAAK,CAAC,OAAO,IAAI,CAAC,CAAC;IACxD,CAAC;IACD,IAAI,UAAU,EAAE,CAAC;QACf,WAAW,CAAC,UAAU,EAAE,WAAW,CAAC,MAAM,EAAE,KAAK,EAAE,QAAQ,CAAC,CAAC,CAAC;IAChE,CAAC;IACD,OAAO,QAAQ,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;AACrC,CAAC;AAED;;;;;;;GAOG;AACH,SAAS,YAAY,CACnB,MAAsB,EACtB,KAAuB;IAEvB,IAAI,CAAC,MAAM,CAAC,MAAM;QAAE,OAAO,EAAE,CAAC;IAC9B,MAAM,KAAK,GAAa,EAAE,CAAC;IAC3B,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;QACzB,MAAM,KAAK,GAAG,cAAc,CAAC,MAAM,CAAC,MAAM,CAAC,GAAG,EAAE,IAAI,CAAC,EAAE,CAAC,CAAC;QACzD,IAAI,CAAC,KAAK,CAAC,MAAM,IAAI,CAAC,CAAC,KAAK,CAAC,OAAO,GAAG,UAAU,CAAC;YAAE,SAAS;QAC7D,KAAK,CAAC,IAAI,CACR,SAAS,IAAI,CAAC,EAAE,6BAA6B,KAAK,CAAC,OAAO,aAAa;YACrE,yEAAyE;YACzE,4CAA4C,IAAI,CAAC,EAAE,uBAAuB,CAC7E,CAAC;IACJ,CAAC;IACD,OAAO,KAAK,CAAC;AACf,CAAC;AAED;;;;;GAKG;AACH,SAAS,WAAW,CAClB,MAAsB,EACtB,KAAuB,EACvB,QAAwC;IAExC,OAAO;QACL,QAAQ,EAAE,gBAAgB;QAC1B,KAAK,EAAE,KAAK,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE;YACxB,MAAM,MAAM,GAAG,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,KAAK,IAAI,CAAC,EAAE,CAAC,CAAC;YACtD,IAAI,CAAC,MAAM;gBAAE,OAAO,EAAE,EAAE,EAAE,IAAI,CAAC,EAAE,EAAE,MAAM,EAAE,MAAM,EAAE,CAAC;YACpD,MAAM,GAAG,GAAG,IAAI,CAAC,MAAM,CAAC,aAAa,EAAE,IAAI,CAAC,EAAE,CAAC,CAAC;YAChD,MAAM,OAAO,GAAG,IAAI,CAAC,GAAG,EAAE,YAAY,CAAC,CAAC;YACxC,MAAM,OAAO,GAAG,UAAU,CAAC,OAAO,CAAC;gBACjC,CAAC,CAAE,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,OAAO,EAAE,MAAM,CAAC,CAAa;gBACxD,CAAC,CAAC,IAAI,CAAC;YACT,MAAM,OAAO,GAAG,IAAI,CAAC,GAAG,EAAE,WAAW,CAAC,CAAC;YACvC,OAAO;gBACL,EAAE,EAAE,IAAI,CAAC,EAAE;gBACX,MAAM,EAAE,MAAM;gBACd,OAAO,EAAE,MAAM,CAAC,KAAK,CAAC,OAAO;gBAC7B,IAAI,EAAE,OAAO,EAAE,IAAI,IAAI,IAAI;gBAC3B,CAAC,EAAE,OAAO,EAAE,CAAC,IAAI,IAAI;gBACrB,OAAO,EAAE,UAAU,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,IAAI;aAC9C,CAAC;QACJ,CAAC,CAAC;KACH,CAAC;AACJ,CAAC"}
@@ -1,7 +1,8 @@
1
1
  import { browserPaths } from "./install.js";
2
2
  import { ffmpegStatus } from "../../render/ffmpeg.js";
3
3
  import { ensureApp, probe } from "../../app-server.js";
4
- import { NO_CONFIG, findConfigFile, loadConfig, loadSceneIds, } from "../../loader.js";
4
+ import { NO_CONFIG, findConfigFile, loadClips, loadConfig, loadSceneIds, } from "../../loader.js";
5
+ import { STALE_DAYS, describeReplay } from "../../driver/replay.js";
5
6
  const NOT_SERVING = "Start the app, point `baseUrl` at where it is served, or add `webServer` to the config.";
6
7
  /**
7
8
  * Is the app the config points at serving — and if not, can it be started?
@@ -95,6 +96,55 @@ async function scenesCheck(cwd) {
95
96
  return { name: "scenes", ok: false, detail: first, fix: rest.join("\n") };
96
97
  }
97
98
  }
99
+ /**
100
+ * Each clip's replay: whether it has one, how many requests it holds, and
101
+ * how old it is. Never a failure: a clip without one records it on its first
102
+ * run. Null for a config without `replay`.
103
+ *
104
+ * The age is the only staleness anyone can tell from here. Whether the
105
+ * server's answers have changed since is not knowable without asking it, so
106
+ * an old replay is pointed out, not judged.
107
+ */
108
+ async function replayCheck(cwd) {
109
+ let config;
110
+ try {
111
+ config = await loadConfig(cwd);
112
+ }
113
+ catch {
114
+ return null;
115
+ }
116
+ const replay = config.replay;
117
+ if (!replay)
118
+ return null;
119
+ let ids;
120
+ try {
121
+ ids = (await loadClips(config, [])).map((clip) => clip.id);
122
+ }
123
+ catch (error) {
124
+ return {
125
+ name: "replay",
126
+ ok: false,
127
+ detail: `the clips did not load: ${error.message.split("\n")[0]}`,
128
+ };
129
+ }
130
+ const lines = ids.map((id) => {
131
+ const found = describeReplay(replay.dir, id);
132
+ if (!found.exists)
133
+ return `${id}: none yet; its first recording makes one`;
134
+ const age = Number.isNaN(found.ageDays)
135
+ ? "of unknown age"
136
+ : `${found.ageDays} days old`;
137
+ const stale = found.ageDays > STALE_DAYS
138
+ ? ", older than any answer should be trusted to stay"
139
+ : "";
140
+ return `${id}: ${found.requests} requests, recorded ${found.recorded.slice(0, 10)}, ${age}${stale}`;
141
+ });
142
+ return {
143
+ name: "replay",
144
+ ok: true,
145
+ detail: [`${replay.dir} (mode ${replay.mode})`, ...lines].join("\n "),
146
+ };
147
+ }
98
148
  /**
99
149
  * Can this machine record and encode?
100
150
  *
@@ -154,6 +204,9 @@ export async function doctor() {
154
204
  const scenes = await scenesCheck(process.cwd());
155
205
  if (scenes)
156
206
  checks.push(scenes);
207
+ const replay = await replayCheck(process.cwd());
208
+ if (replay)
209
+ checks.push(replay);
157
210
  checks.push(await appCheck(process.cwd()));
158
211
  }
159
212
  for (const check of checks) {
@@ -1 +1 @@
1
- {"version":3,"file":"doctor.js","sourceRoot":"","sources":["../../../src/cli/commands/doctor.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"doctor.js","sourceRoot":"","sources":["../../../src/cli/commands/doctor.ts"],"names":[],"mappings":"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"}
@@ -46,12 +46,49 @@ export declare function browserPaths(): {
46
46
  shell: string | null;
47
47
  full: string | null;
48
48
  };
49
- /** Playwright's installer, with its output on this terminal; its exit code. */
50
- export declare function runInstaller(command: string, args: string[]): Promise<number>;
49
+ /** How a run of Playwright's installer ended, and what it said. */
50
+ export type Installed = {
51
+ code: number;
52
+ output: string;
53
+ };
54
+ /**
55
+ * Playwright's installer. `env` is added to this process's environment for
56
+ * that run alone.
57
+ */
58
+ export type Installer = (command: string, args: string[], env?: Record<string, string>) => Promise<Installed>;
59
+ /**
60
+ * Playwright's installer, its output on this terminal as it runs and kept
61
+ * as well, for `fallbackPlatform` to read what it refused. The exit code
62
+ * comes with `close`, not `exit`: the last of the output arrives between
63
+ * the two.
64
+ */
65
+ export declare const runInstaller: Installer;
66
+ /**
67
+ * The platform to run the installer again as, when Playwright refused this
68
+ * one; null when there is none to try.
69
+ *
70
+ * Playwright knows the Ubuntu releases of its day and refuses a newer one
71
+ * outright, the browser as well as the system libraries: "Playwright does
72
+ * not support chromium-headless-shell on ubuntu26.04-x64". GitHub's
73
+ * ubuntu-latest became 26.04 in October 2026, and Playwright 1.59 says
74
+ * exactly that there. Told through PLAYWRIGHT_HOST_PLATFORM_OVERRIDE that
75
+ * the host is 24.04, the newest it knows, it installs that build and that
76
+ * list of libraries, and the clips record the same (a run of
77
+ * petbul/notify-test on 2026-10-31). Only Ubuntu, and only a newer one: for
78
+ * a distribution Playwright never knew there is no stand-in known to work,
79
+ * and its own message stands.
80
+ */
81
+ export declare function fallbackPlatform(output: string): string | null;
82
+ /**
83
+ * The installer, and once more as a platform Playwright knows where it
84
+ * refused this one (`fallbackPlatform`), saying so between the two. An
85
+ * override the person set themselves is theirs: nothing second-guesses it.
86
+ */
87
+ export declare function runWithFallback(command: string, args: string[], io: Pick<InstallerIO, "run" | "write">): Promise<number>;
51
88
  /** What `ensureFullBrowser` touches, so a test can stand in for all of it. */
52
89
  export type InstallerIO = {
53
90
  has: () => boolean;
54
- run: (command: string, args: string[]) => Promise<number>;
91
+ run: Installer;
55
92
  write: (text: string) => void;
56
93
  };
57
94
  /**
@@ -65,16 +65,68 @@ export function browserPaths() {
65
65
  };
66
66
  return { shell: where("chromium-headless-shell"), full: where("chromium") };
67
67
  }
68
- /** Playwright's installer, with its output on this terminal; its exit code. */
69
- export function runInstaller(command, args) {
70
- return new Promise((resolve) => {
71
- const child = spawn(command, args, { stdio: "inherit" });
72
- child.on("error", (error) => {
73
- process.stderr.write(`Could not run the installer: ${error.message}\n`);
74
- resolve(1);
75
- });
76
- child.on("exit", (code) => resolve(code ?? 1));
68
+ /**
69
+ * Playwright's installer, its output on this terminal as it runs and kept
70
+ * as well, for `fallbackPlatform` to read what it refused. The exit code
71
+ * comes with `close`, not `exit`: the last of the output arrives between
72
+ * the two.
73
+ */
74
+ export const runInstaller = (command, args, env = {}) => new Promise((resolve) => {
75
+ let output = "";
76
+ const child = spawn(command, args, {
77
+ stdio: ["inherit", "pipe", "pipe"],
78
+ env: { ...process.env, ...env },
79
+ });
80
+ const tee = (to) => (chunk) => {
81
+ output += chunk.toString();
82
+ to.write(chunk);
83
+ };
84
+ child.stdout?.on("data", tee(process.stdout));
85
+ child.stderr?.on("data", tee(process.stderr));
86
+ child.on("error", (error) => {
87
+ process.stderr.write(`Could not run the installer: ${error.message}\n`);
88
+ resolve({ code: 1, output });
77
89
  });
90
+ child.on("close", (code) => resolve({ code: code ?? 1, output }));
91
+ });
92
+ /**
93
+ * The platform to run the installer again as, when Playwright refused this
94
+ * one; null when there is none to try.
95
+ *
96
+ * Playwright knows the Ubuntu releases of its day and refuses a newer one
97
+ * outright, the browser as well as the system libraries: "Playwright does
98
+ * not support chromium-headless-shell on ubuntu26.04-x64". GitHub's
99
+ * ubuntu-latest became 26.04 in October 2026, and Playwright 1.59 says
100
+ * exactly that there. Told through PLAYWRIGHT_HOST_PLATFORM_OVERRIDE that
101
+ * the host is 24.04, the newest it knows, it installs that build and that
102
+ * list of libraries, and the clips record the same (a run of
103
+ * petbul/notify-test on 2026-10-31). Only Ubuntu, and only a newer one: for
104
+ * a distribution Playwright never knew there is no stand-in known to work,
105
+ * and its own message stands.
106
+ */
107
+ export function fallbackPlatform(output) {
108
+ const refused = /does not support \S+ on ubuntu(\d+)\.(\d+)-([a-z0-9]+)/.exec(output);
109
+ if (!refused)
110
+ return null;
111
+ const [, major, minor, arch] = refused;
112
+ const newer = Number(major) > 24 || (Number(major) === 24 && Number(minor) > 4);
113
+ return newer ? `ubuntu24.04-${arch}` : null;
114
+ }
115
+ /**
116
+ * The installer, and once more as a platform Playwright knows where it
117
+ * refused this one (`fallbackPlatform`), saying so between the two. An
118
+ * override the person set themselves is theirs: nothing second-guesses it.
119
+ */
120
+ export async function runWithFallback(command, args, io) {
121
+ const first = await io.run(command, args);
122
+ if (first.code === 0 || process.env.PLAYWRIGHT_HOST_PLATFORM_OVERRIDE)
123
+ return first.code;
124
+ const as = fallbackPlatform(first.output);
125
+ if (as === null)
126
+ return first.code;
127
+ io.write(`Playwright ${pinnedPlaywright().version} does not know this OS yet. Installing as ${as}, the newest Ubuntu it does, whose build and libraries record the same here:\n` +
128
+ ` PLAYWRIGHT_HOST_PLATFORM_OVERRIDE=${as} ${[command, ...args].join(" ")}\n`);
129
+ return (await io.run(command, args, { PLAYWRIGHT_HOST_PLATFORM_OVERRIDE: as })).code;
78
130
  }
79
131
  /**
80
132
  * Before a command opens a window: the full browser, fetched once if `install`
@@ -94,7 +146,7 @@ export async function ensureFullBrowser(io = {
94
146
  "fetched only the headless shell that records.\n" +
95
147
  `Fetching it once, for Playwright ${playwright.version}:\n` +
96
148
  ` ${[command, ...args].join(" ")}\n`);
97
- return (await io.run(command, args)) === 0;
149
+ return (await runWithFallback(command, args, io)) === 0;
98
150
  }
99
151
  /**
100
152
  * Install the Chromium this version of versioncam records with.
@@ -121,6 +173,9 @@ export async function install(argv) {
121
173
  ? `Both Chromium builds for Playwright ${playwright.version}: the headless shell versioncam records with, and the browser login and preview open:\n`
122
174
  : `The headless Chromium versioncam records with, for Playwright ${playwright.version}:\n`) +
123
175
  ` ${[command, ...args].join(" ")}\n`);
124
- return runInstaller(command, args);
176
+ return runWithFallback(command, args, {
177
+ run: runInstaller,
178
+ write: (text) => process.stdout.write(text),
179
+ });
125
180
  }
126
181
  //# sourceMappingURL=install.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"install.js","sourceRoot":"","sources":["../../../src/cli/commands/install.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"install.js","sourceRoot":"","sources":["../../../src/cli/commands/install.ts"],"names":[],"mappings":"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"}
@@ -1,3 +1,4 @@
1
+ import type { ReplayMode } from "../../driver/replay.js";
1
2
  /**
2
3
  * Record every clip the config points at, serially.
3
4
  *
@@ -8,3 +9,8 @@
8
9
  * error alone.
9
10
  */
10
11
  export declare function record(argv: string[]): Promise<number>;
12
+ /**
13
+ * `--replay <mode>`, checked: a mode misspelt would otherwise be read as the
14
+ * config's, and record over a replay someone meant to keep.
15
+ */
16
+ export declare function replayModeOf(value: string | boolean | undefined): ReplayMode | undefined | Error;